Files
familienarchiv/frontend/CLAUDE.md
Marcel 92da39ed84 chore(routes): delete dev-only demo route
Removes scaffolding pages from initial Paraglide setup that were never
navigated to in production. Shrinks the measured coverage surface and
removes dead code from the production bundle. CLAUDE.md route tables
updated to drop the demo/ entry.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:50:28 +02:00

8.4 KiB

Frontend — Familienarchiv

Overview

SvelteKit 2 application providing the Familienarchiv web UI. Server-side rendered (SSR) where beneficial, with client-side interactivity for document viewing, transcription, annotation, and admin workflows.

Tech Stack

  • Framework: SvelteKit 2 with Svelte 5 (runes mode)
  • Language: TypeScript 5.9
  • Styling: Tailwind CSS 4.1 + custom brand utilities
  • Build Tool: Vite 7
  • Adapter: @sveltejs/adapter-node (Node.js server, not static)
  • i18n: Paraglide.js 2.5 (@inlang/paraglide-js) — German (default), English, Spanish
  • API Client: openapi-fetch + openapi-typescript (generated from backend OpenAPI spec)
  • PDF Rendering: pdfjs-dist (PDF.js)
  • Testing:
    • Unit/Server: Vitest 4 (Node environment)
    • Component: Vitest Browser Mode with Playwright (Chromium)
    • E2E: Playwright (frontend/e2e/)

Project Structure

src/
├── routes/              # SvelteKit file-based routing
│   ├── +layout.svelte   # Global layout: header, nav, auth state
│   ├── +layout.server.ts # Loads current user, injects auth cookie
│   ├── +page.svelte     # Home / document search dashboard
│   ├── documents/       # Document CRUD, detail, edit, upload
│   ├── persons/         # Person directory, detail, edit, merge
│   ├── briefwechsel/    # Bilateral conversation timeline
│   ├── aktivitaeten/    # Unified activity feed (Chronik)
│   ├── admin/           # User, group, tag, OCR, system management
│   ├── api/             # Internal API proxies (server-side only)
│   ├── geschichten/     # Stories (list, [id], [id]/edit, new)
│   ├── stammbaum/       # Family tree
│   ├── enrich/          # Enrichment workflow ([id], done)
│   ├── hilfe/transkription/ # Transcription help page
│   ├── profile/         # User profile settings
│   ├── users/[id]/      # Public user profile page
│   ├── login/ logout/ register/
│   └── forgot-password/ reset-password/
├── lib/                 # Domain-based package structure (mirrors backend)
│   ├── document/        # Document domain: components, stores, services, utils
│   │   ├── annotation/  # Annotation overlay components
│   │   ├── comment/     # Comment thread components
│   │   └── transcription/ # Transcription editor + block logic
│   ├── person/          # Person domain: chips, typeahead, avatar, format
│   │   ├── relationship/ # Relationship form + chip components
│   │   └── genealogy/   # Stammbaum (family tree) components
│   ├── tag/             # Tag domain: TagInput, TagChipList, TagParentPicker
│   ├── geschichte/      # Geschichte (story) domain: editor + card
│   ├── notification/    # Notification bell + dropdown + store
│   ├── activity/        # Activity feed (Chronik) components
│   ├── conversation/    # Bilateral conversation (Briefwechsel) components
│   ├── ocr/             # OCR progress, training cards, trigger
│   ├── user/            # User profile/password/groups section components
│   ├── shared/          # Cross-domain utilities and primitives
│   │   ├── actions/     # Svelte actions (clickOutside, etc.)
│   │   ├── hooks/       # Reusable Svelte state hooks (useTypeahead, etc.)
│   │   ├── server/      # Server-only utilities (locale, session)
│   │   ├── services/    # Client-side service helpers
│   │   ├── utils/       # Pure utility functions (date, search, etc.)
│   │   ├── primitives/  # Generic UI primitives (BackButton, ProgressRing, etc.)
│   │   ├── dashboard/   # Dashboard stat components
│   │   ├── discussion/  # CommentThread + shared discussion UI
│   │   ├── help/        # Help/FAQ page components
│   │   ├── api.server.ts # Typed API client factory
│   │   ├── errors.ts    # Error code mapping (mirrors backend ErrorCode)
│   │   ├── types.ts     # Shared TypeScript types
│   │   ├── relativeTime.ts # Relative time formatting
│   │   └── utils.ts     # Top-level shared utilities
│   ├── generated/       # Auto-generated API types (openapi-typescript)
│   └── paraglide/       # Generated i18n code
├── hooks/               # SvelteKit hooks (handle, handleFetch)
└── ...                  # Other SvelteKit config files

