style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125 insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
---
|
||||
phase: 03-event-write-back-pwa-install
|
||||
plan: "06"
|
||||
plan: '06'
|
||||
subsystem: pwa-frontend
|
||||
tags: [delete, sync-feedback, toast, confirmation-dialog, tdd, zustand, tanstack-query]
|
||||
dependency_graph:
|
||||
requires: ["03-03", "03-05"]
|
||||
provides: ["edit/delete vertical slices", "polled sync-state feedback toast"]
|
||||
affects: ["apps/pwa/src/components/CalendarShell.tsx", "apps/pwa/src/components/EventDetailPopover.tsx"]
|
||||
requires: ['03-03', '03-05']
|
||||
provides: ['edit/delete vertical slices', 'polled sync-state feedback toast']
|
||||
affects:
|
||||
['apps/pwa/src/components/CalendarShell.tsx', 'apps/pwa/src/components/EventDetailPopover.tsx']
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "refetchInterval: (query) => pending ? 3000 : false — conditional poll for SyncStateToast"
|
||||
- "useCalendarStore selector form for new keys — avoids CalendarShell re-renders"
|
||||
- "DeleteConfirmationDialog: useMutation + onSuccess wires lastSyncedUid then closes"
|
||||
- "SyncStateToast invalidateQueries on done/conflict (D-06/D-08); EventForm no longer self-invalidates"
|
||||
- 'refetchInterval: (query) => pending ? 3000 : false — conditional poll for SyncStateToast'
|
||||
- 'useCalendarStore selector form for new keys — avoids CalendarShell re-renders'
|
||||
- 'DeleteConfirmationDialog: useMutation + onSuccess wires lastSyncedUid then closes'
|
||||
- 'SyncStateToast invalidateQueries on done/conflict (D-06/D-08); EventForm no longer self-invalidates'
|
||||
key_files:
|
||||
created:
|
||||
- apps/pwa/src/components/SyncStateToast.tsx
|
||||
@@ -29,13 +30,13 @@ key_files:
|
||||
- apps/pwa/src/components/EventDetailPopover.test.tsx
|
||||
- apps/pwa/src/components/EventForm.test.tsx
|
||||
decisions:
|
||||
- "EventForm.onSuccess calls setLastSyncedUid(uid) instead of invalidateQueries — SyncStateToast owns the cache invalidation on done/conflict (D-06/D-08)"
|
||||
- "DeleteConfirmationDialog mounts unconditionally in CalendarShell (like SyncStateToast); renders null when closed — avoids conditional mount logic in shell"
|
||||
- "SyncStateToast refetchInterval callback form used (not a static number) so it reads current query data for the pending check"
|
||||
- "EventDetailPopover footer tests updated to support selector-form useCalendarStore calls (selector-aware mock pattern)"
|
||||
- 'EventForm.onSuccess calls setLastSyncedUid(uid) instead of invalidateQueries — SyncStateToast owns the cache invalidation on done/conflict (D-06/D-08)'
|
||||
- 'DeleteConfirmationDialog mounts unconditionally in CalendarShell (like SyncStateToast); renders null when closed — avoids conditional mount logic in shell'
|
||||
- 'SyncStateToast refetchInterval callback form used (not a static number) so it reads current query data for the pending check'
|
||||
- 'EventDetailPopover footer tests updated to support selector-form useCalendarStore calls (selector-aware mock pattern)'
|
||||
metrics:
|
||||
duration_minutes: 70
|
||||
completed: "2026-06-05"
|
||||
completed: '2026-06-05'
|
||||
tasks: 3
|
||||
files_created: 4
|
||||
files_modified: 7
|
||||
@@ -47,22 +48,24 @@ metrics:
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commit | Files |
|
||||
|------|------|--------|-------|
|
||||
| 1 | deleteEvent + fetchSyncStatus + Zustand delete/sync keys | `8aeacc8` | client.ts, calendarStore.ts |
|
||||
| 2 | SyncStateToast with polled sync-status (D-05/D-06/D-08/D-09) | `aa7c4c3` | SyncStateToast.tsx, CalendarShell.tsx, EventForm.tsx |
|
||||
| 3 | EventDetailPopover Edit/Delete footer + DeleteConfirmationDialog | `40322e1` | EventDetailPopover.tsx, DeleteConfirmationDialog.tsx, CalendarShell.tsx |
|
||||
| Task | Name | Commit | Files |
|
||||
| ---- | ---------------------------------------------------------------- | --------- | ----------------------------------------------------------------------- |
|
||||
| 1 | deleteEvent + fetchSyncStatus + Zustand delete/sync keys | `8aeacc8` | client.ts, calendarStore.ts |
|
||||
| 2 | SyncStateToast with polled sync-status (D-05/D-06/D-08/D-09) | `aa7c4c3` | SyncStateToast.tsx, CalendarShell.tsx, EventForm.tsx |
|
||||
| 3 | EventDetailPopover Edit/Delete footer + DeleteConfirmationDialog | `40322e1` | EventDetailPopover.tsx, DeleteConfirmationDialog.tsx, CalendarShell.tsx |
|
||||
|
||||
## What Was Built
|
||||
|
||||
### Task 1 — Client calls + Zustand keys (RED: `8357cf9`, GREEN: `8aeacc8`)
|
||||
|
||||
**`apps/pwa/src/api/client.ts`:**
|
||||
|
||||
- `deleteEvent(uid): Promise<void>` — DELETE `/api/events/:uid`, credentials:include, throws on !ok
|
||||
- `fetchSyncStatus(uid): Promise<SyncStatus>` — GET `/api/events/sync-status?uid=`
|
||||
- Exported types: `SyncStatusValue`, `SyncStatus`
|
||||
|
||||
**`apps/pwa/src/store/calendarStore.ts`:**
|
||||
|
||||
- `deleteDialogOpen: boolean` — default false
|
||||
- `deleteDialogUid: string | null` — default null
|
||||
- `lastSyncedUid: string | null` — drives SyncStateToast polling
|
||||
@@ -72,6 +75,7 @@ metrics:
|
||||
### Task 2 — SyncStateToast (RED: `6874e1a`, GREEN: `aa7c4c3`)
|
||||
|
||||
**`apps/pwa/src/components/SyncStateToast.tsx`** (210 lines):
|
||||
|
||||
- `useQuery(['syncStatus', lastSyncedUid], fetchSyncStatus)` with `refetchInterval` callback — 3000ms while pending, disabled on terminal status
|
||||
- States per UI-SPEC: pending (Loader2 spinner, "Syncing…"), done (Check, "Saved"), failed generic (AlertCircle, "Didn't save. Try again."), failed conflict/412 (conflict copy), dead ("Not saved. Check your connection.")
|
||||
- `role="status"` for pending/done; `role="alert"` for failed/dead
|
||||
@@ -87,12 +91,14 @@ metrics:
|
||||
### Task 3 — EventDetailPopover footer + DeleteConfirmationDialog (RED: `2fbeffe`, GREEN: `40322e1`)
|
||||
|
||||
**`apps/pwa/src/components/EventDetailPopover.tsx`:**
|
||||
|
||||
- Replaced `aria-hidden="true"` reserved footer placeholder with a live flex row
|
||||
- Left: "Edit" ghost button (Edit2 icon, `--color-text-primary`) — calls `setEventForm(true, 'edit', uid)` + closes popover
|
||||
- Right: "Delete" ghost button (Trash2 icon, `--color-destructive`) — calls `setDeleteDialog(true, uid)`
|
||||
- Both buttons: 44px touch targets, plain-text label children
|
||||
|
||||
**`apps/pwa/src/components/DeleteConfirmationDialog.tsx`** (208 lines):
|
||||
|
||||
- Centered modal, max-width 320px, `--color-overlay` backdrop, focus trap
|
||||
- `role="dialog"`, `aria-modal="true"`, Escape to cancel
|
||||
- Heading "Delete event?" (18px/600), body "This will be removed from your Fastmail calendar."
|
||||
@@ -123,6 +129,7 @@ grep -q "Delete event?" apps/pwa/src/components/DeleteConfirmationDialog.tsx →
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] EventForm.test.tsx mock lacked setLastSyncedUid**
|
||||
|
||||
- **Found during:** Task 2 GREEN
|
||||
- **Issue:** EventForm calls `useCalendarStore((s) => s.setLastSyncedUid)` (selector form). The existing test mock returned a static object regardless of selector, so the selector call returned the full mock object instead of the setter function.
|
||||
- **Fix:** Updated both the `vi.mock` factory and the `renderForm` helper's `mockImplementation` to support the selector call pattern — `if (typeof selector === 'function') return selector(state)`.
|
||||
@@ -130,24 +137,28 @@ grep -q "Delete event?" apps/pwa/src/components/DeleteConfirmationDialog.tsx →
|
||||
- **Commit:** `aa7c4c3`
|
||||
|
||||
**2. [Rule 2 - Missing] SyncStateToast test used @testing-library/user-event not installed**
|
||||
|
||||
- **Found during:** Task 2 RED
|
||||
- **Issue:** Test imported `userEvent` but only `@testing-library/react` is installed.
|
||||
- **Fix:** Replaced with `fireEvent.click` from `@testing-library/react` (already in project).
|
||||
- **Files modified:** `apps/pwa/src/components/SyncStateToast.test.tsx`
|
||||
|
||||
**3. [Rule 1 - Bug] Fake timers blocked waitFor in SyncStateToast tests**
|
||||
|
||||
- **Found during:** Task 2 GREEN
|
||||
- **Issue:** `vi.useFakeTimers()` in `beforeEach` caused all `waitFor` calls to timeout because `waitFor` uses `setTimeout` internally.
|
||||
- **Fix:** Use `vi.useFakeTimers({ shouldAdvanceTime: true })` only for the specific auto-dismiss and refetch interval tests; use real timers for all query-resolution tests. `afterEach` calls `vi.useRealTimers()`.
|
||||
- **Files modified:** `apps/pwa/src/components/SyncStateToast.test.tsx`
|
||||
|
||||
**4. [Rule 1 - Bug] SyncStateToast JSDoc comment contained "EventSource"**
|
||||
|
||||
- **Found during:** Task 2 verify
|
||||
- **Issue:** Acceptance criteria `grep -c "EventSource" ... returns 0` would have failed due to a comment mentioning "No SSE / EventSource".
|
||||
- **Fix:** Changed comment to "No SSE — polling only (D-09)".
|
||||
- **Files modified:** `apps/pwa/src/components/SyncStateToast.tsx`
|
||||
|
||||
**5. [Rule 3 - Required] EventDetailPopover.test.tsx needed selector-aware mock**
|
||||
|
||||
- **Found during:** Task 3 GREEN (during mock update for new selector calls)
|
||||
- **Issue:** EventDetailPopover now calls `useCalendarStore` in selector form for `setEventForm` and `setDeleteDialog`. Old mock was not selector-aware.
|
||||
- **Fix:** Updated all mock implementations in `EventDetailPopover.test.tsx` to support both selector and non-selector call patterns.
|
||||
@@ -160,6 +171,7 @@ None — all data is wired from real TanStack Query + Zustand state. No placehol
|
||||
## Threat Flags
|
||||
|
||||
No new network endpoints, auth paths, or schema changes introduced. All threats in plan's threat register are mitigated:
|
||||
|
||||
- T-03-17: Two-tap DeleteConfirmationDialog enforced; no inline single-tap delete
|
||||
- T-03-18: failed/dead toast persists until dismissed; server refetch restores event on conflict
|
||||
- T-03-19: fetchSyncStatus is member-scoped server-side (Plan 03-03 T-03-07); client queries current member's uid only
|
||||
@@ -167,10 +179,12 @@ No new network endpoints, auth paths, or schema changes introduced. All threats
|
||||
## Self-Check: PASSED
|
||||
|
||||
Files exist:
|
||||
|
||||
- apps/pwa/src/components/SyncStateToast.tsx — FOUND
|
||||
- apps/pwa/src/components/DeleteConfirmationDialog.tsx — FOUND
|
||||
|
||||
Commits exist:
|
||||
|
||||
- 8357cf9 — FOUND (test RED task 1)
|
||||
- 8aeacc8 — FOUND (feat GREEN task 1)
|
||||
- 6874e1a — FOUND (test RED task 2)
|
||||
|
||||
Reference in New Issue
Block a user