Add RSVP frontend: bottom sheet form, RsvpBar, and localStorage persistence
Introduces BottomSheet and RsvpBar components, integrates the RSVP submission flow into EventDetailView, extends useEventStorage with saveRsvp/getRsvp, and adds unit tests plus an E2E spec for the RSVP workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -116,4 +116,52 @@ describe('useEventStorage', () => {
|
||||
expect(events).toHaveLength(1)
|
||||
expect(events[0]!.title).toBe('New Title')
|
||||
})
|
||||
|
||||
it('saves and retrieves RSVP for an existing event', () => {
|
||||
const { saveCreatedEvent, saveRsvp, getRsvp } = useEventStorage()
|
||||
|
||||
saveCreatedEvent({
|
||||
eventToken: 'abc-123',
|
||||
title: 'Birthday',
|
||||
dateTime: '2026-06-15T20:00:00+02:00',
|
||||
expiryDate: '2026-07-15',
|
||||
})
|
||||
|
||||
saveRsvp('abc-123', 'rsvp-token-1', 'Max', 'Birthday', '2026-06-15T20:00:00+02:00')
|
||||
|
||||
const rsvp = getRsvp('abc-123')
|
||||
expect(rsvp).toEqual({ rsvpToken: 'rsvp-token-1', rsvpName: 'Max' })
|
||||
})
|
||||
|
||||
it('saves RSVP for a new event (not previously stored)', () => {
|
||||
const { saveRsvp, getRsvp, getStoredEvents } = useEventStorage()
|
||||
|
||||
saveRsvp('new-event', 'rsvp-token-2', 'Anna', 'Party', '2026-08-01T18:00:00+02:00')
|
||||
|
||||
const rsvp = getRsvp('new-event')
|
||||
expect(rsvp).toEqual({ rsvpToken: 'rsvp-token-2', rsvpName: 'Anna' })
|
||||
|
||||
const events = getStoredEvents()
|
||||
expect(events).toHaveLength(1)
|
||||
expect(events[0]!.eventToken).toBe('new-event')
|
||||
expect(events[0]!.title).toBe('Party')
|
||||
})
|
||||
|
||||
it('returns undefined RSVP for event without RSVP', () => {
|
||||
const { saveCreatedEvent, getRsvp } = useEventStorage()
|
||||
|
||||
saveCreatedEvent({
|
||||
eventToken: 'abc-123',
|
||||
title: 'Test',
|
||||
dateTime: '2026-06-15T20:00:00+02:00',
|
||||
expiryDate: '2026-07-15',
|
||||
})
|
||||
|
||||
expect(getRsvp('abc-123')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('returns undefined RSVP for unknown event', () => {
|
||||
const { getRsvp } = useEventStorage()
|
||||
expect(getRsvp('unknown')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,6 +4,8 @@ export interface StoredEvent {
|
||||
title: string
|
||||
dateTime: string
|
||||
expiryDate: string
|
||||
rsvpToken?: string
|
||||
rsvpName?: string
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'fete:events'
|
||||
@@ -37,5 +39,25 @@ export function useEventStorage() {
|
||||
return event?.organizerToken
|
||||
}
|
||||
|
||||
return { saveCreatedEvent, getStoredEvents, getOrganizerToken }
|
||||
function saveRsvp(eventToken: string, rsvpToken: string, rsvpName: string, title: string, dateTime: string): void {
|
||||
const events = readEvents()
|
||||
const existing = events.find((e) => e.eventToken === eventToken)
|
||||
if (existing) {
|
||||
existing.rsvpToken = rsvpToken
|
||||
existing.rsvpName = rsvpName
|
||||
} else {
|
||||
events.push({ eventToken, title, dateTime, expiryDate: '', rsvpToken, rsvpName })
|
||||
}
|
||||
writeEvents(events)
|
||||
}
|
||||
|
||||
function getRsvp(eventToken: string): { rsvpToken: string; rsvpName: string } | undefined {
|
||||
const event = readEvents().find((e) => e.eventToken === eventToken)
|
||||
if (event?.rsvpToken && event?.rsvpName) {
|
||||
return { rsvpToken: event.rsvpToken, rsvpName: event.rsvpName }
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
return { saveCreatedEvent, getStoredEvents, getOrganizerToken, saveRsvp, getRsvp }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user