feat(activities): restructure event detail page layout
- Move bookmark heart from top bar to the right of the title. - Replace the When/Where info cards with caption-style lines directly under the title (calendar + map-pin icons + muted text). - Move description above the organizer so it sits right under the title/info separator; push the organizer card to the bottom. - Promote the "you own N tickets" CTA (filled primary "View" button) and demote "Buy another ticket" to outline when the user already owns tickets, so the My-Tickets path is what jumps out. - Tighten ticket availability against the buy button: standalone strip removed, count rendered as an xs muted caption directly under the buy CTA.
This commit is contained in:
parent
ce2488941f
commit
4924e70fe8
1 changed files with 56 additions and 67 deletions
|
|
@ -199,11 +199,6 @@ function goToMyTickets() {
|
|||
<Pencil class="w-4 h-4" />
|
||||
Edit
|
||||
</Button>
|
||||
<BookmarkButton
|
||||
v-if="activity"
|
||||
:pubkey="activity.organizer.pubkey"
|
||||
:d-tag="activity.id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -233,23 +228,23 @@ function goToMyTickets() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<!-- Title + Category -->
|
||||
<!-- Title + bookmark + captions -->
|
||||
<div>
|
||||
<div class="flex items-start gap-2 mb-2">
|
||||
<Badge v-if="categoryLabel" variant="secondary" class="shrink-0 mt-1">
|
||||
<div class="flex flex-wrap items-start gap-2 mb-2">
|
||||
<Badge v-if="categoryLabel" variant="secondary" class="shrink-0">
|
||||
{{ categoryLabel }}
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="activity.lnbitsStatus && activity.lnbitsStatus !== 'approved'"
|
||||
:variant="activity.lnbitsStatus === 'rejected' ? 'destructive' : 'secondary'"
|
||||
class="shrink-0 mt-1 capitalize"
|
||||
class="shrink-0 capitalize"
|
||||
>
|
||||
{{ activity.lnbitsStatus === 'rejected' ? 'Rejected' : 'Pending review' }}
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="activity.isMine"
|
||||
variant="outline"
|
||||
class="shrink-0 mt-1"
|
||||
class="shrink-0"
|
||||
>
|
||||
Yours
|
||||
</Badge>
|
||||
|
|
@ -257,38 +252,41 @@ function goToMyTickets() {
|
|||
<Badge variant="outline" class="text-xs">{{ tag }}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-foreground">
|
||||
{{ activity.title }}
|
||||
</h1>
|
||||
<BookmarkButton
|
||||
:pubkey="activity.organizer.pubkey"
|
||||
:d-tag="activity.id"
|
||||
class="shrink-0 mt-1"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="activity.summary" class="text-muted-foreground mt-2">
|
||||
{{ activity.summary }}
|
||||
</p>
|
||||
|
||||
<!-- When + Where captions -->
|
||||
<div class="mt-3 space-y-1 text-sm text-muted-foreground">
|
||||
<div class="flex items-start gap-1.5">
|
||||
<Calendar class="w-4 h-4 shrink-0 mt-0.5" />
|
||||
<span>
|
||||
{{ dateDisplay }}
|
||||
<span v-if="activity.timezone" class="opacity-70">({{ activity.timezone }})</span>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="activity.location" class="flex items-start gap-1.5">
|
||||
<MapPin class="w-4 h-4 shrink-0 mt-0.5" />
|
||||
<span>{{ activity.location }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<!-- Info section -->
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<!-- When -->
|
||||
<div class="bg-muted/50 rounded-lg p-4 space-y-1">
|
||||
<div class="flex items-center gap-2 text-sm font-medium text-foreground">
|
||||
<Calendar class="w-4 h-4" />
|
||||
{{ t('activities.detail.when') }}
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground">{{ dateDisplay }}</p>
|
||||
<p v-if="activity.timezone" class="text-xs text-muted-foreground/70">
|
||||
{{ activity.timezone }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Where -->
|
||||
<div v-if="activity.location" class="bg-muted/50 rounded-lg p-4 space-y-1">
|
||||
<div class="flex items-center gap-2 text-sm font-medium text-foreground">
|
||||
<MapPin class="w-4 h-4" />
|
||||
{{ t('activities.detail.location') }}
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground">{{ activity.location }}</p>
|
||||
</div>
|
||||
<!-- Description -->
|
||||
<div class="prose prose-sm max-w-none text-foreground">
|
||||
<p class="whitespace-pre-wrap">{{ activity.description }}</p>
|
||||
</div>
|
||||
|
||||
<!-- RSVP -->
|
||||
|
|
@ -304,33 +302,20 @@ function goToMyTickets() {
|
|||
|
||||
<!-- Tickets — gated on the activity carrying ticketInfo (set
|
||||
by the calendar→Activity converter from the AIO custom
|
||||
tickets_* tags on the published event). Sections render
|
||||
bottom-up: availability count, then existing owned
|
||||
tickets (when count > 0) above a Purchase CTA (when
|
||||
capacity remains). -->
|
||||
tickets_* tags on the published event). When the user
|
||||
already owns tickets, the "you have N tickets / view"
|
||||
card is promoted (filled primary CTA) and the buy CTA
|
||||
is demoted (outline). -->
|
||||
<div v-if="activity.ticketInfo" class="space-y-3">
|
||||
<div class="flex items-center justify-center gap-1.5 text-sm text-muted-foreground">
|
||||
<Ticket class="w-4 h-4 shrink-0" />
|
||||
<span v-if="activity.ticketInfo.available === undefined">
|
||||
{{ t('activities.detail.unlimitedTickets', 'Unlimited tickets') }}
|
||||
</span>
|
||||
<span v-else-if="activity.ticketInfo.available > 0">
|
||||
{{ t('activities.detail.ticketsAvailable', { count: activity.ticketInfo.available }) }}
|
||||
</span>
|
||||
<span v-else class="text-destructive font-medium">
|
||||
{{ t('activities.detail.soldOut') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="ownedPaidCount > 0"
|
||||
class="bg-primary/10 border border-primary/30 rounded-lg p-4 flex items-center justify-between gap-3"
|
||||
class="bg-primary/15 border border-primary/40 rounded-lg p-4 flex items-center justify-between gap-3"
|
||||
>
|
||||
<div class="flex items-center gap-2 text-sm font-medium text-foreground">
|
||||
<CheckCircle2 class="w-4 h-4 text-primary shrink-0" />
|
||||
{{ t('activities.detail.ticketsOwned', { count: ownedPaidCount }, ownedPaidCount) }}
|
||||
</div>
|
||||
<Button variant="outline" size="sm" class="gap-1.5 shrink-0" @click="goToMyTickets">
|
||||
<Button size="sm" class="gap-1.5 shrink-0" @click="goToMyTickets">
|
||||
<Ticket class="w-4 h-4" />
|
||||
{{ t('activities.detail.viewMyTickets', 'View in My Tickets') }}
|
||||
</Button>
|
||||
|
|
@ -343,10 +328,11 @@ function goToMyTickets() {
|
|||
<History class="w-4 h-4 shrink-0" />
|
||||
{{ t('activities.detail.pastEvent', 'This event has already happened') }}
|
||||
</div>
|
||||
<div v-else-if="canBuyTicket">
|
||||
<div v-else-if="canBuyTicket" class="space-y-1">
|
||||
<Button
|
||||
class="w-full gap-1.5"
|
||||
size="lg"
|
||||
:variant="ownedPaidCount > 0 ? 'outline' : 'default'"
|
||||
@click="openPurchaseDialog"
|
||||
>
|
||||
<Ticket class="w-4 h-4" />
|
||||
|
|
@ -359,6 +345,14 @@ function goToMyTickets() {
|
|||
: `${activity.ticketInfo.price} ${activity.ticketInfo.currency}` }}
|
||||
</span>
|
||||
</Button>
|
||||
<p class="text-xs text-muted-foreground text-center">
|
||||
<span v-if="activity.ticketInfo.available === undefined">
|
||||
{{ t('activities.detail.unlimitedTickets', 'Unlimited tickets') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('activities.detail.ticketsAvailable', { count: activity.ticketInfo.available }) }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
v-else-if="ownedPaidCount === 0"
|
||||
|
|
@ -375,6 +369,13 @@ function goToMyTickets() {
|
|||
@update:is-open="showPurchaseDialog = $event"
|
||||
/>
|
||||
|
||||
<!-- External references -->
|
||||
<div v-if="activity.tags.length > 0" class="space-y-2">
|
||||
<!-- References would go here in future phases -->
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<!-- Organizer -->
|
||||
<div class="bg-muted/50 rounded-lg p-4">
|
||||
<p class="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2">
|
||||
|
|
@ -382,18 +383,6 @@ function goToMyTickets() {
|
|||
</p>
|
||||
<OrganizerCard :pubkey="activity.organizer.pubkey" />
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<!-- Description -->
|
||||
<div class="prose prose-sm max-w-none text-foreground">
|
||||
<p class="whitespace-pre-wrap">{{ activity.description }}</p>
|
||||
</div>
|
||||
|
||||
<!-- External references -->
|
||||
<div v-if="activity.tags.length > 0" class="space-y-2">
|
||||
<!-- References would go here in future phases -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue