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:
2026-03-08 12:47:53 +01:00
parent d9136481d8
commit be1c5062a2
11 changed files with 856 additions and 42 deletions

View File

@@ -0,0 +1,75 @@
<template>
<div class="rsvp-bar">
<div class="rsvp-bar__inner">
<!-- Status state: already RSVPed -->
<div v-if="hasRsvp" class="rsvp-bar__status">
<span class="rsvp-bar__check" aria-hidden="true"></span>
<span class="rsvp-bar__text">You're attending!</span>
</div>
<!-- CTA state: no RSVP yet -->
<button v-else class="btn-primary rsvp-bar__cta" type="button" @click="$emit('open')">
I'm attending
</button>
</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
hasRsvp?: boolean
}>()
defineEmits<{
open: []
}>()
</script>
<style scoped>
.rsvp-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: center;
z-index: 50;
padding: var(--spacing-md) var(--content-padding);
padding-bottom: calc(var(--spacing-md) + env(safe-area-inset-bottom, 0px));
}
.rsvp-bar__inner {
width: 100%;
max-width: var(--content-max-width);
}
.rsvp-bar__cta {
width: 100%;
}
.rsvp-bar__status {
display: flex;
align-items: center;
justify-content: center;
gap: var(--spacing-xs);
background: var(--color-card);
border-radius: var(--radius-card);
padding: var(--spacing-md) var(--spacing-lg);
box-shadow: var(--shadow-card);
font-weight: 600;
font-size: 0.95rem;
color: var(--color-text);
}
.rsvp-bar__check {
color: #4caf50;
font-size: 1.1rem;
font-weight: 700;
}
.rsvp-bar__text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>