Skip to content

Design: fix layout bugs across Clinical and Work, add On Call and Roster summary cards - #3559

Merged
BigSimmo merged 18 commits into
mainfrom
claude/unified-design-system-6zlw0d
Oct 15, 2026
Merged

BigSimmo merged 18 commits into
mainfrom
claude/unified-design-system-6zlw0d

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

User description

Summary

Before: on a phone, the CPD "Log an activity" bar covered list rows. DSM Compare slots ran off the screen. On Call timeline labels collided ("17:00Now"), and its emergency number wrapped badly. The Teaching series name was cut off, My Day showed the time left twice, and Favourites names split mid-word. Empty differential sections looked tappable, and Services showed a bare link. On Call and Roster had no Work summary card.

After: each of those is fixed. On Call and Roster now open with the owner-approved bold summary card, with a white pill main action (Call switchboard, Who's on).

These come from the owner-reviewed design critique of 15 Oct 2026, limited to the items marked "likely mistakes". Items marked "could be deliberate" are unchanged. Clinical home stays centred, and Clinical buttons keep their corners.

  • CPD: "Log an activity" moves into the summary card as a white pill. The Log page keeps space below its sticky bar. Customise stays inside the gutter. "1 h today" no longer splits.
  • DSM Compare uses the stacked layout on phones. DSM results keep room for the bottom composer.
  • Differential detail: an empty section shows its summary or "None listed". Presentation only, with no clinical content changed.
  • Services: a single Browse link uses the Browse row card. Documents is affected the same way.
  • Formulation builder: the repeated "Formulation builder" eyebrow is removed.
  • On Call: the timeline "Now" label is inset from both ends. "From your roster" uses Geist. The emergency number stays together, with "from your mobile" on a second line.
  • Teaching name wraps. The My Day ring shows percentage done during a shift. Favourites tile names hyphenate.
  • New on-call-hero.tsx. The Roster month strip is replaced by the hero card, and shared hero pill styles are added in work-mode.css.

Areas touched: Personal practice, App experience

RAG impact: no retrieval behaviour change. No ranking, retrieval or search-ordering file was touched.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains PsychSift Production (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed (no decision-support behaviour changed; layout only)

Verification

  • Checks run:
    • npm run verify:cheap: lint and typecheck clean. Unit suite gave "Test Files 1 failed | 1882 passed (1883)".
    • The one failure is tests/my-day-week.dom.test.tsx, "shows a signed-out reader the sample week". It is a findByTestId timeout under load. Rerun alone three times: 23 passed, 23 passed, 1 failed. It doesn't import any changed file, so it's a pre-existing flake.
    • Focused runs by area: "Tests 219 passed (219)" (CPD), "Tests 284 passed (284)" (Clinical), "Tests 279 passed (279)" (On Call and Roster), "Tests 739 passed (739)" (Work text).
    • Phone screenshots at 390 px were checked by eye on the local demo server. Browser suites were left to CI.

Risk and rollout

  • Risk: low. This is layout and styling only, with no data, auth, migration or provider changes. On Call now shows the summary card above the emergency number card.
  • Rollback: revert the single commit.
  • Provider or production effects: None.

Notes

  • Favourites: dictionary words now hyphenate. Unusual drug names, such as Acamprosate, can still split without a hyphen in Chromium.
  • On Call shows the hospital name in the card eyebrow and again in the line below, which keeps its Change button. That's left for owner review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw

Generated by Claude Code


Summary by cubic

Fixes layout bugs from the 15 Oct design review across Clinical and Work, and gives On Call and Roster the shared Work summary hero card. Everything here is layout and styling only; no data, auth, or clinical content behavior changes.

Layout fixes

  • CPD "Log an activity" becomes a white pill in the summary card, replacing the floating dock that covered list rows at 390px, and stays available when a year's entries total 0 h.
  • The CPD Log page reserves space for the floating dock so rows clear it, and Customise stays inside the page gutter.
  • DSM Compare stacks slots full-width on phones instead of the compact rail that cut them off, and results keep room for the bottom composer.
  • On Call timeline "Now" no longer collides with end labels, and the emergency route wraps whole segments with "from your mobile" on its own line.
  • Teaching names wrap, the My Day ring shows percent done instead of repeating time left, and Favourites hyphenate.
  • A Tab-focused control is re-revealed while layout settles after the dock hides; the re-reveal stops once the reader scrolls or presses a key.
  • Empty differential sections say "None listed", a single Browse link renders as a card row, and the Formulation builder drops its repeated eyebrow.

New summary heroes

  • On Call and Roster open with a bold summary hero: time left or next shift as the headline, with a white pill main action (Call switchboard, Who's on).
  • On Call names the switchboard route it dials — a bare extension says it needs a hospital phone — and holds its place with a neutral title while data loads; a failed roster read says "Couldn't load your roster", and a signed-out reader is told to sign in, rather than either claiming no shift is on.
  • Roster's Next picks the next future shift, searching all loaded shifts rather than the Coming up rows so overlapping shifts can't hide it, and opening a day from the hero loads that shift's month first.
  • Hero pills use the pill radius token, a border, and a visible focus ring per the design-system contract, and the hero keeps a placeholder while loading so nothing moves under the thumb.

Written for commit af15fbe. Summary will update on new commits.

View guided diff Turn on auto-fix

Summary by CodeRabbit

  • New Features

    • Added a roster-aware on-call summary with shift details and a switchboard call action.
    • Added a roster hero with shift timing, upcoming shift details, and quick actions.
    • Browse cards now appear on empty mode home pages when a browse option is available.
    • Running shifts now show the percentage completed.
  • Improvements

    • Moved “Log an activity” into the CME year summary card.
    • Comparison tiles stack on phones and display side by side on wider screens.
    • Improved wrapping and readability for favourite labels, service names, phone numbers, and section descriptions.

CodeAnt-AI Description

Add On Call and Roster summary cards and fix mobile layouts

What Changed

  • On Call and Roster now open with a summary card showing shift details and quick actions. On Call offers a switchboard call only when a number is recorded; Roster offers “Who's on” and “Open day.”
  • CPD’s “Log an activity” button now sits inside its summary card. The CPD log and DSM results leave space for floating controls, and DSM comparison slots stack on phones so all remain visible.
  • Fixed phone layouts that split or clipped On Call timeline labels and call numbers, the CPD “today” note, teaching names, and favourite names.
  • Empty differential sections now explain what is listed or where to find it; single-link Services and Documents pages use a Browse card. My Day’s shift ring shows the percentage completed, and Customise stays within the page gutter.

Impact

✅ All DSM comparison slots visible on phones
✅ CPD log rows stay clear of the floating action bar
✅ Switchboard calls available from the On Call summary

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…ter summary cards

CPD Log an activity moves into the summary card, Log page keeps room for the bar,
Customise stays in the gutter, today segment no longer splits. DSM Compare stacks
on phones, DSM results leave room for the composer, empty differential sections say
None listed, Services uses Browse rows, Formulation builder drops its repeat eyebrow.
On Call timeline labels no longer collide, roster label uses Geist, emergency number
keeps together, Teaching name wraps, My Day ring shows percent done, Favourites
names hyphenate. On Call and Roster open with the approved summary card.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw
@supabase

supabase Bot commented Oct 15, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

Next included review available in 36 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 89 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: BigSimmo/PsychSift/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 4afc3b28-1335-4264-91bd-a1d16021a131
📥 Commits

Reviewing files that changed from the base of the PR and between 5ed2132 and af15fbe.

📒 Files selected for processing: 8
  • src/components/differentials/differential-detail-page.tsx
  • src/components/dsm/dsm-search-page.tsx
  • src/components/on-call/now/on-call-hero.tsx
  • src/components/on-call/on-call-home.tsx
  • src/components/phone-focus-clearance.tsx
  • src/components/roster/roster-month-page.tsx
  • tests/on-call-now-hero.dom.test.tsx
  • tests/roster-month-page.dom.test.tsx
📝 Walkthrough

The pull request updates CME logging placement, adds on-call and roster summary heroes, changes DSM comparison slots, and adjusts several independent page behaviors and text layouts.

Changes: CME activity logging

Layer / File(s) Summary
Year summary action and dock layout
src/components/cme/cme-dashboard-catch-up.tsx, src/components/cme/cme-dashboard.tsx, src/app/work-mode.css, src/components/cme/cme-work.css, src/components/cme/cme-log-page.tsx, tests/cme-dashboard*.dom.test.tsx
The year summary accepts and displays the “Log an activity” action and formats the audited-today amount as a non-wrapping note. The dashboard removes its floating action dock, and the tests check the action inside the summary card.

On-call summary

Layer / File(s) Summary
Current on-call status and call action
src/components/on-call/now/on-call-hero.tsx, src/components/on-call/on-call-home.tsx, src/components/on-call/now/*, src/app/work-mode.css, tests/on-call-now*.dom.test.tsx
The on-call page renders a hero with available shift, hospital, and switchboard details. The call action records a handbook contact when an answer is available. Related on-call labels and call-button layout also change.

Roster month summary

Layer / File(s) Summary
Lead shift hero and actions
src/components/roster/roster-month-page.tsx, tests/roster-month-page.dom.test.tsx
The lead shift uses a hero with shift timing, a “Who’s on” link, and an action to open the shift’s day. A rest warning renders in its own card.

DSM comparison slots

Layer / File(s) Summary
Default comparison slot layout
src/components/dsm/dsm-compare-chrome.tsx, tests/dsm-compare-chrome.dom.test.tsx, tests/dsm-ui-smoke.spec.ts, tests/ui-route-coverage.spec.ts
DSM comparison uses the default slot layout instead of the compact layout. Tests check standard slot tiles, including their stacked arrangement at a 390px viewport.

Differential empty-section text

Layer / File(s) Summary
Empty-section description
src/components/differentials/differential-detail-page.tsx
Empty section rows show a trimmed summary when available. Otherwise, they show a fallback based on whether source items or record investigations exist.

Mode home Browse list

Layer / File(s) Summary
Single-link Browse list
src/components/mode-home-start-list.tsx, tests/mode-home-start-list.dom.test.tsx
An empty mode home with one browse link now renders the Browse card list. A test checks the services link’s label and destination.

Shift progress indicator

Layer / File(s) Summary
Running-shift progress ring
src/components/my-day/my-day-today-cards.tsx
The running-shift ring displays the rounded percentage completed and labels it “done.”

Text and spacing refinements

Layer / File(s) Summary
Page text and spacing
src/components/dsm/dsm-search-page.tsx, src/components/favourites/favourites-shelf.tsx, src/components/formulation/formulation-builder-page.tsx, src/components/teaching/teaching-modules.tsx
DSM search removes base bottom padding, favourite labels enable automatic hyphenation, the formulation header removes its eyebrow, and service names can wrap.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant OnCallHome
  participant NowOnCallHero
  participant HandbookRecent
  participant PhoneDialer
  OnCallHome->>NowOnCallHero: pass shift, hospital, and handbook data
  NowOnCallHero->>OnCallHome: render status and available telephone action
  OnCallHome->>NowOnCallHero: user activates telephone action
  NowOnCallHero->>HandbookRecent: record call when a handbook answer is available
  NowOnCallHero->>PhoneDialer: follow the telephone link
Loading

Suggested reviewers: claude

Merge Risk: 🟡 Moderate · up to 5ed21

The roster day sheet can omit colleagues for an out-of-month shift, and the standard verification command is blocked. Fix both before merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 52.63% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 25 files. (2 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly summarizes the main changes: mobile layout fixes across Clinical and Work, plus new On Call and Roster summary cards.
Description check Passed The description is complete and relevant. It includes a detailed summary, areas touched, RAG impact, governance checks, verification results including the known flaky test, risk, rollback, production …
Full details: Docstring Coverage

Explanation

Docstring coverage is 52.63% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 25 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@codeant-ai

codeant-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: af15fbeb
Scan Time: 2026-10-15 19:43:56 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

Comment thread src/components/cme/cme-dashboard.tsx Resolved
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw
…gn-system contract

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 15, 2026

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

@BigSimmo
BigSimmo marked this pull request as ready for review October 15, 2026 17:17

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

BigSimmo has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@codeant-ai

codeant-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
🔄 Incremental review in progress… 5082141 Oct 15, 2026 · 18:19 —
✅ Reviewed your PR 99b13ca Oct 15, 2026 · 17:18 17:33

@codeant-ai

codeant-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T17:26:42.235433Z 99b13ca Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Oct 15, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Fix mobile layouts and add On Call and Roster summary cards

🐞 Bug fix ✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Fix mobile overflow, obscured content, wrapping, and empty-state presentation across Clinical and
 Work.
• Add On Call and Roster summary cards with prominent, context-aware actions.
• Move CPD logging into its summary card and add regression coverage for key layouts.
Diagram

graph TD
  H["Handbook and clock"] --> O["On Call hero"] --> W["WorkHero kit"] --> A["Hero CSS"]
  R["Roster shifts"] --> P["Roster page"] --> W
  C["CPD dashboard"] --> S["CPD summary"] --> A
Loading
High-Level Assessment

Reusing WorkHero for the new summary cards and keeping the other fixes local to their existing components is appropriate. A broader page-shell or action-component redesign would add scope without solving these specific layout problems more directly.

Files changed (28) +476 / -101

Enhancement (4) +217 / -43
work-mode.cssStyle shared Work hero actions +55/-0

Style shared Work hero actions

• Adds primary and quiet pill styles, a loading placeholder, focus treatment, and forced-colors support for hero actions.

src/app/work-mode.css

on-call-hero.tsxAdd the On Call summary card +104/-0

Add the On Call summary card

• Summarizes rostered time remaining or the hospital cover window and offers a switchboard call when a recorded callable number exists. Holds the action’s space during handbook loading.

src/components/on-call/now/on-call-hero.tsx

on-call-home.tsxLead On Call home with its summary +15/-1

Lead On Call home with its summary

• Places the new summary card before the hospital line while retaining the existing emergency and contact sections.

src/components/on-call/on-call-home.tsx

roster-month-page.tsxReplace the Roster lead strip with a hero +43/-42

Replace the Roster lead strip with a hero

• Shows current or upcoming shift context in a WorkHero with Who’s on and Open day actions. Keeps any rest warning in a separate card.

src/components/roster/roster-month-page.tsx

Bug fix (15) +108 / -46
cme-dashboard-catch-up.tsxPlace CPD logging in the year summary +21/-8

Place CPD logging in the year summary

• Accepts a log action in populated and relevant empty summary variants. Keeps the audited-today note together when it wraps.

src/components/cme/cme-dashboard-catch-up.tsx

cme-dashboard.tsxRemove the CPD dashboard dock +22/-18

Remove the CPD dashboard dock

• Moves Log an activity into the summary card and removes the floating dashboard dock. Constrains the Customise control to the page gutter.

src/components/cme/cme-dashboard.tsx

cme-log-page.tsxReserve space below CPD log rows +4/-1

Reserve space below CPD log rows

• Wraps log-page content so its final rows can scroll clear of the remaining floating dock.

src/components/cme/cme-log-page.tsx

cme-work.cssStyle the CPD hero action and dock reserve +31/-0

Style the CPD hero action and dock reserve

• Makes the summary-card log button full-width and adds bottom space beneath log-page rows.

src/components/cme/cme-work.css

differential-detail-page.tsxLabel empty differential sections +3/-1

Label empty differential sections

• Displays the section summary or “None listed” instead of leaving a bare heading.

src/components/differentials/differential-detail-page.tsx

dsm-compare-chrome.tsxStack DSM comparison slots on phones +3/-1

Stack DSM comparison slots on phones

• Uses the default responsive slot layout instead of the compact rail that clipped comparison slots.

src/components/dsm/dsm-compare-chrome.tsx

dsm-search-page.tsxKeep DSM results clear of the composer +4/-1

Keep DSM results clear of the composer

• Adds phone-sized bottom padding so the final result can scroll above the floating search control.

src/components/dsm/dsm-search-page.tsx

favourites-shelf.tsxImprove wrapping of favourite names +1/-1

Improve wrapping of favourite names

• Enables automatic hyphenation for long tile labels within their two-line limit.

src/components/favourites/favourites-shelf.tsx

formulation-builder-page.tsxRemove repeated formulation heading +0/-1

Remove repeated formulation heading

• Drops the redundant Formulation builder eyebrow from the page header.

src/components/formulation/formulation-builder-page.tsx

mode-home-start-list.tsxShow single Browse links as cards +4/-1

Show single Browse links as cards

• Uses the Browse row-card list even when an empty home has only one link, including Services and Documents.

src/components/mode-home-start-list.tsx

my-day-today-cards.tsxShow shift completion in the My Day ring +3/-6

Show shift completion in the My Day ring

• Replaces duplicated time-left text in the running-shift ring with a clamped percentage done.

src/components/my-day/my-day-today-cards.tsx

emergency-pin.tsxKeep emergency numbers together +6/-3

Keep emergency numbers together

• Prevents the primary number from splitting while placing its calling-context text on a separate line.

src/components/on-call/now/emergency-pin.tsx

right-now.tsxSeparate On Call timeline labels +4/-2

Separate On Call timeline labels

• Insets the Now label from both timeline ends to avoid collisions with their time labels.

src/components/on-call/now/right-now.tsx

shift-clock.tsxCorrect roster-note typography +1/-1

Correct roster-note typography

• Applies the standard sans-serif face and tracking to “From your roster.”

src/components/on-call/now/shift-clock.tsx

teaching-modules.tsxWrap single-team Teaching names +1/-1

Wrap single-team Teaching names

• Allows a long team name to wrap instead of truncating it in the context bar.

src/components/teaching/teaching-modules.tsx

Tests (9) +151 / -12
cme-dashboard-today.dom.test.tsxVerify the CPD log action is inside the summary +6/-4

Verify the CPD log action is inside the summary

• Updates the dashboard assertion to require the log button in the summary card and no floating dock.

tests/cme-dashboard-today.dom.test.tsx

cme-dashboard.dom.test.tsxCheck the CPD dashboard’s sole primary action +5/-2

Check the CPD dashboard’s sole primary action

• Confirms Log an activity remains the sole primary action, now inside the summary rather than a dock.

tests/cme-dashboard.dom.test.tsx

dsm-compare-chrome.dom.test.tsxCheck default DSM comparison tiles +4/-2

Check default DSM comparison tiles

• Asserts the three standard slot tiles render instead of compact rail tiles.

tests/dsm-compare-chrome.dom.test.tsx

dsm-ui-smoke.spec.tsVerify stacked DSM slots at phone width +8/-2

Verify stacked DSM slots at phone width

• Checks that three comparison slots appear vertically aligned at a 390px viewport and starter links remain visible.

tests/dsm-ui-smoke.spec.ts

mode-home-start-list.dom.test.tsxCover a single-link Browse card +10/-0

Cover a single-link Browse card

• Verifies the Services empty home renders its catalogue link in the Browse card list.

tests/mode-home-start-list.dom.test.tsx

on-call-now-hero.dom.test.tsxCover On Call hero states +87/-0

Cover On Call hero states

• Tests rostered time and switchboard calling, the absence of an unrecorded number, and the loading placeholder.

tests/on-call-now-hero.dom.test.tsx

on-call-now.dom.test.tsxCheck On Call summary placement and call route +7/-0

Check On Call summary placement and call route

• Adds the hero to page-order assertions and verifies its switchboard link uses the recorded number.

tests/on-call-now.dom.test.tsx

roster-month-page.dom.test.tsxCover upcoming and active Roster heroes +22/-0

Cover upcoming and active Roster heroes

• Checks the new shift summaries, Who’s on link, and Open day action for an active shift.

tests/roster-month-page.dom.test.tsx

ui-route-coverage.spec.tsUpdate DSM route coverage for standard tiles +2/-2

Update DSM route coverage for standard tiles

• Expects the comparison page’s standard slot tiles after removing a selected diagnosis.

tests/ui-route-coverage.spec.ts

@qodo-code-review

qodo-code-review Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

No findings for this group


Remediation recommended

No findings for this group


Informational

No findings for this group

Grey Divider


Resolved findings

1. Search results retain an empty bottom band ✓ Resolved
Description
DsmSearchPage adds a fixed phone-sized bottom pad inside a shell that already owns the composer
reserve. On /dsm/search, the shell provides clearance while the dock is visible and releases it
when the dock hides, but this page pad remains in both states.
Code

src/components/dsm/dsm-search-page.tsx[392]

+      <div className={cn(pageContainer, "space-y-4 px-4 pt-4 pb-[calc(3.25rem+1rem)] sm:px-6 sm:py-6 lg:px-8")}>
Relevance

●●● Strong

The page adds a second fixed composer reserve despite the shell already managing dock clearance.

PR-#3154

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The design review skill prohibits a second dock-sized content pad below a shell-owned composer. The
shell already mounts the phone reserve, while the changed page adds a separate 4.25rem pad that
cannot follow the shell's hidden-dock state.

src/components/dsm/dsm-search-page.tsx[389-392]
src/components/clinical-dashboard/global-search-shell.tsx[1230-1241]
src/components/clinical-dashboard/mobile-composer-reserve.ts[12-22]
docs/search-chrome-behaviour.md[495-501]
Skill: design-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The DSM search page adds dock-sized padding on top of the shared shell's phone composer reserve, leaving unnecessary space after the dock hides.
## Fix Focus Areas
- src/components/dsm/dsm-search-page.tsx[389-392]
## Recommended Fix
Remove the page-local dock-sized bottom padding and rely on the shared mobile composer reserve. Verify the final result clears the visible pill and that the reserve collapses when the pill hides at phone width.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Mobile callers see the wrong number ✓ Resolved
Description
NowOnCallHero displays answer.dial.display for the switchboard line but builds its call pill
from onCallCallRoute, which can select a different mobile number. When the handbook dial is a
hospital extension with a recorded mobile fallback and the hospital-phone preference is off, callers
see the extension while the pill targets the fallback.
Code

src/components/on-call/now/on-call-hero.tsx[53]

+  const switchboardLine = dial ? `Switchboard ${dial.display}` : null;
Relevance

●●● Strong

Displayed contact details must match the selected callable route, especially for extension and
mobile fallback cases.

PR-#3156
PR-#2806

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The hero takes its visible switchboard text from the handbook dial display independently of the
route used for its call link. An extension has no direct telephone link, and the shared routing
helper can select the recorded mobileDial fallback when the hospital-phone preference is off, so
the displayed extension and link target diverge; the UX review skill calls for mobile usability
checks.

src/components/on-call/now/on-call-hero.tsx[51-53]
src/components/on-call/kit/dial-sheet.tsx[32-35]
src/components/on-call/kit/dial-sheet.tsx[53-61]
src/components/on-call/now/on-call-hero.tsx[78-89]
src/components/on-call/kit/dial-sheet.tsx[20-53]
src/lib/on-call/number-resolver.ts[253-275]
Skill: ux-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The On Call hero can display an internal extension while its call pill dials a different mobile fallback.

## Fix Focus Areas
- src/components/on-call/now/on-call-hero.tsx[51-89]
- tests/on-call-now-hero.dom.test.tsx[28-50]

## Recommended Fix
When the call pill is available, derive its visible calling number from `route.display` so it matches the selected link target, and distinguish a mobile fallback from a hospital-phone extension. Preserve separately labelled, accurate handbook extension details, including when no direct call is possible. Add a test with an extension and a distinct mobile route that asserts both the displayed number and the link target.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Focused controls can fall under dock ✗ Dismissed
Description
The capture-phase keydown handler cancels the settle observer for every key, not only a navigation
or activation key. A modifier, typing key, or prevented keydown while focus remains on the same
control stops re-revealing it, so a reserve or layout transition can put that focused control under
the phone dock.
Code

src/components/phone-focus-clearance.tsx[R59-62]

+    // A reader who scrolls, taps or presses a key during the settle window has moved on (Enter on
+    // an in-page jump link scrolls away from the still-focused link), so stop pulling the page
+    // back to the focused control.
+    const userScrollEvents = ["wheel", "touchstart", "pointerdown", "keydown"] as const;
Relevance

●●● Strong

Broad keydown cancellation strands focused controls during layout changes; accessibility-focused
keyboard behavior is consistently accepted.

PR-#3518
PR-#3365

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed handler treats every keydown as evidence that the reader moved on, but keyboard focus
may remain on the same element for ordinary typing or modifier keys. The accessibility review
guidance requires focus management that keeps keyboard-focused controls usable.

src/components/phone-focus-clearance.tsx[59-68]
Skill: accessibility-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The phone focus settle window is cancelled for every `keydown`, including modifier keys, text-entry keys, and prevented key events that do not move focus or activate the current control. This can leave the still-focused control without re-reveal protection while the phone dock reserve continues changing.

## Fix Focus Areas
- src/components/phone-focus-clearance.tsx[59-68]

## Recommended Fix
Only stop settling for keyboard actions that intentionally navigate or activate content, or when focus actually changes. Ignore modifier and ordinary text-entry keydowns, while preserving the Enter/in-page-jump behavior and cleanup of the observer and timer.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View resolved (10)
4. Older iPhones miss focus clearance ✗ Dismissed
Description
PhoneFocusClearance requests a border-box observation without detecting support for that option
or falling back when it is unavailable. Safari versions that expose ResizeObserver but not the
box option cannot reliably report the reserve's padding-only collapse, so a focused control can
move back beneath the dock during the settle window.
Code

src/components/phone-focus-clearance.tsx[R89-92]

+      });
+      // Border box, so the reserve padding collapsing counts as a resize.
+      observer.observe(content, { box: "border-box" });
+      settle = { observer, timer: window.setTimeout(stopSettling, settleWindowMs) };
Relevance

●●● Strong

Concrete older-Safari compatibility risk warrants feature detection or fallback, matching accepted
browser-compatibility fixes.

PR-#3458
PR-#2410

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed code only checks whether the ResizeObserver constructor exists, then requests the
newer box: "border-box" option. The component is mounted globally from the root layout, and the
observed element is the shell's reserve pad whose padding collapse is the layout movement this
change is intended to catch; Safari support data shows the observe options parameter is
unsupported through Safari 15.3, while Safari 15 itself has ResizeObserver support but incomplete
box handling.

src/components/phone-focus-clearance.tsx[81-92]
src/app/layout.tsx[173-173]
src/components/clinical-dashboard/global-search-shell.tsx[1239-1243]
🌐 The ResizeObserver.observe options/box parameter is listed as unsupported in Safari 3.1–15.3 and supported from Safari 15.4.
🌐 Safari 15 ResizeObserver is reported to have incomplete border-box option behavior, while the default content-box observation works.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PhoneFocusClearance` unconditionally calls `ResizeObserver.observe(content, { box: "border-box" })`. Older Safari versions expose `ResizeObserver` but do not support the `box` option reliably, so padding-only changes to the mobile reserve may not trigger the callback and focused controls can remain under the dock.

## Fix Focus Areas
- src/components/phone-focus-clearance.tsx[81-92]

## Recommended Fix
Feature-detect or safely probe support for `box: "border-box"`, and fall back to a supported observation strategy when it is unavailable. Ensure the fallback still detects the reserve transition or another resize signal that occurs when the mobile composer reserve collapses, while preserving the existing cleanup and settle timeout behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Focus settling lacks regression proof ✗ Dismissed
Description
The new ResizeObserver settle path in PhoneFocusClearance has no targeted unit, DOM, or browser
test that triggers a resize and verifies re-reveal or cleanup. A future change to the reserve
transition, observer callback, or 1200ms timeout can therefore regress keyboard focus clearance
without the focused tests detecting it.
Code

src/components/phone-focus-clearance.tsx[R81-92]

+      if (typeof ResizeObserver === "undefined") return;
+      const content = surface.querySelector<HTMLElement>('[data-testid="mobile-composer-reserve-pad"]') ?? surface;
+      const observer = new ResizeObserver(() => {
+        if (document.activeElement !== target) {
+          stopSettling();
+          return;
+        }
+        revealIfCovered(target);
+      });
+      // Border box, so the reserve padding collapsing counts as a resize.
+      observer.observe(content, { box: "border-box" });
+      settle = { observer, timer: window.setTimeout(stopSettling, settleWindowMs) };
Relevance

●●● Strong

Recent reliability changes add focused resize, scroll-hide, and browser regression coverage for
interactive layout behavior.

PR-#3154
PR-#3467

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed component adds the observer, callback, and timeout but no corresponding focused test is
included in the reviewed changes. The testing-review skill requires coverage for new interactive
frontend behavior, while the existing broad phone-scroll tests do not directly exercise this
observer path.

src/components/phone-focus-clearance.tsx[53-63]
src/components/phone-focus-clearance.tsx[81-92]
Skill: testing-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `ResizeObserver` settle path re-reveals a keyboard-focused control after phone layout changes, but the repository has no targeted test that triggers the observer callback or verifies cleanup after focus moves or the settle timeout expires.

## Fix Focus Areas
- src/components/phone-focus-clearance.tsx[81-92]
- tests/phone-focus-clearance.dom.test.tsx[1-1]

## Recommended Fix
Add a focused DOM test with a deterministic `ResizeObserver` stub. Mount `PhoneFocusClearance`, focus a `:focus-visible` control inside `.phone-scroll-surface`, trigger a resize callback while the control is covered, and assert that `scrollIntoView` is called; also verify that moving focus elsewhere disconnects the observer and that the 1200ms timeout stops further callbacks. Keep the test independent of live browser layout and preserve the existing phone media-query setup.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Signed-out users see a false empty shift ✓ Resolved
Description
NowOnCallHero falls through to “No rostered shift on now” when the roster hook reports
signed-out, because the caller only passes loading and error booleans. A 401 therefore
produces an empty shift context without indicating that the roster was unavailable, while the rest
of the On Call page correctly treats signed-out data as inaccessible rather than empty.
Code

src/components/on-call/now/on-call-hero.tsx[R75-77]

+        : rosterError
+          ? "Couldn't load your roster"
+          : "No rostered shift on now";
Relevance

●●● Strong

Recent On Call precedents accept distinguishing unavailable states from confirmed empty data,
including signed-out handling.

PR-#3495
PR-#3225

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The roster hook defines signed-out separately from error and returns it for HTTP 401 responses.
The On Call page deliberately keeps signed-out roster shifts out of the context, while its existing
signed-out handling avoids calling inaccessible data empty; the new hero receives only `status ===
"error", so signed-out` reaches the false empty-roster fallback.

src/components/on-call/now/on-call-hero.tsx[75-77]
src/components/on-call/on-call-home.tsx[344-345]
src/components/roster/use-roster-shifts.ts[30-36]
src/components/roster/use-roster-shifts.ts[92-98]
src/components/on-call/on-call-home.tsx[193-195]
src/components/on-call/on-call-home.tsx[462-468]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new On Call hero receives `rosterError` only for the hook's `error` state, so a `signed-out` roster response leaves an empty context and displays “No rostered shift on now,” falsely implying that the user has no shift.

## Fix Focus Areas
- src/components/on-call/now/on-call-hero.tsx[48-49]
- src/components/on-call/now/on-call-hero.tsx[75-77]
- src/components/on-call/on-call-home.tsx[344-345]

## Recommended Fix
Pass a distinct signed-out state from `useRosterShifts()` to `NowOnCallHero`, and render an appropriate signed-out/loading state instead of the empty-roster title when the roster status is `signed-out`. Preserve the existing “Couldn't load your roster” message for actual read errors.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. On Call keeps checking a finished roster ✗ Dismissed
Description
NowOnCallHero uses rosterLoading || handbookLoading to choose the title “Checking your shift…”,
although handbook loading says nothing about roster status. When the roster has finished with no
active shift but the independent handbook request is still pending, the card continues to describe
the shift as being checked.
Code

src/components/on-call/now/on-call-hero.tsx[R70-72]

+      : rosterLoading || handbookLoading
+        ? "Checking your shift…"
+        : "No rostered shift on now";
Relevance

●●● Strong

Accepted precedents distinguish loading from confirmed empty states; handbook loading should not
keep roster status pending.

PR-#3495
PR-#3225

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The home passes separate roster and handbook loading flags. Its roster context is derived from ready
shifts, while handbookLoading comes solely from handbook status, so either request can remain
pending after the other finishes.

src/components/on-call/now/on-call-hero.tsx[65-72]
src/components/on-call/on-call-home.tsx[191-196]
src/components/on-call/on-call-home.tsx[220-223]
src/components/on-call/on-call-home.tsx[337-345]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A pending handbook request makes the On Call card claim it is checking a roster that has already finished loading.
## Fix Focus Areas
- src/components/on-call/now/on-call-hero.tsx[65-72]
## Recommended Fix
Use `rosterLoading` alone for the shift-checking title. If handbook loading needs its own interim title, describe the handbook state separately rather than implying roster uncertainty.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Overlapping shifts hide the next shift ✓ Resolved
Description
RosterMonthPage searches upcoming for a future start, but nextShifts has already limited that
start-sorted array to three non-ended shifts. If three other shifts overlap the active lead, they
occupy every slot even when a later shift exists, so the hero omits its “Next” detail.
Code

src/components/roster/roster-month-page.tsx[383]

+          const next = upcoming.find((shift) => Date.parse(shift.startsAt) > now.getTime());
Relevance

●●● Strong

Recent roster precedents accept fixes ensuring capped and merged upcoming data does not hide later
shifts.

PR-#3479
PR-#3461

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The page requests only three upcoming entries before its new future-start search. nextShifts
filters by end time, sorts by start and then slices; manually added shifts are not checked for
overlap, so active overlaps can fill those slots.

src/components/roster/roster-month-page.tsx[300-305]
src/components/roster/roster-month-page.tsx[380-386]
src/components/roster/roster-month-model.ts[98-110]
src/lib/roster/shifts/model.ts[137-143]
src/lib/roster/shifts/repository.ts[245-269]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Roster hero can omit a future shift because it searches a three-entry list already filled by overlapping active shifts.
## Fix Focus Areas
- src/components/roster/roster-month-page.tsx[300-305]
- src/components/roster/roster-month-page.tsx[380-386]
## Recommended Fix
Derive the hero’s next shift from non-ended shifts whose start is after `now`, ordered by start, before applying the Coming up list’s three-row limit.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. On Call card says no shift while loading ✓ Resolved
Description
NowOnCallHero falls back to the title "No rostered shift on now" whenever onCallShiftClock and
onCallCoverWindow both return null. While the roster is loading, rosterShifts is [], and while
the handbook is loading, hours is null, so both are always null at that point. On first paint the
card states that the doctor has no shift, then changes to "4 h left" or "After hours until 08:00"
once the data arrives. handbookLoading only holds the pill's place, not the title.
Code

src/components/on-call/now/on-call-hero.tsx[R56-60]

+  const title = clock
+    ? `${onCallDurationWords(clock.minutesLeft)} left`
+    : cover && periodLabel
+      ? `${periodLabel} until ${cover.until}`
+      : "No rostered shift on now";
Relevance

●●● Strong

Recent On Call precedents reject definitive empty-state claims while handbook or roster data remains
unready.

PR-#3495
PR-#3225

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
context comes from rosterShifts, which is [] until shifts.status === "ready". The hero is
rendered from the first paint and gets handbook.hours ?? null, which is null while the handbook
loads. With no shift clock and no cover window, the title takes the "No rostered shift on now"
branch, whatever the real roster says.

src/components/on-call/on-call-home.tsx[189-194]
src/components/on-call/on-call-home.tsx[334-345]
src/components/on-call/now/on-call-hero.tsx[47-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
While the roster or handbook is loading, the On Call hero says "No rostered shift on now", which may be false. It then changes once the data arrives.

## Fix Focus Areas
- src/components/on-call/now/on-call-hero.tsx[29-70]
- src/components/on-call/on-call-home.tsx[189-194]
- src/components/on-call/on-call-home.tsx[337-345]

## Recommended Fix
Pass a `rosterLoading` prop (`shifts.status === "loading"`) into `NowOnCallHero`. While the roster or handbook is loading and no clock or cover window is known, show a neutral placeholder title (for example "Checking your shift…", or a fixed-height skeleton) instead of "No rostered shift on now".

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Active shifts appear as upcoming ✓ Resolved
Description
RosterMonthPage labels upcoming[0] as Next whenever its lead shift is active, but nextShifts
includes shifts that have started and not yet ended. If two roster shifts overlap, the hero can
identify the other already-active shift as the next shift.
Code

src/components/roster/roster-month-page.tsx[R383-386]

+          const next = upcoming[0];
+          const nextWords = next
+            ? `Next: ${SHIFT_KIND_LABEL[next.kind]} ${dayWords(perthDateOf(next.startsAt))} ${perthTimeOf(next.startsAt)}`
+            : null;
Relevance

●●● Strong

Roster precedents accept correcting selectors that confuse active, upcoming, and lead shifts.

PR-#3207
PR-#3461

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The lead selector can pick an active shift; the separate upcoming selector excludes only the lead ID
and shifts that have ended, not other active shifts. The new hero prefixes the first remaining shift
with Next:.

src/lib/roster/shifts-overview.ts[48-66]
src/components/roster/roster-month-model.ts[99-115]
src/components/roster/roster-month-page.tsx[295-309]
src/components/roster/roster-month-page.tsx[380-399]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Next label can describe an overlapping shift that is already in progress.
## Fix Focus Areas
- src/components/roster/roster-month-page.tsx[380-399]
## Recommended Fix
When constructing `nextWords` for an active lead shift, choose a shift whose start is after `now`, or omit the Next text if no such shift is available. Leave the existing Coming up selection unchanged unless its own behavior needs adjustment.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Keyboard focus on CPD's Log button is invisible ✓ Resolved
Description
The new .work-hero .cpd-hero-log > .work-button:focus-visible rule draws a currentColor outline
2px outside the pill. On this pill, currentColor is the hero's own ink (--work-hero-action-ink,
falling back to --mode-identity), and the base primary ::before ring adds a --work-surface gap
and a --mode-identity ring. The card behind is a gradient in that same mode colour (dark ink on a
dark hero in dark themes), so a keyboard user tabbing to "Log an activity" on the CPD Year page sees
no focus ring in either theme.
Code

src/components/cme/cme-work.css[R552-555]

+.work-hero .cpd-hero-log > .work-button:focus-visible {
+  outline: 2px solid currentColor;
+  outline-offset: 2px;
+}
Relevance

●●● Strong

Recent focus precedent rejects invisible primary-button rings and requires a visible contrasting
treatment.

PR-#3443

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The hero's background is a gradient through --mode-identity. The pill's text colour is
--work-hero-action-ink, which falls back to --mode-identity, so an outline in currentColor
matches the background. The base primary rule draws its ring in --mode-identity too, so it
vanishes on the hero for the same reason. The new .work-hero__action:focus-visible rule uses the
pill's fill colour for its outline, which stays visible on the hero. Past PR #3443 fixed this same
pattern: a primary-button focus ring drawn in the colour it sits on.

src/app/work-mode.css[1665-1678]
src/app/work-mode.css[2141-2158]
src/app/work-mode.css[1757-1760]
PR-#3443

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The CPD hero's "Log an activity" pill has no visible focus ring. Its outline uses `currentColor` (the mode colour, or the dark hero ink in dark themes), and the hero background behind it is that same colour.

## Fix Focus Areas
- src/components/cme/cme-work.css[548-555]

## Recommended Fix
Change the outline to `outline: 2px solid var(--work-hero-action-fill, var(--surface-raised));`, matching `.work-hero__action:focus-visible`. Also clear the inherited `::before` focus box-shadow inside `.cpd-hero-log` (`.work-hero .cpd-hero-log > .work-button[data-variant="primary"]:focus-visible::before { box-shadow: none; }`) so the mode-colour ring does not show around the pill.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Emergency numbers get cut off on phones ✓ Resolved
Description
EmergencyRoute applies whitespace-nowrap to the entire primary.display string, including
switchboard-and-extension descriptions. On a narrow phone, that string cannot fit beside the icon
and button padding, and the surrounding emergency card clips its overflow.
Code

src/components/on-call/now/emergency-pin.tsx[96]

+            <span className={cn(modeNumberText, "whitespace-nowrap font-semibold")}>{primary.display}</span>
Relevance

●● Moderate

The finding identifies a plausible narrow-screen clipping bug, but the PR explicitly intends the
number to remain together.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The number resolver produces display strings containing a switchboard number followed by then ext,
and the changed span forbids wrapping that whole string. The containing module uses
overflow-hidden, so text exceeding the available button width is not visible.

src/lib/on-call/number-resolver.ts[285-306]
src/components/on-call/now/emergency-pin.tsx[83-99]
src/components/on-call/kit/calm.ts[23-26]
src/components/mode-kit/recipes.ts[16-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Forcing a complete emergency route onto one line can hide part of a switchboard-and-extension number on narrow screens.
## Fix Focus Areas
- src/components/on-call/now/emergency-pin.tsx[91-98]
## Recommended Fix
Keep individual dialable number segments together while allowing a long route to wrap between segments. Check the result at narrow phone widths without relying on overflow being visible outside the emergency card.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. Differential shows 'None listed' over real items ✓ Resolved
Description
The empty-section branch now shows "None listed" whenever section.summary is blank and
visibleSectionItems returns no items. visibleSectionItems also drops items that duplicate
record.clinicalHinge or the summary, so a section whose only item repeats the clinical hinge is
labelled "None listed" even though it has content. On clinical reference pages, a section such as
warning causes would then read as having nothing to list.
Code

src/components/differentials/differential-detail-page.tsx[263]

+            {section.summary.trim() || "None listed"}
Relevance

●●● Strong

Filtering duplicate content before rendering an empty state can falsely erase real clinical content;
this is a deterministic correctness fix.

PR-#2838

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
visibleSectionItems adds record.clinicalHinge to the excluded set and skips any source item that
matches it. SectionRow takes the empty branch when the filtered list has no items, not only when
section.items is empty, and that branch now prints "None listed".

src/lib/differential-detail.ts[94-108]
src/components/differentials/differential-detail-page.tsx[245-265]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
"None listed" appears for sections whose raw items were all filtered out as duplicates of the clinical hinge or summary. It says the section has nothing when it does.

## Fix Focus Areas
- src/components/differentials/differential-detail-page.tsx[250-265]

## Recommended Fix
Show "None listed" only when `section.items` (and, for test sections, `record.investigations`) are truly empty. When items were filtered out as duplicates, show neutral wording such as "See the key distinction above", or render nothing.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 9 rules
✅ Cross-repo context — repo relationships
✅ REVIEW.md
Review mode: Auto: 🚀 Fast: Localized keyboard-event behavior change with contained UI impact.

Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/components/cme/cme-work.css Resolved
Comment thread src/components/dsm/dsm-search-page.tsx Resolved
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

✅ Committed (7) · ☑ Fixed (7)

Grey Divider

Commits pushed directly to this PR — no separate fix PR opened.

Process — 7 fixed
  • ☑ Fixed: Search results retain an empty bottom band
  • ☑ Fixed: Mobile callers see the wrong number
  • ☑ Fixed: On Call card says no shift while loading
  • ☑ Fixed: Active shifts appear as upcoming
  • ☑ Fixed: Keyboard focus on CPD's Log button is invisible
  • ☑ Fixed: Emergency numbers get cut off on phones
  • ☑ Fixed: Differential shows 'None listed' over real items

@gitar-bot
gitar-bot Bot enabled auto-merge (squash) October 15, 2026 17:24
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 15, 2026

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 15, 2026

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit f266cfe

Comment thread src/components/on-call/on-call-home.tsx Resolved
@codeant-ai

codeant-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

CodeAnt PR Risk: Low Risk

  • The PR appears safe to merge; the On Call hero distinguishes a roster read failure from having no active shift.
  • Regression tests cover the hero states and the updated phone layouts for DSM comparison and the roster summary.

Assessed commit: ea09761bc98c

…ft when the read fails

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 15, 2026

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit ea09761

…er the dock hides

The reveal ran on focusin and one frame later. The reveal scroll hides the
dock, the shell reserve then animates to zero, and late sections above the
results can still grow, which carried an already-revealed control back off
screen (ui-overlap phone Tab test, intermittent on main). Watch the content
box for a short settle window and re-reveal on each resize.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 15, 2026

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 5f86d76

…ps re-revealing once the reader scrolls

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw
gitar-bot[bot]
gitar-bot Bot previously approved these changes Oct 15, 2026

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 176d3c3

…page jump link is not scrolled back to the link

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexgR3MMbRJjXTT2C5PoPw

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

@gitar-bot

gitar-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor
Code Review ✅ Approved 3 closed / 3 findings

🟡 Medium risk · The new roster-aware switchboard pill initiates a phone call on activation, so an accidental tap could dial the hospital unexpectedly.

Adds On Call and Roster summary cards and fixes phone layout bugs across Clinical and Work, including the CPD log bar, DSM Compare slots, and On Call timeline labels. The CPD Year page keeps its Log button when entries total 0 h, the empty-section pointer no longer names a missing heading, and the settle observer no longer pulls the page back after the user scrolls away. No issues remain open.

✅ 3 closed
✅ Edge Case: CPD Year page loses its only Log button when entries total 0 h

📄 src/components/cme/cme-dashboard.tsx:396-397 📄 src/components/cme/cme-dashboard-catch-up.tsx:353-367
nothingLogged is hasTarget && yearEntries.length === 0 && !(totalHours > 0). So a year that has a confirmed target and at least one entry, where the hours still add up to 0 (for example a 0 h activity, like fixture entry c), gets firstLogAction = undefined and logAction set. CmeYearSummary then takes the !(loggedHours > 0) empty branch, which renders only firstLogAction and never logAction. Before this PR the floating dock still showed "Log an activity" here. With the dock removed, the page has no way to log an activity. Fix: render logAction in the empty branch too ({firstLogAction ?? logAction}), or base nothingLogged on the same totalHours test the summary uses.

✅ Bug: Empty-section pointer names a 'key distinction' heading that doesn't exist

📄 src/components/differentials/differential-detail-page.tsx:263-266 📄 src/components/differentials/differential-detail-page.tsx:412
When a section has no summary and no visible items, but its raw section.items (or record.investigations for test sections) is non-empty, the row now says "See the key distinction above". No section on the page has that title. The block it seems to mean is headed "Clinical hinge" or "Presentation hinge" (line 412), and for group-scoped records it says outright that it is not specific to this diagnosis. The check also only tests whether raw items exist. visibleSectionItems drops blank and whitespace-only items as well as items that match the hinge, so a section whose raw items are all blank would still point the reader to the hinge.

Fix: only show the pointer when a raw item actually matches the hinge after cleaning, for example with comparableItemText(cleanDifferentialItem(item)) === comparableItemText(record.clinicalHinge). Use the heading the reader can see ("See the clinical hinge above" or "See the presentation hinge above"), and fall back to "None listed" otherwise.

✅ Edge Case: Settle observer can pull the page back after the user scrolls away

📄 src/components/phone-focus-clearance.tsx:81-92
For 1200 ms after a keyboard focus, every resize of the reserve pad calls revealIfCovered(target), and the only check is whether focus is still on that control. If the user Tab-focuses a control and then swipes or wheel-scrolls within that window, the scroll hides the dock. The shell reserve then animates its padding-bottom, which fires the ResizeObserver on many frames. The control is now above the viewport (rect.top < 0) or below the floor, so scrollIntoView drags the page back to it, against the user's scroll. Fix: end the settle window when the user starts scrolling. Listen once for wheel, touchstart or pointerdown (and optionally scroll keys) while settling, and call stopSettling when one fires.

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Approved

For more verification details, comment gitar display:verbose.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Important

Your trial ends in 6 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more.

Was this helpful? React with 👍 / 👎 | Gitar

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit af15fbe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔴 Large blast radius size:XL This PR changes 500-999 lines, ignoring generated files 🕐 40+ Minutes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants