added flag for products
This commit is contained in:
parent
236743053e
commit
318d35235d
10
ipi-survey-platform/package-lock.json
generated
10
ipi-survey-platform/package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"react-toastify": "^11.0.5"
|
||||
},
|
||||
@ -3568,6 +3569,15 @@
|
||||
"react": ">=16.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-icons": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
||||
"integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"react-toastify": "^11.0.5"
|
||||
},
|
||||
|
||||
@ -1389,7 +1389,7 @@ const location = useLocation();
|
||||
}}
|
||||
loading={isLoadingUnits}
|
||||
error={!!formErrors[`unit_${idx}`]}
|
||||
disabled={!!p.unitName} // Disable if unit is auto-filled from product
|
||||
// disabled={!!p.unitName} // Disable if unit is auto-filled from product
|
||||
/>
|
||||
<div className="h-5">
|
||||
{formErrors[`unit_${idx}`] && (
|
||||
|
||||
@ -159,6 +159,7 @@ const createEmptyProfile = () => ({
|
||||
industryCodeBusiness: '',
|
||||
industryCodeProduction: '',
|
||||
industryCodeCurrent: '',
|
||||
industryCodeMismatchRemarks: '',
|
||||
industryDescription: '',
|
||||
// Establishment contact details
|
||||
contactName: '',
|
||||
@ -816,6 +817,10 @@ const CompanyProfile = () => {
|
||||
};
|
||||
|
||||
const renderStepContent = () => {
|
||||
const fieldProps = (fieldName) => ({
|
||||
disabled: modalMode === 'view',
|
||||
error: fieldErrors[fieldName]
|
||||
});
|
||||
switch (activeStep) {
|
||||
case 0:
|
||||
return (
|
||||
@ -831,6 +836,8 @@ const CompanyProfile = () => {
|
||||
width="100%"
|
||||
required
|
||||
error={fieldErrors.userProfileName}
|
||||
{...fieldProps('userProfileName')}
|
||||
|
||||
/>
|
||||
<TextField
|
||||
label="Email"
|
||||
@ -958,12 +965,24 @@ const CompanyProfile = () => {
|
||||
<div className="mt-4">
|
||||
<TextField
|
||||
label="Remarks"
|
||||
value={form.remarks}
|
||||
onChange={handleFormChange('remarks')}
|
||||
placeholder="Add Remarks"
|
||||
value={form.industryCodeMismatchRemarks}
|
||||
onChange={handleFormChange('industryCodeMismatchRemarks')}
|
||||
placeholder="Enter remarks for industry code mismatch"
|
||||
width="100%"
|
||||
style={{ height: '72px' }}
|
||||
multiline
|
||||
rows={3}
|
||||
/>
|
||||
{/* <div className="mt-2">
|
||||
<TextField
|
||||
label="Industry Description"
|
||||
value={form.industryDescription}
|
||||
onChange={handleFormChange('industryDescription')}
|
||||
placeholder="Enter industry description"
|
||||
width="100%"
|
||||
multiline
|
||||
rows={2}
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1449,7 +1468,7 @@ const CompanyProfile = () => {
|
||||
showToast('success', `Establishment ${newStatus ? 'activated' : 'deactivated'} successfully`);
|
||||
} catch (error) {
|
||||
console.error('Error updating status:', error);
|
||||
const message = error?.response?.data?.message || 'Failed to update status. Please try again.';
|
||||
const message = error?.response?.data?.message || error?.message || 'Failed to update status. Please try again.';
|
||||
showToast('error', message);
|
||||
}
|
||||
};
|
||||
@ -1805,6 +1824,7 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
industryCodeBusiness: item?.industry_code,
|
||||
industryCodeProduction: item?.industry_code_production,
|
||||
industryCodeCurrent: item?.industryCodeCurrent,
|
||||
industryCodeMismatchRemarks: item?.industry_code_mismatch_remarks ?? '',
|
||||
industryDescription: item?.description ?? base.industryDescription,
|
||||
establishmentId: item?.establishment_code ?? '',
|
||||
contactAddress: item?.establishment_address ?? base.contactAddress,
|
||||
@ -2116,13 +2136,47 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleView = async (id) => {
|
||||
// const handleView = async (id) => {
|
||||
// try {
|
||||
// setIsLoading(true);
|
||||
// const response = await fetchEstablishmentDetail(id);
|
||||
// const establishment = mapApiEstablishmentToProfile(response.data);
|
||||
// setForm(establishment);
|
||||
// setSelectedProducts(establishment.products || []);
|
||||
// setModalMode('view');
|
||||
// setIsViewMode(true);
|
||||
// setEditingRow(id);
|
||||
// } catch (error) {
|
||||
// console.error('Error fetching establishment details:', error);
|
||||
// showToast('error', 'Failed to load establishment details');
|
||||
// } finally {
|
||||
// setIsLoading(false);
|
||||
// }
|
||||
// };
|
||||
const handleView = async (id) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await fetchEstablishmentDetail(id);
|
||||
const establishment = mapApiEstablishmentToProfile(response.data);
|
||||
setForm(establishment);
|
||||
setSelectedProducts(establishment.products || []);
|
||||
|
||||
// Format products data before setting to state
|
||||
const formattedProducts = (response.data.establishment_products || []).map(item => {
|
||||
const product = item.product || {};
|
||||
const productName = product.product_name || 'Unnamed Product';
|
||||
const hsCode = product.hs_code || '';
|
||||
|
||||
return {
|
||||
...item,
|
||||
...product,
|
||||
product_name: productName,
|
||||
hs_code: hsCode,
|
||||
label: `${productName}${hsCode ? ` (${hsCode})` : ''}`,
|
||||
value: item.product_id ? String(item.product_id) : '',
|
||||
};
|
||||
});
|
||||
|
||||
setSelectedProducts(formattedProducts);
|
||||
setModalMode('view');
|
||||
setIsViewMode(true);
|
||||
setEditingRow(id);
|
||||
@ -2133,7 +2187,6 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (establishmentId) => {
|
||||
if (!establishmentId) return;
|
||||
const originalIndex = profiles.findIndex((item) => {
|
||||
@ -2507,6 +2560,7 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
permanent_factory_code: form.permanentFactoryCode || '',
|
||||
industry_code: form.industryCodeBusiness || '',
|
||||
industry_code_production: form.industryCodeProduction || form.industryCodeCurrent || '',
|
||||
industry_code_mismatch_remarks: form.industryCodeMismatchRemarks || '',
|
||||
license_number: form.uniqueLicenseNumber || '',
|
||||
isic_code: form.industryCodeProduction || '',
|
||||
description: form.industryDescription || '',
|
||||
@ -2548,7 +2602,8 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
password: form.userProfilePassword || ''
|
||||
},
|
||||
establishment_products: Array.isArray(selectedProducts) && selectedProducts.length > 0
|
||||
? selectedProducts.map(p => ({ product_id: p.id || p.product_id || 0 }))
|
||||
? selectedProducts.map(p => ({ product_id: p.id || p.product_id || 0
|
||||
}))
|
||||
: [],
|
||||
};
|
||||
|
||||
@ -2612,6 +2667,7 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
permanent_factory_code: form.permanentFactoryCode || '',
|
||||
industry_code: form.industryCodeBusiness || '',
|
||||
industry_code_production: form.industryCodeProduction || form.industryCodeCurrent || '',
|
||||
industry_code_mismatch_remarks: form.industryCodeMismatchRemarks || '',
|
||||
license_number: form.uniqueLicenseNumber || '',
|
||||
isic_code: form.industryCodeProduction || '',
|
||||
description: form.industryDescription || '',
|
||||
@ -2650,7 +2706,10 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
// Enhanced product handling for updates - only send product IDs that exist
|
||||
establishment_products: Array.isArray(selectedProducts) && selectedProducts.length > 0
|
||||
? selectedProducts.map(p => ({
|
||||
product_id: p.product_id || p.id || 0
|
||||
product_id: p.product_id || p.id || 0,
|
||||
action_done_by: "admin_user" ,
|
||||
created_by: currentUserId?.id
|
||||
|
||||
}))
|
||||
: []
|
||||
};
|
||||
@ -2784,7 +2843,7 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!modalMode) return undefined;
|
||||
if (!modalMode || isViewMode) return undefined;
|
||||
const handleBeforeUnload = (event) => {
|
||||
if (isDirty) {
|
||||
event.preventDefault();
|
||||
@ -2795,7 +2854,7 @@ const displayedRows = filteredProfiles.map((item) => {
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
};
|
||||
}, [isDirty, modalMode]);
|
||||
}, [isDirty, modalMode, isViewMode]);
|
||||
|
||||
const deletingProfile = React.useMemo(() => {
|
||||
if (deletingRow === null || deletingRow < 0 || deletingRow >= profiles.length) return null;
|
||||
@ -3070,7 +3129,8 @@ const handleImport = async () => {
|
||||
>
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-[#F1F2F4]">
|
||||
<h3 className="text-[18px] font-medium text-[#232528]">
|
||||
{modalMode === 'edit' ? 'Edit Company Profile' : 'Add Profile'}
|
||||
{/* {modalMode === 'edit' ? 'Edit Company Profile' : 'Add Profile'} */}
|
||||
{isViewMode ? 'View Profile' : modalMode === 'edit' ? 'Edit Profile' : 'Add Profile'}
|
||||
</h3>
|
||||
<button
|
||||
type="button"
|
||||
@ -3156,24 +3216,36 @@ const handleImport = async () => {
|
||||
)}
|
||||
</div>
|
||||
{activeStep === formSteps.length - 1 ? (
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
className={`h-10 px-6 rounded-md border border-[#92722A] text-sm font-medium text-[#92722A] bg-white ${isSaving ? 'opacity-60 cursor-not-allowed' : ''}`}
|
||||
onClick={closeModal}
|
||||
disabled={isSaving}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
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)] ${isSaving ? 'opacity-60 cursor-not-allowed' : ''}`}
|
||||
onClick={handleSaveForm}
|
||||
disabled={isSaving}
|
||||
>
|
||||
{isUpdating ? 'Updating…' : isSaving ? 'Saving…' : modalMode === 'edit' ? 'Update' : 'Add'}
|
||||
</button>
|
||||
</div>
|
||||
isViewMode ? (
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
className="h-10 px-6 rounded-md bg-[#92722A] text-sm font-medium text-white hover:bg-[#92722A] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#6B7280]"
|
||||
onClick={closeModal}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
className={`h-10 px-6 rounded-md border border-[#92722A] text-sm font-medium text-[#92722A] bg-white ${isSaving ? 'opacity-60 cursor-not-allowed' : ''}`}
|
||||
onClick={closeModal}
|
||||
disabled={isSaving}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
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)] ${isSaving ? 'opacity-60 cursor-not-allowed' : ''}`}
|
||||
onClick={handleSaveForm}
|
||||
disabled={isSaving}
|
||||
>
|
||||
{isUpdating ? 'Updating…' : isSaving ? 'Saving…' : modalMode === 'edit' ? 'Update' : 'Add'}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
{activeStep > 0 && (
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import React, { useState, useMemo, useCallback, useEffect, useRef } from "react";
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { FaBuilding } from 'react-icons/fa6';
|
||||
import { MdAdminPanelSettings } from "react-icons/md";
|
||||
import { TextField, SelectField, PhoneField } from "@/components/common/FormControls";
|
||||
import Loader from "@/components/common/Loader";
|
||||
import { fetchEstablishmentDetail, updateEstablishment } from '@/services/establishments/establishmentService';
|
||||
@ -14,6 +16,7 @@ const createEmptyProfile = () => ({
|
||||
uniqueLicenseNumber: '',
|
||||
industryCodeBusiness: '',
|
||||
industryCodeCurrent: '',
|
||||
industryCodeMismatchRemarks: '',
|
||||
contactName: '',
|
||||
contactAddress: '',
|
||||
contactCityTown: '',
|
||||
@ -39,8 +42,9 @@ const mapApiEstablishmentToProfile = (apiData) => {
|
||||
userProfileEmail: data.users?.[0]?.email || '',
|
||||
permanentFactoryCode: data.permanent_factory_code || '',
|
||||
uniqueLicenseNumber: data.license_number || '',
|
||||
industryCodeBusiness: data.isic_code || '',
|
||||
industryCodeCurrent: data.isic_code || '',
|
||||
industryCodeBusiness: data.industry_code_production || '',
|
||||
industryCodeCurrent: data.industry_code || '',
|
||||
industryCodeMismatchRemarks: data.industry_code_mismatch_remarks || '',
|
||||
contactName: data.factory_name || '',
|
||||
contactAddress: data.establishment_address || '',
|
||||
contactCityTown: data.establishment_city?.name || '',
|
||||
@ -77,8 +81,6 @@ const computeEmploymentTotals = (data) => {
|
||||
const EditCompanyProfile = () => {
|
||||
const navigate = useNavigate();
|
||||
const { id: establishmentId } = useParams();
|
||||
console.log('EditCompanyProfile mounted, establishmentId from params:', establishmentId);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form, setForm] = useState(createEmptyProfile());
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
@ -412,12 +414,15 @@ const EditCompanyProfile = () => {
|
||||
// Find the product in the loaded products to get the proper format
|
||||
const foundProduct = allProducts.find(p => p.value === String(productId));
|
||||
|
||||
return foundProduct || {
|
||||
value: String(productId),
|
||||
label: productData.product_name || productData.name || 'Unnamed Product',
|
||||
hs_code: productData.hs_code || '',
|
||||
product_name: productData.product_name || productData.name || 'Unnamed Product',
|
||||
isExisting: true // Mark as existing product
|
||||
return {
|
||||
...(foundProduct || {
|
||||
value: String(productId),
|
||||
label: productData.product_name || productData.name || 'Unnamed Product',
|
||||
hs_code: productData.hs_code || '',
|
||||
product_name: productData.product_name || productData.name || 'Unnamed Product',
|
||||
isExisting: true // Mark as existing product
|
||||
}),
|
||||
action_done_by: ep.action_done_by // Add the action_done_by field from the API response
|
||||
};
|
||||
});
|
||||
|
||||
@ -503,7 +508,7 @@ const EditCompanyProfile = () => {
|
||||
permanent_factory_code: form.permanentFactoryCode || "",
|
||||
industry_code: form.industryCodeBusiness || "",
|
||||
license_number: form.uniqueLicenseNumber || "",
|
||||
isic_code: form.industryCodeCurrent || "",
|
||||
industry_code_production: form.industryCodeCurrent || "",
|
||||
description: form.industryDescription || "",
|
||||
establishment_address: form.contactAddress || "",
|
||||
establishment_city_town_id: selectedCity ? Number(selectedCity.value) : null,
|
||||
@ -531,7 +536,10 @@ const EditCompanyProfile = () => {
|
||||
updated_by: 1,
|
||||
establishment_products: Array.isArray(selectedProducts) && selectedProducts.length > 0
|
||||
? selectedProducts.map(p => ({
|
||||
product_id: Number(p.value) || 0
|
||||
product_id: Number(p.value) || 0,
|
||||
action_done_by: p.action_done_by || "establishment_users",
|
||||
created_by: p.created_by || establishmentId
|
||||
|
||||
}))
|
||||
: [],
|
||||
};
|
||||
@ -655,6 +663,21 @@ const EditCompanyProfile = () => {
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
{form.industryCodeCurrent && form.industryCodeBusiness !== form.industryCodeCurrent && (
|
||||
<div className="col-span-full">
|
||||
<TextField
|
||||
label="Remarks"
|
||||
value={form.industryCodeMismatchRemarks || ''}
|
||||
onChange={(e) => handleFormChange('industryCodeMismatchRemarks', e.target.value)}
|
||||
placeholder="Enter remarks for industry code mismatch"
|
||||
width="100%"
|
||||
multiline
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -785,8 +808,24 @@ const EditCompanyProfile = () => {
|
||||
case 3:
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h4 className="text-[16px] font-semibold text-[#232528]">Products</h4>
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Left side: Products Header */}
|
||||
<h4 className="text-[16px] font-semibold text-[#232528]">Products</h4>
|
||||
|
||||
{/* Right side: Icons */}
|
||||
<div className="flex items-center gap-4 text-sm text-medium-600">
|
||||
<div className="flex items-center gap-1">
|
||||
<MdAdminPanelSettings className="text-[#92722A] text-lg" />
|
||||
<span>Admin User</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<FaBuilding className="text-[#92722A]" />
|
||||
<span>Establishment User</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* </div> */}
|
||||
|
||||
{productError && (
|
||||
<p className="text-xs text-[#B91C1C] mt-1">{productError}</p>
|
||||
)}
|
||||
@ -865,15 +904,22 @@ const EditCompanyProfile = () => {
|
||||
const isNewlyAdded = newlyAddedProductIds.has(product.value);
|
||||
return (
|
||||
<li key={product.value} className="p-3 hover:bg-[#F9FAFB] flex justify-between items-center">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
{product.label}
|
||||
</div>
|
||||
{product.hs_code && (
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
HS Code: {product.hs_code}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{product.action_done_by === "admin_user" ? (
|
||||
<MdAdminPanelSettings className="text-[#92722A] text-lg" title="Added by Admin" />
|
||||
) : (
|
||||
<FaBuilding className="text-[#92722A]" title="Added by Establishment" />
|
||||
)}
|
||||
<div>
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
{product.label}
|
||||
</div>
|
||||
{product.hs_code && (
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
HS Code: {product.hs_code}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isNewlyAdded && (
|
||||
<button
|
||||
|
||||
Loading…
Reference in New Issue
Block a user