CU-07: Host-side booking management: book-on-behalf, no-show, notes, guests, cancel/reschedule

Priority: P1 Accounts/sessions: P1 host (ravikantguptaofficial@gmail.com) logged into Calendo via Google; P1's Google Calendar connected; P1's Gmail is the host notification inbox. Invitees use plus-aliases of P1's Gmail. P3 (everythingaichannelemail@gmail.com) is the real guest. Also needs P1's Gmail (mail.google.com) and Google Calendar (calendar.google.com) reachable in the same browser for L3. Parallel-safe: Yes — all created objects are RUNID-scoped; no global availability is rewritten. Exclusive (rewrites global host availability?): No. Estimated time: ~40 minutes for one full run. L3 reality checks: Yes — invitee confirmation email (Gmail), guest invitation email (Gmail + P3 inbox if accessible), real Google Calendar event created/moved/deleted, cancellation email, reschedule email.

Goal

This suite proves the host (meeting owner) can fully manage bookings from the Calendo dashboard end-to-end: create a booking on an invitee's behalf, browse and filter the bookings list, open a booking's detail sidebar and read its fields, mark a past booking as a no-show (and see it counted in analytics), attach an internal note that survives reload, attach an extra guest to a booking, and cancel and reschedule bookings — with each operation reflected in the real Google Calendar and the real Gmail inbox. This is the daily operational surface a real host lives in; if any of it silently fails, the host loses trust that Calendo is the source of truth for their calendar.

Preconditions

Test data

Pick a fresh RUNID at execution time, e.g. a UTC timestamp 20260601-1530. Embed it in every created name/slug/email so reruns never collide.

Steps

