Files
fete/specs/019-ical-download/plan.md
2026-03-13 21:39:31 +01:00

4.2 KiB
Raw Blame History

Implementation Plan: iCal Download

Branch: 019-ical-download | Date: 2026-03-13 | Spec: spec.md Input: Feature specification from /specs/019-ical-download/spec.md

Summary

Add a calendar download button to the event detail page that generates RFC 5545-compliant .ics files client-side. The button appears in the RsvpBar for all non-organizer users (not shown for cancelled events). No backend changes are required — all event data is already available in the frontend after fetching event details.

Technical Context

Language/Version: TypeScript 5.x, Vue 3 (Composition API) Primary Dependencies: None new — uses existing Vue 3, openapi-fetch stack. iCal generation is hand-rolled (RFC 5545 is simple enough; no library needed). Storage: N/A (no persistence; generates file on demand) Testing: Vitest (unit tests for iCal generation + slug utility), Playwright + MSW (E2E for button behavior) Target Platform: PWA, mobile-first (320px768px), all modern browsers Project Type: Web application (frontend-only change) Performance Goals: Instant download (< 50ms generation time, all client-side) Constraints: No external dependencies, no backend changes, UTF-8 encoded output Scale/Scope: 1 new composable, 1 utility, modifications to 2 existing components

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Status Notes
I. Privacy by Design PASS Client-side only, no data sent to external services, no tracking
II. Test-Driven Methodology PLAN Unit tests for iCal generation + slug utility, E2E for button UX
III. API-First Development N/A No new API endpoints — uses existing GetEventResponse data
IV. Simplicity & Quality PLAN Hand-rolled iCal (no library for ~40 lines of format code), minimal changes to existing components
V. Dependency Discipline PASS Zero new dependencies
VI. Accessibility PLAN Aria labels on calendar button, keyboard navigable, WCAG AA contrast

Gate result: PASS — no violations.

Project Structure

Documentation (this feature)

specs/019-ical-download/
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md        # Phase 1 output
├── quickstart.md        # Phase 1 output
└── tasks.md             # Phase 2 output (via /speckit.tasks)

Source Code (repository root)

frontend/src/
├── composables/
│   └── useIcalDownload.ts       # NEW: iCal generation + download trigger
├── utils/
│   └── slugify.ts               # NEW: ASCII slug for filename
├── components/
│   └── RsvpBar.vue              # MODIFIED: add calendar button (2 visual states)
└── views/
    └── EventDetailView.vue      # MODIFIED: pass event data, handle calendar emit

Structure Decision: Frontend-only changes. New composable for iCal logic (consistent with project pattern: useEventStorage, useRelativeTime). Slug utility in utils/ since it's a pure function with no Vue reactivity.

Key Design Decisions

D1: No iCal library

Decision: Hand-roll iCal generation (~40 lines).

Rationale: RFC 5545 VEVENT with 810 properties is trivial. Adding a library (e.g., ical-generator, ics) would violate Principle V (dependency discipline) — we'd use < 5% of its features.

D2: Calendar button visual states

Per FR-006, the calendar button has 2 visual contexts:

State Layout Button Style
Before RSVP Row: [bookmark] [CTA] [calendar] glow-border + glass-inner (matches bookmark)
After RSVP Row: [status-bar (flex)] [calendar (fixed)] glassmorphic bar style (matches status bar)

The button is not shown for cancelled events (RsvpBar remains hidden when event.cancelled).

D3: UID format

Decision: {eventToken}@fete — stable across re-downloads, enables calendar deduplication per FR-003.

D4: SEQUENCE strategy

Decision: Always 0. Per FR-004, a proper version counter requires backend changes (future scope).

Complexity Tracking

No constitution violations to justify.