refactor(ui): standardize page layouts and component styling

Introduce structural CSS classes for page shells, headers, and surface cards
Update primary theme color to red and neutral to zinc across the application
This commit is contained in:
2026-05-08 16:25:42 +08:00
parent bc009cffda
commit 227c64d346
12 changed files with 454 additions and 194 deletions

View File

@@ -1,13 +1,13 @@
<template>
<UContainer class="py-6">
<div class="mx-auto max-w-7xl space-y-4">
<div class="flex flex-col gap-2 lg:flex-row lg:items-end lg:justify-between">
<UContainer class="page-shell">
<div class="space-y-5">
<div class="page-header lg:flex-row lg:items-end lg:justify-between">
<div class="space-y-1">
<UBadge label="Bookings" color="primary" variant="soft" size="sm" class="rounded-full" />
<h1 class="text-2xl font-bold tracking-tight text-highlighted">
<UBadge label="Bookings" color="primary" variant="soft" size="sm" class="page-eyebrow" />
<h1 class="page-title">
Booking list
</h1>
<p class="text-sm text-muted">
<p class="page-description">
{{ auth.isSuperAdmin.value ? 'All submitted bookings across every PIC.' : 'Bookings assigned to you as PIC.' }}
</p>
</div>
@@ -31,7 +31,7 @@
/>
<div v-if="auth.isSuperAdmin.value" class="grid gap-4 xl:grid-cols-[minmax(0,1.2fr)_18rem]">
<UCard class="border border-default bg-default shadow-sm" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
<UCard class="surface-card rounded-lg" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
<template #header>
<div class="space-y-1">
<h2 class="text-base font-semibold text-highlighted">
@@ -75,7 +75,7 @@
</UForm>
</UCard>
<UCard class="border border-default bg-default shadow-sm" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
<UCard class="surface-card rounded-lg" :ui="{ header: 'px-4 py-3', body: 'px-4 py-3' }">
<template #header>
<div class="space-y-1">
<h2 class="text-base font-semibold text-highlighted">
@@ -88,7 +88,7 @@
</template>
<div class="grid gap-4 sm:grid-cols-2">
<div class="rounded-lg border border-default bg-muted/20 p-3">
<div class="surface-panel rounded-lg p-3">
<p class="text-xs uppercase tracking-wide text-muted">
Pending bookings
</p>
@@ -97,7 +97,7 @@
</p>
</div>
<div class="rounded-lg border border-default bg-muted/20 p-3">
<div class="surface-panel rounded-lg p-3">
<p class="text-xs uppercase tracking-wide text-muted">
Pending seats
</p>
@@ -113,7 +113,7 @@
<UCard
v-for="item in inventoryCards"
:key="item.label"
class="border border-default bg-default shadow-sm"
class="metric-card rounded-lg"
:ui="{ body: 'px-4 py-3' }"
>
<div class="space-y-0.5">
@@ -128,7 +128,7 @@
</div>
<div class="grid gap-3 md:grid-cols-3">
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'px-4 py-3' }">
<UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
<div class="space-y-0.5">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
Total Seats
@@ -139,7 +139,7 @@
</div>
</UCard>
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'px-4 py-3' }">
<UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
<div class="space-y-0.5">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
Total bookings
@@ -150,7 +150,7 @@
</div>
</UCard>
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'px-4 py-3' }">
<UCard class="metric-card rounded-lg" :ui="{ body: 'px-4 py-3' }">
<div class="space-y-0.5">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
Booking status
@@ -163,7 +163,7 @@
</div>
<UCard
class="border border-default bg-default shadow-sm"
class="surface-card overflow-hidden rounded-lg"
:ui="{ header: 'px-4 py-3', body: 'p-0 sm:p-0' }"
>
<template #header>
@@ -197,7 +197,7 @@
:empty="searchQuery.trim() ? 'No matching bookings found.' : 'No bookings available yet.'"
sticky="header"
caption="Bookings"
class="min-w-[1120px]"
class="compact-table min-w-[1120px]"
>
<template #customerName-cell="{ row }">
<div class="min-w-0 space-y-0.5 py-0.5">
@@ -363,7 +363,7 @@
class="space-y-4"
@submit="saveBookingDetails"
>
<div v-if="detailsBooking" class="rounded-lg border border-default bg-muted/20 px-3 py-2">
<div v-if="detailsBooking" class="surface-panel rounded-lg px-3 py-2">
<p class="text-sm font-medium text-highlighted">
{{ detailsBooking.customerName }}
</p>
@@ -426,12 +426,12 @@
:disabled="savingDetails || !ticketCatalogItems.length"
:ui="{
fieldset: 'grid grid-cols-2 gap-3',
item: 'rounded-lg border border-default bg-default p-3 data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
item: 'rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
}"
/>
</UFormField>
<div class="rounded-lg border border-default bg-muted/30 px-4 py-3">
<div class="surface-panel rounded-lg px-4 py-3">
<div class="flex items-center justify-between gap-4">
<span class="text-sm font-medium text-muted">Updated total</span>
<div class="text-right">
@@ -490,7 +490,7 @@
>
<template #body>
<div class="space-y-4">
<div v-if="editingBooking" class="rounded-lg border border-default bg-muted/20 px-3 py-2">
<div v-if="editingBooking" class="surface-panel rounded-lg px-3 py-2">
<p class="text-sm font-medium text-highlighted">
{{ editingBooking.customerName }}
</p>
@@ -543,7 +543,7 @@
>
<template #body>
<div class="space-y-4">
<div v-if="transferringBooking" class="rounded-lg border border-default bg-muted/20 px-3 py-2">
<div v-if="transferringBooking" class="surface-panel rounded-lg px-3 py-2">
<p class="text-sm font-medium text-highlighted">
{{ transferringBooking.customerName }}
</p>

