chore(visual): Phase 4 audit polish on event/series surface
Migrates event/series UI from Tailwind/Nuxt UI form components to the zine pattern (dashed borders, CSS-var palette, native inputs). Restructures single-event and series detail/index pages to the two-column grid pattern matching about.vue and member/dashboard.vue. Touches: - app/components/EventSeriesTicketCard.vue — phantom-palette → CSS-var migration (--candle, --ember, --surface), color="gray" → "neutral" - app/components/EventTicketCard.vue — --candle-faint border var - app/components/EventTicketPurchase.vue — accent-color: var(--candle) - app/pages/events/[slug].vue — page-fill flex chain, .event-body grid - app/pages/events/index.vue — series link uses series.id (was _id) - app/pages/series/[id].vue — two-column layout (1fr/280px) + sidebar - app/pages/series/index.vue — full rewrite to dashed-border zine pattern Per docs/specs/events-visual-audit-findings.md Phase 4. Behavior unchanged.
This commit is contained in:
parent
8f0648de57
commit
0f2f1d1cbf
7 changed files with 376 additions and 337 deletions
|
|
@ -1,37 +1,27 @@
|
|||
<template>
|
||||
<div
|
||||
class="series-ticket-card border border-guild-600 dark:border-guild-600 rounded-xl overflow-hidden"
|
||||
>
|
||||
<div class="series-ticket-card" style="border: 1px solid var(--border); overflow: hidden">
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="bg-gradient-to-br from-candlelight-500 to-candlelight-700 dark:from-candlelight-600 dark:to-candlelight-800 p-6"
|
||||
>
|
||||
<div class="p-6" style="background: var(--candle); color: var(--parch-text)">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<Icon
|
||||
name="heroicons:ticket"
|
||||
class="w-5 h-5 text-candlelight-900 dark:text-candlelight-200"
|
||||
/>
|
||||
<span class="text-sm font-semibold text-candlelight-900 dark:text-candlelight-200">
|
||||
<Icon name="heroicons:ticket" class="w-5 h-5" style="color: var(--parch-text)" />
|
||||
<span class="text-sm font-semibold" style="color: var(--parch-text)">
|
||||
Series Pass
|
||||
</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-white mb-1">
|
||||
<h3 class="font-display text-xl font-bold mb-1" style="color: var(--parch-text)">
|
||||
{{ ticket.name }}
|
||||
</h3>
|
||||
<p v-if="ticket.description" class="text-sm text-candlelight-900 dark:text-candlelight-200">
|
||||
<p v-if="ticket.description" class="text-sm" style="color: var(--parch-text); opacity: 0.85">
|
||||
{{ ticket.description }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-right flex-shrink-0">
|
||||
<div class="text-3xl font-bold text-white text-ui-mono">
|
||||
<div class="text-3xl font-bold" style="color: var(--parch-text)">
|
||||
{{ formatPrice(ticket.price, ticket.currency) }}
|
||||
</div>
|
||||
<div
|
||||
v-if="ticket.isEarlyBird"
|
||||
class="text-xs text-candlelight-900 dark:text-candlelight-200 mt-1"
|
||||
>
|
||||
<div v-if="ticket.isEarlyBird" class="text-xs mt-1" style="color: var(--parch-text); opacity: 0.85">
|
||||
Early Bird Price
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -39,29 +29,23 @@
|
|||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="p-6 bg-guild-800/50 dark:bg-guild-700/30">
|
||||
<div class="p-6" style="background: var(--surface)">
|
||||
<!-- What's Included -->
|
||||
<div class="mb-6">
|
||||
<h4 class="text-sm font-semibold text-guild-200 dark:text-guild-200 mb-3 uppercase tracking-wide">
|
||||
<h4 class="text-sm font-semibold mb-3 uppercase tracking-wide" style="color: var(--text-faint)">
|
||||
What's Included
|
||||
</h4>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2 text-guild-300 dark:text-guild-300">
|
||||
<Icon name="heroicons:check-circle" class="w-5 h-5 text-candlelight-400" />
|
||||
<div class="flex items-center gap-2" style="color: var(--text)">
|
||||
<Icon name="heroicons:check-circle" class="w-5 h-5" style="color: var(--candle)" />
|
||||
<span>Access to all {{ totalEvents }} events in the series</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="ticket.isFree && !isMember"
|
||||
class="flex items-center gap-2 text-guild-300 dark:text-guild-300"
|
||||
>
|
||||
<Icon name="heroicons:check-circle" class="w-5 h-5 text-candlelight-400" />
|
||||
<div v-if="ticket.isFree && !isMember" class="flex items-center gap-2" style="color: var(--text)">
|
||||
<Icon name="heroicons:check-circle" class="w-5 h-5" style="color: var(--candle)" />
|
||||
<span>Automatic registration for all sessions</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="memberSavings > 0"
|
||||
class="flex items-center gap-2 text-guild-300 dark:text-guild-300"
|
||||
>
|
||||
<Icon name="heroicons:check-circle" class="w-5 h-5 text-candlelight-400" />
|
||||
<div v-if="memberSavings > 0" class="flex items-center gap-2" style="color: var(--text)">
|
||||
<Icon name="heroicons:check-circle" class="w-5 h-5" style="color: var(--candle)" />
|
||||
<span>Save {{ formatPrice(memberSavings, ticket.currency) }} as a member</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -69,33 +53,31 @@
|
|||
|
||||
<!-- Events List Preview -->
|
||||
<div v-if="events && events.length > 0" class="mb-6">
|
||||
<h4 class="text-sm font-semibold text-guild-200 dark:text-guild-200 mb-3 uppercase tracking-wide">
|
||||
<h4 class="text-sm font-semibold mb-3 uppercase tracking-wide" style="color: var(--text-faint)">
|
||||
Series Schedule
|
||||
</h4>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="(event, index) in events.slice(0, 3)"
|
||||
:key="event.id"
|
||||
class="flex items-start gap-3 p-3 bg-guild-700/50 dark:bg-guild-600/30 rounded-lg"
|
||||
class="flex items-start gap-3 p-3"
|
||||
>
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-candlelight-600/20 border border-candlelight-500/30 flex items-center justify-center flex-shrink-0"
|
||||
class="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
style="background: color-mix(in srgb, var(--candle) 15%, transparent); border: 1px solid var(--candle)"
|
||||
>
|
||||
<span class="text-sm font-bold text-candlelight-300">{{ index + 1 }}</span>
|
||||
<span class="text-sm font-bold" style="color: var(--candle)">{{ index + 1 }}</span>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-medium text-guild-100 dark:text-guild-100 text-sm">
|
||||
<div class="font-medium text-sm" style="color: var(--text)">
|
||||
{{ event.title }}
|
||||
</div>
|
||||
<div class="text-xs text-guild-400 dark:text-guild-400 mt-1">
|
||||
<div class="text-xs mt-1" style="color: var(--text-faint)">
|
||||
{{ formatEventDate(event.startDate) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="events.length > 3"
|
||||
class="text-center text-sm text-guild-400 dark:text-guild-400 pt-2"
|
||||
>
|
||||
<div v-if="events.length > 3" class="text-center text-sm pt-2" style="color: var(--text-faint)">
|
||||
+ {{ events.length - 3 }} more event{{ events.length - 3 !== 1 ? 's' : '' }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -104,13 +86,14 @@
|
|||
<!-- Member Benefit Callout -->
|
||||
<div
|
||||
v-if="ticket.isFree && isMember"
|
||||
class="p-4 bg-candlelight-900/20 border border-candlelight-700/30 rounded-lg mb-6"
|
||||
class="p-4 mb-6"
|
||||
style="background: color-mix(in srgb, var(--candle) 15%, transparent); border: 1px solid var(--candle)"
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<Icon name="heroicons:sparkles" class="w-5 h-5 text-candlelight-400 flex-shrink-0 mt-0.5" />
|
||||
<Icon name="heroicons:sparkles" class="w-5 h-5 flex-shrink-0 mt-0.5" style="color: var(--candle)" />
|
||||
<div>
|
||||
<div class="font-semibold text-candlelight-300 mb-1">Member Benefit</div>
|
||||
<div class="text-sm text-candlelight-400">
|
||||
<div class="font-semibold mb-1" style="color: var(--candle)">Member Benefit</div>
|
||||
<div class="text-sm" style="color: var(--candle)">
|
||||
This series pass is free for Ghost Guild members! Complete registration to secure your spot.
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -120,13 +103,14 @@
|
|||
<!-- Public vs Member Pricing -->
|
||||
<div
|
||||
v-if="!ticket.isFree && publicPrice && ticket.type === 'member'"
|
||||
class="p-4 bg-candlelight-900/20 border border-candlelight-700/30 rounded-lg mb-6"
|
||||
class="p-4 mb-6"
|
||||
style="background: color-mix(in srgb, var(--candle) 15%, transparent); border: 1px solid var(--candle)"
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<Icon name="heroicons:tag" class="w-5 h-5 text-candlelight-400 flex-shrink-0 mt-0.5" />
|
||||
<Icon name="heroicons:tag" class="w-5 h-5 flex-shrink-0 mt-0.5" style="color: var(--candle)" />
|
||||
<div class="flex-1">
|
||||
<div class="font-semibold text-candlelight-300 mb-1">Member Savings</div>
|
||||
<div class="text-sm text-candlelight-400">
|
||||
<div class="font-semibold mb-1" style="color: var(--candle)">Member Savings</div>
|
||||
<div class="text-sm" style="color: var(--candle)">
|
||||
You're saving {{ formatPrice(memberSavings, ticket.currency) }} as a member.
|
||||
Public price: {{ formatPrice(publicPrice, ticket.currency) }}
|
||||
</div>
|
||||
|
|
@ -136,22 +120,15 @@
|
|||
|
||||
<!-- Availability -->
|
||||
<div v-if="availability" class="mb-6">
|
||||
<div
|
||||
v-if="!availability.unlimited && availability.remaining !== null"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<div v-if="!availability.unlimited && availability.remaining !== null" class="flex items-center gap-2">
|
||||
<Icon
|
||||
:name="availability.remaining > 5 ? 'heroicons:check-circle' : 'heroicons:exclamation-triangle'"
|
||||
:class="[
|
||||
'w-5 h-5',
|
||||
availability.remaining > 5 ? 'text-candlelight-400' : 'text-ember-400'
|
||||
]"
|
||||
class="w-5 h-5"
|
||||
:style="{ color: availability.remaining > 5 ? 'var(--candle)' : 'var(--ember)' }"
|
||||
/>
|
||||
<span
|
||||
:class="[
|
||||
'text-sm font-medium',
|
||||
availability.remaining > 5 ? 'text-candlelight-300' : 'text-ember-300'
|
||||
]"
|
||||
class="text-sm font-medium"
|
||||
:style="{ color: availability.remaining > 5 ? 'var(--candle)' : 'var(--ember)' }"
|
||||
>
|
||||
{{ availability.remaining }} series pass{{ availability.remaining !== 1 ? 'es' : '' }} remaining
|
||||
</span>
|
||||
|
|
@ -160,12 +137,12 @@
|
|||
|
||||
<!-- Sold Out / Waitlist -->
|
||||
<div v-if="!available" class="space-y-3">
|
||||
<div class="p-4 bg-ember-900/20 border border-ember-700/30 rounded-lg">
|
||||
<div class="p-4" style="background: var(--ember-bg); border: 1px solid var(--ember)">
|
||||
<div class="flex items-start gap-3">
|
||||
<Icon name="heroicons:exclamation-circle" class="w-5 h-5 text-ember-400 flex-shrink-0 mt-0.5" />
|
||||
<Icon name="heroicons:exclamation-circle" class="w-5 h-5 flex-shrink-0 mt-0.5" style="color: var(--ember)" />
|
||||
<div>
|
||||
<div class="font-semibold text-ember-300 mb-1">Series Pass Sold Out</div>
|
||||
<div class="text-sm text-ember-400">
|
||||
<div class="font-semibold mb-1" style="color: var(--ember)">Series Pass Sold Out</div>
|
||||
<div class="text-sm" style="color: var(--ember)">
|
||||
All series passes have been claimed.
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -174,7 +151,7 @@
|
|||
<UButton
|
||||
v-if="availability?.waitlistAvailable"
|
||||
block
|
||||
color="gray"
|
||||
color="neutral"
|
||||
size="lg"
|
||||
@click="$emit('join-waitlist')"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ const formatPrice = (amount) => {
|
|||
|
||||
.early-bird {
|
||||
color: var(--candle-dim);
|
||||
border-color: rgba(122, 90, 16, 0.35);
|
||||
border-color: var(--candle-faint);
|
||||
}
|
||||
|
||||
.ticket-savings {
|
||||
|
|
|
|||
|
|
@ -463,6 +463,7 @@ const formatEventDate = (date) => {
|
|||
.consent-field input[type="checkbox"] {
|
||||
margin-top: 3px;
|
||||
flex-shrink: 0;
|
||||
accent-color: var(--candle);
|
||||
}
|
||||
.consent-hint {
|
||||
margin-bottom: 14px;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue