Workstream 1: Projekt-Fundament & Design-System (Phase 0)
Greenfield-Next.js-15-App-Router-Gerüst (TS strict) mit: - Route-Groups (auth)/(app) inkl. loading/error/not-found je Group; Guard-Slot-Kommentar im (app)/layout.tsx (vom Auth-WS zu füllen). - "Amtlich"/Netzknoten-Designsystem: Tailwind-Tokens (Navy #1B3A5B, Signalrot #E2231A, Anthrazit, Nebelgrau, bereit/Wartung), tabular-nums, Serif-Display/Inter-Sans via CSS-Variablen, Inline-SVG-Logo. - Radix-Basiskomponenten (button/input/label/badge/tabs/dialog/select/ switch/slider); StatusBadge entspricht asset_status. - Kanonisches src/lib/env.ts (Zod, Fail-Fast) mit ALLEN DB-/Auth-/Geo-Slots inkl. AUTH_URL; isHttps-Ableitung. Zentrale i18n-Tabelle de.ts + t(). - Drizzle-Setup: client.ts (Pool-Singleton), leeres schema/index.ts-Barrel (KEIN Migrations-Eigentümer), drizzle.config.ts, .env.example. - next.config.ts: output:standalone, experimental.authInterrupts, Security-Header. Vitest + Fail-Fast-Env-Test (TDD, 5/5 grün). Bewusst KEINE Auth-Logik und KEINE fachlichen Tabellen. Verifikation: typecheck/lint/test grün; npm run build erzeugt .next/standalone/server.js; curl /anmelden -> lang="de" + FlorianNetz. next/font/google durch CSS-Variablen ersetzt (air-gapped-Build). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
36
src/lib/validation/common.ts
Normal file
36
src/lib/validation/common.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Gemeinsame Zod-Schemas, die an mehreren Grenzen (FormData, JSON, searchParams)
|
||||
* wiederverwendet werden. Feature-Workstreams bauen darauf auf.
|
||||
*/
|
||||
|
||||
export const uuidSchema = z.string().uuid({ message: "Ungültige ID." });
|
||||
|
||||
export const nonEmptyText = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, { message: "Pflichtfeld." });
|
||||
|
||||
export const optionalText = z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.transform((v) => (v === "" ? undefined : v));
|
||||
|
||||
export const latitudeSchema = z
|
||||
.number({ invalid_type_error: "Ungültiger Breitengrad." })
|
||||
.min(-90)
|
||||
.max(90);
|
||||
|
||||
export const longitudeSchema = z
|
||||
.number({ invalid_type_error: "Ungültiger Längengrad." })
|
||||
.min(-180)
|
||||
.max(180);
|
||||
|
||||
export const paginationSchema = z.object({
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
pageSize: z.coerce.number().int().positive().max(100).default(25),
|
||||
});
|
||||
|
||||
export type Pagination = z.infer<typeof paginationSchema>;
|
||||
Reference in New Issue
Block a user