Limit-Status im Upload-Flow anzeigen (Warnbanner + Sperrzustände).
Upload-Fehlercodes auswerten und freundliche Dialoge zeigen.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ArrowLeft, Copy, Download, Loader2, Printer, QrCode, RefreshCw, Share2, X } from 'lucide-react';
|
||||
import { AlertTriangle, ArrowLeft, Copy, Download, Loader2, Printer, QrCode, RefreshCw, Share2, X } from 'lucide-react';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
ADMIN_EVENT_TOOLKIT_PATH,
|
||||
ADMIN_EVENT_PHOTOS_PATH,
|
||||
} from '../constants';
|
||||
import { buildLimitWarnings } from '../lib/limitWarnings';
|
||||
import { InviteLayoutCustomizerPanel, QrLayoutCustomization } from './components/InviteLayoutCustomizerPanel';
|
||||
import { DesignerCanvas } from './components/invite-layout/DesignerCanvas';
|
||||
import {
|
||||
@@ -159,6 +160,7 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
const { slug } = useParams<{ slug?: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('management');
|
||||
const { t: tLimits } = useTranslation('common', { keyPrefix: 'limits' });
|
||||
|
||||
const [state, setState] = React.useState<PageState>({ event: null, invites: [], loading: true, error: null });
|
||||
const [creatingInvite, setCreatingInvite] = React.useState(false);
|
||||
@@ -711,12 +713,46 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
</div>
|
||||
);
|
||||
|
||||
const limitWarnings = React.useMemo(
|
||||
() => buildLimitWarnings(state.event?.limits, tLimits),
|
||||
[state.event?.limits, tLimits]
|
||||
);
|
||||
|
||||
const limitScopeLabels = React.useMemo(
|
||||
() => ({
|
||||
photos: tLimits('photosTitle'),
|
||||
guests: tLimits('guestsTitle'),
|
||||
gallery: tLimits('galleryTitle'),
|
||||
}),
|
||||
[tLimits]
|
||||
);
|
||||
|
||||
return (
|
||||
<AdminLayout
|
||||
title={eventName}
|
||||
subtitle={t('invites.subtitle', 'Manage QR-Einladungen, Drucklayouts und Branding für deine Gäste.')}
|
||||
actions={actions}
|
||||
>
|
||||
{limitWarnings.length > 0 && (
|
||||
<div className="mb-6 space-y-2">
|
||||
{limitWarnings.map((warning) => (
|
||||
<Alert
|
||||
key={warning.id}
|
||||
variant={warning.tone === 'danger' ? 'destructive' : 'default'}
|
||||
className={warning.tone === 'warning' ? 'border-amber-400/50 bg-amber-50 text-amber-900' : undefined}
|
||||
>
|
||||
<AlertTitle className="flex items-center gap-2 text-sm font-semibold">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
{limitScopeLabels[warning.scope]}
|
||||
</AlertTitle>
|
||||
<AlertDescription className="text-sm">
|
||||
{warning.message}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Tabs value={activeTab} onValueChange={handleTabChange} className="space-y-6">
|
||||
<TabsList className="grid w-full max-w-2xl grid-cols-3 gap-1 rounded-full border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface-strong)] p-1 text-sm">
|
||||
<TabsTrigger value="layout" className="rounded-full px-4 py-1.5 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground">
|
||||
@@ -1075,12 +1111,17 @@ export default function EventInvitesPage(): JSX.Element {
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleCreateInvite}
|
||||
disabled={creatingInvite}
|
||||
disabled={creatingInvite || state.event?.limits?.can_add_guests === false}
|
||||
className="bg-primary text-primary-foreground shadow-lg shadow-primary/20 hover:bg-primary/90"
|
||||
>
|
||||
{creatingInvite ? <Loader2 className="mr-1 h-4 w-4 animate-spin" /> : <Share2 className="mr-1 h-4 w-4" />}
|
||||
{t('invites.actions.create', 'Neue Einladung erstellen')}
|
||||
</Button>
|
||||
{!state.loading && state.event?.limits?.can_add_guests === false && (
|
||||
<p className="w-full text-xs text-amber-600">
|
||||
{tLimits('guestsBlocked')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
|
||||
Reference in New Issue
Block a user