diff --git a/README.md b/README.md new file mode 100644 index 0000000..301789a --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +# Föräldraledigplannerare + +A planner for Swedish parental leave (föräldraledighet) — solo or with a +partner — that folds in vacation days and optimizes leave over time so that +as few days as possible are lost at the child's 4th birthday, after which +only 96 days may be saved (48 per parent, in couple mode). + +Full-stack monorepo: TypeScript/React/Vite frontend, Python/FastAPI backend, +SQLite by default (Postgres as an optional extra). + +## Quick start + +From `frontend/`: + +```bash +npm install +npm run dev # runs uvicorn (:8000) and Vite (:5173) together +``` + +Open **`http://localhost:5173`** (not `127.0.0.1` — Vite's dev server binds +IPv6 `::1`, so only `localhost` resolves to it). + +The backend can also run on its own, from `backend/`: + +```bash +uv sync +uv run uvicorn app.main:app --reload --port 8000 +``` + +Seed a ready-to-explore demo workspace (couple, a partner, a child, vacation +history, leave wishes, a deliberate shortfall) through the normal +service/repository layers — no raw SQL: + +```bash +cd backend +uv run python scripts/seed_demo.py --reset +``` + +It prints the demo login (fixed credentials, safe to re-run — without +`--reset` it's a no-op if the demo user already exists). + +The Försäkringskassan scraper (BankID login flow via Playwright) is enabled +by default. Exercise the login UI directly with `npm run login` from +`frontend/`; Chromium must be installed separately: +`uv run playwright install chromium`. + +## The app + +Three tabs: + +- **Översikt** — the headline figure for days at risk at the 4th birthday, + per-parent bars against each parent's savable cap, a year-by-year + timeline, and shortfall/surplus detail below the fold. +- **Kalender** — where leave is authored. Drag a range to create a period, + click a band to edit it, click a day to change how it's funded or mark it + worked/off. A season-preset rail helps place common periods (summer, + Christmas, Easter). +- **Inställningar**. + +**The calendar is the plan.** There's no separate propose-then-apply step: +every edit recomputes and persists immediately, reporting what changed with +an undo. A `LeaveWish` is a recurring intent (e.g. "summer", "Christmas"); +the allocator decides which resource — parental days, vacation, unpaid — +funds each day, without retyping days a user has pinned by hand. + +First run walks a wizard: add the child, choose solo or couple planning +(optionally add a partner), then set balances — fetch from +Försäkringskassan via BankID, enter manually, or skip (figures are then +marked as estimates). + +## Layout + +``` +backend/app/ + entities/ persistence-layer row shapes + models/ Pydantic v2 API schemas + repositories/ abstract repository interfaces + repositories_impl/ sqlite/ (default) and postgres/ (optional) impls + services/ business logic — planning, tips, analytics, auth + scrapers/ Försäkringskassan scraper (Playwright) + routers/ thin FastAPI route handlers +backend/migrations/ Alembic migrations (hand-written, not ORM-generated) +backend/scripts/ seed_demo.py and other dev-only scripts + +frontend/src/ + pages/ route-level screens (Dashboard, Calendar, Settings, ...) + components/ presentational + composite components (BEM CSS) + hooks/ data-fetching/state (the data layer — no TanStack Query) + services/ api.ts (HTTP client) + errors.ts + context/ Auth + Workspace React context +frontend/e2e/ Playwright end-to-end specs +``` + +Routers call services; services call the repository interfaces — never +`repositories_impl` directly. Components consume hooks; hooks call +`services/api.ts` — components never call `fetch` directly. + +## Tests & gates + +Backend (from `backend/`): + +```bash +uv run ruff check . +uv run ruff format --check . +uv run pytest --cov=app --cov-report=term-missing --cov-fail-under=80 +``` + +Frontend (from `frontend/`): + +```bash +npm run typecheck +npm run lint # ESLint, zero warnings allowed +npm run coverage # Vitest, thresholds: lines 96% / functions 91% / branches 92% / statements 96% +``` + +Current numbers: backend 587 tests passing at ~87% coverage (gate: 80%); +frontend 949 tests across 62 files passing at ~99% lines/statements, ~93% +functions, ~95% branches (gates: 96/91/92/96 — see +`frontend/vitest.config.ts`). Gates are a regression ratchet, not a target — +they sit a couple of points below the measured baseline. + +CI (`.github/workflows/ci.yml`) runs all of the above, plus a Playwright E2E +suite that reports but does not yet block merges. + +## Further reading + +- `CLAUDE.md` — conventions, stack details, model-tier rules for contributors. +- `ARCHITECTURE.md` / `PLANNING_ENGINE_SPEC.md` — design and rationale. + Both predate parts of the current behavior; sections marked + **SUPERSEDED** note what changed and why. +- `backend/README.md` — backend-specific setup and the reference vertical + slice to copy when adding a new entity. diff --git a/backend/README.md b/backend/README.md index 805befd..5597905 100644 --- a/backend/README.md +++ b/backend/README.md @@ -51,7 +51,7 @@ user and changes nothing; `--reset` only ever deletes the one demo workspace. `VacationDays` is implemented end-to-end (entity → schema → repo interface → SQLite impl → service → router → tests). **Copy this slice** when adding a new -entity. See `JUNIOR_ONBOARDING.md` in the repo root for task-by-task guidance. +entity. > The inline schema in `db/connection.py` exists only so dev/tests are > self-contained. Production schema is owned by Alembic migrations (a senior task).