Files
F-r-ldraledigplannerare/backend
jacobh2 dc4c0a5edd Show preset wish names in the current UI language
Preset wishes were created with `name: t(preset.labelKey)`, so the
translated string was persisted as the wish's name. A wish created while
the UI was Swedish stayed "Jul" in English forever -- the Swedish text
was the data, so no display-layer change could fix it.

Wish names are user-editable free text, so `name` cannot simply be
translated at render time. Migration 007 adds a nullable
`leave_wish.name_message_key` recording where a name came from: preset
wishes store the i18n key alongside the translated literal, and the UI
renders the key when present.

The key is cleared server-side the moment `name` actually changes,
compared against the stored value rather than trusting the caller, so no
client can forget to clear it and silently revert a user's own name to a
preset label.

An unknown key (a stale row, a renamed key) falls back to the stored
literal via an isTranslationKey guard, so a bad key can never crash the
page or leak a raw key to the user. Rows predating this column keep
displaying their stored name.

_FULL_SCHEMA in db/connection.py is updated in step with the migration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RXMhc9nT6sNz1SL4j8dznL
2026-09-14 22:32:54 +02:00
..
2026-06-14 09:58:30 +02:00
2026-06-01 16:48:21 +02:00
2026-06-01 16:48:21 +02:00
2026-06-14 09:58:30 +02:00
2026-06-01 16:48:21 +02:00
2026-06-14 09:58:30 +02:00

Backend — Föräldraledigplannerare

FastAPI + Python 3.14, packages managed with uv. Layered architecture (entities → repositories → services → routers) with dependency injection so the database backend (SQLite now, Postgres later) is swappable.

Setup

uv sync                 # install deps (incl. dev group)
uv run uvicorn app.main:app --reload   # run dev server on :8000
uv run pytest           # run tests with coverage
uv run ruff check .     # lint

Layout

app/
  config.py            # pydantic-settings (app config + frozen LeaveRules)
  dependencies.py      # DI factories (pick repo impl by settings.database_type)
  entities/            # plain dataclasses (domain)
  models/              # pydantic API schemas
  repositories/        # abstract interfaces
  repositories_impl/   # concrete impls per backend (sqlite/, postgres/)
  services/            # business logic
  routers/             # FastAPI routes
  db/connection.py     # connection + (dev) schema bootstrap
  utils/               # errors, logger

Demo data (manual/browser testing)

scripts/seed_demo.py seeds a realistic, ready-to-explore workspace through the normal service/repository layers (no raw SQL) — a couple workspace with a partner, a child born ~6 months ago, vacation rows for only some years (so the is_assumed badge is visible), day overrides, and a spread of leave wishes (fixed, flexible, and one deliberately underfunded to show a shortfall + remedies + surplus advice). Login credentials are fixed and printed at the end.

uv run python scripts/seed_demo.py            # create once; re-running is a no-op
uv run python scripts/seed_demo.py --reset     # wipe the demo workspace and rebuild

Then open http://localhost:5173 and log in with the printed demo@example.com credentials. Safe to re-run: without --reset it detects the existing demo user and changes nothing; --reset only ever deletes the one demo workspace.

Reference vertical slice

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.

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).