feat(events): calendar date-picker popup, remove calendar page, My Tickets filtering #110
1 changed files with 96 additions and 18 deletions
feat(events): add calendar date visual to My Tickets
Replaces the My-tickets filter that lived on the removed calendar page. A calendar button opens the date-picker popup with per-day dots over the user's own events; picking a day filters the ticket list to it (a removable date chip overrides the upcoming/past toggle while active). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit
76af245192
|
|
@ -10,10 +10,14 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { format } from 'date-fns'
|
||||
import { Ticket, User, CreditCard, CheckCircle, Clock, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-vue-next'
|
||||
import { format, startOfDay, endOfDay } from 'date-fns'
|
||||
import { Ticket, User, CreditCard, CheckCircle, Clock, AlertCircle, ChevronLeft, ChevronRight, CalendarDays, X } from 'lucide-vue-next'
|
||||
import EventCalendarPopup from '../components/EventCalendarPopup.vue'
|
||||
import { useDateLocale } from '../composables/useDateLocale'
|
||||
import type { Event } from '../types/event'
|
||||
|
||||
const { isAuthenticated, userDisplay } = useAuth()
|
||||
const { dateLocale } = useDateLocale()
|
||||
const {
|
||||
tickets,
|
||||
groupedTickets,
|
||||
|
|
@ -50,10 +54,47 @@ function isGroupPast(eventId: string): boolean {
|
|||
return end < new Date()
|
||||
}
|
||||
|
||||
const visibleGroups = computed(() =>
|
||||
groupedTickets.value.filter(g => isGroupPast(g.eventId) === showPast.value),
|
||||
// Calendar popup: visualise the days the user has events. Picking a day
|
||||
// filters the ticket list to it (overriding the upcoming/past toggle);
|
||||
// clearing it returns to the toggle.
|
||||
const calendarOpen = ref(false)
|
||||
const selectedDay = ref<Date | null>(null)
|
||||
|
||||
// The user's events (resolved from their ticket groups) — feeds the
|
||||
// calendar popup's per-day dots.
|
||||
const myEvents = computed<Event[]>(() => {
|
||||
const out: Event[] = []
|
||||
for (const g of groupedTickets.value) {
|
||||
const ev = eventsStore.getEventById(g.eventId)
|
||||
if (ev) out.push(ev)
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
||||
const selectedDayLabel = computed(() =>
|
||||
selectedDay.value
|
||||
? format(selectedDay.value, 'EEE, MMM d', { locale: dateLocale.value })
|
||||
: '',
|
||||
)
|
||||
|
||||
function isGroupOnDay(eventId: string, day: Date): boolean {
|
||||
const ev = eventsStore.getEventById(eventId)
|
||||
if (!ev) return false
|
||||
const end = ev.endDate ?? ev.startDate
|
||||
return ev.startDate <= endOfDay(day) && end >= startOfDay(day)
|
||||
}
|
||||
|
||||
function onSelectDay(date: Date) {
|
||||
selectedDay.value = date
|
||||
}
|
||||
|
||||
const visibleGroups = computed(() => {
|
||||
if (selectedDay.value) {
|
||||
return groupedTickets.value.filter(g => isGroupOnDay(g.eventId, selectedDay.value!))
|
||||
}
|
||||
return groupedTickets.value.filter(g => isGroupPast(g.eventId) === showPast.value)
|
||||
})
|
||||
|
||||
// Tab counts derived from the visible (past/upcoming-filtered) groups so
|
||||
// the badges match what's actually shown.
|
||||
const visibleCounts = computed(() => {
|
||||
|
|
@ -205,10 +246,14 @@ onMounted(async () => {
|
|||
</div>
|
||||
|
||||
<div v-else-if="tickets.length > 0">
|
||||
<!-- Upcoming/Past toggle — own row, left-aligned so it clears the
|
||||
fixed top-right hamburger menu. Defaults to upcoming so the
|
||||
list isn't cluttered with events that already happened. -->
|
||||
<div class="mb-4 inline-flex rounded-md border p-0.5">
|
||||
<!-- Filter row — own row, left-aligned so it clears the fixed
|
||||
top-right hamburger menu. Upcoming/Past toggle by default;
|
||||
when a day is picked from the calendar it's replaced by a
|
||||
removable date chip (the day overrides the toggle). The
|
||||
calendar button opens a popup visualising the user's event
|
||||
dates. -->
|
||||
<div class="mb-4 flex items-center gap-2">
|
||||
<div v-if="!selectedDay" class="inline-flex rounded-md border p-0.5">
|
||||
<Button
|
||||
:variant="!showPast ? 'default' : 'ghost'"
|
||||
size="sm"
|
||||
|
|
@ -226,6 +271,29 @@ onMounted(async () => {
|
|||
Past
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
v-else
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
class="h-7 gap-1.5"
|
||||
aria-label="Clear day filter"
|
||||
@click="selectedDay = null"
|
||||
>
|
||||
<CalendarDays class="w-3.5 h-3.5" />
|
||||
{{ selectedDayLabel }}
|
||||
<X class="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8 shrink-0"
|
||||
:class="{ 'bg-accent text-accent-foreground': selectedDay }"
|
||||
aria-label="Open calendar"
|
||||
@click="calendarOpen = true"
|
||||
>
|
||||
<CalendarDays class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Tabs default-value="all" class="w-full">
|
||||
<TabsList class="grid w-full grid-cols-4">
|
||||
|
|
@ -239,7 +307,7 @@ onMounted(async () => {
|
|||
<TabsContent value="all">
|
||||
<ScrollArea class="h-[600px] w-full pr-4">
|
||||
<div v-if="visibleGroups.length === 0" class="text-center py-8 text-muted-foreground">
|
||||
{{ showPast ? 'No past tickets' : 'No upcoming tickets' }}
|
||||
{{ selectedDay ? 'No tickets on this day' : (showPast ? 'No past tickets' : 'No upcoming tickets') }}
|
||||
</div>
|
||||
<div v-else class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card v-for="group in visibleGroups" :key="group.eventId" class="flex flex-col">
|
||||
|
|
@ -414,5 +482,15 @@ onMounted(async () => {
|
|||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
<!-- Calendar popup: dots show the days the user has events; picking
|
||||
one filters the ticket list to that day. -->
|
||||
<EventCalendarPopup
|
||||
v-model:open="calendarOpen"
|
||||
:events="myEvents"
|
||||
title="Your event dates"
|
||||
description="Pick a day to see your tickets for it"
|
||||
@select-date="onSelectDay"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue