weitere verbesserungen der Guest PWA (vor allem TaskPicker)

This commit is contained in:
Codex Agent
2025-11-12 13:19:28 +01:00
parent 1cec116933
commit d91108c883
20 changed files with 2306 additions and 653 deletions

View File

@@ -269,33 +269,66 @@ function Highlights({ topPhoto, trendingEmotion }: { topPhoto: TopPhotoHighlight
);
}
function SummaryCards({ data }: { data: AchievementsPayload }) {
function FlowSummary({ data, token }: { data: AchievementsPayload; token: string }) {
const personal = data.personal;
const tasksDone = personal?.tasks ?? data.summary.tasksSolved;
const photos = personal?.photos ?? data.summary.totalPhotos;
const likes = personal?.likes ?? data.summary.likesTotal;
const guests = data.summary.uniqueGuests;
const earnedBadges = personal?.badges.filter((badge) => badge.earned).length ?? 0;
const nextBadge = personal?.badges.find((badge) => !badge.earned);
return (
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
<Card>
<CardContent className="flex flex-col gap-1 py-4">
<span className="text-xs uppercase text-muted-foreground">Fotos gesamt</span>
<span className="text-2xl font-semibold text-foreground">{formatNumber(data.summary.totalPhotos)}</span>
</CardContent>
</Card>
<Card>
<CardContent className="flex flex-col gap-1 py-4">
<span className="text-xs uppercase text-muted-foreground">Aktive Gäste</span>
<span className="text-2xl font-semibold text-foreground">{formatNumber(data.summary.uniqueGuests)}</span>
</CardContent>
</Card>
<Card>
<CardContent className="flex flex-col gap-1 py-4">
<span className="text-xs uppercase text-muted-foreground">Erfüllte Aufgaben</span>
<span className="text-2xl font-semibold text-foreground">{formatNumber(data.summary.tasksSolved)}</span>
</CardContent>
</Card>
<Card>
<CardContent className="flex flex-col gap-1 py-4">
<span className="text-xs uppercase text-muted-foreground">Likes insgesamt</span>
<span className="text-2xl font-semibold text-foreground">{formatNumber(data.summary.likesTotal)}</span>
</CardContent>
</Card>
<section className="rounded-[32px] border border-slate-100 bg-white/95 p-6 shadow-sm dark:border-white/10 dark:bg-slate-900/70">
<div className="flex flex-wrap items-center justify-between gap-4">
<div>
<p className="text-xs uppercase tracking-[0.35em] text-slate-500 dark:text-white/60">Dein Flow</p>
<h2 className="mt-1 text-2xl font-semibold text-slate-900 dark:text-white">
{tasksDone === 0 ? 'Starte deine erste Mission.' : 'Weiter so, dein Event lebt!'}
</h2>
<p className="text-sm text-slate-500 dark:text-white/70">
{nextBadge ? `Noch ${Math.max(0, nextBadge.target - nextBadge.progress)} Schritte bis „${nextBadge.title}“.` : 'Alle aktuellen Badges freigeschaltet.'}
</p>
</div>
<div className="flex gap-2">
<Button asChild>
<Link to={`/e/${encodeURIComponent(token)}/tasks`} className="flex items-center gap-2">
<Sparkles className="h-4 w-4" />
Aufgabe ziehen
</Link>
</Button>
<Button variant="outline" asChild>
<Link to={`/e/${encodeURIComponent(token)}/gallery`} className="flex items-center gap-2">
<Camera className="h-4 w-4" />
Galerie öffnen
</Link>
</Button>
</div>
</div>
<div className="mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
<FlowStat label="Deine Aufgaben" value={formatNumber(tasksDone)} />
<FlowStat label="Fotos gesamt" value={formatNumber(photos)} />
<FlowStat label="Likes gesammelt" value={formatNumber(likes)} />
<FlowStat label="Badges" value={`${earnedBadges}/${personal?.badges.length ?? 0}`} />
</div>
<div className="mt-4 grid gap-3 sm:grid-cols-2">
<FlowStat label="Aktive Gäste" value={formatNumber(guests)} />
<FlowStat label="Letzte Mission" value={personal ? personal.guestName || 'Gast' : 'Event'} muted />
</div>
</section>
);
}
function FlowStat({ label, value, muted = false }: { label: string; value: string; muted?: boolean }) {
return (
<div
className={cn(
'rounded-2xl border border-slate-100 bg-white/80 px-4 py-3 text-left shadow-sm dark:border-white/10 dark:bg-white/5',
muted && 'opacity-80'
)}
>
<p className="text-[0.65rem] uppercase tracking-[0.35em] text-slate-400 dark:text-white/60">{label}</p>
<p className="mt-2 text-xl font-semibold text-slate-900 dark:text-white">{value}</p>
</div>
);
}
@@ -393,7 +426,7 @@ export default function AchievementsPage() {
{!loading && !error && data && (
<>
<SummaryCards data={data} />
<FlowSummary data={data} token={token} />
<div className="flex flex-wrap items-center gap-2">
<Button