Skip to content

fix: show schedule exception reasons in the slot tooltip#16426

Open
Valyrian-Code wants to merge 7 commits into
ohcnetwork:developfrom
Valyrian-Code:issues/show-schedule-exception-reasons
Open

fix: show schedule exception reasons in the slot tooltip#16426
Valyrian-Code wants to merge 7 commits into
ohcnetwork:developfrom
Valyrian-Code:issues/show-schedule-exception-reasons

Conversation

@Valyrian-Code

@Valyrian-Code Valyrian-Code commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #16415

Proposed Changes

On the user Availability / Schedule view, when a schedule exception reduces a session's available slots, the slot line is shown struck-through with a tooltip trigger — but hovering shows an empty tooltip; the exception reason is never displayed.

Root causecomputeAppointmentSlots (src/pages/Scheduling/utils.ts) drops conflicting slots and always returns isAvailable: true, exceptions: []:

let conflicting = false;
for (const exception of exceptions) { /* ... */ if (overlap) { conflicting = true; break; } }
if (!conflicting) {
  slots.push({ ..., isAvailable: true, exceptions: [] });   // conflicting slots dropped
}

So every slot has an empty exceptions, and ScheduleHome computes hasExceptions from computedSlots.flatMap((s) => s.exceptions) — which is therefore always false, so the TooltipContent with the reasons never renders. The VirtualSlot.isAvailable / .exceptions fields the consumer relies on were effectively dead.

Fix — attach the overlapping exception(s) to each slot and mark conflicting slots unavailable instead of dropping them:

const slotExceptions = exceptions.filter((exception) => {
  const exceptionStartTime = parse(exception.start_time, "HH:mm:ss", referenceDate);
  const exceptionEndTime = parse(exception.end_time, "HH:mm:ss", referenceDate);
  return exceptionStartTime < slotEndTime && exceptionEndTime > time;
});

slots.push({
  start_time: format(time, "HH:mm") as Time,
  end_time: format(slotEndTime, "HH:mm") as Time,
  isAvailable: slotExceptions.length === 0,
  exceptions: slotExceptions,
});

ScheduleHome is the only consumer; it reads slot.isAvailable (filtered for the count) and slot.exceptions (for the tooltip). The available-slot count is unchanged (filter(isAvailable)), and the tooltip now shows the reason(s).

Reproduce

  1. Create an appointment session (e.g. 09:00–12:00, 30-min slots).
  2. Add an unavailable exception overlapping part of it (e.g. 10:00–10:30) on that day.
  3. Open the user's Availability view on that day → the session shows a struck-through slot line; hovering it now shows Exceptions: <reason> (previously empty).

