Add iCal download feature spec and clean up implemented ideas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
95
specs/019-ical-download/plan.md
Normal file
95
specs/019-ical-download/plan.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Implementation Plan: iCal Download
|
||||
|
||||
**Branch**: `019-ical-download` | **Date**: 2026-03-13 | **Spec**: [spec.md](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 (320px–768px), 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)
|
||||
|
||||
```text
|
||||
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)
|
||||
|
||||
```text
|
||||
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 8–10 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.
|
||||
Reference in New Issue
Block a user