From f6c15beb81da59f1e090103947dd42bb4e0e09ea Mon Sep 17 00:00:00 2001 From: Padreug Date: Mon, 25 May 2026 11:37:46 +0200 Subject: [PATCH 1/2] feat(activities): hide past events by default + "Past events" filter chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes aiolabs/webapp#72. useActivityFilters gains `showPast` (default false) and `togglePast`. applyFilters drops activities whose end-or-start date is before now unless the chip is toggled on. Sits next to the existing temporal filter inside the no-selectedDate branch, so picking a specific past date in the DatePickerStrip still surfaces that day's activities — mirroring how date-pick already bypasses the temporal pills. Counts as an active filter and resets cleanly. ActivitiesPage adds the chip in the role-filter row, outside the auth gate so logged-out users can still browse past events. Uses the lucide `History` icon. ActivityCard gains an `isPast` computed and renders a small Past badge bottom-left when applicable, suppressed when a Pending / Rejected status badge is already taking that slot (creator's own past draft — vanishingly rare, status hint is more actionable). ActivityDetailPage replaces the Buy ticket CTA with a muted "This event has already happened" notice when the event is past, so the buy flow stays unambiguous even when the user lands on a past event by direct link. The owned-tickets pill above still renders so past attendees can still see their tickets. i18n: pastEvents (chip) + past (badge) + pastEvent (detail notice) added to en/es/fr. --- src/i18n/locales/en.ts | 3 ++ src/i18n/locales/es.ts | 3 ++ src/i18n/locales/fr.ts | 3 ++ src/i18n/types.ts | 3 ++ .../activities/components/ActivityCard.vue | 25 ++++++++- .../composables/useActivityFilters.ts | 31 ++++++++++- .../activities/views/ActivitiesPage.vue | 51 ++++++++++++------- .../activities/views/ActivityDetailPage.vue | 22 +++++++- 8 files changed, 118 insertions(+), 23 deletions(-) diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 0c529a2..b72b5fc 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -59,6 +59,8 @@ const messages: LocaleMessages = { thisMonth: 'This Month', myTickets: 'My tickets', hosting: 'Hosting', + pastEvents: 'Past events', + past: 'Past', }, categories: { concert: 'Concert', @@ -104,6 +106,7 @@ const messages: LocaleMessages = { buyAnotherTicket: 'Buy another ticket', viewMyTickets: 'View in My Tickets', soldOut: 'Sold Out', + pastEvent: 'This event has already happened', free: 'Free', }, tickets: { diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index e72be71..4d57c91 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -59,6 +59,8 @@ const messages: LocaleMessages = { thisMonth: 'Este mes', myTickets: 'Mis boletos', hosting: 'Organizo', + pastEvents: 'Eventos pasados', + past: 'Pasado', }, categories: { concert: 'Concierto', @@ -104,6 +106,7 @@ const messages: LocaleMessages = { buyAnotherTicket: 'Comprar otro boleto', viewMyTickets: 'Ver en Mis boletos', soldOut: 'Agotado', + pastEvent: 'Este evento ya pasó', free: 'Gratis', }, tickets: { diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index 3b6ed76..3c429ad 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -59,6 +59,8 @@ const messages: LocaleMessages = { thisMonth: 'Ce mois-ci', myTickets: 'Mes billets', hosting: 'J\'organise', + pastEvents: 'Événements passés', + past: 'Passé', }, categories: { concert: 'Concert', @@ -104,6 +106,7 @@ const messages: LocaleMessages = { buyAnotherTicket: 'Acheter un autre billet', viewMyTickets: 'Voir dans Mes billets', soldOut: 'Épuisé', + pastEvent: 'Cet événement est déjà passé', free: 'Gratuit', }, tickets: { diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 4e5584d..05c7282 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -60,6 +60,8 @@ export interface LocaleMessages { thisMonth: string myTickets: string hosting: string + pastEvents: string + past: string } categories: Record detail: { @@ -79,6 +81,7 @@ export interface LocaleMessages { buyAnotherTicket: string viewMyTickets: string soldOut: string + pastEvent: string free: string } tickets: { diff --git a/src/modules/activities/components/ActivityCard.vue b/src/modules/activities/components/ActivityCard.vue index d16c376..442f423 100644 --- a/src/modules/activities/components/ActivityCard.vue +++ b/src/modules/activities/components/ActivityCard.vue @@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n' import { format } from 'date-fns' import { Card, CardContent } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' -import { MapPin, Calendar, Ticket, User, CheckCircle2 } from 'lucide-vue-next' +import { MapPin, Calendar, Ticket, User, CheckCircle2, History } from 'lucide-vue-next' import BookmarkButton from './BookmarkButton.vue' import { useDateLocale } from '../composables/useDateLocale' import { useOwnedTickets } from '../composables/useOwnedTickets' @@ -58,6 +58,13 @@ const placeholderBg = computed(() => { const hue = hash % 360 return `hsl(${hue}, 40%, 85%)` }) + +const isPast = computed(() => { + const a = props.activity + const end = a.endDate ?? a.startDate + if (!end || isNaN(end.getTime())) return false + return end.getTime() < Date.now() +})