| Duration | ~60 min in the lesson + ~30 min homework |
| Prerequisites | Checkpoint lesson-8.6 (lesson-5.7 is enough: lead endpoint, spam protection, notifications and the CRM with its business time zone). |
| Checkpoint | lesson-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
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.
- 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.
- 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.
- Pause after Part 5. Run the build prompt in plan mode. Give your own notice and horizon. Apply the migrations.
- Enter your rules at
/admin/bookings/settings. - Compare Claude's seven-day slot list with your rules. Read the result of the two-requests test.
- 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. - 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.
- Do the "Check your work" steps, then tag with the commands under "Recap and next".
Check your work
- On the live domain, open
/book. Expected: only slots your rules allow. - Book a slot. Expected:
/book/confirmedshows weekday, date and time with the time zone named. - Read the visitor email, the owner email, the Telegram message and
/admin/bookings. Expected: the same weekday, date and time in each. - Reload
/book. Expected: that slot is gone. - Ask Claude to run the two-simultaneous-bookings test again. Expected: exactly one success.
- 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.
- Your real rules. Enter the rules you would publish, with every day off you know of in the next two months, then look at
/bookas a customer. Done when: you would be content for a stranger to book any slot shown. - 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.
- 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