Group events into five temporal sections with section headers, date subheaders, and context-aware time display (clock time for upcoming, relative for past). Includes new useEventGrouping composable, SectionHeader and DateSubheader components, full unit and E2E test coverage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.0 KiB
Data Model: Event List Temporal Grouping
Feature: 010-event-list-grouping | Date: 2026-03-08
Existing Entities (no changes)
StoredEvent
Location: frontend/src/composables/useEventStorage.ts
| Field | Type | Description |
|---|---|---|
eventToken |
string |
UUID v4, unique identifier |
organizerToken |
string? |
UUID v4, present if user is organizer |
title |
string |
Event title |
dateTime |
string |
ISO 8601 with UTC offset (e.g., "2026-03-15T20:00:00+01:00") |
expiryDate |
string |
ISO 8601 expiry date |
rsvpToken |
string? |
Present if user has RSVP'd |
rsvpName |
string? |
Name used for RSVP |
Note: No changes to StoredEvent. The dateTime field is the sole input for all grouping and sorting logic.
New Types (frontend only)
SectionKey
type SectionKey = 'today' | 'thisWeek' | 'later' | 'past'
Enum-like union type for the four temporal sections. Ordering is fixed: today → thisWeek → later → past.
EventSection
interface EventSection {
key: SectionKey
label: string // Display label: "Today", "This Week", "Later", "Past"
dateGroups: DateGroup[]
emphasized: boolean // true only for 'today' section
}
Represents one temporal section in the grouped list. Sections with no events are omitted entirely (never constructed).
DateGroup
interface DateGroup {
dateKey: string // YYYY-MM-DD (for keying/dedup)
label: string // Formatted via Intl.DateTimeFormat, e.g., "Wed, 12 Mar"
events: StoredEvent[] // Events on this date, sorted by time
showSubheader: boolean // false for "Today" section (FR-005)
}
Groups events within a section by their specific calendar date. The showSubheader flag controls whether the date subheader is rendered (always false in "Today" section per FR-005).
Grouping Algorithm
Input: StoredEvent[], now: Date
Output: EventSection[]
1. Compute boundaries:
- startOfToday = today at 00:00:00 local
- endOfToday = today at 23:59:59.999 local
- endOfWeek = next Sunday at 23:59:59.999 local (or today if today is Sunday)
2. Classify each event by dateTime:
- dateTime < startOfToday → "past"
- startOfToday ≤ dateTime ≤ endOfToday → "today"
- endOfToday < dateTime ≤ endOfWeek → "thisWeek"
- dateTime > endOfWeek → "later"
3. Within each section, group by calendar date (YYYY-MM-DD)
4. Sort:
- today/thisWeek/later: date groups ascending, events within group ascending by time
- past: date groups descending, events within group descending by time
5. Emit only non-empty sections in fixed order: today, thisWeek, later, past
State Transitions
None. Events are static data in localStorage. Temporal classification is computed on each render based on current time. No event mutation occurs.
Validation Rules
No new validation. Existing isValidStoredEvent() in useEventStorage.ts already validates the dateTime field as a parseable ISO 8601 string.