feat(activities): move scan counts below the camera
The Scanned / Sold / Remaining strip moves out of the page header to below the Tabs block. The camera (or scanned list, depending on the active tab) stays prominent at the top; the counts read as a summary footer instead of competing with the title for attention. The stats-error notice follows the counts strip so the warning stays adjacent to the values it affects.
This commit is contained in:
parent
1dfb025df3
commit
4de88f92f5
1 changed files with 41 additions and 39 deletions
|
|
@ -115,45 +115,6 @@ function fmtTime(iso: string) {
|
||||||
{{ event.title }}
|
{{ event.title }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Counts strip — backend-authoritative. Source: the
|
|
||||||
`events_list_event_tickets` RPC, refreshed after every scan.
|
|
||||||
Stays consistent across organizer devices unlike a
|
|
||||||
per-device localStorage count. -->
|
|
||||||
<div class="grid grid-cols-3 gap-2 mb-4">
|
|
||||||
<div class="rounded-lg border border-border bg-muted/30 p-3 text-center">
|
|
||||||
<p class="text-2xl font-bold text-foreground">{{ registeredCount }}</p>
|
|
||||||
<p class="text-[10px] uppercase tracking-wide text-muted-foreground mt-1">Scanned</p>
|
|
||||||
</div>
|
|
||||||
<div class="rounded-lg border border-border bg-muted/30 p-3 text-center">
|
|
||||||
<p class="text-2xl font-bold text-foreground">
|
|
||||||
{{ soldCount ?? '—' }}
|
|
||||||
</p>
|
|
||||||
<p class="text-[10px] uppercase tracking-wide text-muted-foreground mt-1">Sold</p>
|
|
||||||
</div>
|
|
||||||
<div class="rounded-lg border border-border bg-muted/30 p-3 text-center">
|
|
||||||
<p class="text-2xl font-bold text-foreground">
|
|
||||||
{{ remainingCount ?? '—' }}
|
|
||||||
</p>
|
|
||||||
<p class="text-[10px] uppercase tracking-wide text-muted-foreground mt-1">Remaining</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Surface stats fetch failures (e.g. backend missing the /stats
|
|
||||||
endpoint, or wallet ownership rejected). Without this the
|
|
||||||
counts strip silently freezes on the last good value while
|
|
||||||
scans keep landing on the backend. -->
|
|
||||||
<div
|
|
||||||
v-if="statsError"
|
|
||||||
class="mb-4 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-3 text-xs text-destructive"
|
|
||||||
role="alert"
|
|
||||||
>
|
|
||||||
<AlertCircle class="w-4 h-4 mt-0.5 shrink-0" />
|
|
||||||
<div class="min-w-0">
|
|
||||||
<p class="font-medium">Counts may be out of date</p>
|
|
||||||
<p class="text-destructive/80 mt-0.5 break-words">{{ statsError }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Tabs v-model="activeTab" class="w-full">
|
<Tabs v-model="activeTab" class="w-full">
|
||||||
<TabsList class="grid w-full grid-cols-2 mb-4">
|
<TabsList class="grid w-full grid-cols-2 mb-4">
|
||||||
<TabsTrigger value="scanner" class="gap-1.5">
|
<TabsTrigger value="scanner" class="gap-1.5">
|
||||||
|
|
@ -228,6 +189,47 @@ function fmtTime(iso: string) {
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
<!-- Counts strip — backend-authoritative. Source: the
|
||||||
|
`events_list_event_tickets` RPC, refreshed after every scan.
|
||||||
|
Stays consistent across organizer devices unlike a
|
||||||
|
per-device localStorage count. Lives below the scanner so
|
||||||
|
the camera view stays prominent and the counts read as a
|
||||||
|
summary footer rather than a header. -->
|
||||||
|
<div class="grid grid-cols-3 gap-2 mt-4">
|
||||||
|
<div class="rounded-lg border border-border bg-muted/30 p-3 text-center">
|
||||||
|
<p class="text-2xl font-bold text-foreground">{{ registeredCount }}</p>
|
||||||
|
<p class="text-[10px] uppercase tracking-wide text-muted-foreground mt-1">Scanned</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg border border-border bg-muted/30 p-3 text-center">
|
||||||
|
<p class="text-2xl font-bold text-foreground">
|
||||||
|
{{ soldCount ?? '—' }}
|
||||||
|
</p>
|
||||||
|
<p class="text-[10px] uppercase tracking-wide text-muted-foreground mt-1">Sold</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg border border-border bg-muted/30 p-3 text-center">
|
||||||
|
<p class="text-2xl font-bold text-foreground">
|
||||||
|
{{ remainingCount ?? '—' }}
|
||||||
|
</p>
|
||||||
|
<p class="text-[10px] uppercase tracking-wide text-muted-foreground mt-1">Remaining</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Surface stats fetch failures (e.g. backend missing the /stats
|
||||||
|
endpoint, or wallet ownership rejected). Without this the
|
||||||
|
counts strip silently freezes on the last good value while
|
||||||
|
scans keep landing on the backend. -->
|
||||||
|
<div
|
||||||
|
v-if="statsError"
|
||||||
|
class="mt-3 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-3 text-xs text-destructive"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
<AlertCircle class="w-4 h-4 mt-0.5 shrink-0" />
|
||||||
|
<div class="min-w-0">
|
||||||
|
<p class="font-medium">Counts may be out of date</p>
|
||||||
|
<p class="text-destructive/80 mt-0.5 break-words">{{ statsError }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Full-screen result overlay. Tap anywhere to dismiss and
|
<!-- Full-screen result overlay. Tap anywhere to dismiss and
|
||||||
resume the decode loop. Replaces the inline banner so the
|
resume the decode loop. Replaces the inline banner so the
|
||||||
door operator can't miss the outcome — a busy entry meant
|
door operator can't miss the outcome — a busy entry meant
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue