Skip to content

refactor(booking): cache guest page formatters and reuse the slots query key - #3046

Merged
tyler-dane merged 1 commit into
mainfrom
claude/eager-mayer-ahsmms
Sep 1, 2026
Merged

refactor(booking): cache guest page formatters and reuse the slots query key#3046
tyler-dane merged 1 commit into
mainfrom
claude/eager-mayer-ahsmms

Conversation

@tyler-dane

Copy link
Copy Markdown
Contributor

Summary

Three behavior-preserving cleanups left by the booking work packages (#3022-#3045). No user-visible change; no new exports beyond one query-key prefix.

1. public-booking.format.ts cached one formatter and rebuilt six. The file already kept a per-timezone cache for the date-key formatter, with a comment stating that constructing Intl.DateTimeFormat is the expensive part. The other six formatters ignored that and constructed a fresh one on every call, and they are the hot ones: formatBookingSlotTime runs once per open slot, formatBookingMonthDayLabel once per day cell, and listBookingWeekdayHeadings builds 14 formatters per call. Generalized the existing cache into a perTimeZoneFormatter helper and routed all seven through it, which also removes six near-identical option literals. Cache keys are IANA zones already validated by validatePublicBookingSearch, so it stays bounded.

2. PublicBookingMonthGrid formatted labels it threw away. aria-label was computed for every cell before the !day.available early return discarded it, so most of a month's ~35-42 labels were built and dropped. Moved it to where it is used.

3. public-booking.query.ts hand-wrote a query key. The reservation mutation invalidated slots with a literal ["public-booking", "slots", slug] instead of publicBookingQueryKeys -- the only such call site in the web package, and one that would silently stop matching if the key factory changed shape. Added a slotsAll prefix, built slots from it, and invalidated through it. The page and reservation queries also carried the same not-found retry predicate verbatim twice; named it retryUnlessNotFound once.

Simplicity

Net reduction. The formatter change removes six duplicated option literals and the bespoke dateKeyFormatters map in favor of one 15-line helper; the query change removes a duplicated 6-line predicate and a magic key array. Nothing new is abstracted that did not already exist in the file: perTimeZoneFormatter is the existing dateKeyFormatter cache with the option set lifted out.

Automated validation

bun run verify selected the web package and passed test:web, type-check, lint, and knip. Because the change touches rendered guest-page output, the Playwright suites verify skipped for a missing browser were run explicitly after installing Chromium: all 30 e2e/booking specs pass, and the booking a11y specs pass.

bun test:a11y also surfaces 6 failures in app-a11y.spec.ts and datepicker-a11y.spec.ts (week view, event forms, sidebar datepicker). These are pre-existing and unrelated: re-running those two spec files on a clean main (00f3e00) with this branch stashed reproduces the same 6 failures. They are calendar-app color-contrast checks that touch no booking code.

Independent review

No /review handoff for this change. Self-review notes on the risky edge: formatGuestTimeZoneLabel depends on Intl throwing for an invalid timezone, and the construction still happens inside its try (nothing is cached on throw), so the fallback-to-raw-zone behavior is preserved. Intl option property order is not significant, so spreading ...options before timeZone is equivalent to the previous literals.

Test plan

bun install
bun type-check
bun lint
bun test:web -- packages/web/src/booking/     # 126 pass, 0 fail
bun run verify                                # test:web, type-check, lint, knip passed
bunx playwright install chromium
bun test:e2e -- e2e/booking/                  # 30 passed
bun test:a11y                                 # 18 passed; 6 pre-existing app/datepicker failures, reproduced on clean main

Generated by Claude Code

Three cleanups left by the booking work packages, all behavior-preserving.

public-booking.format.ts kept one cached formatter for date keys, with a
comment noting that constructing Intl is the expensive part, while six
other formatters rebuilt theirs on every call. The guest page calls them
per open slot and per day cell, so a month render constructed dozens.
Generalize the existing cache into perTimeZoneFormatter and route all
seven through it, which also drops six near-identical option literals.

PublicBookingMonthGrid built the aria-label for every cell before the
unavailable-day early return discarded it, so most of the month's labels
were formatted and thrown away. Build it where it is used.

The reservation mutation invalidated slots with a hand-written key array
instead of publicBookingQueryKeys, so a change to the key factory would
have silently stopped matching. Add a slotsAll prefix, build slots from
it, and invalidate through it. The page and reservation queries also
carried the same not-found retry predicate twice; name it once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXNUAfQd2mEg5u1Q3oFzn9
@tyler-dane
tyler-dane enabled auto-merge (squash) September 1, 2026 10:26
@tyler-dane
tyler-dane merged commit aebe7dc into main Sep 1, 2026
27 checks passed
@tyler-dane
tyler-dane deleted the claude/eager-mayer-ahsmms branch September 1, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants