Skip to content

WEB-4654 CGM Use - #2000

Merged
henry-tp merged 32 commits into
feat/filter-enhancementsfrom
WEB-4654-cgm-use
Aug 12, 2026
Merged

WEB-4654 CGM Use#2000
henry-tp merged 32 commits into
feat/filter-enhancementsfrom
WEB-4654-cgm-use

Conversation

@henry-tp

Copy link
Copy Markdown
Contributor

No description provided.

henry-tp added 30 commits July 15, 2026 14:03
@henry-tp
henry-tp changed the base branch from develop to feat/filter-enhancements August 12, 2026 20:58
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a0148559-4fba-492c-875d-585bb61a3825

📥 Commits

Reviewing files that changed from the base of the PR and between fbf6603 and 187f974.

⛔ Files ignored due to path filters (1)
  • app/core/icons/tagIcon.svg is excluded by !**/*.svg
📒 Files selected for processing (38)
  • __tests__/unit/app/pages/clinicworkspace/ClinicPatients.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTimeInRange.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/ActiveFiltersTray.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/CGMUseFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/DataRecencyFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/SiteFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/TagFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js
  • app/core/clinicUtils.js
  • app/pages/clinicadmin/clinicadmin.js
  • app/pages/clinicworkspace/ClinicPatients.js
  • app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByTimeInRange.js
  • app/pages/clinicworkspace/components/ActiveFiltersTray.js
  • app/pages/clinicworkspace/components/CGMUseFilterDropdown.js
  • app/pages/clinicworkspace/components/ClearFilterButtons.js
  • app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js
  • app/pages/clinicworkspace/components/SiteFilterDropdown.js
  • app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.js
  • app/pages/clinicworkspace/components/TagFilterDropdown.js
  • app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.js
  • app/pages/clinicworkspace/useClinicMetricsPageName.js
  • app/pages/clinicworkspace/useClinicPatientsFilters.js
  • app/pages/clinicworkspace/useIsClinicAdmin.js
  • locales/en/translation.json
  • test/unit/pages/ClinicPatients.test.js

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added modular patient filtering for tags, clinic sites, data recency, summary periods, CGM use, and time in range.
    • Added searchable filter controls, active-filter chips, clear/reset actions, and patient-count messaging.
    • Added clinic-admin options to edit sites and patient tags.
    • Added localized singular and plural count messages.
  • Bug Fixes

    • Improved filtering states for patients without sites or tags.
    • Improved summary-period display, including GMI and placeholder handling.
  • Tests

    • Expanded coverage across patient filtering, dropdown behavior, active filters, admin controls, and empty states.

Walkthrough

Clinic patient filtering was split into dedicated dropdowns and adapter components. ClinicPatients now uses shared filter state, applied-filter chips, query-state controls, time-in-range thresholds, and shared clinic-admin detection. Extensive unit coverage validates filter behavior and query parameters.

Changes

Clinic patient filter controls

Layer / File(s) Summary
Filter dropdowns and shared controls
app/pages/clinicworkspace/components/*, app/core/clinicUtils.js, app/pages/clinicworkspace/useClinicMetricsPageName.js, app/pages/clinicworkspace/useClinicPatientsFilters.js, app/pages/clinicworkspace/useIsClinicAdmin.js, locales/en/translation.json
Added tag, site, data-recency, CGM-use, time-in-range, summary-period, and clear-filter controls with shared thresholds, translations, metrics, and admin detection.
Filter adapters and applied filters
app/pages/clinicworkspace/clinicPatientsFilters/*, app/pages/clinicworkspace/components/ActiveFiltersTray.js
Added components that merge selections into active filters and display removable filter chips, patient counts, and clear actions.
ClinicPatients integration
app/pages/clinicworkspace/ClinicPatients.js, app/pages/clinicadmin/clinicadmin.js
Replaced inline filtering with modular controls and shared query-state handling. Clinic admin checks now use useIsClinicAdmin.
Filter validation
__tests__/unit/app/pages/clinicworkspace/**/*, test/unit/pages/ClinicPatients.test.js
Added coverage for filter controls, active-filter behavior, query parameters, metrics, special zero-state filters, and summary-period GMI display.

Estimated code review effort: 5 (Critical) | ~90 minutes

Possibly related PRs

Suggested reviewers: clintonium-119

Sequence Diagram(s)

sequenceDiagram
  participant ClinicPatientPage
  participant FilterDropdown
  participant ActiveFiltersList
  participant PatientQuery
  ClinicPatientPage->>FilterDropdown: render and apply filter selection
  FilterDropdown->>ActiveFiltersList: update active filter state
  ActiveFiltersList->>PatientQuery: derive query state and filter parameters
  PatientQuery->>ClinicPatientPage: refresh patient table and empty state
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Description check ❓ Inconclusive No pull request description was provided, so it does not communicate any meaningful information about the changeset. Add a brief description that summarizes the clinic patient filtering refactor and its new filter components and tests.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title identifies the CGM-use filtering change, which is a real part of the broader filtering refactor.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch WEB-4654-cgm-use

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

__tests__/unit/app/pages/clinicworkspace/ClinicPatients.test.js

Parsing error: [BABEL] /tests/unit/app/pages/clinicworkspace/ClinicPatients.test.js: Using babel-preset-react-app requires that you specify NODE_ENV or BABEL_ENV environment variables. Valid values are "development", "test", and "production". Instead, received: undefined. (While processing: "/node_modules/babel-preset-react-app/index.js")

__tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.test.js

Parsing error: [BABEL] /tests/unit/app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.test.js: Using babel-preset-react-app requires that you specify NODE_ENV or BABEL_ENV environment variables. Valid values are "development", "test", and "production". Instead, received: undefined. (While processing: "/node_modules/babel-preset-react-app/index.js")

__tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.test.js

Parsing error: [BABEL] /tests/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.test.js: Using babel-preset-react-app requires that you specify NODE_ENV or BABEL_ENV environment variables. Valid values are "development", "test", and "production". Instead, received: undefined. (While processing: "/node_modules/babel-preset-react-app/index.js")

  • 34 others
🔧 ast-grep (0.45.1)
test/unit/pages/ClinicPatients.test.js

ast-grep timed out on this file


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.

@henry-tp

henry-tp commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

This PR was previously approved up to commit 187f974 in PR #1991 by @clintonium-119

However, it was accidentally closed and deleted, which prevents it from being merged. I am re-opening this new PR to be able to merge it.

I am no seeking re-approval as it was already approved in PR #1991

@henry-tp
henry-tp merged commit dd30a22 into feat/filter-enhancements Aug 12, 2026
4 of 5 checks passed
@henry-tp
henry-tp deleted the WEB-4654-cgm-use branch August 12, 2026 21:09

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (1)
app/pages/clinicworkspace/ClinicPatients.js (1)

885-895: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Make the cleanup update atomic.

useLocalStorage evaluates updater functions against the setter’s captured storedValue. Update it to apply the updater through React’s functional setStoredValue, then use that updater here to preserve unrelated filters.

🤖 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 `@app/pages/clinicworkspace/ClinicPatients.js` around lines 885 - 895, Make
useLocalStorage apply updater functions via React’s functional setStoredValue so
each update receives the latest stored value. In the ClinicPatients
filter-cleanup effect, call setActiveFilters with a functional updater and
derive patientTags and clinicSites from the previous filters, preserving all
unrelated filter fields while removing invalid values.
🧹 Nitpick comments (20)
app/pages/clinicworkspace/components/CGMUseFilterDropdown.js (2)

19-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Derive the PropTypes values from a plain value list.

Line 157 calls getCgmUseFilterOptions(label => label) only to recover the option values. The identity function stands in for t. The intent is not obvious, and the call couples the PropTypes declaration to the label-building signature. Extract the values into a separate constant.

