bug fixed
This commit is contained in:
parent
24e3c9117b
commit
8dca1f0a84
@ -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]">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user