gridops-ai / frontend /src /ProgressScreen.jsx
Aadithya
feat: migrate to Chronos-2 zero-shot with temperature covariates
62f95cd
Raw
History Blame Contribute Delete
7.19 kB
import React, { useState, useEffect } from 'react';
const STAGE_INFO = {
DATA_PIPELINE: {
label: 'Data Pipeline',
description:
'Loading 16 years of PJM hourly energy readings, scanning for gaps and outliers, ' +
'then fitting a SARIMA statistical model to establish a classical baseline forecast.',
estimatedSeconds: '15 – 25s',
},
CHRONOS_INFERENCE: {
label: 'Chronos Inference',
description:
'Running the native zero-shot Amazon Chronos-2 model (120M parameters) via PyTorch ' +
'to generate probabilistic forecasts with p10 / p50 / p90 confidence intervals.',
estimatedSeconds: '10 – 20s',
},
AGENT_REASONING: {
label: 'LangGraph Agent',
description:
'Executing the 7-node LangGraph pipeline: divergence analysis, seasonality detection, ' +
'RAG retrieval from the event database, VaR quantification, and mandate synthesis.',
estimatedSeconds: '10 – 15s',
},
};
const PHASE_ORDER = ['DATA_PIPELINE', 'CHRONOS_INFERENCE', 'AGENT_REASONING'];
export function ProgressScreen({ progress }) {
const [elapsed, setElapsed] = useState(0);
useEffect(() => {
const timer = setInterval(() => setElapsed(prev => prev + 1), 1000);
return () => clearInterval(timer);
}, []);
const currentStage = progress.stage || 'DATA_PIPELINE';
const currentPct = progress.progress || 0;
const currentInfo = STAGE_INFO[currentStage] || STAGE_INFO.DATA_PIPELINE;
const currentPhaseIdx = PHASE_ORDER.indexOf(currentStage);
return (
<div className="progress-page animation-fade-in" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
<div className="progress-hero" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: '32px' }}>
<h1 className="loading-title" style={{ fontSize: '32px', marginBottom: '12px' }}>
Executing GridOps AI Pipeline
</h1>
<p className="loading-subtitle" style={{ maxWidth: '520px', margin: '0 auto' }}>
{currentInfo.description}
</p>
<div style={{ marginTop: '12px', fontSize: '14px', color: 'var(--accent-amber)', fontFamily: "'JetBrains Mono', monospace", fontWeight: 600 }}>
ETA: 1.5 – 2.5 minutes
</div>
</div>
<div className="spinner-container" style={{ minHeight: 'auto', padding: '0 0 40px 0', width: '100%', maxWidth: '560px' }}>
<div className="spinner" style={{ margin: '0 auto 32px auto' }} />
{/* Step progress */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px', marginBottom: '28px', textAlign: 'left' }}>
{PHASE_ORDER.map((key, i) => {
const info = STAGE_INFO[key];
const isDone = i < currentPhaseIdx;
const isActive = i === currentPhaseIdx;
const isPending = i > currentPhaseIdx;
return (
<div key={key} style={{
display: 'flex', alignItems: 'flex-start', gap: '14px',
padding: '14px 18px',
borderRadius: '10px',
background: isActive
? 'rgba(6, 182, 212, 0.08)'
: isDone
? 'rgba(16, 185, 129, 0.06)'
: 'rgba(255,255,255,0.02)',
border: `1px solid ${isActive
? 'rgba(6, 182, 212, 0.25)'
: isDone
? 'rgba(16, 185, 129, 0.2)'
: 'rgba(255,255,255,0.06)'}`,
transition: 'all 0.4s ease',
}}>
{/* Status dot */}
<div style={{
width: '20px', height: '20px', borderRadius: '50%', flexShrink: 0, marginTop: '2px',
display: 'flex', alignItems: 'center', justifyContent: 'center',
background: isActive
? 'rgba(6, 182, 212, 0.15)'
: isDone
? 'rgba(16, 185, 129, 0.15)'
: 'rgba(255,255,255,0.05)',
border: `2px solid ${isActive ? 'var(--accent-cyan)' : isDone ? '#10b981' : 'rgba(255,255,255,0.12)'}`,
}}>
{isDone && (
<svg width="10" height="10" viewBox="0 0 10 10" fill="none">
<polyline points="1.5,5 4,7.5 8.5,2" stroke="#10b981" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)}
{isActive && (
<span style={{ width: '6px', height: '6px', borderRadius: '50%', background: 'var(--accent-cyan)', boxShadow: '0 0 6px rgba(6,182,212,0.8)' }} />
)}
</div>
{/* Text */}
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '3px' }}>
<span style={{
fontSize: '13px', fontWeight: 700,
fontFamily: "'JetBrains Mono', monospace",
color: isActive ? 'var(--accent-cyan)' : isDone ? '#10b981' : 'var(--text-tertiary)',
}}>
{info.label}
</span>
{isActive && (
<span style={{ fontSize: '11px', color: 'var(--accent-cyan)', fontFamily: "'JetBrains Mono', monospace" }}>
{currentPct}%
</span>
)}
{isDone && (
<span style={{ fontSize: '11px', color: '#10b981' }}>Complete</span>
)}
{isPending && (
<span style={{ fontSize: '11px', color: 'var(--text-tertiary)' }}>{info.estimatedSeconds}</span>
)}
</div>
<p style={{ margin: 0, fontSize: '12px', color: isActive ? 'var(--text-secondary)' : 'var(--text-tertiary)', lineHeight: 1.5 }}>
{info.description}
</p>
</div>
</div>
);
})}
</div>
{/* Progress bar */}
<div style={{ width: '100%', height: '4px', background: 'rgba(255,255,255,0.06)', borderRadius: '2px', marginBottom: '16px', overflow: 'hidden' }}>
<div style={{
height: '100%',
width: `${currentPct}%`,
background: 'linear-gradient(90deg, rgba(6,182,212,0.8), var(--accent-cyan))',
borderRadius: '2px',
transition: 'width 0.6s ease',
boxShadow: '0 0 8px rgba(6,182,212,0.4)',
}} />
</div>
{/* Elapsed timer */}
<div style={{ fontSize: '18px', fontFamily: '"JetBrains Mono", monospace', color: 'var(--accent-cyan)', fontWeight: 'bold' }}>
{Math.floor(elapsed / 60)}:{(elapsed % 60).toString().padStart(2, '0')}
</div>
<div style={{ fontSize: '13px', color: 'var(--text-tertiary)', marginTop: '6px' }}>
{progress.message || 'Initializing pipeline…'}
</div>
</div>
</div>
);
}