Skip to content

feat(booking): offer an optional phone call for online events - #70

Merged
shockalotti merged 1 commit into
Calnode:mainfrom
marijnbent:feat/telephone-booking-choice
Sep 21, 2026
Merged

shockalotti merged 1 commit into
Calnode:mainfrom
marijnbent:feat/telephone-booking-choice

Conversation

@marijnbent

@marijnbent marijnbent commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Some guests would rather receive a phone call than join an online meeting. This adds an event-level option for Google Meet and Microsoft Teams events. It is off by default.

When enabled, the booking page and Web Component embed show an optional phone field. A valid number changes that booking to a phone call. Leaving it empty keeps the event's online meeting location. The server validates the choice and stores the selected location type with the booking, so later event edits do not turn a phone booking into an online meeting.

The chosen location is also used for confirmation, calendar reconciliation and rescheduling. A phone booking skips meeting-link generation. New booker-facing text is included in all eight locales; the translations would benefit from native review.

Validation: make build, go test ./..., pnpm check and pnpm test:visual pass. Tests cover a valid number, an empty number, invalid input and attempts to request a phone call when the option is disabled. Screenshots below use local demo data.

This adds migration 00061. It is independent of the accent-color PR and may need renumbering before merge.

Before

Before

After

After

@github-actions

github-actions Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA. ✅
Posted by the CLA Assistant Lite bot.

@marijnbent

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@marijnbent
marijnbent marked this pull request as ready for review September 20, 2026 18:57
@pullfrog

pullfrog Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

This repo's model isn't fully set up yet.

OPENAI_COMPATIBLE_MODEL env var is required when the model is set to "openai-compatible/byok". set it to the model ID served by your OpenAI-compatible endpoint (e.g. a Cloudflare AI Gateway or DashScope model). see https://docs.pullfrog.com/openai-compatible for setup.

Configure model →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using OpenAI-compatible | 𝕏

@shockalotti

Copy link
Copy Markdown
Contributor

@pullfrog please review this PR

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — the lifecycle is sound; two minor suggestions inline.

Reviewed changes

  • Optional phone choice — for Google Meet / Teams event types, the booking page and embed widget accept an optional number; a valid one stores location_type = "phone" and location_value = "tel:<number>" and skips meeting-link generation, while an empty one keeps the video location.
  • Per-booking location type — bookings.location_type (migration 00061) is written at create time and preferred over the event type in the manage page, calendar reconciliation and paid-booking confirmation (COALESCE(NULLIF(b.location_type, ''), et.location_type)), so later event edits don't turn a phone booking into a video one.
  • Surfaces + i18n — allow_phone_call added to the event-type API/editor, the public payload, book.html and embed.js; phone_call_number added to all eight locales.
  • Tests — TestBookingTelephoneChoice covers phone / empty / invalid / disabled inputs; I ran it and ./internal/i18n/, both pass.

I traced every post-creation path that could touch a booking's location (confirmation minting, calendar reconcile, Stripe confirmation, reschedule, reassign, reminders) and found no path that mints a link for a phone booking or overwrites its stored location, and no path that reads the event type's location type where it should read the booking's. The remaining notes are minor.

ℹ️ Migration number collides with the accent-color work

main currently tops out at 00059; this PR adds 00061 and skips 00060. If the accent-color branch also lands a 00061, goose fails on the duplicate version. The PR body already flags this — resolving it (renumber to the next free version after the base branch is final, or coordinate the order) needs to happen before merge.

Technical details
# Migration ordering

## Affected sites
- `internal/db/migrations/00061_telephone_booking_choice.sql` — new migration.

## Required outcome
- Exactly one migration owns version `00061` after all pending branches land on `main`; goose must apply both 00060 and 00061 without a duplicate-version error.

## Open questions for the human
- Is `00060` reserved for the accent-color PR, or should this one be renumbered?

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using opencode-go/deepseek-v4.1-flash | 𝕏

</div>

{#if isOnlineMeeting(form.location_type)}
<label class="flex items-center gap-3 text-sm"><input type="checkbox" bind:checked={allowPhoneCall} />Let invitees choose a phone call by entering their number</label>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of this form uses the shadcn Checkbox + Label pair (see show_taken_slots just above at lines 611-612), whereas this is a raw native <input type="checkbox"> — it renders with browser-default styling and misses the shared focus/disabled states. It also sits above the Location section header even though the option only applies when the location is an online meeting; consider rendering the shadcn pair next to the location select instead.

is_active: form.is_active,
is_public: form.is_public,
show_taken_slots: form.show_taken_slots,
allow_phone_call: allowPhoneCall,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allow_phone_call is sent as the raw state value, so it stays true in the DB when the operator switches the location away from an online meeting — the checkbox is hidden but allowPhoneCall is never cleared, and the flag silently reappears if the type is switched back. All read paths gate on onlineMeetingLocation, so this isn't exploitable today, but sending allowPhoneCall && isOnlineMeeting(form.location_type) (and resetting the state when location_type changes) keeps the stored value honest.

@shockalotti
shockalotti merged commit 6eef3a3 into Calnode:main Sep 21, 2026
2 of 3 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants