Show customer name and phone number in a new panel Add English and Chinese translations for the purchaser label
194 lines
7.5 KiB
Vue
194 lines
7.5 KiB
Vue
<script lang="ts" setup>
|
|
import type { PublicSeatReceipt } from '~~/shared/booking'
|
|
|
|
import {
|
|
formatBookingCurrency,
|
|
getSeatLabel
|
|
} from '~~/shared/booking'
|
|
|
|
import { formatDateTime } from '../../utils/formatters'
|
|
|
|
const route = useRoute()
|
|
const apiClient = useApiClient()
|
|
const { t } = useLocale()
|
|
|
|
const token = String(route.params.token || '')
|
|
|
|
let initialReceipt: PublicSeatReceipt
|
|
|
|
try {
|
|
initialReceipt = await apiClient<PublicSeatReceipt>(`/api/public/seats/${token}`)
|
|
} catch (error: any) {
|
|
throw createError({
|
|
statusCode: error?.statusCode || error?.data?.statusCode || 404,
|
|
statusMessage: error?.data?.statusMessage || error?.message || 'Seat ticket not found'
|
|
})
|
|
}
|
|
|
|
const receipt = ref(initialReceipt)
|
|
|
|
const eventDetails = computed(() => receipt.value.booking.event)
|
|
const ticketLabel = computed(() => receipt.value.booking.ticketLabel || receipt.value.booking.ticketType.toUpperCase())
|
|
const totalFormatted = computed(() => formatBookingCurrency(receipt.value.booking.totalPrice))
|
|
|
|
useSeoMeta({
|
|
title: () => `${getSeatLabel(receipt.value.seat.seatNumber)} - ${eventDetails.value.title}`,
|
|
description: () => `${receipt.value.seat.recipientName || receipt.value.booking.customerName} · ${ticketLabel.value}`,
|
|
ogTitle: () => `${getSeatLabel(receipt.value.seat.seatNumber)} - ${eventDetails.value.title}`,
|
|
ogDescription: () => `${receipt.value.seat.recipientName || receipt.value.booking.customerName} · ${ticketLabel.value}`,
|
|
robots: 'noindex,nofollow'
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<UContainer class="page-shell-narrow">
|
|
<div class="space-y-5 sm:space-y-6">
|
|
<div class="page-header text-center sm:items-center">
|
|
<UBadge label="Seat Ticket" color="primary" variant="soft" class="page-eyebrow" />
|
|
<h1 class="page-title">
|
|
{{ getSeatLabel(receipt.seat.seatNumber) }}
|
|
</h1>
|
|
<p class="page-description">
|
|
{{ eventDetails.title }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="grid gap-4 lg:grid-cols-[18rem_minmax(0,1fr)] lg:gap-6">
|
|
<UCard class="surface-card overflow-hidden rounded-lg" :ui="{ body: 'space-y-4 p-4 sm:p-5' }">
|
|
<div class="surface-panel rounded-lg px-4 py-3 text-left text-sm">
|
|
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
|
|
{{ t('receipt.purchaser') }}
|
|
</p>
|
|
<div class="mt-2 space-y-2">
|
|
<div class="flex min-w-0 items-center gap-2 font-semibold text-highlighted">
|
|
<UIcon name="i-lucide-user-round" class="size-4 shrink-0 text-muted" />
|
|
<span class="min-w-0 break-words">{{ receipt.booking.customerName }}</span>
|
|
</div>
|
|
<div class="flex min-w-0 items-center gap-2 text-default">
|
|
<UIcon name="i-lucide-phone" class="size-4 shrink-0 text-muted" />
|
|
<span class="min-w-0 break-words">{{ receipt.booking.customerPhone }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-1 text-center">
|
|
<p class="text-sm font-semibold text-highlighted">
|
|
QR Code
|
|
</p>
|
|
<p class="text-xs text-muted">
|
|
Present this QR code at check-in.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex justify-center">
|
|
<QrCodeSvg :value="receipt.seat.seatUrl" :size="220" />
|
|
</div>
|
|
|
|
<div class="surface-panel rounded-lg p-4 text-sm text-default">
|
|
<p class="font-medium text-highlighted">
|
|
{{ receipt.seat.recipientName || receipt.booking.customerName }}
|
|
</p>
|
|
<p v-if="receipt.seat.recipientPhone" class="mt-1 text-muted">
|
|
{{ receipt.seat.recipientPhone }}
|
|
</p>
|
|
<p class="mt-3 text-muted">
|
|
{{ receipt.booking.status === 'confirmed' ? 'Booking confirmed' : 'Booking pending confirmation' }}
|
|
</p>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard class="surface-card overflow-hidden rounded-lg" :ui="{ body: 'space-y-5 p-4 sm:p-5' }">
|
|
<div class="space-y-1">
|
|
<h2 class="text-xl font-semibold text-highlighted">
|
|
Booking Details
|
|
</h2>
|
|
<p class="text-sm text-muted">
|
|
Linked back to the main booking receipt.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="grid gap-2 sm:grid-cols-2 sm:gap-3">
|
|
<div class="surface-panel rounded-lg p-4">
|
|
<p class="text-xs uppercase tracking-wide text-muted">
|
|
Guest / Organizer
|
|
</p>
|
|
<p class="mt-2 font-semibold text-highlighted">
|
|
{{ receipt.booking.customerName }}
|
|
</p>
|
|
</div>
|
|
<div class="surface-panel rounded-lg p-4">
|
|
<p class="text-xs uppercase tracking-wide text-muted">
|
|
Ticket Category
|
|
</p>
|
|
<p class="mt-2 font-semibold text-highlighted">
|
|
{{ ticketLabel }}
|
|
</p>
|
|
</div>
|
|
<div class="surface-panel rounded-lg p-4">
|
|
<p class="text-xs uppercase tracking-wide text-muted">
|
|
Total Seats
|
|
</p>
|
|
<p class="mt-2 font-semibold text-highlighted">
|
|
{{ receipt.booking.seatCount }} seats
|
|
</p>
|
|
</div>
|
|
<div class="surface-panel rounded-lg p-4">
|
|
<p class="text-xs uppercase tracking-wide text-muted">
|
|
Total Booking Value
|
|
</p>
|
|
<p class="mt-2 font-semibold text-highlighted">
|
|
{{ totalFormatted }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="surface-panel space-y-3 rounded-lg p-4">
|
|
<div class="flex items-center gap-3 text-sm text-default">
|
|
<UIcon name="i-lucide-calendar-days" class="size-4 text-muted" />
|
|
<span>{{ eventDetails.dateLabel }}</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-sm text-default">
|
|
<UIcon name="i-lucide-clock-6" class="size-4 text-muted" />
|
|
<span>{{ eventDetails.timeLabel }}</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-sm text-default">
|
|
<UIcon name="i-lucide-map-pin" class="size-4 text-muted" />
|
|
<span>{{ eventDetails.venue }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="surface-panel rounded-lg p-4 text-sm text-default">
|
|
<p class="font-medium text-highlighted">
|
|
Seat shared {{ receipt.seat.sharedAt ? formatDateTime(receipt.seat.sharedAt) : 'recently' }}
|
|
</p>
|
|
<p class="mt-1 text-muted">
|
|
Submitted {{ formatDateTime(receipt.booking.createdAt) }}
|
|
</p>
|
|
<p v-if="receipt.booking.confirmedAt" class="mt-1 text-muted">
|
|
Confirmed {{ formatDateTime(receipt.booking.confirmedAt) }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-2 sm:flex-row">
|
|
<UButton
|
|
:to="receipt.receiptUrl"
|
|
label="Open Main Receipt"
|
|
icon="i-lucide-receipt"
|
|
class="min-h-12 flex-1 justify-center"
|
|
/>
|
|
<UButton
|
|
:to="receipt.seat.seatUrl"
|
|
target="_blank"
|
|
label="Open Ticket Link"
|
|
color="neutral"
|
|
variant="outline"
|
|
icon="i-lucide-external-link"
|
|
class="min-h-12 flex-1 justify-center"
|
|
/>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
</div>
|
|
</UContainer>
|
|
</template>
|