'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { formatDate } from '@kit/shared/dates'; import { Badge } from '@kit/ui/badge'; import { Button } from '@kit/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card'; import { Input } from '@kit/ui/input'; import { Label } from '@kit/ui/label'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@kit/ui/tabs'; import { useActionWithToast } from '@kit/ui/use-action-with-toast'; import { STATUS_LABELS, getMemberStatusColor, formatIban, computeAge, computeMembershipYears, } from '../lib/member-utils'; import { createMemberRole, deleteMemberRole, createMemberHonor, deleteMemberHonor, createMandate, revokeMandate, } from '../server/actions/member-actions'; import { MemberDetailHeader } from './member-detail-header'; interface MemberRole { id: string; role_name: string; from_date: string | null; until_date: string | null; is_active: boolean; } interface MemberHonor { id: string; honor_name: string; honor_date: string | null; description: string | null; } interface SepaMandate { id: string; mandate_reference: string; iban: string; bic: string | null; account_holder: string; mandate_date: string; status: string; is_primary: boolean; sequence: string; } interface MemberDetailTabsProps { member: Record; account: string; accountId: string; roles?: MemberRole[]; honors?: MemberHonor[]; mandates?: SepaMandate[]; } function DetailRow({ label, value, }: { label: string; value: React.ReactNode; }) { return (
{label} {value ?? '—'}
); } export function MemberDetailTabs({ member, account, accountId, roles = [], honors = [], mandates = [], }: MemberDetailTabsProps) { const memberId = String(member.id); const status = String(member.status ?? 'active'); const age = computeAge(member.date_of_birth as string | null); const membershipYears = computeMembershipYears( member.entry_date as string | null, ); return (
Stammdaten Mitgliedschaft Finanzen Funktionen & Ehrungen {(roles.length > 0 || honors.length > 0) && ( {roles.length + honors.length} )} Datenschutz {/* Tab 1: Stammdaten */}
Persönliche Daten Kontakt Adresse {Boolean(member.street2) && ( )} { const code = String(member.country ?? 'DE'); const countries: Record = { DE: 'Deutschland', AT: 'Österreich', CH: 'Schweiz', LI: 'Liechtenstein', LU: 'Luxemburg', IT: 'Italien', FR: 'Frankreich', NL: 'Niederlande', BE: 'Belgien', PL: 'Polen', CZ: 'Tschechien', DK: 'Dänemark', }; return countries[code] ?? code; })() } /> {/* Guardian info for youth members */} {Boolean(member.is_youth) && ( Erziehungsberechtigte/r )}
{/* Tab 2: Mitgliedschaft */}
Mitgliedschaft {STATUS_LABELS[status] ?? status} } /> 0 ? `${membershipYears} Jahre` : '< 1 Jahr' } /> {Boolean(member.exit_date) && ( <> )} Merkmale
{Boolean(member.is_honorary) && Ehrenmitglied} {Boolean(member.is_founding_member) && ( Gründungsmitglied )} {Boolean(member.is_youth) && Jugend} {Boolean(member.is_retiree) && Senior} {Boolean(member.is_probationary) && ( Probezeit )} {Boolean(member.is_transferred) && ( Überweisung )} {!member.is_honorary && !member.is_founding_member && !member.is_youth && !member.is_retiree && !member.is_probationary && !member.is_transferred && ( Keine besonderen Merkmale )}
{Boolean(member.notes) && (

Notizen

{String(member.notes)}

)}
{/* Tab 3: Finanzen */}
Bankverbindung
{/* Tab 4: Funktionen & Ehrungen */}
{/* Tab 5: Datenschutz */}
DSGVO-Einwilligungen {Boolean(member.gdpr_consent_date) && ( )} Datenqualität
); } /* ─── Consent Row ─── */ function ConsentRow({ label, value, invert, }: { label: string; value: boolean; invert?: boolean; }) { const isGood = invert ? !value : value; return (
{label} {value ? 'Ja' : 'Nein'}
); } /* ─── Roles Section ─── */ function RolesSection({ roles, memberId, accountId, }: { roles: MemberRole[]; memberId: string; accountId: string; }) { const router = useRouter(); const [showForm, setShowForm] = useState(false); const [roleName, setRoleName] = useState(''); const [fromDate, setFromDate] = useState(''); const [untilDate, setUntilDate] = useState(''); const { execute: executeCreate, isPending: isCreating } = useActionWithToast( createMemberRole, { successMessage: 'Funktion erstellt', onSuccess: () => { setShowForm(false); setRoleName(''); setFromDate(''); setUntilDate(''); router.refresh(); }, }, ); const { execute: executeDeleteRole } = useActionWithToast(deleteMemberRole, { successMessage: 'Funktion gelöscht', onSuccess: () => router.refresh(), }); return ( Funktionen {showForm && (
setRoleName(e.target.value)} placeholder="z.B. Kassier" data-test="role-name-input" />
setFromDate(e.target.value)} />
setUntilDate(e.target.value)} />
)} {roles.length > 0 ? (
{roles.map((role) => (

{role.role_name}

{role.from_date ? formatDate(role.from_date) : '—'} {' — '} {role.until_date ? formatDate(role.until_date) : 'heute'}

))}
) : (

Keine Funktionen zugewiesen.

)}
); } /* ─── Honors Section ─── */ function HonorsSection({ honors, memberId, accountId, }: { honors: MemberHonor[]; memberId: string; accountId: string; }) { const router = useRouter(); const [showForm, setShowForm] = useState(false); const [honorName, setHonorName] = useState(''); const [honorDate, setHonorDate] = useState(''); const [description, setDescription] = useState(''); const { execute: executeCreate, isPending: isCreating } = useActionWithToast( createMemberHonor, { successMessage: 'Ehrung erstellt', onSuccess: () => { setShowForm(false); setHonorName(''); setHonorDate(''); setDescription(''); router.refresh(); }, }, ); const { execute: executeDeleteHonor } = useActionWithToast( deleteMemberHonor, { successMessage: 'Ehrung gelöscht', onSuccess: () => router.refresh(), }, ); return ( Ehrungen {showForm && (
setHonorName(e.target.value)} placeholder="z.B. 25 Jahre Mitgliedschaft" data-test="honor-name-input" />
setHonorDate(e.target.value)} />
setDescription(e.target.value)} placeholder="Optional" />
)} {honors.length > 0 ? (
{honors.map((honor) => (

{honor.honor_name}

{honor.honor_date ? formatDate(honor.honor_date) : '—'} {honor.description && ` — ${honor.description}`}

))}
) : (

Keine Ehrungen vorhanden.

)}
); } /* ─── Mandates Section ─── */ function MandatesSection({ mandates, memberId, accountId, }: { mandates: SepaMandate[]; memberId: string; accountId: string; }) { const router = useRouter(); const [showForm, setShowForm] = useState(false); const [mandateRef, setMandateRef] = useState(''); const [iban, setIban] = useState(''); const [bic, setBic] = useState(''); const [holder, setHolder] = useState(''); const [mandateDate, setMandateDate] = useState( new Date().toISOString().split('T')[0]!, ); const { execute: executeCreate, isPending: isCreating } = useActionWithToast( createMandate, { successMessage: 'Mandat erstellt', onSuccess: () => { setShowForm(false); setMandateRef(''); setIban(''); setBic(''); setHolder(''); router.refresh(); }, }, ); const { execute: executeRevoke } = useActionWithToast(revokeMandate, { successMessage: 'Mandat widerrufen', onSuccess: () => router.refresh(), }); return ( SEPA-Mandate {showForm && (
setMandateRef(e.target.value)} data-test="mandate-ref-input" />
setMandateDate(e.target.value)} />
setIban(e.target.value)} data-test="mandate-iban-input" />
setBic(e.target.value)} />
setHolder(e.target.value)} data-test="mandate-holder-input" />
)} {mandates.length > 0 ? (
{mandates.map((m) => (

{m.mandate_reference}

{m.status} {m.is_primary && Primär}

{formatIban(m.iban)} — {m.account_holder}

{m.status === 'active' && ( )}
))}
) : (

Keine SEPA-Mandate vorhanden.

)}
); }