diff --git a/ipi-survey-platform/package-lock.json b/ipi-survey-platform/package-lock.json index 57f0983..7f6674b 100644 --- a/ipi-survey-platform/package-lock.json +++ b/ipi-survey-platform/package-lock.json @@ -13,7 +13,8 @@ "react": "^19.1.1", "react-dom": "^19.1.1", "react-google-recaptcha": "^3.1.0", - "react-router-dom": "^7.9.4" + "react-router-dom": "^7.9.4", + "react-toastify": "^11.0.5" }, "devDependencies": { "@eslint/js": "^9.36.0", @@ -1982,6 +1983,15 @@ "node": ">=18" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3612,6 +3622,19 @@ "react-dom": ">=18" } }, + "node_modules/react-toastify": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz", + "integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", diff --git a/ipi-survey-platform/package.json b/ipi-survey-platform/package.json index 60e9044..a310338 100644 --- a/ipi-survey-platform/package.json +++ b/ipi-survey-platform/package.json @@ -15,7 +15,8 @@ "react": "^19.1.1", "react-dom": "^19.1.1", "react-google-recaptcha": "^3.1.0", - "react-router-dom": "^7.9.4" + "react-router-dom": "^7.9.4", + "react-toastify": "^11.0.5" }, "devDependencies": { "@eslint/js": "^9.36.0", diff --git a/ipi-survey-platform/public/assets/images/Eye.svg b/ipi-survey-platform/public/assets/images/Eye.svg new file mode 100644 index 0000000..0223ecf --- /dev/null +++ b/ipi-survey-platform/public/assets/images/Eye.svg @@ -0,0 +1,3 @@ + + + diff --git a/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx b/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx index 0f05699..e31650b 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx @@ -14,7 +14,7 @@ const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg'; const downloadIconSrc = '/assets/images/DownloadSimple.svg'; const addIconSrc = '/assets/images/ic_baseline-plus.svg'; const pencilActiveSrc = '/assets/images/PencilSimple.svg'; -const pencilInactiveSrc = '/assets/images/PencilSimple-inactive.svg'; +const pencilInactiveSrc = '/assets/images/pencilsimple-inactive.svg'; const trashActiveSrc = '/assets/images/Trash - active.svg'; const trashInactiveSrc = '/assets/images/Trash - Inactive.svg'; const resetPasswordActiveSrc = '/assets/images/hugeicons_reset-password.svg'; @@ -633,6 +633,7 @@ const CompanyProfile = () => { onCountryCodeChange={handleFormChange('contactCountryCode')} value={form.contactMobileNumber} onChange={handleFormChange('contactMobileNumber')} + // placeholder="---------" width="100%" required error={fieldErrors.contactMobileNumber} @@ -766,6 +767,7 @@ const CompanyProfile = () => { onCountryCodeChange={handleFormChange('corporateCountryCode')} value={form.corporateMobileNumber} onChange={handleFormChange('corporateMobileNumber')} + placeholder="-------" width="100%" required error={fieldErrors.corporateMobileNumber} @@ -987,36 +989,37 @@ const CompanyProfile = () => { ); const headers = [ - renderSortableHeader('Establishment Name', 'establishmentName'), - 'Name', - 'Emirates', + 'Establishment Name', + 'Contact Name', + 'Emirate', 'ISIC Code', 'Establishment ID', 'Products', - renderSortableHeader('Total Employees', 'totalEmployees'), + 'Total Employees', 'Created By', - renderSortableHeader('Created on', 'createdOn'), - renderSortableHeader('Last Updated', 'lastUpdated'), + 'Created On', + 'Last Updated', 'Status', 'Actions', ]; const columnWidths = React.useMemo(() => { - const base = Array(headers.length).fill(undefined); - base[0] = 250; // Establishment Name - base[1] = 118; // Name - base[2] = 118; // Emirates - base[3] = 132; // ISIC Code - base[4] = 208; // Establishment ID - base[5] = 100; // Products - base[6] = 152; // Total Employees - base[7] = 152; // Created By - base[8] = 148; // Created on - base[9] = 136; // Last Updated - base[10] = 120; // Status - base[11] = 119; // Actions + const base = [ + 250, // Establishment Name (250px) + 150, // Contact Name (150px) + 120, // Emirate (120px) + 120, // ISIC Code (120px) + 150, // Establishment ID (150px) + 100, // Products (100px) + 120, // Total Employees (120px) + 120, // Created By (120px) + 120, // Created On (120px) + 120, // Last Updated (120px) + 100, // Status (100px) + 150, // Actions (150px) + ]; return base; - }, [headers.length]); + }, []); const contactToCorporateMap = React.useMemo( () => ({ diff --git a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx index 255dbc8..3cab700 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx @@ -1,17 +1,18 @@ -import React from 'react'; +import React, { useState, useEffect, useMemo, useRef } from 'react'; import Table from '@/components/common/Table'; import StatusBadge from '@/components/common/StatusBadge'; import { TextField, SelectField } from '@/components/common/FormControls'; +import { getUnits, createUnit, updateUnit, deleteUnit } from '@/services/configuration/unitService'; const downloadIconSrc = '/assets/images/DownloadSimple.svg'; const addIconSrc = '/assets/images/ic_baseline-plus.svg'; const pencilActiveSrc = '/assets/images/PencilSimple.svg'; -const pencilInactiveSrc = '/assets/images/PencilSimple-inactive.svg'; +const pencilInactiveSrc = '/assets/images/pencilsimple-inactive.svg'; const trashActiveSrc = '/assets/images/Trash - active.svg'; const trashInactiveSrc = '/assets/images/Trash - Inactive.svg'; const deleteIconSrc = '/assets/images/delete.svg'; const caretDownIconSrc = '/assets/images/caretdown-active.svg'; -const rectangleIconSrc = '/assets/images/rectangle.svg'; +const rectangleIconSrc = '/assets/images/Rectangle.svg'; const checkboxIconSrc = '/assets/images/checkbox.svg'; const createEmptyUnitForm = () => ({ @@ -22,128 +23,72 @@ const createEmptyUnitForm = () => ({ }); const UnitMaster = () => { - const [units, setUnits] = React.useState([ - { - unitName: 'Kg', - description: 'Kilogram', - productsMapped: ['Wheat', 'Barley', 'Oats', 'Corn', 'Soybeans'], - createdBy: 'Akbar', - createdOn: '25/03/2025', - lastUpdated: '31/03/2025', - status: 'Active', - }, - { - unitName: 'Lt', - description: 'Liters', - productsMapped: ['Rice', 'Sorghum', 'Millet', 'Quinoa', 'Buckwheat', 'Corn'], - createdBy: 'Zara', - createdOn: '01/04/2025', - lastUpdated: '10/04/2025', - status: 'Active', - }, - { - unitName: 'Kg', - description: 'Kilogram', - productsMapped: ['Rice', 'Oats'], - createdBy: 'Ravi', - createdOn: '15/05/2025', - lastUpdated: '25/05/2025', - status: 'Inactive', - }, - { - unitName: 'Lt', - description: 'Liters', - productsMapped: ['Wheat', 'Barley', 'Corn', 'Soybeans', 'Millet', 'Quinoa', 'Sorghum'], - createdBy: 'Maya', - createdOn: '10/06/2025', - lastUpdated: '20/06/2025', - status: 'Active', - }, - { - unitName: 'Kg', - description: 'Kilogram', - productsMapped: ['Buckwheat'], - createdBy: 'Jin', - createdOn: '30/07/2025', - lastUpdated: '05/08/2025', - status: 'Active', - }, - { - unitName: 'Lt', - description: 'Liters', - productsMapped: ['Rice', 'Quinoa', 'Millet', 'Corn'], - createdBy: 'Leo', - createdOn: '12/09/2025', - lastUpdated: '20/09/2025', - status: 'Inactive', - }, - { - unitName: 'Kg', - description: 'Kilogram', - productsMapped: ['Wheat', 'Soybeans', 'Corn', 'Sorghum'], - createdBy: 'Lila', - createdOn: '05/10/2025', - lastUpdated: '15/10/2025', - status: 'Active', - }, - { - unitName: 'Lt', - description: 'Liters', - productsMapped: ['Barley', 'Oats', 'Millet'], - createdBy: 'Nina', - createdOn: '30/07/2025', - lastUpdated: '05/08/2025', - status: 'Inactive', - }, - { - unitName: 'Kg', - description: 'Kilogram', - productsMapped: ['Rice', 'Corn', 'Soybeans', 'Quinoa', 'Millet', 'Sorghum'], - createdBy: 'Owen', - createdOn: '10/06/2025', - lastUpdated: '20/06/2025', - status: 'Inactive', - }, - { - unitName: 'Lt', - description: 'Liters', - productsMapped: ['Wheat', 'Barley'], - createdBy: 'Riya', - createdOn: '28/03/2025', - lastUpdated: '31/03/2025', - status: 'Inactive', - }, - ]); - const [editingRow, setEditingRow] = React.useState(null); - const [deletingRow, setDeletingRow] = React.useState(null); - const [modalMode, setModalMode] = React.useState(null); - const [currentPage, setCurrentPage] = React.useState(1); - const pageSize = 10; - const [form, setForm] = React.useState(createEmptyUnitForm()); - const [isProductDropdownOpen, setIsProductDropdownOpen] = React.useState(false); - const productDropdownRef = React.useRef(null); + const [units, setUnits] = useState([]); + const [loading, setLoading] = useState(true); + const [editingRow, setEditingRow] = useState(null); + const [deletingRow, setDeletingRow] = useState(null); + const [modalMode, setModalMode] = useState(null); + const [currentPage, setCurrentPage] = useState(1); + const [form, setForm] = useState(createEmptyUnitForm()); + const [isProductDropdownOpen, setIsProductDropdownOpen] = useState(false); + const productDropdownRef = useRef(null); + const pageSize = 10; // Add this line + const [currentUser, setCurrentUser] = useState(null); + // Fetch units on component mount + useEffect(() => { + fetchUnits(); + }, []); + + const fetchUnits = async () => { + try { + setLoading(true); + const response = await getUnits(); + setUnits(response.data || []); + } catch (error) { + console.error('Error fetching units:', error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + const userProfile = sessionStorage.getItem('user_profile'); + + if (userProfile) { + setCurrentUser(JSON.parse(userProfile)); + } +}, []); const headers = [ 'Unit Name', 'Description', 'Mapped Products', 'Created By', 'Created On', - 'Last Updated', 'Status', 'Actions', ]; - const rows = units.map((item) => [ - item.unitName, - item.description, - Array.isArray(item.productsMapped) ? item.productsMapped.length : item.productsMapped, - item.createdBy, - item.createdOn, - item.lastUpdated, - , - 'actions', - ]); + const rows = useMemo(() => { + return units.map((item) => { + const createdDate = item.created_at ? new Date(item.created_at).toLocaleDateString('en-GB') : '-'; + const currentUserName = currentUser?.name || ''; + const updatedDate = + item.updated_at && item.updated_at !== item.created_at + ? new Date(item.updated_at).toLocaleDateString('en-GB') + : '-'; + + return [ + item.uom || item.unitName, + item.uom_short_name || item.description, + Array.isArray(item.productsMapped) ? item.productsMapped.length : 0, + item.created_by_name || currentUserName || '-', + createdDate, + , + 'actions', + ]; + }); +}, [units]); const statusOptions = React.useMemo( () => [ @@ -183,19 +128,30 @@ const UnitMaster = () => { if (!selected) return; setEditingRow(index); setForm({ - unitName: selected.unitName, - description: selected.description, + unitName: selected.uom || selected.unitName, + description: selected.uom_short_name || selected.description, productsMapped: Array.isArray(selected.productsMapped) ? [...selected.productsMapped] : [], - status: selected.status, + status: selected.is_active ? 'Active' : 'Inactive', }); setModalMode('edit'); setIsProductDropdownOpen(false); - }; +}; const handleDelete = (index) => { setDeletingRow(index); }; + const confirmDelete = async () => { + if (deletingRow === null) return; + + try { + await deleteUnit(units[deletingRow].id); + setUnits(prev => prev.filter((_, index) => index !== deletingRow)); + setDeletingRow(null); + } catch (error) { + console.error('Error deleting unit:', error); + } + }; const closeModal = () => { setModalMode(null); setForm(createEmptyUnitForm()); @@ -219,6 +175,7 @@ const UnitMaster = () => { }); }; + React.useEffect(() => { const handleClickOutside = (event) => { if (!productDropdownRef.current) return; @@ -244,42 +201,65 @@ const UnitMaster = () => { return `${day}/${month}/${year}`; }; - const handleSaveForm = () => { - const selectedProducts = Array.isArray(form.productsMapped) ? form.productsMapped : []; - if (!form.unitName || !form.description || selectedProducts.length === 0 || !form.status) { - return; - } +const handleSaveForm = async () => { + const selectedProducts = Array.isArray(form.productsMapped) ? form.productsMapped : []; + if (!form.unitName || !form.description || !form.status) { + return; + } + + const unitData = { + uom: form.unitName, + uom_short_name: form.description, + is_active: form.status === 'Active', + }; + + try { + setLoading(true); + if (modalMode === 'edit' && editingRow !== null) { - setUnits((prev) => + const updatedUnitData = { + ...unitData, + updated_at: new Date().toISOString(), + updated_by: currentUser?.id || null, + }; + + const updatedUnit = await updateUnit(units[editingRow].id, updatedUnitData); + + setUnits(prev => prev.map((item, index) => index === editingRow ? { ...item, - unitName: form.unitName, - description: form.description, - productsMapped: [...selectedProducts], - status: form.status, - lastUpdated: getToday(), + ...updatedUnit, + updated_at: updatedUnitData.updated_at, + created_at: item.created_at, + created_by: item.created_by, + created_by_name: item.created_by_name, } : item ) ); } else { - setUnits((prev) => [ - { - unitName: form.unitName, - description: form.description, - productsMapped: [...selectedProducts], - createdBy: 'Admin', - createdOn: getToday(), - lastUpdated: getToday(), - status: form.status, - }, - ...prev, - ]); + const newUnit = await createUnit({ + ...unitData, + created_at: new Date().toISOString(), + created_by: currentUser?.id || null, + created_by_name: currentUser?.name || '', + }); + + setUnits(prev => [newUnit, ...prev]); } + + await fetchUnits(); closeModal(); - }; + } catch (error) { + console.error('Error saving unit:', error); + } finally { + setLoading(false); + } +}; + + const deletingUnit = React.useMemo(() => { if (deletingRow === null || deletingRow < 0 || deletingRow >= units.length) { @@ -291,12 +271,18 @@ const UnitMaster = () => { const closeDeleteConfirm = () => { setDeletingRow(null); }; + - const handleDeleteConfirm = () => { + const handleDeleteConfirm = async () => { if (deletingRow === null) return; - setUnits((prev) => prev.filter((_, index) => index !== deletingRow)); - closeDeleteConfirm(); - }; + try { + await deleteUnit(units[deletingRow].id); + setUnits(prev => prev.filter((_, index) => index !== deletingRow)); + closeDeleteConfirm(); + } catch (error) { + console.error('Error deleting unit:', error); + } +}; return (
@@ -317,7 +303,11 @@ const UnitMaster = () => {
- + {loading ? ( +
+
+
+) : ( { totalItems: filteredRows.length, }} /> - + )} {modalMode && (
@@ -399,14 +389,14 @@ const UnitMaster = () => { label="Unit Name" value={form.unitName} onChange={handleFormChange('unitName')} - placeholder="Enter Unit Code" + placeholder="Enter Unit Name" width="100%" />
@@ -475,10 +465,16 @@ const UnitMaster = () => {
@@ -511,7 +507,7 @@ const UnitMaster = () => {

Are you sure you want to delete{' '} - {`${deletingUnit.unitName} - ${deletingUnit.description}`} + {`${deletingUnit?.unitName || deletingUnit?.uom || 'N/A'} - ${deletingUnit?.description || deletingUnit?.uom_short_name || 'N/A'}`} ?