"use client"; import { useState, useMemo } from "react"; import { Baby, Contact, Mail, Phone, Search, ShieldCheck, SlidersHorizontal, UserRound, Home, MapPin, } from "lucide-react"; import { format } from "date-fns"; import { de } from "date-fns/locale"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; export type DirectoryParentDto = { id: string; firstName: string; lastName: string; hasDirectoryOptIn: boolean; email?: string; phone?: string; street?: string; postalCode?: string; city?: string; duties: { id: string; name: string; }[]; }; export type DirectoryFamilyDto = { id: string; name: string; children: { id: string; firstName: string; lastName: string; dateOfBirth?: Date | null; }[]; parents: DirectoryParentDto[]; }; export function DirectoryClient({ initialFamilies, }: { initialFamilies: DirectoryFamilyDto[]; }) { const [searchQuery, setSearchQuery] = useState(""); const [activeFilters, setActiveFilters] = useState>(new Set()); const [selectedFamily, setSelectedFamily] = useState(null); const [privacyDialogOpen, setPrivacyDialogOpen] = useState(false); const toggleFilter = (filterKey: string) => { setActiveFilters((prev) => { const next = new Set(prev); if (next.has(filterKey)) { next.delete(filterKey); } else { next.add(filterKey); } return next; }); }; const filteredFamilies = useMemo(() => { return initialFamilies.filter((family) => { // 1. Search Query filter const query = searchQuery.trim().toLowerCase(); if (query) { const familyNameMatch = family.name.toLowerCase().includes(query); const childMatch = family.children.some( (c) => c.firstName.toLowerCase().includes(query) || c.lastName.toLowerCase().includes(query) ); const parentMatch = family.parents.some( (p) => p.firstName.toLowerCase().includes(query) || p.lastName.toLowerCase().includes(query) || (p.email && p.email.toLowerCase().includes(query)) ); if (!familyNameMatch && !childMatch && !parentMatch) return false; } // 2. Custom filters if (activeFilters.has("telefon")) { const hasPhone = family.parents.some((p) => p.hasDirectoryOptIn && p.phone); if (!hasPhone) return false; } if (activeFilters.has("aemter")) { const hasDuties = family.parents.some((p) => p.duties.length > 0); if (!hasDuties) return false; } if (activeFilters.has("optIn")) { // Show households where all parents in the system have opted in const allOptIn = family.parents.every((p) => p.hasDirectoryOptIn); if (!allOptIn) return false; } return true; }); }, [initialFamilies, searchQuery, activeFilters]); // Recalculate metrics for filtered subset const visibleParentCount = useMemo(() => { return filteredFamilies.reduce( (count, fam) => count + fam.parents.filter((p) => p.hasDirectoryOptIn).length, 0 ); }, [filteredFamilies]); const childCount = useMemo(() => { return filteredFamilies.reduce((count, fam) => count + fam.children.length, 0); }, [filteredFamilies]); const totalVisibleParentCount = useMemo(() => { return initialFamilies.reduce( (count, fam) => count + fam.parents.filter((p) => p.hasDirectoryOptIn).length, 0 ); }, [initialFamilies]); const groupedFamilies = useMemo(() => { const groups = new Map(); filteredFamilies.forEach((family) => { const initial = family.name.trim().slice(0, 1).toUpperCase(); const key = initial || "#"; groups.set(key, [...(groups.get(key) ?? []), family]); }); return Array.from(groups.entries()) .map(([initial, families]) => ({ initial, families })) .sort((a, b) => a.initial.localeCompare(b.initial)); }, [filteredFamilies]); return (
{/* Header */}

KONTAKTE · ADRESSBUCH

Freigegebene{" "} Kontakte

Haushalte und Kontaktdaten, die explizit für das interne Kita-Adressbuch freigegeben wurden.

{totalVisibleParentCount} sichtbar
{/* Main Household List or Empty State */} {initialFamilies.length === 0 ? (

Keine freigegebenen Kontakte

Sobald mindestens ein Elternteil eines Haushalts zustimmt, erscheint die Familie hier.

) : (
{/* Metrics & Search Bar */}
setSearchQuery(e.target.value)} />
{/* Custom Filter Chips */}
{/* Household list cards wrapper */} {filteredFamilies.length === 0 ? (

Keine passenden Kontakte gefunden

Passe deine Suche oder die Filterchips an, um Einträge zu sehen.

) : ( {groupedFamilies.map((group) => (

{group.initial}

{group.families.length}
{group.families.map((family) => { const visibleParents = family.parents.filter( (parent) => parent.hasDirectoryOptIn ); const duties = family.parents.flatMap((parent) => parent.duties); return (
{/* Left: Family Name & Children */}
{family.name.slice(0, 1)}

{family.name}

{family.children.length === 0 ? "Keine Kinder hinterlegt" : family.children .map((child) => `${child.firstName} ${child.lastName}`) .join(", ")}

{/* Middle: Parents */}
{family.parents.map((user) => (

{user.firstName} {user.lastName}

{user.hasDirectoryOptIn ? ( Opt-in ) : ( nicht freigegeben )}
{user.hasDirectoryOptIn && (
{user.email && ( {user.email} )} {user.phone ? ( {user.phone} ) : ( Keine Telefonnummer )}
)}
))}
{/* Right: Badges & Details Action */}
{visibleParents.length} von {family.parents.length} sichtbar {duties.map((duty) => ( {duty.name} ))}
); })}
))}
)}
)} {/* Household Detail Dialog */} !open && setSelectedFamily(null)} > {selectedFamily && ( <>
{selectedFamily.name.slice(0, 1)}
Familie {selectedFamily.name} Detaillierte Kontaktdaten und Haushalts-Zugehörigkeiten.
{/* Children Details */}

Kinder im Haushalt

{selectedFamily.children.length === 0 ? (

Keine Kinder hinterlegt.

) : ( selectedFamily.children.map((child) => (
{child.firstName} {child.lastName}
{child.dateOfBirth && ( Geboren: {format(new Date(child.dateOfBirth), "dd.MM.yyyy", { locale: de })} )}
)) )}
{/* Parents/Users Details */}

Eltern & Kontakte

{selectedFamily.parents.map((parent) => (
{parent.firstName} {parent.lastName} {parent.hasDirectoryOptIn ? ( Freigegeben ) : ( Privat )}
{parent.hasDirectoryOptIn ? (
{parent.email && ( {parent.email} )} {parent.phone ? ( {parent.phone} ) : (
Keine Telefonnummer hinterlegt
)} {/* Address details */} {(parent.street || parent.postalCode || parent.city) && (
{parent.street &&

{parent.street}

} {(parent.postalCode || parent.city) && (

{parent.postalCode} {parent.city}

)}
)}
) : (

Dieser Kontakt hat seine Daten nicht für das Adressbuch freigegeben.

)} {parent.duties.length > 0 && (
{parent.duties.map((duty) => ( {duty.name} ))}
)}
))}
)}
{/* Global Privacy settings dialog */} Sichtbarkeit & Datenschutz Deine Privatsphäre ist uns wichtig. In diesem Adressbuch werden Kontaktdaten nur für Haushalte angezeigt, die dem Opt-in für das interne Kita-Adressbuch explizit zugestimmt haben.

Du kannst deine eigene Zustimmung sowie deine E-Mail-Adresse und Telefonnummer jederzeit in deinen persönlichen Profileinstellungen einsehen und anpassen.
); } function DirectoryMetric({ label, value }: { label: string; value: number }) { return ( {value} {label} ); }