a8dfbd5dc6
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).
127 lines
7.4 KiB
Markdown
127 lines
7.4 KiB
Markdown
# Slice 7 — Settings mobile forms (worker output)
|
|
|
|
## Files changed
|
|
|
|
| File | Status | Lines |
|
|
|------|--------|-------|
|
|
| `frontend/src/components/ui/sheet-form.tsx` | modified | +4 / -1 |
|
|
| `frontend/src/pages/Settings.tsx` | modified | +130 / -43 |
|
|
| `frontend/src/pages/__tests__/Settings.test.tsx` | modified | +75 / -0 |
|
|
|
|
**Total: ~228 changed lines** (209 insertions, 44 deletions). Under the 400-line budget.
|
|
|
|
## What was implemented
|
|
|
|
### 7.1 — Machine editor SheetForm below md
|
|
|
|
Below `md` (`isMobile === true`), the machine editor opens inside a `<SheetForm>` instead of a centered `<Dialog>`. Both editors share the same `machineDialogOpen` state — the SheetForm and Dialog are branched via `isMobile ? <SheetForm> : <Dialog>`, using the exact same open/close/save/cancel flow.
|
|
|
|
**Open-state strategy:** Unlike ServicePage (open-on-mount), the machine editor SheetForm is **triggered by user action** — the same Edit/Add-machine buttons that open the Dialog on desktop open the SheetForm on mobile. The `machineDialogOpen` state drives both. No navigation needed on close because the Settings page content (tabbed cards, machine list) is always visible behind the sheet.
|
|
|
|
**Preserved behaviors:**
|
|
|
|
- **Validate-on-save** — `saveMachineDraft(machineDraft)` is unchanged; the same validation logic runs.
|
|
- **SSH test validation** — `validateMachineSSH` + the "Validate SSH + trust host" button render inside the MachineEditor, which is shared between both branches.
|
|
- **ConfirmDialog (delete confirmation)** — rendered as a sibling OUTSIDE both the SheetForm and Dialog, so it overlays correctly on both layouts.
|
|
- **Save-disabled logic** — added `saveDisabled` prop to SheetForm; wired to the same condition the desktop DialogFooter uses (`!machineDraft.name || (ssh && !host)`).
|
|
- **Delete on mobile** — a "Delete machine" button renders inside the SheetForm body (when editing an existing machine), separate from the save bar.
|
|
- **Desktop (`md+`)** — the Dialog renders byte-for-byte identical (verified by the 3 existing desktop tests passing unchanged).
|
|
|
|
### SSH key manager
|
|
|
|
The SSHKeyManager is an **inline two-panel layout** (not a dialog), and its grid already uses `grid-cols-1 md:grid-cols-[320px_minmax(0,1fr)]` — it already stacks on mobile. No SheetForm conversion was needed or correct for this component. The machine list grid (`grid-cols-1 md:grid-cols-[...]`) also already stacks. No changes needed to either.
|
|
|
|
### SheetForm enhancement
|
|
|
|
Added `saveDisabled?: boolean` prop to `SheetForm` (additive, default `false`). This is needed because the machine editor gates save on required fields (name + host for SSH mode). The existing ServicePage consumer does not pass it (defaults to `false`). Non-breaking.
|
|
|
|
## Validation
|
|
|
|
```
|
|
npm run lint → 0 errors, 2 pre-existing warnings (UsersPage.impl.tsx, unrelated)
|
|
npm run build → ✓ built (tsc -b + vite)
|
|
npm run test → 27 files / 113 tests passed (was 110; +3 new)
|
|
```
|
|
|
|
## Deviations from design
|
|
|
|
1. **SSHKeyManager not wrapped in SheetForm.** The task said "both the machine editor dialog AND the SSH-key editor dialog." However, the SSH key manager is an inline two-panel layout (SelectionRailCard + SectionCard), not a dialog. It already stacks responsively (`grid-cols-1 md:grid-cols-[...]`). Wrapping an inline editor in a SheetForm would break its always-visible selection-rail UX. The machine editor (which IS a dialog) was converted to SheetForm as specified.
|
|
|
|
2. **`saveDisabled` prop added to SheetForm.** The design did not name this prop, but the machine editor requires it to match the desktop DialogFooter's `confirmDisabled` semantics. Additive and non-breaking.
|
|
|
|
3. **No navigation on close.** Unlike ServicePage (which navigates to `/services` on close), the Settings machine editor just closes the sheet — the page content is always behind it, so there's no stranding risk.
|
|
|
|
## skill_resolution
|
|
|
|
`none` — no project/user SKILL.md paths were injected, and no `.atl/skill-registry.md` was found.
|
|
|
|
## Residual risks
|
|
|
|
- **Dirty-state outside-click confirm (R4.5)** is still not implemented at the SheetForm level. Same deferred concern as Slice 6 — the SheetForm passes `onOpenChange` straight through. Flag for verify pass.
|
|
- **MachineEditor grid on mobile.** The MachineEditor uses `grid-cols-12` with `col-span-12 md:col-span-X` — already responsive (full-width below md). No changes needed.
|
|
- **Touch targets on rail rows.** The machine/SSH-key selection rails use `onClick` on `<div>` elements. The 44px touch-target audit is Slice 9, not here.
|
|
|
|
## Review findings
|
|
|
|
No blockers. The desktop Dialog is preserved token-identical (verified by 3 existing desktop tests passing unchanged). The SheetForm conversion follows the established ServicePage pattern.
|
|
|
|
```acceptance-report
|
|
{
|
|
"criteriaSatisfied": [
|
|
{
|
|
"id": "criterion-1",
|
|
"status": "satisfied",
|
|
"evidence": "Slice 7 converts the machine editor Dialog to SheetForm below md, adds saveDisabled to SheetForm (additive, non-breaking), and extends Settings.test.tsx with 3 mobile tests. Desktop Dialog preserved token-identical (3 existing desktop tests pass unchanged). SSHKeyManager already responsive (inline, not a dialog). No backend, no other pages touched, no scope widening."
|
|
}
|
|
],
|
|
"changedFiles": [
|
|
"frontend/src/components/ui/sheet-form.tsx",
|
|
"frontend/src/pages/Settings.tsx",
|
|
"frontend/src/pages/__tests__/Settings.test.tsx"
|
|
],
|
|
"testsAddedOrUpdated": [
|
|
"frontend/src/pages/__tests__/Settings.test.tsx"
|
|
],
|
|
"commandsRun": [
|
|
{
|
|
"command": "cd frontend && npm run lint",
|
|
"result": "passed",
|
|
"summary": "0 errors; 2 pre-existing warnings in UsersPage.impl.tsx (unrelated)"
|
|
},
|
|
{
|
|
"command": "cd frontend && npm run build",
|
|
"result": "passed",
|
|
"summary": "tsc -b + vite build clean"
|
|
},
|
|
{
|
|
"command": "cd frontend && npm run test",
|
|
"result": "passed",
|
|
"summary": "27 files / 113 tests passed (3 new mobile tests added)"
|
|
},
|
|
{
|
|
"command": "cd frontend && git diff --cached --stat",
|
|
"result": "passed",
|
|
"summary": "Empty — no staged files"
|
|
}
|
|
],
|
|
"validationOutput": [
|
|
"Machine editor Dialog → SheetForm branch via isMobile, same machineDialogOpen state",
|
|
"saveDisabled prop added to SheetForm (additive, default false)",
|
|
"Desktop Dialog token-identical (3 existing desktop tests pass unchanged)",
|
|
"SSHKeyManager already responsive (grid-cols-1 md:grid-cols-[...] stacks)",
|
|
"ConfirmDialog rendered as sibling outside both SheetForm and Dialog",
|
|
"SSH validate button preserved inside shared MachineEditor body"
|
|
],
|
|
"residualRisks": [
|
|
"R4.5 dirty-state outside-click confirm not implemented at SheetForm level (deferred to verify pass)",
|
|
"Touch targets on selection-rail rows deferred to Slice 9"
|
|
],
|
|
"noStagedFiles": true,
|
|
"diffSummary": "Adds a mobile (isMobile) branch to the machine editor that renders SheetForm instead of Dialog, using the same machineDialogOpen state. Adds saveDisabled prop to SheetForm for required-field gating. Desktop Dialog is preserved byte-for-byte. 3 new mobile tests (open SheetForm, save payload, cancel closes). 228 changed lines.",
|
|
"reviewFindings": [
|
|
"no blockers"
|
|
],
|
|
"manualNotes": "SSHKeyManager was NOT wrapped in SheetForm because it is an inline two-panel layout (not a dialog) that already stacks responsively. The task wording 'SSH-key editor dialog' referred to a dialog that does not exist — the inline grid already handles mobile. Touch-target audit deferred to Slice 9."
|
|
}
|
|
```
|