Enable users to see all their saved events on the home screen, sorted by date with upcoming events first. Key capabilities: - EventCard with title, relative time display, and organizer/attendee role badge - Sortable EventList with past-event visual distinction (faded style) - Empty state when no events are stored - Swipe-to-delete gesture with confirmation dialog - Floating action button for quick event creation - Rename router param :token → :eventToken across all views - useRelativeTime composable (Intl.RelativeTimeFormat) - useEventStorage: add validation, removeEvent(), reactive versioning - Full E2E and unit test coverage for all new components Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2.8 KiB
2.8 KiB
Data Model: Event List on Home Page
Feature: 009-list-events | Date: 2026-03-08
Entities
StoredEvent (existing — no changes)
The StoredEvent interface in frontend/src/composables/useEventStorage.ts already contains all fields needed for the event list feature.
interface StoredEvent {
eventToken: string // Required — UUID, used for navigation
organizerToken?: string // Present if user created this event
title: string // Required — displayed on card
dateTime: string // Required — ISO 8601, used for sorting + relative time
expiryDate: string // Stored but not displayed in list view
rsvpToken?: string // Present if user RSVP'd to this event
rsvpName?: string // User's name at RSVP time
}
Validation Rules
An event entry is considered valid for display if all of:
eventTokenis a non-empty stringtitleis a non-empty stringdateTimeis a non-empty string that parses to a validDate
Invalid entries are silently excluded from the list (FR-010).
Derived Properties (computed at render time)
| Property | Derivation |
|---|---|
isPast |
new Date(dateTime) < new Date() |
isOrganizer |
organizerToken !== undefined |
isAttendee |
rsvpToken !== undefined && organizerToken === undefined |
relativeTime |
Intl.RelativeTimeFormat applied to dateTime vs now |
detailRoute |
/events/${eventToken} |
Sorting Order
- Upcoming events (
dateTime >= now): ascending bydateTime(soonest first) - Past events (
dateTime < now): descending bydateTime(most recently passed first)
Composable Extension
The useEventStorage composable needs one new function:
function removeEvent(eventToken: string): void {
const events = readEvents().filter((e) => e.eventToken !== eventToken)
writeEvents(events)
}
Returned alongside existing functions from useEventStorage().
State Transitions
localStorage read
│
▼
Parse JSON ──(error)──► empty array
│
▼
Validate entries ──(invalid)──► silently excluded
│
▼
Split: upcoming / past
│
▼
Sort each group
│
▼
Concatenate ──► rendered list
Remove Event Flow
User taps delete icon / swipes left
│
▼
ConfirmDialog opens
│
┌────┴────┐
│ Cancel │ Confirm
│ │ │
│ ▼ ▼
│ removeEvent(token)
│ │
│ ▼
│ Event removed from localStorage
│ List re-renders (event disappears)
└────────────────────────────────┘