Implementiert die Startseite mit Tabs (Fahrzeuge/Geräte/Wehren), Namens-/
Funkrufnamen-Suche und ein dynamisch aus dem aktiven Merkmal-Katalog erzeugtes
Filter-UI (Slider/Multi-Select/Tri-State Switch) plus Status-Filter.
Kern:
- src/lib/search/types.ts: uuid-IDs durchgängig (SearchHit, FacetDef, FilterValue).
- src/lib/search/parse-params.ts: typisiertes Parsen von f.<uuid>=… (number lo..hi,
enum CSV, boolean ja/nein) + q + bereit; Ungültiges wird still verworfen.
- src/lib/search/facets.ts: lädt nur status='active', geltungsbereich in (typ,'both'),
typ<>'text'; min/max je number, Optionen sortiert je enum.
- src/lib/search/query-vehicles.ts: Name+Funkrufname (OR) + Status + UND-verknüpfte
EXISTS-Filter je merkmal_id; Allrad-Regel via expandNameQuery; keine Sortierung.
- src/lib/search/query-equipment.ts: wie Fahrzeuge, ohne Allrad, mit categoryId.
- src/lib/search/query-brigades.ts: Name/Ort/PLZ, nur aktive Wehren.
- src/lib/admin/codes.ts: gemeinsame Allrad-Namensregel (HLFA->HLF, Allrad impliziert);
Eigentum Admin-WS, hier rein/testbar bereitgestellt und importiert.
- src/lib/db/indexes-trgm.sql: nur pg_trgm-GIN-Indizes auf vehicles.name/funkrufname
(idempotent); merkmal_values-Indizes bleiben Eigentum des DB-WS.
UI:
- src/components/search: SearchTabs, SearchBar (debounced q), FilterPanel (dispatch +
Status-Switch), useSearchParams (router.replace ohne Reload, atomares setParams),
StandortBar; facets/{NumberRange,Enum,Boolean}; results/{ResultList,Vehicle,Equipment,
Brigade}Row mit Empty-State und offenem ETA-Slot.
- src/app/(app)/{page,fahrzeuge,geraete,wehren}: Server Components mit requireSession()
als erster Zeile (default-deny in der Tiefe zusätzlich zum Layout-Gate). /fahrzeuge
sortiert bei gesetztem Standort via searchHitsToGeoCandidates + orderByEintreffzeit.
Tests:
- Units (ohne DB): codes, parse-params, query-vehicles (SQL-Render via PgDialect).
- tests/e2e/search.spec.ts geschrieben (deferred — kein Server/DB in Sandbox).
Verifiziert offline: tsc --noEmit (0 Fehler), eslint (0), drizzle-kit check (ok),
vitest src/lib (57 grün), next build (Compiled successfully, Routen registriert).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
placeholder und aria-label im StandortInput waren als deutsche
Strings hardcodet, während alle übrigen UI-Texte über t() aus
@/lib/i18n/de laufen. Das verletzte den i18n-Querschnittsstandard
des Repos.
- search.adresse ("Adresse") und search.adressePlaceholder
("Adresse oder Ort") in de.ts ergänzt
- standort-input.tsx nutzt jetzt t("search.adressePlaceholder")
und t("search.adresse")
- Unit-Test für die neuen i18n-Keys ergänzt
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Behebt zwei BLOCKING-Befunde aus dem Review zu "Projekt-Fundament &
Design-System":
1. Route-Namens-Mismatch (Default-deny-Kerngarantie): Login-Seite lag unter
(auth)/anmelden, der gesamte downstream Auth-/Gating-Vertrag im Plan
erwartet aber /login (NextAuth pages.signIn, requireSession-Redirect,
PUBLIC_ALLOWLIST, Middleware-Matcher, auth-gating.spec toHaveURL(/\/login/),
Datei-Layout (auth)/login/...). Verzeichnis nach (auth)/login umbenannt;
/login als kanonischen Pfad im Guard-Slot-Kommentar von (app)/layout.tsx
dokumentiert, damit Workstream 3 dieselbe Route verwendet.
2. Fehlende Content-Security-Policy in SECURITY_HEADERS: Plan Z.1314 fordert
CSP mit default-src 'self', img-src 'self' data: blob:, worker-src
'self' blob:, frame-ancestors 'none', form-action 'self'; die
security-headers.spec prueft frame-ancestors 'none'. CSP ergaenzt, in
Produktion strikt, im Dev-Modus gelockerte script-src/connect-src
(unsafe-eval + ws:) fuer Next.js-HMR via NODE_ENV.
Verifikation: tsc --noEmit, next lint, next build (Route /login, kein
/anmelden) gruen; CSP zur Laufzeit fuer prod/dev geprueft.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phased, dependency-ordered plan across 11 workstreams (foundation,
schema, auth, admin taxonomy, brigade area, search, geo/ETA, detail,
deployment, seed, tests/security) with exact file paths, code/schema
snippets, ordered tasks and per-task verification. Includes cross-cutting
standards, definition-of-done, and risks. Produced by a fan-out design +
adversarial critique + synthesis workflow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Bundesland confirmed: Niederösterreich; spec references seed catalog
- Vorlagen list corrected to NÖ HLF system + aliases + Allrad rule
- Geräte-Kategorien derived from Beladelisten
- Allrad designation is HLFA n (A infixed), not 'HLF n A'
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Synthesized from the 11 PDFs in unterlagen/: 11 Fahrzeug-Vorlagen
(HLF 1, HLF 1 W, HLF 2-4, VRF, VF, ALF, SSTF, WLF, MTF) with technical
specs and standard Beladung, plus a derived typed Merkmal-Katalog to
seed the dynamic attribute system. Corrects RL numbers that the source
filenames got wrong (FA 04 = VRF, FA 07 = HLF 4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Login-only mutual-aid platform for Austrian volunteer fire brigades to
list vehicles/equipment, searchable by other brigades and sorted by
fastest-arriving (drive-time ETA). Next.js + PostgreSQL/Drizzle + Auth.js
(Authentik OIDC + local argon2id), dynamic admin-curated Merkmal system,
self-hosted OSRM/Nominatim, Docker Compose behind external Traefik.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>