# 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.

```typescript
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:
- `eventToken` is a non-empty string
- `title` is a non-empty string
- `dateTime` is a non-empty string that parses to a valid `Date`

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

1. **Upcoming events** (`dateTime >= now`): ascending by `dateTime` (soonest first)
2. **Past events** (`dateTime < now`): descending by `dateTime` (most recently passed first)

### Composable Extension

The `useEventStorage` composable needs one new function:

```typescript
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)
  └────────────────────────────────┘
```
