From 4a07c5e47c2511d46520be4b3b26a546fa887188 Mon Sep 17 00:00:00 2001 From: Malini Date: Fri, 28 Nov 2025 18:22:08 +0530 Subject: [PATCH] added sort --- .../public/assets/images/active-sort.svg | 3 + .../public/assets/images/inactive-sort.svg | 3 + .../pages/Admin/configuration/IsicHsCodes.jsx | 136 +++++++++++-- .../Admin/configuration/QuarterlyWindows.jsx | 152 +++++++++++++-- .../pages/Admin/configuration/UnitMaster.jsx | 180 ++++++++++++++++-- 5 files changed, 430 insertions(+), 44 deletions(-) create mode 100644 ipi-survey-platform/public/assets/images/active-sort.svg create mode 100644 ipi-survey-platform/public/assets/images/inactive-sort.svg diff --git a/ipi-survey-platform/public/assets/images/active-sort.svg b/ipi-survey-platform/public/assets/images/active-sort.svg new file mode 100644 index 0000000..64fdf42 --- /dev/null +++ b/ipi-survey-platform/public/assets/images/active-sort.svg @@ -0,0 +1,3 @@ + + + diff --git a/ipi-survey-platform/public/assets/images/inactive-sort.svg b/ipi-survey-platform/public/assets/images/inactive-sort.svg new file mode 100644 index 0000000..cff4ce6 --- /dev/null +++ b/ipi-survey-platform/public/assets/images/inactive-sort.svg @@ -0,0 +1,3 @@ + + + diff --git a/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx b/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx index dc5f143..0f93efd 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/IsicHsCodes.jsx @@ -375,6 +375,10 @@ const IsicHsCodes = () => { }); const [showImportModal, setShowImportModal] = React.useState(false); const [isRefreshing, setIsRefreshing] = React.useState(false); + const [sortConfig, setSortConfig] = React.useState({ + key: null, + direction: 'asc' + }); // Get user profile from session const userProfile = React.useMemo(() => { @@ -459,7 +463,7 @@ const IsicHsCodes = () => { if (product.created_by) { try { - createdByName = product.created_by_user?.name || '-'; + createdByName = product.created_by_name?.name || '-'; } catch (error) { console.error('Error fetching creator info:', error); } @@ -471,7 +475,7 @@ const IsicHsCodes = () => { product: product.product_name || 'N/A', unit: product.unit?.uom || 'N/A', estimatedMapped: product.mapped_establishment_count || 0, - createdBy: createdByName, + createdBy: product.created_by_name || 'N/A', createdOn: createdDate || null, updated: updatedDate, createdById: createdById, @@ -499,16 +503,100 @@ const IsicHsCodes = () => { fetchProducts(); }, []); + const handleSort = (key) => { + let direction = 'asc'; + if (sortConfig.key === key && sortConfig.direction === 'asc') { + direction = 'desc'; + } + setSortConfig({ key, direction }); + }; + + const getSortedData = (data) => { + if (!sortConfig.key) return data; + + return [...data].sort((a, b) => { + let aValue = a[sortConfig.key]; + let bValue = b[sortConfig.key]; + + if (sortConfig.key === 'estimatedMapped') { + return sortConfig.direction === 'asc' + ? aValue - bValue + : bValue - aValue; + } + + if (aValue < bValue) { + return sortConfig.direction === 'asc' ? -1 : 1; + } + if (aValue > bValue) { + return sortConfig.direction === 'asc' ? 1 : -1; + } + return 0; + }); + }; + const headers = [ - 'HS Code', - 'Product Name', - 'Description', - 'Unit', - 'Establishment Mapped', - 'Created By', - 'Last Updated', - 'Action', - ]; + { + name: HS Code, + key: 'code', + sortable: true + }, + { + name: Product Name, + key: 'product', + sortable: true + }, + { + name: Description, + key: 'description', + sortable: true + }, + { + name: Unit, + key: 'unit', + sortable: true + }, + { + name: Establishment Mapped, + key: 'estimatedMapped', + sortable: true + }, + { + name: Created By, + key: 'createdBy', + sortable: true + }, + { + name: Last Updated, + key: 'updated', + sortable: true + }, + { + name: 'Actions', + key: 'actions', + sortable: false + } + ].map(header => { + if (!header.sortable) return { name: header.name }; + + const isActive = sortConfig.key === header.key; + const isAsc = sortConfig.direction === 'asc'; + + return { + name: ( +
handleSort(header.key)} + > + {header.name} + Sort +
+ ) + }; + }); // Filter data based on search term React.useEffect(() => { @@ -527,7 +615,9 @@ const IsicHsCodes = () => { setCurrentPage(1); // Reset to first page when searching }, [searchTerm, rowsData]); - const rows = filteredData.map((item) => { + const sortedData = getSortedData(filteredData); + + const rows = sortedData.map((item) => { let displayName = item.createdBy; if (displayName === '-' && item.createdById) { try { @@ -548,7 +638,7 @@ const IsicHsCodes = () => { item.product, item.description || '-', item.unit, - item.estimatedMapped, +
{item.estimatedMapped}
, displayName, item.updated || item.createdOn || '-', 'actions', @@ -1212,10 +1302,21 @@ const IsicHsCodes = () => { {/* Table - Desktop */} -
+
{ // Handle product name and description columns with text wrapping if (colIndex === 1 || colIndex === 2) { @@ -1225,6 +1326,13 @@ const IsicHsCodes = () => { ); } + if (colIndex === 5 || colIndex === 6) { + return ( +
+ {value} +
+ ); + } // Handle action buttons column (index 7) if (colIndex === 7) { diff --git a/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx b/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx index dcf4199..f2b00ee 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx @@ -89,6 +89,10 @@ const QuarterlyWindows = () => { const [isLoading, setIsLoading] = React.useState(false); const [validationErrors, setValidationErrors] = React.useState({}); const [toastData, setToastData] = React.useState(null); + const [sortConfig, setSortConfig] = React.useState({ + key: null, + direction: 'asc' + }); // Format date to yyyy-MM-dd for input fields const formatDateForInput = (dateString) => { @@ -142,19 +146,139 @@ const QuarterlyWindows = () => { }, []); // Table headers with left alignment and no wrapping + const handleSort = (key) => { + let direction = 'asc'; + if (sortConfig.key === key && sortConfig.direction === 'asc') { + direction = 'desc'; + } + setSortConfig({ key, direction }); + }; + + const getSortedData = (data) => { + if (!sortConfig.key) return data; + + return [...data].sort((a, b) => { + let aValue = a[sortConfig.key]; + let bValue = b[sortConfig.key]; + + // Handle numeric comparisons + if (['year', 'assigned', 'responded', 'not_responded', 'submissionCount'].includes(sortConfig.key)) { + aValue = Number(aValue) || 0; + bValue = Number(bValue) || 0; + return sortConfig.direction === 'asc' ? aValue - bValue : bValue - aValue; + } + + // Handle date comparisons + if (['startDate', 'endDate'].includes(sortConfig.key)) { + aValue = aValue ? new Date(aValue).getTime() : 0; + bValue = bValue ? new Date(bValue).getTime() : 0; + return sortConfig.direction === 'asc' ? aValue - bValue : bValue - aValue; + } + + // Default string comparison + aValue = String(aValue || '').toLowerCase(); + bValue = String(bValue || '').toLowerCase(); + + if (aValue < bValue) { + return sortConfig.direction === 'asc' ? -1 : 1; + } + if (aValue > bValue) { + return sortConfig.direction === 'asc' ? 1 : -1; + } + return 0; + }); + }; + const headers = [ - { name: 'Survey Name', className: 'whitespace-nowrap text-left' }, - { name: 'Year', className: 'whitespace-nowrap text-left' }, - { name: 'Quarter', className: 'whitespace-nowrap text-left' }, - { name: 'Start Date', className: 'whitespace-nowrap text-left' }, - { name: 'End Date', className: 'whitespace-nowrap text-left' }, - { name: 'Grace Period', className: 'whitespace-nowrap text-left' }, - { name: 'Assigned', className: 'whitespace-nowrap text-left' }, - { name: 'Responded', className: 'whitespace-nowrap text-left' }, - { name: 'Not Responded', className: 'whitespace-nowrap text-left' }, - { name: 'Submission Count', className: 'whitespace-nowrap text-left' }, - { name: 'Actions', className: 'whitespace-nowrap text-left' }, - ]; + { + name: 'Survey Name', + key: 'survey_name', + sortable: true, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Year', + key: 'year', + sortable: true, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Quarter', + key: 'quarter', + sortable: true, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Start Date', + key: 'startDate', + sortable: true, + className: 'whitespace-nowrap text-left' + }, + { + name: 'End Date', + key: 'endDate', + sortable: true, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Grace Period', + key: 'gracePeriod', + sortable: false, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Assigned', + key: 'assigned', + sortable: false, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Responded', + key: 'responded', + sortable: false, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Not Responded', + key: 'not_responded', + sortable: false, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Submission Count', + key: 'submissionCount', + sortable: false, + className: 'whitespace-nowrap text-left' + }, + { + name: 'Actions', + key: 'actions', + sortable: false, + className: 'whitespace-nowrap text-left' + }, + ].map(header => { + if (!header.sortable) return { name: header.name, className: header.className }; + + const isActive = sortConfig.key === header.key; + const isAsc = sortConfig.direction === 'asc'; + + return { + name: ( +
handleSort(header.key)} + > + {header.name} + Sort +
+ ), + className: header.className + }; + }); const columnwidth = [ 220, // Survey Name @@ -215,7 +339,9 @@ const QuarterlyWindows = () => { ); - const rows = filteredRows.map((item) => [ + const sortedRows = getSortedData(filteredRows); + + const rows = sortedRows.map((item) => [ item.survey_name || '-', item.year, item.quarter, diff --git a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx index 9fb1336..277ec1b 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx @@ -89,8 +89,53 @@ const UnitMaster = () => { const [importError, setImportError] = useState(''); const fileInputRef = useRef(null); const [pageSize, setPageSize] = React.useState(10); - - // const pageSize = 10; + const [sortConfig, setSortConfig] = React.useState({ + key: 'created_at', + direction: 'desc' + }); + + // Sort units based on sortConfig + const sortedUnits = React.useMemo(() => { + const sortableItems = [...units]; + if (sortConfig.key) { + sortableItems.sort((a, b) => { + let aValue, bValue; + + // Get the values to sort by + if (sortConfig.key === 'created_by') { + // For created_by, use created_by_name for sorting + aValue = (a.created_by_name || '').toString().toLowerCase(); + bValue = (b.created_by_name || '').toString().toLowerCase(); + } else if (sortConfig.key === 'created_at' || sortConfig.key === 'updated_at') { + // Handle date fields + aValue = a[sortConfig.key] ? new Date(a[sortConfig.key]) : new Date(0); + bValue = b[sortConfig.key] ? new Date(b[sortConfig.key]) : new Date(0); + } else { + // Handle other string fields + aValue = (a[sortConfig.key] || '').toString().toLowerCase(); + bValue = (b[sortConfig.key] || '').toString().toLowerCase(); + } + + if (aValue < bValue) { + return sortConfig.direction === 'asc' ? -1 : 1; + } + if (aValue > bValue) { + return sortConfig.direction === 'asc' ? 1 : -1; + } + return 0; + }); + } + return sortableItems; + }, [units, sortConfig]); + + // Handle sort request + const requestSort = (key) => { + let direction = 'asc'; + if (sortConfig.key === key && sortConfig.direction === 'asc') { + direction = 'desc'; + } + setSortConfig({ key, direction }); + }; // Fetch units on component mount useEffect(() => { @@ -101,7 +146,7 @@ const UnitMaster = () => { try { setLoading(true); const response = await getUnits(); - + // Handle different response formats let unitsData = []; if (Array.isArray(response)) { @@ -136,13 +181,19 @@ const UnitMaster = () => { }; const filteredUnits = useMemo(() => { - if (!searchTerm) return units; - const term = searchTerm.toLowerCase(); - return units.filter(unit => - (unit.uom && unit.uom.toLowerCase().includes(term)) || - (unit.description && unit.description.toLowerCase().includes(term)) - ); - }, [units, searchTerm]); + let result = [...sortedUnits]; + + // Apply search filter if searchTerm exists + if (searchTerm) { + const term = searchTerm.toLowerCase(); + result = result.filter(unit => + (unit.uom && unit.uom.toLowerCase().includes(term)) || + (unit.description && unit.description.toLowerCase().includes(term)) + ); + } + + return result; + }, [sortedUnits, searchTerm]); useEffect(() => { const userProfile = sessionStorage.getItem('user_profile'); @@ -159,15 +210,110 @@ const UnitMaster = () => { }, []); const headers = [ - 'Unit Name', - 'Description', - 'Created By', - 'Created On', - 'Last Updated', - 'Actions', + { + name: ( +
+ Unit Name + +
+ ), + key: 'uom', + sortable: true + }, + { + name: ( +
+ Description + +
+ ), + key: 'description', + sortable: true + }, + { + name: ( +
+ Created By + +
+ ), + key: 'created_by', + sortable: true + }, + { + name: ( +
+ Created On + +
+ ), + key: 'created_at', + sortable: true + }, + { + name: ( +
+ Last Updated + +
+ ), + key: 'updated_at', + sortable: true + }, + { + name: 'Actions', + key: 'actions', + sortable: false + }, ]; const rows = useMemo(() => { + // Use filteredUnits which is already sorted return filteredUnits.map((item) => { const createdDate = item.created_at ? new Date(item.created_at).toLocaleDateString('en-GB') @@ -176,7 +322,7 @@ const UnitMaster = () => { ? new Date(item.updated_at).toLocaleDateString('en-GB') : '-'; - const createdByName = item.created_by_name || currentUser?.name || '-'; + const createdByName = item.created_by_name ||'-'; return [ item.uom || '-',