bug fixed in overview screen
This commit is contained in:
parent
c257cf356c
commit
43c4d65a25
@ -17,6 +17,13 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
const [submissionData, setSubmissionData] = useState(null);
|
||||
const [quarterPeriods, setQuarterPeriods] = useState(null);
|
||||
const [loadingProduct, setLoadingProduct] = useState(false);
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const [pagination, setPagination] = useState({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
totalItems: 0,
|
||||
pageSizeOptions: [10, 20, 50, 100]
|
||||
});
|
||||
|
||||
// Add state for tracking loading and error states
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@ -71,6 +78,11 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Log is_active status for each product
|
||||
submissionData.products.forEach((product, index) => {
|
||||
console.log(`Product ${index + 1} is_active status:`, product?.is_active);
|
||||
});
|
||||
|
||||
const normalizedTerm = searchTerm.trim().toLowerCase();
|
||||
const filtered = submissionData.products.filter((product) => {
|
||||
// Add null checks for product and product.product
|
||||
@ -84,7 +96,7 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
!normalizedTerm ||
|
||||
productName.toString().toLowerCase().includes(normalizedTerm) ||
|
||||
hsCode.toString().toLowerCase().includes(normalizedTerm) ||
|
||||
status.toLowerCase().includes(ormalizedTerm) ||
|
||||
status.toLowerCase().includes(normalizedTerm) ||
|
||||
'pending'.includes(normalizedTerm);
|
||||
|
||||
const matchesStatus = selectedStatus === '' ||
|
||||
@ -94,6 +106,13 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
|
||||
// Update pagination total items and reset to first page when filters change
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
totalItems: filtered.length,
|
||||
currentPage: 1 // Reset to first page when filters change
|
||||
}));
|
||||
|
||||
console.log('Filtered products:', filtered);
|
||||
return filtered;
|
||||
}, [searchTerm, selectedStatus, submissionData]);
|
||||
@ -133,9 +152,9 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
product.current_cost || '0',
|
||||
product.forecast_quantity || '0',
|
||||
product.forecast_cost || '0',
|
||||
product.is_active ? 'Approved' : 'Pending'
|
||||
product.status || 'Pending'
|
||||
].map((value) => `"${String(value).replace(/"/g, '""')}"`).join(','));
|
||||
});
|
||||
});
|
||||
|
||||
const blob = new Blob([csvRows.join('\n')], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
@ -149,19 +168,45 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
}, [filteredProducts]);
|
||||
|
||||
const handleViewDetails = (product) => {
|
||||
// Use the product data we already have from the submission
|
||||
setSelectedProduct(product);
|
||||
setShowToast(true);
|
||||
};
|
||||
|
||||
const handleToastShown = () => {
|
||||
setShowToast(false);
|
||||
};
|
||||
|
||||
const handlePageChange = (newPage) => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
currentPage: newPage
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePageSizeChange = (newSize) => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
pageSize: newSize,
|
||||
currentPage: 1 // Reset to first page when changing page size
|
||||
}));
|
||||
};
|
||||
|
||||
// Apply pagination to filtered products
|
||||
const paginatedProducts = useMemo(() => {
|
||||
if (!filteredProducts || !Array.isArray(filteredProducts)) return [];
|
||||
const startIndex = (pagination.currentPage - 1) * pagination.pageSize;
|
||||
return filteredProducts.slice(startIndex, startIndex + pagination.pageSize);
|
||||
}, [filteredProducts, pagination.currentPage, pagination.pageSize]);
|
||||
|
||||
const rows = useMemo(() => {
|
||||
if (!filteredProducts || !Array.isArray(filteredProducts)) {
|
||||
console.log('No filtered products available');
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log('Generating rows for filtered products:', filteredProducts);
|
||||
console.log('Generating rows for filtered products:', paginatedProducts);
|
||||
|
||||
return filteredProducts.map((product, index) => {
|
||||
return paginatedProducts.map((product, index) => {
|
||||
// Add null checks for product and product properties
|
||||
const productData = product.product || {};
|
||||
const unit = product.unit || {};
|
||||
@ -179,7 +224,8 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
'text-[#7C5E24] bg-[#F9F7ED]'
|
||||
}`}
|
||||
>
|
||||
{product.is_active ? 'Approved' : 'Pending'}
|
||||
{product.is_active ? 'Submitted' : 'Pending'}
|
||||
{/* {product.is_active ? 'Rejected' : 'Rejected'} */}
|
||||
</span>,
|
||||
new Date(product.updated_at || product.created_at).toLocaleString('en-US', {
|
||||
year: 'numeric',
|
||||
@ -202,6 +248,16 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
});
|
||||
}, [filteredProducts]);
|
||||
|
||||
// Add pagination props to the Table component
|
||||
const tablePagination = {
|
||||
currentPage: pagination.currentPage,
|
||||
onPageChange: handlePageChange,
|
||||
pageSize: pagination.pageSize,
|
||||
totalItems: filteredProducts.length,
|
||||
pageSizeOptions: pagination.pageSizeOptions,
|
||||
onPageSizeChange: handlePageSizeChange
|
||||
};
|
||||
|
||||
// Show loading state for initial data load
|
||||
if (isLoading) {
|
||||
return (
|
||||
@ -311,9 +367,12 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
if (selectedProduct) {
|
||||
return (
|
||||
<ProductDetails
|
||||
product={selectedProduct}
|
||||
quarterPeriods={quarterPeriods}
|
||||
product={selectedProduct}
|
||||
onBack={() => setSelectedProduct(null)}
|
||||
showToast={showToast}
|
||||
onToastShown={handleToastShown}
|
||||
submissionDate={submissionData?.created_at || submissionData?.updated_at || new Date().toISOString()}
|
||||
// submissionStatus={'R'} // Static status for testing
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -385,6 +444,7 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
separated
|
||||
rowGapClass="border-spacing-y-2"
|
||||
className="w-full border-collapse [&_td]:whitespace-nowrap [&_th]:whitespace-nowrap [&_th]:px-3 [&_td]:px-3 [&_th:last-child]:text-right [&_td:last-child]:text-right [&_th:last-child]:pr-4 [&_td:last-child]:pr-4"
|
||||
pagination={tablePagination}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@ -1,32 +1,84 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
// Mock data for the quarterly details - replace with actual data from props
|
||||
const quarterlyData = {
|
||||
q4_oct_quantity: '1,000',
|
||||
q4_nov_quantity: '1,200',
|
||||
q4_dec_quantity: '1,500',
|
||||
q1_jan_quantity: '1,300',
|
||||
q1_feb_quantity: '1,400',
|
||||
q1_mar_quantity: '1,600',
|
||||
q2_apr_quantity: '1,700',
|
||||
q2_may_quantity: '1,800',
|
||||
q2_jun_quantity: '2,000',
|
||||
q4_oct_cost: '10,000',
|
||||
q4_nov_cost: '12,000',
|
||||
q4_dec_cost: '15,000',
|
||||
q1_jan_cost: '13,000',
|
||||
q1_feb_cost: '14,000',
|
||||
q1_mar_cost: '16,000',
|
||||
q2_apr_cost: '17,000',
|
||||
q2_may_cost: '18,000',
|
||||
q2_jun_cost: '20,000',
|
||||
variationReason: 'Seasonal demand increase',
|
||||
zeroTargetReason: 'Expected market expansion',
|
||||
remarks: 'Product shows steady growth with seasonal variations.'
|
||||
};
|
||||
|
||||
const ProductDetails = ({ product, onBack }) => {
|
||||
const ProductDetails = ({ product, onBack, showToast: shouldShowToast, onToastShown, submissionDate, submissionStatus = 'approved' }) => {
|
||||
const [toast, setToast] = useState(null);
|
||||
const toastShownRef = useRef(false);
|
||||
|
||||
// Format date to 'DD-MM-YYYY, hh:mm A' format
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString('en-GB', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldShowToast && !toastShownRef.current) {
|
||||
const formattedDate = formatDate(submissionDate);
|
||||
|
||||
if (product?.is_active) {
|
||||
const status = String(product.is_active).toLowerCase();
|
||||
console.log("statuschecking",status)
|
||||
const toastConfig = {
|
||||
approved: {
|
||||
type: 'approved',
|
||||
bgColor: 'bg-[#F3FAF4]',
|
||||
textColor: 'text-[#3F8E50]',
|
||||
iconColor: 'text-[#4A9D5C]',
|
||||
message: 'Your survey was successfully submitted and is waiting for approval.'
|
||||
},
|
||||
rejected: {
|
||||
type: 'rejected',
|
||||
bgColor: 'bg-red-50',
|
||||
textColor: 'text-red-700',
|
||||
iconColor: 'text-red-500',
|
||||
message: 'Your survey has been rejected. Please review and resubmit.'
|
||||
},
|
||||
submitted: {
|
||||
type: 'submitted',
|
||||
bgColor: 'bg-[#E7F5FF]',
|
||||
textColor: 'text-[#043DFF]',
|
||||
iconColor: 'text-[#286CFF]',
|
||||
message: 'Your survey was successfully submitted and is waiting for approval.'
|
||||
}
|
||||
};
|
||||
|
||||
const toastType = status in toastConfig ? status : 'submitted';
|
||||
setToast(toastConfig[toastType]);
|
||||
} else {
|
||||
// Default pending state (amber)
|
||||
setToast({
|
||||
type: 'pending',
|
||||
message: 'Your survey is in draft status. Please complete and submit it for review.',
|
||||
bgColor: 'bg-amber-50',
|
||||
textColor: 'text-amber-700',
|
||||
iconColor: 'text-amber-500'
|
||||
});
|
||||
}
|
||||
|
||||
toastShownRef.current = true;
|
||||
onToastShown?.();
|
||||
}
|
||||
}, [shouldShowToast, product?.is_active, onToastShown, submissionDate, submissionStatus]);
|
||||
|
||||
const closeToast = () => {
|
||||
setToast(null);
|
||||
};
|
||||
|
||||
// Debug log to check product status and is_active
|
||||
useEffect(() => {
|
||||
console.log('Product Status:', product?.status);
|
||||
console.log('Product is_active:', product?.is_active);
|
||||
}, [product?.status, product?.is_active]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!product) return null;
|
||||
@ -58,14 +110,76 @@ const ProductDetails = ({ product, onBack }) => {
|
||||
q2_jun_cost: product.forecast_cost_period_three || '—',
|
||||
variationReason: product.variation_reason?.reason || '—',
|
||||
zeroTargetReason: product.zero_target_reason?.reason || '—',
|
||||
remarks: product.remarks || 'No remarks provided for this product.'
|
||||
remarks: product.remarks || 'NA'
|
||||
});
|
||||
|
||||
const formattedProduct = formatProductData(product);
|
||||
|
||||
return (
|
||||
<div className="space-y-6 w-full">
|
||||
<div className="bg-white p-4 rounded-t-lg border-b border-[#E2E8F0]">
|
||||
<div className="w-full relative">
|
||||
{/* Toast Notification */}
|
||||
{toast && (
|
||||
<div className="flex justify-center mb-2">
|
||||
<div className={`w-full max-w-[1281px] h-[56px] ${toast.bgColor} ${toast.textColor} rounded-md px-4 py-3 flex items-center shadow-sm`}>
|
||||
{toast.type === 'approved' ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className={`w-5 h-5 mr-2 ${toast.iconColor}`}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
) : toast.type === 'submitted' ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className={`w-5 h-5 mr-2 ${toast.iconColor}`}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 01.67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 11-.67-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
) : toast.type === 'rejected' ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className={`w-5 h-5 mr-2 ${toast.iconColor}`}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className={`w-5 h-5 mr-2 ${toast.iconColor}`}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
<span className="text-sm font-medium">{toast.message}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-white p-4 rounded-t-lg border-b border-[#E2E8F0] mt-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<button
|
||||
@ -73,15 +187,15 @@ const ProductDetails = ({ product, onBack }) => {
|
||||
className="text-[#6B7280] hover:text-[#374151] p-1 -ml-1"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5 15L7.5 10L12.5 5" stroke="#6B7280" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M15.8332 10H4.1665M4.1665 10L9.99984 15.8333M4.1665 10L9.99984 4.16667" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<h1 className="text-lg font-medium text-[#232528]">Product Details - {formattedProduct.name}</h1>
|
||||
<h2 className="text-lg font-medium text-gray-900">Product Details</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg border border-[#E2E8F0] p-6">
|
||||
|
||||
<div className="bg-white rounded-lg shadow-sm border border-[#E2E8F0] p-6">
|
||||
{/* Basic Information */}
|
||||
<div className="mb-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-4">
|
||||
@ -114,7 +228,7 @@ const ProductDetails = ({ product, onBack }) => {
|
||||
<div className="mt-1">
|
||||
<span className={`inline-flex items-center rounded-md px-3 py-1.5 text-sm font-medium ${
|
||||
formattedProduct.status === 'Active'
|
||||
? 'bg-green-50 text-green-700 ring-1 ring-inset ring-green-600/20'
|
||||
? 'bg-[#F3FAF4] text-[#2F663C] ring-1 ring-inset ring-green-600/20'
|
||||
: 'bg-yellow-50 text-yellow-700 ring-1 ring-inset ring-yellow-600/20'
|
||||
}`}>
|
||||
{formattedProduct.status}
|
||||
@ -225,13 +339,13 @@ const ProductDetails = ({ product, onBack }) => {
|
||||
{/* Remarks */}
|
||||
<div className="mt-6">
|
||||
<h3 className="text-sm font-medium text-[#232528] mb-2">Remarks</h3>
|
||||
<div className="border border-[#F3B340] rounded-md p-3 text-sm bg-white">
|
||||
<div className="border border-[#CBA344] rounded-md p-3 text-sm bg-white">
|
||||
{formattedProduct.remarks}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submission Info */}
|
||||
<div className="mt-6 pt-4 border-t border-[#E2E8F0] text-sm">
|
||||
{/* <div className="mt-6 pt-4 border-t border-[#E2E8F0] text-sm">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-[#6B7280]">Submitted by: <span className="text-[#232528] font-medium">
|
||||
@ -254,7 +368,7 @@ const ProductDetails = ({ product, onBack }) => {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -926,37 +926,25 @@ const ProductData = ({
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{quarterPeriods?.previous_month?.previous_period_one } Qty
|
||||
</label>
|
||||
<Input
|
||||
className="w-24"
|
||||
type="number"
|
||||
placeholder="Enter"
|
||||
value={p.octQuantity || ''}
|
||||
onChange={(e) => updateProductField(p.id, 'octQuantity', e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
|
||||
{p.octQuantity || '0'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{quarterPeriods?.previous_month?.previous_period_two } Qty
|
||||
</label>
|
||||
<Input
|
||||
className="w-24"
|
||||
type="number"
|
||||
placeholder="Enter"
|
||||
value={p.novQuantity || ''}
|
||||
onChange={(e) => updateProductField(p.id, 'novQuantity', e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
|
||||
{p.novQuantity || '0'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{quarterPeriods?.previous_month?.previous_period_three} Qty
|
||||
</label>
|
||||
<Input
|
||||
className="w-24"
|
||||
type="number"
|
||||
placeholder="Enter"
|
||||
value={p.decQuantity || ''}
|
||||
onChange={(e) => updateProductField(p.id, 'decQuantity', e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
|
||||
{p.decQuantity || '0'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-end space-x-4 mt-4">
|
||||
@ -964,37 +952,25 @@ const ProductData = ({
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{quarterPeriods?.previous_month?.previous_period_one } Cost (AED)
|
||||
</label>
|
||||
<Input
|
||||
className="w-24"
|
||||
type="number"
|
||||
placeholder="Enter"
|
||||
value={p.octCost || ''}
|
||||
onChange={(e) => updateProductField(p.id, 'octCost', e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
|
||||
{p.octCost || '0'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{quarterPeriods?.previous_month?.previous_period_two } Cost (AED)
|
||||
</label>
|
||||
<Input
|
||||
className="w-24"
|
||||
type="number"
|
||||
placeholder="Enter"
|
||||
value={p.novCost || ''}
|
||||
onChange={(e) => updateProductField(p.id, 'novCost', e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
|
||||
{p.novCost || '0'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
{quarterPeriods?.previous_month?.previous_period_three } Cost (AED)
|
||||
</label>
|
||||
<Input
|
||||
className="w-24"
|
||||
placeholder="Enter"
|
||||
type="number"
|
||||
value={p.decCost || ''}
|
||||
onChange={(e) => updateProductField(p.id, 'decCost', e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
|
||||
{p.decCost || '0'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1119,6 +1095,14 @@ const ProductData = ({
|
||||
<p className="mt-1 text-xs text-red-600">{reasonsError}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Remarks</label>
|
||||
<Textarea
|
||||
placeholder="Enter any additional remarks"
|
||||
value={p.remarks || ''}
|
||||
onChange={(e) => updateProductField(p.id, 'remarks', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SectionBox>
|
||||
|
||||
|
||||
@ -399,6 +399,11 @@ const ReviewSubmit = ({
|
||||
<span className="font-medium">Variation Reason:</span> {product.variationReason}
|
||||
</p>
|
||||
)}
|
||||
{product.remarks && (
|
||||
<p className="text-xs text-gray-600">
|
||||
<span className="font-medium">Remarks:</span> {product.remarks}
|
||||
</p>
|
||||
)}
|
||||
{product.zeroTargetReason && product.zeroTargetReason !== '—' && (
|
||||
<p className="text-xs text-gray-600">
|
||||
<span className="font-medium">Zero Target Reason:</span> {product.zeroTargetReason}
|
||||
|
||||
@ -132,157 +132,6 @@ const createEmptyProfile = () => ({
|
||||
createdById: '',
|
||||
});
|
||||
|
||||
// const companyProfilesSeed = [
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'CASCADE MARINE FOODS LLC',
|
||||
// emirate: 'Sharjah',
|
||||
// isicCode: '2098',
|
||||
// establishmentId: 'MSCensus-23-04-001276',
|
||||
// products: '10',
|
||||
// totalEmployees: '300',
|
||||
// createdBy: 'Amend (Owner)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Omar',
|
||||
// contactName: 'CASCADE MARINE FOODS LLC',
|
||||
// contactEmirate: 'Sharjah',
|
||||
// corporateName: 'CASCADE MARINE FOODS LLC',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Khazan Meat Factory',
|
||||
// emirate: 'Sharjah',
|
||||
// isicCode: '1098',
|
||||
// establishmentId: 'MSCensus-23-05-002829',
|
||||
// products: '6',
|
||||
// totalEmployees: '100',
|
||||
// createdBy: 'Sara (Admin)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Abdul',
|
||||
// contactName: 'Khazan Meat Factory',
|
||||
// contactEmirate: 'Sharjah',
|
||||
// corporateName: 'Khazan Meat Factory',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Spinneys Fresh Food Industries L.L.C',
|
||||
// emirate: 'Dubai',
|
||||
// isicCode: '3198',
|
||||
// establishmentId: 'MSCensus-Dubai-1407',
|
||||
// products: '8',
|
||||
// totalEmployees: '40',
|
||||
// createdBy: 'Shaik (Admin)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Abdul',
|
||||
// contactName: 'Spinneys Fresh Food Industries L.L.C',
|
||||
// contactEmirate: 'Dubai',
|
||||
// corporateName: 'Spinneys Fresh Food Industries L.L.C',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Freshly Frozen Foods Factory L.L.C',
|
||||
// emirate: 'Dubai',
|
||||
// isicCode: '2098',
|
||||
// establishmentId: 'MSCensus-Dubai-906',
|
||||
// products: '4',
|
||||
// totalEmployees: '50',
|
||||
// createdBy: 'Abdul (Manager)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Abdul',
|
||||
// contactName: 'Freshly Frozen Foods Factory L.L.C',
|
||||
// contactEmirate: 'Dubai',
|
||||
// corporateName: 'Freshly Frozen Foods Factory L.L.C',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Sahar Food Industry (L.L.C)',
|
||||
// emirate: 'Dubai',
|
||||
// isicCode: '1095',
|
||||
// establishmentId: 'MSCensus-Dubai-472',
|
||||
// products: '8',
|
||||
// totalEmployees: '80',
|
||||
// createdBy: 'Amend (Owner)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Abdul',
|
||||
// contactName: 'Sahar Food Industry (L.L.C)',
|
||||
// contactEmirate: 'Dubai',
|
||||
// corporateName: 'Sahar Food Industry (L.L.C)',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Continental Food Processing LLC',
|
||||
// emirate: 'Ajman',
|
||||
// isicCode: '0035',
|
||||
// establishmentId: 'MSCensus-23-06-005496',
|
||||
// products: '5',
|
||||
// totalEmployees: '400',
|
||||
// createdBy: 'Sara (Admin)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Abdul',
|
||||
// contactName: 'Continental Food Processing LLC',
|
||||
// contactEmirate: 'Ajman',
|
||||
// corporateName: 'Continental Food Processing LLC',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Al Khazna Poultry Farm',
|
||||
// emirate: 'Abu Dhabi',
|
||||
// isicCode: '0986',
|
||||
// establishmentId: 'MSCensus-23-05-002320',
|
||||
// products: '7',
|
||||
// totalEmployees: '800',
|
||||
// createdBy: 'Sara (Admin)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Fathima',
|
||||
// contactName: 'Al Khazna Poultry Farm',
|
||||
// contactEmirate: 'Abu Dhabi',
|
||||
// corporateName: 'Al Khazna Poultry Farm',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Diamond Meat Processing L.L.C',
|
||||
// emirate: 'Abu Dhabi',
|
||||
// isicCode: '8766',
|
||||
// establishmentId: 'MSCensus-23-05-002899',
|
||||
// products: '7',
|
||||
// totalEmployees: '300',
|
||||
// createdBy: 'Sara (Admin)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Abdul',
|
||||
// contactName: 'Diamond Meat Processing L.L.C',
|
||||
// contactEmirate: 'Abu Dhabi',
|
||||
// corporateName: 'Diamond Meat Processing L.L.C',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Alliance Foods Co L.L.C',
|
||||
// emirate: 'Abu Dhabi',
|
||||
// isicCode: '9883',
|
||||
// establishmentId: 'MSCensus-Dubai-128',
|
||||
// products: '7',
|
||||
// totalEmployees: '200',
|
||||
// createdBy: 'Amend (Owner)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Abdul',
|
||||
// contactName: 'Alliance Foods Co L.L.C',
|
||||
// contactEmirate: 'Abu Dhabi',
|
||||
// corporateName: 'Alliance Foods Co L.L.C',
|
||||
// },
|
||||
// {
|
||||
// ...createEmptyProfile(),
|
||||
// establishmentName: 'Krustasia Foods L.L.C',
|
||||
// emirate: 'Dubai',
|
||||
// isicCode: '9986',
|
||||
// establishmentId: 'MSCensus-23-05-002320',
|
||||
// products: '8',
|
||||
// totalEmployees: '500',
|
||||
// createdBy: 'Sara (Admin)',
|
||||
// createdOn: '18/10/2025',
|
||||
// lastUpdated: 'Fathima',
|
||||
// contactName: 'Krustasia Foods L.L.C',
|
||||
// contactEmirate: 'Dubai',
|
||||
// },
|
||||
// ];
|
||||
|
||||
const CompanyProfile = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -5,6 +5,7 @@ import HeaderBar from '@/components/layout/HeaderBar';
|
||||
import { fetchSubmissionHistory } from '@/services/submissions/submissionService.js';
|
||||
|
||||
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
||||
const BreadcrumbVector = '/assets/images/Breadcrumb-vector.svg';
|
||||
const caretDownSrc = '/assets/images/CaretDown.svg';
|
||||
|
||||
const Badge = ({ variant = 'default', children }) => {
|
||||
@ -28,6 +29,13 @@ const History = () => {
|
||||
const [error, setError] = React.useState('');
|
||||
const [searchTerm, setSearchTerm] = React.useState('');
|
||||
const [statusFilter, setStatusFilter] = React.useState('all');
|
||||
const [yearFilter, setYearFilter] = React.useState('');
|
||||
const [quarterFilter, setQuarterFilter] = React.useState('');
|
||||
const [pagination, setPagination] = React.useState({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
totalItems: 0,
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
const toVariant = React.useCallback((status) => {
|
||||
@ -126,28 +134,68 @@ const History = () => {
|
||||
navigate('/survey', { state: { submission, initialStep: 3 } });
|
||||
}, [navigate]);
|
||||
|
||||
// Get unique years from the data
|
||||
const yearOptions = React.useMemo(() => {
|
||||
const years = [...new Set(rows.map(entry => entry.year).filter(Boolean))].sort((a, b) => b - a);
|
||||
return years;
|
||||
}, [rows]);
|
||||
|
||||
const quarterOptions = [
|
||||
{ label: 'Quarter', value: '' },
|
||||
{ label: 'Q1', value: 'Q1' },
|
||||
{ label: 'Q2', value: 'Q2' },
|
||||
{ label: 'Q3', value: 'Q3' },
|
||||
{ label: 'Q4', value: 'Q4' }
|
||||
];
|
||||
|
||||
const filteredRows = React.useMemo(() => {
|
||||
const term = searchTerm.trim().toLowerCase();
|
||||
const statusValue = statusFilter.toLowerCase();
|
||||
return rows.filter((entry) => {
|
||||
|
||||
const filtered = rows.filter((entry) => {
|
||||
// Status filter
|
||||
const matchesStatus =
|
||||
statusValue === 'all' || toVariant(entry.status) === statusValue || formatStatus(entry.status).toLowerCase() === statusValue;
|
||||
if (!term) return matchesStatus;
|
||||
const haystack = [
|
||||
entry.year,
|
||||
entry.quarter,
|
||||
entry.submission_window,
|
||||
entry.total_products,
|
||||
entry.products_submitted,
|
||||
entry.total_cost,
|
||||
formatStatus(entry.status),
|
||||
formatDate(entry.created_at),
|
||||
]
|
||||
.map((val) => String(val || '').toLowerCase())
|
||||
.join(' ');
|
||||
return matchesStatus && haystack.includes(term);
|
||||
statusValue === 'all' ||
|
||||
toVariant(entry.status) === statusValue ||
|
||||
formatStatus(entry.status).toLowerCase() === statusValue;
|
||||
|
||||
// Year filter
|
||||
const matchesYear = !yearFilter || String(entry.year) === yearFilter;
|
||||
|
||||
// Quarter filter
|
||||
const matchesQuarter = !quarterFilter ||
|
||||
(entry.quarter && entry.quarter.toUpperCase() === quarterFilter.toUpperCase());
|
||||
|
||||
// Search term filter
|
||||
if (term) {
|
||||
const haystack = [
|
||||
entry.year,
|
||||
entry.quarter,
|
||||
entry.submission_window,
|
||||
entry.total_products,
|
||||
entry.products_submitted,
|
||||
entry.total_cost,
|
||||
formatStatus(entry.status),
|
||||
formatDate(entry.created_at),
|
||||
]
|
||||
.map((val) => String(val || '').toLowerCase())
|
||||
.join(' ');
|
||||
|
||||
return matchesStatus && matchesYear && matchesQuarter && haystack.includes(term);
|
||||
}
|
||||
|
||||
return matchesStatus && matchesYear && matchesQuarter;
|
||||
});
|
||||
}, [rows, searchTerm, statusFilter, toVariant, formatStatus, formatDate]);
|
||||
|
||||
// Update total items when filtered rows change
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
totalItems: filtered.length,
|
||||
currentPage: 1, // Reset to first page when filter changes
|
||||
}));
|
||||
|
||||
return filtered;
|
||||
}, [rows, searchTerm, statusFilter, yearFilter, quarterFilter, toVariant, formatStatus, formatDate]);
|
||||
|
||||
const formatNumber = (num) => {
|
||||
if (num === null || num === undefined) return '—';
|
||||
@ -161,6 +209,21 @@ const History = () => {
|
||||
};
|
||||
|
||||
// 🟩 Updated tableRows with expandable details
|
||||
const handlePageChange = (newPage) => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
currentPage: newPage,
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePageSizeChange = (newSize) => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
pageSize: newSize,
|
||||
currentPage: 1, // Reset to first page when page size changes
|
||||
}));
|
||||
};
|
||||
|
||||
const tableRows = filteredRows.map((entry, index) => {
|
||||
const isExpanded = expandedRow === index;
|
||||
return [
|
||||
@ -302,9 +365,22 @@ const History = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-[#F7F7F7]">
|
||||
<HeaderBar />
|
||||
<div className="mx-auto max-w-7xl px-6 py-10">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-2 mt-3">
|
||||
<nav className="flex items-center text-sm text-gray-500 space-x-2">
|
||||
<a href="/" className="hover:text-[#92722A] transition-colors flex items-center gap-1">
|
||||
Dashboard
|
||||
<img src={BreadcrumbVector} alt="" className="h-3 w-3 ml-3" />
|
||||
</a>
|
||||
<span className="hover:text-[#92722A] transition-colors flex items-center ml-2">
|
||||
History
|
||||
<img src={BreadcrumbVector} alt="" className="h-3 w-3 ml-3" />
|
||||
</span>
|
||||
<span className="text-[#92722A] ml-2">Submission History</span>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="mx-auto max-w-7xl px-6 py-2 ">
|
||||
{/* Card Section */}
|
||||
<div className="bg-white p-4 rounded-t-lg border-b border-[#E2E8F0] mb-6">
|
||||
<div className="bg-white p-4 rounded-t-lg border-b border-[#E2E8F0] mb-6 mt-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-2 pr-6">
|
||||
@ -319,13 +395,14 @@ const History = () => {
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="relative">
|
||||
<select
|
||||
value={yearFilter}
|
||||
onChange={(e) => setYearFilter(e.target.value)}
|
||||
className="h-10 w-32 rounded-md border border-[#E2E8F0] bg-white pl-3 pr-8 text-sm text-[#232528] focus:outline-none cursor-pointer appearance-none"
|
||||
>
|
||||
<option value="">Select Year</option>
|
||||
<option value="2024">2024</option>
|
||||
<option value="2023">2023</option>
|
||||
<option value="2022">2022</option>
|
||||
<option value="2021">2021</option>
|
||||
{yearOptions.map(year => (
|
||||
<option key={year} value={year}>{year}</option>
|
||||
))}
|
||||
</select>
|
||||
<svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#6B7280]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
@ -333,13 +410,15 @@ const History = () => {
|
||||
</div>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={quarterFilter}
|
||||
onChange={(e) => setQuarterFilter(e.target.value)}
|
||||
className="h-10 w-32 rounded-md border border-[#E2E8F0] bg-white pl-3 pr-8 text-sm text-[#232528] focus:outline-none cursor-pointer appearance-none"
|
||||
>
|
||||
<option value="">Select Quarter</option>
|
||||
<option value="Q1">Q1</option>
|
||||
<option value="Q2">Q2</option>
|
||||
<option value="Q3">Q3</option>
|
||||
<option value="Q4">Q4</option>
|
||||
{quarterOptions.map(option => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#6B7280]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
@ -360,10 +439,9 @@ const History = () => {
|
||||
<div className="relative">
|
||||
<select
|
||||
className="h-10 w-32 rounded-md border border-[#E2E8F0] bg-white pl-3 pr-8 text-sm text-[#232528] focus:outline-none cursor-pointer appearance-none"
|
||||
disabled
|
||||
>
|
||||
<option value="">Select Product</option>
|
||||
<option value="product1">Product 1</option>
|
||||
<option value="product2">Product 2</option>
|
||||
</select>
|
||||
<svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#6B7280]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
@ -372,10 +450,9 @@ const History = () => {
|
||||
<div className="relative">
|
||||
<select
|
||||
className="h-10 w-32 rounded-md border border-[#E2E8F0] bg-white pl-3 pr-8 text-sm text-[#232528] focus:outline-none cursor-pointer appearance-none"
|
||||
disabled
|
||||
>
|
||||
<option value="">Select Actor</option>
|
||||
<option value="actor1">Actor 1</option>
|
||||
<option value="actor2">Actor 2</option>
|
||||
</select>
|
||||
<svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#6B7280]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
@ -398,11 +475,21 @@ const History = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white w-full rounded-lg shadow-sm ring-1 ring-gray-200">
|
||||
<div className="bg-white w-full rounded-lg shadow-sm ring-1 ring-gray-200 mt-1">
|
||||
<Table
|
||||
headers={headers}
|
||||
rows={tableRows}
|
||||
beforeHeader={toolbar}
|
||||
loading={loading}
|
||||
error={error}
|
||||
className="mt-4"
|
||||
pagination={{
|
||||
currentPage: pagination.currentPage,
|
||||
onPageChange: handlePageChange,
|
||||
pageSize: pagination.pageSize,
|
||||
totalItems: pagination.totalItems,
|
||||
pageSizeOptions: [10, 25, 50, 100],
|
||||
onPageSizeChange: handlePageSizeChange,
|
||||
}}
|
||||
renderCell={(value, ri, ci) => {
|
||||
if (ci === 3) {
|
||||
return <Badge variant={toVariant(filteredRows[ri]?.status)}>{value}</Badge>;
|
||||
|
||||
@ -29,13 +29,11 @@ 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,
|
||||
total: 0,
|
||||
totalPages: 1
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
totalItems: 0,
|
||||
pageSizeOptions: [10, 20, 50, 100]
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -43,9 +41,8 @@ const Overview = () => {
|
||||
const fetchSubmissions = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await getSubmissions(pagination.page, pagination.limit);
|
||||
const response = await getSubmissions(1, 1000); // Get all records
|
||||
setSubmissions(response.data);
|
||||
setPagination(response.pagination);
|
||||
} catch (err) {
|
||||
setError('Failed to load submissions');
|
||||
console.error('Error:', err);
|
||||
@ -55,11 +52,11 @@ const Overview = () => {
|
||||
};
|
||||
|
||||
fetchSubmissions();
|
||||
}, [pagination.page, pagination.limit]);
|
||||
}, []);
|
||||
|
||||
const filteredRows = React.useMemo(() => {
|
||||
const normalizedTerm = searchTerm.trim().toLowerCase();
|
||||
return submissions.filter((record) => {
|
||||
const filtered = submissions.filter((record) => {
|
||||
const matchesSearch =
|
||||
!normalizedTerm ||
|
||||
String(record.year).toLowerCase().includes(normalizedTerm) ||
|
||||
@ -76,29 +73,45 @@ const Overview = () => {
|
||||
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
|
||||
// Update total items in pagination
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
totalItems: filtered.length,
|
||||
currentPage: 1 // Reset to first page when filters change
|
||||
}));
|
||||
|
||||
return filtered;
|
||||
}, [searchTerm, selectedStatus, submissions]);
|
||||
|
||||
// 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
|
||||
return filteredRows.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',
|
||||
]);
|
||||
}, [paginatedRows]);
|
||||
}, [filteredRows]);
|
||||
|
||||
const handlePageChange = (newPage) => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
currentPage: newPage
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePageSizeChange = (newSize) => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
pageSize: newSize,
|
||||
currentPage: 1 // Reset to first page when changing page size
|
||||
}));
|
||||
};
|
||||
|
||||
const downloadCsv = React.useCallback(() => {
|
||||
if (!filteredRows.length) return;
|
||||
@ -185,15 +198,8 @@ const Overview = () => {
|
||||
);
|
||||
|
||||
const handleViewDetails = (rowIndex) => {
|
||||
// Calculate the actual index in the filtered array
|
||||
const actualIndex = (currentPage - 1) * pageSize + rowIndex;
|
||||
setSelectedSubmission(filteredRows[actualIndex]);
|
||||
setSelectedSubmission(filteredRows[rowIndex]);
|
||||
};
|
||||
|
||||
// Reset to first page when search or filter changes
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [searchTerm, selectedStatus]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@ -258,13 +264,6 @@ 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
|
||||
@ -278,8 +277,8 @@ const Overview = () => {
|
||||
}
|
||||
if (columnIndex === 8) { // Action column
|
||||
return (
|
||||
<button
|
||||
className="text-[#92722A] hover:underline text-sm font-medium"
|
||||
<button
|
||||
className="text-[#92722A] hover:underline text-sm font-medium focus:outline-none"
|
||||
onClick={() => handleViewDetails(rowIndex)}
|
||||
>
|
||||
{value}
|
||||
@ -288,6 +287,14 @@ const Overview = () => {
|
||||
}
|
||||
return value;
|
||||
}}
|
||||
pagination={{
|
||||
currentPage: pagination.currentPage,
|
||||
onPageChange: handlePageChange,
|
||||
pageSize: pagination.pageSize,
|
||||
totalItems: pagination.totalItems,
|
||||
pageSizeOptions: pagination.pageSizeOptions,
|
||||
onPageSizeChange: handlePageSizeChange,
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user