feat(activities): fuzzy search on the Tickets roster
Adds a search box above the roster list that fuzzy-matches the holder name and ticket id via the shared useFuzzySearch (Fuse.js) composable. Empty query keeps the unregistered-first sort intact; typing reorders by relevance. The empty-state message now distinguishes "no tickets sold yet" from "no rows matched the current query" so a busy roster + a typo doesn't look like backend trouble.
This commit is contained in:
parent
68c8ee76d8
commit
89853641fa
1 changed files with 43 additions and 3 deletions
|
|
@ -11,16 +11,19 @@ import {
|
|||
ScanLine,
|
||||
RefreshCw,
|
||||
UserCheck,
|
||||
Search,
|
||||
} from 'lucide-vue-next'
|
||||
import { format } from 'date-fns'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import QRScanner from '@/components/ui/qr-scanner.vue'
|
||||
import { useTicketScanner } from '../composables/useTicketScanner'
|
||||
import type { EventTicket } from '../composables/useTicketScanner'
|
||||
import { useEventDetail } from '../composables/useEventDetail'
|
||||
import { useFuzzySearch } from '@/composables/useFuzzySearch'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
|
@ -93,6 +96,24 @@ const unregisteredCount = computed(
|
|||
() => allTickets.value.filter(t => !t.registered).length,
|
||||
)
|
||||
|
||||
// Fuzzy match on holder name + ticket id. When the search box is
|
||||
// empty, Fuse returns the list in its incoming order so our
|
||||
// unregistered-first sort is preserved.
|
||||
const { searchQuery, filteredItems: searchedTickets } = useFuzzySearch(
|
||||
allTickets,
|
||||
{
|
||||
fuseOptions: {
|
||||
keys: [
|
||||
{ name: 'name', weight: 0.7 },
|
||||
{ name: 'id', weight: 0.3 },
|
||||
],
|
||||
threshold: 0.3,
|
||||
ignoreLocation: true,
|
||||
},
|
||||
matchAllWhenSearchEmpty: true,
|
||||
},
|
||||
)
|
||||
|
||||
async function handleManualRegister(ticket: EventTicket) {
|
||||
pendingRegister.value.add(ticket.id)
|
||||
const res = await registerManually(ticket.id)
|
||||
|
|
@ -239,14 +260,27 @@ function fmtTime(iso: string) {
|
|||
</span>
|
||||
</h2>
|
||||
|
||||
<!-- Fuzzy filter on holder name + ticket id (Fuse.js via
|
||||
useFuzzySearch). Empty query → all rows in their
|
||||
sort order; typing → reordered by relevance. -->
|
||||
<div v-if="allTickets.length > 0" class="relative">
|
||||
<Search class="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
type="search"
|
||||
placeholder="Search by name or ticket id…"
|
||||
class="pl-8 h-9"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Unregistered rows lead the list so the operator can act
|
||||
on the actionable ones first; tap "Register" to mark an
|
||||
attendee present without a QR (e.g. lost phone, known
|
||||
in person). Failures surface as a toast; the row reverts. -->
|
||||
<ScrollArea v-if="allTickets.length > 0" class="h-[60vh]">
|
||||
<ScrollArea v-if="searchedTickets.length > 0" class="h-[60vh]">
|
||||
<ul class="space-y-1.5 pr-3">
|
||||
<li
|
||||
v-for="ticket in allTickets"
|
||||
v-for="ticket in searchedTickets"
|
||||
:key="ticket.id"
|
||||
class="flex items-center justify-between gap-2 px-3 py-2 rounded-md bg-muted/40 text-sm"
|
||||
>
|
||||
|
|
@ -289,9 +323,15 @@ function fmtTime(iso: string) {
|
|||
</li>
|
||||
</ul>
|
||||
</ScrollArea>
|
||||
<p v-else class="text-sm text-muted-foreground text-center py-12">
|
||||
<p
|
||||
v-else-if="allTickets.length === 0"
|
||||
class="text-sm text-muted-foreground text-center py-12"
|
||||
>
|
||||
No tickets sold yet.
|
||||
</p>
|
||||
<p v-else class="text-sm text-muted-foreground text-center py-12">
|
||||
No tickets match “{{ searchQuery }}”.
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue