feat(scheduler): group schedule action picker by operator intent (#1446)

* feat(scheduler): group schedule action picker by operator intent

Reorganize the New Schedule action picker from a flat dropdown to a
category-grouped list (Lifecycle, Updates, Security, Maintenance, Backups).

- Extend Combobox component with optional group field on ComboboxOption,
  rendering grouped sections with non-interactive headers when groups are
  present. Flat rendering is unchanged for all other callers.
- Reorder SCHEDULED_ACTIONS by category group and update seven action
  labels per the operator-intent spec.
- Add DEFAULT_SCHEDULED_ACTION_ID constant so picker order and form
  defaults are independently controllable.
- Wire grouped actionOptions into ScheduledOperationsView.
- Update all label references in docs and tests.
- Add Combobox grouping tests, registry order test, and default-constant
  test.

* fix(scheduler): correct Combobox grouping for interleaved groups, docs labels

- Replace last-group-append with Map-based group partitioning so
  interleaved or mixed-group options land in the correct group.
- Add interleaved-groups test and restore non-interactivity test.
- Update stale "Start Stack" references to "Start / Bring Up Stack"
  in doc action-label contexts.
- Update action-picker alt text to describe the new grouped order.
This commit is contained in:
Anso
2026-06-25 01:40:31 -04:00
committed by GitHub
parent 3a22f59057
commit cc78873e65
11 changed files with 267 additions and 62 deletions
@@ -7,6 +7,7 @@ import { describe, it, expect } from 'vitest';
import {
SCHEDULED_ACTIONS,
SCHEDULED_ACTION_CATEGORIES,
DEFAULT_SCHEDULED_ACTION_ID,
getActionById,
resolveTaskAction,
type BackendAction,
@@ -69,4 +70,27 @@ describe('scheduledActions registry', () => {
expect(resolveTaskAction({ action: 'bogus' as BackendAction, target_type: 'system' })).toBeUndefined();
});
});
it('exports DEFAULT_SCHEDULED_ACTION_ID as restart', () => {
expect(DEFAULT_SCHEDULED_ACTION_ID).toBe('restart');
});
it('orders actions by category group', () => {
const ids = SCHEDULED_ACTIONS.map(a => a.id);
// Verify the exact order: lifecycle first, then updates, security, maintenance, backups.
expect(ids).toEqual([
'auto_backup', 'auto_start', 'restart', 'auto_stop', 'auto_down',
'update', 'update-fleet',
'scan',
'prune',
'snapshot',
]);
});
it('preserves the update-fleet alias after resorting', () => {
const fleetUpdate = SCHEDULED_ACTIONS.find(a => a.id === 'update-fleet');
expect(fleetUpdate).toBeDefined();
expect(fleetUpdate!.backendAction).toBe('update');
expect(fleetUpdate!.targetType).toBe('fleet');
});
});