Write a front door for the repository
There was no README at the root, and the backend's own still pointed at an onboarding guide that had been deleted. The new one says what the app is, how to run it, and where the code lives, and states the test gates as they actually are rather than as an older note claimed. It records the two things that waste a newcomer's afternoon: the dev server answers on localhost but not on the loopback address, because Vite binds IPv6 first, and the two design documents are largely superseded and say so inline.
This commit is contained in:
@@ -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.
|
||||
+1
-1
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user