Files
manage/.pi-tmp/followup1-wiring-out.md
T
Developer a8dfbd5dc6 Cleanup: delete dead top-level pages + update docs (Slice 11)
Delete the old top-level page files whose content was migrated into
service-page tabs in slices 5-9:
- pages/Media.tsx, Applications.tsx (-> MediaTab)
- pages/FileBrowser.tsx, FileBrowser.impl.tsx (-> FilesTab)
- pages/Actions.tsx (-> ActionsTab)
- pages/Users.tsx, UsersPage.impl.tsx (replaced by Authentik tabs)
- components/BackupsPage.tsx (-> JobsTab)
- components/ObservabilityPage.tsx (split into Alerts/Links/Metrics tabs)
- hooks/useUsers.ts (orphaned after Users page deletion)
- the corresponding page test files (Media, FileBrowser, Applications,
  Actions, UsersPage) that tested the deleted pages directly.

The service-tab components are the live implementations; ServicePage
renders them. No live code references the deleted files.

Docs: append an Information Architecture section to REQUIREMENTS.md
documenting the services-as-hub model (nav shape, service-page tabs,
service type registry, Users->Authentik, Observability split, legacy
route 404s, empty state). Add a CHANGELOG entry under [Unreleased].

92 frontend tests pass (was 112; -20 deleted page tests); 271 backend
tests pass; lint/build green.

Refs openspec/changes/services-as-hub-ia/ (tasks slice 11).
2026-06-26 20:11:02 +00:00

3.9 KiB

Follow-up 1 — SheetForm isDirty wiring (worker output)

Task

Wire the new isDirty prop of SheetForm into three remaining form consumers (Settings machine editor, message compose, WidgetConfigDialog) so unsaved edits trigger a "Discard changes?" confirm before closing.

Files changed (this worker's scope)

File Status Lines
frontend/src/pages/Settings.tsx modified +25 (isMachineDraftDirty helper + isDirty prop)
frontend/src/pages/UsersPage.impl.tsx modified +5 (isDirty prop on compose SheetForm)
frontend/src/components/WidgetConfigDialog.tsx modified +1 (isDirty prop)
frontend/src/pages/__tests__/Settings.test.tsx modified +23 (dirty guard test)
frontend/src/pages/__tests__/UsersPage.test.tsx modified +31 (compose dirty guard test)
frontend/src/components/__tests__/WidgetConfigDialog.test.tsx modified +14 (draft dirty guard test)

Total: ~99 changed lines — well under the 250-line budget.

isDirty expressions per consumer

1. Settings machine editor (Settings.tsx)

Helper function isMachineDraftDirty(draft, editingMachine):

  • Create mode (editingMachine === null): always dirty (return true).
  • Edit mode: field-by-field comparison of user-editable fields:
    • name, host, mode, port, username, ssh_key_id, enabled, notes
    • services array (sorted JSON.stringify comparison for order-insensitivity)
function isMachineDraftDirty(draft, editingMachine): boolean {
  if (!editingMachine) return true;
  return (
    draft.name !== editingMachine.name ||
    draft.host !== editingMachine.host ||
    draft.mode !== editingMachine.mode ||
    draft.port !== editingMachine.port ||
    draft.username !== editingMachine.username ||
    draft.ssh_key_id !== editingMachine.ssh_key_id ||
    draft.enabled !== editingMachine.enabled ||
    draft.notes !== editingMachine.notes ||
    JSON.stringify([...draft.services].sort()) !==
      JSON.stringify([...editingMachine.services].sort())
  );
}

Note: node_exporter_scrape_host (mentioned in the task) does not exist in either MonitoringMachine or MonitoringMachineInput in this codebase. The comparable editable fields were used instead. Secret fields (ssh_private_key, password) are excluded because they're write-only (the original only has *_set booleans, not values).

2. Message compose (UsersPage.impl.tsx)

isDirty={
  subject.trim() !== "" ||
  htmlBody.trim() !== DEFAULT_HTML_BODY.trim() ||
  attachments.length > 0
}

3. WidgetConfigDialog (WidgetConfigDialog.tsx)

isDirty={draft !== null}

Dirty only in draft mode (when adding/editing a widget). In list mode, draft === nullisDirty = false (nothing to discard). In draft mode, onCancel={reset} returns to the list (does NOT close the sheet), so isDirty prompts before resetting the draft.

Validation

npm run lint   → 0 errors, 2 pre-existing warnings (UsersPage.impl.tsx exhaustive-deps)
npm run build  → ✓ built (tsc -b + vite)
npm run test   → 28 files / 122 tests passed (was 119; +3 new dirty-guard tests)

Deviations from task

  1. node_exporter_scrape_host field: mentioned in the task but does not exist in the type definitions. Used the actual editable fields that exist on both MonitoringMachine and MonitoringMachineInput.
  2. Secret fields excluded from dirty check: ssh_private_key, password, ssh_private_key_passphrase are write-only on the draft and have no comparable value on editingMachine (which only has *_set booleans). Including them would make the form always dirty in edit mode.

skill_resolution

none — no project/user SKILL.md paths were injected; no .atl/skill-registry.md found.

Residual risks

  • None for this worker's scope. The SheetForm primitive and ServicePage wiring were done by the parent and are not touched here.