event admin verfeinert und UI reduziert.
This commit is contained in:
@@ -3,11 +3,10 @@ import React from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
CalendarDays,
|
||||
Camera,
|
||||
AlertTriangle,
|
||||
Sparkles,
|
||||
Users,
|
||||
CalendarDays,
|
||||
Plus,
|
||||
Settings,
|
||||
QrCode,
|
||||
@@ -20,11 +19,11 @@ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import {
|
||||
TenantOnboardingChecklistCard,
|
||||
SectionCard,
|
||||
SectionHeader,
|
||||
StatCarousel,
|
||||
ActionGrid,
|
||||
} from '../components/tenant';
|
||||
import type { ChecklistStep } from '../components/tenant';
|
||||
@@ -83,7 +82,7 @@ export default function DashboardPage() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { user } = useAuth();
|
||||
const { events: ctxEvents, activeEvent: ctxActiveEvent } = useEventContext();
|
||||
const { events: ctxEvents, activeEvent: ctxActiveEvent, selectEvent } = useEventContext();
|
||||
const { progress, markStep } = useOnboardingProgress();
|
||||
const { t, i18n } = useTranslation('dashboard', { keyPrefix: 'dashboard' });
|
||||
const { t: tc } = useTranslation('common');
|
||||
@@ -226,11 +225,19 @@ export default function DashboardPage() {
|
||||
return activePackage.remaining_events > 0;
|
||||
}, [activePackage]);
|
||||
|
||||
const upcomingEvents = getUpcomingEvents(ctxEvents.length ? ctxEvents : events);
|
||||
const publishedEvents = (ctxEvents.length ? ctxEvents : events).filter((event) => event.status === 'published');
|
||||
const primaryEvent = ctxActiveEvent ?? (ctxEvents[0] ?? events[0] ?? null);
|
||||
const eventOptions = ctxEvents.length ? ctxEvents : events;
|
||||
const [selectedSlug, setSelectedSlug] = React.useState<string | null>(() => ctxActiveEvent?.slug ?? eventOptions[0]?.slug ?? null);
|
||||
React.useEffect(() => {
|
||||
setSelectedSlug(ctxActiveEvent?.slug ?? eventOptions[0]?.slug ?? null);
|
||||
}, [ctxActiveEvent?.slug, eventOptions]);
|
||||
const upcomingEvents = getUpcomingEvents(eventOptions);
|
||||
const publishedEvents = eventOptions.filter((event) => event.status === 'published');
|
||||
const primaryEvent = React.useMemo(
|
||||
() => eventOptions.find((event) => event.slug === selectedSlug) ?? eventOptions[0] ?? null,
|
||||
[eventOptions, selectedSlug],
|
||||
);
|
||||
const primaryEventName = primaryEvent ? resolveEventName(primaryEvent.name, primaryEvent.slug) : null;
|
||||
const singleEvent = ctxEvents.length === 1 ? ctxEvents[0] : (events.length === 1 ? events[0] : null);
|
||||
const singleEvent = eventOptions.length === 1 ? eventOptions[0] : null;
|
||||
const singleEventName = singleEvent ? resolveEventName(singleEvent.name, singleEvent.slug) : null;
|
||||
const singleEventDateLabel = singleEvent?.event_date ? formatDate(singleEvent.event_date, dateLocale) : null;
|
||||
const primaryEventLimits = primaryEvent?.limits ?? null;
|
||||
@@ -305,45 +312,6 @@ export default function DashboardPage() {
|
||||
return now >= eventStart && now <= eventStart + windowLengthMs;
|
||||
});
|
||||
}, [events]);
|
||||
const statItems = React.useMemo(
|
||||
() => ([
|
||||
{
|
||||
key: 'activeEvents',
|
||||
label: translate('overview.stats.activeEvents'),
|
||||
value: summary?.active_events ?? publishedEvents.length,
|
||||
hint: translate('overview.stats.publishedHint', { count: publishedEvents.length }),
|
||||
icon: <CalendarDays className="h-4 w-4" />,
|
||||
},
|
||||
{
|
||||
key: 'newPhotos',
|
||||
label: translate('overview.stats.newPhotos', 'Neueste Uploads'),
|
||||
value: summary?.new_photos ?? 0,
|
||||
icon: <Camera className="h-4 w-4" />,
|
||||
},
|
||||
{
|
||||
key: 'taskProgress',
|
||||
label: translate('overview.stats.taskProgress'),
|
||||
value: `${Math.round(summary?.task_progress ?? 0)}%`,
|
||||
icon: <Users className="h-4 w-4" />,
|
||||
},
|
||||
activePackage
|
||||
? {
|
||||
key: 'package',
|
||||
label: translate('overview.stats.activePackage', 'Aktives Paket'),
|
||||
value: activePackage.package_name,
|
||||
icon: <PackageIcon className="h-4 w-4" />,
|
||||
}
|
||||
: null,
|
||||
].filter(Boolean) as {
|
||||
key: string;
|
||||
label: string;
|
||||
value: string | number;
|
||||
hint?: string;
|
||||
icon?: React.ReactNode;
|
||||
}[]),
|
||||
[summary, publishedEvents.length, translate, activePackage],
|
||||
);
|
||||
|
||||
const onboardingChecklist = React.useMemo<ChecklistStep[]>(() => {
|
||||
const steps: ChecklistStep[] = [
|
||||
{
|
||||
@@ -577,6 +545,34 @@ export default function DashboardPage() {
|
||||
) : (
|
||||
<>
|
||||
<div id="overview" className="space-y-6 scroll-mt-32">
|
||||
{eventOptions.length > 1 ? (
|
||||
<SectionCard className="space-y-3">
|
||||
<SectionHeader
|
||||
eyebrow={translate('overview.eventSwitcherEyebrow', 'Events')}
|
||||
title={translate('overview.eventSwitcherTitle', 'Event auswählen')}
|
||||
description={translate('overview.eventSwitcherDescription', 'Wechsle das Event, für das das Dashboard Daten anzeigt.')}
|
||||
/>
|
||||
<Select
|
||||
value={selectedSlug ?? ''}
|
||||
onValueChange={(value) => {
|
||||
setSelectedSlug(value);
|
||||
selectEvent(value);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder={translate('overview.eventSwitcherPlaceholder', 'Event auswählen')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{eventOptions.map((event) => (
|
||||
<SelectItem key={event.slug} value={event.slug}>
|
||||
{resolveEventName(event.name, event.slug)}
|
||||
{event.event_date ? ` — ${formatDate(event.event_date, dateLocale) ?? ''}` : ''}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</SectionCard>
|
||||
) : null}
|
||||
<DashboardEventFocusCard
|
||||
event={primaryEvent}
|
||||
limitWarnings={limitWarnings}
|
||||
@@ -589,20 +585,6 @@ export default function DashboardPage() {
|
||||
onOpenTasks={focusActions.openTasks}
|
||||
onOpenPhotobooth={focusActions.openPhotobooth}
|
||||
/>
|
||||
|
||||
<SectionCard className="space-y-3">
|
||||
<SectionHeader
|
||||
eyebrow={translate('overview.title')}
|
||||
title={translate('overview.title')}
|
||||
description={translate('overview.description')}
|
||||
endSlot={(
|
||||
<Badge className="bg-brand-rose-soft text-brand-rose">
|
||||
{activePackage?.package_name ?? translate('overview.noPackage')}
|
||||
</Badge>
|
||||
)}
|
||||
/>
|
||||
<StatCarousel items={statItems} />
|
||||
</SectionCard>
|
||||
</div>
|
||||
|
||||
<div id="live" className="space-y-6 scroll-mt-32">
|
||||
|
||||
Reference in New Issue
Block a user