Requested in #16.
Let hosts duplicate an existing event type from the Event types list, so building several variants that share configuration (location, duration, buffers, booking window) doesn't mean re-entering everything by hand.
The thing to get right
An event type is not just its row in event_types. It owns:
| What |
Where |
| Intake questions |
questions.event_type_id |
| Hosts, with role + routing priority |
event_type_hosts |
| Event-scoped availability |
availability_rules.event_type_id (NULL means the global default) |
| Per-event email / reminder copy |
event_types.msg_*, event_type_reminders |
A row-only copy produces a clone that looks correct in the editor while having no questions, no hosts, and silently falling back to global availability. That is worse than not shipping the feature, because the failure is invisible until a booker hits the page or a booking lands on the wrong person.
Scope
Notes for whoever picks this up
- The whole copy must be one transaction. The DB pool is
MaxOpenConns(1); never query inside an open rows cursor, materialise to a slice first.
- UI conventions are in
CLAUDE.md (shadcn-svelte, no hand-rolled buttons/modals).
Requested in #16.
Let hosts duplicate an existing event type from the Event types list, so building several variants that share configuration (location, duration, buffers, booking window) doesn't mean re-entering everything by hand.
The thing to get right
An event type is not just its row in
event_types. It owns:questions.event_type_idevent_type_hostsavailability_rules.event_type_id(NULL means the global default)event_types.msg_*,event_type_remindersA row-only copy produces a clone that looks correct in the editor while having no questions, no hosts, and silently falling back to global availability. That is worse than not shipping the feature, because the failure is invisible until a booker hits the page or a booking lands on the wrong person.
Scope
POST /v1/event-types/{slug}/duplicate(or equivalent) copying the row plus all four child sets aboveintro-calltointro-call-copy, then-2etc. on collision; the column isUNIQUE)is_active = 0so a half-configured clone isn't live at a guessable URLprice_cents/currencyfaithfully rather than zeroing them, so nobody accidentally publishes a paid event type at zerobookings.event_type_idisON DELETE RESTRICT)Tooltip)Notes for whoever picks this up
MaxOpenConns(1); never query inside an open rows cursor, materialise to a slice first.CLAUDE.md(shadcn-svelte, no hand-rolled buttons/modals).