added flag for products

This commit is contained in:
Malini 2025-11-22 17:34:44 +05:30
parent 236743053e
commit 318d35235d
5 changed files with 183 additions and 54 deletions

View File

@ -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",

View File

@ -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"
},

View File

@ -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}`] && (

View File

@ -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 && (

View File

@ -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