added missing translations
This commit is contained in:
@@ -26,7 +26,7 @@ import {
|
||||
TenantEvent,
|
||||
} from '../api';
|
||||
import { isAuthError } from '../auth/tokens';
|
||||
import { isApiError } from '../lib/apiError';
|
||||
import { getApiErrorMessage, isApiError } from '../lib/apiError';
|
||||
import { buildLimitWarnings } from '../lib/limitWarnings';
|
||||
import { ADMIN_BILLING_PATH, ADMIN_EVENT_VIEW_PATH, ADMIN_EVENTS_PATH } from '../constants';
|
||||
|
||||
@@ -68,7 +68,8 @@ export default function EventFormPage() {
|
||||
const isEdit = Boolean(slugParam);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { t: tCommon } = useTranslation('common', { keyPrefix: 'errors' });
|
||||
const { t: tErrors } = useTranslation('common', { keyPrefix: 'errors' });
|
||||
const { t: tForm } = useTranslation('management', { keyPrefix: 'eventForm' });
|
||||
const { t: tLimits } = useTranslation('common', { keyPrefix: 'limits' });
|
||||
|
||||
const [form, setForm] = React.useState<EventFormState>({
|
||||
@@ -195,6 +196,21 @@ export default function EventFormPage() {
|
||||
slugSuffixRef.current = null;
|
||||
}, [isEdit, loadedEvent]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isEdit || !loadedEvent || !eventTypes || eventTypes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadedEvent.event_type_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
eventTypeId: prev.eventTypeId ?? eventTypes[0]!.id,
|
||||
}));
|
||||
}, [eventTypes, isEdit, loadedEvent]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isEdit || !eventLoadError) {
|
||||
return;
|
||||
@@ -266,7 +282,7 @@ export default function EventFormPage() {
|
||||
const trimmedName = form.name.trim();
|
||||
|
||||
if (!trimmedName) {
|
||||
setError('Bitte gib einen Eventnamen ein.');
|
||||
setError(tForm('errors.nameRequired', 'Bitte gib einen Eventnamen ein.'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -276,7 +292,7 @@ export default function EventFormPage() {
|
||||
}
|
||||
|
||||
if (!form.eventTypeId) {
|
||||
setError('Bitte wähle einen Event-Typ aus.');
|
||||
setError(tForm('errors.typeRequired', 'Bitte wähle einen Event-Typ aus.'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -297,9 +313,7 @@ export default function EventFormPage() {
|
||||
event_type_id: form.eventTypeId,
|
||||
event_date: form.date || undefined,
|
||||
status,
|
||||
...(shouldIncludePackage && packageIdForSubmit
|
||||
? { package_id: Number(packageIdForSubmit) }
|
||||
: {}),
|
||||
...(packageIdForSubmit ? { package_id: Number(packageIdForSubmit) } : {}),
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -324,25 +338,24 @@ export default function EventFormPage() {
|
||||
const limit = Number(err.meta?.limit ?? 0);
|
||||
const used = Number(err.meta?.used ?? 0);
|
||||
const remaining = Number(err.meta?.remaining ?? Math.max(0, limit - used));
|
||||
const detail = limit > 0
|
||||
? tCommon('eventLimitDetails', { used, limit, remaining })
|
||||
: '';
|
||||
setError(`${tCommon('eventLimit')}${detail ? `\n${detail}` : ''}`);
|
||||
const detail = limit > 0 ? tErrors('eventLimitDetails', { used, limit, remaining }) : '';
|
||||
setError(`${tErrors('eventLimit')}${detail ? `\n${detail}` : ''}`);
|
||||
setShowUpgradeHint(true);
|
||||
break;
|
||||
}
|
||||
case 'event_credits_exhausted': {
|
||||
setError(tCommon('creditsExhausted'));
|
||||
setError(tErrors('creditsExhausted'));
|
||||
setShowUpgradeHint(true);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setError(err.message || tCommon('generic'));
|
||||
const metaErrors = Array.isArray(err.meta?.errors) ? err.meta.errors.filter(Boolean).join('\n') : null;
|
||||
setError(metaErrors || err.message || tErrors('generic'));
|
||||
setShowUpgradeHint(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setError(tCommon('generic'));
|
||||
setError(getApiErrorMessage(err, tErrors('generic')));
|
||||
setShowUpgradeHint(false);
|
||||
}
|
||||
}
|
||||
@@ -439,19 +452,19 @@ export default function EventFormPage() {
|
||||
onClick={() => navigate(ADMIN_EVENTS_PATH)}
|
||||
className="border-pink-200 text-pink-600 hover:bg-pink-50"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" /> Zurück zur Liste
|
||||
<ArrowLeft className="h-4 w-4" /> {tForm('actions.backToList', 'Zurück zur Liste')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<AdminLayout
|
||||
title={isEdit ? 'Event bearbeiten' : 'Neues Event erstellen'}
|
||||
subtitle="Fülle die wichtigsten Angaben aus und teile dein Event mit Gästen."
|
||||
title={isEdit ? tForm('titles.edit', 'Event bearbeiten') : tForm('titles.create', 'Neues Event erstellen')}
|
||||
subtitle={tForm('subtitle', 'Fülle die wichtigsten Angaben aus und teile dein Event mit Gästen.')}
|
||||
actions={actions}
|
||||
>
|
||||
{error && (
|
||||
<Alert variant="destructive">
|
||||
<AlertTitle>Hinweis</AlertTitle>
|
||||
<AlertTitle>{tForm('errors.notice', 'Hinweis')}</AlertTitle>
|
||||
<AlertDescription className="flex flex-col gap-2">
|
||||
{error.split('\n').map((line, index) => (
|
||||
<span key={index}>{line}</span>
|
||||
@@ -459,7 +472,7 @@ export default function EventFormPage() {
|
||||
{showUpgradeHint && (
|
||||
<div>
|
||||
<Button size="sm" variant="outline" onClick={() => navigate(ADMIN_BILLING_PATH)}>
|
||||
{tCommon('goToBilling')}
|
||||
{tErrors('goToBilling', 'Zum Billing')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -490,10 +503,10 @@ export default function EventFormPage() {
|
||||
<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">
|
||||
<Sparkles className="h-5 w-5 text-pink-500" /> Eventdetails
|
||||
<Sparkles className="h-5 w-5 text-pink-500" /> {tForm('sections.details.title', 'Eventdetails')}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm text-slate-600">
|
||||
Name, URL und Datum bestimmen das Auftreten deines Events im Gästeportal.
|
||||
{tForm('sections.details.description', 'Name, URL und Datum bestimmen das Auftreten deines Events im Gästeportal.')}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -503,20 +516,20 @@ export default function EventFormPage() {
|
||||
<form className="space-y-6" onSubmit={handleSubmit}>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="space-y-2 sm:col-span-2">
|
||||
<Label htmlFor="event-name">Eventname</Label>
|
||||
<Label htmlFor="event-name">{tForm('fields.name.label', 'Eventname')}</Label>
|
||||
<Input
|
||||
id="event-name"
|
||||
placeholder="z. B. Sommerfest 2025"
|
||||
placeholder={tForm('fields.name.placeholder', 'z. B. Sommerfest 2025')}
|
||||
value={form.name}
|
||||
onChange={(e) => handleNameChange(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<p className="text-xs text-slate-500">
|
||||
Die Kennung und Event-URL werden automatisch aus dem Namen generiert.
|
||||
{tForm('fields.name.help', 'Die Kennung und Event-URL werden automatisch aus dem Namen generiert.')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="event-date">Datum</Label>
|
||||
<Label htmlFor="event-date">{tForm('fields.date.label', 'Datum')}</Label>
|
||||
<Input
|
||||
id="event-date"
|
||||
type="date"
|
||||
@@ -525,7 +538,7 @@ export default function EventFormPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="event-type">Event-Typ</Label>
|
||||
<Label htmlFor="event-type">{tForm('fields.type.label', 'Event-Typ')}</Label>
|
||||
<Select
|
||||
value={form.eventTypeId ? String(form.eventTypeId) : undefined}
|
||||
onValueChange={(value) => setForm((prev) => ({ ...prev, eventTypeId: Number(value) }))}
|
||||
@@ -533,7 +546,11 @@ export default function EventFormPage() {
|
||||
>
|
||||
<SelectTrigger id="event-type">
|
||||
<SelectValue
|
||||
placeholder={eventTypesLoading ? 'Event-Typ wird geladen…' : 'Event-Typ auswählen'}
|
||||
placeholder={
|
||||
eventTypesLoading
|
||||
? tForm('fields.type.loading', 'Event-Typ wird geladen…')
|
||||
: tForm('fields.type.placeholder', 'Event-Typ auswählen')
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -546,7 +563,7 @@ export default function EventFormPage() {
|
||||
</Select>
|
||||
{!eventTypesLoading && (!sortedEventTypes || sortedEventTypes.length === 0) ? (
|
||||
<p className="text-xs text-amber-600">
|
||||
Keine Event-Typen verfügbar. Bitte lege einen Typ im Adminbereich an.
|
||||
{tForm('fields.type.empty', 'Keine Event-Typen verfügbar. Bitte lege einen Typ im Adminbereich an.')}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -560,10 +577,10 @@ export default function EventFormPage() {
|
||||
/>
|
||||
<div>
|
||||
<Label htmlFor="event-published" className="text-sm font-medium text-slate-800">
|
||||
Event sofort veroeffentlichen
|
||||
{tForm('fields.publish.label', 'Event sofort veröffentlichen')}
|
||||
</Label>
|
||||
<p className="text-xs text-slate-600">
|
||||
Aktiviere diese Option, wenn Gäste das Event direkt sehen sollen. Du kannst den Status später ändern.
|
||||
{tForm('fields.publish.help', 'Aktiviere diese Option, wenn Gäste das Event direkt sehen sollen. Du kannst den Status später ändern.')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -576,16 +593,16 @@ export default function EventFormPage() {
|
||||
>
|
||||
{saving ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" /> Speichert
|
||||
<Loader2 className="h-4 w-4 animate-spin" /> {tForm('actions.saving', 'Speichert')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="h-4 w-4" /> Speichern
|
||||
<Save className="h-4 w-4" /> {tForm('actions.save', 'Speichern')}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button variant="ghost" type="button" onClick={() => navigate(-1)}>
|
||||
Abbrechen
|
||||
{tForm('actions.cancel', 'Abbrechen')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="sm:col-span-2 mt-6">
|
||||
|
||||
Reference in New Issue
Block a user