Files
familysync/apps/pwa/README.md
T
Lucas BergerandClaude Opus 4.8 24bc8d2c32
CI / changes (pull_request) Successful in 6s
CI / api (pull_request) Successful in 2m9s
CI / fast-checks (pull_request) Successful in 2m30s
CI / security (pull_request) Successful in 59s
CI / harness (pull_request) Failing after 12m0s
CI / gate (pull_request) Failing after 2s
style(17): apply prettier formatting to satisfy CI format:check
Reformats 4 phase-17 files (SettingsSheet.tsx, tokens.css, vite.config.ts,
pwa-assets.config.ts) plus 11 pre-existing non-conformant docs/READMEs that
the repo-wide format:check also flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 15:23:35 -04:00

113 lines
6.1 KiB
Markdown

<!-- generated-by: gsd-doc-writer -->
# @familysync/pwa
The React 19 PWA frontend for FamilySync. Delivers a single installable, low-friction interface showing the color-coded family calendar and shared collaborative lists, designed for a mixed Android/Apple household.
Part of the [FamilySync monorepo](../../README.md).
## Stack
| Layer | Library | Version |
| ----------------------------- | --------------------------------- | -------- |
| UI framework | React | ^19.0.0 |
| Build + dev server | Vite | 8.0.16 |
| PWA service worker + manifest | vite-plugin-pwa | ^1.3.0 |
| Calendar widget | @schedule-x/calendar | 4.6.0 |
| Server state | @tanstack/react-query | 5.101.0 |
| UI state | zustand | 5.0.14 |
| Routing | react-router | ^7.17.0 |
| iCalendar parsing | ical.js | 2.2.1 |
| Drag-and-drop (lists) | @dnd-kit/core + @dnd-kit/sortable | ^6 / ^10 |
## Development
Run the PWA dev server from the monorepo root:
```bash
pnpm --filter @familysync/pwa dev
```
The API backend must also be running for most features. See [GETTING-STARTED.md](../../docs/GETTING-STARTED.md) for full stack bring-up instructions.
## Scripts
| Command | What it does |
| ----------------------------------------------- | ------------------------------------------------------------- |
| `pnpm --filter @familysync/pwa dev` | Start Vite dev server (HMR) |
| `pnpm --filter @familysync/pwa build` | Type-check then build production bundle (`tsc && vite build`) |
| `pnpm --filter @familysync/pwa preview` | Serve the production build locally |
| `pnpm --filter @familysync/pwa lint` | Run ESLint over `src/` and `e2e/` with zero warnings allowed |
| `pnpm --filter @familysync/pwa typecheck` | Run `tsc --noEmit` for both `src/` and `e2e/` tsconfigs |
| `pnpm --filter @familysync/pwa test` | Run Vitest unit/integration suite once (`vitest run`) |
| `pnpm --filter @familysync/pwa test:e2e` | Run Playwright end-to-end tests headlessly |
| `pnpm --filter @familysync/pwa test:e2e:ui` | Open the Playwright UI runner |
| `pnpm --filter @familysync/pwa test:e2e:headed` | Run Playwright tests in a headed browser |
## Source layout
```text
src/
api/ # Typed fetch wrappers for @familysync/api (client.ts, listsClient.ts)
components/ # Shared UI components co-located with their *.test.tsx files
hooks/ # Custom React hooks (useListSSE, usePushSubscription, useFocusTrap)
lib/ # Pure helpers: calendarConfig, colorUtils, eventDateTime, hydrateEvents, loginRedirect
routes/ # React Router route components with co-located tests (AdminPage, ListDetail, ListsIndex, LoginPage, SetupPage)
store/ # Zustand stores: calendarStore, listsStore
styles/ # Global CSS
main.tsx # App entry point — React Query client, router, global error handlers
App.tsx # Root component
sw.ts # Workbox service worker entry
test-setup.ts # Vitest + jest-dom global setup
```
Routes and their co-located components own the feature slice. Library code (pure, side-effect-free) lives in `lib/`. Zustand stores hold UI-only state; server data is exclusively managed by TanStack Query.
## Communication with the API
All backend calls go through `src/api/client.ts` and `src/api/listsClient.ts`. Key behavior:
- `credentials: 'include'` on every request so the OIDC session cookie is forwarded.
- `redirect: 'manual'` — a 401 or opaque redirect (the Authelia 302) is caught and thrown as a typed `SessionExpiredError`. The global `QueryCache` / `MutationCache` error handler in `main.tsx` intercepts this and shows the session-expiry interstitial.
- Re-authentication requires a top-level navigation to `/api/login` (handled by `src/lib/loginRedirect.ts`), not a fetch redirect, because browsers block CORS redirects to an external IdP.
- Live list updates are delivered via SSE through `src/hooks/useListSSE.ts`; the hook calls `queryClient.invalidateQueries` on each event so TanStack Query re-fetches.
In development the Vite proxy routes `/api` requests to the API server on port 3000, keeping cookies same-site. In production both apps are served same-origin via the Pangolin/Newt tunnel.
## Testing
Unit and integration tests are co-located with their source files (`*.test.tsx` / `*.test.ts`) and use React Testing Library + `@testing-library/jest-dom`. The test environment is `jsdom`.
```bash
# run once
pnpm --filter @familysync/pwa test
# watch mode (during development)
pnpm --filter @familysync/pwa exec vitest
```
End-to-end tests live in the `e2e/` directory and run with Playwright (`@playwright/test` 1.60.0). They cover login, calendar, lists, layout, admin, and timezone verification flows.
```bash
# headless
pnpm --filter @familysync/pwa test:e2e
# with Playwright UI
pnpm --filter @familysync/pwa test:e2e:ui
```
No coverage threshold is configured. Run `pnpm --filter @familysync/pwa typecheck` separately — Vitest uses esbuild and will not surface TypeScript errors.
## PWA install notes
- The app must be added to the Home Screen on iOS for push notifications to work (iOS 16.4+ minimum).
- Push permission must be requested inside a tap handler; calling `pushManager.subscribe()` on page load is blocked.
- Background sync and background push are not supported on iOS; all push messages must display a visible notification.
## Further reading
- [Architecture overview](../../docs/ARCHITECTURE.md)
- [Getting started](../../docs/GETTING-STARTED.md)
- [Development guide](../../docs/DEVELOPMENT.md)
- [Testing guide](../../docs/TESTING.md)