♻️ Proposed extraction
-const getCgmUseFilterOptions = (t) => [
-  { value: '<0.7', label: t('Less than 70%') },
-  { value: '>=0.7', label: t('70% or more') },
-];
+const CGM_USE_FILTER_VALUES = ['<0.7', '>=0.7'];
+
+const getCgmUseFilterOptions = (t) => [
+  { value: '<0.7', label: t('Less than 70%') },
+  { value: '>=0.7', label: t('70% or more') },
+];
-  timeCGMUsePercent: PropTypes.oneOf(getCgmUseFilterOptions(label => label).map(opt => opt.value)),
+  timeCGMUsePercent: PropTypes.oneOf(CGM_USE_FILTER_VALUES),

The guideline requires UPPER_SNAKE_CASE for constants. As per coding guidelines: "Use PascalCase for components, camelCase for utilities, and UPPER_SNAKE_CASE for constants."

Also applies to: 157-157

🤖 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 `@app/pages/clinicworkspace/components/CGMUseFilterDropdown.js` around lines 19
- 22, Extract the option values into a separate UPPER_SNAKE_CASE constant and
have getCgmUseFilterOptions map those values to translated labels. Update the
PropTypes declaration to derive its values directly from this constant instead
of calling getCgmUseFilterOptions with an identity function.

Source: Coding guidelines


1-18: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Group the imports in the required order. All three new dropdowns place the local trackMetric import among the third-party imports and place the Lodash import after theme-ui. The shared root cause is one deviation from the required import grouping, repeated in each file. The required order is React, PropTypes, Redux, third-party libraries, Lodash imports, theme-ui, then local imports, with a blank line between groups.

  • app/pages/clinicworkspace/components/CGMUseFilterDropdown.js#L1-L18: move trackMetric down to the local group with the other ../../../ imports, and move lodash/noop above the theme-ui import.
  • app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js#L1-L18: apply the same two moves.
  • app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.js#L1-L19: apply the same two moves.
♻️ Example for CGMUseFilterDropdown.js
 import React, { useState } from 'react';
 import PropTypes from 'prop-types';
 import { useSelector } from 'react-redux';
 import { useTranslation } from 'react-i18next';
-import { trackMetric } from '../../../core/metricUtils';
 import { colors as vizColors } from '`@tidepool/viz`';
-
-import { Box, Grid } from 'theme-ui';
 import KeyboardArrowDownRoundedIcon from '`@material-ui/icons/KeyboardArrowDownRounded`';
