docs(18-04): complete admin timezone UI plan
This commit is contained in:
@@ -636,7 +636,7 @@ Plans:
|
|||||||
**Goal:** Make the household timezone an explicit, stored, user-changeable setting — auto-detected from the browser at first run, changeable from the role-gated /admin Settings — and route the server-side all-day "9 AM local" reminder computation through it (replacing the implicit `process.env.TZ` fallback), without touching the already-correct browser-local display/timed-write path.
|
**Goal:** Make the household timezone an explicit, stored, user-changeable setting — auto-detected from the browser at first run, changeable from the role-gated /admin Settings — and route the server-side all-day "9 AM local" reminder computation through it (replacing the implicit `process.env.TZ` fallback), without touching the already-correct browser-local display/timed-write path.
|
||||||
**Requirements**: TBD (decision contract D-01..D-07 from 18-CONTEXT.md)
|
**Requirements**: TBD (decision contract D-01..D-07 from 18-CONTEXT.md)
|
||||||
**Depends on:** Phase 10 (admin role + `/admin` Settings + `app_config`); Phase 11 (all-day reminder computation this rewires). Independent of Phase 17. Phase 12 (setup wizard) not required — seeding is self-contained.
|
**Depends on:** Phase 10 (admin role + `/admin` Settings + `app_config`); Phase 11 (all-day reminder computation this rewires). Independent of Phase 17. Phase 12 (setup wizard) not required — seeding is self-contained.
|
||||||
**Plans:** 3/4 plans executed
|
**Plans:** 4/4 plans complete
|
||||||
|
|
||||||
Plans:
|
Plans:
|
||||||
**Wave 1**
|
**Wave 1**
|
||||||
@@ -650,6 +650,6 @@ Plans:
|
|||||||
|
|
||||||
**Wave 3** *(blocked on Wave 2 completion)*
|
**Wave 3** *(blocked on Wave 2 completion)*
|
||||||
|
|
||||||
- [ ] 18-04-PLAN.md — PWA Timezone section in /admin Settings (searchable IANA picker + detected-zone seed) + client fns (D-02/D-04)
|
- [x] 18-04-PLAN.md — PWA Timezone section in /admin Settings (searchable IANA picker + detected-zone seed) + client fns (D-02/D-04)
|
||||||
|
|
||||||
**UI hint**: yes
|
**UI hint**: yes
|
||||||
|
|||||||
+8
-7
@@ -2,16 +2,16 @@
|
|||||||
gsd_state_version: 1.0
|
gsd_state_version: 1.0
|
||||||
milestone: v1.1
|
milestone: v1.1
|
||||||
milestone_name: Operability & Polish
|
milestone_name: Operability & Polish
|
||||||
status: executing
|
status: verifying
|
||||||
stopped_at: Phase 18 Plan 03 complete — broker rewire done; plan 4 of 4 is next
|
stopped_at: Phase 18 Plan 03 complete — broker rewire done; plan 4 of 4 is next
|
||||||
last_updated: "2026-06-15T02:33:25.687Z"
|
last_updated: "2026-06-15T02:46:09.819Z"
|
||||||
last_activity: 2026-06-15
|
last_activity: 2026-06-15
|
||||||
progress:
|
progress:
|
||||||
total_phases: 23
|
total_phases: 23
|
||||||
completed_phases: 9
|
completed_phases: 10
|
||||||
total_plans: 36
|
total_plans: 36
|
||||||
completed_plans: 35
|
completed_plans: 36
|
||||||
percent: 39
|
percent: 43
|
||||||
---
|
---
|
||||||
|
|
||||||
# Project State
|
# Project State
|
||||||
@@ -27,7 +27,7 @@ See: .planning/PROJECT.md (updated 2026-06-10)
|
|||||||
|
|
||||||
Phase: 18 (auto-timezone-detection-and-ability-to-change-timezone) — EXECUTING
|
Phase: 18 (auto-timezone-detection-and-ability-to-change-timezone) — EXECUTING
|
||||||
Plan: 4 of 4
|
Plan: 4 of 4
|
||||||
Status: Ready to execute
|
Status: Phase complete — ready for verification
|
||||||
Last activity: 2026-06-15
|
Last activity: 2026-06-15
|
||||||
|
|
||||||
### ✅ Resolved Checkpoint — Phase 15 Plan 15-03 Task 2 (human-action)
|
### ✅ Resolved Checkpoint — Phase 15 Plan 15-03 Task 2 (human-action)
|
||||||
@@ -110,6 +110,7 @@ _Updated after each plan completion_
|
|||||||
| Phase 18-auto-timezone-detection-and-ability-to-change-timezone P01 | 2 | 2 tasks | 2 files |
|
| Phase 18-auto-timezone-detection-and-ability-to-change-timezone P01 | 2 | 2 tasks | 2 files |
|
||||||
| Phase 18 P02 | 3 | 2 tasks | 2 files |
|
| Phase 18 P02 | 3 | 2 tasks | 2 files |
|
||||||
| Phase 18 P03 | 28 | 2 tasks | 4 files |
|
| Phase 18 P03 | 28 | 2 tasks | 4 files |
|
||||||
|
| Phase 18 P04 | 15 | 3 tasks | 3 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -256,7 +257,7 @@ Recent decisions affecting current work:
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-06-15T02:33:25.669Z
|
Last session: 2026-06-15T02:46:09.800Z
|
||||||
Stopped at: Phase 18 Plan 03 complete — broker rewire done; plan 4 of 4 is next
|
Stopped at: Phase 18 Plan 03 complete — broker rewire done; plan 4 of 4 is next
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|
||||||
|
|||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
---
|
||||||
|
phase: 18-auto-timezone-detection-and-ability-to-change-timezone
|
||||||
|
plan: "04"
|
||||||
|
subsystem: pwa
|
||||||
|
tags: [timezone, iana, admin, react, tanstack-query, playwright, ui]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 18-02
|
||||||
|
provides: GET/PUT /api/admin/config/timezone (consumed by fetchAdminTimezone/setAdminTimezone)
|
||||||
|
|
||||||
|
provides:
|
||||||
|
- fetchAdminTimezone() — GET /api/admin/config/timezone in PWA API client
|
||||||
|
- setAdminTimezone(tz) — PUT /api/admin/config/timezone in PWA API client
|
||||||
|
- AdminTimezoneResponse interface (timezone: string, isExplicitlySet: boolean)
|
||||||
|
- Timezone section in AdminPage (/admin) — searchable IANA picker, save, detected-zone affordance
|
||||||
|
|
||||||
|
affects:
|
||||||
|
- apps/pwa/src/api/client.ts
|
||||||
|
- apps/pwa/src/routes/AdminPage.tsx
|
||||||
|
- apps/pwa/e2e/timezone-verify.spec.ts (new verification spec)
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- Intl.supportedValuesOf('timeZone') guarded for runtime availability (datalist population)
|
||||||
|
- ARIA combobox role for <input type="text" list="..."> — Playwright locator uses getByRole('combobox') not getByRole('textbox')
|
||||||
|
- useQuery + useMutation with invalidateQueries on success (same pattern as calendars section)
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- apps/pwa/e2e/timezone-verify.spec.ts
|
||||||
|
modified:
|
||||||
|
- apps/pwa/src/api/client.ts
|
||||||
|
- apps/pwa/src/routes/AdminPage.tsx
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Removed AdminTimezoneResponse from AdminPage.tsx import list — ESLint no-unused-vars; type is inferred from useQuery return"
|
||||||
|
- "Input with list= attribute has ARIA combobox role in Chromium, not textbox — Playwright e2e uses getByRole('combobox')"
|
||||||
|
- "timezoneInput state is null when the user has not interacted — effectiveTimezoneInput = timezoneInput ?? storedTimezone preserves stored value as pre-fill"
|
||||||
|
- "setTimezoneInput(null) on mutation success resets local override so save button re-disables to match new stored value"
|
||||||
|
|
||||||
|
requirements-completed: [D-02, D-04]
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 15min
|
||||||
|
completed: 2026-06-15
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 18 Plan 04: Admin Timezone UI Summary
|
||||||
|
|
||||||
|
**Searchable IANA timezone picker in /admin Settings — fetchAdminTimezone/setAdminTimezone in client.ts + Timezone section in AdminPage.tsx — verified end-to-end via Playwright with the real 18-02 endpoints**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** ~15 min
|
||||||
|
- **Started:** 2026-06-15T02:30:00Z
|
||||||
|
- **Completed:** 2026-06-15T02:45:03Z
|
||||||
|
- **Tasks:** 3 (2 auto + 1 playwright-verified checkpoint)
|
||||||
|
- **Files modified/created:** 3
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Added `AdminTimezoneResponse` interface + `fetchAdminTimezone()` + `setAdminTimezone()` to `apps/pwa/src/api/client.ts`, following the exact pattern of `fetchAdminCalendars` / `setSharedCalendar` (credentials: include, redirect: manual, handleAuthResponse)
|
||||||
|
- Added Timezone section to `AdminPage.tsx` after the Shared Calendar section (with `marginBottom` on the preceding section for spacing)
|
||||||
|
- Timezone section features: 4-state loading/error/data pattern; `<input type="text" list="iana-zones">` + `<datalist>` from `Intl.supportedValuesOf('timeZone')` (guarded); "Using system default" notice when `isExplicitlySet=false`; "Use detected: {zone}" affordance (D-02); disabled Save when pending or unchanged; `timezoneMutation.mutate(tz)` on save; `invalidateQueries(['admin','timezone'])` on success
|
||||||
|
- Created `apps/pwa/e2e/timezone-verify.spec.ts` with 6 desktop Playwright tests — all 6 pass against the live 18-02 API endpoints, including the save + persist across reload flow
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
1. **Task 1: Add fetchAdminTimezone + setAdminTimezone to PWA API client** - `57424e6` (feat)
|
||||||
|
2. **Task 2: Add Timezone section to AdminPage** - `43d6689` (feat)
|
||||||
|
3. **Task 3: Playwright timezone round-trip e2e spec** - `3013b53` (test)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `apps/pwa/src/api/client.ts` — added `AdminTimezoneResponse` interface + `fetchAdminTimezone()` + `setAdminTimezone()` in the `/api/admin/*` section
|
||||||
|
- `apps/pwa/src/routes/AdminPage.tsx` — added `timezoneQuery`, `timezoneMutation`, `timezoneInput` state, `detectedTz`, computed values, `<section aria-label="Timezone">` with IANA picker + affordances + save button
|
||||||
|
- `apps/pwa/e2e/timezone-verify.spec.ts` — new 6-test Playwright spec verifying the full round-trip
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- `AdminTimezoneResponse` type import removed from `AdminPage.tsx` — `@typescript-eslint/no-unused-vars` flagged it (type is inferred from `useQuery` return value, not used as an explicit annotation). ESLint clean.
|
||||||
|
- `<input type="text" list="iana-zones">` has ARIA `combobox` role (not `textbox`) in Chromium — discovered via Playwright page snapshot. Updated e2e locators to `getByRole('combobox')`.
|
||||||
|
- The `timezoneInput` state variable is `null` when the user hasn't typed anything; `effectiveTimezoneInput = timezoneInput ?? storedTimezone` ensures the input shows the stored value on load without the save button enabling prematurely.
|
||||||
|
- After mutation success, `setTimezoneInput(null)` resets the local override so the save button re-disables (effectiveTimezoneInput collapses back to the now-updated storedTimezone).
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 1 - Bug] Removed unused `AdminTimezoneResponse` import**
|
||||||
|
- **Found during:** Task 2 (ESLint check)
|
||||||
|
- **Issue:** `type AdminTimezoneResponse` was imported but not used as an explicit type annotation; ESLint `no-unused-vars` reported it as an error
|
||||||
|
- **Fix:** Removed from the import line; the type is inferred via `useQuery`
|
||||||
|
- **Files modified:** `apps/pwa/src/routes/AdminPage.tsx`
|
||||||
|
- **Commit:** `43d6689`
|
||||||
|
|
||||||
|
**2. [Rule 3 - Blocking] API dev container serving stale compiled code**
|
||||||
|
- **Found during:** Task 3 (playwright verification)
|
||||||
|
- **Issue:** The Docker container running the API had the pre-Plan-18-02 `dist/routes/admin.js` baked in — the timezone endpoints returned 404. The `--watch dist/index.js` watcher only restarts on `index.js` change, not sub-module changes.
|
||||||
|
- **Fix:** Rebuilt the API (`pnpm --filter @familysync/api build`), then `docker cp` the updated `dist/routes/admin.js`, `dist/lib/householdTimezone.js`, and `dist/index.js` into the container to trigger a restart.
|
||||||
|
- **Files modified:** container-only (not tracked in git)
|
||||||
|
- **Commit:** N/A (operational, not code)
|
||||||
|
|
||||||
|
## Known Stubs
|
||||||
|
|
||||||
|
None — the Timezone section is fully wired to the real `GET /api/admin/config/timezone` and `PUT /api/admin/config/timezone` endpoints (Plan 18-02). No placeholder data.
|
||||||
|
|
||||||
|
## Threat Flags
|
||||||
|
|
||||||
|
No new threat surface beyond the plan's threat model. T-18-11 (client isAdmin gate is UX only), T-18-12 (server validates IANA via Plan 02 Zod refine), T-18-13 (plain-text JSX children, no dangerouslySetInnerHTML).
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
- `apps/pwa/src/api/client.ts` — FOUND (modified, exports fetchAdminTimezone + setAdminTimezone + AdminTimezoneResponse)
|
||||||
|
- `apps/pwa/src/routes/AdminPage.tsx` — FOUND (modified, contains `aria-label="Timezone"`)
|
||||||
|
- `apps/pwa/e2e/timezone-verify.spec.ts` — FOUND (created, 6 desktop tests all pass)
|
||||||
|
- Commit `57424e6` — FOUND
|
||||||
|
- Commit `43d6689` — FOUND
|
||||||
|
- Commit `3013b53` — FOUND
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 18-auto-timezone-detection-and-ability-to-change-timezone*
|
||||||
|
*Completed: 2026-06-15*
|
||||||
Reference in New Issue
Block a user