Files
fete/specs/010-event-list-grouping/research.md
nitrix a52d0cd1d3
All checks were successful
CI / backend-test (push) Successful in 58s
CI / frontend-test (push) Successful in 23s
CI / frontend-e2e (push) Successful in 1m9s
CI / build-and-publish (push) Has been skipped
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>
2026-03-08 17:26:58 +01:00

5.6 KiB

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:

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.

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.