fix: add missing newlines at the end of JSON files; clean up formatting in page components
Some checks failed
Workflow / ʦ TypeScript (push) Failing after 17m4s
Workflow / ⚫️ Test (push) Has been skipped

This commit is contained in:
T. Zehetbauer
2026-04-02 11:02:58 +02:00
parent b26e5aaafa
commit c6d564836f
56 changed files with 471 additions and 381 deletions

View File

@@ -99,7 +99,12 @@ export default async function BookingDetailPage({ params }: PageProps) {
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<Button variant="ghost" size="icon" asChild aria-label={t('detail.backToBookings')}>
<Button
variant="ghost"
size="icon"
asChild
aria-label={t('detail.backToBookings')}
>
<Link href={`/home/${account}/bookings`}>
<ArrowLeft className="h-4 w-4" aria-hidden="true" />
</Link>

View File

@@ -1,13 +1,13 @@
import Link from 'next/link';
import { ArrowLeft, ChevronLeft, ChevronRight } from 'lucide-react';
import { getTranslations } from 'next-intl/server';
import { createBookingManagementApi } from '@kit/booking-management/api';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { Badge } from '@kit/ui/badge';
import { Button } from '@kit/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';
@@ -139,14 +139,17 @@ export default async function BookingCalendarPage({ params }: PageProps) {
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<Button variant="ghost" size="icon" asChild aria-label={t('calendar.backToBookings')}>
<Button
variant="ghost"
size="icon"
asChild
aria-label={t('calendar.backToBookings')}
>
<Link href={`/home/${account}/bookings`}>
<ArrowLeft className="h-4 w-4" aria-hidden="true" />
</Link>
</Button>
<p className="text-muted-foreground">
{t('calendar.subtitle')}
</p>
<p className="text-muted-foreground">{t('calendar.subtitle')}</p>
</div>
</div>
@@ -154,13 +157,23 @@ export default async function BookingCalendarPage({ params }: PageProps) {
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<Button variant="ghost" size="icon" disabled aria-label={t('calendar.previousMonth')}>
<Button
variant="ghost"
size="icon"
disabled
aria-label={t('calendar.previousMonth')}
>
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
</Button>
<CardTitle>
{MONTH_NAMES[month]} {year}
</CardTitle>
<Button variant="ghost" size="icon" disabled aria-label={t('calendar.nextMonth')}>
<Button
variant="ghost"
size="icon"
disabled
aria-label={t('calendar.nextMonth')}
>
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</Button>
</div>
@@ -232,7 +245,10 @@ export default async function BookingCalendarPage({ params }: PageProps) {
<p className="text-2xl font-bold">{bookings.data.length}</p>
</div>
<Badge variant="outline">
{t('calendar.daysOccupied', { occupied: occupiedDates.size, total: daysInMonth })}
{t('calendar.daysOccupied', {
occupied: occupiedDates.size,
total: daysInMonth,
})}
</Badge>
</div>
</CardContent>

View File

@@ -250,7 +250,10 @@ export default async function BookingsPage({
'secondary'
}
>
{t(STATUS_LABEL_KEYS[String(booking.status)] ?? String(booking.status))}
{t(
STATUS_LABEL_KEYS[String(booking.status)] ??
String(booking.status),
)}
</Badge>
</td>
<td className="p-3 text-right">

View File

@@ -1,7 +1,8 @@
import { getTranslations } from 'next-intl/server';
import { createCourseManagementApi } from '@kit/course-management/api';
import { CreateCourseForm } from '@kit/course-management/components';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';

View File

@@ -88,8 +88,10 @@ export default async function CourseDetailPage({ params }: PageProps) {
'secondary'
}
>
{t(COURSE_STATUS_LABEL_KEYS[String(courseData.status)] ??
String(courseData.status))}
{t(
COURSE_STATUS_LABEL_KEYS[String(courseData.status)] ??
String(courseData.status),
)}
</Badge>
</div>
</CardContent>

View File

@@ -129,7 +129,12 @@ export default async function CourseCalendarPage({
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<Button variant="ghost" size="icon" asChild aria-label={t('calendar.backToCourses')}>
<Button
variant="ghost"
size="icon"
asChild
aria-label={t('calendar.backToCourses')}
>
<Link href={`/home/${account}/courses`}>
<ArrowLeft className="h-4 w-4" aria-hidden="true" />
</Link>
@@ -142,7 +147,12 @@ export default async function CourseCalendarPage({
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<Button variant="ghost" size="icon" asChild aria-label={t('calendar.previousMonth')}>
<Button
variant="ghost"
size="icon"
asChild
aria-label={t('calendar.previousMonth')}
>
<Link
href={`/home/${account}/courses/calendar?month=${
month === 0
@@ -156,7 +166,12 @@ export default async function CourseCalendarPage({
<CardTitle>
{MONTH_NAMES[month]} {year}
</CardTitle>
<Button variant="ghost" size="icon" asChild aria-label={t('calendar.nextMonth')}>
<Button
variant="ghost"
size="icon"
asChild
aria-label={t('calendar.nextMonth')}
>
<Link
href={`/home/${account}/courses/calendar?month=${
month === 11

View File

@@ -196,7 +196,10 @@ export default async function CoursesPage({ params, searchParams }: PageProps) {
'secondary'
}
>
{t(COURSE_STATUS_LABEL_KEYS[String(course.status)] ?? String(course.status))}
{t(
COURSE_STATUS_LABEL_KEYS[String(course.status)] ??
String(course.status),
)}
</Badge>
</td>
<td className="p-3 text-right">

View File

@@ -18,7 +18,10 @@ import { Button } from '@kit/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import { CmsPageShell } from '~/components/cms-page-shell';
import { EVENT_STATUS_LABEL_KEYS, EVENT_STATUS_VARIANT } from '~/lib/status-badges';
import {
EVENT_STATUS_LABEL_KEYS,
EVENT_STATUS_VARIANT,
} from '~/lib/status-badges';
import { DeleteEventButton } from './delete-event-button';
@@ -64,8 +67,10 @@ export default async function EventDetailPage({ params }: PageProps) {
}
className="mt-1"
>
{t(EVENT_STATUS_LABEL_KEYS[String(eventData.status)] ??
String(eventData.status))}
{t(
EVENT_STATUS_LABEL_KEYS[String(eventData.status)] ??
String(eventData.status),
)}
</Badge>
</div>
<Button>

View File

@@ -21,7 +21,10 @@ import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';
import { EmptyState } from '~/components/empty-state';
import { StatsCard } from '~/components/stats-card';
import { EVENT_STATUS_VARIANT, EVENT_STATUS_LABEL_KEYS } from '~/lib/status-badges';
import {
EVENT_STATUS_VARIANT,
EVENT_STATUS_LABEL_KEYS,
} from '~/lib/status-badges';
interface PageProps {
params: Promise<{ account: string }>;
@@ -178,7 +181,10 @@ export default async function EventsPage({ params, searchParams }: PageProps) {
'secondary'
}
>
{t(EVENT_STATUS_LABEL_KEYS[String(event.status)] ?? String(event.status))}
{t(
EVENT_STATUS_LABEL_KEYS[String(event.status)] ??
String(event.status),
)}
</Badge>
</td>
<td className="p-3 text-right font-medium">

View File

@@ -13,7 +13,10 @@ import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';
import { EmptyState } from '~/components/empty-state';
import { StatsCard } from '~/components/stats-card';
import { EVENT_STATUS_VARIANT, EVENT_STATUS_LABEL_KEYS } from '~/lib/status-badges';
import {
EVENT_STATUS_VARIANT,
EVENT_STATUS_LABEL_KEYS,
} from '~/lib/status-badges';
interface PageProps {
params: Promise<{ account: string }>;
@@ -156,7 +159,10 @@ export default async function EventRegistrationsPage({ params }: PageProps) {
'secondary'
}
>
{t(EVENT_STATUS_LABEL_KEYS[event.status] ?? event.status)}
{t(
EVENT_STATUS_LABEL_KEYS[event.status] ??
event.status,
)}
</Badge>
</td>
<td className="p-3 text-right">

View File

@@ -1,13 +1,13 @@
import Link from 'next/link';
import { ArrowLeft } from 'lucide-react';
import { getTranslations } from 'next-intl/server';
import { createFinanceApi } from '@kit/finance/api';
import { formatDate } from '@kit/shared/dates';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { Badge } from '@kit/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';
@@ -66,7 +66,9 @@ export default async function InvoiceDetailPage({ params }: PageProps) {
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>
{t('invoices.invoiceLabel', { number: String(invoice.invoice_number ?? '') })}
{t('invoices.invoiceLabel', {
number: String(invoice.invoice_number ?? ''),
})}
</CardTitle>
<Badge variant={INVOICE_STATUS_VARIANT[status] ?? 'secondary'}>
{t(INVOICE_STATUS_LABEL_KEYS[status] ?? status)}
@@ -125,7 +127,9 @@ export default async function InvoiceDetailPage({ params }: PageProps) {
{/* Line Items */}
<Card>
<CardHeader>
<CardTitle>{t('invoiceForm.lineItems')} ({items.length})</CardTitle>
<CardTitle>
{t('invoiceForm.lineItems')} ({items.length})
</CardTitle>
</CardHeader>
<CardContent>
{items.length === 0 ? (
@@ -140,11 +144,15 @@ export default async function InvoiceDetailPage({ params }: PageProps) {
<th scope="col" className="p-3 text-left font-medium">
{t('invoiceForm.itemDescription')}
</th>
<th scope="col" className="p-3 text-right font-medium">{t('invoiceForm.quantity')}</th>
<th scope="col" className="p-3 text-right font-medium">
{t('invoiceForm.quantity')}
</th>
<th scope="col" className="p-3 text-right font-medium">
{t('invoices.unitPriceCol')}
</th>
<th scope="col" className="p-3 text-right font-medium">{t('invoices.totalCol')}</th>
<th scope="col" className="p-3 text-right font-medium">
{t('invoices.totalCol')}
</th>
</tr>
</thead>
<tbody>
@@ -183,7 +191,9 @@ export default async function InvoiceDetailPage({ params }: PageProps) {
</tr>
<tr>
<td colSpan={3} className="p-3 text-right font-medium">
{t('invoiceForm.tax', { rate: Number(invoice.tax_rate ?? 19) })}
{t('invoiceForm.tax', {
rate: Number(invoice.tax_rate ?? 19),
})}
</td>
<td className="p-3 text-right">
{formatCurrency(invoice.tax_amount ?? 0)}

View File

@@ -216,8 +216,10 @@ export default async function FinancePage({ params, searchParams }: PageProps) {
'secondary'
}
>
{t(BATCH_STATUS_LABEL_KEYS[String(batch.status)] ??
String(batch.status))}
{t(
BATCH_STATUS_LABEL_KEYS[String(batch.status)] ??
String(batch.status),
)}
</Badge>
</td>
<td className="p-3">
@@ -318,7 +320,11 @@ export default async function FinancePage({ params, searchParams }: PageProps) {
'secondary'
}
>
{t(INVOICE_STATUS_LABEL_KEYS[String(invoice.status)] ?? String(invoice.status))}
{t(
INVOICE_STATUS_LABEL_KEYS[
String(invoice.status)
] ?? String(invoice.status),
)}
</Badge>
</td>
</tr>
@@ -347,7 +353,12 @@ export default async function FinancePage({ params, searchParams }: PageProps) {
</Link>
</Button>
) : (
<Button variant="outline" size="sm" disabled aria-label={t('common.previous')}>
<Button
variant="outline"
size="sm"
disabled
aria-label={t('common.previous')}
>
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
</Button>
)}
@@ -366,7 +377,12 @@ export default async function FinancePage({ params, searchParams }: PageProps) {
</Link>
</Button>
) : (
<Button variant="outline" size="sm" disabled aria-label={t('common.next')}>
<Button
variant="outline"
size="sm"
disabled
aria-label={t('common.next')}
>
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</Button>
)}

View File

@@ -1,13 +1,13 @@
import Link from 'next/link';
import { Euro, CreditCard, TrendingUp, ArrowRight } from 'lucide-react';
import { getTranslations } from 'next-intl/server';
import { createFinanceApi } from '@kit/finance/api';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { Badge } from '@kit/ui/badge';
import { Button } from '@kit/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';
@@ -84,9 +84,7 @@ export default async function PaymentsPage({ params }: PageProps) {
<div className="flex w-full flex-col gap-6">
{/* Header */}
<div>
<p className="text-muted-foreground">
{t('payments.subtitle')}
</p>
<p className="text-muted-foreground">{t('payments.subtitle')}</p>
</div>
{/* Stats */}
@@ -121,7 +119,9 @@ export default async function PaymentsPage({ params }: PageProps) {
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base">{t('payments.openInvoices')}</CardTitle>
<CardTitle className="text-base">
{t('payments.openInvoices')}
</CardTitle>
<Badge
variant={openInvoices.length > 0 ? 'default' : 'secondary'}
>
@@ -131,7 +131,10 @@ export default async function PaymentsPage({ params }: PageProps) {
<CardContent>
<p className="text-muted-foreground mb-4 text-sm">
{openInvoices.length > 0
? t('payments.invoicesOpenSummary', { count: openInvoices.length, total: formatCurrency(openTotal) })
? t('payments.invoicesOpenSummary', {
count: openInvoices.length,
total: formatCurrency(openTotal),
})
: t('payments.noOpenInvoices')}
</p>
<Button variant="outline" size="sm" asChild>
@@ -145,7 +148,9 @@ export default async function PaymentsPage({ params }: PageProps) {
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-base">{t('payments.sepaBatches')}</CardTitle>
<CardTitle className="text-base">
{t('payments.sepaBatches')}
</CardTitle>
<Badge variant={batches.length > 0 ? 'default' : 'secondary'}>
{batches.length}
</Badge>
@@ -153,7 +158,10 @@ export default async function PaymentsPage({ params }: PageProps) {
<CardContent>
<p className="text-muted-foreground mb-4 text-sm">
{batches.length > 0
? t('payments.batchSummary', { count: batches.length, total: formatCurrency(sepaTotal) })
? t('payments.batchSummary', {
count: batches.length,
total: formatCurrency(sepaTotal),
})
: t('payments.noBatchesFound')}
</p>
<Button variant="outline" size="sm" asChild>

View File

@@ -1,6 +1,7 @@
import Link from 'next/link';
import { ArrowLeft, Download } from 'lucide-react';
import { getTranslations } from 'next-intl/server';
import { createFinanceApi } from '@kit/finance/api';
import { formatDate } from '@kit/shared/dates';
@@ -8,7 +9,6 @@ import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { Badge } from '@kit/ui/badge';
import { Button } from '@kit/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';
@@ -76,7 +76,9 @@ export default async function SepaBatchDetailPage({ params }: PageProps) {
{/* Summary Card */}
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>{String(batch.description ?? t('sepa.batchFallbackName'))}</CardTitle>
<CardTitle>
{String(batch.description ?? t('sepa.batchFallbackName'))}
</CardTitle>
<Badge variant={BATCH_STATUS_VARIANT[status] ?? 'secondary'}>
{t(BATCH_STATUS_LABEL_KEYS[status] ?? status)}
</Badge>
@@ -133,7 +135,9 @@ export default async function SepaBatchDetailPage({ params }: PageProps) {
{/* Items Table */}
<Card>
<CardHeader>
<CardTitle>{t('sepa.itemCount')} ({items.length})</CardTitle>
<CardTitle>
{t('sepa.itemCount')} ({items.length})
</CardTitle>
</CardHeader>
<CardContent>
{items.length === 0 ? (
@@ -145,10 +149,18 @@ export default async function SepaBatchDetailPage({ params }: PageProps) {
<table className="w-full min-w-[640px] text-sm">
<thead>
<tr className="bg-muted/50 border-b">
<th scope="col" className="p-3 text-left font-medium">Name</th>
<th scope="col" className="p-3 text-left font-medium">IBAN</th>
<th scope="col" className="p-3 text-right font-medium">{t('common.amount')}</th>
<th scope="col" className="p-3 text-left font-medium">{t('common.status')}</th>
<th scope="col" className="p-3 text-left font-medium">
Name
</th>
<th scope="col" className="p-3 text-left font-medium">
IBAN
</th>
<th scope="col" className="p-3 text-right font-medium">
{t('common.amount')}
</th>
<th scope="col" className="p-3 text-left font-medium">
{t('common.status')}
</th>
</tr>
</thead>
<tbody>
@@ -176,7 +188,11 @@ export default async function SepaBatchDetailPage({ params }: PageProps) {
ITEM_STATUS_VARIANT[itemStatus] ?? 'secondary'
}
>
{t(`sepaItemStatus.${itemStatus}` as Parameters<typeof t>[0]) ?? itemStatus}
{t(
`sepaItemStatus.${itemStatus}` as Parameters<
typeof t
>[0],
) ?? itemStatus}
</Badge>
</td>
</tr>

View File

@@ -13,7 +13,10 @@ import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';
import { EmptyState } from '~/components/empty-state';
import { BATCH_STATUS_VARIANT, BATCH_STATUS_LABEL_KEYS } from '~/lib/status-badges';
import {
BATCH_STATUS_VARIANT,
BATCH_STATUS_LABEL_KEYS,
} from '~/lib/status-badges';
interface PageProps {
params: Promise<{ account: string }>;
@@ -114,7 +117,10 @@ export default async function SepaPage({ params }: PageProps) {
'secondary'
}
>
{t(BATCH_STATUS_LABEL_KEYS[String(batch.status)] ?? String(batch.status))}
{t(
BATCH_STATUS_LABEL_KEYS[String(batch.status)] ??
String(batch.status),
)}
</Badge>
</td>
<td className="p-3">

View File

@@ -4,7 +4,24 @@ import { use } from 'react';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { Fish, FileSignature, Building2 } from 'lucide-react';
import {
Fish,
Waves,
Anchor,
BookOpen,
ShieldCheck,
Trophy,
FileSignature,
ScrollText,
ListChecks,
BookMarked,
Building2,
Network,
SearchCheck,
Share2,
PieChart,
LayoutTemplate,
} from 'lucide-react';
import * as z from 'zod';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
@@ -61,65 +78,132 @@ const getAccountFeatures = cache(async (accountSlug: string) => {
return (settings?.features as Record<string, boolean>) ?? {};
});
const iconClasses = 'w-4';
/**
* Inject per-account feature routes (e.g. Fischerei) into the parsed
* navigation config. The entry is inserted right after "Veranstaltungen".
* Inject per-account feature module route groups (Fischerei, Meetings, Verband)
* into the navigation config. These are added as separate collapsible sections
* before the Administration section (last group).
*
* Only modules enabled in the account's settings are shown.
*/
function injectAccountFeatureRoutes(
config: z.output<typeof NavigationConfigSchema>,
account: string,
features: Record<string, boolean>,
): z.output<typeof NavigationConfigSchema> {
if (!features.fischerei && !features.meetings && !features.verband)
return config;
const featureEntries: Array<{
label: string;
path: string;
Icon: React.ReactNode;
}> = [];
const featureGroups: z.output<typeof NavigationConfigSchema>['routes'] = [];
if (features.fischerei) {
featureEntries.push({
label: 'common.routes.fischerei',
path: `/home/${account}/fischerei`,
Icon: <Fish className="w-4" />,
featureGroups.push({
label: 'common:routes.fisheriesManagement',
collapsible: true,
children: [
{
label: 'common:routes.fisheriesOverview',
path: `/home/${account}/fischerei`,
Icon: <Fish className={iconClasses} />,
},
{
label: 'common:routes.fisheriesWaters',
path: `/home/${account}/fischerei/waters`,
Icon: <Waves className={iconClasses} />,
},
{
label: 'common:routes.fisheriesLeases',
path: `/home/${account}/fischerei/leases`,
Icon: <Anchor className={iconClasses} />,
},
{
label: 'common:routes.fisheriesCatchBooks',
path: `/home/${account}/fischerei/catch-books`,
Icon: <BookOpen className={iconClasses} />,
},
{
label: 'common:routes.fisheriesPermits',
path: `/home/${account}/fischerei/permits`,
Icon: <ShieldCheck className={iconClasses} />,
},
{
label: 'common:routes.fisheriesCompetitions',
path: `/home/${account}/fischerei/competitions`,
Icon: <Trophy className={iconClasses} />,
},
],
});
}
if (features.meetings) {
featureEntries.push({
label: 'common.routes.meetings',
path: `/home/${account}/meetings`,
Icon: <FileSignature className="w-4" />,
featureGroups.push({
label: 'common:routes.meetingProtocols',
collapsible: true,
children: [
{
label: 'common:routes.meetingsOverview',
path: `/home/${account}/meetings`,
Icon: <BookMarked className={iconClasses} />,
},
{
label: 'common:routes.meetingsProtocols',
path: `/home/${account}/meetings/protocols`,
Icon: <ScrollText className={iconClasses} />,
},
{
label: 'common:routes.meetingsTasks',
path: `/home/${account}/meetings/tasks`,
Icon: <ListChecks className={iconClasses} />,
},
],
});
}
if (features.verband) {
featureEntries.push({
label: 'common.routes.verband',
path: `/home/${account}/verband`,
Icon: <Building2 className="w-4" />,
featureGroups.push({
label: 'common:routes.associationManagement',
collapsible: true,
children: [
{
label: 'common:routes.associationOverview',
path: `/home/${account}/verband`,
Icon: <Building2 className={iconClasses} />,
},
{
label: 'common:routes.associationHierarchy',
path: `/home/${account}/verband/hierarchy`,
Icon: <Network className={iconClasses} />,
},
{
label: 'common:routes.associationMemberSearch',
path: `/home/${account}/verband/members`,
Icon: <SearchCheck className={iconClasses} />,
},
{
label: 'common:routes.associationEvents',
path: `/home/${account}/verband/events`,
Icon: <Share2 className={iconClasses} />,
},
{
label: 'common:routes.associationReporting',
path: `/home/${account}/verband/reporting`,
Icon: <PieChart className={iconClasses} />,
},
{
label: 'common:routes.associationTemplates',
path: `/home/${account}/verband/templates`,
Icon: <LayoutTemplate className={iconClasses} />,
},
],
});
}
return {
...config,
routes: config.routes.map((group) => {
if (!('children' in group)) return group;
if (featureGroups.length === 0) return config;
const eventsIndex = group.children.findIndex(
(child) => child.label === 'common.routes.events',
);
// Insert before the last group (Administration)
const routes = [...config.routes];
const adminIndex = routes.length - 1;
routes.splice(adminIndex, 0, ...featureGroups);
if (eventsIndex === -1) return group;
const newChildren = [...group.children];
newChildren.splice(eventsIndex + 1, 0, ...featureEntries);
return { ...group, children: newChildren };
}),
};
return { ...config, routes };
}
async function SidebarLayout({

View File

@@ -3,16 +3,16 @@ import { PageBody } from '@kit/ui/page';
export default function AccountLoading() {
return (
<PageBody>
<div className="flex flex-col gap-6 animate-pulse">
<div className="h-8 w-48 rounded-md bg-muted" />
<div className="flex animate-pulse flex-col gap-6">
<div className="bg-muted h-8 w-48 rounded-md" />
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="h-24 rounded-lg bg-muted" />
<div key={i} className="bg-muted h-24 rounded-lg" />
))}
</div>
<div className="space-y-2">
{[1, 2, 3, 4, 5, 6].map((i) => (
<div key={i} className="h-12 w-full rounded bg-muted" />
<div key={i} className="bg-muted h-12 w-full rounded" />
))}
</div>
</div>

View File

@@ -86,7 +86,9 @@ export default async function ProtocolDetailPage({ params }: PageProps) {
{MEETING_TYPE_LABELS[protocol.status] ?? protocol.status}
</Badge>
{protocol.status === 'final' ? (
<Badge variant="default">{t('pages.statusPublished')}</Badge>
<Badge variant="default">
{t('pages.statusPublished')}
</Badge>
) : (
<Badge variant="outline">{t('pages.statusDraft')}</Badge>
)}

View File

@@ -1,7 +1,8 @@
import { getTranslations } from 'next-intl/server';
import { createMemberManagementApi } from '@kit/member-management/api';
import { EditMemberForm } from '@kit/member-management/components';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';

View File

@@ -1,7 +1,8 @@
import { getTranslations } from 'next-intl/server';
import { createModuleBuilderApi } from '@kit/module-builder/api';
import { ModuleForm } from '@kit/module-builder/components';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';

View File

@@ -1,6 +1,7 @@
import { getTranslations } from 'next-intl/server';
import { createModuleBuilderApi } from '@kit/module-builder/api';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { getTranslations } from 'next-intl/server';
import { CmsPageShell } from '~/components/cms-page-shell';

View File

@@ -1,7 +1,8 @@
import { getTranslations } from 'next-intl/server';
import { createModuleBuilderApi } from '@kit/module-builder/api';
import { ModuleForm } from '@kit/module-builder/components';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';

View File

@@ -171,7 +171,11 @@ export default async function NewsletterDetailPage({ params }: PageProps) {
'secondary'
}
>
{t(NEWSLETTER_RECIPIENT_STATUS_LABEL_KEYS[rStatus] ?? rStatus)}
{t(
NEWSLETTER_RECIPIENT_STATUS_LABEL_KEYS[
rStatus
] ?? rStatus,
)}
</Badge>
</td>
</tr>

View File

@@ -204,7 +204,10 @@ export default async function NewsletterPage({
'secondary'
}
>
{t(NEWSLETTER_STATUS_LABEL_KEYS[String(nl.status)] ?? String(nl.status))}
{t(
NEWSLETTER_STATUS_LABEL_KEYS[String(nl.status)] ??
String(nl.status),
)}
</Badge>
</td>
<td className="p-3 text-right">
@@ -241,7 +244,12 @@ export default async function NewsletterPage({
</Link>
</Button>
) : (
<Button variant="outline" size="sm" disabled aria-label={t('common.previous')}>
<Button
variant="outline"
size="sm"
disabled
aria-label={t('common.previous')}
>
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
</Button>
)}
@@ -256,11 +264,19 @@ export default async function NewsletterPage({
href={`/home/${account}/newsletter${buildQuery(queryBase, { page: safePage + 1 })}`}
aria-label={t('common.next')}
>
<ChevronRight className="h-4 w-4" aria-hidden="true" />
<ChevronRight
className="h-4 w-4"
aria-hidden="true"
/>
</Link>
</Button>
) : (
<Button variant="outline" size="sm" disabled aria-label={t('common.next')}>
<Button
variant="outline"
size="sm"
disabled
aria-label={t('common.next')}
>
<ChevronRight className="h-4 w-4" aria-hidden="true" />
</Button>
)}

View File

@@ -1,6 +1,7 @@
import { getTranslations } from 'next-intl/server';
import { CreatePageForm } from '@kit/site-builder/components';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';

View File

@@ -166,10 +166,7 @@ export default async function SiteBuilderDashboard({ params }: Props) {
</thead>
<tbody>
{(pages as SitePage[]).map((page) => (
<tr
key={page.id}
className="hover:bg-muted/30 border-b"
>
<tr key={page.id} className="hover:bg-muted/30 border-b">
<td className="p-3 font-medium">{page.title}</td>
<td className="text-muted-foreground p-3 font-mono text-xs">
/{page.slug}

View File

@@ -1,6 +1,7 @@
import { getTranslations } from 'next-intl/server';
import { CreatePostForm } from '@kit/site-builder/components';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { getTranslations } from 'next-intl/server';
import { AccountNotFound } from '~/components/account-not-found';
import { CmsPageShell } from '~/components/cms-page-shell';

View File

@@ -77,10 +77,7 @@ export default async function PostsManagerPage({ params }: Props) {
</thead>
<tbody>
{(posts as SitePost[]).map((post) => (
<tr
key={post.id}
className="hover:bg-muted/30 border-b"
>
<tr key={post.id} className="hover:bg-muted/30 border-b">
<td className="p-3 font-medium">{post.title}</td>
<td className="p-3">
<Badge

View File

@@ -5,7 +5,6 @@ import { useTransition } from 'react';
import { useRouter } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { toast } from '@kit/ui/sonner';
import {
AlertDialog,
@@ -19,6 +18,7 @@ import {
AlertDialogTrigger,
} from '@kit/ui/alert-dialog';
import { Button } from '@kit/ui/button';
import { toast } from '@kit/ui/sonner';
interface PublishToggleButtonProps {
pageId: string;
@@ -38,11 +38,14 @@ export function PublishToggleButton({
const handleToggle = () => {
startTransition(async () => {
try {
const response = await fetch(`/api/site-builder/pages/${pageId}/publish`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ accountId, isPublished: !isPublished }),
});
const response = await fetch(
`/api/site-builder/pages/${pageId}/publish`,
{
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ accountId, isPublished: !isPublished }),
},
);
if (!response.ok) {
toast.error(t('pages.toggleError'));

View File

@@ -230,9 +230,7 @@ export default function SettingsContent({
return (
<div className="space-y-6">
<div>
<p className="text-muted-foreground">
{t('settings.subtitle')}
</p>
<p className="text-muted-foreground">{t('settings.subtitle')}</p>
</div>
<SettingsSection