weitere verbesserungen der Guest PWA (vor allem TaskPicker)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user