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:
35
specs/010-event-list-grouping/checklists/requirements.md
Normal file
35
specs/010-event-list-grouping/checklists/requirements.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Specification Quality Checklist: Event List Temporal Grouping
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-03-08
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- All items pass. Spec is ready for `/speckit.clarify` or `/speckit.plan`.
|
||||
- One minor note: the Assumptions section mentions `Intl` API and `localStorage` — these are context references to existing behavior, not prescriptive implementation details.
|
||||
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.
|
||||
72
specs/010-event-list-grouping/plan.md
Normal file
72
specs/010-event-list-grouping/plan.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Implementation Plan: Event List Temporal Grouping
|
||||
|
||||
**Branch**: `010-event-list-grouping` | **Date**: 2026-03-08 | **Spec**: `specs/010-event-list-grouping/spec.md`
|
||||
**Input**: Feature specification from `/specs/010-event-list-grouping/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Extend the existing flat event list with temporal section grouping (Today, This Week, Later, Past). The feature is purely client-side: the existing `EventList.vue` computed property that separates events into upcoming/past is refactored into a four-section grouping with section headers, date subheaders, and context-aware time formatting. No backend changes, no new dependencies.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.9 (frontend only)
|
||||
**Primary Dependencies**: Vue 3, Vue Router 5 (existing — no additions)
|
||||
**Storage**: localStorage via `useEventStorage.ts` composable (existing — no changes)
|
||||
**Testing**: Vitest (unit), Playwright + MSW (E2E)
|
||||
**Target Platform**: PWA, mobile-first, all modern browsers
|
||||
**Project Type**: Web application (frontend enhancement)
|
||||
**Performance Goals**: Grouping computation < 1ms for 100 events (trivial — single array pass)
|
||||
**Constraints**: Client-side only, no additional network requests, offline-capable
|
||||
**Scale/Scope**: Typically < 50 events per user in localStorage
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. Privacy by Design | PASS | No new data collection. Grouping uses existing `dateTime` field. No external services. |
|
||||
| II. Test-Driven Methodology | PASS | Unit tests for grouping logic + E2E tests for all user stories planned. TDD enforced. |
|
||||
| III. API-First Development | N/A | No API changes — purely frontend enhancement. |
|
||||
| IV. Simplicity & Quality | PASS | Minimal new code: one composable for grouping, template changes in EventList. No over-engineering. |
|
||||
| V. Dependency Discipline | PASS | No new dependencies. Uses browser `Intl` API and existing `Date` methods. |
|
||||
| VI. Accessibility | PASS | Section headers use semantic HTML (`<h2>`/`<h3>`), ARIA landmarks, keyboard navigable. WCAG AA contrast enforced. |
|
||||
|
||||
**Gate result: PASS** — no violations.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/010-event-list-grouping/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0 output
|
||||
├── data-model.md # Phase 1 output
|
||||
├── spec.md # Feature specification
|
||||
└── tasks.md # Phase 2 output (via /speckit.tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ ├── EventList.vue # MODIFY — add section grouping to template + computed
|
||||
│ │ ├── EventCard.vue # MODIFY — add time format mode prop
|
||||
│ │ ├── SectionHeader.vue # NEW — temporal section header component
|
||||
│ │ └── DateSubheader.vue # NEW — date subheader component
|
||||
│ ├── composables/
|
||||
│ │ ├── useEventGrouping.ts # NEW — grouping logic (pure function)
|
||||
│ │ ├── useRelativeTime.ts # EXISTING — no changes
|
||||
│ │ └── useEventStorage.ts # EXISTING — no changes
|
||||
│ └── components/__tests__/
|
||||
│ ├── EventList.spec.ts # MODIFY — update for grouped structure
|
||||
│ ├── EventCard.spec.ts # MODIFY — add time format tests
|
||||
│ └── useEventGrouping.spec.ts # NEW — unit tests for grouping logic
|
||||
├── e2e/
|
||||
│ └── home-events.spec.ts # MODIFY — add temporal grouping E2E tests
|
||||
```
|
||||
|
||||
**Structure Decision**: Frontend-only changes. Two new small components (SectionHeader, DateSubheader) and one new composable (useEventGrouping). Existing components modified minimally.
|
||||
118
specs/010-event-list-grouping/research.md
Normal file
118
specs/010-event-list-grouping/research.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# 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.
|
||||
138
specs/010-event-list-grouping/spec.md
Normal file
138
specs/010-event-list-grouping/spec.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Feature Specification: Event List Temporal Grouping
|
||||
|
||||
**Feature Branch**: `010-event-list-grouping`
|
||||
**Created**: 2026-03-08
|
||||
**Status**: Draft
|
||||
**Input**: User description: "Extend the event list with temporal grouping so users know if an event is today, this week, or further in the future."
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Temporal Section Headers (Priority: P1)
|
||||
|
||||
As a user viewing my event list, I want events grouped under clear date-based section headers so I can instantly see what's happening today, this week, and later without reading individual dates.
|
||||
|
||||
The list displays events under these temporal sections (in order):
|
||||
|
||||
1. **Today** — events happening today
|
||||
2. **This Week** — events from tomorrow through end of current week (Sunday)
|
||||
3. **Later** — upcoming events beyond this week
|
||||
4. **Past** — events that have already occurred
|
||||
|
||||
Each section only appears if it contains at least one event. Empty sections are hidden entirely.
|
||||
|
||||
**Why this priority**: The core value of this feature — temporal orientation at a glance. Without section headers, the rest of the feature has no foundation.
|
||||
|
||||
**Independent Test**: Can be fully tested by adding events with various dates to localStorage and verifying they appear under the correct section headers.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a user has events today, tomorrow, next week, and last week, **When** they view the event list, **Then** they see four sections: "Today", "This Week", "Later", and "Past" with events correctly distributed.
|
||||
2. **Given** a user has only events for today, **When** they view the event list, **Then** only the "Today" section is visible — no empty sections appear.
|
||||
3. **Given** a user has no events at all, **When** they view the event list, **Then** the empty state is shown (as currently implemented).
|
||||
4. **Given** it is Sunday and an event is scheduled for Monday, **When** the user views the list, **Then** the Monday event appears under "Later" (not "This Week"), because the current week ends on Sunday.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Date Subheaders Within Sections (Priority: P2)
|
||||
|
||||
Within each section (except "Today"), events are further grouped by their specific date with a subheader showing the formatted date (e.g., "Sat, 17 Sep"). This mirrors the inspiration layout where individual dates appear as smaller headings under the main temporal section.
|
||||
|
||||
Within the "Today" section, no date subheader is needed since all events share the same date.
|
||||
|
||||
**Why this priority**: Adds finer-grained orientation within sections — especially important when "This Week" or "Later" contain multiple events across different days.
|
||||
|
||||
**Independent Test**: Can be tested by adding multiple events on different days within the same temporal section and verifying date subheaders appear.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a user has events on Wednesday and Friday of this week, **When** they view the "This Week" section, **Then** events are grouped under date subheaders like "Wed, 12 Mar" and "Fri, 14 Mar".
|
||||
2. **Given** a user has three events today, **When** they view the "Today" section, **Then** no date subheader appears — events are listed directly under the "Today" header.
|
||||
3. **Given** two events on the same future date, **When** the user views the list, **Then** both appear under a single date subheader for that day, sorted by time.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Enhanced Event Card Information (Priority: P2)
|
||||
|
||||
Each event card within the grouped list shows time information relevant to its context:
|
||||
|
||||
- **Today's events**: Show the time (e.g., "18:30") prominently, since the date is implied by the section.
|
||||
- **Future events**: Show the time (e.g., "18:30") — the date is provided by the subheader.
|
||||
- **Past events**: Continue showing relative time (e.g., "3 days ago") as currently implemented, since exact time matters less.
|
||||
|
||||
The existing role badges (Organizer/Attendee) and event title remain as-is.
|
||||
|
||||
**Why this priority**: Completes the information design — users need different time representations depending on temporal context.
|
||||
|
||||
**Independent Test**: Can be tested by checking that event cards display the correct time format based on which section they appear in.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an event today at 18:30, **When** the user views the "Today" section, **Then** the card shows "18:30" (not "in 3 hours").
|
||||
2. **Given** an event on Friday at 10:00, **When** the user views it under "This Week", **Then** the card shows "10:00".
|
||||
3. **Given** a past event from 3 days ago, **When** the user views the "Past" section, **Then** the card shows "3 days ago" as it does currently.
|
||||
|
||||
---
|
||||
|
||||
### User Story 4 - Today Section Visual Emphasis (Priority: P3)
|
||||
|
||||
The "Today" section header and its event cards receive subtle visual emphasis to draw the user's attention to what's happening now. This could be a slightly larger section header, bolder typography, or a subtle highlight — consistent with the Electric Dusk design system.
|
||||
|
||||
Past events continue to appear visually faded (reduced opacity/saturation) as currently implemented.
|
||||
|
||||
**Why this priority**: Nice visual polish that reinforces the temporal hierarchy, but the feature works without it.
|
||||
|
||||
**Independent Test**: Can be verified visually by checking that the "Today" section stands out compared to other sections.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** events exist for today and later, **When** the user views the list, **Then** the "Today" section is visually more prominent than other sections.
|
||||
2. **Given** only past events exist, **When** the user views the list, **Then** the "Past" section uses the existing faded treatment without any special emphasis.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- What happens when the user's device clock is set incorrectly? Events may appear in the wrong section — this is acceptable, no special handling needed.
|
||||
- What happens at midnight when "today" changes? The grouping updates on next page load or navigation; real-time re-sorting is not required.
|
||||
- What happens with an event at exactly midnight (00:00)? It belongs to the day it falls on — same as any other time.
|
||||
- What happens when a section has many events (10+)? All events are shown; no pagination or truncation within sections.
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: System MUST group events into temporal sections: "Today", "This Week", "Later", and "Past".
|
||||
- **FR-002**: System MUST hide sections that contain no events.
|
||||
- **FR-003**: System MUST display section headers with the temporal label (e.g., "Today", "This Week").
|
||||
- **FR-004**: System MUST display date subheaders within "This Week", "Later", and "Past" sections when events span multiple days.
|
||||
- **FR-005**: System MUST NOT display a date subheader within the "Today" section.
|
||||
- **FR-006**: System MUST sort events within each section by time ascending (earliest first) for upcoming events and by time descending (most recent first) for past events.
|
||||
- **FR-007**: System MUST display clock time (e.g., "18:30") on event cards in "Today", "This Week", and "Later" sections.
|
||||
- **FR-008**: System MUST display relative time (e.g., "3 days ago") on event cards in the "Past" section.
|
||||
- **FR-009**: System MUST visually emphasize the "Today" section compared to other sections.
|
||||
- **FR-010**: System MUST continue to fade past events visually (as currently implemented).
|
||||
- **FR-011**: System MUST preserve existing functionality: role badges, swipe-to-delete, delete confirmation, empty state.
|
||||
- **FR-012**: "This Week" MUST include events from tomorrow through the end of the current calendar week (Sunday).
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **Temporal Section**: A grouping label ("Today", "This Week", "Later", "Past") that organizes events by their relationship to the current date.
|
||||
- **Date Subheader**: A formatted date label (e.g., "Sat, 17 Sep") that groups events within a temporal section by their specific date.
|
||||
- **StoredEvent**: Existing entity — no changes to its structure are required. The `dateTime` field is used for all grouping and sorting logic.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: Users can identify how many events they have today within 2 seconds of viewing the list.
|
||||
- **SC-002**: Every event in the list is assigned to exactly one temporal section — no event appears in multiple sections or is missing.
|
||||
- **SC-003**: Section ordering is always consistent: Today > This Week > Later > Past.
|
||||
- **SC-004**: The feature works entirely client-side with no additional network requests beyond what currently exists.
|
||||
- **SC-005**: All existing event list functionality (delete, navigation, role badges) continues to work unchanged.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The user's locale and timezone are used for determining "today" and formatting dates/times (via the browser's `Intl` API, consistent with existing approach).
|
||||
- "Week" follows ISO convention where Monday is the first day of the week. "This Week" runs from tomorrow through Sunday.
|
||||
- The design system (Electric Dusk + Sora) applies to all new visual elements. The inspiration screenshot's color theme is explicitly NOT adopted.
|
||||
- No backend changes are needed — this is a purely frontend enhancement to the existing client-side event list.
|
||||
189
specs/010-event-list-grouping/tasks.md
Normal file
189
specs/010-event-list-grouping/tasks.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# Tasks: Event List Temporal Grouping
|
||||
|
||||
**Input**: Design documents from `/specs/010-event-list-grouping/`
|
||||
**Prerequisites**: plan.md, spec.md, research.md, data-model.md
|
||||
|
||||
**Tests**: Included — spec.md references TDD (Constitution II), and research.md explicitly plans unit + E2E test updates.
|
||||
|
||||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup
|
||||
|
||||
**Purpose**: No new project setup needed — this is a frontend-only enhancement to an existing codebase. Phase 1 is empty.
|
||||
|
||||
*(No tasks — existing project structure is sufficient.)*
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Create the core grouping composable and its types — all user stories depend on this logic.
|
||||
|
||||
**CRITICAL**: No user story work can begin until this phase is complete.
|
||||
|
||||
- [ ] T001 Define `SectionKey`, `EventSection`, and `DateGroup` types in `frontend/src/composables/useEventGrouping.ts`
|
||||
- [ ] T002 Implement `useEventGrouping` composable with section classification, date grouping, and sorting logic in `frontend/src/composables/useEventGrouping.ts`
|
||||
- [ ] T003 Write unit tests for `useEventGrouping` covering all four sections, empty-section omission, sort order, and Sunday edge case in `frontend/src/components/__tests__/useEventGrouping.spec.ts`
|
||||
|
||||
**Checkpoint**: Grouping logic is fully tested and ready for consumption by UI components.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 — Temporal Section Headers (Priority: P1) MVP
|
||||
|
||||
**Goal**: Events appear grouped under "Today", "This Week", "Later", and "Past" section headers. Empty sections are hidden.
|
||||
|
||||
**Independent Test**: Add events with various dates to localStorage, verify they appear under correct section headers.
|
||||
|
||||
### Tests for User Story 1
|
||||
|
||||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||||
|
||||
- [ ] T004 [P] [US1] Write unit tests for `SectionHeader.vue` rendering section label and emphasis flag in `frontend/src/components/__tests__/SectionHeader.spec.ts`
|
||||
- [ ] T005 [P] [US1] Update `EventList.spec.ts` tests to expect grouped section structure instead of flat list in `frontend/src/components/__tests__/EventList.spec.ts`
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [ ] T006 [P] [US1] Create `SectionHeader.vue` component with section label (`<h2>`) and `aria-label` in `frontend/src/components/SectionHeader.vue`
|
||||
- [ ] T007 [US1] Refactor `EventList.vue` template to use `useEventGrouping`, render `<section>` per temporal group with `SectionHeader`, and hide empty sections in `frontend/src/components/EventList.vue`
|
||||
- [ ] T008 [US1] Update E2E tests in `home-events.spec.ts` to verify section headers appear with correct events distributed across "Today", "This Week", "Later", "Past" in `frontend/e2e/home-events.spec.ts`
|
||||
|
||||
**Checkpoint**: Event list shows temporal section headers. All four acceptance scenarios pass.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 — Date Subheaders Within Sections (Priority: P2)
|
||||
|
||||
**Goal**: Within each section (except "Today"), events are further grouped by date with formatted subheaders like "Wed, 12 Mar".
|
||||
|
||||
**Independent Test**: Add multiple events on different days within one section, verify date subheaders appear.
|
||||
|
||||
### Tests for User Story 2
|
||||
|
||||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||||
|
||||
- [ ] T009 [P] [US2] Write unit tests for `DateSubheader.vue` rendering formatted date label in `frontend/src/components/__tests__/DateSubheader.spec.ts`
|
||||
- [ ] T010 [P] [US2] Add unit tests to `EventList.spec.ts` verifying date subheaders appear within sections and are absent in "Today" in `frontend/src/components/__tests__/EventList.spec.ts`
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [ ] T011 [P] [US2] Create `DateSubheader.vue` component with formatted date (`<h3>`) using `Intl.DateTimeFormat` in `frontend/src/components/DateSubheader.vue`
|
||||
- [ ] T012 [US2] Update `EventList.vue` template to render `DateSubheader` within each section's date groups, skipping subheader for "Today" section (`showSubheader` flag) in `frontend/src/components/EventList.vue`
|
||||
- [ ] T013 [US2] Add E2E test scenarios for date subheaders: multiple days within a section, no subheader in "Today" in `frontend/e2e/home-events.spec.ts`
|
||||
|
||||
**Checkpoint**: Date subheaders render correctly within sections. "Today" section has no subheader.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 — Enhanced Event Card Time Display (Priority: P2)
|
||||
|
||||
**Goal**: Event cards show clock time ("18:30") in Today/This Week/Later sections and relative time ("3 days ago") in Past section.
|
||||
|
||||
**Independent Test**: Check event cards display the correct time format based on which section they appear in.
|
||||
|
||||
### Tests for User Story 3
|
||||
|
||||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||||
|
||||
- [ ] T014 [P] [US3] Add unit tests to `EventCard.spec.ts` for `timeDisplayMode` prop: `'clock'` renders formatted time, `'relative'` renders relative time in `frontend/src/components/__tests__/EventCard.spec.ts`
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [ ] T015 [US3] Add `timeDisplayMode` prop (`'clock' | 'relative'`) to `EventCard.vue`, render clock time via `Intl.DateTimeFormat({ hour: '2-digit', minute: '2-digit' })` or existing `formatRelativeTime()` in `frontend/src/components/EventCard.vue`
|
||||
- [ ] T016 [US3] Update `EventList.vue` to pass `timeDisplayMode="clock"` for today/thisWeek/later sections and `timeDisplayMode="relative"` for past section in `frontend/src/components/EventList.vue`
|
||||
- [ ] T017 [US3] Add E2E test scenarios verifying clock time in future sections and relative time in past section in `frontend/e2e/home-events.spec.ts`
|
||||
|
||||
**Checkpoint**: Time display adapts to section context. All three acceptance scenarios pass.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: User Story 4 — Today Section Visual Emphasis (Priority: P3)
|
||||
|
||||
**Goal**: The "Today" section header is visually more prominent (bolder, slightly larger, accent border) than other sections.
|
||||
|
||||
**Independent Test**: Visual verification that "Today" stands out compared to other section headers.
|
||||
|
||||
- [ ] T018 [US4] Add `.section--today` CSS class to `SectionHeader.vue` with `font-weight: 800`, `font-size: 1.1rem`, and left border accent (`#F06292`) — triggered by `emphasized` prop in `frontend/src/components/SectionHeader.vue`
|
||||
- [ ] T019 [US4] Verify `EventList.vue` passes `emphasized: true` for the "Today" section (already set via `EventSection.emphasized` from data model) in `frontend/src/components/EventList.vue`
|
||||
- [ ] T020 [US4] Add visual E2E assertion checking that the "Today" section header has the emphasis CSS class applied in `frontend/e2e/home-events.spec.ts`
|
||||
|
||||
**Checkpoint**: "Today" section is visually distinct. Past events remain faded.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Final validation and regression checks.
|
||||
|
||||
- [ ] T021 Run full unit test suite (`cd frontend && npm run test:unit`) and fix any regressions
|
||||
- [ ] T022 Run full E2E test suite and verify all existing functionality (swipe-to-delete, role badges, empty state, navigation) still works in `frontend/e2e/home-events.spec.ts`
|
||||
- [ ] T023 Verify accessibility: section headers are `<h2>`, date subheaders are `<h3>`, sections have `aria-label`, keyboard navigation works
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: Empty — no work needed
|
||||
- **Foundational (Phase 2)**: No dependencies — can start immediately
|
||||
- **US1 (Phase 3)**: Depends on Phase 2 (grouping composable)
|
||||
- **US2 (Phase 4)**: Depends on Phase 3 (needs section structure in EventList)
|
||||
- **US3 (Phase 5)**: Depends on Phase 3 (needs section context for time mode)
|
||||
- **US4 (Phase 6)**: Depends on Phase 3 (needs SectionHeader component)
|
||||
- **Polish (Phase 7)**: Depends on all user stories being complete
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1 (P1)**: Can start after Foundational — no dependencies on other stories
|
||||
- **US2 (P2)**: Depends on US1 (needs section structure in template to add subheaders)
|
||||
- **US3 (P2)**: Depends on US1 (needs section context to determine time mode), independent of US2
|
||||
- **US4 (P3)**: Depends on US1 (needs SectionHeader component), independent of US2/US3
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- **Phase 2**: T001 must precede T002; T003 can run after T002
|
||||
- **Phase 3**: T004 and T005 in parallel; T006 in parallel with tests; T007 after T006
|
||||
- **Phase 4**: T009 and T010 in parallel; T011 in parallel with tests; T012 after T011
|
||||
- **Phase 5**: T014 can start as soon as US1 is done; T015 after T014; T016 after T015
|
||||
- **Phase 6**: T018 can run in parallel with Phase 5 (different files)
|
||||
- **US3 and US4** can run in parallel after US1 completes
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: After US1 Completes
|
||||
|
||||
```bash
|
||||
# These can run in parallel (different files, no dependencies):
|
||||
Task: T009 [US2] Write DateSubheader unit tests
|
||||
Task: T014 [US3] Write EventCard time mode unit tests
|
||||
Task: T018 [US4] Add .section--today CSS to SectionHeader.vue
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
|
||||
1. Complete Phase 2: Foundational (grouping composable + tests)
|
||||
2. Complete Phase 3: User Story 1 (section headers in EventList)
|
||||
3. **STOP and VALIDATE**: Test US1 independently
|
||||
4. Deploy/demo if ready — list is already grouped with headers
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Phase 2 → Grouping logic ready
|
||||
2. Add US1 → Section headers visible → Deploy/Demo (MVP!)
|
||||
3. Add US2 → Date subheaders within sections → Deploy/Demo
|
||||
4. Add US3 → Context-aware time display → Deploy/Demo
|
||||
5. Add US4 → Visual polish for "Today" → Deploy/Demo
|
||||
6. Each story adds value without breaking previous stories
|
||||
Reference in New Issue
Block a user