From 4d31eb4d42c765b1af4208aa7ceb60a41efcc723 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Tue, 25 Nov 2025 15:50:34 +0100 Subject: [PATCH] event admin verfeinert und UI reduziert. --- resources/js/admin/components/AdminLayout.tsx | 4 +- .../dashboard/DashboardEventFocusCard.tsx | 39 +- resources/js/admin/pages/DashboardPage.tsx | 104 +++--- resources/js/admin/pages/EventDetailPage.tsx | 115 +++--- resources/js/admin/pages/EventInvitesPage.tsx | 6 +- .../InviteLayoutCustomizerPanel.tsx | 337 ++++++++---------- 6 files changed, 245 insertions(+), 360 deletions(-) diff --git a/resources/js/admin/components/AdminLayout.tsx b/resources/js/admin/components/AdminLayout.tsx index 1ccc162..d5f2008 100644 --- a/resources/js/admin/components/AdminLayout.tsx +++ b/resources/js/admin/components/AdminLayout.tsx @@ -169,7 +169,7 @@ export function AdminLayout({ title, subtitle, actions, children, disableCommand
-
+

{t('app.brand')}

@@ -177,7 +177,7 @@ export function AdminLayout({ title, subtitle, actions, children, disableCommand {subtitle ?

{subtitle}

: null}
-
+
{disableCommandShelf ? : null} {actions} diff --git a/resources/js/admin/components/dashboard/DashboardEventFocusCard.tsx b/resources/js/admin/components/dashboard/DashboardEventFocusCard.tsx index bdb7235..18910dc 100644 --- a/resources/js/admin/components/dashboard/DashboardEventFocusCard.tsx +++ b/resources/js/admin/components/dashboard/DashboardEventFocusCard.tsx @@ -106,6 +106,7 @@ export function DashboardEventFocusCard({ description: t('actions.photosHint', 'Neueste Uploads ansehen und verstecken.'), icon: Camera, handler: onOpenPhotos, + disabled: !isLive, }, { key: 'invites', @@ -130,8 +131,6 @@ export function DashboardEventFocusCard({ }, ]; - const latestUploads = summary?.new_photos ?? 0; - return (
@@ -172,11 +171,11 @@ export function DashboardEventFocusCard({
-
+
{overviewStats.map((stat) => ( -
-

{stat.label}

-

{stat.value}

+
+

{stat.label}

+

{stat.value}

))}
@@ -187,7 +186,8 @@ export function DashboardEventFocusCard({ key={action.key} type="button" onClick={action.handler} - className="flex items-start gap-3 rounded-2xl border border-slate-200 bg-white/90 p-4 text-left transition hover:border-rose-200 hover:bg-rose-50 dark:border-white/10 dark:bg-white/5" + disabled={action.disabled} + className="flex items-start gap-3 rounded-2xl border border-slate-200 bg-white/90 p-4 text-left transition hover:border-rose-200 hover:bg-rose-50 disabled:cursor-not-allowed disabled:opacity-60 dark:border-white/10 dark:bg-white/5" >
@@ -197,31 +197,6 @@ export function DashboardEventFocusCard({ ))}
- -
-
-

{t('latestUploads.title', 'Neueste Uploads')}

-

{latestUploads}

-

{t('latestUploads.hint', 'Gerade eingetroffen – prüfe sie schnell.')}

- -
-
-

{t('invitesCard.title', 'Galerie & Einladungen')}

-

- {t('invitesCard.description', 'Kopiere den Gästelink oder exportiere QR-Karten.')} -

-
- - -
-
-
diff --git a/resources/js/admin/pages/DashboardPage.tsx b/resources/js/admin/pages/DashboardPage.tsx index bea75e4..0c6b13e 100644 --- a/resources/js/admin/pages/DashboardPage.tsx +++ b/resources/js/admin/pages/DashboardPage.tsx @@ -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(() => 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: , - }, - { - key: 'newPhotos', - label: translate('overview.stats.newPhotos', 'Neueste Uploads'), - value: summary?.new_photos ?? 0, - icon: , - }, - { - key: 'taskProgress', - label: translate('overview.stats.taskProgress'), - value: `${Math.round(summary?.task_progress ?? 0)}%`, - icon: , - }, - activePackage - ? { - key: 'package', - label: translate('overview.stats.activePackage', 'Aktives Paket'), - value: activePackage.package_name, - icon: , - } - : 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(() => { const steps: ChecklistStep[] = [ { @@ -577,6 +545,34 @@ export default function DashboardPage() { ) : ( <>
+ {eventOptions.length > 1 ? ( + + + + + ) : null} - - - - {activePackage?.package_name ?? translate('overview.noPackage')} - - )} - /> - -
diff --git a/resources/js/admin/pages/EventDetailPage.tsx b/resources/js/admin/pages/EventDetailPage.tsx index 668be1e..aed1690 100644 --- a/resources/js/admin/pages/EventDetailPage.tsx +++ b/resources/js/admin/pages/EventDetailPage.tsx @@ -1,6 +1,6 @@ // @ts-nocheck import React from 'react'; -import { useNavigate, useParams, useSearchParams, useLocation } from 'react-router-dom'; +import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { ArrowLeft, @@ -19,6 +19,7 @@ import { ShoppingCart, Menu, Users, + AlertTriangle, } from 'lucide-react'; import toast from 'react-hot-toast'; @@ -85,7 +86,6 @@ export default function EventDetailPage() { const { slug: slugParam } = useParams<{ slug?: string }>(); const [searchParams] = useSearchParams(); const navigate = useNavigate(); - const location = useLocation(); const { t } = useTranslation('management'); const { t: tCommon } = useTranslation('common'); @@ -427,7 +427,7 @@ const shownWarningToasts = React.useRef>(new Set());
-
+

{t('events.workspace.hero.badge', 'Event')}

@@ -435,6 +435,24 @@ const shownWarningToasts = React.useRef>(new Set());

{t('events.workspace.hero.description', 'Konzentriere dich auf Aufgaben, Moderation und Einladungen für dieses Event.')}

+
+ + + {getStatusLabel(event, t)} + + + + {formatDate(event.event_date)} + + + + {t('events.workspace.hero.liveBadge', 'Live?')} {event.is_active ? t('events.workspace.activeYes', 'Ja') : t('events.workspace.activeNo', 'Nein')} + + + + {resolveEventType(event)} + +
-
navigate(ADMIN_EVENT_TASKS_PATH(event.slug))} /> void }) { - const { t } = useTranslation('management'); - - const statusLabel = getStatusLabel(event, t); - - return ( - - -
- } label={t('events.workspace.fields.status', 'Status')} value={statusLabel} /> - } label={t('events.workspace.fields.active', 'Aktiv für Gäste')} value={event.is_active ? t('events.workspace.activeYes', 'Ja') : t('events.workspace.activeNo', 'Nein')} /> - } label={t('events.workspace.fields.date', 'Eventdatum')} value={formatDate(event.event_date)} /> - } label={t('events.workspace.fields.eventType', 'Event-Typ')} value={resolveEventType(event)} /> - - {stats && ( -
-

{t('events.workspace.fields.insights', 'Letzte Aktivität')}

-

- {t('events.workspace.fields.uploadsTotal', { - defaultValue: '{{count}} Uploads gesamt', - count: stats.uploads_total ?? stats.total ?? 0, - })} - {' · '} - {t('events.workspace.fields.uploadsToday', { - defaultValue: '{{count}} Uploads (24h)', - count: stats.uploads_24h ?? stats.recent_uploads ?? 0, - })} -

-

- {t('events.workspace.fields.likesTotal', { - defaultValue: '{{count}} Likes vergeben', - count: stats.likes_total ?? stats.likes ?? 0, - })} -

-
- )} - -
-
- ); -} - function QuickActionsMenu({ slug, navigate }: { slug: string; navigate: ReturnType }) { const { t } = useTranslation('management'); @@ -577,7 +554,8 @@ function QuickActionsMenu({ slug, navigate }: { slug: string; navigate: ReturnTy