Limit-Status im Upload-Flow anzeigen (Warnbanner + Sperrzustände).

Upload-Fehlercodes auswerten und freundliche Dialoge zeigen.
This commit is contained in:
Codex Agent
2025-11-01 19:50:17 +01:00
parent 2c14493604
commit 79b209de9a
55 changed files with 3348 additions and 462 deletions

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { ArrowLeft, Loader2, Save, Sparkles } from 'lucide-react';
import { AlertTriangle, ArrowLeft, Loader2, Save, Sparkles } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
@@ -25,6 +25,7 @@ import {
} from '../api';
import { isAuthError } from '../auth/tokens';
import { isApiError } from '../lib/apiError';
import { buildLimitWarnings } from '../lib/limitWarnings';
import { ADMIN_BILLING_PATH, ADMIN_EVENT_VIEW_PATH, ADMIN_EVENTS_PATH } from '../constants';
interface EventFormState {
@@ -66,6 +67,7 @@ export default function EventFormPage() {
const navigate = useNavigate();
const { t: tCommon } = useTranslation('common', { keyPrefix: 'errors' });
const { t: tLimits } = useTranslation('common', { keyPrefix: 'limits' });
const [form, setForm] = React.useState<EventFormState>({
name: '',
@@ -193,6 +195,20 @@ export default function EventFormPage() {
const loading = isEdit ? eventLoading : false;
const limitWarnings = React.useMemo(() => {
if (!isEdit) {
return [];
}
return buildLimitWarnings(loadedEvent?.limits, tLimits);
}, [isEdit, loadedEvent?.limits, tLimits]);
const limitScopeLabels = React.useMemo(() => ({
photos: tLimits('photosTitle'),
guests: tLimits('guestsTitle'),
gallery: tLimits('galleryTitle'),
}), [tLimits]);
function ensureSlugSuffix(): string {
if (!slugSuffixRef.current) {
slugSuffixRef.current = Math.random().toString(36).slice(2, 7);
@@ -394,11 +410,11 @@ export default function EventFormPage() {
actions={actions}
>
{error && (
<Alert variant="destructive">
<AlertTitle>Hinweis</AlertTitle>
<AlertDescription className="flex flex-col gap-2">
{error.split('\n').map((line, index) => (
<span key={index}>{line}</span>
<Alert variant="destructive">
<AlertTitle>Hinweis</AlertTitle>
<AlertDescription className="flex flex-col gap-2">
{error.split('\n').map((line, index) => (
<span key={index}>{line}</span>
))}
{showUpgradeHint && (
<div>
@@ -411,6 +427,26 @@ export default function EventFormPage() {
</Alert>
)}
{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>
)}
<Card className="border-0 bg-white/85 shadow-xl shadow-fuchsia-100/60">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-xl text-slate-900">