add now features

This commit is contained in:
Matthias Hochmeister
2026-03-01 14:41:45 +01:00
parent e76946ed8a
commit 5b8f40ab9a
14 changed files with 2044 additions and 84 deletions

View File

@@ -0,0 +1,99 @@
// =============================================================================
// Atemschutz (Breathing Apparatus) — Domain Model
// =============================================================================
import { z } from 'zod';
// ── Enums ───────────────────────────────────────────────────────────────────
export const UNTERSUCHUNG_ERGEBNIS_VALUES = ['tauglich', 'bedingt_tauglich', 'nicht_tauglich'] as const;
export type UntersuchungErgebnis = typeof UNTERSUCHUNG_ERGEBNIS_VALUES[number];
// ── Core DB Row ─────────────────────────────────────────────────────────────
export interface AtemschutzTraeger {
id: string;
user_id: string;
atemschutz_lehrgang: boolean;
lehrgang_datum: Date | null;
untersuchung_datum: Date | null;
untersuchung_gueltig_bis: Date | null;
untersuchung_ergebnis: UntersuchungErgebnis | null;
leistungstest_datum: Date | null;
leistungstest_gueltig_bis: Date | null;
leistungstest_bestanden: boolean | null;
bemerkung: string | null;
created_at: Date;
updated_at: Date;
}
// ── View Row (extended with user info + computed fields) ────────────────────
export interface AtemschutzUebersicht extends AtemschutzTraeger {
user_name: string | null;
user_given_name: string | null;
user_family_name: string | null;
user_email: string;
mitglied_status: string | null;
dienstgrad: string | null;
untersuchung_gueltig: boolean;
untersuchung_tage_rest: number | null;
leistungstest_gueltig: boolean;
leistungstest_tage_rest: number | null;
einsatzbereit: boolean;
}
// ── Dashboard KPI ───────────────────────────────────────────────────────────
export interface AtemschutzStats {
total: number; // total tracked members
mitLehrgang: number; // have completed course
untersuchungGueltig: number; // valid medical exam
untersuchungAbgelaufen: number; // expired medical exam
untersuchungBaldFaellig: number; // expires within 90 days
leistungstestGueltig: number; // valid performance test
leistungstestAbgelaufen: number; // expired performance test
leistungstestBaldFaellig: number; // expires within 30 days
einsatzbereit: number; // fully qualified
}
// ── Zod Validation Schemas ──────────────────────────────────────────────────
const uuidString = z.string().regex(
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
'Ungültige UUID'
);
const isoDate = z.string().regex(
/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/,
'Erwartet ISO-Datum im Format YYYY-MM-DD'
);
export const CreateAtemschutzSchema = z.object({
user_id: uuidString,
atemschutz_lehrgang: z.boolean().default(false),
lehrgang_datum: isoDate.optional(),
untersuchung_datum: isoDate.optional(),
untersuchung_gueltig_bis: isoDate.optional(),
untersuchung_ergebnis: z.enum(UNTERSUCHUNG_ERGEBNIS_VALUES).optional(),
leistungstest_datum: isoDate.optional(),
leistungstest_gueltig_bis: isoDate.optional(),
leistungstest_bestanden: z.boolean().optional(),
bemerkung: z.string().max(2000).optional(),
});
export type CreateAtemschutzData = z.infer<typeof CreateAtemschutzSchema>;
export const UpdateAtemschutzSchema = z.object({
atemschutz_lehrgang: z.boolean().optional(),
lehrgang_datum: isoDate.nullable().optional(),
untersuchung_datum: isoDate.nullable().optional(),
untersuchung_gueltig_bis: isoDate.nullable().optional(),
untersuchung_ergebnis: z.enum(UNTERSUCHUNG_ERGEBNIS_VALUES).nullable().optional(),
leistungstest_datum: isoDate.nullable().optional(),
leistungstest_gueltig_bis: isoDate.nullable().optional(),
leistungstest_bestanden: z.boolean().nullable().optional(),
bemerkung: z.string().max(2000).nullable().optional(),
});
export type UpdateAtemschutzData = z.infer<typeof UpdateAtemschutzSchema>;