View File

@@ -160,20 +160,20 @@ async function cancelBookingConfirmation() {
</script>
<template>
<UContainer class="py-8">
<div class="mx-auto max-w-3xl space-y-5">
<div class="space-y-2 text-center">
<UBadge :label="t('confirm.badge')" color="primary" variant="soft" class="rounded-full" />
<h1 class="text-2xl font-bold tracking-tight text-highlighted sm:text-3xl">
<UContainer class="page-shell-compact">
<div class="space-y-5">
<div class="page-header text-center sm:items-center">
<UBadge :label="t('confirm.badge')" color="primary" variant="soft" class="page-eyebrow" />
<h1 class="page-title">
{{ t('confirm.title') }}
</h1>
<p class="text-sm text-muted">
<p class="page-description">
{{ t('confirm.description') }}
</p>
</div>
<UCard
class="border border-default bg-default shadow-sm"
class="surface-card overflow-hidden rounded-lg"
:ui="{ body: 'space-y-4 p-4 sm:p-5' }"
>
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
@@ -197,7 +197,7 @@ async function cancelBookingConfirmation() {
icon="i-lucide-badge-check"
/>
<div class="overflow-hidden rounded-xl border border-default">
<div class="overflow-hidden rounded-lg border border-default">
<div
v-for="row in detailRows"
:key="row.label"
@@ -221,7 +221,7 @@ async function cancelBookingConfirmation() {
</div>
</div>
<div class="flex flex-col-reverse gap-3 sm:flex-row sm:justify-end">
<div class="action-row">
<UButton
to="/"
:label="t('confirm.backToForm')"

View File

@@ -234,30 +234,58 @@ async function bookTicket(event: FormSubmitEvent<typeof form>) {
</script>
<template>
<UContainer class="py-8">
<div class="mx-auto max-w-2xl">
<div class="mb-8 text-center">
<h1 class="text-3xl font-extrabold tracking-tight text-highlighted sm:text-4xl">
{{ bookingConfig.event.title }}
</h1>
</div>
<UContainer class="page-shell-narrow">
<div class="grid gap-8 xl:grid-cols-[minmax(0,1fr)_34rem] xl:items-start">
<section class="space-y-6 xl:sticky xl:top-6">
<div class="page-header">
<UBadge :label="t('layout.brand')" color="primary" variant="soft" class="page-eyebrow" />
<h1 class="page-title">
{{ bookingConfig.event.title }}
</h1>
<p class="page-description">
{{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
</p>
</div>
<UCard id="booking-form" class="border border-default bg-default" :ui="{
header: 'space-y-4',
body: 'space-y-6'
}">
<template #header>
<div class="space-y-3">
<div v-for="detail in eventDetails" :key="detail.label"
class="flex items-center gap-3 text-sm text-default">
<UIcon :name="detail.icon" class="size-4 text-muted" />
<span>{{ detail.value }}</span>
<div class="grid gap-3 sm:grid-cols-3 xl:grid-cols-1">
<div
v-for="detail in eventDetails"
:key="detail.label"
class="surface-card rounded-lg p-4"
>
<div class="flex items-start gap-3">
<div class="flex size-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
<UIcon :name="detail.icon" class="size-5" />
</div>
<div class="min-w-0">
<p class="text-xs font-semibold uppercase text-muted">
{{ detail.label }}
</p>
<p class="mt-1 text-sm font-semibold leading-6 text-highlighted break-words">
{{ detail.value }}
</p>
</div>
</div>
</div>
</template>
</div>
</section>
<UCard
id="booking-form"
class="surface-card overflow-hidden rounded-lg"
:ui="{ body: 'space-y-6 p-5 sm:p-6' }"
>
<div class="space-y-1">
<p class="text-sm font-semibold text-primary">
{{ t('booking.bookNow') }}
</p>
<h2 class="text-xl font-semibold text-highlighted">
{{ t('booking.ticketCategory') }}
</h2>
</div>
<UForm :state="form" :validate="validateBooking" class="space-y-6" @submit="bookTicket">
<div class="space-y-5">
<div class="grid gap-5 sm:grid-cols-2">
<UFormField name="name" :label="t('booking.name')" required>
<UInput v-model="form.name" size="xl" class="w-full" :placeholder="t('booking.namePlaceholder')" />
</UFormField>
@@ -268,47 +296,68 @@ async function bookTicket(event: FormSubmitEvent<typeof form>) {
</div>
<UFormField :label="t('booking.bookingMode')" name="bookingMode">
<URadioGroup v-model="form.bookingMode" orientation="horizontal" variant="card" indicator="hidden"
:items="bookingModeOptions" :ui="{
<URadioGroup
v-model="form.bookingMode"
orientation="horizontal"
variant="card"
indicator="hidden"
:items="bookingModeOptions"
:ui="{
fieldset: 'grid grid-cols-2 gap-3',
item: 'rounded-xl border border-default bg-default p-3 data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
}" />
</UFormField>
<UFormField :label="quantityLabel" name="quantity">
<UInputNumber v-model="form.quantity" size="xl" class="w-full" :min="1" :step="1" />
<template #help>
{{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
</template>
</UFormField>
<UFormField :label="t('booking.ticketCategory')" name="ticketType">
<URadioGroup v-model="form.ticketType" orientation="horizontal" variant="card" indicator="hidden"
:items="ticketCatalogOptions" :ui="{
fieldset: 'grid grid-cols-2 gap-3',
item: 'rounded-xl border border-default bg-default p-3 data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
}" />
</UFormField>
<div class="rounded-xl border border-default bg-muted px-4 py-4">
<div class="flex items-center justify-between gap-4">
<span class="text-sm font-medium text-muted">{{ t('common.totalPrice') }}</span>
<span class="text-2xl font-bold text-highlighted">{{ totalFormatted }}</span>
</div>
</div>
<UFormField :label="t('booking.personInCharge')">
<USelect
v-model="selectedPersonInCharge"
size="xl"
class="w-full"
:items="personInCharge"
:disabled="!personInCharge.length"
item: 'rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
}"
/>
</UFormField>
<UButton id="getTicketBtn" type="submit" :label="t('booking.bookNow')" size="xl"
class="w-full justify-center" :disabled="!selectedPersonInCharge || !selectedBookingMode || !selectedTicket" :loading="submittingBooking" />
<div class="grid gap-5 sm:grid-cols-[minmax(0,1fr)_minmax(0,1.1fr)]">
<UFormField :label="quantityLabel" name="quantity">
<UInputNumber v-model="form.quantity" size="xl" class="w-full" :min="1" :step="1" />
<template #help>
{{ t('booking.seatGeneration', { count: seatCount, seatLabel: locale === 'zh' ? '座位' : `seat${seatCount === 1 ? '' : 's'}` }) }}
</template>
</UFormField>
<UFormField :label="t('booking.personInCharge')">
<USelect
v-model="selectedPersonInCharge"
size="xl"
class="w-full"
:items="personInCharge"
:disabled="!personInCharge.length"
/>
</UFormField>
</div>
<UFormField :label="t('booking.ticketCategory')" name="ticketType">
<URadioGroup
v-model="form.ticketType"
orientation="horizontal"
variant="card"
indicator="hidden"
:items="ticketCatalogOptions"
:ui="{
fieldset: 'grid grid-cols-1 gap-3 sm:grid-cols-2',
item: 'rounded-lg border border-default bg-default p-3 transition-colors data-[state=checked]:border-primary data-[state=checked]:bg-primary/5'
}"
/>
</UFormField>
<div class="surface-panel rounded-lg px-4 py-4">
<div class="flex items-center justify-between gap-4">
<span class="text-sm font-medium text-muted">{{ t('common.totalPrice') }}</span>
<span class="text-2xl font-bold tabular-nums text-highlighted">{{ totalFormatted }}</span>
</div>
</div>
<UButton
id="getTicketBtn"
type="submit"
:label="t('booking.bookNow')"
size="xl"
class="w-full justify-center"
:disabled="!selectedPersonInCharge || !selectedBookingMode || !selectedTicket"
:loading="submittingBooking"
/>
</UForm>
</UCard>
</div>

View File

@@ -1,13 +1,59 @@
<template>
<UContainer class="py-10 lg:py-16">
<div class="mx-auto max-w-md">
<UCard class="border border-default bg-default shadow-sm">
<UContainer class="page-shell-narrow">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1fr)_27rem] lg:items-center">
<section class="page-header">
<UBadge :label="t('login.badge')" color="primary" variant="soft" class="page-eyebrow" />
<h1 class="page-title">
{{ t('login.title') }}
</h1>
<p class="page-description">
{{ t('layout.brand') }}
</p>
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-1">
<div class="surface-card rounded-lg p-4">
<div class="flex items-center gap-3">
<div class="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
<UIcon name="i-lucide-lock-keyhole" class="size-5" />
</div>
<div>
<p class="text-sm font-semibold text-highlighted">
{{ t('login.signIn') }}
</p>
<p class="text-sm text-muted">
{{ t('login.remember') }}
</p>
</div>
</div>
</div>
<div class="surface-card rounded-lg p-4">
<div class="flex items-center gap-3">
<div class="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
<UIcon name="i-lucide-fingerprint" class="size-5" />
</div>
<div>
<p class="text-sm font-semibold text-highlighted">
{{ t('login.passkey') }}
</p>
<p class="text-sm text-muted">
{{ t('login.or') }}
</p>
</div>
</div>
</div>
</div>
</section>
<UCard class="surface-card overflow-hidden rounded-lg">
<template #header>
<div class="space-y-2">
<UBadge :label="t('login.badge')" color="primary" variant="soft" class="rounded-full" />
<h1 class="text-3xl font-bold text-highlighted">
{{ t('login.title') }}
</h1>
<div class="space-y-1">
<p class="text-sm font-semibold text-primary">
{{ t('login.badge') }}
</p>
<h2 class="text-xl font-semibold text-highlighted">
{{ t('login.signIn') }}
</h2>
</div>
</template>
@@ -45,22 +91,20 @@
<div class="my-6 flex items-center gap-3">
<div class="h-px flex-1 bg-default" />
<span class="text-xs font-semibold uppercase tracking-[0.2em] text-muted">{{ t('login.or') }}</span>
<span class="text-xs font-semibold uppercase text-muted">{{ t('login.or') }}</span>
<div class="h-px flex-1 bg-default" />
</div>
<div class="space-y-4">
<UButton
:label="t('login.passkey')"
color="neutral"
variant="outline"
size="xl"
class="w-full justify-center"
icon="i-lucide-fingerprint"
:loading="passkeyPending"
@click="loginWithPasskey"
/>
</div>
<UButton
:label="t('login.passkey')"
color="neutral"
variant="outline"
size="xl"
class="w-full justify-center"
icon="i-lucide-fingerprint"
:loading="passkeyPending"
@click="loginWithPasskey"
/>
</UCard>
</div>
</UContainer>

View File

@@ -1,9 +1,9 @@
<template>
<UContainer class="py-8">
<div class="mx-auto max-w-6xl space-y-4">
<div class="space-y-1.5">
<UBadge label="Super Admin" color="primary" variant="soft" class="rounded-full" />
<h1 class="text-2xl font-bold text-highlighted sm:text-3xl">
<UContainer class="page-shell">
<div class="space-y-5">
<div class="page-header">
<UBadge label="Super Admin" color="primary" variant="soft" class="page-eyebrow" />
<h1 class="page-title">
User management
</h1>
</div>
@@ -16,7 +16,7 @@
icon="i-lucide-key-round"
/>
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'p-0 sm:p-0' }">
<UCard class="surface-card overflow-hidden rounded-lg" :ui="{ body: 'p-0 sm:p-0' }">
<template #header>
<div class="flex flex-col gap-2.5 lg:flex-row lg:items-center lg:justify-between">
<div class="flex flex-col gap-2 sm:flex-row sm:items-center">
@@ -49,8 +49,8 @@
</template>
<div class="overflow-x-auto">
<div class="min-w-[900px]" aria-label="Users">
<div class="grid grid-cols-[56px_minmax(190px,1.4fr)_minmax(150px,1fr)_120px_minmax(190px,1.2fr)_150px_minmax(230px,auto)] items-center gap-3 border-b border-default bg-muted px-4 py-2 text-xs font-semibold uppercase text-muted">
<div class="compact-table min-w-[900px]" aria-label="Users">
<div class="grid grid-cols-[56px_minmax(190px,1.4fr)_minmax(150px,1fr)_120px_minmax(190px,1.2fr)_150px_minmax(230px,auto)] items-center gap-3 border-b border-default bg-muted px-4 py-3 text-xs font-semibold uppercase text-muted">
<div>Order</div>
<div>Display Name</div>
<div>PIC Phone</div>

View File

@@ -365,23 +365,23 @@ async function openBatchShare() {
</script>
<template>
<UContainer class="py-6 sm:py-8">
<div class="mx-auto max-w-5xl space-y-5">
<div class="space-y-1 text-center">
<UBadge :label="t('receipt.badge')" color="primary" variant="soft" class="rounded-full" />
<h1 class="text-2xl font-bold tracking-tight text-highlighted sm:text-3xl">
<UContainer class="page-shell-narrow">
<div class="space-y-6">
<div class="page-header text-center sm:items-center">
<UBadge :label="t('receipt.badge')" color="primary" variant="soft" class="page-eyebrow" />
<h1 class="page-title">
{{ eventDetails.title }}
</h1>
</div>
<div class="rounded-2xl border border-default bg-default p-1.5 shadow-sm">
<div class="surface-card rounded-lg p-1.5">
<div class="flex gap-1.5">
<button
v-for="tab in tabs"
:key="tab.id"
type="button"
class="flex min-h-10 flex-1 items-center justify-center gap-1.5 rounded-xl px-3 py-2 text-xs font-medium transition sm:text-sm"
:class="activeTab === tab.id
class="flex min-h-10 flex-1 items-center justify-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium transition sm:text-sm"
:class="activeTab === tab.id
? 'bg-primary text-inverted shadow-sm'
: 'bg-elevated text-default hover:bg-muted'"
@click="activeTab = tab.id"
@@ -394,17 +394,17 @@ async function openBatchShare() {
<UCard
v-if="activeTab === 'main'"
class="border border-default bg-default shadow-sm"
class="surface-card overflow-hidden rounded-lg"
:ui="{ body: 'p-5 sm:p-8' }"
>
<div class="mx-auto flex max-w-sm flex-col items-center gap-5 text-center">
<div class="rounded-[1.75rem] border border-default bg-elevated p-5 shadow-sm">
<div class="surface-panel rounded-lg p-5 shadow-sm">
<QrCodeSvg :value="receipt.receiptUrl" :size="240" />
</div>
<div class="grid w-full gap-2 sm:grid-cols-3">
<div class="rounded-2xl border border-default bg-elevated px-4 py-3 text-left">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
<div class="surface-panel rounded-lg px-4 py-3 text-left">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
{{ t('common.category') }}
</p>
<p class="mt-1 text-sm font-semibold text-highlighted">
@@ -412,8 +412,8 @@ async function openBatchShare() {
</p>
</div>
<div class="rounded-2xl border border-default bg-elevated px-4 py-3 text-left">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
<div class="surface-panel rounded-lg px-4 py-3 text-left">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
{{ t('common.seats') }}
</p>
<p class="mt-1 text-sm font-semibold text-highlighted">
@@ -421,8 +421,8 @@ async function openBatchShare() {
</p>
</div>
<div class="rounded-2xl border border-default bg-elevated px-4 py-3 text-left">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
<div class="surface-panel rounded-lg px-4 py-3 text-left">
<p class="text-[11px] font-medium uppercase tracking-wide text-muted">
{{ t('common.totalPrice') }}
</p>
<p class="mt-1 text-sm font-semibold text-highlighted">
@@ -443,10 +443,10 @@ async function openBatchShare() {
<UCard
v-else-if="activeTab === 'status'"
class="border border-default bg-default shadow-sm"
class="surface-card overflow-hidden rounded-lg"
:ui="{ body: 'p-0' }"
>
<div class="overflow-hidden rounded-2xl">
<div class="overflow-hidden rounded-lg">
<div
v-for="row in statusRows"
:key="row.label"
@@ -467,7 +467,7 @@ async function openBatchShare() {
<UCard
v-else
class="border border-default bg-default shadow-sm"
class="surface-card overflow-hidden rounded-lg"
:ui="{ header: 'px-4 py-3', body: 'p-0' }"
>
<template #header>
@@ -494,7 +494,7 @@ async function openBatchShare() {
:data="receipt.seats"
:columns="seatColumns"
:caption="t('common.seats')"
class="min-w-[560px] sm:min-w-[720px]"
class="compact-table min-w-[560px] sm:min-w-[720px]"
>
<template #seatNumber-cell="{ row }">
<div class="min-w-0 space-y-0.5 py-0.5">
@@ -502,7 +502,7 @@ async function openBatchShare() {
<p class="text-sm font-semibold leading-tight text-highlighted sm:text-base">
{{ getSeatLabel(row.original.seatNumber, locale) }}
</p>
<UBadge
<UBadge
:label="row.original.sharedAt ? t('receipt.sharedStatus') : t('receipt.availableStatus')"
:color="row.original.sharedAt ? 'primary' : 'neutral'"
variant="soft"
@@ -586,7 +586,7 @@ async function openBatchShare() {
<template #body>
<div class="space-y-4">
<div class="grid gap-3 sm:grid-cols-2">
<div class="rounded-2xl border border-default bg-elevated p-4">
<div class="surface-panel rounded-lg p-4">
<p class="text-xs uppercase tracking-wide text-muted">
{{ t('receipt.seatsToShare') }}
</p>
@@ -600,7 +600,7 @@ async function openBatchShare() {
/>
</div>
<div class="rounded-2xl border border-default bg-elevated p-4">
<div class="surface-panel rounded-lg p-4">
<p class="text-xs uppercase tracking-wide text-muted">
{{ t('receipt.nextSeats') }}
</p>

View File

@@ -32,20 +32,20 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
</script>
<template>
<UContainer class="py-8">
<div class="mx-auto max-w-4xl space-y-6">
<div class="space-y-2 text-center">
<UBadge label="Seat Ticket" color="primary" variant="soft" class="rounded-full" />
<h1 class="text-2xl font-bold tracking-tight text-highlighted sm:text-3xl">
<UContainer class="page-shell-narrow">
<div class="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="text-sm text-muted">
<p class="page-description">
{{ eventDetails.title }}
</p>
</div>
<div class="grid gap-6 lg:grid-cols-[18rem_minmax(0,1fr)]">
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'space-y-4 p-4 sm:p-5' }">
<UCard class="surface-card overflow-hidden rounded-lg" :ui="{ body: 'space-y-4 p-4 sm:p-5' }">
<div class="space-y-1 text-center">
<p class="text-sm font-semibold text-highlighted">
QR Code
@@ -59,7 +59,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
<QrCodeSvg :value="receipt.seat.seatUrl" :size="220" />
</div>
<div class="rounded-2xl border border-default bg-elevated p-4 text-sm text-default">
<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>
@@ -72,7 +72,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
</div>
</UCard>
<UCard class="border border-default bg-default shadow-sm" :ui="{ body: 'space-y-5 p-4 sm:p-5' }">
<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
@@ -83,7 +83,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
</div>
<div class="grid gap-3 sm:grid-cols-2">
<div class="rounded-2xl border border-default bg-elevated p-4">
<div class="surface-panel rounded-lg p-4">
<p class="text-xs uppercase tracking-wide text-muted">
Guest / Organizer
</p>
@@ -91,7 +91,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
{{ receipt.booking.customerName }}
</p>
</div>
<div class="rounded-2xl border border-default bg-elevated p-4">
<div class="surface-panel rounded-lg p-4">
<p class="text-xs uppercase tracking-wide text-muted">
Ticket Category
</p>
@@ -99,7 +99,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
{{ ticketLabel }}
</p>
</div>
<div class="rounded-2xl border border-default bg-elevated p-4">
<div class="surface-panel rounded-lg p-4">
<p class="text-xs uppercase tracking-wide text-muted">
Total Seats
</p>
@@ -107,7 +107,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
{{ receipt.booking.seatCount }} seats
</p>
</div>
<div class="rounded-2xl border border-default bg-elevated p-4">
<div class="surface-panel rounded-lg p-4">
<p class="text-xs uppercase tracking-wide text-muted">
Total Booking Value
</p>
@@ -117,7 +117,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
</div>
</div>
<div class="space-y-3 rounded-2xl border border-default bg-elevated p-4">
<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>
@@ -132,7 +132,7 @@ const totalFormatted = computed(() => formatBookingCurrency(receipt.value.bookin
</div>
</div>
<div class="rounded-2xl border border-default bg-elevated p-4 text-sm text-default">
<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>

View File

@@ -1,17 +1,17 @@
<template>
<UContainer class="py-8">
<div class="mx-auto max-w-5xl space-y-6">
<div class="space-y-2">
<UBadge label="Security" color="primary" variant="soft" class="rounded-full" />
<h1 class="text-3xl font-bold text-highlighted">
<UContainer class="page-shell-narrow">
<div class="space-y-6">
<div class="page-header">
<UBadge label="Security" color="primary" variant="soft" class="page-eyebrow" />
<h1 class="page-title">
Password and passkey settings
</h1>
</div>
<div class="grid gap-6 lg:grid-cols-[1.05fr_0.95fr]">
<UCard class="border border-default bg-default">
<UCard class="surface-card overflow-hidden rounded-lg">
<template #header>
<h2 class="text-xl font-semibold text-highlighted">
<h2 class="text-lg font-semibold text-highlighted">
Change password
</h2>
</template>
@@ -39,15 +39,15 @@
</UForm>
</UCard>
<UCard class="border border-default bg-default">
<UCard class="surface-card overflow-hidden rounded-lg">
<template #header>
<h2 class="text-xl font-semibold text-highlighted">
<h2 class="text-lg font-semibold text-highlighted">
Passkeys
</h2>
</template>
<div class="space-y-5">
<div class="rounded-2xl border border-default bg-muted/40 p-4">
<div class="surface-panel rounded-lg p-4">
<div class="flex items-center justify-between gap-3">
<div>
<div class="text-sm font-semibold text-highlighted">
@@ -79,7 +79,7 @@
<div
v-for="passkey in passkeys"
:key="passkey.id"
class="rounded-2xl border border-default bg-default px-4 py-4"
class="surface-panel rounded-lg px-4 py-4"
>
<div class="flex items-center justify-between gap-3">
<div>