added missing translations
This commit is contained in:
@@ -36,7 +36,8 @@ import { filterEmotionsByEventType } from '../lib/emotions';
|
||||
import { buildEventTabs } from '../lib/eventTabs';
|
||||
|
||||
export default function EventTasksPage() {
|
||||
const { t } = useTranslation(['management', 'dashboard']);
|
||||
const { t } = useTranslation('management', { keyPrefix: 'eventTasks' });
|
||||
const { t: tDashboard } = useTranslation('dashboard');
|
||||
const params = useParams<{ slug?: string }>();
|
||||
const [searchParams] = useSearchParams();
|
||||
const slug = params.slug ?? searchParams.get('slug') ?? null;
|
||||
@@ -67,7 +68,7 @@ export default function EventTasksPage() {
|
||||
setAvailableTasks((prev) => prev.filter((task) => !assignedIds.has(task.id)));
|
||||
} catch (err) {
|
||||
if (!isAuthError(err)) {
|
||||
setError(t('management.tasks.errors.assign', 'Tasks konnten nicht geladen werden.'));
|
||||
setError(t('errors.assign', 'Tasks konnten nicht geladen werden.'));
|
||||
}
|
||||
}
|
||||
}, [t]);
|
||||
@@ -88,7 +89,7 @@ export default function EventTasksPage() {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!slug) {
|
||||
setError(t('management.tasks.errors.missingSlug', 'Kein Event-Slug angegeben.'));
|
||||
setError(t('errors.missingSlug', 'Kein Event-Slug angegeben.'));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -120,7 +121,7 @@ export default function EventTasksPage() {
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
if (!isAuthError(err)) {
|
||||
setError(t('management.tasks.errors.load', 'Event-Tasks konnten nicht geladen werden.'));
|
||||
setError(t('errors.load', 'Event-Tasks konnten nicht geladen werden.'));
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
@@ -146,7 +147,7 @@ export default function EventTasksPage() {
|
||||
setSelected([]);
|
||||
} catch (err) {
|
||||
if (!isAuthError(err)) {
|
||||
setError(t('management.tasks.errors.assign', 'Tasks konnten nicht zugewiesen werden.'));
|
||||
setError(t('errors.assign', 'Tasks konnten nicht zugewiesen werden.'));
|
||||
}
|
||||
} finally {
|
||||
setSaving(false);
|
||||
@@ -178,14 +179,13 @@ export default function EventTasksPage() {
|
||||
}, [event, assignedTasks.length, t]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!event?.event_type?.slug) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setCollectionsLoading(true);
|
||||
setCollectionsError(null);
|
||||
getTaskCollections({ per_page: 6, event_type: event.event_type.slug })
|
||||
const eventTypeSlug = event?.event_type?.slug ?? null;
|
||||
const query = eventTypeSlug ? { per_page: 6, event_type: eventTypeSlug } : { per_page: 6 };
|
||||
|
||||
getTaskCollections(query)
|
||||
.then((result) => {
|
||||
if (cancelled) return;
|
||||
setCollections(result.data);
|
||||
@@ -193,7 +193,7 @@ export default function EventTasksPage() {
|
||||
.catch((err) => {
|
||||
if (cancelled) return;
|
||||
if (!isAuthError(err)) {
|
||||
setCollectionsError(t('management.tasks.collections.error', 'Kollektionen konnten nicht geladen werden.'));
|
||||
setCollectionsError(t('collections.error', 'Kollektionen konnten nicht geladen werden.'));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -244,7 +244,7 @@ export default function EventTasksPage() {
|
||||
try {
|
||||
await importTaskCollection(collection.id, slug);
|
||||
toast.success(
|
||||
t('management.tasks.collections.imported', {
|
||||
t('collections.imported', {
|
||||
defaultValue: 'Mission Pack "{{name}}" importiert.',
|
||||
name: collection.name,
|
||||
}),
|
||||
@@ -252,16 +252,16 @@ export default function EventTasksPage() {
|
||||
await hydrateTasks(event);
|
||||
} catch (err) {
|
||||
if (!isAuthError(err)) {
|
||||
toast.error(t('management.tasks.collections.importFailed', 'Mission Pack konnte nicht importiert werden.'));
|
||||
toast.error(t('collections.importFailed', 'Mission Pack konnte nicht importiert werden.'));
|
||||
}
|
||||
} finally {
|
||||
setImportingCollectionId(null);
|
||||
}
|
||||
}, [event, hydrateTasks, slug, t]);
|
||||
|
||||
const isPhotoOnlyMode = React.useMemo(() => {
|
||||
const tasksEnabled = React.useMemo(() => {
|
||||
const mode = event?.engagement_mode ?? (event?.settings as any)?.engagement_mode;
|
||||
return mode === 'photo_only';
|
||||
return mode !== 'photo_only';
|
||||
}, [event?.engagement_mode, event?.settings]);
|
||||
|
||||
async function handleModeChange(checked: boolean) {
|
||||
@@ -271,7 +271,7 @@ export default function EventTasksPage() {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const nextMode = checked ? 'photo_only' : 'tasks';
|
||||
const nextMode = checked ? 'tasks' : 'photo_only';
|
||||
const updated = await updateEvent(slug, {
|
||||
settings: {
|
||||
...(event.settings ?? {}),
|
||||
@@ -292,8 +292,8 @@ export default function EventTasksPage() {
|
||||
if (!isAuthError(err)) {
|
||||
setError(
|
||||
checked
|
||||
? t('management.tasks.errors.photoOnlyEnable', 'Foto-Modus konnte nicht aktiviert werden.')
|
||||
: t('management.tasks.errors.photoOnlyDisable', 'Foto-Modus konnte nicht deaktiviert werden.'),
|
||||
? t('errors.photoOnlyEnable', 'Foto-Modus konnte nicht aktiviert werden.')
|
||||
: t('errors.photoOnlyDisable', 'Foto-Modus konnte nicht deaktiviert werden.'),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
@@ -304,21 +304,21 @@ export default function EventTasksPage() {
|
||||
const actions = (
|
||||
<Button variant="outline" onClick={() => navigate(ADMIN_EVENTS_PATH)} className="border-pink-200 text-pink-600">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
{t('management.tasks.actions.back', 'Zurück zur Übersicht')}
|
||||
{t('actions.back', 'Zurück zur Übersicht')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<AdminLayout
|
||||
title={t('management.tasks.title', 'Aufgaben & Missionen')}
|
||||
subtitle={t('management.tasks.subtitle', 'Stelle Mission Cards und Aufgaben für dieses Event zusammen.')}
|
||||
title={t('title', 'Aufgaben & Missionen')}
|
||||
subtitle={t('subtitle', 'Stelle Mission Cards und Aufgaben für dieses Event zusammen.')}
|
||||
actions={actions}
|
||||
tabs={eventTabs}
|
||||
currentTabKey="tasks"
|
||||
>
|
||||
{error && (
|
||||
<Alert variant="destructive">
|
||||
<AlertTitle>{t('dashboard.alerts.errorTitle', 'Fehler')}</AlertTitle>
|
||||
<AlertTitle>{tDashboard('alerts.errorTitle', 'Fehler')}</AlertTitle>
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
@@ -327,22 +327,22 @@ export default function EventTasksPage() {
|
||||
<TaskSkeleton />
|
||||
) : !event ? (
|
||||
<Alert variant="destructive">
|
||||
<AlertTitle>{t('management.tasks.alerts.notFoundTitle', 'Event nicht gefunden')}</AlertTitle>
|
||||
<AlertDescription>{t('management.tasks.alerts.notFoundDescription', 'Bitte kehre zur Eventliste zurück.')}</AlertDescription>
|
||||
<AlertTitle>{t('alerts.notFoundTitle', 'Event nicht gefunden')}</AlertTitle>
|
||||
<AlertDescription>{t('alerts.notFoundDescription', 'Bitte kehre zur Eventliste zurück.')}</AlertDescription>
|
||||
</Alert>
|
||||
) : (
|
||||
<>
|
||||
<Tabs value={tab} onValueChange={(value) => setTab(value as 'tasks' | 'packs')} className="space-y-6">
|
||||
<TabsList className="grid gap-2 rounded-2xl bg-slate-100/80 p-1 sm:grid-cols-2">
|
||||
<TabsTrigger value="tasks">{t('management.tasks.tabs.tasks', 'Aufgaben')}</TabsTrigger>
|
||||
<TabsTrigger value="packs">{t('management.tasks.tabs.packs', 'Mission Packs')}</TabsTrigger>
|
||||
<TabsTrigger value="tasks">{t('tabs.tasks', 'Aufgaben')}</TabsTrigger>
|
||||
<TabsTrigger value="packs">{t('tabs.packs', 'Mission Packs')}</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="tasks" className="space-y-6">
|
||||
<Card className="border-0 bg-white/85 shadow-xl shadow-pink-100/60">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl text-slate-900">{renderName(event.name, t)}</CardTitle>
|
||||
<CardDescription className="text-sm text-slate-600">
|
||||
{t('management.tasks.eventStatus', {
|
||||
{t('eventStatus', {
|
||||
status: statusLabels[event.status as keyof typeof statusLabels] ?? event.status,
|
||||
})}
|
||||
</CardDescription>
|
||||
@@ -350,52 +350,44 @@ export default function EventTasksPage() {
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-900">
|
||||
{t('management.tasks.modes.title', 'Aufgaben & Foto-Modus')}
|
||||
{t('modes.title', 'Aufgaben & Foto-Modus')}
|
||||
</p>
|
||||
<p className="text-xs text-slate-600">
|
||||
{isPhotoOnlyMode
|
||||
? t(
|
||||
'management.tasks.modes.photoOnlyHint',
|
||||
'Der Foto-Modus ist aktiv. Gäste können Fotos hochladen, sehen aber keine Aufgaben.',
|
||||
)
|
||||
: t(
|
||||
'management.tasks.modes.tasksHint',
|
||||
'Aufgaben sind aktiv. Gäste sehen Mission Cards in der App.',
|
||||
)}
|
||||
{tasksEnabled
|
||||
? t('modes.tasksHint', 'Aufgaben sind aktiv. Gäste sehen Mission Cards in der App.')
|
||||
: t('modes.photoOnlyHint', 'Der Foto-Modus ist aktiv. Gäste können Fotos hochladen, sehen aber keine Aufgaben.')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs uppercase tracking-wide text-slate-500">
|
||||
{isPhotoOnlyMode
|
||||
? t('management.tasks.modes.photoOnly', 'Foto-Modus')
|
||||
: t('management.tasks.modes.tasks', 'Aufgaben aktiv')}
|
||||
{tasksEnabled ? t('modes.tasks', 'Aufgaben aktiv') : t('modes.photoOnly', 'Foto-Modus')}
|
||||
</span>
|
||||
<Switch
|
||||
checked={isPhotoOnlyMode}
|
||||
checked={tasksEnabled}
|
||||
onCheckedChange={handleModeChange}
|
||||
disabled={modeSaving}
|
||||
aria-label={t('management.tasks.modes.switchLabel', 'Foto-Modus aktivieren')}
|
||||
aria-label={t('modes.switchLabel', 'Aufgaben aktivieren/deaktivieren')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{modeSaving ? (
|
||||
<div className="flex items-center gap-2 text-xs text-slate-500">
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
{t('management.tasks.modes.updating', 'Einstellung wird gespeichert ...')}
|
||||
{t('modes.updating', 'Einstellung wird gespeichert ...')}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="grid gap-3 text-xs sm:grid-cols-3">
|
||||
<SummaryPill
|
||||
label={t('management.tasks.summary.assigned', 'Zugeordnete Tasks')}
|
||||
label={t('summary.assigned', 'Zugeordnete Tasks')}
|
||||
value={assignedTasks.length}
|
||||
/>
|
||||
<SummaryPill
|
||||
label={t('management.tasks.summary.library', 'Bibliothek')}
|
||||
label={t('summary.library', 'Bibliothek')}
|
||||
value={availableTasks.length}
|
||||
/>
|
||||
<SummaryPill
|
||||
label={t('management.tasks.summary.mode', 'Aktiver Modus')}
|
||||
value={isPhotoOnlyMode ? t('management.tasks.summary.photoOnly', 'Nur Fotos') : t('management.tasks.summary.tasksMode', 'Mission Cards')}
|
||||
label={t('summary.mode', 'Aktiver Modus')}
|
||||
value={tasksEnabled ? t('summary.tasksMode', 'Mission Cards') : t('summary.photoOnly', 'Nur Fotos')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -403,14 +395,11 @@ export default function EventTasksPage() {
|
||||
<CardContent className="pb-0">
|
||||
<Alert variant="default" className="rounded-2xl border border-dashed border-emerald-200 bg-emerald-50/60 text-xs text-slate-700">
|
||||
<AlertTitle className="text-sm font-semibold text-slate-900">
|
||||
{t('management.tasks.library.hintTitle', 'Weitere Vorlagen in der Aufgaben-Bibliothek')}
|
||||
{t('library.hintTitle', 'Weitere Vorlagen in der Aufgaben-Bibliothek')}
|
||||
</AlertTitle>
|
||||
<AlertDescription className="mt-1 flex flex-wrap items-center gap-2">
|
||||
<span>
|
||||
{t(
|
||||
'management.tasks.library.hintCopy',
|
||||
'Lege eigene Aufgaben, Emotionen oder Mission Packs zentral an und nutze sie in mehreren Events.',
|
||||
)}
|
||||
{t('library.hintCopy', 'Lege eigene Aufgaben, Emotionen oder Mission Packs zentral an und nutze sie in mehreren Events.')}
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -419,7 +408,7 @@ export default function EventTasksPage() {
|
||||
className="mt-1 rounded-full border-emerald-300 text-emerald-700 hover:bg-emerald-100"
|
||||
onClick={() => navigate(buildEngagementTabPath('tasks'))}
|
||||
>
|
||||
{t('management.tasks.library.open', 'Aufgaben-Bibliothek öffnen')}
|
||||
{t('library.open', 'Aufgaben-Bibliothek öffnen')}
|
||||
</Button>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
@@ -429,14 +418,14 @@ export default function EventTasksPage() {
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<h3 className="flex items-center gap-2 text-sm font-semibold text-slate-900">
|
||||
<Sparkles className="h-4 w-4 text-pink-500" />
|
||||
{t('management.tasks.sections.assigned.title', 'Zugeordnete Tasks')}
|
||||
{t('sections.assigned.title', 'Zugeordnete Tasks')}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 rounded-full border border-slate-200 px-3 py-1">
|
||||
<Search className="h-4 w-4 text-slate-500" />
|
||||
<Input
|
||||
value={taskSearch}
|
||||
onChange={(event) => setTaskSearch(event.target.value)}
|
||||
placeholder={t('management.tasks.sections.assigned.search', 'Aufgaben suchen...')}
|
||||
placeholder={t('sections.assigned.search', 'Aufgaben suchen...')}
|
||||
className="h-8 border-0 bg-transparent text-sm focus-visible:ring-0"
|
||||
/>
|
||||
</div>
|
||||
@@ -446,8 +435,8 @@ export default function EventTasksPage() {
|
||||
<EmptyState
|
||||
message={
|
||||
taskSearch.trim()
|
||||
? t('management.tasks.sections.assigned.noResults', 'Keine Aufgaben zum Suchbegriff.')
|
||||
: t('management.tasks.sections.assigned.empty', 'Noch keine Tasks zugewiesen.')
|
||||
? t('sections.assigned.noResults', 'Keine Aufgaben zum Suchbegriff.')
|
||||
: t('sections.assigned.empty', 'Noch keine Tasks zugewiesen.')
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
@@ -462,11 +451,11 @@ export default function EventTasksPage() {
|
||||
<section className="space-y-3">
|
||||
<h3 className="flex items-center gap-2 text-sm font-semibold text-slate-900">
|
||||
<PlusCircle className="h-4 w-4 text-emerald-500" />
|
||||
{t('management.tasks.sections.library.title', 'Tasks aus Bibliothek hinzufügen')}
|
||||
{t('sections.library.title', 'Tasks aus Bibliothek hinzufügen')}
|
||||
</h3>
|
||||
<div className="space-y-2 rounded-2xl border border-emerald-100 bg-white/90 p-3 shadow-sm max-h-72 overflow-y-auto">
|
||||
{availableTasks.length === 0 ? (
|
||||
<EmptyState message={t('management.tasks.sections.library.empty', 'Keine Tasks in der Bibliothek gefunden.')} />
|
||||
<EmptyState message={t('sections.library.empty', 'Keine Tasks in der Bibliothek gefunden.')} />
|
||||
) : (
|
||||
availableTasks.map((task) => (
|
||||
<label key={task.id} className="flex items-start gap-3 rounded-xl border border-transparent p-2 transition hover:border-emerald-200">
|
||||
@@ -477,7 +466,7 @@ export default function EventTasksPage() {
|
||||
checked ? [...prev, task.id] : prev.filter((id) => id !== task.id)
|
||||
)
|
||||
}
|
||||
disabled={isPhotoOnlyMode}
|
||||
disabled={!tasksEnabled}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-900">{task.title}</p>
|
||||
@@ -489,9 +478,9 @@ export default function EventTasksPage() {
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => void handleAssign()}
|
||||
disabled={saving || selected.length === 0 || isPhotoOnlyMode}
|
||||
disabled={saving || selected.length === 0 || !tasksEnabled}
|
||||
>
|
||||
{saving ? <Loader2 className="h-4 w-4 animate-spin" /> : t('management.tasks.actions.assign', 'Ausgewählte Tasks zuweisen')}
|
||||
{saving ? <Loader2 className="h-4 w-4 animate-spin" /> : t('actions.assign', 'Ausgewählte Tasks zuweisen')}
|
||||
</Button>
|
||||
</section>
|
||||
</CardContent>
|
||||
@@ -577,7 +566,7 @@ function MissionPackGrid({
|
||||
importingId: number | null;
|
||||
onViewAll: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation('management');
|
||||
const { t } = useTranslation('management', { keyPrefix: 'eventTasks.collections' });
|
||||
|
||||
return (
|
||||
<Card className="border border-slate-200 bg-white/90">
|
||||
@@ -585,20 +574,20 @@ function MissionPackGrid({
|
||||
<div>
|
||||
<CardTitle className="flex items-center gap-2 text-base text-slate-900">
|
||||
<Layers className="h-5 w-5 text-pink-500" />
|
||||
{t('management.tasks.collections.title', 'Mission Packs')}
|
||||
{t('title', 'Mission Packs')}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-sm text-slate-600">
|
||||
{t('management.tasks.collections.subtitle', 'Importiere Aufgaben-Kollektionen, die zu deinem Event passen.')}
|
||||
{t('subtitle', 'Importiere Aufgaben-Kollektionen, die zu deinem Event passen.')}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button variant="outline" onClick={onViewAll}>
|
||||
{t('management.tasks.collections.viewAll', 'Alle Kollektionen ansehen')}
|
||||
{t('viewAll', 'Alle Kollektionen ansehen')}
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{error ? (
|
||||
<Alert variant="destructive">
|
||||
<AlertTitle>{t('management.tasks.collections.errorTitle', 'Kollektionen nicht verfügbar')}</AlertTitle>
|
||||
<AlertTitle>{t('errorTitle', 'Kollektionen nicht verfügbar')}</AlertTitle>
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
@@ -610,7 +599,7 @@ function MissionPackGrid({
|
||||
))}
|
||||
</div>
|
||||
) : collections.length === 0 ? (
|
||||
<EmptyState message={t('management.tasks.collections.empty', 'Keine empfohlenen Kollektionen gefunden.')} />
|
||||
<EmptyState message={t('empty', 'Keine empfohlenen Kollektionen gefunden.')} />
|
||||
) : (
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{collections.map((collection) => (
|
||||
@@ -621,15 +610,15 @@ function MissionPackGrid({
|
||||
<p className="text-xs text-slate-500">{collection.description}</p>
|
||||
) : null}
|
||||
<Badge variant="outline" className="w-fit border-slate-200 text-slate-600">
|
||||
{t('management.tasks.collections.tasksCount', {
|
||||
{t('tasksCount', {
|
||||
defaultValue: '{{count}} Aufgaben',
|
||||
count: collection.tasks_count,
|
||||
})}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-between text-xs text-slate-500">
|
||||
<span>{collection.event_type?.name ?? t('management.tasks.collections.genericType', 'Allgemein')}</span>
|
||||
<span>{collection.is_global ? t('management.tasks.collections.global', 'Global') : t('management.tasks.collections.custom', 'Custom')}</span>
|
||||
<span>{collection.event_type?.name ?? t('genericType', 'Allgemein')}</span>
|
||||
<span>{collection.is_global ? t('global', 'Global') : t('custom', 'Custom')}</span>
|
||||
</div>
|
||||
<Button
|
||||
className="mt-4 rounded-full bg-brand-rose text-white"
|
||||
@@ -639,7 +628,7 @@ function MissionPackGrid({
|
||||
{importingId === collection.id ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
t('management.tasks.collections.importCta', 'Mission Pack importieren')
|
||||
t('importCta', 'Mission Pack importieren')
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -793,13 +782,13 @@ function SummaryPill({ label, value }: { label: string; value: string | number }
|
||||
function mapPriority(priority: TenantTask['priority'], translate: (key: string, defaultValue: string) => string): string {
|
||||
switch (priority) {
|
||||
case 'low':
|
||||
return translate('management.tasks.priorities.low', 'Niedrig');
|
||||
return translate('management.eventTasks.priorities.low', 'Niedrig');
|
||||
case 'high':
|
||||
return translate('management.tasks.priorities.high', 'Hoch');
|
||||
return translate('management.eventTasks.priorities.high', 'Hoch');
|
||||
case 'urgent':
|
||||
return translate('management.tasks.priorities.urgent', 'Dringend');
|
||||
return translate('management.eventTasks.priorities.urgent', 'Dringend');
|
||||
default:
|
||||
return translate('management.tasks.priorities.medium', 'Mittel');
|
||||
return translate('management.eventTasks.priorities.medium', 'Mittel');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user