Add temporal grouping to event list (Today/This Week/Next Week/Later/Past)
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>
This commit is contained in:
91
specs/010-event-list-grouping/data-model.md
Normal file
91
specs/010-event-list-grouping/data-model.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 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
|
||||
|
||||
```typescript
|
||||
type SectionKey = 'today' | 'thisWeek' | 'later' | 'past'
|
||||
```
|
||||
|
||||
Enum-like union type for the four temporal sections. Ordering is fixed: today → thisWeek → later → past.
|
||||
|
||||
### EventSection
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```typescript
|
||||
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.
|
||||
Reference in New Issue
Block a user