diff --git a/src/pages/AdminPage.jsx b/src/pages/AdminPage.jsx
index c3e5c1a..babe249 100644
--- a/src/pages/AdminPage.jsx
+++ b/src/pages/AdminPage.jsx
@@ -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 () => {
diff --git a/src/pages/OrchestrationPanel.jsx b/src/pages/OrchestrationPanel.jsx
index 9417600..5c04ea1 100644
--- a/src/pages/OrchestrationPanel.jsx
+++ b/src/pages/OrchestrationPanel.jsx
@@ -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() {
- Spreadsheets sent in by a colleague through the upload API. Tick the files you - want and press Start selected 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 Recent runs to follow + its stages. Anything still waiting for a decision appears below, where you can + tick files and press Start selected 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.
@@ -286,13 +314,71 @@ export function OrchestrationPanel() { )} + {/* ---- runs that started without anyone pressing anything -------- */} + {recent.length > 0 && ( ++ Uploads start on arrival, so this is where a colleague's files show up. + Pick one to see its eleven stages. +
+
- Nothing waiting. Files sent to /api/uploads/catalog{' '}
- appear here to be orchestrated.
+ Nothing waiting for review — files sent to{' '}
+ /api/uploads/catalog start on their own and
+ appear under Recent runs.
+
+ Set UPLOAD_AUTORUN=false to hold them here for
+ a decision instead.