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
7073fe43b0
commit
e0f21822a7
1 changed files with 41 additions and 39 deletions
|
|
@ -115,45 +115,6 @@ function fmtTime(iso: string) {
|
|||
{{ event.title }}
|
||||
</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">
|
||||
<TabsList class="grid w-full grid-cols-2 mb-4">
|
||||
<TabsTrigger value="scanner" class="gap-1.5">
|
||||
|
|
@ -228,6 +189,47 @@ function fmtTime(iso: string) {
|
|||
</TabsContent>
|
||||
</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
|
||||
resume the decode loop. Replaces the inline banner so the
|
||||
door operator can't miss the outcome — a busy entry meant
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue