Skip to content

External booking form

vue
<template>
  <div class="layout">
    <ZaptimeCalendar :config="config" @time-slot-changed="(s) => (slot = s)" />

    <form v-if="slot" @submit.prevent="submit">
      <h2>{{ getFormattedDayInMonth(slot.start) }}, {{ getFormattedTime(slot.start) }}</h2>

      <input v-model="form.firstName" placeholder="First name" />
      <input v-model="form.lastName" placeholder="Last name" />
      <input v-model="form.email" type="email" required placeholder="Email" />
      <input v-model="form.phone" type="tel" placeholder="Phone" />
      <input v-model="company" placeholder="Company" />
      <label><input v-model="terms" type="checkbox" required /> I agree to the terms</label>

      <p v-if="error" role="alert">{{ error }}</p>
      <button type="submit" :disabled="submitting">Book</button>
    </form>

    <p v-if="booked">Booked. Confirmation sent to {{ booked.data.userEmail }}.</p>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref } from "vue";
import { ZaptimeCalendar, book } from "@zaptime/vue3";
import type { ZaptimeConfig } from "@zaptime/vue3";
import {
  SlotNoLongerAvailableError,
  slotNoLongerAvailableText,
  useCalendar,
  useDateFormatters,
} from "@zaptime/core";
import type { ReservationResponse, TimeSlot } from "@zaptime/core";

const config: ZaptimeConfig = {
  token: import.meta.env.VITE_ZAPTIME_TOKEN,
  externalBooking: true,
};

// UUIDs of the custom fields, copied from the booking form in the dashboard
const FIELD_COMPANY = "760b2779-9f16-4eec-b8ab-39750ac4a19f";
const FIELD_TERMS = "d3014753-b096-4ca7-976d-457c4dcc42a6";

const slot = ref<TimeSlot | undefined>();
const form = reactive({ firstName: "", lastName: "", email: "", phone: "" });
const company = ref("");
const terms = ref(false);
const submitting = ref(false);
const error = ref<string>();
const booked = ref<ReservationResponse>();

const { getDays } = useCalendar();
const { getFormattedTime, getFormattedDayInMonth } = useDateFormatters();

async function submit() {
  submitting.value = true;
  error.value = undefined;
  try {
    const res = await book({
      ...form,
      customFields: [
        { uuid: FIELD_COMPANY, value: company.value },
        { uuid: FIELD_TERMS, value: terms.value },
      ],
    });
    if (res.success) {
      booked.value = res;
      slot.value = undefined;
    } else {
      error.value = "Please check the entered details.";
    }
  } catch (e) {
    if (e instanceof SlotNoLongerAvailableError) {
      error.value = slotNoLongerAvailableText(config.locale);
      slot.value = undefined;
      await getDays();
    } else {
      error.value = "Booking failed. Please try again.";
    }
  } finally {
    submitting.value = false;
  }
}
</script>

With externalBooking: true the calendar hides its "Confirm" button and never emits booking-confirmed; your form is the only way to submit. See Working with Time Slots and Custom Fields.