bug fix for atemschutz

This commit is contained in:
Matthias Hochmeister
2026-03-01 19:19:12 +01:00
parent 2630224edd
commit 6495ca94d1
17 changed files with 5116 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
import { z } from 'zod';
// ---------------------------------------------------------------------------
// Core DB-mapped interfaces
// ---------------------------------------------------------------------------
export interface VeranstaltungKategorie {
id: string;
name: string;
beschreibung?: string | null;
farbe?: string | null;
icon?: string | null;
erstellt_von?: string | null;
erstellt_am: Date;
aktualisiert_am: Date;
}
export interface Veranstaltung {
id: string;
titel: string;
beschreibung?: string | null;
ort?: string | null;
ort_url?: string | null;
kategorie_id?: string | null;
datum_von: Date;
datum_bis: Date;
ganztaegig: boolean;
zielgruppen: string[];
alle_gruppen: boolean;
max_teilnehmer?: number | null;
anmeldung_erforderlich: boolean;
anmeldung_bis?: Date | null;
erstellt_von: string;
abgesagt: boolean;
abgesagt_grund?: string | null;
abgesagt_am?: Date | null;
erstellt_am: Date;
aktualisiert_am: Date;
// Joined / enriched fields
kategorie_name?: string | null;
kategorie_farbe?: string | null;
kategorie_icon?: string | null;
erstellt_von_name?: string | null;
}
/** Lightweight version for calendar and list views */
export interface VeranstaltungListItem {
id: string;
titel: string;
ort?: string | null;
kategorie_id?: string | null;
kategorie_name?: string | null;
kategorie_farbe?: string | null;
kategorie_icon?: string | null;
datum_von: Date;
datum_bis: Date;
ganztaegig: boolean;
alle_gruppen: boolean;
zielgruppen: string[];
abgesagt: boolean;
anmeldung_erforderlich: boolean;
}
// ---------------------------------------------------------------------------
// Zod schemas -- Kategorien
// ---------------------------------------------------------------------------
export const CreateKategorieSchema = z.object({
name: z
.string()
.min(2, 'Name muss mindestens 2 Zeichen haben')
.max(255),
beschreibung: z.string().max(500).optional().nullable(),
farbe: z
.string()
.regex(/^#[0-9a-fA-F]{6}$/, 'Farbe muss ein gültiger Hex-Farbwert sein (z.B. #1976d2)')
.optional(),
icon: z.string().max(100).optional(),
});
export type CreateKategorieData = z.infer<typeof CreateKategorieSchema>;
export const UpdateKategorieSchema = CreateKategorieSchema.partial();
export type UpdateKategorieData = z.infer<typeof UpdateKategorieSchema>;
// ---------------------------------------------------------------------------
// Zod schemas -- Veranstaltungen
// ---------------------------------------------------------------------------
const VeranstaltungBaseSchema = z.object({
titel: z
.string()
.min(3, 'Titel muss mindestens 3 Zeichen haben')
.max(500),
beschreibung: z.string().max(5000).optional().nullable(),
ort: z.string().max(500).optional().nullable(),
// Plain string validator — some users use relative paths or internal URLs
ort_url: z.string().max(1000).optional().nullable(),
kategorie_id: z.string().uuid('kategorie_id muss eine gültige UUID sein').optional().nullable(),
datum_von: z
.string()
.datetime({ offset: true, message: 'datum_von muss ein ISO-8601 Datum mit Zeitzone sein' })
.transform((s) => new Date(s)),
datum_bis: z
.string()
.datetime({ offset: true, message: 'datum_bis muss ein ISO-8601 Datum mit Zeitzone sein' })
.transform((s) => new Date(s)),
ganztaegig: z.boolean().default(false),
zielgruppen: z.array(z.string()).default([]),
alle_gruppen: z.boolean().default(false),
max_teilnehmer: z.number().int().positive().optional().nullable(),
anmeldung_erforderlich: z.boolean().default(false),
anmeldung_bis: z
.string()
.datetime({ offset: true, message: 'anmeldung_bis muss ein ISO-8601 Datum mit Zeitzone sein' })
.transform((s) => new Date(s))
.optional()
.nullable(),
});
export const CreateVeranstaltungSchema = VeranstaltungBaseSchema.refine(
(d) => d.datum_bis >= d.datum_von,
{ message: 'datum_bis muss nach datum_von liegen', path: ['datum_bis'] }
);
export type CreateVeranstaltungData = z.infer<typeof CreateVeranstaltungSchema>;
export const UpdateVeranstaltungSchema = VeranstaltungBaseSchema.partial().refine(
(d) => d.datum_bis == null || d.datum_von == null || d.datum_bis >= d.datum_von,
{ message: 'datum_bis muss nach datum_von liegen', path: ['datum_bis'] }
);
export type UpdateVeranstaltungData = z.infer<typeof UpdateVeranstaltungSchema>;
export const CancelVeranstaltungSchema = z.object({
abgesagt_grund: z
.string()
.min(5, 'Bitte gib einen Grund für die Absage an (min. 5 Zeichen)')
.max(1000),
});
export type CancelVeranstaltungData = z.infer<typeof CancelVeranstaltungSchema>;