"Run" hiess in dieser App immer schon der abgesteckte Abschnitt eines Events, auf dem gefahren und gestoppt wird — also das, was im Rennsport Stage heisst. "Run" ist daneben der einzelne Durchgang eines Fahrers, und genau der steht hier als `times`. Zwei Bedeutungen fuer ein Wort, an einer Stelle, an der beide vorkommen. Die Migration benennt um statt neu anzulegen: Collection und Feld behalten ihre IDs, PocketBase benennt Tabelle und Spalte um, die Daten bleiben stehen. Gesucht wird ueber die Collection-ID und nicht ueber den Namen, damit sie auch auf einer frischen Datenbank durchlaeuft, deren Snapshot die Collection bereits `stages` nennt. Im Frontend wandert `times.run` zu `times.stage`, der Store heisst stages.svelte.ts, und aus /events/[id]/runs/[runId] wird /events/[id]/stages/[stageId]. Nicht umbenannt: `running`, `allRunning` und RunningTimesToast. Das sind laufende Zeiten und keine Stages — dieselben Buchstaben, andere Sache. Und die Artikel: Der Run war maskulin, die Stage ist feminin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P32KoesVtABd6xWsqMKzhr
175 lines
No EOL
7.8 KiB
Markdown
175 lines
No EOL
7.8 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
stammtisch-hersbruck.de is a SvelteKit 5 application using Svelte 5's new runes syntax ($state, $props, etc.) with PocketBase as the backend. The app manages events, stages, riders, times and teams for a motorsport/cycling event organization.
|
|
|
|
The repository is a monorepo: the SvelteKit app lives in `frontend/`, the PocketBase instance in `backend/`.
|
|
|
|
## Development Commands
|
|
|
|
Alle Frontend-Befehle laufen aus `frontend/`:
|
|
|
|
```bash
|
|
cd frontend
|
|
|
|
# Start development server (runs on http://stammtisch-hersbruck.de.localhost:31337)
|
|
npm run dev
|
|
|
|
# Build for production
|
|
npm run build
|
|
|
|
# Preview production build
|
|
npm run preview
|
|
|
|
# Type-check Svelte files
|
|
npm run check
|
|
|
|
# Type-check with watch mode
|
|
npm run check:watch
|
|
|
|
# Generate TypeScript types from the schema migration in backend/pb_migrations
|
|
# (no running instance, no token, no network access needed)
|
|
npm run generate-pocketbase-types
|
|
```
|
|
|
|
Backend (PocketBase) aus `backend/`:
|
|
|
|
```bash
|
|
cd backend
|
|
|
|
# PocketBase lokal starten (Admin-UI auf http://127.0.0.1:8090/_/)
|
|
docker compose up -d --build
|
|
|
|
# Stoppen
|
|
docker compose down
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Backend Integration (PocketBase)
|
|
|
|
- **API Base URL**: konfigurierbar über `PUBLIC_PB_URL` in `frontend/.env`.
|
|
Default ist die produktive Instanz `https://api.stammtisch-hersbruck.de`;
|
|
für das lokale Backend aus `backend/` auf `http://127.0.0.1:8090` umstellen.
|
|
- **Schema**: versioniert als Migration in `backend/pb_migrations/`, kommt per
|
|
`COPY` ins Image (kein Bind-Mount — der würde das Image-Verzeichnis
|
|
überdecken). Im Admin-UI erzeugte Migrationen liegen deshalb zunächst nur im
|
|
Container und müssen mit `docker compose cp` ins Repo geholt werden; siehe
|
|
`backend/README.md`.
|
|
- **Type-safe client**: The PocketBase client is typed using auto-generated types in `frontend/src/lib/types.d.ts`
|
|
- **Collections**: users, teams, team_invites, events, stages, riders, times,
|
|
trails, trail_versions, trail_flags, trail_markers, trail_comments,
|
|
event_participants, event_series
|
|
- **Regeln**: Jede Zugriffsregel, die `@request.auth.id` erwähnt, beginnt mit
|
|
`@request.auth.id != "" && (…)`. Ohne diesen Vorspann ist eine *leere*
|
|
Relation gleich dem leeren `@request.auth.id` einer anonymen Anfrage — ein
|
|
Team ohne Admins stünde damit offen im Netz. Siehe
|
|
`backend/pb_migrations/1754501700_rules_require_login.js`.
|
|
- **Hooks**: `backend/pb_hooks/` enthält serverseitiges JS für das, was keine
|
|
Collection-Regel abbilden kann — derzeit die öffentlichen Routen der
|
|
Einladungslinks. Jeder Route-Handler läuft in einer eigenen JS-Laufzeit;
|
|
gemeinsamer Code gehört in ein Modul und wird per
|
|
``require(`${__hooks}/…`)`` im Handler geholt. Details in
|
|
`backend/README.md`.
|
|
- **Authentication**: Handled through `src/lib/stores/pocketbase.svelte.ts` with the `AuthStore` class
|
|
- **File handling**: Use `getFileURL(record, file, options)` helper for PocketBase file URLs
|
|
|
|
### State Management Pattern
|
|
|
|
The app uses Svelte 5 runes for state management with a custom store pattern:
|
|
|
|
1. **Global stores** in `src/lib/stores/`:
|
|
- `pocketbase.svelte.ts`: PocketBase client, auth, and collection operations
|
|
- `app.svelte.ts`: Global app state (theme, navigation, confirm dialogs, hotkeys)
|
|
- `teams.svelte.ts`: Example of collection-specific store pattern
|
|
|
|
2. **Collection store pattern**:
|
|
- Each collection has a context-based store (see `teams.svelte.ts` as template)
|
|
- Use `setTeamContext()` in parent and `getTeamContext()` in children
|
|
- Stores provide: `records`, `refresh()`, `create()`, `edit()`, `remove()`
|
|
- The `collections` helper in `pocketbase.svelte.ts` provides reusable CRUD operations
|
|
|
|
3. **Auth flow**:
|
|
- Auth state lives in `auth` store from `pocketbase.svelte.ts`
|
|
- Cookie-based persistence available via `auth.cookie` flag
|
|
- Auth store syncs with PocketBase `authStore.onChange()`
|
|
|
|
### UI Components
|
|
|
|
- **UI Library**: Using shadcn-svelte components (bits-ui based)
|
|
- **Component path alias**: `@/components/ui/*` maps to `$lib/components/ui/*`
|
|
- **Styling**: Tailwind CSS 4.x with custom configuration
|
|
- **Dark mode**: Handled by `mode-watcher` package, toggle with `toggleMode()`
|
|
- **Tooltips**: Global `tooltip` action available from `app.svelte.ts` using tippy.js
|
|
|
|
### Important Patterns
|
|
|
|
**Svelte 5 Runes**: This project uses Svelte 5 syntax exclusively:
|
|
- `$state()` for reactive state (not `let` with `$:`)
|
|
- `$props()` for component props
|
|
- `$derived()` for computed values
|
|
- `{@render children?.()}` for slot content
|
|
|
|
**Type Generation**: After modifying the schema migration in `backend/pb_migrations/`, run `npm run generate-pocketbase-types` to update `frontend/src/lib/types.d.ts`. The script reads the collections array straight out of the migration (`frontend/scripts/schema-to-json.mjs`) — schema and types come from the same versioned source and cannot drift apart. Commit the regenerated file.
|
|
|
|
**Async Data Loading**: Root layout (`src/routes/+layout.svelte`) shows pattern:
|
|
```svelte
|
|
{#await load()}
|
|
<!-- loading state -->
|
|
{:then _}
|
|
<!-- main content -->
|
|
{/await}
|
|
```
|
|
|
|
**Confirm Dialogs**: Use `app.confirm.request()` for user confirmations (see `events.svelte.ts` remove pattern).
|
|
|
|
**Hotkeys**: Use `app.hotkey(event, condition, callback)` which auto-ignores input fields and contenteditable elements.
|
|
|
|
**Tooltips**: Never use the `title` attribute for tooltips — appwide. `<Button>`
|
|
takes a `tooltip="…"` prop (it also becomes the `aria-label`, so icon-only
|
|
buttons keep an accessible name); every other element uses the action:
|
|
`use:tooltip={{ content: '…' }}` from `app.svelte.ts`. Both render through
|
|
tippy.js, which the action initializes, updates and destroys.
|
|
|
|
### Project Configuration
|
|
|
|
- **Repo-Struktur**: Monorepo mit `frontend/` (SvelteKit) und `backend/`
|
|
(PocketBase). Ein einziges Git-Repo im Root.
|
|
- **Dev server port**: 31337 (strict mode, custom domain: `stammtisch-hersbruck.de.localhost`)
|
|
- **Path alias**: `@/*` resolves to `./src/lib/*` (configured in frontend/svelte.config.js)
|
|
- **Adapter**: `@sveltejs/adapter-node` — Deployment läuft über Coolify mit
|
|
`node build`. `adapter-auto` erkennt Coolify nicht und erzeugt kein `build/`.
|
|
- **Node**: `engines` verlangt `^20.19 || ^22.12 || >=24`; mit
|
|
`engine-strict=true` (`.npmrc`) bricht `npm ci` sonst mit `EBADENGINE` ab.
|
|
- **Deployment-Env**: `PUBLIC_PB_URL` muss dort gesetzt sein — sie wird zur
|
|
Buildzeit eingesetzt, und die lokale `.env` ist gitignored.
|
|
|
|
### File Structure Notes
|
|
|
|
- Routes are in `src/routes/` following SvelteKit conventions. **Routennamen
|
|
immer auf Englisch** (`/invite/[token]`, nicht `/einladung/[token]`) —
|
|
Oberflächentexte sind deutsch, Pfade nicht.
|
|
- Der Menüpunkt **Team** (`/dashboard/team`) führt Team und Kader an einer
|
|
Stelle: Kennzahlen, Teamdaten, Einladungslinks und die Fahrerliste samt der
|
|
Verwaltungsknöpfe der Teamleitung. Unter **Einstellungen** steht nur noch,
|
|
was einen selbst betrifft (Profil, Flag-Typen).
|
|
- **Ein Fahrer, ein Name, eine Richtung**: Ein Konto entsteht immer am Fahrer
|
|
(`riders.createLogin`), es gibt kein Verknüpfen bestehender Konten und kein
|
|
Lösen. Angezeigt wird ausschließlich der Fahrername — der Kontoname wäre
|
|
eine zweite Wahrheit, die niemand geradeziehen könnte, weil
|
|
`users.updateRule` nur den Kontoinhaber selbst ändern lässt.
|
|
- Reusable components in `src/lib/components/`
|
|
- Stores use `.svelte.ts` extension for Svelte 5 runes
|
|
- Static assets in `static/`
|
|
- `components.json` configures shadcn-svelte CLI
|
|
|
|
## When Working with This Codebase
|
|
|
|
- Always use Svelte 5 runes syntax, never legacy Svelte syntax
|
|
- Use the official Svelte MCP server to validate Svelte code
|
|
- Collection stores should follow the pattern in `teams.svelte.ts`
|
|
- PocketBase operations go through the `collections` helper for consistency
|
|
- UI components should use the shadcn-svelte imports from `@/components/ui/` |