More import optimizations + reset optimizations

This commit is contained in:
2025-01-11 00:18:03 -05:00
parent 7ce5092b69
commit 0bc86a3fee
3 changed files with 183 additions and 147 deletions

View File

@@ -50,6 +50,7 @@ export function Settings() {
const [isResetting, setIsResetting] = useState(false);
const [updateProgress, setUpdateProgress] = useState<ImportProgress | null>(null);
const [importProgress, setImportProgress] = useState<ImportProgress | null>(null);
const [purchaseOrdersProgress, setPurchaseOrdersProgress] = useState<ImportProgress | null>(null);
const [resetProgress, setResetProgress] = useState<ImportProgress | null>(null);
const [eventSource, setEventSource] = useState<EventSource | null>(null);
const [limits, setLimits] = useState<ImportLimits>({
@@ -58,49 +59,48 @@ export function Settings() {
purchaseOrders: 0
});
// Helper function to update progress state
const updateProgressState = (progressData: any) => {
const operation = progressData.operation?.toLowerCase() || '';
const progressUpdate = {
status: progressData.status || 'running',
operation: progressData.operation,
current: progressData.current !== undefined ? Number(progressData.current) : undefined,
total: progressData.total !== undefined ? Number(progressData.total) : undefined,
rate: progressData.rate !== undefined ? Number(progressData.rate) : undefined,
percentage: progressData.percentage,
elapsed: progressData.elapsed,
remaining: progressData.remaining,
error: progressData.error,
message: progressData.message,
added: progressData.added,
updated: progressData.updated,
skipped: progressData.skipped,
duration: progressData.duration
};
if (operation.includes('products import completed')) {
setImportProgress(null);
} else if (operation.includes('products import')) {
setImportProgress(prev => ({ ...prev, ...progressUpdate }));
} else if (operation.includes('orders import') && !operation.includes('purchase')) {
setImportProgress(prev => ({ ...prev, ...progressUpdate }));
} else if (operation.includes('purchase orders import')) {
setPurchaseOrdersProgress(prev => ({ ...prev, ...progressUpdate }));
}
};
// Helper to connect to event source
const connectToEventSource = useCallback((type: 'update' | 'import' | 'reset') => {
if (eventSource) {
eventSource.close();
}
console.log('Connecting to event source:', type); // Debug log
const source = new EventSource(`${config.apiUrl}/csv/${type}/progress`, {
withCredentials: true
});
setEventSource(source);
source.onopen = () => {
console.log('Event source connected:', type); // Debug log
};
source.onerror = () => {
console.log('Event source error:', type, source.readyState); // Debug log
if (source.readyState === EventSource.CLOSED) {
source.close();
setEventSource(null);
// Only reset states if we're not in a completed state
const progress = type === 'update' ? updateProgress :
type === 'import' ? importProgress :
resetProgress;
if (progress?.status !== 'complete') {
// Reset the appropriate state based on type
if (type === 'update') {
setIsUpdating(false);
setUpdateProgress(null);
} else if (type === 'import') {
setIsImporting(false);
setImportProgress(null);
} else if (type === 'reset') {
setIsResetting(false);
setResetProgress(null);
}
}
}
};
source.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
@@ -108,44 +108,18 @@ export function Settings() {
(typeof data.progress === 'string' ? JSON.parse(data.progress) : data.progress)
: data;
// Update the appropriate progress state based on type
const setProgress = type === 'update' ? setUpdateProgress :
type === 'import' ? setImportProgress :
setResetProgress;
// Also set the operation state if we're getting progress
if (progressData.status === 'running') {
if (type === 'update') setIsUpdating(true);
else if (type === 'import') setIsImporting(true);
else if (type === 'reset') setIsResetting(true);
}
setProgress(prev => {
// If we're getting a new operation, clear out old messages
if (progressData.operation && progressData.operation !== prev?.operation) {
return {
status: progressData.status || 'running',
operation: progressData.operation,
current: progressData.current !== undefined ? Number(progressData.current) : undefined,
total: progressData.total !== undefined ? Number(progressData.total) : undefined,
rate: progressData.rate !== undefined ? Number(progressData.rate) : undefined,
percentage: progressData.percentage,
elapsed: progressData.elapsed,
remaining: progressData.remaining,
message: progressData.message,
error: progressData.error,
added: progressData.added,
updated: progressData.updated,
skipped: progressData.skipped,
duration: progressData.duration
};
}
// Otherwise update existing state
return {
// Handle different types of progress
if (type === 'import') {
updateProgressState(progressData);
} else {
// For non-import operations, use the existing logic
const setProgress = type === 'update' ? setUpdateProgress :
type === 'reset' ? setResetProgress :
setImportProgress;
setProgress(prev => ({
...prev,
status: progressData.status || prev?.status || 'running',
operation: progressData.operation || prev?.operation,
status: progressData.status || 'running',
operation: progressData.operation,
current: progressData.current !== undefined ? Number(progressData.current) : prev?.current,
total: progressData.total !== undefined ? Number(progressData.total) : prev?.total,
rate: progressData.rate !== undefined ? Number(progressData.rate) : prev?.rate,
@@ -158,8 +132,15 @@ export function Settings() {
updated: progressData.updated !== undefined ? progressData.updated : prev?.updated,
skipped: progressData.skipped !== undefined ? progressData.skipped : prev?.skipped,
duration: progressData.duration || prev?.duration
};
});
}));
}
// Set operation state if we're getting progress
if (progressData.status === 'running') {
if (type === 'update') setIsUpdating(true);
else if (type === 'import') setIsImporting(true);
else if (type === 'reset') setIsResetting(true);
}
if (progressData.status === 'complete') {
source.close();
@@ -172,6 +153,7 @@ export function Settings() {
} else if (type === 'import') {
setIsImporting(false);
setImportProgress(null);
setPurchaseOrdersProgress(null);
} else if (type === 'reset') {
setIsResetting(false);
setResetProgress(null);
@@ -196,7 +178,7 @@ export function Settings() {
handleError(`${type.charAt(0).toUpperCase() + type.slice(1)}`, progressData.error || 'Unknown error');
}
} catch (error) {
console.error('Error parsing event data:', error); // Debug log
console.error('Error parsing event data:', error);
}
};
}, []); // Remove dependencies that might prevent initial connection
@@ -339,7 +321,7 @@ export function Settings() {
setImportProgress({ status: 'running', operation: 'Starting import process' });
try {
// Connect to SSE for progress updates
// Connect to SSE for progress updates first
connectToEventSource('import');
// Make the import request
@@ -354,20 +336,23 @@ export function Settings() {
if (!response.ok) {
const data = await response.json().catch(() => ({}));
if (data.error === 'Import already in progress') {
// If there's already an import, just let the SSE connection handle showing progress
return;
// Only handle error if we don't have any progress yet
if (!importProgress?.current && !purchaseOrdersProgress?.current) {
throw new Error(data.error || 'Failed to start CSV import');
}
throw new Error(data.error || 'Failed to start CSV import');
}
} catch (error) {
if (eventSource) {
eventSource.close();
setEventSource(null);
// Only clean up if we don't have any progress
if (!importProgress?.current && !purchaseOrdersProgress?.current) {
if (eventSource) {
eventSource.close();
setEventSource(null);
}
setIsImporting(false);
setImportProgress(null);
setPurchaseOrdersProgress(null);
handleError('Data import', error instanceof Error ? error.message : 'Unknown error');
}
setIsImporting(false);
setImportProgress(null);
handleError('Data import', error instanceof Error ? error.message : 'Unknown error');
}
};
@@ -479,7 +464,11 @@ export function Settings() {
if (error.includes('cancelled') ||
error.includes('Process exited with code 143') ||
error.includes('Operation cancelled') ||
error.includes('500 Internal Server Error')) {
error.includes('500 Internal Server Error') ||
// Skip "Failed to start" errors if we have active progress
(error.includes('Failed to start CSV import') && (importProgress || purchaseOrdersProgress)) ||
// Skip connection errors if we have active progress
(error.includes('Failed to fetch') && (importProgress || purchaseOrdersProgress))) {
return;
}
toast.error(`${operation} failed: ${error}`);
@@ -615,7 +604,29 @@ export function Settings() {
)}
</div>
{isImporting && renderProgress(importProgress)}
{isImporting && (
<div className="space-y-4">
{/* Show products progress */}
{importProgress?.operation?.toLowerCase().includes('products') && (
<div>
{renderProgress(importProgress)}
</div>
)}
{/* Show orders progress */}
{importProgress?.operation?.toLowerCase().includes('orders import') &&
!importProgress.operation.toLowerCase().includes('purchase') && (
<div>
{renderProgress(importProgress)}
</div>
)}
{/* Show purchase orders progress */}
{purchaseOrdersProgress && (
<div>
{renderProgress(purchaseOrdersProgress)}
</div>
)}
</div>
)}
</CardContent>
</Card>