setSearchTerm(event.target.value)}
className="w-full h-10 rounded-md border border-[#E5E7EB] pl-10 pr-4 text-sm focus:outline-none"
@@ -2205,8 +2231,7 @@ const CompanyProfile = () => {
onPageChange: setCurrentPage,
pageSize,
totalItems: filteredProfiles.length,
- pageSizeOptions,
- onPageSizeChange: setPageSize,
+ pageSizeOptions: [10, 20, 50, 100],
}}
/>
)}
diff --git a/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx b/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx
index ff05c1e..2ca665b 100644
--- a/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx
+++ b/ipi-survey-platform/src/pages/Admin/configuration/QuarterlyWindows.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import Table from '@/components/common/Table';
import { TextField, SelectField, DateField } from '@/components/common/FormControls';
import StatusBadge from '@/components/common/StatusBadge';
+import { createQuarterlyWindow, updateQuarterlyWindow, getQuarterlyWindows } from '@/services/configuration/quarterlyWindows';
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
const addIconSrc = '/assets/images/ic_baseline-plus.svg';
@@ -15,7 +16,7 @@ const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
const caretDownSrc = '/assets/images/CaretDown-black.svg';
const createEmptyQuarterForm = () => ({
- establishment: '',
+ establishment: '-', // Default to hyphen
year: '',
quarter: '',
startDate: '',
@@ -26,173 +27,7 @@ const createEmptyQuarterForm = () => ({
});
const QuarterlyWindows = () => {
- const [quarterData, setQuarterData] = React.useState([
- {
- establishment: 'Cascade Marine Foods LLC',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '0',
- status: 'Active',
- },
- {
- establishment: 'Khazan Meat Factory',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '40',
- status: 'Active',
- },
- {
- establishment: 'Spinneys Fresh Food Industries L.L.C',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '30',
- status: 'Active',
- },
- {
- establishment: 'Freshly Frozen Foods Factory L.L.C',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '15',
- status: 'Active',
- },
- {
- establishment: 'Sahar Food Industry (L.L.C)',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '25',
- status: 'Active',
- },
- {
- establishment: 'Continental Food Processing LLC',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '60',
- status: 'Inactive',
- },
- {
- establishment: 'Al Khazna Poultry Farm',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '15',
- status: 'Active',
- },
- {
- establishment: 'Diamond Meat Processing L.L.C',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '10',
- status: 'Active',
- },
- {
- establishment: 'Alliance Foods Co L.L.C',
- year: '2025',
- quarter: 'Q1',
- startDate: '11/12/2025',
- endDate: '31/03/2025',
- gracePeriod: '15 days',
- submissionCount: '44',
- status: 'Active',
- },
- {
- establishment: 'Krustasia Foods L.L.C',
- year: '2024',
- quarter: 'Q4',
- startDate: '14/10/2024',
- endDate: '31/12/2024',
- gracePeriod: '15 days',
- submissionCount: '25',
- status: 'Inactive',
- },
- {
- establishment: 'Gulf Coast Seafood Exports',
- year: '2025',
- quarter: 'Q2',
- startDate: '01/04/2025',
- endDate: '30/06/2025',
- gracePeriod: '15 days',
- submissionCount: '32',
- status: 'Active',
- },
- {
- establishment: 'Emirates Food Logistics',
- year: '2025',
- quarter: 'Q2',
- startDate: '01/04/2025',
- endDate: '30/06/2025',
- gracePeriod: '15 days',
- submissionCount: '18',
- status: 'Active',
- },
- {
- establishment: 'Global Seafood Traders',
- year: '2025',
- quarter: 'Q2',
- startDate: '01/04/2025',
- endDate: '30/06/2025',
- gracePeriod: '15 days',
- submissionCount: '22',
- status: 'Active',
- },{
- establishment: 'Global Seafood Traders',
- year: '2025',
- quarter: 'Q2',
- startDate: '01/04/2025',
- endDate: '30/06/2025',
- gracePeriod: '15 days',
- status: 'Active',
- },
- {
- establishment: 'Global Seafood Traders',
- year: '2025',
- quarter: 'Q2',
- startDate: '01/04/2025',
- endDate: '30/06/2025',
- gracePeriod: '15 days',
- status: 'Active',
- },
- {
- establishment: 'Global Seafood Traders',
- year: '2025',
- quarter: 'Q2',
- startDate: '01/04/2025',
- endDate: '30/06/2025',
- gracePeriod: '15 days',
- status: 'Active',
- },
- {
- establishment: 'Global Seafood Traders',
- year: '2025',
- quarter: 'Q2',
- startDate: '01/04/2025',
- endDate: '30/06/2025',
- gracePeriod: '15 days',
- status: 'Active',
- },
- ]);
+ const [quarterData, setQuarterData] = React.useState([]);
const [editingRow, setEditingRow] = React.useState(null);
const [deletingRow, setDeletingRow] = React.useState(null);
const [modalMode, setModalMode] = React.useState(null);
@@ -205,6 +40,42 @@ const QuarterlyWindows = () => {
const [hoveredDelete, setHoveredDelete] = React.useState(null);
const [currentPage, setCurrentPage] = React.useState(1);
const pageSize = 10;
+ const [isLoading, setIsLoading] = React.useState(false);
+
+ // Fetch quarterly windows data on component mount
+ React.useEffect(() => {
+ const fetchQuarterlyWindows = async () => {
+ setIsLoading(true);
+ try {
+ const response = await getQuarterlyWindows();
+ if (response.status === 'success' && Array.isArray(response.data)) {
+ // Transform the API response to match the expected format
+ const formattedData = response.data.map(item => ({
+ id: item.id,
+ establishment: item.establishment || '-', // Default to hyphen if empty
+ year: item.year?.toString() || '',
+ quarter: item.quarter || '',
+ startDate: item.start_date ? new Date(item.start_date).toLocaleDateString('en-GB') : '',
+ endDate: item.end_date ? new Date(item.end_date).toLocaleDateString('en-GB') : '',
+ gracePeriod: item.grace_periods_days ? `${item.grace_periods_days} days` : '0 days',
+ submissionCount: '0', // Default value since it's not in the API response
+ status: item.is_active ? 'Active' : 'Inactive'
+ }));
+ setQuarterData(formattedData);
+ } else {
+ console.error('Unexpected API response format:', response);
+ // Optionally set some error state here
+ }
+ } catch (error) {
+ console.error('Error fetching quarterly windows:', error);
+ // You might want to add error handling UI here
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchQuarterlyWindows();
+ }, []);
const deletingQuarter = React.useMemo(() => {
if (deletingRow === null || deletingRow < 0 || deletingRow >= quarterData.length) {
@@ -213,8 +84,8 @@ const QuarterlyWindows = () => {
return quarterData[deletingRow];
}, [deletingRow, quarterData]);
- const headers = ['Year', 'Quarter', 'Start Date', 'End Date', 'Grace Period', 'Submission Count', 'Status', 'Actions'];
-
+ const headers = ['Survey Name', 'Year', 'Quarter', 'Start Date', 'End Date', 'Grace Period', 'Submission Count', 'Status', 'Actions'];
+ const columnwidth = [300, 100, 100, 100, 100, 100, 100, 100, 100];
const filteredRows = React.useMemo(() => {
const term = searchTerm.trim().toLowerCase();
const statusValue = statusFilter.toLowerCase();
@@ -240,6 +111,7 @@ const QuarterlyWindows = () => {
}, [quarterData, searchTerm, statusFilter]);
const rows = filteredRows.map((item) => [
+ item.establishment,
item.year,
item.quarter,
item.startDate,
@@ -309,13 +181,62 @@ const QuarterlyWindows = () => {
setDeletingRow(null);
};
- const handleSave = () => {
- if (modalMode === 'add') {
- setQuarterData((prev) => [...prev, form]);
- } else if (modalMode === 'edit' && editingRow !== null) {
- setQuarterData((prev) => prev.map((item, idx) => (idx === editingRow ? form : item)));
+ const handleSave = async () => {
+ try {
+ const formatDate = (dateString) => {
+ if (!dateString) return '';
+ const [day, month, year] = dateString.split('/');
+ return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
+ };
+
+ const formattedData = {
+ year: parseInt(form.year, 10) || 0,
+ quarter: form.quarter || 'Q1',
+ start_date: formatDate(form.startDate) || new Date().toISOString().split('T')[0],
+ end_date: formatDate(form.endDate) || new Date().toISOString().split('T')[0],
+ grace_periods_days: parseInt(form.gracePeriod?.split(' ')[0], 10) || 0,
+ is_active: form.status === 'Active',
+ establishment: '-', // Default to hyphen if empty
+ };
+
+ if (modalMode === 'add') {
+ const response = await createQuarterlyWindow(formattedData);
+ if (response.status === 'success') {
+ const newItem = {
+ ...formattedData,
+ id: response.data.id || Date.now(), // Use the ID from response or generate a temporary one
+ startDate: form.startDate,
+ endDate: form.endDate,
+ gracePeriod: formattedData.grace_periods_days ? `${formattedData.grace_periods_days} days` : '0 days',
+ submissionCount: '0',
+ status: formattedData.is_active ? 'Active' : 'Inactive'
+ };
+ setQuarterData((prev) => [...prev, newItem]);
+ }
+ } else if (modalMode === 'edit' && editingRow !== null) {
+ const response = await updateQuarterlyWindow(quarterData[editingRow].id, formattedData);
+ if (response.status === 'success') {
+ setQuarterData((prev) =>
+ prev.map((item, idx) =>
+ idx === editingRow
+ ? {
+ ...item,
+ ...formattedData,
+ startDate: form.startDate,
+ endDate: form.endDate,
+ gracePeriod: formattedData.grace_periods_days ? `${formattedData.grace_periods_days} days` : '0 days',
+ status: formattedData.is_active ? 'Active' : 'Inactive'
+ }
+ : item
+ )
+ );
+ }
+ }
+ handleCloseModal();
+ } catch (error) {
+ console.error('Error saving quarterly window:', error);
+ // You might want to add error handling UI here
}
- handleCloseModal();
};
const handleDelete = () => {
@@ -402,6 +323,7 @@ const QuarterlyWindows = () => {
{
if (colIndex === headers.length - 1) {
@@ -464,7 +386,7 @@ const QuarterlyWindows = () => {
>
- {modalMode === 'edit' ? 'Edit Quarter' : 'Add Quarter'}
+ {modalMode === 'edit' ? 'Edit Quarter' : 'Create Quarterly Survey'}
{modalMode === 'edit' && (
@@ -495,8 +417,23 @@ const QuarterlyWindows = () => {
-
-
+
+
+ setForm({ ...form, establishment: e.target.value })}
+ placeholder="Enter survey name"
+ />
+
+
+ setForm({ ...form, quarter: e.target.value })}
+ options={['Q1', 'Q2', 'Q3', 'Q4'].map((q) => ({ label: q, value: q }))}
+ placeholder="Select Quarter"
+ />
{
})}
placeholder="Select Year"
/>
- setForm({ ...form, quarter: e.target.value })}
- options={['Q1', 'Q2', 'Q3', 'Q4'].map((q) => ({ label: q, value: q }))}
- placeholder="Select Quarter"
- />
setForm({ ...form, startDate: e.target.value })}
- placeholder="Select Start Date"
+ placeholder="Select Date"
/>
setForm({ ...form, endDate: e.target.value })}
- placeholder="Select End Date"
+ placeholder="Select Date"
/>
setForm({ ...form, gracePeriod: e.target.value })}
options={['5 days', '10 days', '15 days', '20 days', '30 days'].map((item) => ({
@@ -561,7 +491,7 @@ const QuarterlyWindows = () => {
className="h-10 px-8 rounded-md bg-[#92722A] text-sm font-medium text-white shadow-[0_8px_18px_rgba(146,114,42,0.35)]"
onClick={handleSave}
>
- {modalMode === 'edit' ? 'Update Quarter' : 'Add'}
+ {modalMode === 'edit' ? 'Update Quarter' : 'Publish Now'}
diff --git a/ipi-survey-platform/src/pages/Overview/Overview.jsx b/ipi-survey-platform/src/pages/Overview/Overview.jsx
index 86bf97a..51c841d 100644
--- a/ipi-survey-platform/src/pages/Overview/Overview.jsx
+++ b/ipi-survey-platform/src/pages/Overview/Overview.jsx
@@ -1,5 +1,5 @@
// src/pages/Overview/Overview.jsx
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { getSubmissions } from '@/services/submissions/submissionService';
import HeaderBar from '@/components/layout/HeaderBar';
@@ -29,6 +29,8 @@ const Overview = () => {
const [submissions, setSubmissions] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
+ const [currentPage, setCurrentPage] = useState(1);
+ const pageSize = 10; // Number of items per page
const [pagination, setPagination] = useState({
page: 1,
limit: 100,
@@ -76,17 +78,27 @@ const Overview = () => {
});
}, [searchTerm, selectedStatus, submissions]);
- const rows = filteredRows.map((record) => [
+ // Calculate paginated data
+ const paginatedRows = useMemo(() => {
+ if (!filteredRows.length) return [];
+ const startIndex = (currentPage - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+ return filteredRows.slice(startIndex, endIndex);
+ }, [filteredRows, currentPage, pageSize]);
+
+ const rows = useMemo(() => {
+ return paginatedRows.map((record) => [
record.year,
record.quarter,
`${record.quarter} ${record.year}`,
10, // Always show 10 for Total Products
record.product_count || 0, // Show actual submitted products count
`-`, // Total cost not in API
- record.status,
- formatDate(record.created_at),
- 'View Details',
- ]);
+ record.status,
+ formatDate(record.created_at),
+ 'View Details',
+ ]);
+ }, [paginatedRows]);
const downloadCsv = React.useCallback(() => {
if (!filteredRows.length) return;
@@ -173,8 +185,15 @@ const Overview = () => {
);
const handleViewDetails = (rowIndex) => {
- setSelectedSubmission(filteredRows[rowIndex]);
+ // Calculate the actual index in the filtered array
+ const actualIndex = (currentPage - 1) * pageSize + rowIndex;
+ setSelectedSubmission(filteredRows[actualIndex]);
};
+
+ // Reset to first page when search or filter changes
+ useEffect(() => {
+ setCurrentPage(1);
+ }, [searchTerm, selectedStatus]);
if (loading) {
return (
@@ -239,6 +258,13 @@ const Overview = () => {
beforeHeader={toolbar}
separated
rowGapClass="border-spacing-y-2"
+ pagination={{
+ currentPage,
+ onPageChange: setCurrentPage,
+ pageSize,
+ totalItems: filteredRows.length,
+ pageSizeOptions: [10, 25, 50, 100]
+ }}
className="w-full min-w-[1200px] [&_td]:whitespace-nowrap [&_th]:whitespace-nowrap"
renderCell={(value, rowIndex, columnIndex) => {
if (columnIndex === 6) { // Status column
diff --git a/ipi-survey-platform/src/pages/PublicContactForm.jsx b/ipi-survey-platform/src/pages/PublicContactForm.jsx
index 606715e..9de4b98 100644
--- a/ipi-survey-platform/src/pages/PublicContactForm.jsx
+++ b/ipi-survey-platform/src/pages/PublicContactForm.jsx
@@ -74,8 +74,8 @@ const PublicContactForm = () => {
const validate = (data = formData) => {
const newErrors = {};
- if (!data.establishmentName.trim()) newErrors.establishmentName = "Establishment Name is required.";
- if (!data.email.trim()) newErrors.email = "Registered Email is required.";
+ if (!data.establishmentName.trim()) newErrors.establishmentName = "Required.";
+ if (!data.email.trim()) newErrors.email = "Required.";
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email))
newErrors.email = "Enter a valid email address.";
@@ -280,15 +280,15 @@ const PublicContactForm = () => {
- If you cannot read the code, click "Refresh" or type:{" "}
- {captcha}
+ Enter the characters in the image. Can't read it? Refresh for a new code.
+
{
+ try {
+ const response = await getRequest(admin);
+ return response.data || [];
+ } catch (error) {
+ console.error('Error fetching units:', error);
+ throw error;
+ }
+};
+
+export const createadminusers = async (admindata) => {
+ try {
+ const response = await postRequest(admin, admindata);
+ return response.data;
+ } catch (error) {
+ console.error('Error creating unit:', error);
+ throw error;
+ }
+};
+
+export const updateAdminUser = async (id, admindata) => {
+ try {
+ const response = await putRequest(`${admin}/${id}`, admindata);
+ return response.data;
+ } catch (error) {
+ console.error('Error updating unit:', error);
+ throw error;
+ }
+};
+export const getAdminUserById = async (id) => {
+ try {
+ const response = await getRequest(`${admin}/${id}`);
+ return response.data;
+ } catch (error) {
+ console.error('Error fetching admin user:', error);
+ throw error;
+ }
+};
+
+export const deleteUnit = async (id) => {
+ try {
+ const response = await deleteRequest(`${UNIT_MASTER_ENDPOINT}/${id}`);
+ return response.data;
+ } catch (error) {
+ console.error('Error deleting unit:', error);
+ throw error;
+ }
+};
\ No newline at end of file