Phase 0 — Set up the event type and capture host slug

  1. Action: Go to https://calendo.dev/dashboard/. Confirm the dashboard loaded (sidebar visible, #appLayout). On the Overview, locate the share/booking URL text (#bookingLinkValue) and record the value after user= as <HOST_SLUG>. Expect: A non-empty booking link like https://calendo.dev/booking/?user=<HOST_SLUG>. [L1]
    • Capture screenshot: cu07-00-dashboard-overview.
  2. Action: On the Overview, click the "+ New" event-type button (#overviewCreateEtBtn). In the modal (#editEtModal) fill name = CU07 Host Mgmt <RUNID> (#editEtName), select duration 30 (#editEtDuration), and set description CU-07 runbook event (#editEtDesc). Expect: Modal open with fields populated. [L1]
  3. Action: Before saving, enable "Allow guests" (#etAllowGuestsToggle — the toggle near the "Allow guests" label). Then click Create/Save (#editEtSaveBtn). Expect: Modal closes; the new event type CU07 Host Mgmt <RUNID> appears in the overview event-type list (#overviewEventTypes). [L1][L2]
    • Note the derived slug cu07-host-mgmt-<RUNID>.
    • Capture screenshot: cu07-01-event-type-created.

Phase 1 — Book on behalf (future) + L3 calendar/email

  1. Action: Open the Bookings tab (sidebar link .sidebar-nav a[data-tab="bookings"]). Expect: Bookings panel active (#panel-bookings.active); filter bar shows Upcoming/Past/Cancelled (.filter-btn[data-filter="upcoming"] active by default); the "+ Book on Behalf" button is visible (#bookOnBehalfBtn). [L1]
  2. Action: Click "+ Book on Behalf" (#bookOnBehalfBtn). Expect: The book-on-behalf form appears (#bookOnBehalfForm becomes visible). [L1]
  3. Action: In the form, select the event type CU07 Host Mgmt <RUNID> in the Event Type dropdown (#bobEventType); set Date & Time (#bobStartTime, a datetime-local interpreted in the HOST's timezone) to a clearly future, in-availability slot (e.g. tomorrow at 10:00 AM host time); enter Invitee Name CU07 OnBehalf <RUNID> (#bobName) and Invitee Email ravikantguptaofficial+inv-onbehalf-<RUNID>@gmail.com (#bobEmail); leave Notes (#bobNotes) empty. Expect: All fields populated. [L1]
  4. Action: Click "Create Booking" (#bobSubmitBtn). Expect: A toast "Booking created" appears; the form hides; the bookings list refreshes and shows a meeting row containing CU07 OnBehalf <RUNID> under the Upcoming filter. [L1][L2]
    • Capture screenshot: cu07-02-book-on-behalf-created.
    • Why this order: the dashboard reloads bookings after creation, so the new row should be present before you assert.
  5. Action: Reload the page (https://calendo.dev/dashboard/) and return to Bookings → Upcoming. Expect: CU07 OnBehalf <RUNID> still present after reload (persistence). [L2]

Phase 2 — Create the PAST booking (no-show + notes target)

  1. Action: Click "+ Book on Behalf" again (#bookOnBehalfBtn). Select CU07 Host Mgmt <RUNID> (#bobEventType); set Date & Time (#bobStartTime) to ~10 minutes in the PAST (today's date, current host time minus ~10 min); enter Invitee Name CU07 NoShow <RUNID> (#bobName) and Email ravikantguptaofficial+inv-noshow-<RUNID>@gmail.com (#bobEmail). Click "Create Booking" (#bobSubmitBtn). Expect: Toast "Booking created". [L1][L2]
    • Why a past time: the "Mark no-show" action only renders for past confirmed Calendo bookings, and notes are most realistically exercised on a completed meeting. If the server rejects a past start_time, fall back to the nearest-allowed near-future time, wait until it is past, then proceed — and note this in the report.
  2. Action: Switch to the Past filter (.filter-btn[data-filter="past"]). Expect: CU07 NoShow <RUNID> appears in the Past list. [L1][L2]
    • Capture screenshot: cu07-03-past-booking.

Phase 3 — Filters sanity

  1. Action: Click Upcoming (.filter-btn[data-filter="upcoming"]). Expect: CU07 OnBehalf <RUNID> is present; CU07 NoShow <RUNID> is NOT present (it is past). [L1]
  2. Action: Click Cancelled (.filter-btn[data-filter="cancelled"]). Expect: Neither CU07 OnBehalf <RUNID> nor CU07 NoShow <RUNID> appears (nothing cancelled yet); empty-state or unrelated rows only. [L1]
  3. Action: Return to Upcoming and type CU07 OnBehalf <RUNID> into the meeting search box (#meetingSearch). Expect: Only the matching booking row remains visible; clearing the search restores the list. [L1]

Phase 4 — Detail sidebar fields

  1. Action: On the Past filter, click the CU07 NoShow <RUNID> meeting row (.meeting-row, which calls openMeetingDetail(...)). Expect: The detail sidebar slides in (#meetingDetailSidebar gains class open; overlay #mdsOverlay becomes visible). Header shows status "Past meeting" (#mdsStatus), event title CU07 Host Mgmt <RUNID> (#mdsTitle), and the date/time line (#mdsDatetime). [L1]
  2. Action: Read the sidebar body (#mdsBody). Expect: An "Invitee" section showing CU07 NoShow <RUNID> (with avatar initials, .mds-invitee-avatar) and the invitee email ravikantguptaofficial+inv-noshow-<RUNID>@gmail.com; a "Location" section; a "Notes" section with an empty textarea (#mdsNotesTextarea) and a "Save notes" button; and a "Timeline" section beginning with "Event booked by …". [L1]
    • Capture screenshot: cu07-04-detail-sidebar.

Phase 5 — Internal note persists across reload

  1. Action: In the Notes textarea (#mdsNotesTextarea), type Internal note CU07 <RUNID> — invitee did not join. Click "Save notes" (the saveSidebarNotes(...) button in the Notes section). Expect: Toast "Notes saved". [L1]
  2. Action: Close the sidebar (closeMeetingDetail() via the × .mds-close or overlay), then reload the dashboard, return to Bookings → Past, and reopen the CU07 NoShow <RUNID> row. Expect: The Notes textarea (#mdsNotesTextarea) is pre-filled with Internal note CU07 <RUNID> — invitee did not join. [L2]
    • Capture screenshot: cu07-05-note-persisted.

Phase 6 — Mark no-show + analytics reflects it

  1. Action: Before marking, open the Analytics tab (.sidebar-nav a[data-tab="analytics"]) and record the current No-Show Rate value (#statNoShowRate) and Total Bookings (#statTotal). Expect: Stats render (panel #panel-analytics.active, #analyticsStats visible). Record the baseline No-Show Rate %. [L1]
    • Capture screenshot: cu07-06-analytics-before.
  2. Action: Go back to Bookings → Past, open the CU07 NoShow <RUNID> row, and click "Mark no-show" in the sidebar actions (#mdsActions → the button rendered by markNoShow(<id>), orange-bordered "Mark no-show"). Expect: A confirm dialog (#confirmDialog) appears asking to mark the booking with CU07 NoShow <RUNID> as a no-show. [L1]
  3. Action: Confirm in the dialog (#dialogConfirm). Expect: Toast "Booking marked as no-show"; the sidebar closes; the bookings list refreshes. [L1][L2]
  4. Action: Re-open Analytics (data-tab="analytics"). Expect: The No-Show Rate (#statNoShowRate) is now greater than the baseline recorded in step 18 (it should increase because one confirmed booking became a no-show). Also confirm the per-event-type table row for CU07 Host Mgmt <RUNID> shows a non-zero No-Show Rate. [L2]
    • Capture screenshot: cu07-07-analytics-after-noshow.
    • Note: the dashboard UI surfaces no-show as a RATE (%), not a raw count. The raw no_show_count exists in the /api/analytics response but is not shown in the UI — do not expect a numeric count widget.
  5. Action (regression confirm): Go to Bookings, re-open the CU07 NoShow <RUNID> row. Expect: Status is no longer eligible for "Mark no-show" (the action no longer renders, since status is now no_show); the row no longer appears under Upcoming. [L2]

Phase 7 — Guest on a booking + L3 guest email

Host-side note: Calendo has NO dashboard control to add a guest to an already-created booking, and book-on-behalf does not accept guests. Guests are attached at booking-creation time on the public booking page when the event type allows guests. This phase therefore creates a public booking WITH a guest as the realistic way to exercise guest handling, then verifies the host sees the guest in the booking detail.

  1. Action: In a new tab, open the public booking page https://calendo.dev/booking/?user=<HOST_SLUG>&event=cu07-host-mgmt-<RUNID>. Expect: The booking view loads (#booking-view) with a calendar. [L1]
  2. Action: Pick the first available day (.calendar-day.available) and first time slot (.time-slot). In the confirm form fill Name CU07 Guest <RUNID> (#input-name) and Email ravikantguptaofficial+inv-guest-<RUNID>@gmail.com (#input-email). In the "Additional Guests" section (#guests-section, visible because allow_guests is ON), click "+ Add guest" (#add-guest-btn) and type everythingaichannelemail@gmail.com into the new guest input (.guest-email-input). Expect: Guest row added; form ready. [L1]
    • Capture screenshot: cu07-08-public-booking-with-guest.
  3. Action: Click "Confirm Booking" (#btn-confirm). Expect: Confirmation screen with title containing "Booking Confirmed" (.confirmation-title), and a Cancel/Reschedule manage area (#confirmation-manage). [L1][L2]
  4. Action: Back in the dashboard tab, reload and open Bookings → Upcoming, then open the CU07 Guest <RUNID> row. Expect: The sidebar Invitee section also lists the guest everythingaichannelemail@gmail.com (rendered from guest_emails in #mdsBody). [L2]
    • Capture screenshot: cu07-09-guest-in-sidebar.

Phase 8 — Cancel from the dashboard + L3

  1. Action: Create the cancel target as a future public booking (so it has cancel/reschedule tokens): open https://calendo.dev/booking/?user=<HOST_SLUG>&event=cu07-host-mgmt-<RUNID>, pick an available slot, fill Name CU07 Cancel <RUNID> (#input-name) and Email ravikantguptaofficial+inv-cancel-<RUNID>@gmail.com (#input-email), click "Confirm Booking" (#btn-confirm). Expect: "Booking Confirmed". [L1]
  2. Action: In the dashboard, Bookings → Upcoming, open the CU07 Cancel <RUNID> row and click "Cancel" in the sidebar actions (#mdsActions → red "Cancel" button calling cancelBooking(<id>)). Expect: Confirm dialog (#confirmDialog) asking to cancel the booking with CU07 Cancel <RUNID>. [L1]
  3. Action: Confirm (#dialogConfirm). Expect: Toast "Booking cancelled"; sidebar closes; list refreshes. [L1][L2]
  4. Action: Click the Cancelled filter (.filter-btn[data-filter="cancelled"]). Expect: CU07 Cancel <RUNID> appears with a cancelled badge (.badge-cancelled); switching to Upcoming (.filter-btn[data-filter="upcoming"]) shows it is gone. [L2]
    • Capture screenshot: `cu07-10-cancelled.

Phase 9 — Reschedule + L3

Host-side note: Calendo has NO dashboard "Reschedule" button (the sidebar only offers Cancel / Mark no-show). The host reschedules by following the reschedule link from the booking confirmation page or the confirmation email (/booking/reschedule.html?token=...). This phase uses that supported path.

  1. Action: Create the reschedule target as a future public booking: open https://calendo.dev/booking/?user=<HOST_SLUG>&event=cu07-host-mgmt-<RUNID>, pick an available slot, fill Name CU07 Resched <RUNID> (#input-name) and Email ravikantguptaofficial+inv-resched-<RUNID>@gmail.com (#input-email), click "Confirm Booking" (#btn-confirm). On the confirmation screen, locate the manage links (#confirmation-manage) and note/click the "Reschedule" link (a[href*="/booking/reschedule.html"]). Expect: Confirmation shown; Reschedule link present. [L1]
    • Record the original date/time of this booking (you will verify it MOVED).
    • Capture screenshot: cu07-11-resched-confirmation.
  2. Action: Follow the Reschedule link to https://calendo.dev/booking/reschedule.html?token=.... Expect: Reschedule UI loads (#reschedule-ui); current booking shows event CU07 Host Mgmt <RUNID> (#current-event) and the original datetime (#current-datetime); a calendar (#calendar) is visible. [L1]
  3. Action: Pick a DIFFERENT available day (.calendar-day.available) and a different time slot (.time-slot) from the original, then click "Confirm Reschedule" (#btn-reschedule). Expect: Success state (#success-state) with title "Booking Rescheduled" (.confirmation-title). [L1][L2]
    • Record the NEW date/time.
    • Capture screenshot: `cu07-12-rescheduled.
  4. Action: In the dashboard, reload and open Bookings → Upcoming, open the CU07 Resched <RUNID> row. Expect: The sidebar #mdsDatetime shows the NEW time, not the original. [L2]

L3 reality checks

Run these after the corresponding phases. Use the same browser, signed into Google as ravikantguptaofficial@gmail.com. If a Google re-auth wall blocks you, do NOT log in with a password — flag it and mark the check BLOCKED.

A. Book-on-behalf invitee email (Phase 1).

B. Book-on-behalf Google Calendar event (Phase 1).

C. Guest invitation email (Phase 7).

D. Cancellation email + calendar removal (Phase 8).

E. Reschedule email + calendar moved (Phase 9).

Note on no-show (Phase 6): there is no external email or calendar mutation expected from marking no-show — it is an internal status change verified at L2 via the analytics rate. No L3 for no-show.

Cleanup

Leave both host accounts clean. Do all of this as P1 in the dashboard unless noted.

  1. Cancel remaining active bookings created by this run: Bookings → Upcoming. For each of CU07 OnBehalf <RUNID>, CU07 Guest <RUNID>, CU07 Resched <RUNID> still active: open the row → sidebar "Cancel" (cancelBooking) → confirm (#dialogConfirm). (CU07 Cancel <RUNID> is already cancelled; CU07 NoShow <RUNID> is a past no-show and needs no action.)
  2. Delete the event type: Event Types / Overview → open CU07 Host Mgmt <RUNID> → delete it (edit panel delete control). This removes it from the public booking surface.
  3. Verify Google Calendar is clean: In calendar.google.com, confirm no remaining future events titled/containing CU07 ... <RUNID> (cancelling Calendo bookings should remove them; manually delete any orphans).
  4. Gmail: The plus-aliased emails remain in P1's inbox for audit — optionally label/archive anything matching <RUNID>. Do not delete unless instructed; they are evidence.
  5. P3 guest invite: If you confirmed it in everythingaichannelemail@gmail.com, optionally archive that thread; no account changes needed.

Pass/Fail criteria

The run PASSES only if ALL of the following hold:

Evidence to capture

Manual residue / cannot-verify