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}
+

+
+ )
+ };
+ });
// 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}
+

+
+ ),
+ 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 || '-',