Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
89 lines
6.2 KiB
Markdown
89 lines
6.2 KiB
Markdown
---
|
|
phase: 12-initial-setup-wizard
|
|
plan: 05
|
|
subsystem: setup-wizard-frontend
|
|
tags: [setup, pwa, uat-gap-closure, a11y]
|
|
requires:
|
|
- "GET /api/setup/status { setupComplete, dbName } (Plan 06)"
|
|
- "SetupStatusResponse.dbName?: string | null typed field (Plan 06)"
|
|
provides:
|
|
- "Instance step intro copy trimmed (no DB-vs-env-file aside)"
|
|
- "Read-only, disabled DB-name field under App URL, populated from status dbName"
|
|
- "Instance field values lifted to SetupPage so Back navigation preserves them"
|
|
affects:
|
|
- apps/pwa setup wizard Instance step (SetupPage.tsx)
|
|
tech-stack:
|
|
added: []
|
|
patterns:
|
|
- "useQuery({ queryKey: ['setupStatus'], queryFn: fetchSetupStatus }) reads non-secret dbName into a read-only field"
|
|
- "Step-level field values lifted to the parent (SetupPage) so step unmount no longer drops entries"
|
|
- "Sensitive app password deliberately NOT lifted — stays in Step3 local state, cleared on unmount (T-12-15)"
|
|
key-files:
|
|
created: []
|
|
modified:
|
|
- apps/pwa/src/routes/SetupPage.tsx
|
|
- apps/pwa/src/routes/SetupPage.test.tsx
|
|
decisions:
|
|
- "D-12-05-LIFT: only the four non-secret Instance fields are lifted to SetupPage; the Fastmail app password is never lifted or persisted (T-12-15 preserved)."
|
|
- "D-12-05-DBNAME: DB-name field renders the dbName value only; DB_HOST/DB_PORT/DB_USER/DB_PASSWORD appear solely as static env-var names in helper text, never as values (T-12-3DB)."
|
|
- "D-12-05-VALSTATE: Step2 validation state (db/oidc/vapid pass flags) is intentionally NOT lifted — only field values persist across Back; operator re-runs Save & Validate after returning."
|
|
metrics:
|
|
duration_minutes: 9
|
|
completed: 2026-06-16
|
|
---
|
|
|
|
# Phase 12 Plan 05: Instance-Step Gap Closure (copy trim, DB-name field, Back persistence) Summary
|
|
|
|
Closed UAT gaps 1, 3 (frontend half), and 4 on the PWA Instance step (`SetupPage.tsx`): dropped the confusing DB-vs-env-file implementation aside, added a read-only env-derived DB-name field so the "database connection verified" row has an on-screen referent, and lifted the four Instance field values into `SetupPage` so navigating Back from the Calendar step no longer wipes entered config.
|
|
|
|
## What Was Built
|
|
|
|
### Task 1 — Drop DB-vs-env aside + add read-only DB-name field (gaps 1, 3-frontend)
|
|
Commit `35db5c5`.
|
|
|
|
- **Gap 1**: Removed the sentence "These are written to the database — not your environment file." from the Instance step intro `<p>`, keeping the first sentence ("Enter your instance's connection details.").
|
|
- **Gap 3 (frontend)**: Added a labelled, `readOnly` + `disabled` input ("Database") directly under the App URL field, populated from `fetchSetupStatus().dbName` via `useQuery({ queryKey: ['setupStatus'], staleTime: 0, retry: false })`. The field is greyed out (`--color-surface-dim` background, `--color-text-secondary` text), carries `aria-readonly="true"` and `tabIndex={-1}`, and shows `—` while loading/null. Helper text explains the DB is configured via the server's Docker environment (DB_HOST/DB_PORT/DB_USER/DB_PASSWORD as static names) and is not entered here. The existing "Database connection verified." ValidationRow is unchanged.
|
|
- Tests assert the dropped sentence is absent, the DB field renders `readOnly`/`disabled`/`aria-readonly` with the mocked `dbName: 'familysync'`, and the existing DB validation row still appears on Save & Validate.
|
|
|
|
### Task 2 — Preserve Instance fields across Back navigation (gap 4)
|
|
Commit `a13fc11`.
|
|
|
|
- Introduced an `InstanceFields` shape (`appUrl`, `oidcIssuer`, `oidcClientId`, `vapidPublicKey`) owned by `SetupPage` (`instanceFields` / `setInstanceFields`), passed to `Step2Config` as `fields` / `setFields` props. `Step2Config` now reads/writes these through the lifted setters instead of its own local `useState`. Validation/mutation logic is unchanged.
|
|
- The Fastmail app password (Step 3) is **not** lifted — it remains in `Step3Credential` local state and is cleared on unmount when navigating away (T-12-15 preserved).
|
|
- Tests: filling the Instance step, validating to GREEN, advancing to the Calendar step, then clicking Back restores all four Instance values; a second test confirms a typed app password is empty after Back→forward (Step 3 re-mounts fresh).
|
|
|
|
## Verification
|
|
|
|
- `cd apps/pwa && pnpm test -- SetupPage` → **263 passed (22 files)**.
|
|
- `cd apps/pwa && pnpm typecheck` → clean (tsc + e2e tsconfig).
|
|
- `grep -c "not your environment file" apps/pwa/src/routes/SetupPage.tsx` → **0**.
|
|
- `grep -c "dangerouslySetInnerHTML" apps/pwa/src/routes/SetupPage.tsx` → **0**.
|
|
- `grep -nE "sessionStorage|localStorage" apps/pwa/src/routes/SetupPage.tsx` → **no matches** (no client-side persistence of any field, secret or otherwise).
|
|
- `grep -c "readOnly" apps/pwa/src/routes/SetupPage.tsx` → **1** (the DB-name field).
|
|
- DB_HOST/DB_PORT/DB_USER/DB_PASSWORD appear only as static env-var names in helper/error copy — never fetched or rendered as values.
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written. Implementation note: the two tasks both restructure the `Step2Config` signature/body and the same intro paragraph, so they were authored together and then committed as two atomic, individually-GREEN commits (Task 1 commit verified GREEN with 261 tests before Task 2's state-lifting and Back-navigation tests were added).
|
|
|
|
## Threat Surface
|
|
|
|
| Threat ID | Disposition | Outcome |
|
|
|-----------|-------------|---------|
|
|
| T-12-15 (app password disclosure) | mitigate | Preserved — password stays in Step3 local state, type="password", NOT lifted, NOT persisted to storage; cleared on unmount. Test asserts it is empty after Back→forward. |
|
|
| T-12-14 (XSS in Instance/DB copy) | mitigate | All new copy + dbName rendered as plain-text JSX children; `dangerouslySetInnerHTML` grep = 0. |
|
|
| T-12-3DB (DB secret/topology disclosure) | mitigate | Only `dbName` value is fetched and rendered; DB_HOST/DB_PORT/DB_USER/DB_PASSWORD appear solely as static env-var names in helper text. |
|
|
|
|
No new security-relevant surface introduced beyond the planned `threat_model`.
|
|
|
|
## Known Stubs
|
|
|
|
None.
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- `apps/pwa/src/routes/SetupPage.tsx` — modified, exists.
|
|
- `apps/pwa/src/routes/SetupPage.test.tsx` — modified, exists.
|
|
- Commit `35db5c5` (Task 1) — FOUND in git log.
|
|
- Commit `a13fc11` (Task 2) — FOUND in git log.
|