Frontend Automate Orchestration
This commit is contained in:
@@ -61,6 +61,12 @@ export function AdminPage() {
|
||||
// sent something while you were looking at a different screen. A failed poll
|
||||
// is swallowed: a transient network blip must not replace the panel with an
|
||||
// error, and the next tick retries.
|
||||
//
|
||||
// Expect a steady zero on a deployment running UPLOAD_AUTORUN=true: uploads
|
||||
// go straight into the pipeline and never wait for review, so there is
|
||||
// nothing to be notified about. The badge renders only above zero, so that
|
||||
// reads as "nothing to do" rather than as a broken counter, and the poll
|
||||
// stays because the setting can be turned off without a redeploy.
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
const check = async () => {
|
||||
|
||||
@@ -81,6 +81,7 @@ function fmtSecs(from, to) {
|
||||
|
||||
export function OrchestrationPanel() {
|
||||
const [inbox, setInbox] = useState(null);
|
||||
const [recent, setRecent] = useState([]);
|
||||
const [selected, setSelected] = useState(() => new Set());
|
||||
const [batch, setBatch] = useState(null);
|
||||
const [busy, setBusy] = useState('');
|
||||
@@ -104,9 +105,34 @@ export function OrchestrationPanel() {
|
||||
}
|
||||
};
|
||||
|
||||
// Runs that started without anyone here pressing anything.
|
||||
//
|
||||
// UPLOAD_AUTORUN sends a colleague's upload straight into the pipeline, so
|
||||
// the inbox it used to wait in is empty and the run has an id this screen was
|
||||
// never told. Without this list an auto-started batch is invisible: its
|
||||
// eleven stages are recorded and served, and nothing renders them. Slim
|
||||
// responses, so twenty of these do not carry twenty product manifests.
|
||||
const loadRecent = async () => {
|
||||
try {
|
||||
const { batches } = await api.listCatalogBatches(20);
|
||||
setRecent(batches || []);
|
||||
} catch {
|
||||
/* the inbox call surfaces auth/network trouble; one banner is enough */
|
||||
}
|
||||
};
|
||||
|
||||
const watch = (run) => {
|
||||
setBatch(run);
|
||||
batchIdRef.current = TERMINAL.has(run.status) ? null : run.batch_id;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadInbox();
|
||||
const timer = setInterval(loadInbox, INBOX_POLL_MS);
|
||||
loadRecent();
|
||||
const timer = setInterval(() => {
|
||||
loadInbox();
|
||||
loadRecent();
|
||||
}, INBOX_POLL_MS);
|
||||
return () => clearInterval(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
@@ -210,10 +236,12 @@ export function OrchestrationPanel() {
|
||||
<Workflow className="h-5 w-5 text-amber-500" /> Dagster Orchestration
|
||||
</h2>
|
||||
<p className="text-xs text-slate-500 leading-relaxed max-w-3xl">
|
||||
Spreadsheets sent in by a colleague through the upload API. Tick the files you
|
||||
want and press <strong>Start selected</strong> to send them through the 11-stage
|
||||
pipeline as one batch, run by Dagster instead of this server's own worker.
|
||||
Every stage each file passes through is recorded below and kept after the run
|
||||
Spreadsheets sent in by a colleague through the upload API. Those uploads now
|
||||
start on their own — pick one under <strong>Recent runs</strong> to follow
|
||||
its stages. Anything still waiting for a decision appears below, where you can
|
||||
tick files and press <strong>Start selected</strong> to send them through the
|
||||
11-stage pipeline as one batch, run by Dagster instead of this server's own
|
||||
worker. Every stage each file passes through is recorded and kept after the run
|
||||
finishes.
|
||||
</p>
|
||||
</div>
|
||||
@@ -286,13 +314,71 @@ export function OrchestrationPanel() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ---- runs that started without anyone pressing anything -------- */}
|
||||
{recent.length > 0 && (
|
||||
<div className="rounded-2xl border border-ink-900/10 bg-paper-50 p-5 shadow-xs">
|
||||
<h3 className="font-display text-sm font-bold text-ink-950 mb-1">Recent runs</h3>
|
||||
<p className="text-[11px] text-slate-500 mb-3">
|
||||
Uploads start on arrival, so this is where a colleague's files show up.
|
||||
Pick one to see its eleven stages.
|
||||
</p>
|
||||
<ul className="space-y-1.5">
|
||||
{recent.map((run) => {
|
||||
const Icon = FILE_ICON[run.status] || Clock;
|
||||
const live = !TERMINAL.has(run.status);
|
||||
return (
|
||||
<li key={run.batch_id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => watch(run)}
|
||||
className={`w-full flex items-center gap-3 px-3 py-2 rounded-lg border text-left transition cursor-pointer hover:border-amber-500/40 ${
|
||||
batch?.batch_id === run.batch_id
|
||||
? 'border-amber-500/40 bg-amber-500/5'
|
||||
: 'border-ink-900/10 bg-white'
|
||||
}`}
|
||||
>
|
||||
<Icon
|
||||
className={`h-4 w-4 shrink-0 ${live ? 'text-amber-600' : 'text-slate-400'} ${
|
||||
run.status === 'running' ? 'animate-spin' : ''
|
||||
}`}
|
||||
/>
|
||||
<span className="font-mono text-[11px] text-slate-500 shrink-0">
|
||||
{run.batch_id.slice(0, 8)}
|
||||
</span>
|
||||
<span className="text-xs text-ink-950 truncate flex-1">
|
||||
{run.files_total} file(s)
|
||||
{run.submitted_by ? ` from ${run.submitted_by}` : ''}
|
||||
</span>
|
||||
<span
|
||||
className={`shrink-0 px-2 py-0.5 rounded-md border text-[10px] font-bold ${
|
||||
FILE_TONE[run.status] || FILE_TONE.queued
|
||||
}`}
|
||||
>
|
||||
{run.status}
|
||||
</span>
|
||||
<span className="shrink-0 text-[10px] text-slate-400 font-mono w-16 text-right">
|
||||
{ago(run.created_at)}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ---- the drops waiting to be started --------------------------- */}
|
||||
{inbox && submissions.length === 0 && !batch && (
|
||||
<div className="rounded-2xl border border-ink-900/10 bg-paper-50 p-10 text-center">
|
||||
<Workflow className="h-8 w-8 text-slate-300 mx-auto mb-3" />
|
||||
<p className="text-xs text-slate-500">
|
||||
Nothing waiting. Files sent to <code className="font-mono">/api/uploads/catalog</code>{' '}
|
||||
appear here to be orchestrated.
|
||||
Nothing waiting for review — files sent to{' '}
|
||||
<code className="font-mono">/api/uploads/catalog</code> start on their own and
|
||||
appear under Recent runs.
|
||||
</p>
|
||||
<p className="text-[11px] text-slate-400 mt-2">
|
||||
Set <code className="font-mono">UPLOAD_AUTORUN=false</code> to hold them here for
|
||||
a decision instead.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user