'use client'; import { useCallback, useEffect, useMemo, useState } from 'react'; import toast from 'react-hot-toast'; import { useLanguage } from '@/context/LanguageContext'; import { financeApi, type EventFinance } from '@/lib/api'; import { Skeleton } from '@/components/ui/Skeleton'; import { SubNav } from './ui'; import { SummaryView } from './SummaryView'; import { ExpensesView } from './ExpensesView'; import { SplitView } from './SplitView'; import { LifecycleStepper } from './LifecycleStepper'; import { deriveLifecycle } from './derive'; export type FinanceSection = 'summary' | 'expenses' | 'split'; const SECTIONS: FinanceSection[] = ['summary', 'expenses', 'split']; /** Keep the open section in the URL (?fin=expenses) so reloads and shared links land on it. */ function readSection(): FinanceSection { if (typeof window === 'undefined') return 'summary'; const v = new URLSearchParams(window.location.search).get('fin'); return SECTIONS.includes(v as FinanceSection) ? (v as FinanceSection) : 'summary'; } function writeSection(section: FinanceSection) { const url = new URL(window.location.href); if (section === 'summary') url.searchParams.delete('fin'); else url.searchParams.set('fin', section); window.history.replaceState(window.history.state, '', url); } /** * Finance tab: P&L summary with charts, expenses and other income, and the * partner split with payouts. The server already scopes what comes back (e.g. * collaborators only get their own share), so this renders what it receives. */ export function FinanceTab({ eventId }: { eventId: string }) { const { t } = useLanguage(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [section, setSectionState] = useState('summary'); useEffect(() => { setSectionState(readSection()); }, []); const setSection = useCallback((s: FinanceSection) => { setSectionState(s); writeSection(s); }, []); const load = useCallback(async () => { try { setData(await financeApi.get(eventId)); } catch (error: any) { toast.error(error.message || t('admin.finance.loadError')); } finally { setLoading(false); } }, [eventId, t]); useEffect(() => { load(); }, [load]); // Recomputed with every reload, so "ended X days ago" stays current. const lifecycle = useMemo(() => (data ? deriveLifecycle(data) : null), [data]); if (loading) { return (
{Array.from({ length: 4 }).map((_, i) => )}
); } if (!data || !lifecycle) return

{t('admin.finance.loadError')}

; return (
items={[ { key: 'summary', label: t('admin.finance.subnav.summary') }, { key: 'expenses', label: t('admin.finance.subnav.expenses'), count: data.expenses.length + data.otherIncome.length }, { key: 'split', label: t('admin.finance.subnav.split'), count: data.partners.length }, ]} active={section} onChange={setSection} /> {section === 'summary' && } {section === 'expenses' && } {section === 'split' && }
); }