bug fixed in admin dashboard
This commit is contained in:
parent
d0d8bd13f3
commit
9986f7c16b
@ -84,10 +84,16 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
Rejected
|
||||
</span>
|
||||
);
|
||||
} else if (normalized === 'pending') {
|
||||
} else if (normalized === 'submitted') {
|
||||
return (
|
||||
<span className="px-3 py-1 rounded-md text-blue-600 bg-blue-50 font-medium text-sm whitespace-nowrap">
|
||||
Pending
|
||||
Submitted
|
||||
</span>
|
||||
);
|
||||
} else if (normalized === 'resubmitted') {
|
||||
return (
|
||||
<span className="px-3 py-1 rounded-md text-[#7C5E24] bg-[#F9F7ED] font-medium text-sm whitespace-nowrap">
|
||||
Resubmitted
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
@ -133,12 +139,14 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="border border-[#E5E7EB] bg-white shadow-[0_16px_32px_rgba(15,23,42,0.06)] rounded-[8px]"
|
||||
style={{
|
||||
width: '1250px',
|
||||
minHeight: filtered.length === 0 || loading ? 'auto' : '780px',
|
||||
}}
|
||||
>
|
||||
className="border border-[#E5E7EB] bg-white shadow-[0_8px_16px_rgba(15,23,42,0.05)] rounded-[6px] w-full overflow-hidden py-2"
|
||||
style={{
|
||||
minHeight: filtered.length === 0 || loading ? 'auto' : '200px',
|
||||
}}
|
||||
>
|
||||
<h2 className="px-4 pr-100 py-2 mt-3 text-[18px] leading-[28px] font-medium text-[#232528]">
|
||||
{titleText}
|
||||
</h2>
|
||||
<div className="space-y-[1px] overflow-x-auto">
|
||||
{/* <div className="px-6 pt-6 pb-4">
|
||||
<h2 className="text-[18px] leading-[28px] font-medium text-[#232528]">
|
||||
@ -155,7 +163,7 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
No Submission Found
|
||||
</div>
|
||||
) : (
|
||||
<div className="min-w-[1200px]">
|
||||
<div className="w-full overflow-x-auto pt-5 h-[550px]">
|
||||
<Table
|
||||
headers={headers}
|
||||
rows={tableRows}
|
||||
@ -235,7 +243,7 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/admin/validations')}
|
||||
className="inline-flex h-[44px] w-[190px] items-center justify-center gap-2 rounded-md bg-[#9E792B] text-sm font-medium text-white shadow-[0_8px_18px_rgba(146,114,42,0.35)] hover:bg-[#846522]"
|
||||
className="inline-flex h-[44px] w-[190px] items-center justify-center gap-2 rounded-md bg-[#9E792B] text-sm font-medium text-white shadow-[0_8px_18px_rgba(146,114,42,0.35)] hover:bg-[#846522] py-6"
|
||||
>
|
||||
View All Submissions
|
||||
</button>
|
||||
|
||||
@ -3,12 +3,14 @@ import React from 'react';
|
||||
const toneClasses = {
|
||||
green: 'bg-[#F3FAF4] text-[#2F663C] ring-1 ring-[#C4E5CC]',
|
||||
gray: 'bg-[#E9EDF3] text-[#232528] ring-1 ring-[#D6DBE4]',
|
||||
resubmitted: 'bg-[#F9F7ED] text-[#8B8000] ring-1 ring-[#E5DFC5]',
|
||||
};
|
||||
|
||||
const normalizeTone = (tone, status) => {
|
||||
if (tone) return tone;
|
||||
const normalizedStatus = String(status || '').toLowerCase();
|
||||
if (normalizedStatus === 'active' || normalizedStatus === 'open') return 'green';
|
||||
if (normalizedStatus === 'resubmitted') return 'resubmitted';
|
||||
return 'gray';
|
||||
};
|
||||
|
||||
|
||||
@ -30,16 +30,28 @@ const getStatusBadge = (status) => {
|
||||
Approved
|
||||
</span>
|
||||
);
|
||||
} else if (normalized === 'submitted') {
|
||||
return (
|
||||
<span className="px-3 py-1 rounded-md text-green-700 bg-green-50 font-medium text-sm whitespace-nowrap">
|
||||
Submitted
|
||||
</span>
|
||||
);
|
||||
} else if (normalized === 'resubmitted') {
|
||||
return (
|
||||
<span className="px-3 py-1 rounded-md text-[#7C5E24] bg-[#F9F7ED] font-medium text-sm whitespace-nowrap">
|
||||
Resubmitted
|
||||
</span>
|
||||
);
|
||||
} else if (['pending', 'under review', 'in-progress'].includes(normalized)) {
|
||||
return (
|
||||
<span className="px-3 py-1 rounded-md text-blue-600 bg-blue-50 font-medium text-sm whitespace-nowrap">
|
||||
Under Review
|
||||
</span>
|
||||
);
|
||||
} else if (['rejected', 'returned'].includes(normalized)) {
|
||||
} else if (normalized === 'rejected') {
|
||||
return (
|
||||
<span className="px-3 py-1 rounded-md text-red-600 bg-red-50 font-medium text-sm whitespace-nowrap">
|
||||
Returned
|
||||
Rejected
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
@ -63,7 +75,7 @@ const downloadCsv = (data) => {
|
||||
const normalized = String(item.status || '').toLowerCase();
|
||||
if (normalized === 'approved') return 'Approved';
|
||||
if (['pending', 'under review', 'in-progress'].includes(normalized)) return 'Under Review';
|
||||
if (['rejected', 'returned'].includes(normalized)) return 'Returned';
|
||||
if (['rejected', 'rejected'].includes(normalized)) return 'Returned';
|
||||
return item.status || '—';
|
||||
})();
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ const SurveyCarousel = ({
|
||||
setCurrentIndex((prev) => (prev === surveys.length - 1 ? 0 : prev + 1));
|
||||
|
||||
return (
|
||||
<div className="bg-black rounded-lg shadow-sm ring-1 ring-gray-200 mb-8 ml-2 ">
|
||||
<div className="bg-black rounded-lg shadow-sm ring-1 w-[1275px] ring-gray-200 h-[148px] mb-8 ml-2 ">
|
||||
<div className="rounded-none bg-[#F2ECCF] px-8 py-8 min-h-[148px] flex items-center justify-between transition-all duration-500">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-[18px] leading-[28px] font-medium text-[#232528]">
|
||||
|
||||
@ -44,7 +44,8 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
'Approved': 'bg-[#F3FAF4] text-[#2F663C]',
|
||||
'Submitted': 'bg-[#E7F5FF] text-[#003CFF]',
|
||||
'Pending': 'bg-[#F9F7ED] text-[#7C5E24]',
|
||||
'Rejected': 'bg-[#FEF2F2] text-[#B52520]'
|
||||
'Rejected': 'bg-[#FEF2F2] text-[#B52520]',
|
||||
'Resubmitted': 'bg-[#F9F7ED] text-[#7C5E24]'
|
||||
};
|
||||
|
||||
const statusClass = statusMap[status] || 'bg-gray-100 text-gray-800';
|
||||
@ -172,7 +173,7 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
product.current_cost || '0',
|
||||
product.forecast_quantity || '0',
|
||||
product.forecast_cost || '0',
|
||||
product.status || 'Pending'
|
||||
product.status || 'Submitted'
|
||||
].map((value) => `"${String(value).replace(/"/g, '""')}"`).join(','));
|
||||
});
|
||||
|
||||
@ -231,8 +232,8 @@ export const DetailedOverview = ({ submission, onBack }) => {
|
||||
productData.hs_code || '—',
|
||||
productData.product_name || '—',
|
||||
unit.uom || 'N/A',
|
||||
product.previous_quantity || '0',
|
||||
`AED ${product.previous_cost || '0'}`,
|
||||
product.current_quantity || '0',
|
||||
`AED ${product.current_cost || '0'}`,
|
||||
// <span
|
||||
// key={`status-${index}`}
|
||||
// className={`inline-flex items-center justify-center rounded-md px-3 py-1 text-xs font-medium ${
|
||||
|
||||
@ -50,6 +50,13 @@ const ProductDetails = ({ product, onBack, showToast: shouldShowToast, onToastSh
|
||||
textColor: 'text-[#043DFF]',
|
||||
iconColor: 'text-[#286CFF]',
|
||||
message: 'Your survey was successfully submitted and is waiting for approval.'
|
||||
},
|
||||
resubmitted: {
|
||||
type: 'resubmitted',
|
||||
bgColor: 'bg-[#E7F5FF]',
|
||||
textColor: 'text-[#043DFF]',
|
||||
iconColor: 'text-[#286CFF]',
|
||||
message: 'Your survey was successfully Resubmitted and is waiting for approval.'
|
||||
}
|
||||
};
|
||||
|
||||
@ -182,9 +189,11 @@ const ProductDetails = ({ product, onBack, showToast: shouldShowToast, onToastSh
|
||||
industry_code: product.industry_code,
|
||||
license_number: product.license_number,
|
||||
isic_code: product.isic_code,
|
||||
emirate: product.emirate
|
||||
emirate: product.emirate,
|
||||
status: product.status // Include status for reference
|
||||
},
|
||||
isResubmit: true
|
||||
isResubmit: true,
|
||||
submissionId: product.submission_id // Add submissionId for resubmission
|
||||
}
|
||||
});
|
||||
}}
|
||||
|
||||
@ -252,6 +252,7 @@ const ReviewSubmit = ({
|
||||
onToastClose = () => {},
|
||||
isSubmitting = false,
|
||||
hasSubmitted = false,
|
||||
submitButtonText = 'Submit',
|
||||
}) => {
|
||||
const [confirm, setConfirm] = React.useState(false);
|
||||
const [remarks, setRemarks] = React.useState(() => {
|
||||
@ -734,11 +735,10 @@ const ReviewSubmit = ({
|
||||
<h3 className="text-base font-semibold text-gray-900 mb-4">Remarks</h3>
|
||||
<div className="space-y-2">
|
||||
<textarea
|
||||
placeholder="Enter your remarks here..."
|
||||
readOnly
|
||||
value={remarks}
|
||||
onChange={(e) => setRemarks(e.target.value)}
|
||||
className="w-full min-h-[100px] p-3 border border-#CBA344 rounded-md focus:ring-2 focus:ring-[#CBA344] focus:border-transparent"
|
||||
value={remarks || ''}
|
||||
placeholder="No remarks provided"
|
||||
className="w-full min-h-[100px] p-3 border border-gray-200 rounded-md bg-gray-50 text-gray-700 resize-none focus:outline-none focus:ring-0 focus:border-gray-200"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -792,7 +792,7 @@ const ReviewSubmit = ({
|
||||
: 'bg-[#92722A] text-white hover:bg-[#7b5c1f]'
|
||||
}`}
|
||||
>
|
||||
<span>{submitLabel}</span>
|
||||
<span>{submitButtonText}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -33,6 +33,7 @@ const ValidationReview = () => {
|
||||
const [actionLoading, setActionLoading] = React.useState(false);
|
||||
const [error, setError] = React.useState(null);
|
||||
const [submissionData, setSubmissionData] = React.useState(null);
|
||||
const [quarterPeriods, setQuarterPeriods] = React.useState(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchSubmission = async () => {
|
||||
@ -49,10 +50,16 @@ const ValidationReview = () => {
|
||||
const result = await getSubmissionById(id);
|
||||
|
||||
let data = null;
|
||||
if (result?.status === 'success' && result?.data) {
|
||||
if (result?.status === 'success') {
|
||||
data = result.data;
|
||||
if (result.quarter_periods) {
|
||||
setQuarterPeriods(result.quarter_periods);
|
||||
}
|
||||
} else if (result?.data) {
|
||||
data = result.data;
|
||||
if (result.quarter_periods) {
|
||||
setQuarterPeriods(result.quarter_periods);
|
||||
}
|
||||
} else {
|
||||
data = result;
|
||||
}
|
||||
@ -239,10 +246,9 @@ const ValidationReview = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const quarterPeriods = submissionData?.quarter_periods || {};
|
||||
const previousQuarter = quarterPeriods.previous_quarter || 'Q1';
|
||||
const currentQuarter = quarterPeriods.current_quarter || 'Q2';
|
||||
const forecastQuarter = quarterPeriods.forecast_quarter || 'Q3';
|
||||
const previousQuarter = quarterPeriods?.previous_quarter;
|
||||
const currentQuarter = quarterPeriods?.current_quarter;
|
||||
const forecastQuarter = quarterPeriods?.forecast_quarter;
|
||||
const year = submissionData?.year || new Date().getFullYear();
|
||||
|
||||
const products = submissionData?.products || [];
|
||||
@ -380,13 +386,7 @@ const ValidationReview = () => {
|
||||
</section>
|
||||
|
||||
{productDetails.length > 0 ? (
|
||||
<div
|
||||
className={`grid gap-4 ${
|
||||
productDetails.length === 1
|
||||
? 'grid-cols-1'
|
||||
: 'grid-cols-1 lg:grid-cols-2'
|
||||
}`}
|
||||
>
|
||||
<div className={`grid gap-4 ${productDetails.length === 1 ? 'grid-cols-1' : 'grid-cols-1 lg:grid-cols-2'}`}>
|
||||
{productDetails.map((product, idx) => (
|
||||
<section
|
||||
key={product.code + idx}
|
||||
@ -420,113 +420,180 @@ const ValidationReview = () => {
|
||||
Metric
|
||||
</th>
|
||||
<th colSpan="3" className="bg-[#F2ECCF] text-[#92722A] px-4 py-2 text-xs font-semibold uppercase text-center">
|
||||
Q4-2024 (Previous Quarter)
|
||||
{quarterPeriods?.previous_quarter}-{quarterPeriods?.previous_year} (Previous Quarter)
|
||||
</th>
|
||||
<th colSpan="3" className="bg-[#F2ECCF] text-[#92722A] px-4 py-2 text-xs font-semibold uppercase text-center">
|
||||
Q1-2025 (Current Quarter)
|
||||
{quarterPeriods?.current_quarter}-{quarterPeriods?.current_year} (Current Quarter)
|
||||
</th>
|
||||
<th colSpan="3" className="bg-[#F2ECCF] text-[#92722A] px-4 py-2 text-xs font-semibold uppercase text-center">
|
||||
Q2-2025 (Next Quarter)
|
||||
{quarterPeriods?.forecast_quarter}-{quarterPeriods?.forecast_year} (Next Quarter)
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th className="bg-[#F4F8FF] px-4 py-2 font-medium text-[#286CFF]">Oct</th>
|
||||
<th className="bg-[#F4F8FF] px-4 py-2 font-medium text-[#286CFF]">Nov</th>
|
||||
<th className="bg-[#F4F8FF] px-4 py-2 font-medium text-[#286CFF]">Dec</th>
|
||||
<th className="bg-[#F4F8FF] px-4 py-2 font-medium text-[#286CFF]">{quarterPeriods?.current_month?.current_period_one}</th>
|
||||
<th className="bg-[#F4F8FF] px-4 py-2 font-medium text-[#286CFF]">{quarterPeriods?.current_month?.current_period_two}</th>
|
||||
<th className="bg-[#F4F8FF] px-4 py-2 font-medium text-[#286CFF]">{quarterPeriods?.current_month?.current_period_three}</th>
|
||||
|
||||
<th className="bg-[#F3FAF4] px-4 py-2 font-medium text-[#2F663C]">Jan</th>
|
||||
<th className="bg-[#F3FAF4] px-4 py-2 font-medium text-[#2F663C]">Feb</th>
|
||||
<th className="bg-[#F3FAF4] px-4 py-2 font-medium text-[#2F663C]">Mar</th>
|
||||
<th className="bg-[#F3FAF4] px-4 py-2 font-medium text-[#2F663C]">{quarterPeriods?.forecast_month?.forecast_period_one}</th>
|
||||
<th className="bg-[#F3FAF4] px-4 py-2 font-medium text-[#2F663C]">{quarterPeriods?.forecast_month?.forecast_period_two}</th>
|
||||
<th className="bg-[#F3FAF4] px-4 py-2 font-medium text-[#2F663C]">{quarterPeriods?.forecast_month?.forecast_period_three}</th>
|
||||
|
||||
<th className="bg-[#FFF6EC] px-4 py-2 font-medium text-[#F29F0E]">Apr</th>
|
||||
<th className="bg-[#FFF6EC] px-4 py-2 font-medium text-[#F29F0E]">May</th>
|
||||
<th className="bg-[#FFF6EC] px-4 py-2 font-medium text-[#F29F0E]">Jun</th>
|
||||
<th className="bg-[#FFF6EC] px-4 py-2 font-medium text-[#F29F0E]">{quarterPeriods?.previous_month?.previous_period_one}</th>
|
||||
<th className="bg-[#FFF6EC] px-4 py-2 font-medium text-[#F29F0E]">{quarterPeriods?.previous_month?.previous_period_two}</th>
|
||||
<th className="bg-[#FFF6EC] px-4 py-2 font-medium text-[#F29F0E]">{quarterPeriods?.previous_month?.previous_period_three}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Quantity (liters)
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">1200</td>
|
||||
<td className="px-4 py-3 text-red-600">1180 ▼</td>
|
||||
<td className="px-4 py-3 text-green-600">1250 ▲</td>
|
||||
{submissionData?.products?.map((product, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{/* Quantity Row */}
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Quantity ({product.unit?.uom || 'units'})
|
||||
</td>
|
||||
{/* Previous Quarter Quantities */}
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.previous_quantity_period_one || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.previous_quantity_period_two || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.previous_quantity_period_three || '0'}
|
||||
</td>
|
||||
|
||||
{/* Current Quarter Quantities */}
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.current_quantity_period_one || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.current_quantity_period_two || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.current_quantity_period_three || '0'}
|
||||
</td>
|
||||
|
||||
{/* Forecast Quarter Quantities */}
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.forecast_quantity_period_one || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.forecast_quantity_period_two || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.forecast_quantity_period_three || '0'}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<td className="px-4 py-3 text-[#232528]">1280</td>
|
||||
<td className="px-4 py-3 text-[#232528]">1340</td>
|
||||
<td className="px-4 py-3 text-[#232528]">1375</td>
|
||||
{/* Cost Row */}
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Cost (AED)
|
||||
</td>
|
||||
{/* Previous Quarter Costs */}
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.previous_cost_period_one || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.previous_cost_period_two || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.previous_cost_period_three || '0'}
|
||||
</td>
|
||||
|
||||
{/* Current Quarter Costs */}
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.current_cost_period_one || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.current_cost_period_two || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.current_cost_period_three || '0'}
|
||||
</td>
|
||||
|
||||
{/* Forecast Quarter Costs */}
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.forecast_cost_period_one || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.forecast_cost_period_two || '0'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">
|
||||
{product.forecast_cost_period_three || '0'}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{/* Remarks Row (if exists) */}
|
||||
{product.remarks && (
|
||||
<tr className="border-t border-[#E5E7EB] bg-gray-100">
|
||||
<td className="px-4 py-3 font-medium text-[#232528]">
|
||||
Remarks
|
||||
</td>
|
||||
<td colSpan="9" className="px-4 py-3 text-[#232528]">
|
||||
{product.remarks}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{/* Current Quarter Reason Row */}
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Reason (Current)
|
||||
</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center bg-[#E9F6EC] text-[#1E7C34] font-medium rounded-md">
|
||||
{product.variation_reason?.reason || 'No reason provided'}
|
||||
{product.other_variation_reason && (
|
||||
<div className="text-xs mt-1">Note: {product.other_variation_reason}</div>
|
||||
)}
|
||||
</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
</tr>
|
||||
|
||||
<td className="px-4 py-3 text-[#232528]">1400</td>
|
||||
<td className="px-4 py-3 text-[#232528]">1450</td>
|
||||
<td className="px-4 py-3 text-[#232528]">1500</td>
|
||||
</tr>
|
||||
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Cost (AED)
|
||||
</td>
|
||||
<td className="px-4 py-3 text-[#232528]">39,000</td>
|
||||
<td className="px-4 py-3 text-red-600">37,500 ▼</td>
|
||||
<td className="px-4 py-3 text-green-600">41,000 ▲</td>
|
||||
|
||||
<td className="px-4 py-3 text-[#232528]">40,000</td>
|
||||
<td className="px-4 py-3 text-[#232528]">42,000</td>
|
||||
<td className="px-4 py-3 text-[#232528]">43,500</td>
|
||||
|
||||
<td className="px-4 py-3 text-[#232528]">44,000</td>
|
||||
<td className="px-4 py-3 text-[#232528]">45,000</td>
|
||||
<td className="px-4 py-3 text-[#232528]">46,000</td>
|
||||
</tr>
|
||||
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Reason (Current)
|
||||
</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center bg-[#E9F6EC] text-[#1E7C34] font-medium rounded-md">
|
||||
Seasonal Increase
|
||||
</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
</tr>
|
||||
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Reason (Forecast)
|
||||
</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center bg-[#FFF2E0] text-[#D97706] font-medium rounded-md">
|
||||
Projected demand
|
||||
</td>
|
||||
</tr>
|
||||
{/* Forecast Quarter Reason Row */}
|
||||
<tr className="border-t border-[#E5E7EB]">
|
||||
<td className="bg-[#FFFCF2] px-4 py-3 font-medium text-[#232528]">
|
||||
Reason (Forecast)
|
||||
</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center text-[#6B7280]">--</td>
|
||||
<td colSpan="3" className="px-4 py-3 text-center bg-[#FFF2E0] text-[#D97706] font-medium rounded-md">
|
||||
{product.zero_target_reason?.reason || 'No reason provided'}
|
||||
{product.other_zero_target_reason && (
|
||||
<div className="text-xs mt-1">Note: {product.other_zero_target_reason}</div>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="text-xs text-[#4B5563] mt-2">
|
||||
<p>
|
||||
<span className="text-[#16A34A] font-semibold">Green ▲</span> indicates upward trend;{' '}
|
||||
<span className="text-[#DC2626] font-semibold">Red ▼</span> indicates downward trend compared to previous month or quarter
|
||||
</p>
|
||||
<p className="mt-3 text-sm text-[#4B5563]">
|
||||
<strong className="font-semibold text-[#232528]">Remarks:</strong> NA
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<label className="block text-sm font-semibold mb-1 text-[#232528]">Admin Remarks</label>
|
||||
<textarea
|
||||
rows="2"
|
||||
className="w-full rounded-[10px] border border-[#D1D5DB] bg-[#FFFCF2] px-4 py-2 text-sm text-[#232528] focus:ring-2 focus:ring-[#92722A] focus:outline-none"
|
||||
placeholder="Add short note..."
|
||||
></textarea>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
|
||||
<div className="mt-4">
|
||||
{/* <p>
|
||||
<span className="text-[#16A34A] font-semibold">Green ▲</span> indicates upward trend;{' '}
|
||||
<span className="text-[#DC2626] font-semibold">Red ▼</span> indicates downward trend compared to previous month or quarter
|
||||
</p> */}
|
||||
<p className="mt-3 text-sm text-[#4B5563]">
|
||||
<strong className="font-semibold text-[#232528]">Remarks:</strong> NA
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<label className="block text-sm font-semibold mb-1 text-[#232528]">Admin Remarks</label>
|
||||
<textarea
|
||||
rows="2"
|
||||
className="w-full rounded-[10px] border border-[#D1D5DB] bg-[#FFFCF2] px-4 py-2 text-sm text-[#232528] focus:ring-2 focus:ring-[#92722A] focus:outline-none"
|
||||
placeholder="Add short note..."
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg bg-amber-50 border border-amber-200 p-6 text-center">
|
||||
<p className="text-amber-800">No products found for this submission.</p>
|
||||
|
||||
@ -13,6 +13,8 @@ const STATUS_VARIANTS = {
|
||||
Approved: { background: '#F3FAF4', text: '#2F663C', border: '#F3FAF4' },
|
||||
Pending: { background: '#E7F5FF', text: '#286CFF', border: '#E7F5FF' },
|
||||
Rejected: { background: '#FEF2F2', text: '#B52520', border: '#FEF2F2' },
|
||||
Resubmitted: { background: '#F9F7ED', text: '#7C5E24', border: '#FEF2F2' },
|
||||
Submitted: { background: '#E7F5FF', text: '#003CFF', border: '#FEF2F2' },
|
||||
};
|
||||
|
||||
const SUMMARY_VARIANTS = {
|
||||
@ -20,8 +22,12 @@ const SUMMARY_VARIANTS = {
|
||||
approved: { background: '#F3FAF4', label: '#2F663C', value: '#232528' },
|
||||
pending: { background: '#E7F5FF', label: '#003CFF', value: '#232528' },
|
||||
rejected: { background: '#FEF2F2', label: '#B52520', value: '#232528' },
|
||||
resubmitted: { background: '#F9F7ED', text: '#7C5E24', border: '#FEF2F2' },
|
||||
submitted: { background: '#E7F5FF', text: '#003CFF', border: '#FEF2F2' },
|
||||
|
||||
};
|
||||
|
||||
|
||||
const formatDateTime = (value) => {
|
||||
if (!value) return '—';
|
||||
const date = new Date(value);
|
||||
|
||||
@ -1947,6 +1947,7 @@ const requiredFields = [
|
||||
factory_name: form.establishmentName || form.contactName || '',
|
||||
permanent_factory_code: form.permanentFactoryCode || '',
|
||||
industry_code: form.industryCodeBusiness || form.industryCodeCurrent || '',
|
||||
industry_code_production: form.industryCodeBusiness || form.industryCodeCurrent || '',
|
||||
license_number: form.uniqueLicenseNumber || '',
|
||||
isic_code: form.isicCode || form.industryCodeBusiness || '',
|
||||
description: form.industryDescription || '',
|
||||
@ -2025,6 +2026,7 @@ const requiredFields = [
|
||||
factory_name: form.establishmentName || form.contactName || '',
|
||||
permanent_factory_code: form.permanentFactoryCode || '',
|
||||
industry_code: form.industryCodeBusiness || form.industryCodeCurrent || '',
|
||||
industry_code_production: form.industryCodeBusiness || form.industryCodeCurrent || '',
|
||||
license_number: form.uniqueLicenseNumber || '',
|
||||
isic_code: form.isicCode || form.industryCodeBusiness || '',
|
||||
description: form.industryDescription || '',
|
||||
|
||||
@ -11,15 +11,40 @@ const BreadcrumbVector = '/assets/images/Breadcrumb-vector.svg';
|
||||
const caretDownSrc = '/assets/images/CaretDown.svg';
|
||||
|
||||
const Badge = ({ variant = 'default', children }) => {
|
||||
// Define all possible status variants and their styles
|
||||
const styles = {
|
||||
approved: 'bg-[#F3FAF4] text-[#2F663C]',
|
||||
submitted: 'bg-[#E7F5FF] text-[#003CFF]',
|
||||
pending: 'bg-[#F9F7ED] text-[#7C5E24]',
|
||||
rejected: 'bg-[#FEF2F2] text-[#B52520]',
|
||||
default: 'bg-gray-50 text-gray-700',
|
||||
// Approved status
|
||||
approved: 'text-green-700 bg-green-50',
|
||||
|
||||
// Submitted status
|
||||
submitted: 'text-green-700 bg-green-50',
|
||||
|
||||
// Under Review status (includes pending and in-progress)
|
||||
'under review': 'text-blue-600 bg-blue-50',
|
||||
pending: 'text-blue-600 bg-blue-50',
|
||||
'in-progress': 'text-blue-600 bg-blue-50',
|
||||
|
||||
// Rejected status (includes returned)
|
||||
rejected: 'text-red-600 bg-red-50',
|
||||
returned: 'text-red-600 bg-red-50',
|
||||
|
||||
// Resubmitted status
|
||||
resubmitted: 'text-[#7C5E24] bg-[#F9F7ED]',
|
||||
|
||||
// Default fallback
|
||||
default: 'text-gray-600 bg-gray-50',
|
||||
};
|
||||
|
||||
// Normalize the variant key
|
||||
const normalizedVariant = String(variant || '').toLowerCase().trim();
|
||||
|
||||
// Get the appropriate style, fallback to default if not found
|
||||
const style = styles[normalizedVariant] || styles.default;
|
||||
|
||||
return (
|
||||
<span className={`inline-flex items-center justify-center rounded-full text-xs font-medium px-3 py-1 ${styles[variant.toLowerCase()] || styles.default}`}>
|
||||
<span
|
||||
className={`inline-flex items-center justify-center rounded-md px-3 py-1 text-sm font-medium whitespace-nowrap ${style}`}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
@ -180,8 +205,9 @@ const formatExportDateTime = (value) => {
|
||||
const text = String(status || '').toLowerCase();
|
||||
if (text === 'approved') return 'approved';
|
||||
if (text === 'submitted') return 'submitted';
|
||||
if (text === 'rejected') return 'rejected';
|
||||
if (text === 'pending' || text === 'in-progress' || text === 'under review') return 'pending';
|
||||
if (text === 'resubmitted') return 'resubmitted';
|
||||
if (text === 'rejected' || text === 'returned') return 'rejected';
|
||||
if (text === 'pending' || text === 'in-progress' || text === 'under review') return 'under review';
|
||||
return 'default';
|
||||
}, []);
|
||||
|
||||
@ -375,7 +401,7 @@ const filteredRows = useMemo(() => {
|
||||
'Quarter',
|
||||
'HS Code',
|
||||
'Product',
|
||||
'Status',
|
||||
{ name: 'Status', className: 'text-center' },
|
||||
'Actors',
|
||||
'Details'
|
||||
];
|
||||
@ -850,14 +876,24 @@ const tableRows = auditHistory.map((entry, index) => {
|
||||
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,
|
||||
}}
|
||||
columnWidths={[
|
||||
{ width: 180, style: { whiteSpace: 'normal', wordWrap: 'break-word' } }, // Submission Date
|
||||
{ width: 80, style: { whiteSpace: 'normal', wordWrap: 'break-word' } }, // Year
|
||||
{ width: 80, style: { whiteSpace: 'normal', wordWrap: 'break-word' } }, // Quarter
|
||||
{ width: 120, style: { whiteSpace: 'normal', wordWrap: 'break-word' } }, // HS Code
|
||||
{ width: 200, style: { whiteSpace: 'normal', wordWrap: 'break-word' } }, // Product
|
||||
{ width: 130, align: 'center', style: { whiteSpace: 'normal', wordWrap: 'break-word' } }, // Status
|
||||
{ width: 140, style: { whiteSpace: 'normal', wordWrap: 'break-word' } }, // Actors
|
||||
{ width: 270, style: { whiteSpace: 'normal', wordWrap: 'break-word' } } // Details
|
||||
]}
|
||||
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 === 5) { // Status column
|
||||
return <Badge variant={toVariant(filteredRows[ri]?.status)}>{formatStatus(filteredRows[ri]?.status)}</Badge>;
|
||||
|
||||
@ -14,6 +14,7 @@ const statusStyles = {
|
||||
submitted: 'text-[#003CFF] bg-[#E7F5FF]',
|
||||
pending: 'text-[#7C5E24] bg-[#F9F7ED]',
|
||||
rejected: 'text-[#B52520] bg-[#FEF2F2]',
|
||||
resubmitted: 'text-[#7C5E24] bg-[#F9F7ED]',
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { submitSurvey, fetchSubmissionDetail } from '@/services/submissions/submissionService';
|
||||
import { submitSurvey, fetchSubmissionDetail, resubmitSurvey } from '@/services/submissions/submissionService';
|
||||
import { fetchEstablishmentDetail, fetchEstablishmentDashboard } from '@/services/establishments/establishmentService';
|
||||
import EstablishmentInfo from '@/components/survey/EstablishmentInfo/EstablishmentInfo';
|
||||
import HeaderBar from '@/components/layout/HeaderBar';
|
||||
@ -71,6 +71,8 @@ const Survey = () => {
|
||||
const [establishmentLoading, setEstablishmentLoading] = React.useState(false);
|
||||
const [establishmentError, setEstablishmentError] = React.useState('');
|
||||
const isViewMode = React.useMemo(() => Boolean(locationState.submission), [locationState.submission]);
|
||||
const isResubmit = React.useMemo(() => locationState?.isResubmit === true, [locationState?.isResubmit]);
|
||||
const submissionId = React.useMemo(() => locationState?.submissionId || locationState?.submission?.id, [locationState]);
|
||||
const toastTimeoutRef = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -98,8 +100,18 @@ const Survey = () => {
|
||||
|
||||
const initializeSurveyData = async () => {
|
||||
try {
|
||||
// If this is a resubmission, use the quarter and year from the submission
|
||||
if (isResubmit && locationState?.submission) {
|
||||
if (!cancelled) {
|
||||
setEstablishmentData(prev => ({
|
||||
...prev,
|
||||
quarter: locationState.submission.quarter || '',
|
||||
year: locationState.submission.year?.toString() || ''
|
||||
}));
|
||||
}
|
||||
}
|
||||
// If quarter and year are not provided in location state, fetch them
|
||||
if (!locationState?.currentQuarter || !locationState?.currentYear) {
|
||||
else if (!locationState?.currentQuarter || !locationState?.currentYear) {
|
||||
const { quarter, year } = await fetchCurrentQuarterAndYear();
|
||||
if (!cancelled) {
|
||||
setEstablishmentData(prev => ({
|
||||
@ -128,7 +140,7 @@ const Survey = () => {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [locationState?.currentQuarter, locationState?.currentYear]); // Only depend on these values
|
||||
}, [locationState?.currentQuarter, locationState?.currentYear]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
@ -204,19 +216,39 @@ const Survey = () => {
|
||||
|
||||
const mergeEstablishmentData = (detail) => {
|
||||
const summary = locationState.submission || {};
|
||||
const establishment = detail?.establishment || {};
|
||||
|
||||
return {
|
||||
quarter: ensureString(detail?.quarter) || ensureString(summary.quarter) || '',
|
||||
year: ensureString(detail?.year) || ensureString(summary.year) || '',
|
||||
establishmentName: ensureString(summary.establishment_name) || establishmentData.establishmentName || '',
|
||||
permanentFactoryCode:
|
||||
ensureString(summary.permanent_factory_code) ||
|
||||
ensureString(detail?.establishment_id) ||
|
||||
establishmentData.permanentFactoryCode || '',
|
||||
industryCode: ensureString(summary.industry_code) || establishmentData.industryCode || '',
|
||||
licenseNumber: ensureString(summary.license_number) || establishmentData.licenseNumber || '',
|
||||
isicCode: ensureString(summary.isic_code) || establishmentData.isicCode || '',
|
||||
emirate: ensureString(summary.emirate) || establishmentData.emirate || '',
|
||||
employeeInfo: { ...establishmentData.employeeInfo },
|
||||
establishmentName: ensureString(establishment.factory_name) ||
|
||||
ensureString(summary.establishment_name) ||
|
||||
establishmentData.establishmentName || '',
|
||||
permanentFactoryCode: ensureString(establishment.permanent_factory_code) ||
|
||||
ensureString(summary.permanent_factory_code) ||
|
||||
ensureString(detail?.establishment_id) ||
|
||||
establishmentData.permanentFactoryCode || '',
|
||||
industryCode: ensureString(establishment.industry_code) ||
|
||||
ensureString(summary.industry_code) ||
|
||||
establishmentData.industryCode || '',
|
||||
licenseNumber: ensureString(establishment.license_number) ||
|
||||
ensureString(summary.license_number) ||
|
||||
establishmentData.licenseNumber || '',
|
||||
isicCode: ensureString(establishment.isic_code) ||
|
||||
ensureString(summary.isic_code) ||
|
||||
establishmentData.isicCode || '',
|
||||
emirate: ensureString(establishment.establishment_emirate_id) ||
|
||||
ensureString(establishment.emirate) ||
|
||||
ensureString(summary.emirate) ||
|
||||
establishmentData.emirate || '',
|
||||
employeeInfo: {
|
||||
emiratiMale: ensureString(establishment.emirati_male) || establishmentData.employeeInfo.emiratiMale || '',
|
||||
emiratiFemale: ensureString(establishment.emirati_female) || establishmentData.employeeInfo.emiratiFemale || '',
|
||||
nonEmiratiMale: ensureString(establishment.non_emirati_male) || establishmentData.employeeInfo.nonEmiratiMale || '',
|
||||
nonEmiratiFemale: ensureString(establishment.non_emirati_female) || establishmentData.employeeInfo.nonEmiratiFemale || '',
|
||||
totalEmirati: ensureString(establishment.total_emirati) || establishmentData.employeeInfo.totalEmirati || '',
|
||||
totalEmployees: ensureString(establishment.total_employees) || establishmentData.employeeInfo.totalEmployees || '',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -224,20 +256,50 @@ const Survey = () => {
|
||||
if (!Array.isArray(detailProducts)) return [];
|
||||
return detailProducts.map((product, index) => ({
|
||||
id: product?.id ?? index + 1,
|
||||
productId: product?.product_id || product?.product?.id || '',
|
||||
product: ensureString(product?.product?.product_name) || ensureString(product?.product_id) || '',
|
||||
unit: ensureString(product?.unit?.name) || ensureString(product?.unit_id) || '',
|
||||
unit: ensureString(product?.unit?.uom) || ensureString(product?.unit?.name) || ensureString(product?.unit_id) || '',
|
||||
unitId: product?.unit_id || product?.unit?.id || '',
|
||||
capacity: ensureString(product?.annual_installed_capacity),
|
||||
previousQuantity: ensureString(product?.previous_quantity),
|
||||
previousCost: ensureString(product?.previous_cost),
|
||||
currentQuantity: ensureString(product?.current_quantity),
|
||||
currentCost: ensureString(product?.current_cost),
|
||||
forecastQuantity: ensureString(product?.forecast_quantity),
|
||||
forecastCost: ensureString(product?.forecast_cost),
|
||||
variationReason: ensureString(product?.variation_reason_master_id),
|
||||
otherVariationReason: ensureString(product?.other_variation_reason),
|
||||
zeroTargetReason: ensureString(product?.zero_target_reason_master_id),
|
||||
otherZeroTargetReason: ensureString(product?.other_zero_target_reason),
|
||||
remarks: ensureString(product?.remarks),
|
||||
|
||||
// Previous Quarter (Q4) - October, November, December
|
||||
octQuantity: ensureString(product?.previous_quantity_period_one || ''),
|
||||
novQuantity: ensureString(product?.previous_quantity_period_two || ''),
|
||||
decQuantity: ensureString(product?.previous_quantity_period_three || ''),
|
||||
octCost: ensureString(product?.previous_cost_period_one || ''),
|
||||
novCost: ensureString(product?.previous_cost_period_two || ''),
|
||||
decCost: ensureString(product?.previous_cost_period_three || ''),
|
||||
|
||||
// Current Quarter (Q1) - January, February, March
|
||||
janQuantity: ensureString(product?.current_quantity_period_one || ''),
|
||||
febQuantity: ensureString(product?.current_quantity_period_two || ''),
|
||||
marQuantity: ensureString(product?.current_quantity_period_three || ''),
|
||||
janCost: ensureString(product?.current_cost_period_one || ''),
|
||||
febCost: ensureString(product?.current_cost_period_two || ''),
|
||||
marCost: ensureString(product?.current_cost_period_three || ''),
|
||||
|
||||
// Forecast Quarter (Q2) - April, May, June
|
||||
aprQuantity: ensureString(product?.forecast_quantity_period_one || ''),
|
||||
mayQuantity: ensureString(product?.forecast_quantity_period_two || ''),
|
||||
junQuantity: ensureString(product?.forecast_quantity_period_three || ''),
|
||||
aprCost: ensureString(product?.forecast_cost_period_one || ''),
|
||||
mayCost: ensureString(product?.forecast_cost_period_two || ''),
|
||||
junCost: ensureString(product?.forecast_cost_period_three || ''),
|
||||
|
||||
// Totals (for backward compatibility)
|
||||
previousQuantity: ensureString(product?.previous_quantity || ''),
|
||||
previousCost: ensureString(product?.previous_cost || ''),
|
||||
currentQuantity: ensureString(product?.current_quantity || ''),
|
||||
currentCost: ensureString(product?.current_cost || ''),
|
||||
forecastQuantity: ensureString(product?.forecast_quantity || ''),
|
||||
forecastCost: ensureString(product?.forecast_cost || ''),
|
||||
|
||||
// Reasons and remarks
|
||||
variationReason: ensureString(product?.variation_reason_master_id || ''),
|
||||
otherVariationReason: ensureString(product?.other_variation_reason || ''),
|
||||
zeroTargetReason: ensureString(product?.zero_target_reason_master_id || ''),
|
||||
otherZeroTargetReason: ensureString(product?.other_zero_target_reason || ''),
|
||||
remarks: ensureString(product?.remarks || '')
|
||||
}));
|
||||
};
|
||||
|
||||
@ -309,7 +371,7 @@ const Survey = () => {
|
||||
return String(value);
|
||||
}, []);
|
||||
|
||||
const buildSubmissionPayload = React.useCallback(() => {
|
||||
const buildSubmissionPayload = React.useCallback((isResubmitFlow = false) => {
|
||||
const info = establishmentData?.employeeInfo ?? {};
|
||||
return {
|
||||
quarter: establishmentData?.quarter ?? '',
|
||||
@ -324,11 +386,13 @@ const Survey = () => {
|
||||
products: productsData.map((product) => {
|
||||
const variationId = parseNumber(product.variationReason, null);
|
||||
const zeroTargetId = parseNumber(product.zeroTargetReason, null);
|
||||
const productId = parseNumber(product.id);
|
||||
|
||||
// Map monthly data to period-specific fields
|
||||
return {
|
||||
...(isResubmitFlow && productId ? { id: productId } : {}), // Only include ID during resubmit
|
||||
product_id: parseNumber(product.productId) || parseNumber(product.product),
|
||||
unit_id: parseNumber(product.unit),
|
||||
unit_id: parseNumber(product.unitId) || parseNumber(product.unit),
|
||||
annual_installed_capacity: stringify(product.capacity),
|
||||
|
||||
// Previous Quarter (Q4) - October, November, December
|
||||
@ -412,7 +476,31 @@ const Survey = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = React.useCallback(async () => {
|
||||
const handleSubmit = React.useCallback(async (e) => {
|
||||
if (e) e.preventDefault();
|
||||
if (isSubmitting) return;
|
||||
|
||||
// Validate current step before proceeding
|
||||
if (step === 1) {
|
||||
const establishmentError = validateEstablishmentInfo();
|
||||
if (establishmentError) {
|
||||
setError(establishmentError);
|
||||
return;
|
||||
}
|
||||
} else if (step === 2) {
|
||||
const productError = validateProductData();
|
||||
if (productError) {
|
||||
setError(productError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the final step (step 3) and it's a resubmission, include the submission ID
|
||||
if (step === 3 && isResubmit && submissionId) {
|
||||
await handleResubmit();
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
const payload = buildSubmissionPayload();
|
||||
@ -432,6 +520,57 @@ const Survey = () => {
|
||||
}
|
||||
}, [buildSubmissionPayload, showToast]);
|
||||
|
||||
const handleResubmit = async () => {
|
||||
setIsSubmitting(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const payload = {
|
||||
...buildSubmissionPayload(true), // Pass true to indicate resubmission flow
|
||||
status: 'Resubmitted', // Set status to Resubmitted for resubmissions
|
||||
id: submissionId // Include submission ID in the payload
|
||||
};
|
||||
|
||||
// Use the resubmitSurvey API with the submission ID and payload
|
||||
await resubmitSurvey(submissionId, payload);
|
||||
|
||||
setShowSuccessPopup(true);
|
||||
setHasSubmitted(true);
|
||||
showToast('success', 'Survey resubmitted successfully!');
|
||||
} catch (error) {
|
||||
console.error('Resubmission failed:', error);
|
||||
const message = error?.response?.data?.message || error?.message || 'Failed to resubmit survey. Please try again.';
|
||||
setError(message);
|
||||
showToast('error', message);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFinalSubmit = async () => {
|
||||
if (isResubmit && submissionId) {
|
||||
await handleResubmit();
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const payload = buildSubmissionPayload();
|
||||
await submitSurvey(payload);
|
||||
setShowSuccessPopup(true);
|
||||
setHasSubmitted(true);
|
||||
showToast('success', 'Survey submitted successfully!');
|
||||
} catch (error) {
|
||||
const message = error?.response?.data?.message || error?.message || 'Failed to submit survey. Please try again.';
|
||||
setError(message);
|
||||
showToast('error', message);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const StepBadge = ({ state, number }) => {
|
||||
if (state === 'active') {
|
||||
return (
|
||||
@ -634,7 +773,8 @@ const Survey = () => {
|
||||
establishment={establishmentData}
|
||||
products={productsData}
|
||||
onBack={() => setStep(2)}
|
||||
onSubmit={handleSubmit}
|
||||
onSubmit={handleFinalSubmit}
|
||||
submitButtonText={isResubmit ? 'Resubmit' : 'Submit'}
|
||||
toast={toast}
|
||||
onToastClose={() => {
|
||||
if (toastTimeoutRef.current) {
|
||||
@ -644,7 +784,7 @@ const Survey = () => {
|
||||
setToast(null);
|
||||
}}
|
||||
isSubmitting={isSubmitting}
|
||||
hasSubmitted={hasSubmitted}
|
||||
hasSubmitted={false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -674,12 +814,12 @@ const Survey = () => {
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
Submission successful
|
||||
{isResubmit ? 'Resubmission successful' : 'Submission successful'}
|
||||
</h3>
|
||||
|
||||
{/* Message */}
|
||||
<p className="text-sm text-gray-700">
|
||||
Thank you. Your IP Quarterly Survey for <span className="font-semibold text-gray-900">{establishmentData?.quarter} {establishmentData?.year}</span> has been submitted.
|
||||
Thank you. Your IP Quarterly Survey for <span className="font-semibold text-gray-900">{establishmentData?.quarter} {establishmentData?.year}</span> has been {isResubmit ? 'resubmitted' : 'submitted'}.
|
||||
</p>
|
||||
|
||||
{/* Button */}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user