146 lines
No EOL
5.9 KiB
Vue
146 lines
No EOL
5.9 KiB
Vue
<!-- eslint-disable vue/multi-word-component-names -->
|
|
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { useEvents } from '@/composables/useEvents'
|
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
|
import { Button } from '@/components/ui/button'
|
|
import { format } from 'date-fns'
|
|
import PurchaseTicketDialog from '@/components/events/PurchaseTicketDialog.vue'
|
|
import { RefreshCw } from 'lucide-vue-next'
|
|
|
|
const { upcomingEvents, pastEvents, isLoading, error, refresh } = useEvents()
|
|
const showPurchaseDialog = ref(false)
|
|
const selectedEvent = ref<{
|
|
id: string
|
|
name: string
|
|
price_per_ticket: number
|
|
currency: string
|
|
} | null>(null)
|
|
|
|
function formatDate(dateStr: string) {
|
|
return format(new Date(dateStr), 'PPP')
|
|
}
|
|
|
|
function handlePurchaseClick(event: {
|
|
id: string
|
|
name: string
|
|
price_per_ticket: number
|
|
currency: string
|
|
}) {
|
|
selectedEvent.value = event
|
|
showPurchaseDialog.value = true
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="container mx-auto py-8 px-4">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h1 class="text-3xl font-bold text-foreground">Events</h1>
|
|
<Button variant="secondary" size="sm" @click="refresh" :disabled="isLoading">
|
|
<RefreshCw class="w-4 h-4 mr-2" :class="{ 'animate-spin': isLoading }" />
|
|
Refresh
|
|
</Button>
|
|
</div>
|
|
|
|
<Tabs default-value="upcoming" class="w-full">
|
|
<TabsList class="grid w-full grid-cols-2">
|
|
<TabsTrigger value="upcoming">Upcoming Events</TabsTrigger>
|
|
<TabsTrigger value="past">Past Events</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<div v-if="error" class="mt-4 p-4 bg-destructive/15 text-destructive rounded-lg">
|
|
{{ error.message }}
|
|
</div>
|
|
|
|
<TabsContent value="upcoming">
|
|
<ScrollArea class="h-[600px] w-full pr-4" v-if="upcomingEvents.length">
|
|
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<Card v-for="event in upcomingEvents" :key="event.id" class="flex flex-col">
|
|
<CardHeader>
|
|
<CardTitle class="text-foreground">{{ event.name }}</CardTitle>
|
|
<CardDescription>{{ event.info }}</CardDescription>
|
|
</CardHeader>
|
|
<CardContent class="flex-grow">
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">Start Date:</span>
|
|
<span class="text-foreground">{{ formatDate(event.event_start_date) }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">End Date:</span>
|
|
<span class="text-foreground">{{ formatDate(event.event_end_date) }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">Tickets Available:</span>
|
|
<span class="text-foreground">{{ event.amount_tickets - event.sold }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">Price:</span>
|
|
<span class="text-foreground">{{ event.price_per_ticket }} {{ event.currency }}</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button
|
|
class="w-full"
|
|
variant="default"
|
|
:disabled="event.amount_tickets <= event.sold"
|
|
@click="handlePurchaseClick(event)"
|
|
>
|
|
Buy Ticket
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
</div>
|
|
</ScrollArea>
|
|
<div v-else-if="!isLoading" class="text-center py-8 text-muted-foreground">
|
|
No upcoming events found
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="past">
|
|
<ScrollArea class="h-[600px] w-full pr-4" v-if="pastEvents.length">
|
|
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<Card v-for="event in pastEvents" :key="event.id" class="flex flex-col opacity-75">
|
|
<CardHeader>
|
|
<CardTitle class="text-foreground">{{ event.name }}</CardTitle>
|
|
<CardDescription>{{ event.info }}</CardDescription>
|
|
</CardHeader>
|
|
<CardContent class="flex-grow">
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">Start Date:</span>
|
|
<span class="text-foreground">{{ formatDate(event.event_start_date) }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">End Date:</span>
|
|
<span class="text-foreground">{{ formatDate(event.event_end_date) }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">Total Tickets:</span>
|
|
<span class="text-foreground">{{ event.amount_tickets }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-muted-foreground">Tickets Sold:</span>
|
|
<span class="text-foreground">{{ event.sold }}</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</ScrollArea>
|
|
<div v-else-if="!isLoading" class="text-center py-8 text-muted-foreground">
|
|
No past events found
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
|
|
<PurchaseTicketDialog
|
|
v-if="selectedEvent"
|
|
:event="selectedEvent"
|
|
v-model:is-open="showPurchaseDialog"
|
|
/>
|
|
</div>
|
|
</template> |