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