mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
fix(scheduler): show every Timeline lane dot and rename the Maintenance lane to Upkeep (#1618)
The Timeline lane marker lacked shrink-0, so inside the fixed 80px label column the widest labels flex-shrank their color dot to zero width. The Maintenance and Lifecycle dots collapsed and rendered as no dot at all, while the shorter labels kept theirs. Add shrink-0 so every lane dot renders at a consistent size. Maintenance was the only label wide enough to fill the 80px column on its own, which is what left its dot no room. Rename it to Upkeep so all five labels fit and align. A registry test now pins each lane's key, order, and label, and asserts every lane carries a color, so a future rewrite of the action registry cannot silently rename a lane or drop a dot color.
This commit is contained in:
@@ -26,7 +26,7 @@ Open the **Schedules** tab from the top navigation bar. The page opens on the Ti
|
||||
The Timeline plots every firing of every enabled task across a rolling 24-hour window starting from the current minute.
|
||||
|
||||
- **Masthead.** A `NEXT 24 HOURS` kicker, an italic display heading, the window's start and end timestamps in a monospace range, and a right-anchored **Next** pill that reads out the time and task name of the next firing and a relative countdown.
|
||||
- **Five lanes.** Lifecycle (label blue), Updates (success green), Security (label purple), Maintenance (warning amber), and Backups (brand cyan). The Lifecycle lane holds stack lifecycle actions (Backup Stack Compose Files, Start / Bring Up Stack, Restart Stack, Stop Stack, Take Stack Down) and standalone container actions (Restart Container, Stop Container, Start Container); Updates holds per-node and fleet image updates; Security holds vulnerability scans; Maintenance holds node resource prunes; Backups holds fleet snapshots.
|
||||
- **Five lanes.** Lifecycle (label blue), Updates (success green), Security (label purple), Upkeep (warning amber), and Backups (brand cyan). The Lifecycle lane holds stack lifecycle actions (Backup Stack Compose Files, Start / Bring Up Stack, Restart Stack, Stop Stack, Take Stack Down) and standalone container actions (Restart Container, Stop Container, Start Container); Updates holds per-node and fleet image updates; Security holds vulnerability scans; Upkeep holds node resource prunes; Backups holds fleet snapshots.
|
||||
- **Pills.** One pill per firing within the window, positioned proportionally to the firing's time. Each pill shows the firing time and a target: the stack for stack actions, the container name for container actions, the selected node for prune and scan, and "Entire fleet" for a fleet snapshot. Hover a pill for the full detail (action, task name, and node). Pills are color-matched to their lane. Click a pill to open the run history sheet for that task.
|
||||
- **Now rail.** A glowing vertical rail at the current minute, anchored to the left of the track at page open and drifting right as time passes (the page recomputes positions periodically).
|
||||
- **Axis.** Six monospace time ticks run along the bottom, evenly spaced through the window.
|
||||
@@ -74,7 +74,7 @@ The All tasks toggle swaps the lane track for a sortable table.
|
||||
|
||||
## Creating a scheduled task
|
||||
|
||||
Click **New Schedule** in the header. The form opens in a centered modal. The Action picker lists every supported operation, grouped by category: Lifecycle, Updates, Security, Maintenance, and Backups.
|
||||
Click **New Schedule** in the header. The form opens in a centered modal. The Action picker lists every supported operation, grouped by category: Lifecycle, Updates, Security, Upkeep, and Backups.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/scheduled-operations/action-picker.png" alt="The New scheduled task modal with the Action combobox expanded. The dropdown groups actions under category headers: Stack lifecycle (Backup Stack Compose Files, Start / Bring Up Stack, Restart Stack, Stop Stack, Take Stack Down), Updates (Auto-update Stack, Auto-update All Stacks on Node), Security (Scan Node Images), Maintenance (Prune Node Resources), and Backups (Create Fleet Snapshot). Below the picker, partly visible, sit a Services row with 'echo' and 'prober' checkboxes, the Cron Expression input, the Enabled toggle, and the Delete after successful run checkbox." />
|
||||
|
||||
@@ -621,7 +621,7 @@ export default function ScheduledOperationsView({ filterNodeId, onClearFilter, p
|
||||
<div key={lane.key} className="grid grid-cols-[80px_1fr] items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="w-1.5 h-1.5 rounded-full"
|
||||
className="w-1.5 h-1.5 rounded-full shrink-0"
|
||||
style={{ backgroundColor: lane.color }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
@@ -373,7 +373,7 @@ describe('ScheduledOperationsView', () => {
|
||||
it('renders the five registry category lanes in the timeline view', async () => {
|
||||
render(<ScheduledOperationsView />);
|
||||
// Timeline is the default view; the lane track always renders.
|
||||
for (const lane of ['Lifecycle', 'Updates', 'Security', 'Maintenance', 'Backups']) {
|
||||
for (const lane of ['Lifecycle', 'Updates', 'Security', 'Upkeep', 'Backups']) {
|
||||
expect(await screen.findByText(lane)).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -49,6 +49,25 @@ describe('scheduledActions registry', () => {
|
||||
}
|
||||
});
|
||||
|
||||
describe('timeline lanes', () => {
|
||||
it('pins each lane key, order, and label so a registry rewrite cannot silently rename or reorder one', () => {
|
||||
expect(SCHEDULED_ACTION_CATEGORIES.map(lane => [lane.key, lane.label])).toEqual([
|
||||
['lifecycle', 'Lifecycle'],
|
||||
['updates', 'Updates'],
|
||||
['security', 'Security'],
|
||||
['maintenance', 'Upkeep'],
|
||||
['backups', 'Backups'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('gives every lane a non-empty color and background so its Timeline dot always renders', () => {
|
||||
for (const lane of SCHEDULED_ACTION_CATEGORIES) {
|
||||
expect(lane.color.trim()).not.toBe('');
|
||||
expect(lane.bg.trim()).not.toBe('');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('marks scan and prune as local-node actions', () => {
|
||||
expect(getActionById('scan')).toMatchObject({ requiresNode: true, nodeScope: 'local' });
|
||||
expect(getActionById('prune')).toMatchObject({ requiresNode: true, nodeScope: 'local' });
|
||||
|
||||
@@ -183,6 +183,6 @@ export const SCHEDULED_ACTION_CATEGORIES: ScheduledActionCategoryLane[] = [
|
||||
{ key: 'lifecycle', label: 'Lifecycle', color: 'var(--label-blue)', bg: 'var(--label-blue-bg)' },
|
||||
{ key: 'updates', label: 'Updates', color: 'var(--success)', bg: 'oklch(from var(--success) l c h / 0.18)' },
|
||||
{ key: 'security', label: 'Security', color: 'var(--label-purple)', bg: 'var(--label-purple-bg)' },
|
||||
{ key: 'maintenance', label: 'Maintenance', color: 'var(--warning)', bg: 'oklch(from var(--warning) l c h / 0.18)' },
|
||||
{ key: 'maintenance', label: 'Upkeep', color: 'var(--warning)', bg: 'oklch(from var(--warning) l c h / 0.18)' },
|
||||
{ key: 'backups', label: 'Backups', color: 'var(--brand)', bg: 'oklch(from var(--brand) l c h / 0.18)' },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user