For per-domain component inventories, see the domain READMEs in src/lib/<domain>/README.md.

API Client Pattern

→ See CONTRIBUTING.md §Frontend API client

LLM reminder: check !result.response.ok (not result.error — breaks when spec has no error responses); cast errors as result.error as unknown as { code?: string }; use result.data! after an ok check. For multipart/form-data (file uploads), bypass the typed client and use raw fetch.

Form Actions Pattern

// +page.server.ts
export const actions = {
	default: async ({ request, fetch }) => {
		const formData = await request.formData();
		const name = formData.get('name') as string; // cast needed — FormData returns FormDataEntryValue
		// ...
		return fail(400, { error: 'message' }); // on error
		throw redirect(303, '/target'); // on success
	}
};

Date Handling

→ See CONTRIBUTING.md §Date handling

LLM reminder: always append T12:00:00 when constructing new Date() from an ISO date string — prevents UTC timezone off-by-one errors. Forms use German dd.mm.yyyy format via handleDateInput() with a hidden ISO input.

Styling Conventions (Tailwind CSS 4)

Brand color tokens (defined in layout.css):

Token / Utility CSS variable Usage
brand-navy --palette-navy Tailwind utility — buttons, headers, primary text
brand-mint --palette-mint Tailwind utility — accents, hover underlines, icons
--palette-sand --palette-sand Palette constant only — use bg-canvas or bg-surface

Typography:

  • font-serif (Tinos) — body text, document titles, names
  • font-sans (Montserrat) — labels, metadata, UI chrome

Card pattern for content sections:

<div class="rounded-sm border border-line bg-surface shadow-sm p-6">
    <h2 class="text-xs font-bold uppercase tracking-widest text-ink-3 mb-5">Section</h2>
    <!-- content -->
</div>

Key UI Components

→ See per-domain READMEs: src/lib/person/README.md, src/lib/tag/README.md, src/lib/document/README.md, src/lib/shared/README.md

LLM reminder: BackButton is at $lib/shared/primitives/BackButton.svelte — use it for all back navigation; never a static <a href>. API client is at $lib/shared/api.server.

How to Run

Development

cd frontend
npm install
npm run dev         # Dev server on port 5173

Build & Preview

npm run build       # Production build
npm run preview     # Preview production build

Code Quality

npm run lint        # Prettier + ESLint check
npm run format      # Auto-fix formatting
npm run check       # svelte-check (type checking)

Testing

npm run test              # Vitest unit + server tests (headless)
npm run test:coverage     # Coverage report (server project only)
npm run test:e2e          # Playwright E2E tests
npm run test:e2e:headed   # Playwright E2E with visible browser
npm run test:e2e:ui       # Playwright UI mode

Regenerate API Types

Requires backend running with --spring.profiles.active=dev:

npm run generate:api

Vite Proxy

During development, /api calls are proxied to the Spring Boot backend. The proxy injects the Authorization header from the auth_token cookie automatically (see vite.config.ts).

i18n (Paraglide)

Translations live in messages/{de,en,es}.json. The compiler generates type-safe helpers in src/lib/paraglide/. Run compilation manually with:

npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide

Or let the Vite plugin handle it automatically during dev/build.