+import { bindPopover, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks';
+
 import noop from 'lodash/noop';
 
-import { bindPopover, bindTrigger, usePopupState } from 'material-ui-popup-state/hooks';
+import { Box, Grid } from 'theme-ui';
 
+import { trackMetric } from '../../../core/metricUtils';
 import Button from '../../../components/elements/Button';

As per coding guidelines: "Group imports in the required order with blank lines between groups: React, PropTypes, Redux, third-party libraries, Lodash specific imports, theme-ui, then local imports."

🤖 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 `@app/pages/clinicworkspace/components/CGMUseFilterDropdown.js` around lines 1
- 18, Reorder imports in CGMUseFilterDropdown.js (lines 1-18),
DataRecencyFilterDropdown.js (lines 1-18), and SummaryPeriodFilterDropdown.js
(lines 1-19): place lodash/noop before theme-ui, move trackMetric into the local
../../../ import group, and preserve blank lines between the required React,
PropTypes, Redux, third-party, Lodash, theme-ui, and local groups.

Source: Coding guidelines

app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.js (1)

12-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Pass the setter directly, or wrap it in useCallback.

handleChange forwards its argument to setActiveSummaryPeriod without change. The wrapper adds no behavior, and a new function identity is created on every render.

♻️ Proposed simplification
-  const handleChange = (summaryPeriod) => {
-    setActiveSummaryPeriod(summaryPeriod);
-  };
-
   return (
     <SummaryPeriodFilterDropdown
-      onChange={handleChange}
+      onChange={setActiveSummaryPeriod}
       activeSummaryPeriod={activeSummaryPeriod}
     />
   );

The sibling adapters need the wrapper because they merge into activeFilters. This one does not. As per coding guidelines: "use useCallback for callback props".

🤖 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 `@app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.js`
around lines 12 - 20, Update FilterBySummaryPeriod’s SummaryPeriodFilterDropdown
usage to pass setActiveSummaryPeriod directly as onChange, removing the
redundant handleChange wrapper; alternatively memoize the wrapper with
useCallback if the component requires it.

Source: Coding guidelines

app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js (1)

128-131: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keep one fallback for filterOptions.

Line 130 sets a default parameter, and line 180 repeats the fallback with filterOptions || lastDataFilterOptions. The default parameter covers undefined, and the || covers an explicit null. Both paths resolve to the same value. Drop the default parameter or the inline fallback so the source of the default is unambiguous.

♻️ Proposed simplification
-            filterOptions={filterOptions || lastDataFilterOptions}
+            filterOptions={filterOptions}

Also applies to: 176-184

🤖 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 `@app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js` around
lines 128 - 131, Keep only one fallback for filterOptions in the component
function and its usage near the filter construction: either remove the default
parameter or remove the inline “|| lastDataFilterOptions” fallback. Preserve the
existing behavior for undefined and null values while making the default source
unambiguous.
__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js (3)

53-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename the Jest test titles to the required form. The new dropdown test files use behavior-describing titles that do not follow the required should do X when Y form. The shared root cause is one naming convention applied inconsistently across the new Jest suites.

  • __tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js#L53-L53: rename all five it titles in this file, for example should call onChange with the selected ranges when Apply is clicked.
  • __tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js#L48-L48: rename all three it titles in this file, for example should apply the summary period when a different radio is selected.

As per coding guidelines: "In Jest tests, use jest.fn() for mocks, clear mocks in beforeEach or afterEach, use descriptive names like should do X when Y, and test interactions with userEvent."

🤖 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
`@__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js`
at line 53, Rename all five Jest test titles in
__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js
(anchor 53-53) and all three titles in
__tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js
(sibling 48-48) to descriptive “should do X when Y” forms, accurately describing
each test’s behavior and trigger; make no other changes.

Source: Coding guidelines


12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Declare the metricUtils mock explicitly, and drop the unused mock handle. Both new dropdown test files import trackMetric as mockTrackMetric and call mockTrackMetric.mockClear() without a local jest.mock for that module. The shared root cause is an implicit dependency on a global mock configuration.

  • __tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js#L12-L12: this file never asserts on mockTrackMetric. Remove the import and the mockClear() call at line 49, or add assertions for the open and close metrics. This file already declares jest.mock('launchdarkly-react-client-sdk') at line 14, which shows the intended explicit style.
  • __tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js#L10-L10: this file asserts on mockTrackMetric, so add jest.mock('@app/core/metricUtils') and import through the @app alias for consistency with line 9.
🤖 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
`@__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js`
at line 12, In
__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js:12-12,
remove the unused trackMetric import and its mockClear call, since this test
does not assert metrics. In
__tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js:10-10,
explicitly mock `@app/core/metricUtils` and import trackMetric through the `@app`
alias, preserving its existing metric assertions.

100-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Confirm the popup stays open across the rerender call.

This test opens the dropdown, changes the useFlags return value, and then calls rerender(ui()). The assertion at line 110 depends on usePopupState keeping the open state through the rerender. That holds while React reconciles the same element type and preserves the internal state. The behavior is implicit, so a future change to the popup state library or to the wrapper structure would make this test pass or fail for the wrong reason. Add an explicit assertion that the dropdown is still open after the rerender.

🧪 Proposed guard assertion
       useFlags.mockReturnValue({ showExtremeHigh: true });
       rerender(ui());
+      expect(screen.getByTestId('time-in-range-filter-dropdown')).toBeInTheDocument();
       expect(screen.getByRole('checkbox', { name: /Extremely High/ })).toBeInTheDocument();
🤖 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
`@__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js`
around lines 100 - 111, Update the test “shows the highest range option only
when the showExtremeHigh flag is set” to explicitly assert that the Time in
Range dropdown remains open immediately after rerender(ui()). Keep the existing
Extremely High visibility assertion and use the dropdown’s existing open-state
indicator or trigger/popup query.
__tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.test.js (1)

46-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the required should do X when Y test titles.

Both it titles state the behavior but do not follow the required form.

✏️ Proposed titles
-    it('calls setActiveSummaryPeriod with the newly selected period', async () => {
+    it('should call setActiveSummaryPeriod with the selected period when Apply is clicked', async () => {
-    it('reflects the active summary period in the trigger label and the pre-selected radio', async () => {
+    it('should show the active period in the trigger label and pre-check its radio when activeSummaryPeriod is set', async () => {

As per coding guidelines: "In Jest tests, use jest.fn() for mocks, clear mocks in beforeEach or afterEach, use descriptive names like should do X when Y, and test interactions with userEvent."

Also applies to: 64-64

🤖 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
`@__tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.test.js`
at line 46, Rename the affected Jest test titles in the filter summary period
tests to follow the required “should do X when Y” format, including the test at
the visible setActiveSummaryPeriod case and the additional test at the
referenced location. Preserve each test’s existing behavior and assertions.

Source: Coding guidelines

__tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js (1)

93-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the close-icon metric.

The tests assert the open, apply, and cancel metrics. SummaryPeriodFilterDropdown also emits 'Clinic - Summary period filter close' from onClickCloseIcon. No test exercises that path, so a regression in the close handler stays undetected. Add one assertion that clicks the popover close icon and checks the emitted event.

🤖 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
`@__tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js`
around lines 93 - 111, Add coverage in the SummaryPeriodFilterDropdown tests for
the popover close-icon path: open the dropdown, click the close icon through its
accessible control, and assert mockTrackMetric was called with 'Clinic - Summary
period filter close' and the same clinicId/pageName metadata used by the other
metric assertions.
app/pages/clinicworkspace/components/SiteFilterDropdown.js (2)

204-218: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use past-tense metric event names.

The new event names use open, close, apply, and clear. Rename them to the established past-tense form. Update the matching test expectations.

  • app/pages/clinicworkspace/components/SiteFilterDropdown.js#L204-L218: rename clear and apply events.
  • app/pages/clinicworkspace/components/SiteFilterDropdown.js#L255-L298: rename open and close events.
  • app/pages/clinicworkspace/components/TagFilterDropdown.js#L207-L220: rename clear and apply events.
  • app/pages/clinicworkspace/components/TagFilterDropdown.js#L258-L301: rename open and close events.
  • __tests__/unit/app/pages/clinicworkspace/components/SiteFilterDropdown.test.js#L93-L130: update metric assertions.
  • __tests__/unit/app/pages/clinicworkspace/components/TagFilterDropdown.test.js#L93-L130: update metric assertions.

Based on learnings, use past-tense event name strings for trackMetric calls.

🤖 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 `@app/pages/clinicworkspace/components/SiteFilterDropdown.js` around lines 204
- 218, Rename the trackMetric event strings to their established past-tense
forms for clear/apply in SiteFilterDropdown and TagFilterDropdown, and for
open/close in both dropdown components. Update the corresponding metric
assertions in app/pages/clinicworkspace/components/SiteFilterDropdown.js lines
204-218 and 255-298, app/pages/clinicworkspace/components/TagFilterDropdown.js
lines 207-220 and 258-301, and both matching test files at lines 93-130 to
expect the renamed events.

Source: Learnings


37-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Define PropTypes for the private dropdown components.

EditSitesAction, DropdownContent, EditTagsAction, and DropdownContent accept props without runtime contracts. Define their callback and filter-array PropTypes.

  • app/pages/clinicworkspace/components/SiteFilterDropdown.js#L37-L77: add PropTypes for EditSitesAction and DropdownContent.
  • app/pages/clinicworkspace/components/TagFilterDropdown.js#L37-L80: add PropTypes for EditTagsAction and DropdownContent.

As per coding guidelines, “Define PropTypes for all component props.”

🤖 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 `@app/pages/clinicworkspace/components/SiteFilterDropdown.js` around lines 37 -
77, Define runtime PropTypes for the private components EditSitesAction and
DropdownContent in app/pages/clinicworkspace/components/SiteFilterDropdown.js
(lines 37-77), covering the callback props and clinicSites filter array. Apply
the same PropTypes definitions to EditTagsAction and DropdownContent in
app/pages/clinicworkspace/components/TagFilterDropdown.js (lines 37-80),
covering their callbacks and tag filter array.

Source: Coding guidelines

app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.js (1)

25-30: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Align the timeCGMUsePercent PropType with the sibling components.

ActiveFiltersTray.js (line 273) and AppliedFiltersList.js (line 123) both declare this field as PropTypes.oneOf(['<0.7', '>=0.7']). This file declares PropTypes.string. The value is used directly as the cgm.timeCGMUsePercent query parameter, so the narrower type documents the contract and catches an invalid value at the adapter boundary.

♻️ Proposed refactor
 FilterByCGMUse.propTypes = {
   activeFilters: PropTypes.shape({
-    timeCGMUsePercent: PropTypes.string,
+    timeCGMUsePercent: PropTypes.oneOf(['<0.7', '>=0.7']),
   }),
   setActiveFilters: PropTypes.func,
 };
🤖 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 `@app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.js` around
lines 25 - 30, Update the `timeCGMUsePercent` declaration in
`FilterByCGMUse.propTypes` from `PropTypes.string` to `PropTypes.oneOf(['<0.7',
'>=0.7'])`, matching the contract used by `ActiveFiltersTray` and
`AppliedFiltersList`.
app/pages/clinicworkspace/components/ActiveFiltersTray.js (2)

114-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add PropTypes to Chip and ChipGroup.

ActiveFiltersTray declares PropTypes at lines 269-281, but Chip (line 114) and ChipGroup (line 167) declare none. Both are React components with required props: Chip needs label and onRemove, and ChipGroup needs chips and onRemove.

♻️ Proposed addition
+Chip.propTypes = {
+  label: PropTypes.string.isRequired,
+  onRemove: PropTypes.func.isRequired,
+};
+
 const ChipGroup = ({ prefix, chips, onRemove }) => {
+ChipGroup.propTypes = {
+  prefix: PropTypes.node,
+  chips: PropTypes.arrayOf(PropTypes.shape({
+    type: PropTypes.string.isRequired,
+    value: PropTypes.string,
+    label: PropTypes.string,
+  })),
+  onRemove: PropTypes.func.isRequired,
+};
+
 const ActiveFiltersTray = ({

As per coding guidelines: "Define PropTypes for all component props."

🤖 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 `@app/pages/clinicworkspace/components/ActiveFiltersTray.js` around lines 114 -
183, Add PropTypes declarations for the Chip and ChipGroup components. Mark
Chip.label and Chip.onRemove as required, and mark ChipGroup.chips and
ChipGroup.onRemove as required, reusing the file’s existing PropTypes import and
conventions.

Source: Coding guidelines


68-112: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared chip logic from useTagChips and useSiteChips.

The two hooks are structurally identical. They differ only in the filter key, the clinic collection, the special-state constant, and the empty label. A single parameterized hook removes the duplication and keeps future changes, such as new sorting rules, in one place.

♻️ Proposed refactor
-const useTagChips = (patientTags = []) => {
-  const { t } = useTranslation();
-  const selectedClinicId = useSelector(state => state.blip.selectedClinicId);
-  const clinic = useSelector(state => state.blip.clinics?.[selectedClinicId]);
-
-  if (isEqual(patientTags, SPECIAL_FILTER_STATES.ZERO_TAGS)) {
-    return [{
-      type: 'patientTags',
-      value: SPECIAL_FILTER_STATES.ZERO_TAGS[0],
-      label: t('No tags'),
-    }];
-  }
-
-  return patientTags
-    .map(id => ({
-      type: 'patientTags',
-      value: id,
-      label: find(clinic?.patientTags, { id })?.name,
-    }))
-    .filter(chip => chip.label)
-    .toSorted((a, b) => utils.compareLabels(a.label, b.label));
-};
-
-const useSiteChips = (clinicSites = []) => {
-  const { t } = useTranslation();
-  const selectedClinicId = useSelector(state => state.blip.selectedClinicId);
-  const clinic = useSelector(state => state.blip.clinics?.[selectedClinicId]);
-
-  if (isEqual(clinicSites, SPECIAL_FILTER_STATES.ZERO_SITES)) {
-    return [{
-      type: 'clinicSites',
-      value: SPECIAL_FILTER_STATES.ZERO_SITES[0],
-      label: t('No clinic sites'),
-    }];
-  }
-
-  return clinicSites
-    .map(id => ({
-      type: 'clinicSites',
-      value: id,
-      label: find(clinic?.sites, { id })?.name,
-    }))
-    .filter(chip => chip.label)
-    .toSorted((a, b) => utils.compareLabels(a.label, b.label));
-};
+const useEntityChips = ({ ids = [], type, clinicField, zeroState, zeroLabel }) => {
+  const selectedClinicId = useSelector(state => state.blip.selectedClinicId);
+  const clinic = useSelector(state => state.blip.clinics?.[selectedClinicId]);
+
+  if (isEqual(ids, zeroState)) {
+    return [{ type, value: zeroState[0], label: zeroLabel }];
+  }
+
+  return ids
+    .map(id => ({ type, value: id, label: find(clinic?.[clinicField], { id })?.name }))
+    .filter(chip => chip.label)
+    .toSorted((a, b) => utils.compareLabels(a.label, b.label));
+};

Then call it from ActiveFiltersTray, where t is already available:

   const primaryChips = usePrimaryChips(filters);
-  const tagChips = useTagChips(filters.patientTags);
-  const siteChips = useSiteChips(filters.clinicSites);
+  const tagChips = useEntityChips({
+    ids: filters.patientTags,
+    type: 'patientTags',
+    clinicField: 'patientTags',
+    zeroState: SPECIAL_FILTER_STATES.ZERO_TAGS,
+    zeroLabel: t('No tags'),
+  });
+  const siteChips = useEntityChips({
+    ids: filters.clinicSites,
+    type: 'clinicSites',
+    clinicField: 'sites',
+    zeroState: SPECIAL_FILTER_STATES.ZERO_SITES,
+    zeroLabel: t('No clinic sites'),
+  });
🤖 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 `@app/pages/clinicworkspace/components/ActiveFiltersTray.js` around lines 68 -
112, Extract the duplicated logic from useTagChips and useSiteChips into one
parameterized chip hook or helper that accepts the filter key, clinic
collection, special-state value, and translated empty-state label. Preserve the
existing special-state handling, label lookup, filtering, and sorting, then
update ActiveFiltersTray to call the shared implementation for tags and sites
using the existing t function.
app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.js (1)

24-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move the vizUtils.bg destructure below the import block.

Line 27 places a const statement between import statements at lines 24-25 and lines 28-29. The code works because ES module imports are hoisted, but it breaks the grouped import block and hides the two local imports that follow it.

♻️ Proposed refactor
 import { colors } from '../../../themes/baseTheme';
 import { MGDL_UNITS } from '../../../core/constants';
-
-const { reshapeBgClassesToBgBounds, generateBgRangeLabels } = vizUtils.bg;
 import useClinicMetricsPageName from '../useClinicMetricsPageName';
 import { timeInRangeFilterThresholds } from '../../../core/clinicUtils';
+
+const { reshapeBgClassesToBgBounds, generateBgRangeLabels } = vizUtils.bg;

As per coding guidelines: "Group imports in the required order with blank lines between groups: React, PropTypes, Redux, third-party libraries, Lodash specific imports, theme-ui, then local imports."

🤖 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 `@app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.js` around
lines 24 - 29, Move the vizUtils.bg destructuring declaration below the complete
import block, keeping useClinicMetricsPageName and timeInRangeFilterThresholds
grouped with the other local imports; preserve the existing import ordering and
add the required group spacing without changing behavior.

Source: Coding guidelines

app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.js (1)

86-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive hasActiveFilters from getPatientQueryState instead of duplicating the checks.

Lines 86-93 repeat the exact condition already implemented in getPatientQueryState at lines 17-24. Two copies of the same rule will drift when a new filter key is added, and the tray would then hide while a filter is still applied.

♻️ Proposed refactor
-  const hasSearchActive = !!patientListSearchTextInput;
-
-  const hasActiveFilters = !!(
-    activeFilters.lastData ||
-    activeFilters.lastDataType ||
-    activeFilters.timeCGMUsePercent ||
-    activeFilters.timeInRange?.length > 0 ||
-    activeFilters.patientTags?.length > 0 ||
-    activeFilters.clinicSites?.length > 0
-  );
-
-  const isRendered = hasActiveFilters || hasSearchActive;
-
-  if (!isRendered) return null;
-
-  const patientQueryState = getPatientQueryState(activeFilters, patientListSearchTextInput);
+  const hasSearchActive = !!patientListSearchTextInput;
+  const patientQueryState = getPatientQueryState(activeFilters, patientListSearchTextInput);
+
+  if (patientQueryState === PATIENT_QUERY_STATE.NONE) return null;
🤖 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 `@app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.js` around
lines 86 - 93, Update hasActiveFilters in AppliedFiltersList to derive its value
from the existing getPatientQueryState result instead of duplicating individual
activeFilters checks. Reuse that helper’s established active-filter state so
newly supported filter keys remain consistent with the tray’s visibility
behavior.
app/pages/clinicworkspace/components/ClearFilterButtons.js (1)

15-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Set an explicit button type on ClearButton.

styled.button produces a native button that defaults to type="submit". If this component is ever rendered inside a form, a click submits the form in addition to calling the handler. Set the type on the styled component so every usage is safe.

♻️ Proposed refactor
 const ClearButton = styled.button`
   background: none;
   color: ${vizColors.indigo30};
   border: none;
   padding: 0;
   font: inherit;
   cursor: pointer;
   text-underline-offset: 4px;
   text-decoration: underline;
 `;
+
+ClearButton.defaultProps = { type: 'button' };

An alternative is to pass type="button" at each of the four call sites.

🤖 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 `@app/pages/clinicworkspace/components/ClearFilterButtons.js` around lines 15 -
24, Update the styled.button definition for ClearButton to set its default
native button type to "button", ensuring all usages avoid unintended form
submission without changing the call sites.
__tests__/unit/app/pages/clinicworkspace/ClinicPatients.test.js (1)

388-388: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename the six new tests to the should ... when ... form.

All six new test names start with maps an applied ... filter into the getPatientsForClinic query. The surrounding tests in this file use the should ... form, for example should allow creating a new site for a workspace at line 524.

♻️ Proposed renames
-          it('maps an applied tag filter into the getPatientsForClinic query', async () => {
+          it('should include tags in the getPatientsForClinic query when a tag filter is applied', async () => {
-          it('maps an applied summary period filter into the getPatientsForClinic query', async () => {
+          it('should set the period in the getPatientsForClinic query when a summary period is applied', async () => {
-          it('maps an applied site filter into the getPatientsForClinic query', async () => {
+          it('should include sites in the getPatientsForClinic query when a site filter is applied', async () => {
-          it('maps an applied data recency filter into the getPatientsForClinic query', async () => {
+          it('should include lastData bounds in the getPatientsForClinic query when a data recency filter is applied', async () => {
-          it('maps an applied time in range filter into the getPatientsForClinic query', async () => {
+          it('should include range comparators in the getPatientsForClinic query when a time in range filter is applied', async () => {
-          it('maps an applied cgm use filter into the getPatientsForClinic query', async () => {
+          it('should include timeCGMUsePercent in the getPatientsForClinic query when a CGM use filter is applied', async () => {

As per coding guidelines: "In Jest tests, use jest.fn() for mocks, clear mocks in beforeEach or afterEach, use descriptive names like should do X when Y, and test interactions with userEvent."

Also applies to: 408-408, 427-427, 447-447, 475-475, 500-500

🤖 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 `@__tests__/unit/app/pages/clinicworkspace/ClinicPatients.test.js` at line 388,
Rename the six newly added test cases around the getPatientsForClinic query to
the repository’s “should ... when ...” naming convention, preserving each test’s
existing filter-specific behavior and intent. Update the tests at the applied
tag, status, search, pagination, sorting, and other filter scenarios without
changing their implementations.

Source: Coding guidelines

app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.js (1)

21-21: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Hoist customLastDataFilterOptions to module scope. lastDataFilterOptions is an array, and reject removes only value 7. The current component creates a new options array on each render.

🤖 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 `@app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.js` at
line 21, Move the customLastDataFilterOptions definition out of the component
and into module scope, alongside lastDataFilterOptions, so reject is evaluated
only once while preserving the existing removal of value 7.
app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js (1)

1-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reorder the import groups.

Place Redux imports before other third-party imports. In the adapters, place the Lodash import before local action and metric imports.

  • app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js#L1-L10: move lodash/noop before the local Redux action and metric imports.
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.js#L1-L10: move lodash/noop before the local Redux action and metric imports.
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.test.js#L1-L9: move Redux imports before Testing Library imports.
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.test.js#L1-L9: move Redux imports before Testing Library imports.
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.test.js#L1-L13: move Redux imports before Testing Library imports.
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.test.js#L1-L13: move Redux imports before Testing Library imports.
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTimeInRange.test.js#L1-L10: move Redux imports before Testing Library imports.
  • __tests__/unit/app/pages/clinicworkspace/components/CGMUseFilterDropdown.test.js#L1-L10: move Redux imports before Testing Library imports.
  • __tests__/unit/app/pages/clinicworkspace/components/DataRecencyFilterDropdown.test.js#L1-L10: move Redux imports before Testing Library imports.

As per coding guidelines: “Group imports in the required order with blank lines between groups.”

🤖 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 `@app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js` around
lines 1 - 10, Reorder imports into the required groups with blank lines between
them. In app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js and
FilterByTags.js, place lodash/noop before the local Redux action and metric
imports; in each listed test file, place Redux imports before Testing Library
imports. Apply the same ordering to all nine specified files and make no other
changes.

Source: Coding guidelines

🤖 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 `@app/pages/clinicworkspace/ClinicPatients.js`:
- Around line 1054-1056: Guard the timeInRangeFilterThresholds lookup in the
activeFilters.timeInRange iteration before destructuring, skipping unknown
filter keys so the fetch-options effect continues rendering. Also remove
unrecognized keys from activeFilters.timeInRange so filter counts and
applied-filter chips match the filters used in the query.
- Line 3157: Update the memoized renderPeopleTable callback dependencies to
include patientListSearchTextInput, handleClearSearch, and handleResetFilters.
Wrap handleClearSearch and handleResetFilters in useCallback with complete
dependencies before adding them, ensuring patientQueryState and clear controls
update when search text or filter actions change.

In `@app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js`:
- Around line 29-32: Update handleClickEditSites and the corresponding tag edit
handler so each fetch dispatch occurs before trackMetric, with tracking called
immediately afterward and using the selected past-tense event name. Update the
metric expectations in
app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js:104-109 and
app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.js:106-111 to
match; apply the handler changes in
app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js:29-32 and
FilterByTags.js:29-32.

In `@app/pages/clinicworkspace/components/CGMUseFilterDropdown.js`:
- Around line 24-28: Define PropTypes for the inner DropdownContent component in
app/pages/clinicworkspace/components/CGMUseFilterDropdown.js lines 24-28 for
required onClose, onChange, and timeCGMUsePercent props;
app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js lines 20-26
for onClose, onChange, lastData, lastDataType, and filterOptions; and
app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.js lines 32-36
for onClose, onChange, and activeSummaryPeriod, using the appropriate existing
PropTypes shapes and requiredness.

In `@app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js`:
- Around line 156-164: Update the iconLabel on the last-data-filter-trigger
Button to use the same “Data Recency” wording as its visible label, and update
any tests or selectors that depend on “Filter by last upload.”
- Line 89: In DataRecencyFilterDropdown.js at lines 89-89 and 150-150, define
and reuse one approved metric-name prefix for every open, close, apply, and
clear event, replacing the inconsistent Last data/Last upload variants while
preserving or migrating existing dashboard names. In CGMUseFilterDropdown.js at
line 117, apply the same consistency to all dropdown events by standardizing CGM
Use versus CGM use under one approved prefix; update each affected trackMetric
call rather than only the cited clear event.

In `@app/pages/clinicworkspace/components/SiteFilterDropdown.js`:
- Around line 260-267: Use react-i18next through the existing component
translation pattern, then pass the translated strings to the trigger iconLabel
props: update SiteFilterDropdown.js lines 260-267 for “Filter by clinic sites”
and TagFilterDropdown.js lines 263-270 for “Filter by patient tags”; ensure both
components obtain the translation function via useTranslation() or
withTranslation().
- Around line 89-92: Replace the unsupported toSorted call in
sortedSiteFilterOptions with sort on the newly mapped array, preserving label
comparison and avoiding mutation of the source sites. Apply the same change in
app/pages/clinicworkspace/components/TagFilterDropdown.js lines 92-95.

---

Outside diff comments:
In `@app/pages/clinicworkspace/ClinicPatients.js`:
- Around line 885-895: Make useLocalStorage apply updater functions via React’s
functional setStoredValue so each update receives the latest stored value. In
the ClinicPatients filter-cleanup effect, call setActiveFilters with a
functional updater and derive patientTags and clinicSites from the previous
filters, preserving all unrelated filter fields while removing invalid values.

---

Nitpick comments:
In `@__tests__/unit/app/pages/clinicworkspace/ClinicPatients.test.js`:
- Line 388: Rename the six newly added test cases around the
getPatientsForClinic query to the repository’s “should ... when ...” naming
convention, preserving each test’s existing filter-specific behavior and intent.
Update the tests at the applied tag, status, search, pagination, sorting, and
other filter scenarios without changing their implementations.

In
`@__tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.test.js`:
- Line 46: Rename the affected Jest test titles in the filter summary period
tests to follow the required “should do X when Y” format, including the test at
the visible setActiveSummaryPeriod case and the additional test at the
referenced location. Preserve each test’s existing behavior and assertions.

In
`@__tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js`:
- Around line 93-111: Add coverage in the SummaryPeriodFilterDropdown tests for
the popover close-icon path: open the dropdown, click the close icon through its
accessible control, and assert mockTrackMetric was called with 'Clinic - Summary
period filter close' and the same clinicId/pageName metadata used by the other
metric assertions.

In
`@__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js`:
- Line 53: Rename all five Jest test titles in
__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js
(anchor 53-53) and all three titles in
__tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js
(sibling 48-48) to descriptive “should do X when Y” forms, accurately describing
each test’s behavior and trigger; make no other changes.
- Line 12: In
__tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js:12-12,
remove the unused trackMetric import and its mockClear call, since this test
does not assert metrics. In
__tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js:10-10,
explicitly mock `@app/core/metricUtils` and import trackMetric through the `@app`
alias, preserving its existing metric assertions.
- Around line 100-111: Update the test “shows the highest range option only when
the showExtremeHigh flag is set” to explicitly assert that the Time in Range
dropdown remains open immediately after rerender(ui()). Keep the existing
Extremely High visibility assertion and use the dropdown’s existing open-state
indicator or trigger/popup query.

In `@app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.js`:
- Around line 86-93: Update hasActiveFilters in AppliedFiltersList to derive its
value from the existing getPatientQueryState result instead of duplicating
individual activeFilters checks. Reuse that helper’s established active-filter
state so newly supported filter keys remain consistent with the tray’s
visibility behavior.

In `@app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.js`:
- Around line 25-30: Update the `timeCGMUsePercent` declaration in
`FilterByCGMUse.propTypes` from `PropTypes.string` to `PropTypes.oneOf(['<0.7',
'>=0.7'])`, matching the contract used by `ActiveFiltersTray` and
`AppliedFiltersList`.

In `@app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.js`:
- Line 21: Move the customLastDataFilterOptions definition out of the component
and into module scope, alongside lastDataFilterOptions, so reject is evaluated
only once while preserving the existing removal of value 7.

In `@app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js`:
- Around line 1-10: Reorder imports into the required groups with blank lines
between them. In
app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js and
FilterByTags.js, place lodash/noop before the local Redux action and metric
imports; in each listed test file, place Redux imports before Testing Library
imports. Apply the same ordering to all nine specified files and make no other
changes.

In `@app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.js`:
- Around line 12-20: Update FilterBySummaryPeriod’s SummaryPeriodFilterDropdown
usage to pass setActiveSummaryPeriod directly as onChange, removing the
redundant handleChange wrapper; alternatively memoize the wrapper with
useCallback if the component requires it.

In `@app/pages/clinicworkspace/components/ActiveFiltersTray.js`:
- Around line 114-183: Add PropTypes declarations for the Chip and ChipGroup
components. Mark Chip.label and Chip.onRemove as required, and mark
ChipGroup.chips and ChipGroup.onRemove as required, reusing the file’s existing
PropTypes import and conventions.
- Around line 68-112: Extract the duplicated logic from useTagChips and
useSiteChips into one parameterized chip hook or helper that accepts the filter
key, clinic collection, special-state value, and translated empty-state label.
Preserve the existing special-state handling, label lookup, filtering, and
sorting, then update ActiveFiltersTray to call the shared implementation for
tags and sites using the existing t function.

In `@app/pages/clinicworkspace/components/CGMUseFilterDropdown.js`:
- Around line 19-22: Extract the option values into a separate UPPER_SNAKE_CASE
constant and have getCgmUseFilterOptions map those values to translated labels.
Update the PropTypes declaration to derive its values directly from this
constant instead of calling getCgmUseFilterOptions with an identity function.
- Around line 1-18: Reorder imports in CGMUseFilterDropdown.js (lines 1-18),
DataRecencyFilterDropdown.js (lines 1-18), and SummaryPeriodFilterDropdown.js
(lines 1-19): place lodash/noop before theme-ui, move trackMetric into the local
../../../ import group, and preserve blank lines between the required React,
PropTypes, Redux, third-party, Lodash, theme-ui, and local groups.

In `@app/pages/clinicworkspace/components/ClearFilterButtons.js`:
- Around line 15-24: Update the styled.button definition for ClearButton to set
its default native button type to "button", ensuring all usages avoid unintended
form submission without changing the call sites.

In `@app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js`:
- Around line 128-131: Keep only one fallback for filterOptions in the component
function and its usage near the filter construction: either remove the default
parameter or remove the inline “|| lastDataFilterOptions” fallback. Preserve the
existing behavior for undefined and null values while making the default source
unambiguous.

In `@app/pages/clinicworkspace/components/SiteFilterDropdown.js`:
- Around line 204-218: Rename the trackMetric event strings to their established
past-tense forms for clear/apply in SiteFilterDropdown and TagFilterDropdown,
and for open/close in both dropdown components. Update the corresponding metric
assertions in app/pages/clinicworkspace/components/SiteFilterDropdown.js lines
204-218 and 255-298, app/pages/clinicworkspace/components/TagFilterDropdown.js
lines 207-220 and 258-301, and both matching test files at lines 93-130 to
expect the renamed events.
- Around line 37-77: Define runtime PropTypes for the private components
EditSitesAction and DropdownContent in
app/pages/clinicworkspace/components/SiteFilterDropdown.js (lines 37-77),
covering the callback props and clinicSites filter array. Apply the same
PropTypes definitions to EditTagsAction and DropdownContent in
app/pages/clinicworkspace/components/TagFilterDropdown.js (lines 37-80),
covering their callbacks and tag filter array.

In `@app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.js`:
- Around line 24-29: Move the vizUtils.bg destructuring declaration below the
complete import block, keeping useClinicMetricsPageName and
timeInRangeFilterThresholds grouped with the other local imports; preserve the
existing import ordering and add the required group spacing without changing
behavior.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a0148559-4fba-492c-875d-585bb61a3825

📥 Commits

Reviewing files that changed from the base of the PR and between fbf6603 and 187f974.

⛔ Files ignored due to path filters (1)
  • app/core/icons/tagIcon.svg is excluded by !**/*.svg
📒 Files selected for processing (38)
  • __tests__/unit/app/pages/clinicworkspace/ClinicPatients.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.test.js
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTimeInRange.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/ActiveFiltersTray.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/CGMUseFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/DataRecencyFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/SiteFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/TagFilterDropdown.test.js
  • __tests__/unit/app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.test.js
  • app/core/clinicUtils.js
  • app/pages/clinicadmin/clinicadmin.js
  • app/pages/clinicworkspace/ClinicPatients.js
  • app/pages/clinicworkspace/clinicPatientsFilters/AppliedFiltersList.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByDataRecency.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterBySummaryPeriod.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.js
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByTimeInRange.js
  • app/pages/clinicworkspace/components/ActiveFiltersTray.js
  • app/pages/clinicworkspace/components/CGMUseFilterDropdown.js
  • app/pages/clinicworkspace/components/ClearFilterButtons.js
  • app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js
  • app/pages/clinicworkspace/components/SiteFilterDropdown.js
  • app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.js
  • app/pages/clinicworkspace/components/TagFilterDropdown.js
  • app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.js
  • app/pages/clinicworkspace/useClinicMetricsPageName.js
  • app/pages/clinicworkspace/useClinicPatientsFilters.js
  • app/pages/clinicworkspace/useIsClinicAdmin.js
  • locales/en/translation.json
  • test/unit/pages/ClinicPatients.test.js

Comment on lines +24 to +28
const DropdownContent = ({
onClose,
onChange,
timeCGMUsePercent,
}) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Define PropTypes for each DropdownContent component. All three new dropdowns declare PropTypes for the exported wrapper but omit them for the inner DropdownContent component. The shared root cause is one missing PropTypes declaration per file. DropdownContent receives required callbacks and value props, so a wrong prop type stays silent.

  • app/pages/clinicworkspace/components/CGMUseFilterDropdown.js#L24-L28: add PropTypes for onClose, onChange, and timeCGMUsePercent.
  • app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js#L20-L26: add PropTypes for onClose, onChange, lastData, lastDataType, and filterOptions.
  • app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.js#L32-L36: add PropTypes for onClose, onChange, and activeSummaryPeriod.
🛡️ Example for CGMUseFilterDropdown.js
+DropdownContent.propTypes = {
+  onClose: PropTypes.func.isRequired,
+  onChange: PropTypes.func.isRequired,
+  timeCGMUsePercent: PropTypes.string,
+};
+
 const CGMUseFilterDropdown = ({

As per coding guidelines: "Define PropTypes for all component props."

📍 Affects 3 files
  • app/pages/clinicworkspace/components/CGMUseFilterDropdown.js#L24-L28 (this comment)
  • app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js#L20-L26
  • app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.js#L32-L36
🤖 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 `@app/pages/clinicworkspace/components/CGMUseFilterDropdown.js` around lines 24
- 28, Define PropTypes for the inner DropdownContent component in
app/pages/clinicworkspace/components/CGMUseFilterDropdown.js lines 24-28 for
required onClose, onChange, and timeCGMUsePercent props;
app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js lines 20-26
for onClose, onChange, lastData, lastDataType, and filterOptions; and
app/pages/clinicworkspace/components/SummaryPeriodFilterDropdown.js lines 32-36
for onClose, onChange, and activeSummaryPeriod, using the appropriate existing
PropTypes shapes and requiredness.

Source: Coding guidelines

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

🛑 Comments failed to post (7)
app/pages/clinicworkspace/ClinicPatients.js (2)

1054-1056: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard the timeInRangeFilterThresholds lookup against unknown filter keys.

Line 1055 destructures timeInRangeFilterThresholds[filter] with no existence check. activeFilters.timeInRange is persisted state, so it can contain a key that is not present in timeInRangeFilterThresholds. The destructure then throws a TypeError inside the fetch-options effect and the patient list fails to render.

This is reachable today. test/unit/pages/ClinicPatients.test.js line 928 seeds timeInRange: ['timeInLowPercent'], which is not one of the six threshold keys (timeInVeryLowPercent, timeInAnyLowPercent, timeInTargetPercent, timeInAnyHighPercent, timeInVeryHighPercent, timeInExtremeHighPercent). Any user whose stored filters predate a range rename hits the same path.

🐛 Proposed fix
         forEach(activeFilters.timeInRange, filter => {
-          let { comparator, value } = timeInRangeFilterThresholds[filter];
+          const threshold = timeInRangeFilterThresholds[filter];
+          if (!threshold) return; // ignore unrecognized persisted range keys
+
+          let { comparator, value } = threshold;
           value = value / 100;

Consider also dropping unrecognized keys from activeFilters.timeInRange so the filter count and the applied-filter chips stay consistent with the query.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        forEach(activeFilters.timeInRange, filter => {
          const threshold = timeInRangeFilterThresholds[filter];
          if (!threshold) return; // ignore unrecognized persisted range keys

          let { comparator, value } = threshold;
          value = value / 100;
🤖 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 `@app/pages/clinicworkspace/ClinicPatients.js` around lines 1054 - 1056, Guard
the timeInRangeFilterThresholds lookup in the activeFilters.timeInRange
iteration before destructuring, skipping unknown filter keys so the
fetch-options effect continues rendering. Also remove unrecognized keys from
activeFilters.timeInRange so filter counts and applied-filter chips match the
filters used in the query.

3157-3157: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add patientListSearchTextInput to the renderPeopleTable dependencies.

Line 3157 reads patientListSearchTextInput to derive patientQueryState, but the dependency array at lines 3207-3220 omits it. renderPeopleTable is memoized, so when the user types or clears the search text without any other listed dependency changing, the memoized callback keeps the previous patientQueryState.

The user-visible result is a stale empty state and stale clear controls. Example: with a filter applied and no search text, the state is FILTER_ONLY and only Reset All Filters renders. After the user types a search term that matches nothing, the state should become FILTER_AND_SEARCH and Clear Search should appear, but the stale value keeps it hidden.

The same array also omits handleClearSearch and handleResetFilters. Those are plain function declarations that are recreated on every render, so wrap them in useCallback before adding them.

🐛 Proposed fix
   }, [
     activeFilters,
     clinic?.fetchedPatientCount,
     columns,
     data,
     defaultPatientFetchOptions.sort,
     handlePageChange,
     handleSortChange,
     loading,
     patientFetchOptions,
+    patientListSearchTextInput,
     setActiveFilters,
     showSummaryData,
     tableStyle,
   ]);

As per coding guidelines: "use useCallback for callback props, use useMemo for expensive computations, and respect react-hooks/exhaustive-deps."

Also applies to: 3207-3220

🤖 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 `@app/pages/clinicworkspace/ClinicPatients.js` at line 3157, Update the
memoized renderPeopleTable callback dependencies to include
patientListSearchTextInput, handleClearSearch, and handleResetFilters. Wrap
handleClearSearch and handleResetFilters in useCallback with complete
dependencies before adding them, ensuring patientQueryState and clear controls
update when search text or filter actions change.

Source: Coding guidelines

app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js (1)

29-32: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Use the established metric event contract.

Dispatch the fetch action first. Call trackMetric immediately after dispatch. Use a past-tense event name. Update the exact metric assertions with the selected event name.

  • app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js#L29-L32: move trackMetric after dispatch and rename the event.
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.js#L29-L32: move trackMetric after dispatch and rename the event.
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.test.js#L104-L109: update the expected event name.
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.test.js#L106-L111: update the expected event name.

Based on learnings: use past-tense event names and call trackMetric immediately after dispatching the related action at request initiation.

📍 Affects 4 files
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js#L29-L32 (this comment)
  • app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.js#L29-L32
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.test.js#L104-L109
  • __tests__/unit/app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.test.js#L106-L111
🤖 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 `@app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js` around
lines 29 - 32, Update handleClickEditSites and the corresponding tag edit
handler so each fetch dispatch occurs before trackMetric, with tracking called
immediately afterward and using the selected past-tense event name. Update the
metric expectations in
app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js:104-109 and
app/pages/clinicworkspace/clinicPatientsFilters/FilterByTags.js:106-111 to
match; apply the handler changes in
app/pages/clinicworkspace/clinicPatientsFilters/FilterBySites.js:29-32 and
FilterByTags.js:29-32.

Source: Learnings

app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js (2)

89-89: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List every clinic filter metric event name to check naming consistency.
rg -no --type=js "'Clinic - [^']+'" app | sort -u

Repository: tidepool-org/blip

Length of output: 10078


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- DataRecencyFilterDropdown.js ---'
sed -n '1,210p' app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js

printf '%s\n' '--- CGMUseFilterDropdown.js ---'
sed -n '1,175p' app/pages/clinicworkspace/components/CGMUseFilterDropdown.js

printf '%s\n' '--- Exact event-name references outside the components ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  -e 'Clinic - Last upload' \
  -e 'Clinic - Last data' \
  -e 'Clinic - CGM Use' \
  -e 'Clinic - CGM use' \
  app test tests 2>/dev/null || true

printf '%s\n' '--- Metric definitions and analytics consumers ---'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  -e 'trackMetric' \
  -e 'Last upload' \
  -e 'Last data' \
  -e 'CGM Use' \
  -e 'CGM use' \
  . | head -300

Repository: tidepool-org/blip

Length of output: 44858


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- metricUtils and bootstrap transport ---'
fd -i 'metricUtils' app
sed -n '1,180p' app/core/metricUtils.js 2>/dev/null || true
sed -n '35,90p' app/bootstrap.js

printf '%s\n' '--- Neighboring clinic filter metric implementations ---'
sed -n '180,320p' app/pages/clinicworkspace/components/SiteFilterDropdown.js
sed -n '185,320p' app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.js

printf '%s\n' '--- Read-only event grouping ---'
python3 - <<'PY'
import pathlib
import re
from collections import defaultdict

files = [
    pathlib.Path('app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js'),
    pathlib.Path('app/pages/clinicworkspace/components/CGMUseFilterDropdown.js'),
]
pattern = re.compile(r"trackMetric\(\s*'([^']+)'")
for path in files:
    events = pattern.findall(path.read_text())
    print(path)
    for event in events:
        print(f'  {event}')
    groups = defaultdict(list)
    for event in events:
        words = event.split(' filter ')
        groups[words[0]].append(event)
    if len(set(events)) != len(events):
        print('  duplicate event names: yes')
    print('  unique event names:', len(set(events)))
PY

Repository: tidepool-org/blip

Length of output: 10497


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- metricUtils and bootstrap transport ---'
fd -i 'metricUtils' app
sed -n '1,180p' app/core/metricUtils.js 2>/dev/null || true
sed -n '35,90p' app/bootstrap.js

printf '%s\n' '--- Neighboring clinic filter metric implementations ---'
sed -n '180,320p' app/pages/clinicworkspace/components/SiteFilterDropdown.js
sed -n '185,320p' app/pages/clinicworkspace/components/TimeInRangeFilterDropdown.js

printf '%s\n' '--- Read-only event grouping ---'
python3 - <<'PY'
import pathlib
import re

files = [
    pathlib.Path('app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js'),
    pathlib.Path('app/pages/clinicworkspace/components/CGMUseFilterDropdown.js'),
]
pattern = re.compile(r"trackMetric\(\s*'([^']+)'")
for path in files:
    events = pattern.findall(path.read_text())
    print(path)
    for event in events:
        print(f'  {event}')
    print('  unique event names:', len(set(events)))
PY

Repository: tidepool-org/blip

Length of output: 10497


Use one metric-name family per dropdown. trackMetric sends these strings as distinct event names, so the inconsistent Last data/Last upload and CGM Use/CGM use names split analytics funnels. Define one approved prefix per component and build all open, close, apply, and clear events from it. Preserve or migrate existing dashboard names before renaming them.

📍 Affects 2 files
  • app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js#L89-L89 (this comment)
  • app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js#L150-L150
  • app/pages/clinicworkspace/components/CGMUseFilterDropdown.js#L117-L117
🤖 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 `@app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js` at line
89, In DataRecencyFilterDropdown.js at lines 89-89 and 150-150, define and reuse
one approved metric-name prefix for every open, close, apply, and clear event,
replacing the inconsistent Last data/Last upload variants while preserving or
migrating existing dashboard names. In CGMUseFilterDropdown.js at line 117,
apply the same consistency to all dropdown events by standardizing CGM Use
versus CGM use under one approved prefix; update each affected trackMetric call
rather than only the cited clear event.

156-164: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the icon label with the visible trigger label.

The trigger displays Data Recency at line 163, but iconLabel at line 160 reads "Filter by last upload". Screen readers announce the combined name, so the spoken name does not match the visible name. The id at line 156 also uses last-data-filter-trigger while the child control ids use last-upload-*. Use Data Recency wording in the icon label.

♿ Proposed fix
-          iconLabel="Filter by last upload"
+          iconLabel="Filter by data recency"

If a test or an existing selector queries Filter by last upload, update it in the same change.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

          id="last-data-filter-trigger"
          selected={!!lastData}
          {...bindTrigger(lastDataPopupFilterState)}
          icon={KeyboardArrowDownRoundedIcon}
          iconLabel="Filter by data recency"
          sx={{ fontSize: 0, lineHeight: 1.3 }}
        >
          {t('Data Recency')}
        </Button>
🤖 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 `@app/pages/clinicworkspace/components/DataRecencyFilterDropdown.js` around
lines 156 - 164, Update the iconLabel on the last-data-filter-trigger Button to
use the same “Data Recency” wording as its visible label, and update any tests
or selectors that depend on “Filter by last upload.”
app/pages/clinicworkspace/components/SiteFilterDropdown.js (2)

89-92: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -HI -a 'package.json|.browserslistrc|babel.config.*|.babelrc*' .
rg -n --hidden 'browserslist|toSorted|core-js|polyfill' \
  -g 'package.json' -g '.browserslistrc' -g 'babel.config.*' -g '.babelrc*' -g '*.js' .

ast-grep run \
  --lang javascript \
  --pattern '$ARRAY.toSorted($$$)' \
  app/pages/clinicworkspace/components

Repository: tidepool-org/blip

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- tracked configuration files ---'
git ls-files \
  'package.json' \
  '.browserslistrc' \
  'babel.config.*' \
  '.babelrc*' \
  'config/**' \
  'public/**' | sed -n '1,120p'

printf '%s\n' '--- package scripts, browserslist, and polyfill-related dependencies ---'
python3 - <<'PY'
import json
from pathlib import Path

p = Path('package.json')
if p.exists():
    data = json.loads(p.read_text())
    print('scripts:', json.dumps(data.get('scripts', {}), indent=2))
    print('browserslist:', json.dumps(data.get('browserslist'), indent=2))
    deps = {**data.get('dependencies', {}), **data.get('devDependencies', {})}
    for name in sorted(deps):
        if any(term in name.lower() for term in ('babel', 'core-js', 'polyfill', 'browserslist')):
            print(f'{name}: {deps[name]}')
PY

printf '%s\n' '--- tracked browser/polyfill references ---'
git grep -n -E 'browserslist|toSorted|core-js|polyfill' -- \
  'package.json' '.browserslistrc' 'babel.config.*' '.babelrc*' \
  '*.js' '*.jsx' '*.ts' '*.tsx' 'config/**' 'public/**' \
  ':!**/node_modules/**' || true

printf '%s\n' '--- tracked toSorted call sites ---'
git grep -n -E '\\.toSorted\\s*\\(' -- \
  '*.js' '*.jsx' '*.ts' '*.tsx' ':!**/node_modules/**' || true

Repository: tidepool-org/blip

Length of output: 4264


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Babel configuration ---'
cat -n babel.config.js

printf '%s\n' '--- webpack polyfill entry configuration ---'
sed -n '175,225p' webpack.config.js

printf '%s\n' '--- package dependency metadata ---'
python3 - <<'PY'
import json
from pathlib import Path

for name in [
    'node_modules/@babel/polyfill/package.json',
    'node_modules/core-js/package.json',
    'node_modules/@babel/preset-env/package.json',
]:
    p = Path(name)
    print(f'--- {name} ---')
    if p.exists():
        data = json.loads(p.read_text())
        for key in ('version', 'main', 'dependencies', 'peerDependencies'):
            if key in data:
                print(f'{key}: {json.dumps(data[key], indent=2)}')
    else:
        print('not present')
PY

printf '%s\n' '--- polyfill imports and direct core-js usage ---'
git grep -n -E \"`@babel/polyfill`|core-js/(stable|features)|core-js/modules|regenerator-runtime\" -- \
  ':!**/node_modules/**' || true

printf '%s\n' '--- tracked toSorted call sites ---'
git grep -n -F '.toSorted(' -- \
  '*.js' '*.jsx' '*.ts' '*.tsx' ':!**/node_modules/**' || true

Repository: tidepool-org/blip

Length of output: 7336


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- `@babel/polyfill` implementation ---'
cat -n node_modules/@babel/polyfill/lib/index.js 2>/dev/null || true
cat -n node_modules/@babel/polyfill/lib/noConflict.js 2>/dev/null || true

printf '%s\n' '--- installed core-js package locations and versions ---'
find node_modules -path '*/core-js/package.json' -print \
  | while read -r p; do
      printf '%s: ' "$p"
      python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("version"))' "$p"
    done | sort

printf '%s\n' '--- toSorted polyfill availability in installed core-js packages ---'
find node_modules -path '*/core-js/features/array/to-sorted.js' -o \
            -path '*/core-js/modules/es.array.to-sorted.js' \
  -print

printf '%s\n' '--- application polyfill and Babel target references ---'
git grep -n -E '(`@babel/polyfill`|core-js/(stable|features)|useBuiltIns|targets|browserslist|toSorted)' -- \
  package.json babel.config.js webpack.config.js app jest.setup.js test '*.js' '*.jsx' \
  ':!**/node_modules/**' || true

Repository: tidepool-org/blip

Length of output: 4040


Replace toSorted with a supported sorting implementation.

@babel/polyfill uses core-js 2.6.12 and does not provide Array.prototype.toSorted. Babel does not transform this method. In browsers without toSorted, either dropdown fails during option computation. Use .sort() on the newly mapped array or add a toSorted polyfill in both dropdowns.

📍 Affects 2 files
  • app/pages/clinicworkspace/components/SiteFilterDropdown.js#L89-L92 (this comment)
  • app/pages/clinicworkspace/components/TagFilterDropdown.js#L92-L95
🤖 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 `@app/pages/clinicworkspace/components/SiteFilterDropdown.js` around lines 89 -
92, Replace the unsupported toSorted call in sortedSiteFilterOptions with sort
on the newly mapped array, preserving label comparison and avoiding mutation of
the source sites. Apply the same change in
app/pages/clinicworkspace/components/TagFilterDropdown.js lines 92-95.

260-267: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate the trigger accessible labels.

The hard-coded iconLabel values expose English-only names to assistive technology. Pass both labels through t().

  • app/pages/clinicworkspace/components/SiteFilterDropdown.js#L260-L267: translate Filter by clinic sites.
  • app/pages/clinicworkspace/components/TagFilterDropdown.js#L263-L270: translate Filter by patient tags.

As per coding guidelines, “Use react-i18next via useTranslation() or withTranslation() for translations.”

📍 Affects 2 files
  • app/pages/clinicworkspace/components/SiteFilterDropdown.js#L260-L267 (this comment)
  • app/pages/clinicworkspace/components/TagFilterDropdown.js#L263-L270
🤖 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 `@app/pages/clinicworkspace/components/SiteFilterDropdown.js` around lines 260
- 267, Use react-i18next through the existing component translation pattern,
then pass the translated strings to the trigger iconLabel props: update
SiteFilterDropdown.js lines 260-267 for “Filter by clinic sites” and
TagFilterDropdown.js lines 263-270 for “Filter by patient tags”; ensure both
components obtain the translation function via useTranslation() or
withTranslation().

Source: Coding guidelines

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant