Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/frontend/packages/swarm-ui/src/pages/schedules/ScheduleCard.tsx
iris 0af7f53e88 swarm-ui: schedules page says what a past start does, no longer claims a one-shot fired
A start in the past fires the latest missed time once, right away, so the
form says that instead of 'already passed'. A one-shot past its time reads
'past' / 'time passed' (the controller's clock, not a record of firing) and
keeps its pause button. The edit form compares against what it opened with,
so a schedule changing under it is not reverted on save.
2026-10-04 17:49:42 +02:00

88 lines
2.6 KiB
TypeScript

// <ScheduleCard> — one scheduled prompt as a card: its name as the title,
// "agents: foo, bar" beneath, then the message, the cadence and when it
// fires next. Edit, pause/resume and remove sit at the foot; edit swaps the
// card for `ScheduleForm` (the page owns that swap).
import { Badge, type BadgeTone } from "@hive/shared/badge.js";
import { Button } from "../../ui/button/Button.js";
import { Card } from "../../ui/card/Card.js";
import { RelativeTime } from "../../ui/relative-time/RelativeTime.js";
import {
fmtInterval,
fmtLocal,
scheduleState,
type Schedule,
type ScheduleState,
} from "./ScheduleTypes.js";
import "./ScheduleCard.css";
const STATE_TONE: Record<ScheduleState, BadgeTone> = {
active: "positive",
paused: "warning",
past: "neutral",
};
export function ScheduleCard({
schedule,
pending,
onEdit,
onTogglePaused,
onRemove,
}: {
schedule: Schedule;
/** A pause/resume or remove for this schedule is in flight. */
pending: boolean;
onEdit: () => void;
onTogglePaused: () => void;
onRemove: () => void;
}) {
const state = scheduleState(schedule);
return (
<Card class="ui-schedule-card">
<div class="ui-schedule-card-head">
<h3 class="ui-schedule-card-name">{schedule.name}</h3>
<Badge tone={STATE_TONE[state]} value={state} />
<Badge
value={
schedule.interval_seconds === null
? "once"
: `every ${fmtInterval(schedule.interval_seconds)}`
}
/>
</div>
<div class="ui-schedule-card-agents">
agents: {schedule.agents.join(", ")}
</div>
<p class="ui-schedule-card-body">{schedule.body}</p>
<div class="ui-schedule-card-next">
{schedule.next_fire_unix !== null ? (
<>
next fire:{" "}
<span title={fmtLocal(schedule.next_fire_unix)}>
<RelativeTime epochMs={schedule.next_fire_unix * 1000} upcoming />
</span>
</>
) : state === "paused" ? (
"paused, will not fire"
) : (
<>
time passed{" "}
<span title={fmtLocal(schedule.anchor_unix)}>
<RelativeTime epochMs={schedule.anchor_unix * 1000} />
</span>
</>
)}
</div>
<div class="ui-schedule-card-actions">
<Button onClick={onEdit} disabled={pending}>
edit
</Button>
<Button onClick={onTogglePaused} disabled={pending}>
{schedule.paused ? "resume" : "pause"}
</Button>
<Button onClick={onRemove} disabled={pending}>
remove
</Button>
</div>
</Card>
);
}