Implement watch-event feature (017) with bookmark in RsvpBar
All checks were successful
CI / backend-test (push) Successful in 1m0s
CI / frontend-test (push) Successful in 27s
CI / frontend-e2e (push) Successful in 1m30s
CI / build-and-publish (push) Has been skipped

Add client-side watch/bookmark functionality: users can save events to
localStorage without RSVPing via a bookmark button next to the "I'm attending"
CTA. Watched events appear in the event list with a "Watching" label.
Bookmark is only visible for visitors (not attendees or organizers).

Includes spec, plan, research, tasks, unit tests, and E2E tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 22:20:57 +01:00
parent e01d5ee642
commit c450849e4d
22 changed files with 1266 additions and 31 deletions

View File

@@ -194,6 +194,71 @@ describe('useEventStorage', () => {
})
})
describe('useEventStorage saveWatch / isStored', () => {
beforeEach(() => {
clearStorage()
})
it('saves a watch-only event (no rsvpToken, no organizerToken)', () => {
const { saveWatch, getStoredEvents } = useEventStorage()
saveWatch('watch-1', 'Concert', '2026-07-01T20:00:00+02:00')
const events = getStoredEvents()
expect(events).toHaveLength(1)
expect(events[0]!.eventToken).toBe('watch-1')
expect(events[0]!.title).toBe('Concert')
expect(events[0]!.dateTime).toBe('2026-07-01T20:00:00+02:00')
expect(events[0]!.rsvpToken).toBeUndefined()
expect(events[0]!.organizerToken).toBeUndefined()
})
it('does not duplicate if event already stored', () => {
const { saveWatch, saveRsvp, getStoredEvents } = useEventStorage()
saveRsvp('evt-1', 'rsvp-1', 'Max', 'Party', '2026-07-01T20:00:00+02:00')
saveWatch('evt-1', 'Party', '2026-07-01T20:00:00+02:00')
expect(getStoredEvents()).toHaveLength(1)
expect(getStoredEvents()[0]!.rsvpToken).toBe('rsvp-1')
})
it('isStored returns true for watched events', () => {
const { saveWatch, isStored } = useEventStorage()
saveWatch('watch-1', 'Concert', '2026-07-01T20:00:00+02:00')
expect(isStored('watch-1')).toBe(true)
})
it('isStored returns true for attended events', () => {
const { saveRsvp, isStored } = useEventStorage()
saveRsvp('evt-1', 'rsvp-1', 'Max', 'Party', '2026-07-01T20:00:00+02:00')
expect(isStored('evt-1')).toBe(true)
})
it('isStored returns true for organized events', () => {
const { saveCreatedEvent, isStored } = useEventStorage()
saveCreatedEvent({
eventToken: 'evt-1',
organizerToken: 'org-1',
title: 'My Event',
dateTime: '2026-07-01T20:00:00+02:00',
})
expect(isStored('evt-1')).toBe(true)
})
it('isStored returns false for unknown tokens', () => {
const { isStored } = useEventStorage()
expect(isStored('unknown')).toBe(false)
})
})
describe('isValidStoredEvent', () => {
// Import directly since it's an exported function
let isValidStoredEvent: (e: unknown) => boolean