diff --git a/src/features/kontakte/hooks.ts b/src/features/kontakte/hooks.ts new file mode 100644 index 0000000..1de5580 --- /dev/null +++ b/src/features/kontakte/hooks.ts @@ -0,0 +1,71 @@ +import { dbExec, useDbQuery } from "@/shared/hooks/use-db"; +import type { KontaktFormData, TherapeutFormData } from "./schema"; + +interface TherapeutMitKontakte { + id: number; + name: string; + stadt: string | null; + therapieform: string | null; + letzter_kontakt: string | null; + letztes_ergebnis: string | null; + kontakte_gesamt: number; +} + +interface KontaktRow { + id: number; + therapeut_id: number; + datum: string; + kanal: string; + ergebnis: string; + notiz: string | null; + antwort_datum: string | null; +} + +export function useTherapeutenListe() { + return useDbQuery(` + SELECT + t.id, t.name, t.stadt, t.therapieform, + (SELECT k.datum FROM kontakt k WHERE k.therapeut_id = t.id ORDER BY k.datum DESC LIMIT 1) as letzter_kontakt, + (SELECT k.ergebnis FROM kontakt k WHERE k.therapeut_id = t.id ORDER BY k.datum DESC LIMIT 1) as letztes_ergebnis, + (SELECT COUNT(*) FROM kontakt k WHERE k.therapeut_id = t.id) as kontakte_gesamt + FROM therapeut t + ORDER BY t.erstellt_am DESC + `); +} + +export function useKontakteForTherapeut(therapeutId: number) { + return useDbQuery( + "SELECT * FROM kontakt WHERE therapeut_id = $1 ORDER BY datum DESC", + [therapeutId], + [therapeutId], + ); +} + +export async function createTherapeut( + data: TherapeutFormData, +): Promise { + const result = await dbExec( + `INSERT INTO therapeut (name, adresse, plz, stadt, telefon, email, website, therapieform) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + RETURNING id`, + [ + data.name, + data.adresse, + data.plz, + data.stadt, + data.telefon, + data.email, + data.website, + data.therapieform, + ], + ); + return (result.rows[0] as { id: number }).id; +} + +export async function createKontakt(data: KontaktFormData) { + await dbExec( + `INSERT INTO kontakt (therapeut_id, datum, kanal, ergebnis, notiz) + VALUES ($1, $2, $3, $4, $5)`, + [data.therapeut_id, data.datum, data.kanal, data.ergebnis, data.notiz], + ); +} diff --git a/src/features/kontakte/index.ts b/src/features/kontakte/index.ts new file mode 100644 index 0000000..54e7c51 --- /dev/null +++ b/src/features/kontakte/index.ts @@ -0,0 +1,8 @@ +export { + createKontakt, + createTherapeut, + useKontakteForTherapeut, + useTherapeutenListe, +} from "./hooks"; +export type { KontaktFormData, TherapeutFormData } from "./schema"; +export { kontaktFormSchema, therapeutFormSchema } from "./schema"; diff --git a/src/features/kontakte/schema.test.ts b/src/features/kontakte/schema.test.ts new file mode 100644 index 0000000..6cddaef --- /dev/null +++ b/src/features/kontakte/schema.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; +import { kontaktFormSchema, therapeutFormSchema } from "./schema"; + +describe("therapeutFormSchema", () => { + it("accepts valid therapist", () => { + const result = therapeutFormSchema.safeParse({ + name: "Dr. Schmidt", + plz: "10115", + stadt: "Berlin", + }); + expect(result.success).toBe(true); + }); + + it("requires name", () => { + const result = therapeutFormSchema.safeParse({ name: "" }); + expect(result.success).toBe(false); + }); + + it("allows empty optional fields", () => { + const result = therapeutFormSchema.safeParse({ name: "Dr. Schmidt" }); + expect(result.success).toBe(true); + }); +}); + +describe("kontaktFormSchema", () => { + it("accepts valid contact", () => { + const result = kontaktFormSchema.safeParse({ + therapeut_id: 1, + datum: "2026-03-10", + kanal: "telefon", + ergebnis: "absage", + }); + expect(result.success).toBe(true); + }); + + it("rejects missing date", () => { + const result = kontaktFormSchema.safeParse({ + therapeut_id: 1, + datum: "", + kanal: "telefon", + ergebnis: "absage", + }); + expect(result.success).toBe(false); + }); +}); diff --git a/src/features/kontakte/schema.ts b/src/features/kontakte/schema.ts new file mode 100644 index 0000000..5819239 --- /dev/null +++ b/src/features/kontakte/schema.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; +import { kontaktErgebnisEnum, kontaktKanalEnum } from "@/shared/db/schema"; + +export const therapeutFormSchema = z.object({ + name: z.string().min(1, "Name ist erforderlich."), + adresse: z.string().optional().default(""), + plz: z + .string() + .regex(/^\d{5}$/, "Bitte gib eine gültige PLZ ein.") + .optional() + .or(z.literal("")), + stadt: z.string().optional().default(""), + telefon: z.string().optional().default(""), + email: z.string().email("Ungültige E-Mail.").optional().or(z.literal("")), + website: z.string().optional().default(""), + therapieform: z.string().optional().default(""), +}); + +export type TherapeutFormData = z.infer; + +export const kontaktFormSchema = z.object({ + therapeut_id: z.number(), + datum: z.string().min(1, "Datum ist erforderlich."), + kanal: kontaktKanalEnum, + ergebnis: kontaktErgebnisEnum, + notiz: z.string().optional().default(""), +}); + +export type KontaktFormData = z.infer;