continued the kita-planer

This commit is contained in:
t.indorf
2026-05-08 14:32:14 +02:00
parent b686e714ff
commit 7aff691803
85 changed files with 9434 additions and 588 deletions
+173
View File
@@ -0,0 +1,173 @@
"use client";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import {
ArrowRight,
CheckCircle2,
Home,
MapPin,
Phone,
Sparkles,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Progress } from "@/components/ui/progress";
type UserRoleDto =
| "SUPERADMIN"
| "ADMIN"
| "KOORDINATOR"
| "ERZIEHER"
| "ELTERN";
type OnboardingDialogProps = {
user: {
phone: string | null;
street: string | null;
postalCode: string | null;
city: string | null;
familyId: string | null;
role: UserRoleDto;
};
family: {
childrenCount: number;
} | null;
};
const HIDE_ONBOARDING_DIALOG_KEY = "hideOnboardingDialog";
export function OnboardingDialog({ user, family }: OnboardingDialogProps) {
const [open, setOpen] = useState(false);
const status = useMemo(() => {
const hasPhone = !!user.phone?.trim();
const hasAddress =
!!user.street?.trim() && !!user.postalCode?.trim() && !!user.city?.trim();
const needsFamilyData = user.role !== "ERZIEHER";
const hasFamilyAndChildren =
!needsFamilyData || (!!user.familyId && !!family?.childrenCount);
const missingItems = [
!hasPhone ? { label: "Telefonnummer fehlt", icon: Phone } : null,
!hasAddress ? { label: "Adresse fehlt", icon: MapPin } : null,
!hasFamilyAndChildren
? { label: "Haushalts-/Kinderdaten unvollständig", icon: Home }
: null,
].filter(Boolean) as { label: string; icon: typeof Phone }[];
const completedCount = [hasPhone, hasAddress, hasFamilyAndChildren].filter(
Boolean,
).length;
const progress = completedCount === 3 ? 100 : completedCount * 33;
return {
completedCount,
progress,
missingItems,
hasFamilyAndChildren,
isComplete: completedCount === 3,
};
}, [family?.childrenCount, user]);
useEffect(() => {
if (status.isComplete) return;
const hidden = localStorage.getItem(HIDE_ONBOARDING_DIALOG_KEY);
if (hidden !== "true") {
const timer = window.setTimeout(() => setOpen(true), 0);
return () => window.clearTimeout(timer);
}
}, [status.isComplete]);
if (status.isComplete) {
return null;
}
const ctaLabel = status.hasFamilyAndChildren
? "Kontaktdaten hinterlegen"
: "Haushalt & Kinder einrichten";
function hidePermanently() {
localStorage.setItem(HIDE_ONBOARDING_DIALOG_KEY, "true");
setOpen(false);
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="overflow-hidden p-0 sm:max-w-xl">
<div className="bg-emerald-950 px-6 py-6 text-white">
<div className="mb-5 flex h-11 w-11 items-center justify-center rounded-full bg-emerald-300 text-emerald-950">
<Sparkles className="h-5 w-5" />
</div>
<DialogHeader className="space-y-2 text-left">
<DialogTitle className="text-2xl leading-tight">
Dein Kita-Profil ist fast bereit
</DialogTitle>
<DialogDescription className="text-emerald-50/80">
Vervollständige die wichtigsten Daten, damit Notdienst,
Adressbuch und Familienverwaltung reibungslos funktionieren.
</DialogDescription>
</DialogHeader>
</div>
<div className="space-y-5 px-6 py-6">
<div>
<div className="mb-2 flex items-center justify-between gap-3 text-sm">
<span className="font-medium">Profil-Fortschritt</span>
<span className="text-muted-foreground">
{status.completedCount}/3 erledigt
</span>
</div>
<Progress value={status.progress} />
<p className="mt-2 text-xs text-muted-foreground">
{status.progress}% vollständig
</p>
</div>
<div className="rounded-md border bg-muted/30 p-4">
<p className="mb-3 text-sm font-medium">Noch offen</p>
<ul className="space-y-2">
{status.missingItems.map(({ label, icon: Icon }) => (
<li key={label} className="flex items-center gap-2 text-sm">
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-destructive/10 text-destructive">
<Icon className="h-3.5 w-3.5" />
</span>
{label}
</li>
))}
</ul>
</div>
<div className="flex items-start gap-2 rounded-md bg-emerald-50 p-3 text-sm text-emerald-950">
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0" />
<p>
Du kannst die App weiter nutzen. Diese Erinnerung hilft nur beim
sauberen Einrichten der wichtigsten MVP-Daten.
</p>
</div>
</div>
<DialogFooter className="gap-2 border-t px-6 py-4 sm:justify-between sm:space-x-0">
<Button type="button" variant="ghost" onClick={hidePermanently}>
Nicht mehr anzeigen
</Button>
<Button asChild onClick={() => setOpen(false)}>
<Link href="/dashboard/profil">
{ctaLabel}
<ArrowRight className="h-4 w-4" />
</Link>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}