Calendar Booking
Kitchen sink demo showing the calendar component in different meeting states.
No existing meeting. Buyer selects a date and time.
Full page version

Acme Solutions
Introduction Call
Friday, March 20 UTC
Component documentation
CalendarBookingView is a stateful booking component that handles the full meeting lifecycle: selecting a time, viewing a pending request, and displaying a confirmed meeting. It can be rendered inline or inside CalendarModal.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
vendor | CalendarVendor | Required | Vendor name, logo, meeting title, duration, and optional buffer hours. |
buyer | CalendarBuyer | Required | Buyer name, email, and optional company. Used in confirmation view and tracking. |
closable | boolean | false | Shows a close button in the header. Use when rendered inside a modal. |
availabilityConfig | CalendarAvailabilityConfig | undefined | Blocked dates, blocked day-of-week, and per-date blocked time ranges. |
initialMeeting | ExistingMeeting | undefined | Pre-populate from backend state. When provided with status requested or confirmed, the component opens directly to the meeting info view. A cancelled status falls through to the calendar picker. |
Events
| Event | Payload | Description |
|---|---|---|
book | { date: Date; time: string } | Fires when the buyer confirms a time slot. Also fires on reschedule (edit then re-book). |
cancel | { date: Date; time: string } | Fires when the buyer cancels a request or confirmed meeting. Component returns to the calendar picker. |
close | none | Fires when the close button is clicked. Only relevant when closable is true. |
Meeting states
The component manages three internal steps. When no initialMeeting is provided, it starts in the select step. When the backend already has meeting data, pass it via initialMeeting to skip straight to the appropriate view.
Calendar picker. The buyer chooses a date and time. This is the default state and the state after a cancel or reschedule action.
Meeting request received. Shows the selected date/time with a "Pending" badge. The buyer can edit the time or cancel.
Meeting confirmed by the vendor. Shows a "Confirmed" badge, calendar invite messaging, and the option to reschedule or cancel.
State flow
The transition from requested to confirmed happens in the backend. On the next page load, pass the updated initialMeeting with status: 'confirmed'. To force a re-render when the prop changes, use a :key bound to the status.
Usage examples
New booking (no backend state)
<CalendarBookingView
:vendor="vendor"
:buyer="buyer"
@book="onBook"
@cancel="onCancel"
/>Pre-populated from backend (requested)
<CalendarBookingView
:vendor="vendor"
:buyer="buyer"
:initial-meeting="{
status: 'requested',
date: meetingDate,
time: { label: '10:00 AM', hour: 10, minute: 0, available: true }
}"
@book="onBook"
@cancel="onCancel"
/>Pre-populated from backend (confirmed)
<CalendarBookingView
:vendor="vendor"
:buyer="buyer"
:initial-meeting="{
status: 'confirmed',
date: meetingDate,
time: { label: '2:30 PM', hour: 14, minute: 30, available: true }
}"
@book="onReschedule"
@cancel="onCancel"
/>Inside a modal
<CalendarModal :open="modalOpen" @close="modalOpen = false">
<CalendarBookingView
:vendor="vendor"
:buyer="buyer"
:initial-meeting="existingMeeting"
closable
@book="onBook"
@cancel="onCancel"
@close="modalOpen = false"
/>
</CalendarModal>Re-render on backend status change
<CalendarBookingView
:key="meeting.status"
:vendor="vendor"
:buyer="buyer"
:initial-meeting="meeting"
@book="onBook"
@cancel="onCancel"
/> Binding :key to the meeting status ensures the component fully re-mounts when the backend transitions from requested to confirmed (or any other change).
Types reference
All types are exported from ~/components/calendar/types.
interface CalendarVendor {
name: string
logo: string
meetingTitle: string
meetingDuration: number
bufferHours?: number // hours before a slot is bookable (default: 4)
}
interface CalendarBuyer {
name: string
email: string
company?: string
}
type MeetingStatus = 'requested' | 'confirmed' | 'cancelled'
interface ExistingMeeting {
status: MeetingStatus
date: Date // midnight-normalized date
time: TimeSlot // the booked slot
}
interface TimeSlot {
label: string // e.g. "10:00 AM"
hour: number // 0-23
minute: number // 0-59
available: boolean
}
interface CalendarAvailabilityConfig {
unavailableDates?: string[] // "YYYY-MM-DD" format
unavailableSlots?: Record<string, BlockedTimeRange[]>
unavailableDaysOfWeek?: number[] // 0=Sun, 6=Sat
}