added missing translations

This commit is contained in:
Codex Agent
2025-11-26 14:41:39 +01:00
parent ff168834b4
commit ecac9507a4
35 changed files with 2812 additions and 256 deletions

View File

@@ -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">