fix(#2650,#2649): drop reminders tab + pending_reminders chip
hive-c0re's broker reminder store and /api/reminders endpoint were removed in PR #2644 (reminders migrated to in-container sqlite store). The dashboard had a QU3U3D R3M1ND3RS section on the schedules tab backed by that endpoint, and a per-agent badge driven by pending_reminders (always 0 after the migration). Remove both. Per-agent reminders now surface through get_loose_ends / the todos pill on the agent page, consistent with the todos migration. - frontend/packages/dashboard/src/schedules.js: drop refreshReminders, renderReminders, applyRemindersChanged; drop appendLinkified import (no longer used); update module comment. - frontend/packages/dashboard/src/tabs.js: drop applyRemindersChanged and refreshReminders imports; remove reminders-section from managed list; remove refreshReminders call site; drop reminders_changed from SSE dispatch; simplify countdown ticker (reminder-due gone). - frontend/packages/dashboard/src/dashboard.html: remove QU3U3D R3M1ND3RS section. - frontend/packages/dashboard/src/dashboard.css: remove reminder list + row CSS. - frontend/packages/dashboard/src/common.css: remove .badge-reminder. - frontend/packages/dashboard/src/swarm.js: remove pending_reminders from fingerprint key and badge render.
This commit is contained in:
parent
ee072149c9
commit
fab2fdcc0a
6 changed files with 21 additions and 180 deletions
|
|
@ -1,114 +1,24 @@
|
|||
// Dashboard SCH3DUL3S tab — reminders + scheduled-prompts.
|
||||
// Dashboard SCH3DUL3S tab — scheduled-prompts.
|
||||
//
|
||||
// Reminders (a separate sqlite table, not part of /api/state) and the
|
||||
// operator's scheduled prompts: the lists, the inline create row, the
|
||||
// per-row edit form, and the fire-now / cancel actions. Live updates
|
||||
// arrive via the `reminders_changed` / `schedules_changed` dashboard
|
||||
// events (wired into the entry's mutation dispatch table); the
|
||||
// tab-activation re-fetch is the cold-load / reconnect recovery path.
|
||||
// The operator's scheduled prompts: the list, the inline create row,
|
||||
// the per-row edit form, and the fire-now / cancel actions. Live updates
|
||||
// arrive via the `schedules_changed` dashboard event (wired into the
|
||||
// entry's mutation dispatch table); the tab-activation re-fetch is the
|
||||
// cold-load / reconnect recovery path.
|
||||
//
|
||||
// Owns its own module state (the schedules list + edit-in-progress
|
||||
// tracking); reads the shared agent roster from state.js for the
|
||||
// target-chip pickers. Render + format helpers come from util.js.
|
||||
//
|
||||
// Note: per-agent reminders moved to the in-container store in PR #2644
|
||||
// and are no longer listed here — they surface via get_loose_ends /
|
||||
// the todos pill on the agent page instead.
|
||||
|
||||
import { $, el, appendLinkified } from './common.js';
|
||||
import { $, el } from './common.js';
|
||||
import { themedConfirm, themedToast } from './modal.js';
|
||||
import { paintAtomic, epochSec, fmtAgo, fmtDuration } from './util.js';
|
||||
import { containersState } from './state.js';
|
||||
import { asyncBtn } from '@hive/shared/forms.js';
|
||||
|
||||
export async function refreshReminders() {
|
||||
const liveRoot = $('reminders-section');
|
||||
if (!liveRoot) return;
|
||||
try {
|
||||
const resp = await fetch('/api/reminders');
|
||||
if (!resp.ok) {
|
||||
paintAtomic(liveRoot, (root) => {
|
||||
root.append(el('p', { class: 'empty' }, 'reminders unavailable: http ' + resp.status));
|
||||
});
|
||||
return;
|
||||
}
|
||||
const rows = await resp.json();
|
||||
renderReminders(rows);
|
||||
} catch (err) {
|
||||
paintAtomic(liveRoot, (root) => {
|
||||
root.append(el('p', { class: 'empty' }, 'reminders fetch failed: ' + err));
|
||||
});
|
||||
}
|
||||
}
|
||||
function renderReminders(rows) {
|
||||
const liveRoot = $('reminders-section');
|
||||
if (!liveRoot) return;
|
||||
paintAtomic(liveRoot, (root) => {
|
||||
if (!rows.length) {
|
||||
root.append(el('p', { class: 'empty' }, 'no queued reminders'));
|
||||
return;
|
||||
}
|
||||
const ul = el('ul', { class: 'reminders' });
|
||||
for (const r of rows) {
|
||||
const failed = (r.attempt_count || 0) > 0;
|
||||
const li = el('li', { class: 'reminder-row' + (failed ? ' reminder-failed' : '') });
|
||||
const dueIn = epochSec(r.due_at) - Math.floor(Date.now() / 1000);
|
||||
const dueLabel = dueIn <= 0
|
||||
? `overdue ${fmtAgo(r.due_at)}`
|
||||
: `in ${fmtDuration(dueIn)}`;
|
||||
const head = el('div', { class: 'reminder-head' },
|
||||
el('span', { class: 'agent' }, r.agent), ' ',
|
||||
el('span', {
|
||||
class: 'meta reminder-due',
|
||||
title: new Date(r.due_at).toISOString(),
|
||||
'data-due-at': String(epochSec(r.due_at)),
|
||||
}, dueLabel),
|
||||
' ',
|
||||
el('span', { class: 'meta' }, `· id ${r.id}`),
|
||||
);
|
||||
if (r.file_path) {
|
||||
head.append(' ', el('span', { class: 'meta' }, '· payload → '));
|
||||
appendLinkified(head, r.file_path);
|
||||
}
|
||||
if (failed) {
|
||||
head.append(' ', el('span',
|
||||
{
|
||||
class: 'badge badge-warn',
|
||||
title: 'consecutive failed delivery attempts (capped at 5; over the cap the scheduler stops retrying until you click R3TRY or cancel)',
|
||||
},
|
||||
`⚠ ${r.attempt_count} failed`));
|
||||
}
|
||||
const body = el('div', { class: 'reminder-body' });
|
||||
appendLinkified(body, r.message);
|
||||
li.append(head, body);
|
||||
if (r.last_error) {
|
||||
li.append(el('div', { class: 'reminder-error' },
|
||||
el('span', { class: 'msg-sep' }, 'error: '),
|
||||
r.last_error,
|
||||
));
|
||||
}
|
||||
const actions = el('div', { class: 'reminder-actions' });
|
||||
if (failed) {
|
||||
// Retry resets the failure counters so the scheduler picks
|
||||
// the row up again on its next 5s tick. No data-no-refresh
|
||||
// — the resulting refreshState re-fires refreshReminders.
|
||||
const retryForm = el('form', {
|
||||
method: 'POST', action: '/api/retry-reminder/' + r.id,
|
||||
class: 'inline', 'data-async': '',
|
||||
});
|
||||
retryForm.append(el('button',
|
||||
{ type: 'submit', class: 'btn btn-restart' }, '↻ R3TRY'));
|
||||
actions.append(retryForm);
|
||||
}
|
||||
const cancelForm = el('form', {
|
||||
method: 'POST', action: '/api/cancel-reminder/' + r.id,
|
||||
class: 'inline', 'data-async': '',
|
||||
'data-confirm': `cancel reminder ${r.id} for ${r.agent}? this drops the queued delivery; no undo.`,
|
||||
});
|
||||
cancelForm.append(el('button', { type: 'submit', class: 'btn btn-deny' }, '✗ C4NC3L'));
|
||||
actions.append(cancelForm);
|
||||
li.append(actions);
|
||||
ul.append(li);
|
||||
}
|
||||
root.append(ul);
|
||||
});
|
||||
}
|
||||
// ─── scheduled prompts ─────────────────────────────────────────────────
|
||||
// Backend exposes `/api/schedules` (snapshot), `/api/schedules`
|
||||
// (POST, operator-direct submit), `/api/schedules/{id}/cancel`
|
||||
|
|
@ -1144,6 +1054,3 @@ export function applySchedulesChanged(ev) {
|
|||
schedulesState = (ev.schedules || []).slice();
|
||||
renderSchedulesList();
|
||||
}
|
||||
export function applyRemindersChanged(ev) {
|
||||
renderReminders(ev.reminders || []);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue