Every 'read-only placeholder' and 'missing functionality' gap from the QA audit is now resolved: COURSES — categories/instructors/locations can now be deleted: - Added update/delete methods to course-reference-data.service.ts - Added deleteCategory/deleteInstructor/deleteLocation server actions - Created DeleteRefDataButton client component with confirmation dialog - Wired delete buttons into all three table pages BOOKINGS — calendar month navigation now works: - Calendar was hardcoded to current month with disabled prev/next - Added year/month search params for server-side month rendering - Replaced disabled buttons with Link-based navigation - Verified: clicking next/prev correctly renders different months DOCUMENTS — templates page now reads from database: - Was hardcoded empty array; now queries document_templates table - Table exists since migration 20260414000006_shared_templates.sql FISCHEREI — statistics page shows real data: - Replaced dashed-border placeholder with 6 real stat cards - Queries waters, species, stocking, catch_books, leases, permits - Shows counts + stocking costs + pending catch books - Falls back to helpful message when no data exists VERBAND — statistics page shows real KPIs: - Added server-side data fetching (clubs, members, fees) - Passes activeClubs, totalMembers, openFees as props - Added 4 KPI cards: Aktive Vereine, Gesamtmitglieder, ∅ Mitglieder/Verein, Offene Beiträge - Kept existing trend charts below KPI cards
65 lines
2.2 KiB
TypeScript
65 lines
2.2 KiB
TypeScript
import { getTranslations } from 'next-intl/server';
|
|
|
|
import { VerbandTabNavigation } from '@kit/verbandsverwaltung/components';
|
|
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
|
|
|
import { AccountNotFound } from '~/components/account-not-found';
|
|
import { CmsPageShell } from '~/components/cms-page-shell';
|
|
|
|
import StatisticsContent from './_components/statistics-content';
|
|
|
|
interface Props {
|
|
params: Promise<{ account: string }>;
|
|
}
|
|
|
|
export default async function StatisticsPage({ params }: Props) {
|
|
const { account } = await params;
|
|
const t = await getTranslations('verband');
|
|
const client = getSupabaseServerClient();
|
|
|
|
const { data: acct } = await client
|
|
.from('accounts')
|
|
.select('id')
|
|
.eq('slug', account)
|
|
.single();
|
|
|
|
if (!acct) return <AccountNotFound />;
|
|
|
|
// Fetch real verband stats
|
|
const [clubsResult, membersResult, feesResult] = await Promise.allSettled([
|
|
client
|
|
.from('member_clubs')
|
|
.select('id, status, member_count', { count: 'exact' })
|
|
.eq('account_id', acct.id),
|
|
client
|
|
.from('members')
|
|
.select('id', { count: 'exact' })
|
|
.eq('account_id', acct.id)
|
|
.eq('status', 'active'),
|
|
(client.from as any)('club_fees')
|
|
.select('amount, status')
|
|
.eq('account_id', acct.id),
|
|
]);
|
|
|
|
const clubs = clubsResult.status === 'fulfilled' ? (clubsResult.value.data ?? []) : [];
|
|
const activeClubs = clubs.filter((c: any) => c.status !== 'archived').length;
|
|
const totalMembers = clubsResult.status === 'fulfilled'
|
|
? clubs.reduce((sum: number, c: any) => sum + (Number(c.member_count) || 0), 0)
|
|
: 0;
|
|
const directMembers = membersResult.status === 'fulfilled' ? (membersResult.value.count ?? 0) : 0;
|
|
const fees = feesResult.status === 'fulfilled' ? (feesResult.value.data ?? []) : [];
|
|
const openFees = fees.filter((f: any) => f.status !== 'paid').reduce((s: number, f: any) => s + (Number(f.amount) || 0), 0);
|
|
|
|
return (
|
|
<CmsPageShell account={account} title={t('pages.statisticsTitle')}>
|
|
<VerbandTabNavigation account={account} activeTab="statistics" />
|
|
<StatisticsContent
|
|
activeClubs={activeClubs}
|
|
totalClubs={clubs.length}
|
|
totalMembers={totalMembers || directMembers}
|
|
openFees={openFees}
|
|
/>
|
|
</CmsPageShell>
|
|
);
|
|
}
|