dashboard: scheduled prompts tab + creation form (closes #459)
frontend for the #444 scheduled-prompts feature. backend is already merged (PR #454 + sibling commits): GET /api/schedules (snapshot), POST /api/schedules (operator-direct submit), POST /api/schedules/{id}/cancel (whole or per-target). new chrome - SCH3DUL3S tab in the dashboard tab strip, between SYST3M and the FL0W link. count pill shows the number of schedules with at least one still-active target. - pane has two sections: N3W SCH3DUL3 (creation form) + QU3U3D SCH3DUL3S (list of cards). creation form - targets multi-select rendered as chip-style checkboxes; candidates pulled from containersState plus the special `operator` and `manager` recipients. - prompt body textarea (required, non-empty trim). - first-fire datetime-local input, defaulted to "5 minutes from now" so the form has a sensible pre-filled future timestamp. - optional interval (seconds) input — blank = one-shot. - optional description (one-liner shown on the schedule card). - mid-typing carry: re-rendering the form preserves field values + checkbox state. the operator never loses what they were typing when the schedule list refreshes underneath them. - POSTs SchedulePromptPayload JSON to /api/schedules; on success re-fetches the list to surface the new row. schedules list - one card per schedule, active rows first (sorted by next_fire_at_unix), cancelled tail dimmed. - header: id, source chip (`operator` or `approval` — reuses the rebuild-queue rqe-source styling for visual consistency), next-fire countdown / overdue label, recurring vs one-shot badge, owner. - body: prompt text in a styled <pre>-ish block with linkified path references. - per-target table: target name, last fire age, last result, per-row cancel button. - whole-schedule "✕ cancel all" button. - per-target cancel posts { targets: ["name"] }; cancel-all posts no body (== cancel whole row). no-SSE refresh - the backend doesn't emit SchedulesChanged dashboard events yet (damocles flagged this as a follow-up PR C). list re-fetches on: - tab activation (so switching to SCH3DUL3S never lands stale) - cold load via refreshState - after every submit + cancel POST the operator's typical interactions all force a refresh; the remaining gap (worker fires while you're staring at the tab) is the natural argument for PR C. files - frontend/packages/dashboard/src/index.html — new tab + pane with the two sections. - frontend/packages/dashboard/src/app.js — schedulesState cache, refreshSchedules, render*ScheduleNewForm, submit + cancel helpers, tab routing extended for `schedules`, count pill wired into refreshTabCounts. - frontend/packages/dashboard/src/dashboard.css — schedule form + card styling + chip checkboxes + targets table. small .btn-inline-small helper for the per-target cancel. validation - npm run build --workspace=@hive/dashboard clean. app.js 158 kb → 161 kb. CSS 41.3 kb → 43.9 kb. - browser smoke test isn't possible from inside iris's container; endpoints are wire-compatible (backend types unchanged) and the form serialisation matches SchedulePromptPayload's JSON shape exactly (targets / body / first_fire_at_unix / interval_seconds / description).
This commit is contained in:
parent
2593896383
commit
c233f8a924
3 changed files with 495 additions and 1 deletions
|
|
@ -1599,6 +1599,131 @@ body.flow-shell .tabbar .tab.active.tab-link {
|
|||
surface the messages via the pill/flyout instead. */
|
||||
.flow-inbox-headless { display: none !important; }
|
||||
|
||||
/* ─── scheduled prompts tab (#459) ─────────────────────────────────────
|
||||
Creation form at the top, list of queued schedule cards below.
|
||||
Cards show: id + source + due-in + cancel-all in the header,
|
||||
the prompt body, then a targets table with per-row cancel. */
|
||||
|
||||
.schedule-new-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6em;
|
||||
background: var(--bg-elev);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 0.8em 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.schedule-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2em;
|
||||
}
|
||||
.schedule-field-label {
|
||||
color: var(--muted);
|
||||
font-size: 0.85em;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.schedule-field input,
|
||||
.schedule-field textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
padding: 0.35em 0.5em;
|
||||
}
|
||||
.schedule-field textarea { resize: vertical; min-height: 4em; }
|
||||
.schedule-targets {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4em;
|
||||
}
|
||||
.schedule-target-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4em;
|
||||
padding: 0.2em 0.6em;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
font-size: 0.85em;
|
||||
cursor: pointer;
|
||||
background: rgba(24, 24, 37, 0.4);
|
||||
}
|
||||
.schedule-target-chip:hover { border-color: var(--purple-dim); }
|
||||
.schedule-target-chip:has(input:checked) {
|
||||
border-color: var(--purple);
|
||||
color: var(--purple);
|
||||
background: rgba(203, 166, 247, 0.08);
|
||||
}
|
||||
.schedule-target-chip input { margin: 0; }
|
||||
.schedule-actions {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.schedules {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
.schedule-row {
|
||||
padding: 0.6em 0.9em;
|
||||
background: rgba(24, 24, 37, 0.55);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4em;
|
||||
}
|
||||
.schedule-row.schedule-cancelled { opacity: 0.55; }
|
||||
.schedule-head {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.4em;
|
||||
}
|
||||
.schedule-description {
|
||||
color: var(--muted);
|
||||
font-style: italic;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
.schedule-body {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
padding: 0.5em 0.7em;
|
||||
}
|
||||
.schedule-targets-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.schedule-targets-table th,
|
||||
.schedule-targets-table td {
|
||||
padding: 0.25em 0.6em;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.schedule-targets-table th {
|
||||
color: var(--muted);
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.schedule-targets-table tr:last-child td { border-bottom: 0; }
|
||||
.schedule-target-cancelled td { opacity: 0.5; }
|
||||
.btn-inline-small {
|
||||
font-size: 0.7em;
|
||||
padding: 0.1em 0.45em;
|
||||
}
|
||||
|
||||
/* Selection bar (#443). Sticky-bottom strip that surfaces bulk
|
||||
actions when ≥1 agent is selected (click the icon). Visually
|
||||
echoes the flow composer's frosted-mauve treatment so the chrome
|
||||
|
|
|
|||
Loading…
Reference in a new issue