rewrite process stepper: data-driven steps, erstgespräch list/form, conditional TSS
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,25 @@
|
||||
import { useForm } from "@tanstack/react-form";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { ChevronDown, ChevronRight, Plus } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useTherapeutenListe } from "@/features/kontakte/hooks";
|
||||
import { Badge } from "@/shared/components/ui/badge";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import { DateInput } from "@/shared/components/ui/date-input";
|
||||
import { Label } from "@/shared/components/ui/label";
|
||||
import { Separator } from "@/shared/components/ui/separator";
|
||||
import { Switch } from "@/shared/components/ui/switch";
|
||||
import type { ProzessSchritt } from "@/shared/db/schema";
|
||||
import { dbExec } from "@/shared/hooks/use-db";
|
||||
import { PROZESS_SCHRITTE } from "@/shared/lib/constants";
|
||||
import type { ErstgespraechRow, ProcessStatus } from "../hooks";
|
||||
import {
|
||||
addSitzung,
|
||||
createErstgespraech,
|
||||
setTssKontaktiert,
|
||||
updateErstgespraech,
|
||||
useErstgespraeche,
|
||||
useSitzungen,
|
||||
} from "../hooks";
|
||||
import { PhaseCard } from "./phase-card";
|
||||
|
||||
interface ProcessStepperProps {
|
||||
aktuellerSchritt: ProzessSchritt;
|
||||
kontaktGesamt: number;
|
||||
absagen: number;
|
||||
onUpdate: () => void;
|
||||
}
|
||||
|
||||
const inputClasses =
|
||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring";
|
||||
|
||||
@@ -27,177 +28,236 @@ function todayISO() {
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
interface ProcessStepperProps {
|
||||
processStatus: ProcessStatus;
|
||||
tssKontaktiertDatum: string | null;
|
||||
kontaktGesamt: number;
|
||||
absagen: number;
|
||||
onUpdate: () => void;
|
||||
}
|
||||
|
||||
export function ProcessStepper({
|
||||
aktuellerSchritt,
|
||||
processStatus,
|
||||
tssKontaktiertDatum,
|
||||
kontaktGesamt,
|
||||
absagen,
|
||||
onUpdate,
|
||||
}: ProcessStepperProps) {
|
||||
// Map legacy step to current steps
|
||||
const effectiveSchritt =
|
||||
aktuellerSchritt === "sprechstunde_absolviert"
|
||||
? "diagnose_erhalten"
|
||||
: aktuellerSchritt;
|
||||
|
||||
const currentIndex = PROZESS_SCHRITTE.findIndex(
|
||||
(s) => s.key === effectiveSchritt,
|
||||
);
|
||||
const visibleSteps = processStatus.steps.filter((s) => s.visible);
|
||||
const currentIndex = visibleSteps.findIndex((s) => s.status === "aktuell");
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex items-baseline justify-between">
|
||||
<h1 className="text-2xl font-bold">Dein Fortschritt</h1>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Schritt {currentIndex + 1} von {PROZESS_SCHRITTE.length}
|
||||
</span>
|
||||
{currentIndex >= 0 && (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Schritt {currentIndex + 1} von {visibleSteps.length}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
{PROZESS_SCHRITTE.map((schritt, i) => {
|
||||
const status =
|
||||
i < currentIndex
|
||||
? "erledigt"
|
||||
: i === currentIndex
|
||||
? "aktuell"
|
||||
: "offen";
|
||||
|
||||
return (
|
||||
<PhaseCard
|
||||
key={schritt.key}
|
||||
label={schritt.label}
|
||||
beschreibung={schritt.beschreibung}
|
||||
status={status}
|
||||
index={i}
|
||||
>
|
||||
{status === "aktuell" && (
|
||||
<StepAction
|
||||
schritt={schritt.key}
|
||||
kontaktGesamt={kontaktGesamt}
|
||||
absagen={absagen}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
)}
|
||||
</PhaseCard>
|
||||
);
|
||||
})}
|
||||
{visibleSteps.map((step, i) => (
|
||||
<PhaseCard
|
||||
key={step.key}
|
||||
label={step.label}
|
||||
beschreibung={step.beschreibung}
|
||||
status={step.status}
|
||||
stepNumber={i + 1}
|
||||
>
|
||||
{step.key === "erstgespraech" && (
|
||||
<ErstgespraechAction onUpdate={onUpdate} />
|
||||
)}
|
||||
{step.key === "tss" && step.status === "aktuell" && (
|
||||
<TssAction onUpdate={onUpdate} />
|
||||
)}
|
||||
{step.key === "tss" &&
|
||||
step.status === "erledigt" &&
|
||||
tssKontaktiertDatum && <TssDone datum={tssKontaktiertDatum} />}
|
||||
{step.key === "eigensuche" && (
|
||||
<EigensucheInfo kontaktGesamt={kontaktGesamt} absagen={absagen} />
|
||||
)}
|
||||
{step.key === "antrag" && step.status === "aktuell" && (
|
||||
<AntragLink />
|
||||
)}
|
||||
</PhaseCard>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StepAction({
|
||||
schritt,
|
||||
kontaktGesamt,
|
||||
absagen,
|
||||
onUpdate,
|
||||
}: {
|
||||
schritt: ProzessSchritt;
|
||||
kontaktGesamt: number;
|
||||
absagen: number;
|
||||
onUpdate: () => void;
|
||||
}) {
|
||||
switch (schritt) {
|
||||
case "neu":
|
||||
return <SprechstundeForm onDone={onUpdate} />;
|
||||
case "diagnose_erhalten":
|
||||
return (
|
||||
function ErstgespraechAction({ onUpdate }: { onUpdate: () => void }) {
|
||||
const { data: erstgespraeche, refetch } = useErstgespraeche();
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const handleUpdate = () => {
|
||||
refetch();
|
||||
onUpdate();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{erstgespraeche.length > 0 && (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="space-y-2">
|
||||
{erstgespraeche.map((eg) => (
|
||||
<ErstgespraechCard
|
||||
key={eg.id}
|
||||
erstgespraech={eg}
|
||||
onUpdate={handleUpdate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{showForm ? (
|
||||
<>
|
||||
<Separator />
|
||||
<ErstgespraechForm
|
||||
onDone={() => {
|
||||
setShowForm(false);
|
||||
handleUpdate();
|
||||
}}
|
||||
onCancel={() => setShowForm(false)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
await dbExec(
|
||||
`UPDATE nutzer SET
|
||||
aktueller_schritt = 'tss_beantragt',
|
||||
tss_beantragt = TRUE,
|
||||
tss_beantragt_datum = CURRENT_DATE,
|
||||
aktualisiert_am = NOW()
|
||||
WHERE id = 1`,
|
||||
);
|
||||
onUpdate();
|
||||
}}
|
||||
>
|
||||
TSS kontaktiert
|
||||
<Button size="sm" onClick={() => setShowForm(true)}>
|
||||
<Plus className="mr-1 size-4" />
|
||||
Erstgespräch hinzufügen
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case "tss_beantragt":
|
||||
return (
|
||||
<AdvanceButton
|
||||
nextStep="eigensuche"
|
||||
label="Eigensuche gestartet"
|
||||
onDone={onUpdate}
|
||||
/>
|
||||
);
|
||||
case "eigensuche":
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{kontaktGesamt} Kontaktversuche, davon {absagen} Absagen.{" "}
|
||||
<Link to="/kontakte" className="text-primary underline">
|
||||
Kontakte verwalten
|
||||
</Link>
|
||||
</div>
|
||||
<AdvanceButton
|
||||
nextStep="antrag_gestellt"
|
||||
label="Kostenerstattung beantragt"
|
||||
onDone={onUpdate}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case "antrag_gestellt":
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Dein Kostenerstattungsantrag wurde eingereicht.{" "}
|
||||
<Link to="/antrag" className="text-primary underline">
|
||||
Zum Antrag
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function AdvanceButton({
|
||||
nextStep,
|
||||
label,
|
||||
onDone,
|
||||
}: {
|
||||
nextStep: ProzessSchritt;
|
||||
label: string;
|
||||
onDone: () => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
await dbExec(
|
||||
"UPDATE nutzer SET aktueller_schritt = $1, aktualisiert_am = NOW() WHERE id = 1",
|
||||
[nextStep],
|
||||
);
|
||||
onDone();
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SprechstundeForm({ onDone }: { onDone: () => void }) {
|
||||
function ErstgespraechCard({
|
||||
erstgespraech,
|
||||
onUpdate,
|
||||
}: {
|
||||
erstgespraech: ErstgespraechRow;
|
||||
onUpdate: () => void;
|
||||
}) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border p-3">
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-3 text-left text-sm"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
<span className="flex-1 font-medium">
|
||||
{erstgespraech.therapeut_name}
|
||||
{erstgespraech.therapeut_stadt
|
||||
? ` (${erstgespraech.therapeut_stadt})`
|
||||
: ""}
|
||||
</span>
|
||||
<span className="flex items-center gap-2">
|
||||
{erstgespraech.diagnose && (
|
||||
<Badge variant="secondary">{erstgespraech.diagnose}</Badge>
|
||||
)}
|
||||
{erstgespraech.dringlichkeitscode && <Badge>Dringlichkeit</Badge>}
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{erstgespraech.sitzung_count}{" "}
|
||||
{Number(erstgespraech.sitzung_count) === 1
|
||||
? "Sitzung"
|
||||
: "Sitzungen"}
|
||||
</span>
|
||||
{expanded ? (
|
||||
<ChevronDown className="size-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="size-4 text-muted-foreground" />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{expanded && (
|
||||
<div className="mt-3 space-y-3">
|
||||
<SitzungList sprechstundeId={erstgespraech.id} onUpdate={onUpdate} />
|
||||
{editing ? (
|
||||
<EditErstgespraechForm
|
||||
erstgespraech={erstgespraech}
|
||||
onDone={() => {
|
||||
setEditing(false);
|
||||
onUpdate();
|
||||
}}
|
||||
onCancel={() => setEditing(false)}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setEditing(true)}
|
||||
>
|
||||
Diagnose bearbeiten
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SitzungList({
|
||||
sprechstundeId,
|
||||
onUpdate,
|
||||
}: {
|
||||
sprechstundeId: number;
|
||||
onUpdate: () => void;
|
||||
}) {
|
||||
const { data: sitzungen, refetch } = useSitzungen(sprechstundeId);
|
||||
const [addingDate, setAddingDate] = useState("");
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-medium text-muted-foreground">Sitzungen</p>
|
||||
<ul className="space-y-1">
|
||||
{sitzungen.map((s) => (
|
||||
<li key={s.id} className="text-sm">
|
||||
{new Date(s.datum).toLocaleDateString("de-DE")}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="flex items-center gap-2">
|
||||
<DateInput value={addingDate} onChange={(iso) => setAddingDate(iso)} />
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={!addingDate}
|
||||
onClick={async () => {
|
||||
await addSitzung(sprechstundeId, addingDate);
|
||||
setAddingDate("");
|
||||
refetch();
|
||||
onUpdate();
|
||||
}}
|
||||
>
|
||||
Sitzung hinzufügen
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ErstgespraechForm({
|
||||
onDone,
|
||||
onCancel,
|
||||
}: {
|
||||
onDone: () => void;
|
||||
onCancel: () => void;
|
||||
}) {
|
||||
const { data: therapeuten, loading } = useTherapeutenListe();
|
||||
const [saved, setSaved] = useState(false);
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
@@ -209,145 +269,258 @@ function SprechstundeForm({ onDone }: { onDone: () => void }) {
|
||||
onSubmit: async ({ value }) => {
|
||||
const therapeutId = Number(value.therapeut_id);
|
||||
if (!therapeutId) return;
|
||||
|
||||
await dbExec(
|
||||
`INSERT INTO sprechstunde (therapeut_id, datum, ergebnis, diagnose, dringlichkeitscode) VALUES ($1, $2, 'erstgespraech', $3, $4)`,
|
||||
[
|
||||
therapeutId,
|
||||
value.datum,
|
||||
value.diagnose || null,
|
||||
value.dringlichkeitscode,
|
||||
],
|
||||
await createErstgespraech(
|
||||
therapeutId,
|
||||
value.datum,
|
||||
value.diagnose || null,
|
||||
value.dringlichkeitscode,
|
||||
);
|
||||
await dbExec(
|
||||
`UPDATE nutzer SET
|
||||
aktueller_schritt = 'diagnose_erhalten',
|
||||
dringlichkeitscode = $1,
|
||||
dringlichkeitscode_datum = CASE WHEN $1 = TRUE THEN $2::date ELSE NULL END,
|
||||
aktualisiert_am = NOW()
|
||||
WHERE id = 1`,
|
||||
[value.dringlichkeitscode, value.datum],
|
||||
);
|
||||
setSaved(true);
|
||||
onDone();
|
||||
},
|
||||
});
|
||||
|
||||
if (saved) {
|
||||
return (
|
||||
<p className="text-sm font-medium text-green-600 dark:text-green-400">
|
||||
Erstgespräch erfasst.
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
className="space-y-3"
|
||||
>
|
||||
<p className="text-sm font-medium">Erstgespräch dokumentieren</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Freie Termine findest du unter{" "}
|
||||
<span className="font-mono font-medium text-primary">116117</span>{" "}
|
||||
(Telefon oder online).
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
<form.Field name="therapeut_id">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<Label>Therapeut:in</Label>
|
||||
{loading ? (
|
||||
<p className="text-sm text-muted-foreground">Laden…</p>
|
||||
) : therapeuten.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Lege zuerst unter{" "}
|
||||
<Link to="/kontakte" className="text-primary underline">
|
||||
Kontakte
|
||||
</Link>{" "}
|
||||
einen Eintrag an.
|
||||
</p>
|
||||
) : (
|
||||
<select
|
||||
className={inputClasses}
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
>
|
||||
<option value="">Bitte wählen…</option>
|
||||
{therapeuten.map((t) => (
|
||||
<option key={t.id} value={t.id}>
|
||||
{t.name}
|
||||
{t.stadt ? ` (${t.stadt})` : ""}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="datum">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<Label>Datum</Label>
|
||||
<DateInput
|
||||
value={field.state.value}
|
||||
onChange={(iso) => field.handleChange(iso)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="diagnose">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<Label>Diagnose</Label>
|
||||
<input
|
||||
type="text"
|
||||
className={inputClasses}
|
||||
placeholder="z.B. F32.1"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="dringlichkeitscode">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
id="dringlichkeitscode-new"
|
||||
checked={field.state.value}
|
||||
onCheckedChange={(checked) => field.handleChange(checked)}
|
||||
/>
|
||||
<Label htmlFor="dringlichkeitscode-new">
|
||||
Dringlichkeitscode erhalten
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button type="button" size="sm" variant="outline" onClick={onCancel}>
|
||||
Abbrechen
|
||||
</Button>
|
||||
<Button type="submit" size="sm" disabled={therapeuten.length === 0}>
|
||||
Erstgespräch durchgeführt
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function EditErstgespraechForm({
|
||||
erstgespraech,
|
||||
onDone,
|
||||
onCancel,
|
||||
}: {
|
||||
erstgespraech: ErstgespraechRow;
|
||||
onDone: () => void;
|
||||
onCancel: () => void;
|
||||
}) {
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
diagnose: erstgespraech.diagnose ?? "",
|
||||
dringlichkeitscode: erstgespraech.dringlichkeitscode,
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
await updateErstgespraech(
|
||||
erstgespraech.id,
|
||||
value.diagnose || null,
|
||||
value.dringlichkeitscode,
|
||||
);
|
||||
onDone();
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
className="space-y-3"
|
||||
>
|
||||
<form.Field name="diagnose">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<Label>Diagnose</Label>
|
||||
<input
|
||||
type="text"
|
||||
className={inputClasses}
|
||||
placeholder="z.B. F32.1"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="dringlichkeitscode">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
id="dringlichkeitscode-edit"
|
||||
checked={field.state.value}
|
||||
onCheckedChange={(checked) => field.handleChange(checked)}
|
||||
/>
|
||||
<Label htmlFor="dringlichkeitscode-edit">
|
||||
Dringlichkeitscode erhalten
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button type="button" size="sm" variant="outline" onClick={onCancel}>
|
||||
Abbrechen
|
||||
</Button>
|
||||
<Button type="submit" size="sm">
|
||||
Speichern
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function TssAction({ onUpdate }: { onUpdate: () => void }) {
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
className="space-y-3"
|
||||
>
|
||||
<p className="text-sm font-medium">Erstgespräch dokumentieren</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Freie Termine findest du unter{" "}
|
||||
<span className="font-mono font-medium text-primary">116117</span>{" "}
|
||||
(Telefon oder online).
|
||||
</p>
|
||||
|
||||
<form.Field name="therapeut_id">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<Label>Therapeut:in</Label>
|
||||
{loading ? (
|
||||
<p className="text-sm text-muted-foreground">Laden…</p>
|
||||
) : therapeuten.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Lege zuerst unter{" "}
|
||||
<Link to="/kontakte" className="text-primary underline">
|
||||
Kontakte
|
||||
</Link>{" "}
|
||||
einen Eintrag an.
|
||||
</p>
|
||||
) : (
|
||||
<select
|
||||
className={inputClasses}
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
>
|
||||
<option value="">Bitte wählen…</option>
|
||||
{therapeuten.map((t) => (
|
||||
<option key={t.id} value={t.id}>
|
||||
{t.name}
|
||||
{t.stadt ? ` (${t.stadt})` : ""}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="datum">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<Label>Datum</Label>
|
||||
<DateInput
|
||||
value={field.state.value}
|
||||
onChange={(iso) => field.handleChange(iso)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="diagnose">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<Label>Diagnose</Label>
|
||||
<input
|
||||
type="text"
|
||||
className={inputClasses}
|
||||
placeholder="z.B. F32.1"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<form.Field name="dringlichkeitscode">
|
||||
{(field) => (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
id="dringlichkeitscode"
|
||||
checked={field.state.value}
|
||||
onCheckedChange={(checked) => field.handleChange(checked)}
|
||||
/>
|
||||
<Label htmlFor="dringlichkeitscode">
|
||||
Dringlichkeitscode erhalten
|
||||
</Label>
|
||||
</div>
|
||||
{!field.state.value && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Ohne Dringlichkeitscode kann die TSS dich ggf. nicht
|
||||
vermitteln. Frage in der Sprechstunde gezielt danach.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit" size="sm" disabled={therapeuten.length === 0}>
|
||||
Erstgespräch durchgeführt
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
await setTssKontaktiert();
|
||||
onUpdate();
|
||||
}}
|
||||
>
|
||||
TSS kontaktiert
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TssDone({ datum }: { datum: string }) {
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
TSS kontaktiert am {new Date(datum).toLocaleDateString("de-DE")}
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function EigensucheInfo({
|
||||
kontaktGesamt,
|
||||
absagen,
|
||||
}: {
|
||||
kontaktGesamt: number;
|
||||
absagen: number;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{kontaktGesamt} Kontaktversuche, davon {absagen} Absagen.{" "}
|
||||
<Link to="/kontakte" className="text-primary underline">
|
||||
Kontakte verwalten
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function AntragLink() {
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Dein Kostenerstattungsantrag kann eingereicht werden.{" "}
|
||||
<Link to="/antrag" className="text-primary underline">
|
||||
Zum Antrag
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user