bug fixed

This commit is contained in:
Malini 2025-12-11 17:10:44 +05:30
parent 24e3c9117b
commit 8dca1f0a84

View File

@ -29,7 +29,7 @@ const createEmptyCodeForm = () => ({
});
// Toast notification component
const Toast = ({ message, type = 'success', onClose, position = 'modal' }) => {
const Toast = ({ message, type = 'success', onClose, position = 'page' }) => {
React.useEffect(() => {
const timer = setTimeout(() => {
onClose();
@ -50,20 +50,10 @@ const Toast = ({ message, type = 'success', onClose, position = 'modal' }) => {
}
};
// For modal toasts, we'll use relative positioning
if (position === 'modal') {
return (
<div className="w-full mb-4">
<div className={`px-4 py-2 rounded-md text-sm ${getToastStyles()}`}>
{message}
</div>
</div>
);
}
// Default page-level toast
return (
<div className="fixed top-0 left-0 w-full flex justify-center z-50 mt-4">
<div className={`fixed top-4 left-1/2 -translate-x-1/2 z-[9999] transition-all duration-300 ${
message ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-2 pointer-events-none'
}`}>
<div className={`px-6 py-3 rounded-md shadow-lg font-medium ${getToastStyles()}`}>
{message}
</div>
@ -72,14 +62,48 @@ const Toast = ({ message, type = 'success', onClose, position = 'modal' }) => {
};
// Import Modal Component
const ImportModal = ({ isOpen, onClose, onImport }) => {
const ImportModal = ({ isOpen, onClose, onImport, showToast: showToastProp }) => {
const [file, setFile] = React.useState(null);
const [dragActive, setDragActive] = React.useState(false);
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState('');
const [toast, setToast] = React.useState({
show: false,
message: '',
type: 'success',
position: 'modal'
});
const fileInputRef = React.useRef(null);
const showToast = (message, type = 'success', position = 'modal') => {
console.log('showToast called with:', { message, type, position });
// Always use local toast state for the modal
setToast(prev => {
console.log('Updating toast state');
return {
...prev,
show: true,
message,
type,
position
};
});
// Auto-hide after delay
const timer = setTimeout(() => {
console.log('Hiding toast after timeout');
setToast(prev => ({ ...prev, show: false }));
}, 3000);
// Cleanup
return () => {
console.log('Cleaning up toast timeout');
clearTimeout(timer);
};
};
const handleDrag = (e) => {
e.preventDefault();
e.stopPropagation();
@ -128,6 +152,7 @@ const ImportModal = ({ isOpen, onClose, onImport }) => {
};
const handleImport = async () => {
console.log('handleImport called');
if (!file) {
setError('Please select a file to import');
return;
@ -137,42 +162,33 @@ const ImportModal = ({ isOpen, onClose, onImport }) => {
setLoading(true);
setError('');
// Call the uploadCSV API from productService
console.log('Calling productService.uploadCSV');
const response = await productService.uploadCSV(file);
console.log('Upload response:', response);
// Check the response status
if (response.status === 'failed') {
console.log('Import failed with response:', response);
let errorMsg = response.message || 'Import failed';
// Add duplicate HS codes to the error message if they exist
// if (response.duplicate_hs_codes_in_system?.length > 0) {
// errorMsg += `\nDuplicate HS codes found: ${response.duplicate_hs_codes_in_system.join(', ')}`;
// }
// // Show error summary if available
// if (response.summary) {
// errorMsg += `\nTotal records: ${response.summary.total_records}`;
// errorMsg += `\nImported: ${response.summary.imported}`;
// errorMsg += `\nSkipped: ${response.summary.skipped}`;
if (response.summary.errors?.length > 0) {
errorMsg += '\n\nErrors:';
response.summary.errors.forEach(err => {
errorMsg += `\nRow ${err.row}: ${err.error}`;
});
}
if (response.summary?.errors?.length > 0) {
errorMsg += '\n\nErrors:';
response.summary.errors.forEach(err => {
errorMsg += `\nRow ${err.row}: ${err.error}`;
});
}
setError(errorMsg);
return;
}
// Call the onImport callback with the response data
await onImport(response);
// Reset and close on success
// Close the modal first
setFile(null);
onClose();
// Then call onImport which will trigger the parent's success handling
console.log('Calling onImport');
await onImport(response);
} catch (err) {
console.error('Error uploading CSV:', err);
let errorMessage = 'Failed to import file. Please try again.';
@ -206,27 +222,7 @@ const ImportModal = ({ isOpen, onClose, onImport }) => {
onClose();
};
// const downloadSampleCSV = () => {
// const sampleData = [
// ['HS Code', 'Product Name', 'Unit'],
// ['0101', 'Live Horses', 'kg'],
// ['0102', 'Live Bovine Animals', 'kg'],
// ];
// 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', 'hs-codes-sample.csv');
// document.body.appendChild(link);
// link.click();
// document.body.removeChild(link);
// URL.revokeObjectURL(url);
// };
const downloadSampleCSV = async () => {
try {
const response = await productService.downloadSampleFile();
@ -272,6 +268,18 @@ const ImportModal = ({ isOpen, onClose, onImport }) => {
{/* Content */}
<div className="p-6 space-y-4">
{/* Toast notification within modal */}
{toast.show && toast.position === 'modal' && (
<Toast
message={toast.message}
type={toast.type}
position="modal"
onClose={() => {
console.log('Toast onClose called');
setToast(prev => ({ ...prev, show: false }));
}}
/>
)}
{/* File Upload Area */}
<div
className={`border-2 border-dashed rounded-lg p-6 text-center cursor-pointer transition-colors ${
@ -393,7 +401,7 @@ const IsicHsCodes = () => {
show: false,
message: '',
type: 'success',
position: 'modal' // 'page' or 'modal'
position: 'page' // 'page' or 'modal'
});
const [showImportModal, setShowImportModal] = React.useState(false);
const [isRefreshing, setIsRefreshing] = React.useState(false);
@ -693,11 +701,11 @@ const IsicHsCodes = () => {
setModalMode('view');
} else {
console.error('Invalid product data received from API');
showToast('Failed to load product details', 'error');
setToast({ show: true, message: 'Failed to load product details', type: 'error' });
}
} catch (error) {
console.error('Error fetching product details:', error);
showToast('Error loading product details', 'error');
setToast({ show: true, message: 'Error loading product details', type: 'error' });
} finally {
setIsLoading(false);
}
@ -734,11 +742,11 @@ const IsicHsCodes = () => {
setModalMode('edit');
} else {
console.error('Empty response received from API');
showToast('Failed to load product details for editing', 'error');
setToast({ show: true, message: 'Failed to load product details for editing', type: 'error' });
}
} catch (error) {
console.error('Error fetching product details for edit:', error);
showToast('Error loading product details for editing', 'error');
setToast({ show: true, message: 'Error loading product details for editing', type: 'error' });
} finally {
setIsLoading(false);
}
@ -752,7 +760,7 @@ const IsicHsCodes = () => {
if (!selected || !selected.id) {
console.error('No product selected or missing ID');
showToast('Cannot delete: Product information is incomplete', 'error');
setToast({ show: true, message: 'Cannot delete: Product information is incomplete', type: 'error' });
return;
}
@ -778,7 +786,7 @@ const IsicHsCodes = () => {
setShowDeleteConfirm(true);
} catch (error) {
console.error('Error preparing product for deletion:', error);
showToast('Error loading product details for deletion', 'error');
setToast({ show: true, message: 'Error loading product details for deletion', type: 'error' });
} finally {
setIsLoading(false);
}
@ -799,7 +807,7 @@ const IsicHsCodes = () => {
if (deletingRowIndex === null || deletingRowIndex < 0 || deletingRowIndex >= rowsData.length) {
const errorMsg = 'Invalid row selected for deletion';
console.error(errorMsg, { deletingRowIndex, rowsDataLength: rowsData.length });
showToast(errorMsg, 'error');
setToast({ show: true, message: errorMsg, type: 'error' });
setShowDeleteConfirm(false);
setDeletingRowIndex(null);
return;
@ -832,7 +840,7 @@ const IsicHsCodes = () => {
newData.splice(deletingRowIndex, 1);
setRowsData(newData);
showToast('Product deleted successfully!', 'success');
setToast({ show: true, message: 'Product deleted successfully!', type: 'success' });
} catch (apiError) {
console.error('API Error:', apiError);
if (apiError.response && apiError.response.status === 204) {
@ -850,18 +858,18 @@ const IsicHsCodes = () => {
newData.splice(deletingRowIndex, 1);
setRowsData(newData);
showToast('Product deleted successfully!', 'success');
setToast({ show: true, message: 'Product deleted successfully!', type: 'success' });
} else {
const errorMessage = apiError.response?.data?.message || 'Failed to delete product. Please try again.';
setError(errorMessage);
showToast(errorMessage, 'error');
setToast({ show: true, message: errorMessage, type: 'error' });
}
}
} catch (error) {
console.error('Error in delete process:', error);
const errorMessage = error.message || 'An unexpected error occurred';
setError(errorMessage);
showToast(errorMessage, 'error');
setToast({ show: true, message: errorMessage, type: 'error' });
} finally {
setIsLoading(false);
setShowDeleteConfirm(false);
@ -886,8 +894,8 @@ const IsicHsCodes = () => {
setForm(createEmptyCodeForm());
};
const showToast = (message, type = 'success', position = 'modal') => {
setToast({ show: true, message, type, position });
const showToast = (message, type = 'success') => {
setToast({ show: true, message, type });
setTimeout(() => {
setToast(prev => ({ ...prev, show: false }));
}, 3000);
@ -905,7 +913,7 @@ const IsicHsCodes = () => {
await new Promise(resolve => setTimeout(resolve, 1500));
// Show success message
showToast('HS Codes imported successfully! New codes appear in the list.', 'success');
setToast({ show: true, message: 'HS Codes imported successfully! New codes appear in the list.', type: 'success' });
// In a real implementation, you would:
// 1. Call your import API endpoint
@ -913,7 +921,7 @@ const IsicHsCodes = () => {
// 3. Update the rowsData state with the new data
} catch (error) {
showToast('Failed to import CSV file. Please try again.', 'error');
setToast({ show: true, message: 'Failed to import CSV file. Please try again.', type: 'error' });
}
};
@ -945,14 +953,14 @@ const IsicHsCodes = () => {
document.body.removeChild(link);
URL.revokeObjectURL(url);
showToast('CSV exported successfully!', 'success');
setToast({ show: true, message: 'CSV exported successfully!', type: 'success' });
};
const handleSaveForm = async () => {
if (!form.code || !form.product) {
const errorMsg = 'Please fill in all required fields';
console.error(errorMsg);
showToast(errorMsg, 'error');
setToast({ show: true, message: errorMsg, type: 'error' });
return;
}
@ -996,7 +1004,7 @@ const IsicHsCodes = () => {
if (!productId) {
const errorMsg = 'Cannot update product: Product ID not found';
console.error(errorMsg);
showToast(errorMsg, 'error');
setToast({ show: true, message: errorMsg, type: 'error' });
return;
}
@ -1040,7 +1048,7 @@ const IsicHsCodes = () => {
});
// Show success message and reload the page after a short delay
showToast('Product updated successfully!', 'success');
setToast({ show: true, message: 'Product updated successfully!', type: 'success' });
closeModal();
setTimeout(() => {
window.location.reload();
@ -1052,7 +1060,7 @@ const IsicHsCodes = () => {
if (error.response?.data?.message) {
errorMessage = error.response.data.message;
}
showToast(errorMessage, 'error');
setToast({ show: true, message: errorMessage, type: 'error' });
}
}
@ -1077,7 +1085,7 @@ const IsicHsCodes = () => {
setRowsData(prev => [newProduct, ...prev]);
setFilteredData(prev => [newProduct, ...prev]);
showToast('Product created successfully!', 'success');
setToast({ show: true, message: 'Product created successfully!', type: 'success' });
closeModal();
// Reload the page after a short delay to reflect changes
@ -1090,7 +1098,7 @@ const IsicHsCodes = () => {
if (createError.response?.data?.message) {
errorMessage = createError.response.data.message;
}
showToast(errorMessage, 'error');
setToast({ show: true, message: errorMessage, type: 'error' });
}
} catch (error) {
console.error('Error in handleSaveForm:', error);
@ -1172,20 +1180,12 @@ const IsicHsCodes = () => {
setRowsData(formattedData);
setFilteredData(formattedData);
setToast({ show: true, message: 'HS Codes imported successfully!', type: 'success' });
}
setToast({
show: true,
message: 'Data imported successfully!',
type: 'success'
});
} catch (err) {
console.error('Error refreshing product list:', err);
setToast({
show: true,
message: 'Data imported but failed to refresh the list. ' + (err.response?.data?.message || 'Please refresh the page manually.'),
type: 'error'
});
} catch (error) {
console.error('Error refreshing HS codes:', error);
setToast({ show: true, message: 'Failed to refresh HS codes', type: 'error' });
} finally {
setIsRefreshing(false);
}
@ -1194,12 +1194,11 @@ const IsicHsCodes = () => {
return (
<div className="relative">
{/* Page-level toasts */}
{toast.show && toast.position === 'page' && (
{toast.show && (
<Toast
message={toast.message}
type={toast.type}
position="page"
onClose={() => setToast(prev => ({ ...prev, show: false }))}
onClose={() => setToast(prev => ({ ...prev, show: false }))}
/>
)}
@ -1207,7 +1206,10 @@ const IsicHsCodes = () => {
<ImportModal
isOpen={showImportModal}
onClose={() => setShowImportModal(false)}
onImport={handleImportSuccess}
onImport={(response) => {
handleImportSuccess(response);
setToast({ show: true, message: 'HS Codes imported successfully!', type: 'success' });
}}
/>
<div className="bg-white w-full rounded-lg shadow-sm ring-1 ring-[#E5E7EB]">