feat: extend event toolkit and polish guest pwa
This commit is contained in:
@@ -8,6 +8,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
|
||||
import { AdminLayout } from '../components/AdminLayout';
|
||||
import {
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
getEvent,
|
||||
getEventTasks,
|
||||
getTasks,
|
||||
updateEvent,
|
||||
TenantEvent,
|
||||
TenantTask,
|
||||
} from '../api';
|
||||
@@ -34,6 +36,7 @@ export default function EventTasksPage() {
|
||||
const [selected, setSelected] = React.useState<number[]>([]);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [saving, setSaving] = React.useState(false);
|
||||
const [modeSaving, setModeSaving] = React.useState(false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
const statusLabels = React.useMemo(
|
||||
@@ -101,6 +104,35 @@ export default function EventTasksPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const isPhotoOnlyMode = event?.engagement_mode === 'photo_only';
|
||||
|
||||
async function handleModeChange(checked: boolean) {
|
||||
if (!event || !slug) return;
|
||||
|
||||
setModeSaving(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const nextMode = checked ? 'photo_only' : 'tasks';
|
||||
const updated = await updateEvent(slug, {
|
||||
settings: {
|
||||
engagement_mode: nextMode,
|
||||
},
|
||||
});
|
||||
setEvent(updated);
|
||||
} catch (err) {
|
||||
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.'),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
setModeSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
const actions = (
|
||||
<Button variant="outline" onClick={() => navigate(ADMIN_EVENTS_PATH)} className="border-pink-200 text-pink-600">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
@@ -138,6 +170,45 @@ export default function EventTasksPage() {
|
||||
status: statusLabels[event.status as keyof typeof statusLabels] ?? event.status,
|
||||
})}
|
||||
</CardDescription>
|
||||
<div className="mt-4 flex flex-col gap-4 rounded-2xl border border-slate-200 bg-white/70 p-4 text-sm text-slate-700">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-900">
|
||||
{t('management.tasks.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 werden in der Gäste-App angezeigt. Deaktiviere sie für einen reinen Foto-Modus.',
|
||||
)}
|
||||
</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')}
|
||||
</span>
|
||||
<Switch
|
||||
checked={isPhotoOnlyMode}
|
||||
onCheckedChange={handleModeChange}
|
||||
disabled={modeSaving}
|
||||
aria-label={t('management.tasks.modes.switchLabel', 'Foto-Modus aktivieren')}
|
||||
/>
|
||||
</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 ...')}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 lg:grid-cols-2">
|
||||
<section className="space-y-3">
|
||||
@@ -182,6 +253,7 @@ export default function EventTasksPage() {
|
||||
checked ? [...prev, task.id] : prev.filter((id) => id !== task.id)
|
||||
)
|
||||
}
|
||||
disabled={isPhotoOnlyMode}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-900">{task.title}</p>
|
||||
@@ -191,10 +263,13 @@ export default function EventTasksPage() {
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
<Button onClick={() => void handleAssign()} disabled={saving || selected.length === 0}>
|
||||
<Button
|
||||
onClick={() => void handleAssign()}
|
||||
disabled={saving || selected.length === 0 || isPhotoOnlyMode}
|
||||
>
|
||||
{saving ? <Loader2 className="h-4 w-4 animate-spin" /> : t('management.tasks.actions.assign', 'Ausgewählte Tasks zuweisen')}
|
||||
</Button>
|
||||
</section>
|
||||
</section>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user