vue2-booking-calendar
v1.2.0
Published
A customizable booking calendar component for Vue 2 with service, specialist, and customer management
Maintainers
Readme
vue2-booking-calendar
A customizable booking calendar component for Vue 2 with service, specialist, and customer management.
Features
- Month, week, and day views
- Booking creation with bidirectional cascading dropdowns (brand ↔ branch ↔ service ↔ specialist ↔ customer)
- Select any field first — all other dropdowns filter automatically
- View booking details with full customer info (name, email, phone, address, notes)
- No-show and cancel booking with confirmation dialogs
- Specialist availability side panel with booked slot detection
- New customer inline creation from booking form
- Per-service duration with auto-computed end times
- Customizable business hours (standard, 24h, and overnight schedules)
- Event callbacks with custom function editors and live log
- Full JSON configuration for dropdown options and sample events
- Drag and drop events
- SVG icons throughout
- Fully customizable via slots
Installation
npm install vue2-booking-calendarQuick Start
import Vue from 'vue'
import Vuex from 'vuex'
import BookingCalendar, { storeConfig } from 'vue2-booking-calendar'
import 'vue2-booking-calendar/dist/style.css'
Vue.use(Vuex)
Vue.use(BookingCalendar)
const store = new Vuex.Store(storeConfig)
new Vue({
store,
template: '<BookingCalendar />'
}).$mount('#app')Configuration
All configuration is managed through the Vuex store config object. You can override defaults before creating the store:
const store = new Vuex.Store({
...storeConfig,
state() {
const state = storeConfig.state()
// Override config
state.config.businessHoursStart = 9
state.config.businessHoursEnd = 17
state.config.timeFormat = '24h'
state.config.showLogo = false
return state
}
})Config Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| slotDuration | number | 60 | Time slot duration in minutes (15, 30, 60) |
| businessHoursStart | number | 8 | Business hours start (0-23) |
| businessHoursEnd | number | 18 | Business hours end (0-23) |
| is24HourOperation | boolean | false | Enable 24-hour operation (disables business hours filtering) |
| timeFormat | string | '12h' | Time display format ('12h' or '24h') |
| firstDayOfWeek | number | 0 | First day of week (0 = Sunday, 1 = Monday) |
| showWeekends | boolean | true | Show weekend columns |
| maxVisibleEvents | number | 3 | Max events visible per day in month view |
| defaultEventDuration | number | 60 | Fallback duration when service has no duration |
| sidebarPosition | string | 'left' | Mini calendar sidebar position ('left', 'right', 'none') |
| showHeader | boolean | true | Show calendar navigation header |
| showLogo | boolean | true | Show the logo header bar |
| showCustomizationPanel | boolean | true | Show the customization settings panel |
| showCurrentTime | boolean | true | Show current time indicator line |
| highlightToday | boolean | true | Highlight today's column |
| eventBorderRadius | string | 'rounded' | Event card style ('rounded' or 'square') |
| dayStartHour | number | 0 | Hour to start the day view from |
Booking Field Visibility
Control which fields appear in the booking form and view modal:
bookingFields: {
brand: true, // Show in create/edit form
branch: true,
service: true,
date: true,
time: true,
specialist: true,
customer: true,
color: true
}
viewFields: {
brand: true, // Show in view booking modal
branch: true,
service: true,
date: true,
time: true,
specialist: true,
customer: true
}Business Hours
Standard schedule (e.g. 8 AM – 6 PM):
businessHoursStart: 8,
businessHoursEnd: 18,
is24HourOperation: falseOvernight schedule (e.g. 8 PM – 5 AM):
businessHoursStart: 20,
businessHoursEnd: 5,
is24HourOperation: false24-hour operation:
is24HourOperation: trueDropdown Data Formats
Brand
{ "uuid": "b1a2c3d4-...", "name": "RUSH Salon" }Branch
{
"uuid": "br1-0001-...",
"brand_uuid": "b1a2c3d4-...",
"name": "Downtown"
}Service
{
"uuid": "sv-0001",
"name": "Haircut",
"duration": 60,
"brands": ["b1a2c3d4-..."],
"branches": ["br1-0001-...", "br1-0002-..."],
"specialists": ["sp-0001", "sp-0002"]
}duration— service duration in minutes, used to auto-compute booking end timebrands— list of brand UUIDs that offer this servicebranches— list of branch UUIDs where this service is availablespecialists— list of specialist UUIDs who can perform this service
Specialist
{
"uuid": "sp-0001",
"name": "Maria Santos",
"services": ["sv-0001", "sv-0002"],
"brands": ["b1a2c3d4-..."],
"branch_uuid": "br1-0001-...",
"availability": {
"monday": ["09:00", "09:30", "10:00", "10:30", "11:00"],
"tuesday": ["09:00", "09:30", "10:00"],
"wednesday": [],
"thursday": ["14:00", "14:30", "15:00"],
"friday": ["09:00", "09:30", "10:00", "10:30"],
"saturday": [],
"sunday": []
}
}branch_uuid— single branch the specialist is assigned toavailability— time slots per day of week (used by the availability side panel)- When a specialist + date are selected, the availability panel shows available/booked slots
Customer
{
"uuid": "cu-0001",
"name": "John Doe",
"email": "[email protected]",
"phone": "+1 (555) 100-2001",
"gender": "Male",
"address": "123 Main St, New York, NY 10001",
"notes": "Prefers short fade on sides"
}All customer details are displayed in the view booking modal. New customers can be created inline from the booking form.
Time Slots
["09:00", "09:30", "10:00", "10:30", "11:00", "11:30", "12:00", ...]Time slots outside business hours are automatically disabled in the booking form.
Event Data Format
{
"id": "s1",
"title": "Haircut",
"brand": "b1a2c3d4-...",
"branch": "br1-0001-...",
"service": "sv-0001",
"specialist": "sp-0001",
"customer": "cu-0001",
"start": "2025-01-10T09:00:00.000Z",
"end": "2025-01-10T10:00:00.000Z",
"color": "#FF6B00"
}Callbacks
All callbacks can be toggled on/off and have customizable function bodies:
| Callback | Fires When | Payload |
|----------|-----------|---------|
| onEventCreate | New booking saved | { id, title, start, end } |
| onEventUpdate | Booking edited | { id, title, start, end } |
| onEventDelete | Booking deleted | { id } |
| onEventClick | Booking clicked/viewed | { id, title } |
| onDateChange | Calendar navigated | { date, direction } |
| onViewChange | View switched | { view } |
| onSlotClick | Empty slot clicked | { date, hour } |
| onEventDrop | Event drag & dropped | { id, from, to } |
| onSpecialistScheduleCheck | Availability panel loads | { specialist_uuid, specialist_name, date, day, available_slots, booked_slots, total_available, total_booked } |
Custom Callback Functions
Each callback has an editable function body that receives a payload argument:
callbackFns: {
onEventCreate: 'console.log("Event created:", payload)',
onSpecialistScheduleCheck: `
// Fetch real availability from API
fetch('/api/availability?specialist=' + payload.specialist_uuid + '&date=' + payload.date)
.then(r => r.json())
.then(data => console.log(data))
`
}Callback Examples
onEventCreate — Send new booking to API
callbackFns: {
onEventCreate: `
fetch('/api/bookings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
.then(r => r.json())
.then(data => console.log('Booking saved:', data.id))
.catch(err => console.error('Failed to save:', err))
`
}onEventUpdate — Sync changes to backend
callbackFns: {
onEventUpdate: `
fetch('/api/bookings/' + payload.id, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
.then(() => console.log('Booking updated:', payload.id))
`
}onEventDelete — Delete from backend with confirmation log
callbackFns: {
onEventDelete: `
fetch('/api/bookings/' + payload.id, { method: 'DELETE' })
.then(() => console.log('Booking deleted:', payload.id))
`
}onEventClick — Track booking views in analytics
callbackFns: {
onEventClick: `
if (window.gtag) {
gtag('event', 'view_booking', {
booking_id: payload.id,
booking_title: payload.title
})
}
console.log('Viewed booking:', payload.title)
`
}onDateChange — Load events for the new date range
callbackFns: {
onDateChange: `
console.log('Calendar navigated:', payload.direction, 'to', payload.date)
// Fetch events for the new visible range
// fetch('/api/bookings?date=' + payload.date)
// .then(r => r.json())
// .then(events => store.commit('SET_EVENTS', events))
`
}onViewChange — Persist user's preferred view
callbackFns: {
onViewChange: `
localStorage.setItem('calendarView', payload.view)
console.log('View changed to:', payload.view)
`
}onSlotClick — Pre-fill booking from clicked slot
callbackFns: {
onSlotClick: `
console.log('Slot clicked:', payload.date, 'at', payload.hour + ':00')
`
}onEventDrop — Update booking after drag & drop
callbackFns: {
onEventDrop: `
fetch('/api/bookings/' + payload.id + '/reschedule', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ from: payload.from, to: payload.to })
})
.then(() => console.log('Booking rescheduled:', payload.id))
`
}onSpecialistScheduleCheck — Fetch real-time availability from API
callbackFns: {
onSpecialistScheduleCheck: `
console.log('Checking schedule for', payload.specialist_name, 'on', payload.date)
console.log('Day:', payload.day)
console.log('Available slots:', payload.total_available)
console.log('Booked slots:', payload.total_booked)
console.log('Available times:', payload.available_slots.join(', '))
console.log('Booked times:', payload.booked_slots.join(', '))
// Example: Fetch real availability from your API
// fetch('/api/specialists/' + payload.specialist_uuid + '/availability?date=' + payload.date)
// .then(r => r.json())
// .then(data => {
// // Update specialist availability dynamically
// console.log('Real availability:', data)
// })
`
}Disabling Specific Callbacks
Toggle individual callbacks on/off:
// Via config
callbacks: {
onEventCreate: true, // enabled
onEventUpdate: true, // enabled
onEventDelete: false, // disabled
onEventClick: false, // disabled
onDateChange: true, // enabled
onViewChange: false, // disabled
onSlotClick: false, // disabled
onEventDrop: true, // enabled
onSpecialistScheduleCheck: true // enabled
}
// Or at runtime
store.commit('SET_CONFIG', { key: 'callbacks.onEventDelete', value: false })
store.commit('SET_CONFIG', { key: 'callbacks.onEventDrop', value: true })Updating Callback Functions at Runtime
// Update a callback function body
store.commit('SET_CONFIG', {
key: 'callbackFns.onEventCreate',
value: 'fetch("/api/bookings", { method: "POST", body: JSON.stringify(payload) })'
})
// Reset to default
store.commit('SET_CONFIG', {
key: 'callbackFns.onEventCreate',
value: 'console.log("Event created:", payload)'
})Vuex Store API
Mutations
| Mutation | Payload | Description |
|----------|---------|-------------|
| SET_DATE | Date | Set current calendar date |
| SET_VIEW | string | Set view ('month', 'week', 'day') |
| SET_EVENTS | array | Replace all events |
| ADD_EVENT | object | Add a single event |
| UPDATE_EVENT | object | Update event by id |
| DELETE_EVENT | string | Delete event by id |
| SET_CONFIG | { key, value } | Update config (supports dot notation: 'bookingFields.brand') |
| SHOW_EVENT_MODAL | boolean | Show/hide create/edit modal |
| SHOW_VIEW_MODAL | boolean | Show/hide view modal |
Actions
| Action | Payload | Description |
|--------|---------|-------------|
| next | — | Navigate to next period |
| prev | — | Navigate to previous period |
| today | — | Navigate to today |
| changeView | string | Switch view with callback |
| addEvent | object | Add event with callback |
| updateEvent | object | Update event with callback |
| deleteEvent | string | Delete event with callback |
| openCreateModal | slot? | Open create booking modal |
| openEditModal | event | Open edit booking modal |
| openViewModal | event | Open view booking modal |
| moveEvent | { id, newDate, newHour } | Move event (drag & drop) |
| clearAllEvents | — | Remove all events |
| resetEvents | — | Reset to sample data |
Programmatic Config Updates
Update any config at runtime:
// Simple value
store.commit('SET_CONFIG', { key: 'timeFormat', value: '24h' })
// Nested value
store.commit('SET_CONFIG', { key: 'bookingFields.color', value: false })
// Replace dropdown options
store.commit('SET_CONFIG', {
key: 'dropdownOptions.brand',
value: [
{ uuid: 'b1', name: 'My Brand' },
{ uuid: 'b2', name: 'Other Brand' }
]
})
// Replace all events
store.commit('SET_EVENTS', myEventsArray)License
MIT
