fcsc bug fix
This commit is contained in:
parent
f78534b258
commit
236743053e
@ -119,7 +119,7 @@ export const SurveyStatus = () => {
|
||||
|
||||
return {
|
||||
title: `${quarter} ${year} Survey`,
|
||||
subtitle: 'Complete your quarterly Industrial Production Index (IPI) data submission',
|
||||
subtitle: 'Complete your quarterly Index of Industrial Production (IIP) data submission',
|
||||
dueDate: formatDate(end_date),
|
||||
relativeDue: formatRelativeDays(end_date),
|
||||
estTime: '15–20 minutes',
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { getEstablishmentProducts } from '../../../services/submissions/submissionService';
|
||||
import Table from '@/components/common/Table';
|
||||
|
||||
const caretDownSrc = '/assets/images/CaretDown.svg';
|
||||
const backVectorSrc = '/assets/images/BackVector.svg';
|
||||
const mailIconSrc = '/assets/images/material-symbols_mail-outline.svg';
|
||||
@ -6,7 +10,6 @@ const phoneIconSrc = '/assets/images/line-md_phone.svg';
|
||||
const calendarIcon = '/assets/images/Duedate.svg';
|
||||
const clockIcon = '/assets/images/mingcute_time-line.svg';
|
||||
const nextIcon = '/assets/images/mdi_page-next-outline.svg';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
|
||||
const Field = ({ label, placeholder = '', type = 'text', value = '', onChange = () => {}, disabled = true }) => (
|
||||
<div>
|
||||
@ -95,11 +98,40 @@ const EstablishmentInfo = ({
|
||||
isComplete = false,
|
||||
}) => {
|
||||
const [showInfoCard, setShowInfoCard] = React.useState(true);
|
||||
const [products, setProducts] = React.useState([]);
|
||||
const [loadingProducts, setLoadingProducts] = React.useState(false);
|
||||
const [productsError, setProductsError] = React.useState('');
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
// Use a ref to persist the survey data across re-renders
|
||||
const surveyDataRef = React.useRef(null);
|
||||
|
||||
// Fetch establishment products
|
||||
React.useEffect(() => {
|
||||
const fetchProducts = async () => {
|
||||
const establishmentId = sessionStorage.getItem('establishment_id');
|
||||
if (!establishmentId) return;
|
||||
|
||||
setLoadingProducts(true);
|
||||
try {
|
||||
const response = await getEstablishmentProducts(establishmentId);
|
||||
if (response.status === 'success') {
|
||||
console.log('Products data:', response.data); // Log the products data
|
||||
setProducts(response.data || []);
|
||||
} else {
|
||||
setProductsError(response.message || 'Failed to load products');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching products:', error);
|
||||
setProductsError('Failed to load products. Please try again later.');
|
||||
} finally {
|
||||
setLoadingProducts(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchProducts();
|
||||
}, []);
|
||||
|
||||
const [surveyData, setSurveyData] = React.useState(() => {
|
||||
// Initialize from location state if available, otherwise use defaults
|
||||
if (location.state?.survey) {
|
||||
@ -280,7 +312,7 @@ const EstablishmentInfo = ({
|
||||
</div>
|
||||
)}
|
||||
<h2 className="text-xl font-semibold text-[#232528] mb-2">
|
||||
Industrial Production Index (IPI) Survey: {surveyData.quarter} {surveyData.year}
|
||||
IIP (Index of Industrial Production): {surveyData.quarter} {surveyData.year}
|
||||
</h2>
|
||||
|
||||
{/* Survey Information */}
|
||||
@ -309,7 +341,7 @@ const EstablishmentInfo = ({
|
||||
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-[#6C4527] mb-4">
|
||||
You're completing the <b>IPI Quarterly Survey</b> for <b>{surveyData.quarter} {surveyData.year}</b>. This step shows establishment details already registered with us. <b>Step 1 is read-only.</b> To make corrections, update them in <b>Profile → Edit Profile</b>, then return to this survey.
|
||||
You're completing the <b>IIP Quarterly Survey</b> for <b>{surveyData.quarter} {surveyData.year}</b>. This step shows establishment details already registered with us. <b>Step 1 is read-only.</b> To make corrections, update them in <b>Profile → Edit Profile</b>, then return to this survey.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
@ -365,54 +397,7 @@ const EstablishmentInfo = ({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Blue Info Box */}
|
||||
<div className="bg-[#E8F3FF] border border-[#2F80ED] text-[#0B4DA2] px-4 py-2 rounded-md mt-3 flex items-center gap-2 text-sm">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={2}
|
||||
stroke="#0B4DA2"
|
||||
className="w-5 h-5 flex-shrink-0"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
{/* <p>
|
||||
<span className="font-medium">Need to update details?</span> Go to{' '}
|
||||
<b>Profile → Edit Profile</b>. Changes saved there will appear{' '}
|
||||
<a href="#" className="underline font-medium text-[#0B4DA2] hover:text-[#063B82]">
|
||||
here
|
||||
</a>.
|
||||
</p> */}
|
||||
{/* <p className="text-sm text-[#043DFF] bg-[#E7F5FF]">
|
||||
<span className="font-medium">Need to update details?</span> Go to Profile →{' '}
|
||||
<a
|
||||
href={`/admin/configuration/profile/edit=${sessionStorage.getItem('establishment_id') || ''}`}
|
||||
className="underline font-medium text-[#043DFF] hover:text-[#063B82]"
|
||||
onClick={handleEditProfile}
|
||||
|
||||
>
|
||||
Edit Profile
|
||||
</a>
|
||||
. Changes saved there will appear here.
|
||||
</p> */}
|
||||
<p className="text-sm text-[#043DFF] bg-[#E7F5FF]">
|
||||
<span className="font-medium">Need to update details?</span> Go to Profile →{' '}
|
||||
<a
|
||||
href={`/admin/configuration/edit-profile/${sessionStorage.getItem('establishment_id') || ''}`}
|
||||
className="underline font-medium text-[#043DFF] hover:text-[#063B82]"
|
||||
onClick={handleEditProfile}
|
||||
>
|
||||
Edit Profile
|
||||
</a>
|
||||
|
||||
. Changes saved there will appear here.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<h2 className="text-lg font-semibold text-[#232528]">Step 1: Review Establishment Information</h2>
|
||||
{isComplete && (
|
||||
@ -532,6 +517,31 @@ const EstablishmentInfo = ({
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="mt-10">
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-4">Products</h3>
|
||||
{loadingProducts ? (
|
||||
<div className="flex justify-center py-4">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-[#92722A]" />
|
||||
</div>
|
||||
) : productsError ? (
|
||||
<div className="text-red-600 text-sm py-2">{productsError}</div>
|
||||
) : products.length > 0 ? (
|
||||
<Table
|
||||
headers={['Product Name', 'HS Code', 'Description']}
|
||||
rows={products.map(product => [
|
||||
product['product.product_name'] || 'N/A',
|
||||
product['product.hs_code'] || 'N/A',
|
||||
product['product.hs_description'] || 'N/A'
|
||||
])}
|
||||
columnWidths={['33.33%', '33.33%', '33.33%']}
|
||||
separated={true}
|
||||
className="w-full"
|
||||
/>
|
||||
) : (
|
||||
<div className="text-gray-500 text-sm py-2">No products found for this establishment.</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<div className="mt-6">
|
||||
{/* Employee Information */}
|
||||
<Card>
|
||||
@ -588,6 +598,109 @@ const EstablishmentInfo = ({
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* <Card className="mt-10">
|
||||
<h3 className="text-sm font-semibold text-gray-900 mb-4">Products</h3>
|
||||
{loadingProducts ? (
|
||||
<div className="flex justify-center py-4">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-[#92722A]" />
|
||||
</div>
|
||||
) : productsError ? (
|
||||
<div className="text-red-600 text-sm py-2">{productsError}</div>
|
||||
) : products.length > 0 ? (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Product Name
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
HS Code
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Unit
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
UOM
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{products.map((product, index) => (
|
||||
<tr key={product.id || index} className={index % 2 === 0 ? 'bg-white' : 'bg-gray-50'}>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{product['product.product_name'] || 'N/A'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{product['product.hs_code'] || 'N/A'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{product['product.unit.uom_short_name'] || 'N/A'}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{product['product.unit.uom'] || 'N/A'}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-gray-500 text-sm py-2">No products found for this establishment.</div>
|
||||
)}
|
||||
</Card> */}
|
||||
{/* Products Table */}
|
||||
|
||||
|
||||
{/* Blue Info Box */}
|
||||
<div className="bg-[#E8F3FF] border border-[#2F80ED] text-[#0B4DA2] px-4 py-2 rounded-md mt-3 flex items-center gap-2 text-sm">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={2}
|
||||
stroke="#0B4DA2"
|
||||
className="w-5 h-5 flex-shrink-0"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
{/* <p>
|
||||
<span className="font-medium">Need to update details?</span> Go to{' '}
|
||||
<b>Profile → Edit Profile</b>. Changes saved there will appear{' '}
|
||||
<a href="#" className="underline font-medium text-[#0B4DA2] hover:text-[#063B82]">
|
||||
here
|
||||
</a>.
|
||||
</p> */}
|
||||
{/* <p className="text-sm text-[#043DFF] bg-[#E7F5FF]">
|
||||
<span className="font-medium">Need to update details?</span> Go to Profile →{' '}
|
||||
<a
|
||||
href={`/admin/configuration/profile/edit=${sessionStorage.getItem('establishment_id') || ''}`}
|
||||
className="underline font-medium text-[#043DFF] hover:text-[#063B82]"
|
||||
onClick={handleEditProfile}
|
||||
|
||||
>
|
||||
Edit Profile
|
||||
</a>
|
||||
. Changes saved there will appear here.
|
||||
</p> */}
|
||||
<p className="text-sm text-[#043DFF] bg-[#E7F5FF]">
|
||||
<span className="font-medium">Need to update details?</span> Go to Profile →{' '}
|
||||
<a
|
||||
href={`/admin/configuration/edit-profile/${sessionStorage.getItem('establishment_id') || ''}`}
|
||||
className="underline font-medium text-[#043DFF] hover:text-[#063B82]"
|
||||
onClick={handleEditProfile}
|
||||
>
|
||||
Edit Profile
|
||||
</a>
|
||||
|
||||
. Changes saved there will appear here.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Footer actions */}
|
||||
<div className="mt-8 flex flex-col sm:flex-row justify-between gap-3">
|
||||
<button
|
||||
|
||||
@ -1108,7 +1108,7 @@ const location = useLocation();
|
||||
<div className="text-[#6C4527] space-y-2">
|
||||
<h3 className="text-base font-semibold mb-2">Why we collect monthly product data</h3>
|
||||
<p className="text-sm mb-4">
|
||||
This step collects <strong>monthly output quantity and production cost</strong> for each product you manufacture or process. The data inform the <strong>Industrial Production Index (IPI)</strong> and capacity-utilization statistics. Results are published <strong>only in aggregated form</strong> and your information is protected by national statistics confidentiality provisions.
|
||||
This step collects <strong>monthly output quantity and production cost</strong> for each product you manufacture or process. The data inform the <strong>Index of Industrial Production (IIP)</strong> and capacity-utilization statistics. Results are published <strong>only in aggregated form</strong> and your information is protected by national statistics confidentiality provisions.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-2 text-sm">
|
||||
|
||||
@ -70,7 +70,7 @@ const AdminUsers = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const pageSize = 10;
|
||||
// const pageSize = 10;
|
||||
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [selectedUser, setSelectedUser] = useState(null);
|
||||
@ -92,7 +92,8 @@ const AdminUsers = () => {
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [resettingPassword, setResettingPassword] = useState(false);
|
||||
const [resetErrors, setResetErrors] = useState({});
|
||||
|
||||
const [pageSize, setPageSize] = React.useState(10);
|
||||
|
||||
// Helper: show toast
|
||||
const showToast = (message, type = 'success') => {
|
||||
setToastData({ message, type });
|
||||
@ -655,6 +656,10 @@ const AdminUsers = () => {
|
||||
pageSize,
|
||||
totalItems: users.length,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
onPageSizeChange: (size) => {
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
}
|
||||
}}
|
||||
renderCell={(value, rowIndex, colIndex) => {
|
||||
if (colIndex === 4) {
|
||||
|
||||
@ -19,6 +19,7 @@ const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
||||
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
|
||||
const addIconSrc = '/assets/images/ic_baseline-plus.svg';
|
||||
const pencilActiveSrc = '/assets/images/PencilSimple.svg';
|
||||
const viewIconSrc = '/assets/images/Eye.svg';
|
||||
const pencilInactiveSrc = '/assets/images/pencilsimple-inactive.svg';
|
||||
const trashActiveSrc = '/assets/images/Trash-active.svg';
|
||||
const trashInactiveSrc = '/assets/images/Trash-Inactive.svg';
|
||||
@ -323,13 +324,13 @@ const validateCSVFile = (file, existingEstablishments = []) => {
|
||||
}
|
||||
|
||||
// Check Factory Name
|
||||
// if (factoryNameIndex >= 0 && !values[factoryNameIndex]) {
|
||||
// rowErrors.push({
|
||||
// column: 'Factory Name',
|
||||
// message: 'Factory Name is required',
|
||||
// value: ''
|
||||
// });
|
||||
// }
|
||||
if (factoryNameIndex >= 0 && !values[factoryNameIndex]) {
|
||||
rowErrors.push({
|
||||
column: 'Factory Name',
|
||||
message: 'Factory Name is required',
|
||||
value: ''
|
||||
});
|
||||
}
|
||||
|
||||
// Check Email
|
||||
if (emailIndex >= 0) {
|
||||
@ -414,6 +415,35 @@ const validateCSVFile = (file, existingEstablishments = []) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Log detailed validation errors to console
|
||||
console.group('CSV Validation Errors');
|
||||
if (duplicateErrors.length > 0) {
|
||||
console.group('Duplicate Errors');
|
||||
duplicateErrors.forEach((error, index) => {
|
||||
console.group(`Error ${index + 1} (Row ${error.row})`);
|
||||
console.log('Type:', error.isSystemDuplicate ? 'System Duplicate' : 'File Duplicate');
|
||||
console.log('Establishment ID:', error.value);
|
||||
console.log('Message:', error.message);
|
||||
if (error.duplicateRows) {
|
||||
console.log('Duplicate Rows:', error.duplicateRows.join(', '));
|
||||
}
|
||||
console.groupEnd();
|
||||
});
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
if (otherErrors.length > 0) {
|
||||
console.group('Validation Errors');
|
||||
otherErrors.forEach((error, index) => {
|
||||
console.group(`Error ${index + 1} (Row ${error.row})`);
|
||||
console.log('Column:', error.column);
|
||||
console.log('Value:', error.value);
|
||||
console.log('Message:', error.message);
|
||||
console.groupEnd();
|
||||
});
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
// Customize message based on error types
|
||||
if (duplicateErrors.length > 0 && otherErrors.length === 0) {
|
||||
if (duplicateErrors.some(e => e.isSystemDuplicate)) {
|
||||
@ -499,6 +529,8 @@ const CompanyProfile = () => {
|
||||
const [isRefreshing, setIsRefreshing] = React.useState(false);
|
||||
const [isUpdating, setIsUpdating] = React.useState(false);
|
||||
const [formSubmitted, setFormSubmitted] = React.useState(false);
|
||||
const [isViewMode, setIsViewMode] = React.useState(false);
|
||||
|
||||
|
||||
// Import CSV state
|
||||
const [importModalOpen, setImportModalOpen] = React.useState(false);
|
||||
@ -510,6 +542,10 @@ const CompanyProfile = () => {
|
||||
const [toastData, setToastData] = React.useState(null);
|
||||
const [validationResults, setValidationResults] = React.useState(null);
|
||||
|
||||
|
||||
const filteredAvailableProducts = availableProducts.filter(
|
||||
(product) => !selectedProducts.some((sp) => sp.id === product.id)
|
||||
);
|
||||
// Load current user from session storage
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
@ -526,6 +562,7 @@ const CompanyProfile = () => {
|
||||
const handleCloseModal = () => {
|
||||
setModalMode(null);
|
||||
setEditingRow(null);
|
||||
setIsViewMode(false);
|
||||
setForm(createEmptyProfile());
|
||||
setActiveStep(0);
|
||||
setFieldErrors({});
|
||||
@ -860,61 +897,79 @@ const CompanyProfile = () => {
|
||||
</div>
|
||||
);
|
||||
case 1:
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h4 className="text-[16px] font-semibold text-[#232528]">Identification Particulars</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<TextField
|
||||
label="Permanent Factory Code"
|
||||
value={form.permanentFactoryCode}
|
||||
onChange={handleFormChange('permanentFactoryCode')}
|
||||
placeholder="Enter Factory Code"
|
||||
width="100%"
|
||||
// required
|
||||
// error={fieldErrors.permanentFactoryCode}
|
||||
/>
|
||||
<TextField
|
||||
label="Unique License Number"
|
||||
value={form.uniqueLicenseNumber}
|
||||
onChange={handleFormChange('uniqueLicenseNumber')}
|
||||
placeholder="Enter License Number"
|
||||
width="100%"
|
||||
required
|
||||
error={fieldErrors.uniqueLicenseNumber}
|
||||
/>
|
||||
<TextField
|
||||
label="Industry Code (Business Register)"
|
||||
value={form.industryCodeBusiness}
|
||||
onChange={handleFormChange('industryCodeBusiness')}
|
||||
placeholder="Enter Code"
|
||||
width="100%"
|
||||
// required
|
||||
// error={fieldErrors.industryCodeBusiness}
|
||||
/>
|
||||
<TextField
|
||||
label="Industry Code (Current Production)"
|
||||
value={form.industryCodeProduction}
|
||||
onChange={handleFormChange('industryCodeProduction')}
|
||||
placeholder="Enter Code"
|
||||
width="100%"
|
||||
// required
|
||||
// error={fieldErrors.industryCodeProduction}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<TextField
|
||||
label="Description"
|
||||
value={form.industryDescription}
|
||||
onChange={handleFormChange('industryDescription')}
|
||||
placeholder="Enter Description"
|
||||
width="100%"
|
||||
style={{ height: '72px' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h4 className="text-[16px] font-semibold text-[#232528]">
|
||||
Identification Particulars
|
||||
</h4>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
|
||||
<TextField
|
||||
label="Permanent Factory Code"
|
||||
value={form.permanentFactoryCode}
|
||||
onChange={handleFormChange('permanentFactoryCode')}
|
||||
placeholder="Enter Factory Code"
|
||||
width="100%"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Unique License Number"
|
||||
value={form.uniqueLicenseNumber}
|
||||
onChange={handleFormChange('uniqueLicenseNumber')}
|
||||
placeholder="Enter License Number"
|
||||
width="100%"
|
||||
required
|
||||
error={fieldErrors.uniqueLicenseNumber}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Industry Code (Business Register)"
|
||||
value={form.industryCodeBusiness}
|
||||
onChange={handleFormChange('industryCodeBusiness')}
|
||||
placeholder="Enter Code"
|
||||
width="100%"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Industry Code (Current Production)"
|
||||
value={form.industryCodeProduction}
|
||||
onChange={handleFormChange('industryCodeProduction')}
|
||||
placeholder="Enter Code"
|
||||
width="100%"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Description Field */}
|
||||
<div className="mt-4">
|
||||
<TextField
|
||||
label="Description"
|
||||
value={form.industryDescription}
|
||||
onChange={handleFormChange('industryDescription')}
|
||||
placeholder="Enter Description"
|
||||
width="100%"
|
||||
style={{ height: '72px' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 🟡 Show Remarks ONLY IF Current Production code is filled AND different from Business Register */}
|
||||
{form.industryCodeProduction && form.industryCodeBusiness !== form.industryCodeProduction && (
|
||||
<div className="mt-4">
|
||||
<TextField
|
||||
label="Remarks"
|
||||
value={form.remarks}
|
||||
onChange={handleFormChange('remarks')}
|
||||
placeholder="Add Remarks"
|
||||
width="100%"
|
||||
style={{ height: '72px' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
@ -1007,7 +1062,7 @@ const CompanyProfile = () => {
|
||||
onChange={handleFormChange('contactPersonName')}
|
||||
placeholder="Enter Name"
|
||||
width="100%"
|
||||
// error={fieldErrors.contactPersonName}
|
||||
error={fieldErrors.contactPersonName}
|
||||
/>
|
||||
<TextField
|
||||
label="Contact Person Designation"
|
||||
@ -1143,26 +1198,26 @@ const CompanyProfile = () => {
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-[#92722A]"></div>
|
||||
</div>
|
||||
) : availableProducts.length > 0 ? (
|
||||
<ul className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto">
|
||||
{availableProducts.map((product) => (
|
||||
<li key={product.id} className="p-3 hover:bg-[#F9FAFB] flex justify-between items-center">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
{product.hsCode || product.hs_code || ''}
|
||||
{(product.hsCode || product.hs_code) && (product.productName || product.label || product.product_name) ? ' - ' : ''}
|
||||
{product.productName || product.label || product.product_name || ''}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleAddProduct(product)}
|
||||
className="text-[#92722A] hover:text-[#7A5F1E] text-sm font-medium"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto">
|
||||
{filteredAvailableProducts.map((product) => (
|
||||
<li key={product.id} className="p-3 hover:bg-[#F9FAFB] flex justify-between items-center">
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
{product.hsCode || product.hs_code || ''}
|
||||
{(product.hsCode || product.hs_code) && (product.productName || product.label || product.product_name) ? ' - ' : ''}
|
||||
{product.productName || product.label || product.product_name || ''}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleAddProduct(product)}
|
||||
className="text-[#92722A] hover:text-[#7A5F1E] text-sm font-medium"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
) : (
|
||||
<div className="p-4 text-center text-sm text-[#6B7280]">
|
||||
{productSearchTerm ? 'No matching products found' : 'No products available'}
|
||||
@ -1257,6 +1312,8 @@ const CompanyProfile = () => {
|
||||
!selectedProducts.some(sp => sp.id === p.id || sp.product_id === p.product_id)
|
||||
);
|
||||
setAvailableProducts(available);
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading products:', error);
|
||||
@ -1433,53 +1490,112 @@ const CompanyProfile = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const displayedRows = filteredProfiles.map((item) => {
|
||||
return [
|
||||
item.establishmentName,
|
||||
item.userProfileName || item.contactPersonName || item.createdBy || '',
|
||||
item.emirate,
|
||||
item.isicCode,
|
||||
item.establishmentId,
|
||||
item.productCount ?? '0',
|
||||
item.totalEmployees,
|
||||
item.createdBy,
|
||||
item.createdOn,
|
||||
item.updated_at ? formatDate(item.updated_at) : (item.lastUpdated || '-'),
|
||||
renderStatusBadge(item.status),
|
||||
(
|
||||
<div className="flex items-center gap-2" key={`actions-${item.establishmentId}`}>
|
||||
<button
|
||||
type="button"
|
||||
className="h-[24px] w-[24px] grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||
title="Edit"
|
||||
aria-label="Edit profile"
|
||||
onClick={() => handleEdit(item.apiId ?? item.establishmentId)}
|
||||
>
|
||||
<img
|
||||
src={pencilActiveSrc}
|
||||
alt="Edit"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="h-[20px] w-[40px] grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||
title={item.status === 'Active' ? 'Deactivate' : 'Activate'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleToggleStatus(item.apiId, item.status === 'Active');
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={item.status === 'Active' ? activeToggleSrc : inactiveToggleSrc}
|
||||
alt={item.status === 'Active' ? 'Active' : 'Inactive'}
|
||||
className="h-[20px] w-[40px] object-contain"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
];
|
||||
});
|
||||
const displayedRows = filteredProfiles.map((item) => {
|
||||
return [
|
||||
// Establishment Name
|
||||
item.establishmentName || "-",
|
||||
|
||||
// Contact Name (userProfileName → contactPersonName → createdBy → "-")
|
||||
item.userProfileName || item.contactPersonName || "-",
|
||||
|
||||
// Emirate
|
||||
item.emirate || "-",
|
||||
|
||||
// ISIC Code
|
||||
item.isicCode || "-",
|
||||
|
||||
// Establishment ID
|
||||
item.establishmentId || "-",
|
||||
|
||||
// Product Count
|
||||
item.productCount ?? "0",
|
||||
|
||||
// Total Employees
|
||||
item.totalEmployees || "-",
|
||||
|
||||
// Created By
|
||||
item.createdBy || "-",
|
||||
|
||||
// Created On
|
||||
item.createdOn || "-",
|
||||
|
||||
// Updated On (formatted or fallback)
|
||||
item.updated_at
|
||||
? formatDate(item.updated_at)
|
||||
: (item.lastUpdated || "-"),
|
||||
|
||||
// Status
|
||||
renderStatusBadge(item.status),
|
||||
|
||||
// Action Buttons
|
||||
(
|
||||
<div
|
||||
className="flex items-center gap-2"
|
||||
key={`actions-${item.establishmentId}`}
|
||||
>
|
||||
{/* Edit */}
|
||||
<button
|
||||
type="button"
|
||||
className="h-[24px] w-[24px] grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||
title="Edit"
|
||||
aria-label="Edit profile"
|
||||
onClick={() =>
|
||||
handleEdit(item.apiId ?? item.establishmentId)
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={pencilActiveSrc}
|
||||
alt="Edit"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
</button>
|
||||
|
||||
{/* View */}
|
||||
<button
|
||||
type="button"
|
||||
className="h-[24px] w-[24px] grid place-items-center rounded hover:bg-gray-50 cursor-pointer ml-1"
|
||||
title="View"
|
||||
aria-label="View profile"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleView(item.apiId ?? item.establishmentId);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={viewIconSrc}
|
||||
alt="View"
|
||||
className="h-[24px] w-[24px]"
|
||||
/>
|
||||
</button>
|
||||
|
||||
{/* Toggle Status */}
|
||||
<button
|
||||
type="button"
|
||||
className="h-[20px] w-[40px] grid place-items-center rounded hover:bg-gray-50 cursor-pointer"
|
||||
title={item.status === "Active" ? "Deactivate" : "Activate"}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleToggleStatus(
|
||||
item.apiId,
|
||||
item.status === "Active"
|
||||
);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={
|
||||
item.status === "Active"
|
||||
? activeToggleSrc
|
||||
: inactiveToggleSrc
|
||||
}
|
||||
alt={item.status === "Active" ? "Active" : "Inactive"}
|
||||
className="h-[20px] w-[40px] object-contain"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
const handle = setTimeout(() => {
|
||||
@ -1670,13 +1786,13 @@ const CompanyProfile = () => {
|
||||
|
||||
let creatorName = '-';
|
||||
if (currentUser && item?.created_by && currentUser.id === item.created_by) {
|
||||
const name = currentUser.name || currentUser.username || `User ${item.created_by}`;
|
||||
const name = currentUser.name || currentUser.username || ` ${item.created_by_name}`;
|
||||
creatorName = name.replace(/([a-zA-Z])([A-Z])/g, '$1 $2');
|
||||
} else if (item?.created_by) {
|
||||
const name = item?.created_by_name ||
|
||||
item?.created_by_user?.name ||
|
||||
item?.creator?.name ||
|
||||
`User ${item.created_by}`;
|
||||
` ${item.created_by_name}`;
|
||||
creatorName = name.replace(/([a-zA-Z])([A-Z])/g, '$1 $2');
|
||||
}
|
||||
|
||||
@ -2000,6 +2116,24 @@ const CompanyProfile = () => {
|
||||
}
|
||||
};
|
||||
|
||||
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 handleDelete = (establishmentId) => {
|
||||
if (!establishmentId) return;
|
||||
const originalIndex = profiles.findIndex((item) => {
|
||||
@ -2798,7 +2932,7 @@ const handleImport = async () => {
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative min-w-[150px]">
|
||||
<div className="relative min-w-[100px] ">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search By Establishment Name"
|
||||
@ -2837,7 +2971,7 @@ const handleImport = async () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={openImportModal}
|
||||
className="h-10 px-2 rounded-[6px] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 bg-[#F7F7F7] text-[#232528] hover:bg-gray-50"
|
||||
className="h-10 px-2 rounded-[6px] border border-[#C3C6CB] text-sm inline-flex items-center gap-1 bg-[#F7F7F7] text-[#232528] hover:bg-gray-50"
|
||||
>
|
||||
<img src={uploadImportIconSrc} alt="Import" className="h-5 w-5" />
|
||||
<span className="font-medium">Import CSV</span>
|
||||
|
||||
@ -92,6 +92,7 @@ const EditCompanyProfile = () => {
|
||||
const [contactCityOptions, setContactCityOptions] = useState([]);
|
||||
const [availableProducts, setAvailableProducts] = useState([]);
|
||||
const [selectedProducts, setSelectedProducts] = useState([]);
|
||||
const [newlyAddedProductIds, setNewlyAddedProductIds] = useState(new Set());
|
||||
const [productSearchTerm, setProductSearchTerm] = useState('');
|
||||
const [isLoadingProducts, setIsLoadingProducts] = useState(false);
|
||||
const [productError, setProductError] = useState("");
|
||||
@ -288,6 +289,9 @@ const EditCompanyProfile = () => {
|
||||
const updated = [...prev, product];
|
||||
return updated;
|
||||
});
|
||||
|
||||
// Mark this product as newly added
|
||||
setNewlyAddedProductIds(prev => new Set([...prev, product.value]));
|
||||
|
||||
// Remove from available products
|
||||
setAvailableProducts(prev => prev.filter(p => p.value !== product.value));
|
||||
@ -298,6 +302,10 @@ const EditCompanyProfile = () => {
|
||||
|
||||
// Remove product handler
|
||||
const handleRemoveProduct = (product) => {
|
||||
// Only allow removing newly added products
|
||||
if (!newlyAddedProductIds.has(product.value)) {
|
||||
return;
|
||||
}
|
||||
setSelectedProducts(prev => prev.filter(p => p.value !== product.value));
|
||||
|
||||
// Add back to available products if not already there and matches search
|
||||
@ -408,11 +416,14 @@ const EditCompanyProfile = () => {
|
||||
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'
|
||||
product_name: productData.product_name || productData.name || 'Unnamed Product',
|
||||
isExisting: true // Mark as existing product
|
||||
};
|
||||
});
|
||||
|
||||
setSelectedProducts(formattedSelectedProducts);
|
||||
// Initialize newly added products as empty since we're loading existing data
|
||||
setNewlyAddedProductIds(new Set());
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@ -850,22 +861,33 @@ const EditCompanyProfile = () => {
|
||||
<div className="p-4">
|
||||
{selectedProducts.length > 0 ? (
|
||||
<ul className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto">
|
||||
{selectedProducts.map((product) => (
|
||||
<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}
|
||||
{selectedProducts.map((product) => {
|
||||
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>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveProduct(product)}
|
||||
className="text-[#EF4444] hover:text-[#DC2626] text-sm font-medium"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
{isNewlyAdded && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveProduct(product)}
|
||||
className="text-[#EF4444] hover:text-[#DC2626] text-sm font-medium"
|
||||
title="Remove product"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="p-4 text-center text-sm text-[#6B7280] border border-[#E5E7EB] rounded-md">
|
||||
|
||||
@ -350,7 +350,7 @@ const IsicHsCodes = () => {
|
||||
const [deletingRow, setDeletingRow] = React.useState(null);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
|
||||
const [currentPage, setCurrentPage] = React.useState(1);
|
||||
const pageSize = 10;
|
||||
const [pageSize, setPageSize] = React.useState(10);
|
||||
const [modalMode, setModalMode] = React.useState(null);
|
||||
const [form, setForm] = React.useState(createEmptyCodeForm());
|
||||
const [unitOptions, setUnitOptions] = React.useState([]);
|
||||
@ -484,6 +484,7 @@ const IsicHsCodes = () => {
|
||||
const headers = [
|
||||
'HS Code',
|
||||
'Product Name',
|
||||
'Description',
|
||||
'Unit',
|
||||
'Establishment Mapped',
|
||||
'Created By',
|
||||
@ -500,7 +501,8 @@ const IsicHsCodes = () => {
|
||||
const filtered = rowsData.filter(
|
||||
(item) =>
|
||||
item.code.toLowerCase().includes(lowercasedSearch) ||
|
||||
item.product.toLowerCase().includes(lowercasedSearch)
|
||||
item.product.toLowerCase().includes(lowercasedSearch) ||
|
||||
item.description.toLowerCase().includes(lowercasedSearch)
|
||||
);
|
||||
setFilteredData(filtered);
|
||||
}
|
||||
@ -526,6 +528,7 @@ const IsicHsCodes = () => {
|
||||
return [
|
||||
item.code,
|
||||
item.product,
|
||||
item.description || '-',
|
||||
item.unit,
|
||||
item.estimatedMapped,
|
||||
displayName,
|
||||
@ -550,9 +553,9 @@ const IsicHsCodes = () => {
|
||||
setForm({
|
||||
code: product.hs_code || '',
|
||||
product: product.product_name || '',
|
||||
description: product.hs_description || '',
|
||||
unit: product.unit_id ? String(product.unit_id) : '',
|
||||
status: product.is_active ? 'Active' : 'Inactive',
|
||||
description: product.hs_description || '',
|
||||
createdBy: selected.createdBy,
|
||||
createdOn: selected.createdOn || new Date(product.created_at).toLocaleDateString('en-GB'),
|
||||
updated: selected.updated || (product.updated_at ? new Date(product.updated_at).toLocaleDateString('en-GB') : '')
|
||||
@ -599,9 +602,9 @@ const IsicHsCodes = () => {
|
||||
id: productData.id,
|
||||
code: productData.hs_code || '',
|
||||
product: productData.product_name || '',
|
||||
description: productData.hs_description || '',
|
||||
unit: productData.unit_id ? String(productData.unit_id) : '',
|
||||
status: productData.is_active ? 'Active' : 'Inactive',
|
||||
description: productData.hs_description || ''
|
||||
};
|
||||
|
||||
console.log('Mapped form data:', formData);
|
||||
@ -801,6 +804,7 @@ const IsicHsCodes = () => {
|
||||
return [
|
||||
item.code,
|
||||
item.product,
|
||||
item.description || '-',
|
||||
item.unit,
|
||||
item.estimatedMapped,
|
||||
item.createdBy,
|
||||
@ -854,6 +858,7 @@ const IsicHsCodes = () => {
|
||||
const productData = {
|
||||
hs_code: form.code,
|
||||
product_name: form.product,
|
||||
hs_description: form.description || '',
|
||||
unit_id: form.unit ? parseInt(form.unit) : null,
|
||||
// Preserve the original created_by value for updates, or use current user for new records
|
||||
created_by: modalMode === 'edit' ? (existingProduct?.created_by || userProfile?.id) : (userProfile?.id || null)
|
||||
@ -890,6 +895,7 @@ const IsicHsCodes = () => {
|
||||
// Only update these specific fields
|
||||
code: form.code,
|
||||
product: form.product,
|
||||
description: form.description || '',
|
||||
unit: selectedUnit ? selectedUnit.label : existingProduct?.unit || 'N/A',
|
||||
unit_id: form.unit ? parseInt(form.unit) : existingProduct?.unit_id || null,
|
||||
updated: new Date().toLocaleDateString('en-GB'),
|
||||
@ -940,6 +946,7 @@ const IsicHsCodes = () => {
|
||||
id: response.data?.id || Date.now(),
|
||||
code: form.code,
|
||||
product: form.product,
|
||||
description: form.description || '',
|
||||
unit: selectedUnit ? selectedUnit.label : 'N/A',
|
||||
unit_id: form.unit ? parseInt(form.unit) : null,
|
||||
estimatedMapped: 0,
|
||||
@ -1031,6 +1038,7 @@ const IsicHsCodes = () => {
|
||||
id: product.id,
|
||||
code: product.hs_code || 'N/A',
|
||||
product: product.product_name || 'N/A',
|
||||
description: product.hs_description || '',
|
||||
unit: product.unit?.uom || 'N/A',
|
||||
estimatedMapped: 0,
|
||||
createdBy: creatorName,
|
||||
@ -1188,17 +1196,17 @@ const IsicHsCodes = () => {
|
||||
headers={headers}
|
||||
rows={rows}
|
||||
renderCell={(value, rowIndex, colIndex) => {
|
||||
// Handle product name column (index 1) with text wrapping
|
||||
if (colIndex === 1) {
|
||||
// Handle product name and description columns with text wrapping
|
||||
if (colIndex === 1 || colIndex === 2) {
|
||||
return (
|
||||
<div className="max-w-[300px] whitespace-normal break-words">
|
||||
<div className="max-w-[200px] whitespace-normal break-words">
|
||||
{value}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Handle action buttons column (index 6)
|
||||
if (colIndex === 6) {
|
||||
// Handle action buttons column (index 7)
|
||||
if (colIndex === 7) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
@ -1257,6 +1265,11 @@ const IsicHsCodes = () => {
|
||||
onPageChange: setCurrentPage,
|
||||
pageSize,
|
||||
totalItems: filteredData.length,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
onPageSizeChange: (size) => {
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@ -1394,8 +1407,10 @@ const IsicHsCodes = () => {
|
||||
placeholder="Enter Product Name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 mt-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[#374151] mb-1">
|
||||
Unit <span className="text-[#EF4444]">*</span>
|
||||
@ -1424,7 +1439,21 @@ const IsicHsCodes = () => {
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[#374151] mb-1">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
value={form.description || ''}
|
||||
onChange={handleFormChange("description")}
|
||||
disabled={modalMode === "view"}
|
||||
rows={1}
|
||||
className="w-full px-3 py-2 text-sm border border-[#CBA344] rounded-md focus:outline-none focus:ring-1 focus:ring-[#92722A] focus:border-[#92722A] min-h-[38px]"
|
||||
placeholder="Enter product description"
|
||||
style={{ resize: 'vertical' }}
|
||||
/>
|
||||
</div>
|
||||
{/* <div>
|
||||
<label className="block text-sm font-medium text-[#374151] mb-1">
|
||||
Status <span className="text-[#EF4444]">*</span>
|
||||
|
||||
@ -88,8 +88,9 @@ const UnitMaster = () => {
|
||||
const [importLoading, setImportLoading] = useState(false);
|
||||
const [importError, setImportError] = useState('');
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
const pageSize = 10;
|
||||
const [pageSize, setPageSize] = React.useState(10);
|
||||
|
||||
// const pageSize = 10;
|
||||
|
||||
// Fetch units on component mount
|
||||
useEffect(() => {
|
||||
@ -779,6 +780,11 @@ const UnitMaster = () => {
|
||||
onPageChange: setCurrentPage,
|
||||
pageSize,
|
||||
totalItems: filteredUnits.length,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
onPageSizeChange: (size) => {
|
||||
setPageSize(size);
|
||||
setCurrentPage(1);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -13,7 +13,7 @@ const Banner = () => {
|
||||
|
||||
{/* Title */}
|
||||
<h1 className="text-2xl md:text-3xl font-semibold text-gray-800 mb-6">
|
||||
Industrial Production Index (IPI) Survey – UAE
|
||||
IIP (Index of Industrial Production) – UAE
|
||||
</h1>
|
||||
|
||||
{/* Description */}
|
||||
|
||||
@ -6,10 +6,10 @@ const Industrial = () => {
|
||||
{/* Left Side: Content */}
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-4">
|
||||
What is the Industrial Production Index (IPI)?
|
||||
What is the Index of Industrial Production (IIP)?
|
||||
</h2>
|
||||
<p className="text-gray-700 leading-relaxed text-justify">
|
||||
The Industrial Production Index (IPI) measures changes in the volume
|
||||
The Index of Industrial Production (IIP) measures changes in the volume
|
||||
of industrial output over time. It tracks production across
|
||||
manufacturing activities classified by ISIC and is a key official
|
||||
indicator used to monitor economic activity and inform policy.
|
||||
|
||||
@ -76,7 +76,7 @@ const Navbar = () => {
|
||||
{/* Desktop Menu */}
|
||||
<nav className="hidden md:flex items-center space-x-8 text-[15px] font-medium">
|
||||
<button onClick={() => handleClick("about-ipi")} className={linkClass("about-ipi")}>
|
||||
About IPI
|
||||
About IIP
|
||||
</button>
|
||||
<button onClick={() => handleClick("how-it-works")} className={linkClass("how-it-works")}>
|
||||
How it works
|
||||
@ -116,7 +116,7 @@ const Navbar = () => {
|
||||
onClick={() => handleClick(id)}
|
||||
className={`${linkClass(id)} block w-full text-left`}
|
||||
>
|
||||
{id === "about-ipi" && "About IPI"}
|
||||
{id === "about-ipi" && "About IIP"}
|
||||
{id === "how-it-works" && "How it works"}
|
||||
{id === "eligibility" && "Eligibility"}
|
||||
{id === "faqs" && "FAQs"}
|
||||
|
||||
@ -5,7 +5,7 @@ const Survey = () => {
|
||||
<section className="mx-auto px-6 md:px-10 lg:px-24 py-12">
|
||||
{/* Heading */}
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-4">
|
||||
Who should complete the IPI <span className="text-gray-900">survey</span>?
|
||||
Who should complete the IIP <span className="text-gray-900">survey</span>?
|
||||
</h2>
|
||||
|
||||
{/* Content */}
|
||||
|
||||
@ -318,7 +318,7 @@ const Login = () => {
|
||||
<img src={logoSrc} alt="FCSC Logo" className="mx-auto h-16 w-auto" />
|
||||
</div>
|
||||
<h1 className="text-[17px] font-semibold text-[#1F2937] mt-0">
|
||||
Industrial Production Index (IPI) Survey Portal
|
||||
IIP (Index of Industrial Production)
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user