Fix frontend reset script and visual tweaks

This commit is contained in:
2025-02-11 22:15:13 -05:00
parent 67d57c8872
commit 4dcc1f9e90
4 changed files with 140 additions and 65 deletions

View File

@@ -57,25 +57,36 @@ async function fullReset() {
message: 'Step 1/3: Resetting database...' message: 'Step 1/3: Resetting database...'
}); });
await runScript(path.join(__dirname, 'reset-db.js')); await runScript(path.join(__dirname, 'reset-db.js'));
outputProgress({
status: 'complete',
operation: 'Database reset step complete',
message: 'Database reset finished, moving to import...'
});
// Step 2: Import from Production // Step 2: Import from Production
outputProgress({ outputProgress({
operation: 'Database reset complete', operation: 'Starting import',
message: 'Step 2/3: Importing from production...' message: 'Step 2/3: Importing from production...'
}); });
await runScript(path.join(__dirname, 'import-from-prod.js')); await runScript(path.join(__dirname, 'import-from-prod.js'));
outputProgress({
status: 'complete',
operation: 'Import step complete',
message: 'Import finished, moving to metrics calculation...'
});
// Step 3: Calculate Metrics // Step 3: Calculate Metrics
outputProgress({ outputProgress({
operation: 'Import complete', operation: 'Starting metrics calculation',
message: 'Step 3/3: Calculating metrics...' message: 'Step 3/3: Calculating metrics...'
}); });
await runScript(path.join(__dirname, 'calculate-metrics.js')); await runScript(path.join(__dirname, 'calculate-metrics.js'));
// Final completion message
outputProgress({ outputProgress({
status: 'complete', status: 'complete',
operation: 'Full reset complete', operation: 'Full reset complete',
message: 'Successfully completed database reset, import, and metrics calculation' message: 'Successfully completed all steps: database reset, import, and metrics calculation'
}); });
} catch (error) { } catch (error) {
outputProgress({ outputProgress({

View File

@@ -57,18 +57,29 @@ async function fullUpdate() {
message: 'Step 1/2: Importing from production...' message: 'Step 1/2: Importing from production...'
}); });
await runScript(path.join(__dirname, 'import-from-prod.js')); await runScript(path.join(__dirname, 'import-from-prod.js'));
outputProgress({
status: 'complete',
operation: 'Import step complete',
message: 'Import finished, moving to metrics calculation...'
});
// Step 2: Calculate Metrics // Step 2: Calculate Metrics
outputProgress({ outputProgress({
operation: 'Import complete', operation: 'Starting metrics calculation',
message: 'Step 2/2: Calculating metrics...' message: 'Step 2/2: Calculating metrics...'
}); });
await runScript(path.join(__dirname, 'calculate-metrics.js')); await runScript(path.join(__dirname, 'calculate-metrics.js'));
outputProgress({
status: 'complete',
operation: 'Metrics step complete',
message: 'Metrics calculation finished'
});
// Final completion message
outputProgress({ outputProgress({
status: 'complete', status: 'complete',
operation: 'Full update complete', operation: 'Full update complete',
message: 'Successfully completed import and metrics calculation' message: 'Successfully completed all steps: import and metrics calculation'
}); });
} catch (error) { } catch (error) {
outputProgress({ outputProgress({

View File

@@ -85,8 +85,41 @@ function runScript(scriptPath, type, clients) {
child.stdout.on('data', (data) => { child.stdout.on('data', (data) => {
const text = data.toString(); const text = data.toString();
output += text; output += text;
// Send raw output directly
sendProgressToClients(clients, text); // Split by lines to handle multiple JSON outputs
const lines = text.split('\n');
lines.filter(line => line.trim()).forEach(line => {
try {
// Try to parse as JSON but don't let it affect the display
const jsonData = JSON.parse(line);
// Only end the process if we get a final status
if (jsonData.status === 'complete' || jsonData.status === 'error' || jsonData.status === 'cancelled') {
if (jsonData.status === 'complete' && !jsonData.operation?.includes('complete')) {
// Don't close for intermediate completion messages
sendProgressToClients(clients, line);
return;
}
// Close only on final completion/error/cancellation
switch (type) {
case 'update':
activeFullUpdate = null;
break;
case 'reset':
activeFullReset = null;
break;
}
if (jsonData.status === 'error') {
reject(new Error(jsonData.error || 'Unknown error'));
} else {
resolve({ output });
}
}
} catch (e) {
// Not JSON, just display as is
}
// Always send the raw line
sendProgressToClients(clients, line);
});
}); });
child.stderr.on('data', (data) => { child.stderr.on('data', (data) => {
@@ -110,10 +143,8 @@ function runScript(scriptPath, type, clients) {
const error = `Script ${scriptPath} exited with code ${code}`; const error = `Script ${scriptPath} exited with code ${code}`;
sendProgressToClients(clients, error); sendProgressToClients(clients, error);
reject(new Error(error)); reject(new Error(error));
} else {
sendProgressToClients(clients, `${type} completed successfully`);
resolve({ output });
} }
// Don't resolve here - let the completion message from the script trigger the resolve
}); });
child.on('error', (err) => { child.on('error', (err) => {

View File

@@ -181,25 +181,30 @@ export function DataManagement() {
// Try to parse for status updates, but don't affect display // Try to parse for status updates, but don't affect display
try { try {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if ( if (data.status === 'complete' || data.status === 'error' || data.status === 'cancelled') {
data.status === "complete" || // Only close and reset state if this is the final completion message
data.status === "error" || if (data.operation === 'Full update complete' ||
data.status === "cancelled" data.status === 'error' ||
) { data.status === 'cancelled') {
source.close(); source.close();
setEventSource(null); setEventSource(null);
setIsUpdating(false); setIsUpdating(false);
if (data.status === "complete") { if (data.status === 'complete') {
toast.success("Update completed successfully"); toast.success("Update completed successfully");
fetchHistory(); fetchHistory();
} else if (data.status === "error") { } else if (data.status === 'error') {
toast.error(`Update failed: ${data.error || "Unknown error"}`); toast.error(`Update failed: ${data.error || 'Unknown error'}`);
} else { } else {
toast.warning("Update cancelled"); toast.warning("Update cancelled");
}
}
// For intermediate completions, just show a toast
else if (data.status === 'complete') {
toast.success(data.message || "Step completed");
} }
} }
} catch (error) { } catch (error) {
// Not JSON, just display as is // Not JSON, just display as is
} }
}; };
@@ -246,25 +251,30 @@ export function DataManagement() {
// Try to parse for status updates, but don't affect display // Try to parse for status updates, but don't affect display
try { try {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if ( if (data.status === 'complete' || data.status === 'error' || data.status === 'cancelled') {
data.status === "complete" || // Only close and reset state if this is the final completion message
data.status === "error" || if (data.operation === 'Full reset complete' ||
data.status === "cancelled" data.status === 'error' ||
) { data.status === 'cancelled') {
source.close(); source.close();
setEventSource(null); setEventSource(null);
setIsResetting(false); setIsResetting(false);
if (data.status === "complete") { if (data.status === 'complete') {
toast.success("Reset completed successfully"); toast.success("Reset completed successfully");
fetchHistory(); fetchHistory();
} else if (data.status === "error") { } else if (data.status === 'error') {
toast.error(`Reset failed: ${data.error || "Unknown error"}`); toast.error(`Reset failed: ${data.error || 'Unknown error'}`);
} else { } else {
toast.warning("Reset cancelled"); toast.warning("Reset cancelled");
} }
} }
} catch (error) { // For intermediate completions, just show a toast
else if (data.status === 'complete') {
toast.success(data.message || "Step completed");
}
}
} catch (error) {
// Not JSON, just display as is // Not JSON, just display as is
} }
}; };
@@ -511,17 +521,23 @@ export function DataManagement() {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className=""> <div className="">
{tableStatus.map((table) => ( {tableStatus.length > 0 ? (
<div tableStatus.map((table) => (
key={table.table_name} <div
className="flex justify-between text-sm items-center py-2 border-b last:border-0" key={table.table_name}
> className="flex justify-between text-sm items-center py-2 border-b last:border-0"
<span className="font-medium">{table.table_name}</span> >
<span className="text-sm text-gray-600"> <span className="font-medium">{table.table_name}</span>
{formatStatusTime(table.last_sync_timestamp)} <span className="text-sm text-gray-600">
</span> {formatStatusTime(table.last_sync_timestamp)}
</span>
</div>
))
) : (
<div className="text-sm text-muted-foreground py-4 text-center">
No imports have been performed yet.<br/>Run a full update or reset to import data.
</div> </div>
))} )}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@@ -532,17 +548,23 @@ export function DataManagement() {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className=""> <div className="">
{moduleStatus.map((module) => ( {moduleStatus.length > 0 ? (
<div moduleStatus.map((module) => (
key={module.module_name} <div
className="flex justify-between text-sm items-center py-2 border-b last:border-0" key={module.module_name}
> className="flex justify-between text-sm items-center py-2 border-b last:border-0"
<span className="font-medium">{module.module_name}</span> >
<span className="text-sm text-gray-600"> <span className="font-medium">{module.module_name}</span>
{formatStatusTime(module.last_calculation_timestamp)} <span className="text-sm text-gray-600">
</span> {formatStatusTime(module.last_calculation_timestamp)}
</span>
</div>
))
) : (
<div className="text-sm text-muted-foreground py-4 text-center">
No metrics have been calculated yet.<br/>Run a full update or reset to calculate metrics.
</div> </div>
))} )}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>