AI Global Academy Join the waitlist

Courses / Bonus: what else you can build

Lesson 10.3 · 60 minOnline booking

Duration~60 min in the lesson + ~30 min homework
PrerequisitesCheckpoint lesson-8.6 (lesson-5.7 is enough: lead endpoint, spam protection, notifications and the CRM with its business time zone).
Checkpointlesson-10.3

What you will have

The site has a /book page where a customer picks a free time slot. The booking creates a lead, sends the existing email and Telegram notifications with the time, appears at /admin/bookings and on the lead card, and the database itself refuses a second booking for the same slot.

Video

The video for this lesson is not recorded yet.

Prompts used in this lesson

Part 5 — Building it

Prompt to Claude
Purpose: let a customer pick a free time slot on my site. The booking must be
a lead in my CRM with a time attached, and no slot may ever be booked twice.

Context: read docs/crm-spec.md (business time zone, pipeline, screens), the
lead endpoint at /api/leads with its validation and Turnstile check, the
notification code from lesson 4.5 and the lead card. Reuse them; do not copy.

Plan first: list the tables and routes you will add and confirm that nothing
existing is renamed or changed. Wait for my approval.

Build:
1. Tables (migrations, no anonymous access): availability_rules (weekday,
   start time, end time, slot length); availability_exceptions (date, note);
   bookings (id, created_at, lead_id, starts_at, ends_at, status "confirmed"
   or "cancelled", cancelled_at). A unique rule on starts_at for confirmed
   bookings only, so a cancelled slot can be booked again. Store with the
   rules a minimum notice in hours and a horizon in days; ask me for both.
2. One server function that returns free slots: rules minus exceptions,
   minus confirmed bookings, minus slots inside the notice or beyond the
   horizon. Rule times are in the business time zone, also across a clock
   change; moments are stored in UTC.
3. GET /api/bookings/slots returns free slots only, no customer data.
4. Public page /book: pick a day, pick a slot, then name, email, phone and
   message as on the lead form, with the same validation and Turnstile. It
   says "Times are shown in <city> time."
5. POST /api/bookings: validates like /api/leads, re-checks the slot against
   the rules, then creates the lead (created_via "form", landing_page
   "/book") and the booking as one operation, so a rejected booking leaves no
   lead behind. If the database rejects the slot as taken, answer "just
   taken" and the page shows fresh slots. On success go to /book/confirmed.
6. The visitor's confirmation email and my owner email and Telegram message
   include the weekday, date and time in the business time zone.
7. If the project has the section 6 data layer events, the booking form
   pushes the same ones with form_id "booking". No new event names.
8. CRM: /admin/bookings lists upcoming bookings with a "Cancel" action;
   /admin/bookings/settings edits rules, notice, horizon and days off; the
   lead card shows the lead's booking; "Bookings" in the admin navigation.

Do not build: customer self-service cancel or change, reminders, calendar
sync, payment, more than one booking per slot.

Verify before reporting: run the build and existing tests; write and run a
test that sends two bookings for the same slot at the same moment and shows
one success, one "just taken", one confirmed booking and one lead; list the
free slots for the next seven days; make one booking on the local site
through the browser connection. Say what you could not verify.

Do along

Work on your own project. Pause the video where a step says so.

  1. Pause after Part 2. Write your five rules on paper: days and hours, slot length, minimum notice, how far ahead, days off in the next month. If you are unsure, start strict; you can open more later.
  2. Pause after Part 3. Decide: are times shown in the business time zone (service at a place) or also in the visitor's (service online)? Adapt point 5 of the prompt if it is the second.
  3. Pause after Part 5. Run the build prompt in plan mode. Give your own notice and horizon. Apply the migrations.
  4. Enter your rules at /admin/bookings/settings.
  5. Compare Claude's seven-day slot list with your rules. Read the result of the two-requests test.
  6. Pause after Part 6. Make one booking as a customer with a second email address. Read the time in the confirmation page, both emails, Telegram and /admin/bookings.
  7. Commit, push, apply the migrations to the live database, and repeat one booking on the live domain. Cancel the test bookings and delete the test leads.
  8. Do the "Check your work" steps, then tag with the commands under "Recap and next".

Check your work

  1. On the live domain, open /book. Expected: only slots your rules allow.
  2. Book a slot. Expected: /book/confirmed shows weekday, date and time with the time zone named.
  3. Read the visitor email, the owner email, the Telegram message and /admin/bookings. Expected: the same weekday, date and time in each.
  4. Reload /book. Expected: that slot is gone.
  5. Ask Claude to run the two-simultaneous-bookings test again. Expected: exactly one success.
  6. Cancel the booking in the CRM and reload /book. Expected: the slot is back.

Common problems

  • Times are right on the page but off in the email or Telegram → a message formats the stored moment without the business time zone → "Use one shared function to format booking times in the business time zone everywhere, and show me the four outputs for one booking."
  • The two-requests test shows two confirmed bookings → the unique rule is missing in the database → "Show me the migration with the unique rule on confirmed bookings and rerun the test."
  • No slots at all → rules entered wrongly, a zero horizon, or migrations not applied to the live database → ask Claude to print the free slots and explain each exclusion.

Homework

About 30 minutes, on your own, after the lesson. No other lesson depends on it.

  1. Your real rules. Enter the rules you would publish, with every day off you know of in the next two months, then look at /book as a customer. Done when: you would be content for a stranger to book any slot shown.
  2. Your confirmation email. Have Claude show you the confirmation email's text, rewrite it in your own words, and send yourself a test booking. Read it on a phone. Done when: it answers "when, where, what do I need to do, how do I change it". Commit without a tag.
  3. Your by-hand procedure. Write docs/booking-procedure.md: what you do when a customer asks to cancel or move a booking, when you must cancel yourself, and whether you send a reminder. Done when: someone covering for you could follow it. Commit without a tag.

Save your work

git add -A
git commit -m "Lesson 10.3: online booking with availability rules, bookings in the CRM"
git tag lesson-10.3
git push
git push --tags