User-visible symptom
A consultant authors a 2-hour availability block (e.g. 7:00 PM – 9:00 PM). On the public / explore booking surface:
- The availability calendar shows the 7 PM – 9 PM block correctly (rendered as one merged bar).
- The pricing / booking-length toggle shows one-hour windows (7:00–8:00, 7:30–8:30, 8:00–9:00) instead of the combined 7–9 session.
- Selecting the "2-hour slot" pricing option renders "No available slots for the selected date" (empty), even though the 7–9 block exists and is shown on the availability calendar.
This is the same class of symptom the user reported last session and is confirmed reproducible from the code path.
Root cause (verified)
app/explore/experts/[consultantId]/components/ConsultationPricingToggle.tsx:92-129 computes availableSlots via breakDownSlotsPreservingStatus over the server's 30-minute atoms:
selectedDuration = activePlanOption?.durationInHours ?? 1; // default 1h
availableSlots = breakDownSlotsPreservingStatus(slotsWithAllocation, selectedDuration, timezone);
utils/timeSlotsProcessing.ts:902-988 (breakDownSlotsPreservingStatus):
- Merges the API 30-min atoms into contiguous blocks via
mergeConsecutiveSlots — exact adjacency only (:655).
- Segments each merged block by sliding a fixed-millisecond window of length
durationInHours, but only emitting a window while windowStart + durationInMillis <= slotEnd (:931).
Two concrete failure modes fall out:
- Empty-on-duration (2h shows nothing): the loop only emits exact
durationInHours windows and has no tolerance/fallback. For durationInHours = 2 (durationInMillis = 7,200,000), a merged block must be at least 2 wall-clock-hours long in the fixed-ms sense. If the merged block is even slightly short of that (a non-30-min-boundary end, a blocked atom in the middle that prevents a full merge, or an authored window that is "2h" in some displayed sense but not in absolute ms), every windowStart + 2h <= slotEnd check fails and the function returns [] → "No available slots".
- Wrong-length fragments (default shows 1h windows for a 2h block): with
selectedDuration = 1 (default), the same function slices a 7–9 block into 7–8, 7:30–8:30, 8–9. The authored combined 2-hour session is never offered as one booking unless the user happens to toggle to exactly 2h AND the block is exactly 2h in absolute ms.
Why it matters
- Users cannot book the session the consultant actually offers (the combined 2-hour block), or are told there are no slots at all.
- The public availability calendar and the booking toggle disagree on the same availability data — eroding trust and causing dropped bookings at launch.
- The
durationInHours concept conflates "a pricing tier's length" with "the segment the generator must emit", with no fallback to a shorter-yet-valid window or to the authored block itself.
Suggested fix direction
- Give
breakDownSlotsPreservingStatus (and breakDownSlotsByDuration) a fallback: if no exact durationInHours window fits a merged block, emit the largest fully-contained window(s) rather than empty, or fall back to the authored block that the availability calendar shows.
- Decide the canonical behavior: either (a) the toggle re-segments at the selected duration with a floor that always returns at least the authored contiguous window, or (b) the calendar should not over-merge what the booking layer can't satisfy — the two layers must agree on "bookable".
- Make the merge before segmentation symmetric with the display merge (see the merge-tolerance issue), so a block shown on the calendar is always segmentable into a bookable window of the selected length.
- Add a regression test: authored block 7–9, toggle 1h → emits 7–8/8–9; toggle 2h → emits 7–9 (never empty); toggle 2h with a 1-minute-short block → emits a valid window, not empty.
Related
Terms
pricing toggle · duration · slot window · segmentation · empty slots · booking length · sliding window
User-visible symptom
A consultant authors a 2-hour availability block (e.g. 7:00 PM – 9:00 PM). On the public / explore booking surface:
This is the same class of symptom the user reported last session and is confirmed reproducible from the code path.
Root cause (verified)
app/explore/experts/[consultantId]/components/ConsultationPricingToggle.tsx:92-129computesavailableSlotsviabreakDownSlotsPreservingStatusover the server's 30-minute atoms:utils/timeSlotsProcessing.ts:902-988(breakDownSlotsPreservingStatus):mergeConsecutiveSlots— exact adjacency only (:655).durationInHours, but only emitting a window whilewindowStart + durationInMillis <= slotEnd(:931).Two concrete failure modes fall out:
durationInHourswindows and has no tolerance/fallback. FordurationInHours = 2(durationInMillis = 7,200,000), a merged block must be at least 2 wall-clock-hours long in the fixed-ms sense. If the merged block is even slightly short of that (a non-30-min-boundary end, a blocked atom in the middle that prevents a full merge, or an authored window that is "2h" in some displayed sense but not in absolute ms), everywindowStart + 2h <= slotEndcheck fails and the function returns[]→ "No available slots".selectedDuration = 1(default), the same function slices a 7–9 block into 7–8, 7:30–8:30, 8–9. The authored combined 2-hour session is never offered as one booking unless the user happens to toggle to exactly 2h AND the block is exactly 2h in absolute ms.Why it matters
durationInHoursconcept conflates "a pricing tier's length" with "the segment the generator must emit", with no fallback to a shorter-yet-valid window or to the authored block itself.Suggested fix direction
breakDownSlotsPreservingStatus(andbreakDownSlotsByDuration) a fallback: if no exactdurationInHourswindow fits a merged block, emit the largest fully-contained window(s) rather than empty, or fall back to the authored block that the availability calendar shows.Related
ExpertPricing.tsx/ConsultationPricingToggle.tsxbut only for viewport-fit / nav / availability-dot responsiveness — it does not touch the segmentation logic inbreakDownSlotsPreservingStatus, so this bug is open.mergeConsecutiveSlotsvs 1-min displaymergeSlots).Terms
pricing toggle · duration · slot window · segmentation · empty slots · booking length · sliding window