Add more tables to db and processing to import script, add error logging to import, add test data snapshots (untested)

This commit is contained in:
2025-01-11 13:07:07 -05:00
parent 1d022cae01
commit 30018ad882
5 changed files with 1029 additions and 117 deletions

View File

@@ -58,6 +58,9 @@ export function Settings() {
orders: 0,
purchaseOrders: 0
});
const [isCreatingSnapshot, setIsCreatingSnapshot] = useState(false);
const [isRestoringSnapshot, setIsRestoringSnapshot] = useState(false);
const [snapshotProgress, setSnapshotProgress] = useState<ImportProgress | null>(null);
// Helper function to update progress state
const updateProgressState = (progressData: any) => {
@@ -87,11 +90,15 @@ export function Settings() {
setImportProgress(prev => ({ ...prev, ...progressUpdate }));
} else if (operation.includes('purchase orders import')) {
setPurchaseOrdersProgress(prev => ({ ...prev, ...progressUpdate }));
} else if (operation.includes('metrics') || operation.includes('vendor metrics')) {
setImportProgress(prev => ({ ...prev, ...progressUpdate }));
} else if (operation.includes('snapshot')) {
setSnapshotProgress(prev => ({ ...prev, ...progressUpdate }));
}
};
// Helper to connect to event source
const connectToEventSource = useCallback((type: 'update' | 'import' | 'reset') => {
const connectToEventSource = useCallback((type: 'update' | 'import' | 'reset' | 'snapshot') => {
if (eventSource) {
eventSource.close();
}
@@ -389,6 +396,63 @@ export function Settings() {
}
};
// Add handlers for snapshot operations
const handleCreateSnapshot = async () => {
try {
setIsCreatingSnapshot(true);
setSnapshotProgress({ status: 'running', operation: 'Creating test data snapshot' });
// Connect to SSE for progress updates
connectToEventSource('snapshot');
const response = await fetch(`${config.apiUrl}/snapshot/create`, {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to create snapshot');
}
} catch (error) {
console.error('Error creating snapshot:', error);
if (eventSource) {
eventSource.close();
setEventSource(null);
}
setIsCreatingSnapshot(false);
setSnapshotProgress(null);
toast.error('Failed to create snapshot');
}
};
const handleRestoreSnapshot = async () => {
try {
setIsRestoringSnapshot(true);
setSnapshotProgress({ status: 'running', operation: 'Restoring test data snapshot' });
// Connect to SSE for progress updates
connectToEventSource('snapshot');
const response = await fetch(`${config.apiUrl}/snapshot/restore`, {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to restore snapshot');
}
} catch (error) {
console.error('Error restoring snapshot:', error);
if (eventSource) {
eventSource.close();
setEventSource(null);
}
setIsRestoringSnapshot(false);
setSnapshotProgress(null);
toast.error('Failed to restore snapshot');
}
};
// Cleanup on unmount
useEffect(() => {
return () => {
@@ -625,6 +689,30 @@ export function Settings() {
{renderProgress(purchaseOrdersProgress)}
</div>
)}
{/* Show metrics calculation progress */}
{importProgress?.operation?.toLowerCase().includes('metrics') && (
<div>
<Progress value={Number(importProgress.percentage)} className="mb-2" />
<p className="text-sm text-muted-foreground">
{importProgress.message || importProgress.operation || 'Calculating metrics...'}
{importProgress.current && importProgress.total && (
<> ({importProgress.current} of {importProgress.total})</>
)}
</p>
{importProgress.elapsed && (
<p className="text-xs text-muted-foreground">
Elapsed: {importProgress.elapsed}
{importProgress.remaining && <> Remaining: {importProgress.remaining}</>}
</p>
)}
</div>
)}
{/* Show vendor metrics progress */}
{importProgress?.operation?.toLowerCase().includes('vendor metrics') && (
<div>
{renderProgress(importProgress)}
</div>
)}
</div>
)}
</CardContent>
@@ -637,36 +725,104 @@ export function Settings() {
<CardDescription>Drop all tables and recreate the database schema. This will delete ALL data.</CardDescription>
</CardHeader>
<CardContent>
<div className="flex gap-2">
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" disabled={isResetting || isImporting || isUpdating}>
Reset Database
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete all data from the database.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleResetDB}>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{resetProgress && (
<div className="mt-4">
<Progress value={Number(resetProgress.percentage)} className="mb-2" />
<p className="text-sm text-muted-foreground">
{resetProgress.message || 'Resetting database...'}
</p>
</div>
)}
</CardContent>
</Card>
{/* Test Data Snapshots Card */}
<Card>
<CardHeader>
<CardTitle>Test Data Snapshots</CardTitle>
<CardDescription>Create and restore test data snapshots for development and testing.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex space-x-4">
<Button
onClick={handleCreateSnapshot}
disabled={isCreatingSnapshot || isRestoringSnapshot || isImporting || isUpdating || isResetting}
>
{isCreatingSnapshot ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Creating Snapshot...
</>
) : (
<>Create Snapshot</>
)}
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="destructive"
className="flex-1"
disabled={isUpdating || isImporting}
variant="secondary"
disabled={isCreatingSnapshot || isRestoringSnapshot || isImporting || isUpdating || isResetting}
>
Reset Database
{isRestoringSnapshot ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Restoring...
</>
) : (
<>Restore Snapshot</>
)}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogTitle>Restore test data snapshot?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete all data
from the database and reset it to its initial state.
This will replace your current database with the test data snapshot. Any unsaved changes will be lost.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleResetDB}>
Reset Database
</AlertDialogAction>
<AlertDialogAction onClick={handleRestoreSnapshot}>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
{isResetting && renderProgress(resetProgress)}
{snapshotProgress && (
<div>
<Progress value={Number(snapshotProgress.percentage)} className="mb-2" />
<p className="text-sm text-muted-foreground">
{snapshotProgress.message || 'Processing snapshot...'}
</p>
</div>
)}
<div className="text-sm text-muted-foreground">
<p>The test data snapshot includes:</p>
<ul className="list-disc list-inside mt-2">
<li>~100 diverse products with associated data</li>
<li>Orders from the last 6 months</li>
<li>Purchase orders from the last 6 months</li>
<li>Categories and product relationships</li>
</ul>
</div>
</CardContent>
</Card>