diff --git a/src/modules/activities/views/ActivityDetailPage.vue b/src/modules/activities/views/ActivityDetailPage.vue index 1929ca2..c347363 100644 --- a/src/modules/activities/views/ActivityDetailPage.vue +++ b/src/modules/activities/views/ActivityDetailPage.vue @@ -8,15 +8,18 @@ import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Separator } from '@/components/ui/separator' import { - Calendar, MapPin, ArrowLeft, Pencil, + Calendar, MapPin, ArrowLeft, Pencil, Ticket, CheckCircle2, } from 'lucide-vue-next' import { useActivityDetail } from '../composables/useActivityDetail' import BookmarkButton from '../components/BookmarkButton.vue' import RSVPButton from '../components/RSVPButton.vue' import OrganizerCard from '../components/OrganizerCard.vue' +import PurchaseTicketDialog from '../components/PurchaseTicketDialog.vue' import { NIP52_KINDS } from '../types/nip52' import { useAuth } from '@/composables/useAuthService' import { useActivitiesStore } from '../stores/activities' +import { useOwnedTickets } from '../composables/useOwnedTickets' +import { toastService } from '@/core/services/ToastService' import { injectService, SERVICE_TOKENS } from '@/core/di-container' import type { TicketApiService } from '../services/TicketApiService' import type { TicketedEvent } from '../types/ticket' @@ -94,6 +97,56 @@ const categoryLabel = computed(() => { function goBack() { router.push({ name: 'activities' }) } + +// --- Ticket purchase + owned-tickets surface ---------------------- + +const { getTickets, paidCount, refresh: refreshOwnedTickets } = useOwnedTickets() + +const ownedTicketsForActivity = computed(() => getTickets(activityId)) +const ownedPaidCount = computed(() => paidCount(activityId)) + +const purchaseEvent = computed(() => { + const a = activity.value + if (!a || !a.ticketInfo) return null + return { + id: a.id, + name: a.title, + price_per_ticket: a.ticketInfo.price, + currency: a.ticketInfo.currency, + allow_fiat: a.ticketInfo.allowFiat, + fiat_currency: a.ticketInfo.fiatCurrency, + } +}) + +// available === undefined → unlimited capacity, button always shown +// available === 0 → sold out, button hidden +// available > 0 → button shown +const canBuyTicket = computed(() => { + const info = activity.value?.ticketInfo + if (!info) return false + return info.available === undefined || info.available > 0 +}) + +const showPurchaseDialog = ref(false) + +function openPurchaseDialog() { + if (!isAuthenticated.value) { + toastService.info('Log in to buy tickets') + return + } + showPurchaseDialog.value = true +} + +// Re-fetch the user's tickets when the purchase dialog closes (the +// buyer may have just paid). The inventory side updates automatically +// via the relay republish from the events extension. +watch(showPurchaseDialog, (open) => { + if (!open) refreshOwnedTickets() +}) + +function goToMyTickets() { + router.push('/my-tickets') +}