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>
119 lines
5.6 KiB
Markdown
119 lines
5.6 KiB
Markdown
# Research: Event List Temporal Grouping
|
|
|
|
**Feature**: 010-event-list-grouping | **Date**: 2026-03-08
|
|
|
|
## 1. Week Boundary Calculation
|
|
|
|
**Decision**: Use ISO 8601 week convention (Monday = first day of week). "This Week" spans from tomorrow through Sunday of the current week.
|
|
|
|
**Rationale**: The spec explicitly states "ISO convention where Monday is the first day of the week" (Assumptions section). The browser's `Date.getDay()` returns 0 for Sunday, 1 for Monday — straightforward to compute end-of-week as next Sunday 23:59:59.
|
|
|
|
**Implementation**: Compare event date against:
|
|
- `startOfToday` and `endOfToday` for "Today"
|
|
- `startOfTomorrow` and `endOfSunday` for "This Week"
|
|
- `after endOfSunday` for "Later"
|
|
- `before startOfToday` for "Past"
|
|
|
|
Edge case (spec scenario 4): On Sunday, "This Week" is empty (tomorrow is already next week Monday), so events for Monday appear under "Later". This falls out naturally from the algorithm.
|
|
|
|
**Alternatives considered**:
|
|
- Using a date library (date-fns, luxon): Rejected — dependency discipline (Constitution V). Native `Date` + `Intl` is sufficient for this logic.
|
|
- Locale-dependent week start: Rejected — spec mandates ISO convention explicitly.
|
|
|
|
## 2. Date Formatting for Subheaders
|
|
|
|
**Decision**: Use `Intl.DateTimeFormat` with `{ weekday: 'short', day: 'numeric', month: 'short' }` to produce labels like "Wed, 12 Mar".
|
|
|
|
**Rationale**: Consistent with existing use of `Intl.RelativeTimeFormat` in `useRelativeTime.ts`. Respects user locale for month/weekday names. No external dependency needed.
|
|
|
|
**Alternatives considered**:
|
|
- Hardcoded English day/month names: Rejected — the project already uses `Intl` APIs for locale awareness.
|
|
- Full date format (e.g., "Wednesday, March 12, 2026"): Rejected — too long for mobile cards.
|
|
|
|
## 3. Time Display on Event Cards
|
|
|
|
**Decision**: Add a `timeDisplayMode` prop to `EventCard.vue` with two modes:
|
|
- `'clock'`: Shows formatted time (e.g., "18:30") using `Intl.DateTimeFormat` with `{ hour: '2-digit', minute: '2-digit' }`
|
|
- `'relative'`: Shows relative time (e.g., "3 days ago") using existing `formatRelativeTime()`
|
|
|
|
**Rationale**: Spec requires different time representations per section: clock time for Today/This Week/Later, relative time for Past. A prop-driven approach keeps EventCard stateless regarding section context.
|
|
|
|
**Alternatives considered**:
|
|
- EventCard determining its own display mode: Rejected — card shouldn't know about sections; parent owns that context.
|
|
- Passing a pre-formatted string: Viable but less type-safe. A mode enum is clearer.
|
|
|
|
## 4. Grouping Data Structure
|
|
|
|
**Decision**: The `useEventGrouping` composable returns an array of section objects:
|
|
|
|
```typescript
|
|
interface EventSection {
|
|
key: 'today' | 'thisWeek' | 'later' | 'past'
|
|
label: string // "Today", "This Week", "Later", "Past"
|
|
events: GroupedEvent[]
|
|
}
|
|
|
|
interface DateGroup {
|
|
date: string // ISO date string (YYYY-MM-DD) for keying
|
|
label: string // Formatted date label (e.g., "Wed, 12 Mar")
|
|
events: StoredEvent[]
|
|
}
|
|
|
|
interface GroupedEvent extends StoredEvent {
|
|
dateGroup: string // ISO date for sub-grouping
|
|
}
|
|
```
|
|
|
|
Actually, simpler: the composable returns sections, each containing date groups, each containing events.
|
|
|
|
```typescript
|
|
interface EventSection {
|
|
key: 'today' | 'thisWeek' | 'later' | 'past'
|
|
label: string
|
|
dateGroups: DateGroup[]
|
|
}
|
|
|
|
interface DateGroup {
|
|
dateKey: string // YYYY-MM-DD
|
|
label: string // Formatted: "Wed, 12 Mar"
|
|
events: StoredEvent[]
|
|
}
|
|
```
|
|
|
|
**Rationale**: Two-level grouping (section → date → events) matches the spec's hierarchy. Empty sections are simply omitted from the returned array (FR-002). The "Today" section still has one DateGroup but the template skips rendering its subheader (FR-005).
|
|
|
|
**Alternatives considered**:
|
|
- Flat list with section markers: Harder to template, mixes data and presentation.
|
|
- Map/Record structure: Arrays preserve ordering guarantees (Today → This Week → Later → Past).
|
|
|
|
## 5. Visual Emphasis for "Today" Section
|
|
|
|
**Decision**: Apply a CSS class `.section--today` to the Today section that uses:
|
|
- Slightly larger section header (font-weight: 800, font-size: 1.1rem vs 700/1rem for others)
|
|
- A subtle left border accent using the primary gradient pink (`#F06292`)
|
|
|
|
**Rationale**: Consistent with Electric Dusk design system. Subtle enough not to distract but visually distinct. The existing past-event fade (opacity: 0.6, saturate: 0.5) already handles the other end of the spectrum.
|
|
|
|
**Alternatives considered**:
|
|
- Background highlight: Could clash with card backgrounds on mobile.
|
|
- Icon/emoji prefix: Spec doesn't mention icons; keep it typography-driven per design system.
|
|
|
|
## 6. Accessibility Considerations
|
|
|
|
**Decision**:
|
|
- Section headers are `<h2>` elements
|
|
- Date subheaders are `<h3>` elements
|
|
- The event list container keeps its existing `role="list"`
|
|
- Each section is a `<section>` element with `aria-label` matching the section label
|
|
|
|
**Rationale**: Constitution VI requires semantic HTML and ARIA. The heading hierarchy (h2 > h3) provides screen reader navigation landmarks. The `<section>` element with label allows assistive technology to announce section boundaries.
|
|
|
|
## 7. Existing Test Updates
|
|
|
|
**Decision**:
|
|
- Existing `EventList.spec.ts` unit tests will be updated to account for the new grouped structure (sections instead of flat list)
|
|
- Existing `home-events.spec.ts` E2E tests will be extended with new scenarios for temporal grouping
|
|
- New `useEventGrouping.spec.ts` tests the pure grouping function in isolation
|
|
|
|
**Rationale**: TDD (Constitution II). The grouping logic is a pure function — ideal for thorough unit testing with various date combinations and edge cases.
|