feat: pre-existing local changes — fischerei, verband, modules, members, packages
Commits all remaining uncommitted local work: - apps/web: fischerei, verband, modules, members-cms, documents, newsletter, meetings, site-builder, courses, bookings, events, finance pages and components - apps/web: marketing page updates, layout, paths config, next.config.mjs, styles/makerkit.css - apps/web/i18n: documents, fischerei, marketing, verband (de+en) - packages/features: finance, fischerei, member-management, module-builder, newsletter, sitzungsprotokolle, verbandsverwaltung server APIs and components - packages/ui: button.tsx updates - pnpm-lock.yaml
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
Activity,
|
||||
BedDouble,
|
||||
} from 'lucide-react';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
import { createBookingManagementApi } from '@kit/booking-management/api';
|
||||
import { createCourseManagementApi } from '@kit/course-management/api';
|
||||
@@ -44,6 +45,7 @@ export default async function TeamAccountHomePage({
|
||||
}: TeamAccountHomePageProps) {
|
||||
const { account } = await params;
|
||||
const client = getSupabaseServerClient();
|
||||
const t = await getTranslations('common');
|
||||
|
||||
const { data: acct } = await client
|
||||
.from('accounts')
|
||||
@@ -111,41 +113,47 @@ export default async function TeamAccountHomePage({
|
||||
{/* Stats Row */}
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<StatsCard
|
||||
title="Mitglieder"
|
||||
title={t('dashboard.members')}
|
||||
value={memberStats.active}
|
||||
icon={<UserCheck className="h-5 w-5" />}
|
||||
description={`${memberStats.total} gesamt, ${memberStats.pending} ausstehend`}
|
||||
icon={<UserCheck className="h-5 w-5" aria-hidden="true" />}
|
||||
description={t('dashboard.membersDescription', {
|
||||
total: memberStats.total,
|
||||
pending: memberStats.pending,
|
||||
})}
|
||||
/>
|
||||
<StatsCard
|
||||
title="Kurse"
|
||||
title={t('dashboard.courses')}
|
||||
value={courseStats.openCourses}
|
||||
icon={<GraduationCap className="h-5 w-5" />}
|
||||
description={`${courseStats.totalCourses} gesamt, ${courseStats.totalParticipants} Teilnehmer`}
|
||||
icon={<GraduationCap className="h-5 w-5" aria-hidden="true" />}
|
||||
description={t('dashboard.coursesDescription', {
|
||||
total: courseStats.totalCourses,
|
||||
participants: courseStats.totalParticipants,
|
||||
})}
|
||||
/>
|
||||
<StatsCard
|
||||
title="Offene Rechnungen"
|
||||
title={t('dashboard.openInvoices')}
|
||||
value={openInvoices.length}
|
||||
icon={<FileText className="h-5 w-5" />}
|
||||
description="Entwürfe zum Versenden"
|
||||
icon={<FileText className="h-5 w-5" aria-hidden="true" />}
|
||||
description={t('dashboard.openInvoicesDescription')}
|
||||
/>
|
||||
<StatsCard
|
||||
title="Newsletter"
|
||||
title={t('dashboard.newsletters')}
|
||||
value={newsletters.length}
|
||||
icon={<Mail className="h-5 w-5" />}
|
||||
description="Erstellt"
|
||||
icon={<Mail className="h-5 w-5" aria-hidden="true" />}
|
||||
description={t('dashboard.newslettersDescription')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||
{/* Letzte Aktivität */}
|
||||
{/* Recent Activity */}
|
||||
<Card className="lg:col-span-2">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Activity className="h-5 w-5" />
|
||||
Letzte Aktivität
|
||||
<Activity className="h-5 w-5" aria-hidden="true" />
|
||||
{t('dashboard.recentActivity')}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Aktuelle Buchungen und Veranstaltungen
|
||||
{t('dashboard.recentActivityDescription')}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -160,14 +168,14 @@ export default async function TeamAccountHomePage({
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-full bg-blue-500/10 p-2 text-blue-600">
|
||||
<BedDouble className="h-4 w-4" />
|
||||
<BedDouble className="h-4 w-4" aria-hidden="true" />
|
||||
</div>
|
||||
<div>
|
||||
<Link
|
||||
href={`/home/${account}/bookings/${String(booking.id)}`}
|
||||
className="text-sm font-medium hover:underline"
|
||||
>
|
||||
Buchung{' '}
|
||||
{t('dashboard.bookingFrom')}{' '}
|
||||
{booking.check_in
|
||||
? formatDate(booking.check_in as string)
|
||||
: '—'}
|
||||
@@ -194,7 +202,10 @@ export default async function TeamAccountHomePage({
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-full bg-amber-500/10 p-2 text-amber-600">
|
||||
<CalendarDays className="h-4 w-4" />
|
||||
<CalendarDays
|
||||
className="h-4 w-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Link
|
||||
@@ -216,20 +227,22 @@ export default async function TeamAccountHomePage({
|
||||
|
||||
{bookings.data.length === 0 && events.data.length === 0 && (
|
||||
<EmptyState
|
||||
icon={<Activity className="h-8 w-8" />}
|
||||
title="Noch keine Aktivitäten"
|
||||
description="Aktuelle Buchungen und Veranstaltungen werden hier angezeigt."
|
||||
icon={<Activity className="h-8 w-8" aria-hidden="true" />}
|
||||
title={t('dashboard.recentActivityEmpty')}
|
||||
description={t('dashboard.recentActivityEmptyDescription')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Schnellaktionen */}
|
||||
{/* Quick Actions */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Schnellaktionen</CardTitle>
|
||||
<CardDescription>Häufig verwendete Aktionen</CardDescription>
|
||||
<CardTitle>{t('dashboard.quickActions')}</CardTitle>
|
||||
<CardDescription>
|
||||
{t('dashboard.quickActionsDescription')}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-2">
|
||||
<Link
|
||||
@@ -237,10 +250,10 @@ export default async function TeamAccountHomePage({
|
||||
className="border-border bg-background hover:bg-muted hover:text-foreground inline-flex w-full items-center justify-between gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-all"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<UserPlus className="h-4 w-4" />
|
||||
Neues Mitglied
|
||||
<UserPlus className="h-4 w-4" aria-hidden="true" />
|
||||
{t('dashboard.newMember')}
|
||||
</span>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
@@ -248,10 +261,10 @@ export default async function TeamAccountHomePage({
|
||||
className="border-border bg-background hover:bg-muted hover:text-foreground inline-flex w-full items-center justify-between gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-all"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<GraduationCap className="h-4 w-4" />
|
||||
Neuer Kurs
|
||||
<GraduationCap className="h-4 w-4" aria-hidden="true" />
|
||||
{t('dashboard.newCourse')}
|
||||
</span>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
@@ -260,9 +273,9 @@ export default async function TeamAccountHomePage({
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Mail className="h-4 w-4" />
|
||||
Newsletter erstellen
|
||||
{t('dashboard.createNewsletter')}
|
||||
</span>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
@@ -270,10 +283,10 @@ export default async function TeamAccountHomePage({
|
||||
className="border-border bg-background hover:bg-muted hover:text-foreground inline-flex w-full items-center justify-between gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-all"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<BedDouble className="h-4 w-4" />
|
||||
Neue Buchung
|
||||
<BedDouble className="h-4 w-4" aria-hidden="true" />
|
||||
{t('dashboard.newBooking')}
|
||||
</span>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
@@ -281,98 +294,14 @@ export default async function TeamAccountHomePage({
|
||||
className="border-border bg-background hover:bg-muted hover:text-foreground inline-flex w-full items-center justify-between gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-all"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Plus className="h-4 w-4" />
|
||||
Neue Veranstaltung
|
||||
<Plus className="h-4 w-4" aria-hidden="true" />
|
||||
{t('dashboard.newEvent')}
|
||||
</span>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
<ArrowRight className="h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Module Overview Row */}
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm font-medium">
|
||||
Buchungen
|
||||
</p>
|
||||
<p className="text-2xl font-bold">{bookings.total}</p>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{
|
||||
bookings.data.filter(
|
||||
(b: Record<string, unknown>) =>
|
||||
b.status === 'confirmed' || b.status === 'checked_in',
|
||||
).length
|
||||
}{' '}
|
||||
aktiv
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href={`/home/${account}/bookings`}
|
||||
className="hover:bg-muted hover:text-foreground inline-flex h-9 w-9 items-center justify-center rounded-lg text-sm font-medium transition-all"
|
||||
>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm font-medium">
|
||||
Veranstaltungen
|
||||
</p>
|
||||
<p className="text-2xl font-bold">{events.total}</p>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{
|
||||
events.data.filter(
|
||||
(e: Record<string, unknown>) =>
|
||||
e.status === 'published' ||
|
||||
e.status === 'registration_open',
|
||||
).length
|
||||
}{' '}
|
||||
aktiv
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href={`/home/${account}/events`}
|
||||
className="hover:bg-muted hover:text-foreground inline-flex h-9 w-9 items-center justify-center rounded-lg text-sm font-medium transition-all"
|
||||
>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm font-medium">
|
||||
Kurse abgeschlossen
|
||||
</p>
|
||||
<p className="text-2xl font-bold">
|
||||
{courseStats.completedCourses}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
von {courseStats.totalCourses} insgesamt
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href={`/home/${account}/courses`}
|
||||
className="hover:bg-muted hover:text-foreground inline-flex h-9 w-9 items-center justify-center rounded-lg text-sm font-medium transition-all"
|
||||
>
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</CmsPageShell>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user