add process stepper dashboard with phase cards, contact stats
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { Badge } from "@/shared/components/ui/badge";
|
||||
import { Card, CardContent } from "@/shared/components/ui/card";
|
||||
import { cn } from "@/shared/lib/utils";
|
||||
|
||||
type PhaseStatus = "erledigt" | "aktuell" | "offen";
|
||||
|
||||
interface PhaseCardProps {
|
||||
label: string;
|
||||
beschreibung: string;
|
||||
status: PhaseStatus;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export function PhaseCard({
|
||||
label,
|
||||
beschreibung,
|
||||
status,
|
||||
index,
|
||||
}: PhaseCardProps) {
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"transition-all",
|
||||
status === "aktuell" && "ring-2 ring-primary border-primary",
|
||||
status === "erledigt" && "opacity-60",
|
||||
)}
|
||||
>
|
||||
<CardContent className="flex items-start gap-4">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-8 w-8 shrink-0 items-center justify-center rounded-full border-2 text-sm font-bold",
|
||||
status === "erledigt" &&
|
||||
"border-primary bg-primary text-primary-foreground",
|
||||
status === "aktuell" && "border-primary text-primary",
|
||||
status === "offen" &&
|
||||
"border-muted-foreground/40 text-muted-foreground/40",
|
||||
)}
|
||||
>
|
||||
{status === "erledigt" ? "✓" : index + 1}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{label}</span>
|
||||
{status === "aktuell" && <Badge>Aktuell</Badge>}
|
||||
</div>
|
||||
{status === "aktuell" && (
|
||||
<p className="text-sm text-muted-foreground">{beschreibung}</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/shared/components/ui/card";
|
||||
import type { ProzessSchritt } from "@/shared/db/schema";
|
||||
import { PROZESS_SCHRITTE } from "@/shared/lib/constants";
|
||||
import { PhaseCard } from "./phase-card";
|
||||
|
||||
interface ProcessStepperProps {
|
||||
aktuellerSchritt: ProzessSchritt;
|
||||
kontaktGesamt: number;
|
||||
absagen: number;
|
||||
}
|
||||
|
||||
export function ProcessStepper({
|
||||
aktuellerSchritt,
|
||||
kontaktGesamt,
|
||||
absagen,
|
||||
}: ProcessStepperProps) {
|
||||
const currentIndex = PROZESS_SCHRITTE.findIndex(
|
||||
(s) => s.key === aktuellerSchritt,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-lg flex-col gap-6 p-4">
|
||||
<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>
|
||||
</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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{currentIndex >= 3 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Kontaktübersicht</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{kontaktGesamt} Kontaktversuche insgesamt, davon {absagen}{" "}
|
||||
Absagen.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { ProzessSchritt } from "@/shared/db/schema";
|
||||
import { dbExec, useDbQuery } from "@/shared/hooks/use-db";
|
||||
|
||||
interface NutzerRow {
|
||||
id: number;
|
||||
name: string;
|
||||
aktueller_schritt: ProzessSchritt;
|
||||
dringlichkeitscode: boolean;
|
||||
tss_beantragt: boolean;
|
||||
krankenkasse: string;
|
||||
}
|
||||
|
||||
interface KontaktStats {
|
||||
gesamt: number;
|
||||
absagen: number;
|
||||
warteliste: number;
|
||||
keine_antwort: number;
|
||||
}
|
||||
|
||||
export function useNutzer() {
|
||||
return useDbQuery<NutzerRow>("SELECT * FROM nutzer LIMIT 1");
|
||||
}
|
||||
|
||||
export function useKontaktStats() {
|
||||
return useDbQuery<KontaktStats>(`
|
||||
SELECT
|
||||
COUNT(*) as gesamt,
|
||||
COUNT(*) FILTER (WHERE ergebnis = 'absage') as absagen,
|
||||
COUNT(*) FILTER (WHERE ergebnis = 'warteliste') as warteliste,
|
||||
COUNT(*) FILTER (WHERE ergebnis = 'keine_antwort') as keine_antwort
|
||||
FROM kontakt
|
||||
`);
|
||||
}
|
||||
|
||||
export async function updateSchritt(schritt: ProzessSchritt) {
|
||||
await dbExec(
|
||||
"UPDATE nutzer SET aktueller_schritt = $1, aktualisiert_am = NOW() WHERE id = 1",
|
||||
[schritt],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { ProcessStepper } from "./components/process-stepper";
|
||||
export { updateSchritt, useKontaktStats, useNutzer } from "./hooks";
|
||||
Reference in New Issue
Block a user