Tagging: @ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate the bug. A faithful E2E needs an availability + an overlapping unavailable exception (date-pickers) + a hover-tooltip assertion on a specific date — a brittle flow. I verified locally that the existing userScheduleCreation spec still passes (slot count unchanged). Happy to add an E2E if you can advise on a non-flaky approach for the tooltip (see Schedule exception reasons never shown: computeAppointmentSlots leaves tooltip empty #16415).
  • Update product documentation. (N/A — bug fix.)
  • Ensure that UI text is placed in I18n files. (No new strings; uses existing exceptions key + exception reasons.)
  • Prepare a screenshot or demo video for the changelog entry. (Repro steps above.)
  • Request peer reviews.
  • Complete QA on mobile devices.
  • Complete QA on desktop devices.
  • Add or update Playwright tests for related changes. (See first item — pending a non-flaky approach.)

Summary by CodeRabbit

  • New Features

    • Schedule detail popovers can now include unavailable appointment slots and show overlapping exception details for each slot.
  • Bug Fixes

    • Improved slot generation and time parsing to correctly handle short time formats (e.g., "HH:mm") and report availability/exceptions more accurately, reducing incorrect availability displays when exception windows overlap.

computeAppointmentSlots dropped conflicting slots and always returned
isAvailable=true / exceptions=[], so ScheduleHome's hasExceptions was
always false and the slot tooltip rendered empty. Attach overlapping
exceptions to each slot and mark conflicting ones unavailable; the
available-slot count is unchanged (filter(isAvailable)) and the tooltip
now shows the exception reasons. See issue ohcnetwork#16415.
@Valyrian-Code Valyrian-Code requested review from a team and Copilot June 3, 2026 13:07
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

computeAppointmentSlots now pre-parses exceptions, attaches overlapping exceptions to each generated slot (setting isAvailable accordingly), and accepts includeUnavailableSlots to optionally return unavailable slots; ScheduleHome requests unavailable slots for tooltip rendering.

Changes

Slot Exception Filtering and Attachment

Layer / File(s) Summary
API: add includeUnavailableSlots parameter
src/pages/Scheduling/utils.ts
computeAppointmentSlots signature now accepts includeUnavailableSlots?: boolean (default false) to control whether unavailable slots with their overlapping exceptions are returned.
Core: pre-parse exceptions and attach overlaps
src/pages/Scheduling/utils.ts
Exceptions are parsed once into parsedExceptions; each slot computes slotExceptions by filtering overlapping parsed exceptions, sets isAvailable based on whether slotExceptions is empty, and populates the slot's exceptions array. Unavailable slots are only included in the returned list when includeUnavailableSlots is true.
Caller: request unavailable slots for tooltip
src/components/Schedule/ScheduleHome.tsx
ScheduleHome (ScheduleTemplateAvailabilityItem) now calls computeAppointmentSlots(..., { includeUnavailableSlots: true }) so unavailable slots and their exception reasons are available for tooltip rendering.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'fix: show schedule exception reasons in the slot tooltip' accurately describes the main change: enabling exception reasons to appear in tooltips by fixing how computeAppointmentSlots handles conflicting slots.
Description check ✅ Passed The PR description is comprehensive and well-structured, providing root cause analysis, code examples, reproduction steps, and addressing all relevant merge checklist items with clear justifications.
Linked Issues check ✅ Passed The PR implementation fully addresses all objectives from issue #16415: computeAppointmentSlots now attaches overlapping exceptions to each slot, marks conflicting slots as unavailable, preserves available-slot counts, and restores tooltip functionality.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #16415; modifications to computeAppointmentSlots signature and its call in ScheduleHome are necessary to attach exceptions to slots and display them in tooltips as intended.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Updates appointment slot computation to attach overlapping exceptions to each slot and mark slots as unavailable instead of skipping them.

Changes:

  • Replace per-slot conflict detection loop with an exceptions.filter(...) to gather overlapping exceptions.
  • Always push slots, using isAvailable derived from whether overlaps exist.
  • Populate each slot’s exceptions array with the overlaps found.

Comment thread src/pages/Scheduling/utils.ts Outdated
Comment thread src/pages/Scheduling/utils.ts Outdated
@greptile-apps

greptile-apps Bot commented Jun 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a bug where schedule exception reasons were never shown in the slot tooltip on the Availability view. The root cause was that computeAppointmentSlots discarded any slot that overlapped an exception rather than marking it unavailable and attaching the exception, so downstream consumers always received exceptions: [].

  • The new implementation keeps every slot and uses Array.filter to collect all exceptions that overlap a given slot's time window, then sets isAvailable: slotExceptions.length === 0 and exceptions: slotExceptions.
  • The available-slot count in ScheduleHome is unchanged (it still filters by isAvailable), and the tooltip's hasExceptions flag now correctly evaluates to true when overlapping exceptions exist.

Confidence Score: 5/5

Safe to merge — the change is a small, self-contained fix with no side-effects on the available-slot count or other callers.

The diff is minimal and the logic is correct: every slot is now emitted with the right isAvailable flag and a properly populated exceptions array. The ScheduleHome consumer deduplicates exceptions via new Set, which works on object identity — and since Array.filter preserves the same references from the input exceptions array, a single exception that overlaps multiple slots will still be deduplicated correctly. No other callers of computeAppointmentSlots exist in the codebase.

No files require special attention.

Important Files Changed

Filename Overview
src/pages/Scheduling/utils.ts Fixes computeAppointmentSlots to keep conflicting slots with isAvailable: false and attached exceptions instead of silently dropping them, so the tooltip reason now surfaces correctly in ScheduleHome

Reviews (1): Last reviewed commit: "fix: surface schedule exception reasons ..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🚀 Preview Deployment Ready!

🔗 Preview URL: https://pr-16426.care-preview-a7w.pages.dev

📱 Mobile Access:
Scan the QR code below to open the preview on your mobile device.

QR Code


This preview will be automatically updated when you push new commits to this PR.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🎭 Playwright Test Results

Status: ❌ Failed
Test Shards: 3

Metric Count
Total Tests 320
✅ Passed 317
❌ Failed 3
⏭️ Skipped 0

📊 Detailed results are available in the playwright-final-report artifact.

Run: #9486

Addresses review feedback on computeAppointmentSlots:
- Behavior compatibility: returning unavailable slots is now opt-in via
  `includeUnavailableSlots` (default false = previous "available slots
  only" behavior). ScheduleHome passes true so the tooltip can read the
  overlapping exceptions; any other caller is unaffected.
- Performance: parse each exception's time window once outside the loop,
  determine availability with some() (early-exit on first overlap), and
  only build the overlapping-exception list for unavailable slots that
  are actually returned.
Copilot AI review requested due to automatic review settings June 3, 2026 13:22

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread src/pages/Scheduling/utils.ts Outdated
Comment thread src/pages/Scheduling/utils.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Schedule/ScheduleHome.tsx (1)

447-449: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Optional: dedupe exceptions by id rather than object identity.

new Set(computedSlots.flatMap((slot) => slot.exceptions)) currently dedupes correctly only because computeAppointmentSlots re-emits the same ScheduleException references across slots. This couples this component to an implementation detail of the util; if it ever returns cloned objects, duplicates would slip through. Deduping by id is more robust and also collapses entries that genuinely share an id.

♻️ Proposed change
-  const exceptions = [
-    ...new Set(computedSlots.flatMap((slot) => slot.exceptions)),
-  ];
+  const exceptions = [
+    ...new Map(
+      computedSlots.flatMap((slot) => slot.exceptions).map((e) => [e.id, e]),
+    ).values(),
+  ];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Schedule/ScheduleHome.tsx` around lines 447 - 449, The current
dedupe for exceptions uses object identity which can fail if
computeAppointmentSlots returns cloned ScheduleException objects; update the
exceptions construction to dedupe by exception.id instead: iterate
computedSlots.flatMap(slot => slot.exceptions) and collapse into a Map keyed by
exception.id (or use a reducer keyed by id) to keep the first occurrence for
each id, then take the Map values as the exceptions array; reference the
computedSlots variable and the exceptions constant to locate where to replace
the Set-based dedupe.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/Scheduling/utils.ts`:
- Around line 91-108: Extract the overlap predicate into a single reusable
function/closure (e.g., overlaps = ({start, end}) => start < slotEndTime && end
> time) and use it both to compute isAvailable (isAvailable =
!parsedExceptions.some(overlaps)) and to build the exceptions list
(parsedExceptions.filter(overlaps).map(({ exception }) => exception)) so the
availability check and attached exceptions cannot diverge; keep references to
parsedExceptions, isAvailable, includeUnavailableSlots, slots, start_time and
end_time when making the change.
- Around line 79-83: The parsedExceptions mapping in utils.ts currently always
parses exception.start_time/end_time with date-fns parse using "HH:mm:ss", which
yields Invalid Date for inputs like "HH:mm"; update the logic in the
exceptions.map block that builds parsedExceptions to detect whether each time
string includes seconds (e.g., contains two colons) or to normalize the string
by appending ":00" when seconds are absent, then call parse with the matching
format ("HH:mm" or "HH:mm:ss") (or always parse the normalized "HH:mm:ss") and
validate the resulting Date objects (reference the parsedExceptions variable,
exception.start_time, exception.end_time, parse, and referenceDate) so overlap
checks aren’t broken by Invalid Date values.

---

Outside diff comments:
In `@src/components/Schedule/ScheduleHome.tsx`:
- Around line 447-449: The current dedupe for exceptions uses object identity
which can fail if computeAppointmentSlots returns cloned ScheduleException
objects; update the exceptions construction to dedupe by exception.id instead:
iterate computedSlots.flatMap(slot => slot.exceptions) and collapse into a Map
keyed by exception.id (or use a reducer keyed by id) to keep the first
occurrence for each id, then take the Map values as the exceptions array;
reference the computedSlots variable and the exceptions constant to locate where
to replace the Set-based dedupe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f0d5674f-4150-4319-8aac-927a14e5eead

📥 Commits

Reviewing files that changed from the base of the PR and between 31cf0a2 and c7e6bcd.

📒 Files selected for processing (2)
  • src/components/Schedule/ScheduleHome.tsx
  • src/pages/Scheduling/utils.ts

Comment thread src/pages/Scheduling/utils.ts
Comment thread src/pages/Scheduling/utils.ts Outdated
Address review feedback:
- Compute each slot's overlapping exceptions in a single filter pass and
  derive isAvailable from its length, instead of a some() check followed
  by a separate filter for unavailable slots.
- Extract a named ComputeAppointmentSlotsOptions interface for the options
  parameter instead of an inline anonymous type.
The exception time inputs produce HH:mm values (e.g. "00:00"/"23:59"),
so hardcoding parse(..., "HH:mm:ss") would yield Invalid Date and break
the overlap checks. Add a parseScheduleTime helper that selects the
format by length, and use it for both availability and exception times.

(The single-pass overlap from the previous commit already keeps
isAvailable and the attached exceptions derived from one computation.)
Copilot AI review requested due to automatic review settings June 3, 2026 13:41

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/pages/Scheduling/utils.ts:1

  • When includeUnavailableSlots is false (the default), this still builds slotExceptions by filtering/mapping the full exception list for every slot. Previously the code could short-circuit on the first conflict. Consider splitting the logic: use parsedExceptions.some(...) (early-exit) to compute isAvailable when unavailable slots are not needed, and only build the full slotExceptions array when includeUnavailableSlots is true. This preserves the new behavior while avoiding a potential per-slot performance regression.
import {

Comment thread src/pages/Scheduling/utils.ts Outdated
Use value.trim().split(":").length to choose HH:mm vs HH:mm:ss so format
selection is structural (and tolerant of surrounding whitespace) rather
than relying on the brittle string-length heuristic.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/Scheduling/utils.ts`:
- Around line 55-63: parseScheduleTime currently returns the result of
parse(...) without validating it, allowing "Invalid Date" to propagate; update
parseScheduleTime to validate the parsed Date (similar to getDurationInMinutes)
and throw a clear error or return a controlled failure when parse yields an
invalid date. Locate parseScheduleTime and ensure after calling parse(trimmed,
pattern, referenceDate) you check for Invalid Date (or use isValid(parsed)) and
handle it (throw new Error with the original value and pattern or return a
defined sentinel) so callers won't silently proceed with invalid dates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 075a2b29-6064-4a85-b807-cd776b95aa5b

📥 Commits

Reviewing files that changed from the base of the PR and between 48f28a0 and 6592ad8.

📒 Files selected for processing (1)
  • src/pages/Scheduling/utils.ts

Comment thread src/pages/Scheduling/utils.ts

@nihal467 nihal467 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add the screenshot of the changes made in the PR

Copilot AI review requested due to automatic review settings June 8, 2026 18:53

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment thread src/pages/Scheduling/utils.ts
Comment thread src/pages/Scheduling/utils.ts
Comment thread src/pages/Scheduling/utils.ts
@github-actions github-actions Bot added the stale label Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Schedule exception reasons never shown: computeAppointmentSlots leaves tooltip empty

3 participants