continued the kita-planer
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user