- {quarterPeriods?.forecast_month?.forecast_period_three } Cost (AED)*
+ {quarterPeriods?.forecast_month?.forecast_period_three } Cost ( )*
{
+ React.useEffect(() => {
+ const timer = setTimeout(() => {
+ onClose();
+ }, 3000);
+
+ return () => clearTimeout(timer);
+ }, [onClose]);
+
+ const getToastStyles = () => {
+ switch (type) {
+ case 'success':
+ return 'bg-green-50 border-green-200 text-green-800';
+ case 'error':
+ return 'bg-red-50 border-red-200 text-red-800';
+ case 'warning':
+ return 'bg-yellow-50 border-yellow-200 text-yellow-800';
+ default:
+ return 'bg-blue-50 border-blue-200 text-blue-800';
+ }
+ };
+
+ const getIcon = () => {
+ switch (type) {
+ case 'success':
+ return '';
+ case 'error':
+ return '';
+ case 'warning':
+ return '';
+ default:
+ return '';
+ }
+ };
+
+ return (
+
+ {/* {getIcon()} */}
+ {message}
+
+ ✕
+
+
+ );
+};
const PASSWORD_POLICY = {
@@ -210,7 +263,27 @@ const CompanyProfile = () => {
const [isUpdating, setIsUpdating] = React.useState(false);
const [formSubmitted, setFormSubmitted] = React.useState(false);
+ // Import CSV state
+ const [importModalOpen, setImportModalOpen] = React.useState(false);
+ const [file, setFile] = React.useState(null);
+ const [dragActive, setDragActive] = React.useState(false);
+ const [importLoading, setImportLoading] = React.useState(false);
+ const [importError, setImportError] = React.useState('');
+ const fileInputRef = React.useRef(null);
+ const [toastData, setToastData] = React.useState(null);
+ // Load current user from session storage
+ React.useEffect(() => {
+ try {
+ const profile = sessionStorage.getItem('user_profile');
+ if (profile) {
+ const userData = JSON.parse(profile);
+ setCurrentUser(userData);
+ }
+ } catch (error) {
+ console.error('Error parsing user profile:', error);
+ }
+ }, []);
const handleCloseModal = () => {
setModalMode(null);
setEditingRow(null);
@@ -340,6 +413,53 @@ const CompanyProfile = () => {
[form, requiredFieldsByStep]
);
+ // Fixed Import CSV Functions
+ const openImportModal = () => {
+ setImportModalOpen(true);
+ setFile(null);
+ setImportError('');
+ setDragActive(false);
+ };
+
+ const closeImportModal = () => {
+ setImportModalOpen(false);
+ setFile(null);
+ setImportError('');
+ setDragActive(false);
+ };
+
+ const handleDrag = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (e.type === "dragenter" || e.type === "dragover") {
+ setDragActive(true);
+ } else if (e.type === "dragleave") {
+ setDragActive(false);
+ }
+ };
+
+ const downloadSampleCSV = () => {
+ const sampleData = [
+ ['Establishment Id', 'Factory Name', 'Email', 'Emirate', 'Total Employment', 'HS Code 1'],
+ ['EST001', 'ABC Manufacturing', 'contact@abcmanufacturing.com', 'Dubai', '150', '1234567890'],
+ ['EST002', 'XYZ Industries', 'info@xyzindustries.com', 'Sharjah', '200', '9876543210'],
+ ['EST003', 'Global Textiles Ltd', 'sales@globaltextiles.com', 'Abu Dhabi', '85', '4567890123']
+ ];
+ const csvContent = sampleData.map(row =>
+ row.map(field => `"${field}"`).join(',')
+ ).join('\n');
+
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.setAttribute('download', 'company-profile-template.csv');
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ };
+
// Add this useEffect to load the current user from session storage
const currentUserId = React.useMemo(() => {
try {
@@ -1287,9 +1407,7 @@ const loadProducts = async () => {
item.emirate,
item.isicCode,
item.establishmentId,
- item.products && item.products.length > 0
- ? item.products.map(p => p.hsCode ? `${p.hsCode} - ${p.productName || p.label || p.product_name || 'N/A'}` : (p.productName || p.product_name || 'N/A')).filter(Boolean).join(', ')
- : '-',
+ item.productCount ?? '0',
item.totalEmployees,
item.createdBy,
item.createdOn,
@@ -1570,7 +1688,8 @@ const corporateFieldKeys = Object.values(contactToCorporateMap);
contactCityTownId: item?.establishment_city_town_id ? String(item.establishment_city_town_id) : '',
contactEmirate: establishmentEmirateName,
contactEmirateId: item?.establishment_emirate_id ? String(item.establishment_emirate_id) : '',
- products: base.products,
+ products: Array.isArray(item?.products) ? item.products : [],
+ productCount: item?.product_count ?? 0,
totalEmployees: totalEmployees !== '' && totalEmployees !== null ? String(totalEmployees) : '',
status: item?.is_active === false ? 'Inactive' : 'Active',
isActive: item?.is_active !== false,
@@ -2591,6 +2710,235 @@ const requiredFields = [
return profiles[deletingRow];
}, [deletingRow, profiles]);
+
+ const handleImport = async () => {
+ if (!file) {
+ const errorMsg = "Please select a CSV file to import.";
+ setImportError(errorMsg);
+ setToastData({
+ message: errorMsg,
+ type: "error",
+ });
+ throw new Error(errorMsg);
+ }
+
+ if (file.size > 10 * 1024 * 1024) {
+ const errorMsg = "File size too large. Please select a file smaller than 10MB.";
+ setImportError(errorMsg);
+ setToastData({
+ message: errorMsg,
+ type: "error",
+ });
+ throw new Error(errorMsg);
+ }
+
+ try {
+ setImportLoading(true);
+ setImportError("");
+
+ // Create FormData with proper file field
+ const formData = new FormData();
+ formData.append("file", file);
+
+ // Debug: Check FormData contents
+ console.log('FormData entries before upload:');
+ for (let pair of formData.entries()) {
+ console.log(pair[0] + ': ', pair[1]);
+ }
+
+ let response;
+ let successMessage = "Company profiles imported successfully!";
+
+ try {
+ console.log('Attempting first upload...');
+ response = await uploadCompanyProfileCSV(formData);
+ successMessage = response.message || successMessage;
+ } catch (firstError) {
+ console.log('First upload attempt failed, trying alternative:', firstError);
+ try {
+ console.log('Attempting alternative upload...');
+ response = await uploadCompanyProfileCSVAlternative(formData);
+ successMessage = response.message || successMessage;
+ } catch (secondError) {
+ console.log('Alternative upload also failed:', secondError);
+
+ // Extract detailed error message from backend response
+ let detailedErrorMessage = "Upload failed. Please try again.";
+
+ // Check for backend error response structure - prioritize backend message
+ if (secondError.response?.data?.message) {
+ detailedErrorMessage = secondError.response.data.message;
+ }
+ // If no specific backend message, check for common error patterns
+ else if (secondError.message) {
+ // Remove "Request failed with status code 500" type messages
+ if (secondError.message.includes("Request failed with status code")) {
+ // Check if there's any additional data with the error
+ if (secondError.response?.data) {
+ // Try to extract meaningful message from response data
+ const responseData = secondError.response.data;
+ if (typeof responseData === 'string' && responseData.includes("Duplicate Establishment IDs")) {
+ detailedErrorMessage = responseData;
+ } else if (responseData.error) {
+ detailedErrorMessage = responseData.error;
+ } else if (responseData.details) {
+ detailedErrorMessage = responseData.details;
+ } else {
+ // Generic server error without specific details
+ detailedErrorMessage = "Data already exists in the system. Please remove or update the duplicate entries and try again.";
+}
+
+ } else {
+ // Generic server error without specific details
+ detailedErrorMessage = "Server error occurred during upload. Please try again.";
+ }
+ } else {
+ // For other non-status code errors, use the original message
+ detailedErrorMessage = secondError.message;
+ }
+ }
+
+ // Check for specific error patterns and provide user-friendly messages
+ if (detailedErrorMessage.includes("No file uploaded") ||
+ detailedErrorMessage.includes("No file found")) {
+ detailedErrorMessage = "Please select a valid CSV file to upload.";
+ } else if (detailedErrorMessage.includes("token") ||
+ detailedErrorMessage.includes("auth") ||
+ detailedErrorMessage.includes("Authentication")) {
+ detailedErrorMessage = "Authentication failed. Please check your login credentials.";
+ } else if (detailedErrorMessage.includes("400") ||
+ detailedErrorMessage.includes("Bad Request")) {
+ detailedErrorMessage = "Invalid file format or missing required data. Please check your CSV file.";
+ } else if (detailedErrorMessage.includes("Duplicate Establishment IDs") ||
+ detailedErrorMessage.includes("duplicate") ||
+ detailedErrorMessage.includes("already exist")) {
+ // Keep the original backend message for duplicate IDs
+ detailedErrorMessage = "Duplicate Establishment IDs found (in file or already exist in system). Resolve and re-upload.";
+ }
+
+ // Throw the detailed error with backend message
+ throw new Error(detailedErrorMessage);
+ }
+ }
+
+ // SUCCESS: Show green success message
+ setToastData({
+ message: successMessage,
+ type: "success",
+ });
+
+ closeImportModal();
+
+ // Refresh the list
+ try {
+ setIsLoading(true);
+ const res = await fetchEstablishments();
+ const payload = res?.data ?? res;
+ const records = Array.isArray(payload?.data)
+ ? payload.data
+ : Array.isArray(payload)
+ ? payload
+ : [];
+
+ setProfiles(records.map(mapApiEstablishmentToProfile));
+ setTotalItems(records.length);
+ } catch (refreshErr) {
+ console.log("Error refreshing data:", refreshErr);
+ } finally {
+ setIsLoading(false);
+ }
+
+ return response;
+
+ } catch (error) {
+ console.error("Error importing CSV:", error);
+
+ let errorMessage = error.message || "Upload failed. Please try again.";
+
+ // Remove any "Request failed with status code" prefixes
+ if (errorMessage.includes("Request failed with status code")) {
+ // Extract the actual error message after the status code
+ const parts = errorMessage.split("Request failed with status code");
+ if (parts.length > 1) {
+ // Try to find the actual error content
+ const actualError = parts[1].replace(/^\d+\s*/, '').trim();
+ if (actualError && actualError.length > 0) {
+ errorMessage = actualError;
+ } else {
+ errorMessage = "Server error occurred. Please try again.";
+ }
+ }
+ }
+
+ // Additional error handling
+ if (errorMessage.includes("expired") || errorMessage.includes("Authentication")) {
+ errorMessage = "Authentication failed. Please check your login credentials.";
+ }
+
+ // Ensure duplicate ID error shows the proper message
+ if (errorMessage.includes("Duplicate Establishment IDs") ||
+ errorMessage.includes("duplicate") ||
+ errorMessage.includes("already exist")) {
+ errorMessage = "Duplicate Establishment IDs found (in file or already exist in system). Resolve and re-upload.";
+ }
+
+ setImportError(errorMessage);
+
+ // ERROR: Show red error message
+ setToastData({
+ message: errorMessage,
+ type: "error",
+ });
+
+ // Re-throw the error so calling code can handle it
+ throw error;
+
+ } finally {
+ setImportLoading(false);
+ }
+};
+ // Fixed file validation
+ const handleFileChange = (e) => {
+ if (e.target.files && e.target.files[0]) {
+ const selectedFile = e.target.files[0];
+
+ const isValidType = selectedFile.type === 'text/csv' ||
+ selectedFile.name.toLowerCase().endsWith('.csv') ||
+ selectedFile.type === 'application/vnd.ms-excel';
+
+ if (isValidType) {
+ setFile(selectedFile);
+ setImportError('');
+ } else {
+ setImportError('Please upload a CSV file only. Supported formats: .csv');
+ setFile(null);
+ }
+ }
+ };
+
+ // Fixed drag and drop handler
+ const handleDrop = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setDragActive(false);
+
+ if (e.dataTransfer.files && e.dataTransfer.files[0]) {
+ const droppedFile = e.dataTransfer.files[0];
+
+ const isValidType = droppedFile.type === 'text/csv' ||
+ droppedFile.name.toLowerCase().endsWith('.csv') ||
+ droppedFile.type === 'application/vnd.ms-excel';
+
+ if (isValidType) {
+ setFile(droppedFile);
+ setImportError('');
+ } else {
+ setImportError('Please upload a CSV file only. Supported formats: .csv');
+ }
+ }
+ };
+
+
return (
{toast && (
@@ -2682,6 +3030,14 @@ const requiredFields = [
width="180px"
variant="toolbar"
/>
+
+
+ Import CSV
+
+
+ )}
+ {/* Fixed Import CSV Modal */}
+ {importModalOpen && (
+
+ )}
+ {/* Custom Toast Notification */}
+ {toastData && (
+