Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dual Valueset for Medication Request Instructions #9996

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from

Conversation

amjithtitus09
Copy link
Contributor

@amjithtitus09 amjithtitus09 commented Jan 15, 2025

Proposed Changes

@ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate bug / test a new feature.
  • Update product documentation.
  • Ensure that UI text is kept in I18n files.
  • Prep screenshot or demo video for changelog entry, and attach it to issue.
  • Request for Peer Reviews
  • Completion of QA

Summary by CodeRabbit

  • New Features

    • Added localization strings for dosage instructions and PRN reasons
    • Introduced a new MedicationsTable component for displaying medication details
    • Enhanced medication statement and request handling with improved type definitions
    • Added multi-value selection for medication options
  • Improvements

    • Updated medication-related components with more flexible UI and state management
    • Improved formatting of medication dosage and instructions
    • Added more robust handling of medication data across various components
  • Localization

    • Expanded English language translation support for medication-related interactions

@amjithtitus09 amjithtitus09 requested a review from a team as a code owner January 15, 2025 15:06
Copy link
Contributor

coderabbitai bot commented Jan 15, 2025

Walkthrough

This pull request introduces comprehensive changes to medication-related components and types across the application. The modifications enhance medication request and statement handling, improve localization for dosage instructions, and introduce new utility functions for formatting medication details. The changes span multiple files, including localization, component implementations, type definitions, and utility functions, focusing on improving the user interface and data management for medication-related interactions.

Changes

File Change Summary
public/locale/en.json Added localization keys for dosage instructions and PRN reasons
src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx Updated imports, replaced ValueSetSelect with MultiValueSetSelect, added notes column
src/pages/Encounters/PrintPrescription.tsx Replaced custom medication table with new MedicationsTable component
src/components/Medicine/MedicationsTable.tsx New component for rendering medications with formatting utilities
src/types/emr/medicationStatement.ts Introduced MedicationStatementRequest, made dosage_text and information_source required
src/components/Medicine/utils.ts Added formatDosage and formatSig helper functions

Sequence Diagram

sequenceDiagram
    participant User
    participant MedicationRequestQuestion
    participant MultiValueSetSelect
    participant MedicationsTable

    User->>MedicationRequestQuestion: Enter medication details
    MedicationRequestQuestion->>MultiValueSetSelect: Select instructions/PRN reasons
    MultiValueSetSelect-->>MedicationRequestQuestion: Return selected values
    MedicationRequestQuestion->>MedicationsTable: Render medication details
    MedicationsTable->>User: Display formatted medication information
Loading

Possibly related PRs

Suggested labels

needs review, work-in-progress

Suggested reviewers

  • rithviknishad
  • gigincg

Poem

🐰 Hop, hop, through medication's maze,
With new components and localized praise,
MultiValueSetSelect, a bunny's delight,
Formatting dosage with algorithmic might,
Code hops forward, clear and bright! 🌈💊


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Jan 15, 2025

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit c4f7424
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/678a541ed4ca1c0008003303
😎 Deploy Preview https://deploy-preview-9996--care-ohc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

cloudflare-workers-and-pages bot commented Jan 15, 2025

Deploying care-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: c4f7424
Status: ✅  Deploy successful!
Preview URL: https://4b10a91a.care-fe.pages.dev
Branch Preview URL: https://dual-valueset-field-medicati.care-fe.pages.dev

View logs

Copy link

cypress bot commented Jan 15, 2025

CARE    Run #4348

Run Properties:  status check passed Passed #4348  •  git commit c4f74241e1: Dual Valueset for Medication Request Instructions
Project CARE
Branch Review dual-valueset-field-medication
Run status status check passed Passed #4348
Run duration 02m 54s
Commit git commit c4f74241e1: Dual Valueset for Medication Request Instructions
Committer Amjith Titus
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 5
View all changes introduced in this branch ↗︎

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/components/Questionnaire/DualValueSetSelect.tsx (3)

28-39: Consider enhancing type safety and documentation for the interface.

The interface is well-structured but could benefit from:

  1. JSDoc comments explaining each prop's purpose
  2. Stricter typing for the systems tuple to ensure exactly 2 elements
  3. Consider using enums or constants for TabIndex instead of magic numbers
+/** Index of the active tab (0 or 1) */
 type TabIndex = 0 | 1;
 
+/** Props for the DualValueSetSelect component */
 interface DualValueSetSelectProps {
+  /** Array of exactly two value set systems */
   systems: [ValueSetSystem, ValueSetSystem];
+  /** Currently selected values for each tab */
   values?: [Code | null, Code | null];
+  /** Callback when a value is selected in either tab */
   onSelect: (index: TabIndex, value: Code | null) => void;
   placeholders?: [string, string];
   disabled?: boolean;
   count?: number;
   searchPostFix?: string;
   labels: [string, string];
 }

51-68: Consider optimizing data fetching and state management.

The current implementation could benefit from:

  1. Memoizing the query key array to prevent unnecessary re-renders
  2. Using a reducer instead of multiple useState hooks
  3. Adding error handling for the query
+const queryKey = useMemo(
+  () => ["valueset", systems[activeTab], "expand", count, search],
+  [systems, activeTab, count, search]
+);

 const searchQuery = useQuery({
-  queryKey: ["valueset", systems[activeTab], "expand", count, search],
+  queryKey,
   queryFn: query.debounced(routes.valueset.expand, {
     pathParams: { system: systems[activeTab] },
     body: { count, search: search + searchPostFix },
   }),
+  onError: (error) => {
+    // Handle error appropriately
+  }
 });

91-134: Enhance accessibility features in the UI.

Consider adding:

  1. ARIA labels for better screen reader support
  2. Keyboard navigation between tabs
  3. Focus management when the popover opens/closes
 <Popover open={open} onOpenChange={setOpen}>
   <PopoverTrigger asChild disabled={disabled}>
     <Button
       variant="outline"
       role="combobox"
+      aria-label={placeholders[0]}
+      aria-expanded={open}
       className={cn(
         "w-full justify-start h-auto min-h-10 py-2",
         "text-left",
         !hasValues && "text-gray-400",
       )}
     >
src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (1)

652-692: Add error handling for value selection callbacks.

The conditional rendering looks good, but consider adding error handling for the onSelect callbacks to handle edge cases gracefully.

 onSelect={(index, value) => {
+  try {
     if (index === 0) {
       handleUpdateDosageInstruction({
         as_needed_for: value || undefined,
       });
     } else {
       handleUpdateDosageInstruction({
         additional_instruction: value ? [value] : undefined,
       });
     }
+  } catch (error) {
+    // Handle error appropriately
+    console.error('Error updating dosage instruction:', error);
+  }
 }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31aac73 and 0835c63.

📒 Files selected for processing (3)
  • public/locale/en.json (1 hunks)
  • src/components/Questionnaire/DualValueSetSelect.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (1)
public/locale/en.json (1)

1641-1641: LGTM! Translation key added correctly.

The new translation key "prn_reason" is properly added and matches the component's requirements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (2)

758-790: Consider adding input validation for notes.

The notes implementation handles desktop and mobile layouts well, but consider adding:

  1. Length validation for notes
  2. Character validation to prevent problematic inputs
 <Input
   value={medication.note || ""}
   onChange={(e) => onUpdate?.({ note: e.target.value })}
   placeholder={t("add_notes")}
   disabled={disabled}
   className="h-9 text-sm"
+  maxLength={500}
+  pattern="[A-Za-z0-9\s.,!?()-]+"
 />

Line range hint 454-790: Consider extracting the notes input logic into a separate component.

The current implementation mixes notes handling with the medication grid row. Consider extracting it into a reusable MedicationNotes component to improve maintainability and reusability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0835c63 and 52aac94.

📒 Files selected for processing (3)
  • public/locale/en.json (2 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (7 hunks)
  • src/pages/Encounters/PrintPrescription.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • public/locale/en.json
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: cypress-run (1)
🔇 Additional comments (4)
src/pages/Encounters/PrintPrescription.tsx (1)

208-208: LGTM! Clean variable declaration.

The notes variable is appropriately extracted from the medication object.

src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (3)

40-41: LGTM! Clean imports.

The new components are properly imported.


Line range hint 174-204: LGTM! Grid layout properly updated.

The grid layout has been correctly updated to accommodate the new notes column while maintaining proper alignment and responsiveness.


656-696: LGTM! Well-structured dual value set implementation.

The conditional rendering between PRN and non-PRN cases is handled cleanly, with proper state management and translations.

✅ Verification successful

Translation keys verified and properly implemented

All translation keys used in the component are present in the locale files with appropriate values:

  • prn_reason
  • select_prn_reason
  • additional_instructions
  • select_additional_instructions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify translation keys for PRN reason
rg -g '*.json' '"prn_reason":\s*"' public/locale/

Length of output: 103


Script:

#!/bin/bash
# Description: Verify all related translation keys
rg -g '*.json' -e '"prn_reason":\s*"' -e '"select_prn_reason":\s*"' -e '"additional_instructions":\s*"' -e '"select_additional_instructions":\s*"' public/locale/

Length of output: 455

@@ -230,6 +230,7 @@ export const PrintPrescription = (props: {
</TableCell>
<TableCell className="py-2 px-3">
{remarks || "-"}
{notes ? ` (${t("note")}: ${notes})` : ""}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Missing translations for "note" key in non-English locale files

The "note" translation key exists only in en.json but is missing in:

  • public/locale/hi.json
  • public/locale/kn.json
  • public/locale/ml.json
  • public/locale/mr.json
  • public/locale/ta.json
🔗 Analysis chain

Verify translation key exists.

The implementation looks good, but ensure the translation key "note" exists in the i18n files.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the "note" translation key exists
rg -g '*.json' '"note":\s*"' public/locale/

Length of output: 85


Script:

#!/bin/bash
# List all locale files and check for "note" key in each
fd . 'public/locale' -e json --exec-batch sh -c 'echo "=== Locale files ==="; cat "{}" | jq -r "select(.note != null) | input_filename"'

Length of output: 165


Script:

#!/bin/bash
# List all locale files and show content around "note" key
echo "=== Available locale files ==="
fd . 'public/locale' -e json
echo -e "\n=== Content check for 'note' key ==="
rg -g '*.json' -C 2 '"note":' public/locale/

Length of output: 633

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
src/components/Medicine/MedicationsTable.tsx (3)

20-30: Add input validation and error handling to getFrequencyDisplay.

The function should validate the timing object structure before accessing properties to prevent potential runtime errors.

 function getFrequencyDisplay(
   timing?: MedicationRequestDosageInstruction["timing"],
 ) {
   if (!timing) return undefined;
+  if (typeof timing !== 'object') return undefined;
   const code = reverseFrequencyOption(timing);
   if (!code) return undefined;
+  if (!(code in MEDICATION_REQUEST_TIMING_OPTIONS)) return undefined;
   return {
     code,
     meaning: MEDICATION_REQUEST_TIMING_OPTIONS[code].display,
   };
 }

33-46: Add type safety to formatDosage function.

The function should handle edge cases and provide type safety for the dose_and_rate object.

 function formatDosage(instruction: MedicationRequestDosageInstruction) {
   if (!instruction.dose_and_rate) return "";
 
+  // Ensure dose_and_rate is an object
+  if (typeof instruction.dose_and_rate !== 'object') return "";
+
   if (instruction.dose_and_rate.type === "calculated") {
     const { dose_range } = instruction.dose_and_rate;
-    if (!dose_range) return "";
+    if (!dose_range?.low?.value || !dose_range?.low?.unit?.display ||
+        !dose_range?.high?.value || !dose_range?.high?.unit?.display) return "";
     return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
   }
 
   const { dose_quantity } = instruction.dose_and_rate;
   if (!dose_quantity?.value || !dose_quantity.unit) return "";
 
   return `${dose_quantity.value} ${dose_quantity.unit.display}`;
 }

70-72: Add prop validation for medications array.

Consider adding runtime validation for the medications prop to ensure data integrity.

+import { z } from "zod";
+
+const MedicationSchema = z.object({
+  id: z.string(),
+  medication: z.object({
+    display: z.string().optional(),
+  }).optional(),
+  dosage_instruction: z.array(z.any()),
+  status: z.string().optional(),
+  note: z.string().optional(),
+});
+
 interface MedicationsTableProps {
   medications: MedicationRequestRead[];
 }
src/components/Medicine/MedicineAdministrationSheet/index.tsx (2)

Line range hint 44-49: Optimize search filtering performance.

Consider memoizing the filtered results to prevent unnecessary re-computations on each render.

+import { useMemo } from "react";

-const filteredMedications = medications?.results?.filter(
+const filteredMedications = useMemo(() => medications?.results?.filter(
     (med: MedicationRequestRead) => {
       if (!searchQuery.trim()) return true;
       const searchTerm = searchQuery.toLowerCase().trim();
       const medicationName = med.medication?.display?.toLowerCase() || "";
       return medicationName.includes(searchTerm);
     },
-  );
+  ), [medications?.results, searchQuery]);

147-156: Enhance accessibility for scrollable content.

Add ARIA labels to improve screen reader support for the scrollable medication tables.

-              <div className="min-w-[800px]">
+              <div className="min-w-[800px]" role="region" aria-label="Medications list">
                 <TabsContent value="active" className="p-2">
                   <MedicationsTable medications={activeMedications || []} />
                 </TabsContent>
                 <TabsContent value="discontinued" className="p-2">
                   <MedicationsTable
                     medications={discontinuedMedications || []}
                   />
                 </TabsContent>
               </div>
src/pages/Encounters/PrintPrescription.tsx (1)

127-127: Add print-specific styles for MedicationsTable.

Consider adding print-specific styles to ensure optimal layout and readability when printed.

-          <MedicationsTable medications={medications.results} />
+          <div className="print:break-inside-avoid">
+            <MedicationsTable 
+              medications={medications.results}
+              className="print:text-sm print:border-collapse" 
+            />
+          </div>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52aac94 and ba4f391.

📒 Files selected for processing (3)
  • src/components/Medicine/MedicationsTable.tsx (1 hunks)
  • src/components/Medicine/MedicineAdministrationSheet/index.tsx (4 hunks)
  • src/pages/Encounters/PrintPrescription.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (1)
src/components/Medicine/MedicationsTable.tsx (1)

124-127: Missing translations for "note" key in non-English locale files.

The "note" translation key needs to be added to all locale files.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (1)

471-476: Avoid Duplicate Labels for 'Notes' Field

The Label component for the "notes" field is rendered twice within the desktopLayout condition, causing redundancy.

Consider moving the Label component outside the conditional rendering to avoid duplication:

          {/* Notes */}
          <div className="lg:px-2 lg:py-1 lg:border-r overflow-hidden">
+           <Label className="mb-1.5 block text-sm lg:hidden">{t("notes")}</Label>
            {desktopLayout ? (
              <>
-               <Label className="mb-1.5 block text-sm lg:hidden">{t("notes")}</Label>
                <Input
                  value={medication.note || ""}
                  onChange={(e) => onUpdate?.({ note: e.target.value })}
                  placeholder={t("add_notes")}
                  disabled={disabled}
                  className="h-9 text-sm"
                />
              </>
            ) : (
              <NotesInput
                className="mt-2"
                questionnaireResponse={{
                  question_id: "",
                  structured_type: "medication_statement",
                  link_id: "",
                  values: [],
                  note: medication.note,
                }}
                updateQuestionnaireResponseCB={(response) => {
                  onUpdate?.({ note: response.note });
                }}
                disabled={disabled}
              />
            )}
          </div>
src/types/emr/medicationStatement.ts (1)

43-51: LGTM! Consider adding JSDoc comments.

The new MedicationStatementRequest type is well-structured and appropriately omits server-side fields (id, patient, encounter). Consider adding JSDoc comments to document the purpose and usage of this type.

+/**
+ * Represents a medication statement request without server-side fields.
+ * Used for creating new medication statements or updating existing ones.
+ */
 export type MedicationStatementRequest = {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba4f391 and 91dcf55.

📒 Files selected for processing (6)
  • public/locale/en.json (4 hunks)
  • src/components/Questionnaire/QuestionRenderer.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (6 hunks)
  • src/components/Questionnaire/structured/types.ts (3 hunks)
  • src/types/emr/medicationStatement.ts (1 hunks)
  • src/types/questionnaire/form.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • public/locale/en.json
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: cypress-run (1)
🔇 Additional comments (5)
src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (1)

488-499: Verify the Use of Empty Identifiers in NotesInput Component

In the NotesInput component, you're passing a questionnaireResponse with empty question_id and link_id. If these identifiers are required by the NotesInput component or its internal logic, this could lead to potential issues.

Please confirm that passing empty identifiers does not cause any unintended side effects in the NotesInput component.

src/types/questionnaire/form.ts (1)

Line range hint 5-33: Ensure Consistency After Updating Types from MedicationStatement to MedicationStatementRequest

The change from MedicationStatement[] to MedicationStatementRequest[] in the ResponseValue type is significant. Please verify that all usages of ResponseValue throughout the codebase are updated accordingly to accommodate this type change.

Run the following script to find all usages of ResponseValue and check for any potential type conflicts:

✅ Verification successful

Type Change from MedicationStatement[] to MedicationStatementRequest[] is Safely Isolated

The type change is properly contained within the type definition. The codebase handles medication statement responses through dedicated components and type-safe structures, with no direct manipulation of the medication statement types in response processing.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of ResponseValue and check for MedicationStatement

rg 'ResponseValue' -A 5 | rg 'MedicationStatement'

Length of output: 48


Script:

#!/bin/bash
# Find all ResponseValue usages with context
echo "=== ResponseValue usages ==="
rg -t typescript "ResponseValue" -B 2 -A 2

# Find type assertions or casts related to ResponseValue
echo -e "\n=== Type assertions ==="
rg -t typescript "as ResponseValue|<ResponseValue>" -B 2 -A 2

# Check test files specifically
echo -e "\n=== Test file usages ==="
fd "test|spec" -e ts -e tsx --exec rg -l "ResponseValue" {}

Length of output: 433


Script:

#!/bin/bash
# Find all TypeScript files containing ResponseValue
echo "=== Files using ResponseValue ==="
fd -e ts -e tsx --exec rg -l "ResponseValue" {}

# Search for both old and new type references
echo -e "\n=== MedicationStatement usage ==="
fd -e ts -e tsx --exec rg -l "MedicationStatement[^\w]" {}

# Look for response handling code with context
echo -e "\n=== Response handling context ==="
fd -e ts -e tsx --exec rg --context 3 "ResponseValue.*MedicationStatement" {}

Length of output: 865


Script:

#!/bin/bash
# Check content of key files
echo "=== QuestionnaireResponse type content ==="
cat src/types/questionnaire/questionnaireResponse.ts

echo -e "\n=== QuestionInput component handling ==="
cat src/components/Questionnaire/QuestionTypes/QuestionInput.tsx

Length of output: 8353

src/components/Questionnaire/structured/types.ts (1)

Line range hint 5-28: Update Usages After Changing Types to MedicationStatementRequest

The types in StructuredDataMap and StructuredRequestMap have been updated to use MedicationStatementRequest instead of MedicationStatement. Ensure that all other parts of the code that rely on these interfaces are updated to reflect this change.

Please confirm that all usages of StructuredDataMap and StructuredRequestMap are compatible with the new types. You can run this script to help identify any discrepancies:

✅ Verification successful

Type Changes are Correctly Implemented

The change from MedicationStatement to MedicationStatementRequest in StructuredDataMap and StructuredRequestMap is correctly implemented and all usages are compatible. The types are properly used through the generic DataTypeFor and RequestTypeFor types, maintaining the correct distinction between request and response types throughout the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all references to StructuredDataMap and StructuredRequestMap that might be affected.

rg 'Structured(Data|Request)Map' -A 5

Length of output: 1460


Script:

#!/bin/bash
# Find files importing from the types file and check MedicationStatement usage
rg -l "from ['\"].*Questionnaire/structured/types['\"]"

# Check for any MedicationStatement (without Request) usage
rg "MedicationStatement[^R]" -A 2

Length of output: 9678


Script:

#!/bin/bash
# Check the content of handlers.ts to verify type usage
cat src/components/Questionnaire/structured/handlers.ts

Length of output: 4886

src/components/Questionnaire/QuestionRenderer.tsx (2)

17-17: LGTM! Addition follows existing pattern.

The addition of "medication_statement" to FULL_WIDTH_QUESTION_TYPES is consistent with the existing pattern and aligns with the PR objectives.


Line range hint 8-9: Verify the structured question type definition.

Let's ensure that "medication_statement" is properly defined in the StructuredQuestionType type.

✅ Verification successful

Type definition verified successfully

The "medication_statement" type is properly defined in StructuredQuestionType as one of the valid values in the union type at src/types/questionnaire/question.ts.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that "medication_statement" is defined in StructuredQuestionType

# Search for StructuredQuestionType definition
rg -A 10 "type StructuredQuestionType = " || rg -A 10 "enum StructuredQuestionType"

Length of output: 84


Script:

#!/bin/bash
# Search for StructuredQuestionType in TypeScript files with various patterns
echo "=== Searching for StructuredQuestionType ==="
rg -t typescript -t tsx "StructuredQuestionType" -A 5

echo -e "\n=== Searching for medication_statement ==="
rg -t typescript -t tsx "medication_statement"

Length of output: 370


Script:

#!/bin/bash
# Search in .ts and .tsx files explicitly
echo "=== Searching for StructuredQuestionType ==="
rg "StructuredQuestionType" --glob "*.{ts,tsx}" -A 5

echo -e "\n=== Using ast-grep for type definition ==="
ast-grep --pattern 'type StructuredQuestionType = $_'

echo -e "\n=== Searching for medication_statement ==="
rg "medication_statement" --glob "*.{ts,tsx}"

Length of output: 9475

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (4)

Line range hint 174-204: Consider extracting grid column configuration.

The grid layout is well-structured, but the column configuration could be extracted into a constant to improve maintainability. This would make it easier to modify column widths and ensure consistency across the component.

+const GRID_COLUMNS = {
+  medicine: '280px',
+  dosage: '180px',
+  frequency: '170px',
+  duration: '160px',
+  instructions: '300px',
+  route: '180px',
+  site: '250px',
+  method: '180px',
+  intent: '160px',
+  notes: '200px',
+  actions: '48px'
+};
+
+const GRID_TEMPLATE = Object.values(GRID_COLUMNS).join(',');
+
-<div className="hidden lg:grid grid-cols-[280px,180px,170px,160px,300px,180px,250px,180px,160px,200px,48px] bg-gray-50 border-b text-sm font-medium text-gray-500">
+<div className={cn("hidden lg:grid bg-gray-50 border-b text-sm font-medium text-gray-500", `grid-cols-[${GRID_TEMPLATE}]`)}>

656-696: Enhance type safety for system identifiers.

The implementation is logically sound, but the system identifiers could benefit from type safety to prevent potential errors.

+const INSTRUCTION_SYSTEMS = {
+  PRN_REASON: 'system-as-needed-reason',
+  ADDITIONAL_INSTRUCTION: 'system-additional-instruction'
+} as const;
+
 {dosageInstruction?.as_needed_boolean ? (
   <DualValueSetSelect
     systems={[
-      "system-as-needed-reason",
-      "system-additional-instruction",
+      INSTRUCTION_SYSTEMS.PRN_REASON,
+      INSTRUCTION_SYSTEMS.ADDITIONAL_INSTRUCTION,
     ]}

763-765: Remove duplicate label in desktop view.

The label is rendered twice in desktop view, as it's already shown in the header.

-            <Label className="mb-1.5 block text-sm lg:hidden">
-              {t("notes")}
-            </Label>

777-783: Improve questionnaireResponse structure.

The questionnaireResponse object contains empty strings and arrays that could be better structured or omitted.

-            questionnaireResponse={{
-              question_id: "",
-              structured_type: "medication_request",
-              link_id: "",
-              values: [],
-              note: medication.note,
-            }}
+            questionnaireResponse={{
+              structured_type: "medication_request",
+              note: medication.note,
+            }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91dcf55 and 74c4c8d.

📒 Files selected for processing (1)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (1)
src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (1)

40-41: LGTM! Clean import organization.

The new imports for DualValueSetSelect and NotesInput are properly organized and follow the existing pattern.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/components/Medicine/utils.ts (2)

4-17: Consider enhancing type safety and readability.

The function handles dosage formatting well, but could be improved:

 export function formatDosage(instruction: MedicationRequestDosageInstruction) {
   if (!instruction.dose_and_rate) return "";
 
   if (instruction.dose_and_rate.type === "calculated") {
     const { dose_range } = instruction.dose_and_rate;
     if (!dose_range) return "";
-    return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
+    const { low, high } = dose_range;
+    return `${low.value}${low.unit.display} - ${high.value}${high.unit.display}`;
   }
 
   const { dose_quantity } = instruction.dose_and_rate;
   if (!dose_quantity?.value || !dose_quantity.unit) return "";
 
   return `${dose_quantity.value} ${dose_quantity.unit.display}`;
 }

19-38: Add JSDoc documentation and optimize the implementation.

The function could benefit from documentation and a minor optimization:

+/**
+ * Formats dosage instructions in Rx style
+ * @param instruction - The dosage instruction object
+ * @returns Formatted string containing route, method, and site information
+ */
 export function formatSig(instruction: MedicationRequestDosageInstruction) {
   const parts: string[] = [];
+
+  // Early return if no formatting needed
+  if (!instruction.route?.display && !instruction.method?.display && !instruction.site?.display) {
+    return "";
+  }
 
   // Add route if present
   if (instruction.route?.display) {
     parts.push(`Via ${instruction.route.display}`);
   }
src/components/Questionnaire/DualValueSetSelect.tsx (2)

31-40: Add JSDoc documentation for the props interface.

The props interface could benefit from detailed documentation:

+/**
+ * Props for the DualValueSetSelect component
+ * @interface DualValueSetSelectProps
+ * @property {[ValueSetSystem, ValueSetSystem]} systems - Array of two value set systems
+ * @property {[Code | null, Code | null]} [values] - Currently selected values for both systems
+ * @property {(index: TabIndex, value: Code | null) => void} onSelect - Callback when a value is selected
+ * @property {[string, string]} [placeholders] - Placeholder texts for both inputs
+ * @property {boolean} [disabled] - Whether the component is disabled
+ * @property {number} [count] - Number of items to show in the list
+ * @property {string} [searchPostFix] - String to append to search query
+ * @property {[string, string]} labels - Labels for both tabs
+ */
 interface DualValueSetSelectProps {
   systems: [ValueSetSystem, ValueSetSystem];
   values?: [Code | null, Code | null];
   onSelect: (index: TabIndex, value: Code | null) => void;
   placeholders?: [string, string];
   disabled?: boolean;
   count?: number;
   searchPostFix?: string;
   labels: [string, string];
 }

92-136: Enhance keyboard accessibility.

The component could benefit from improved keyboard interaction:

 <Button
   variant="outline"
   role="combobox"
+  aria-expanded={open}
+  aria-haspopup="listbox"
   className={cn(
     "w-full justify-start h-auto min-h-10 py-2",
     "text-left",
     !hasValues && "text-gray-400",
   )}
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      setOpen(true);
+    }
+  }}
 >
src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (1)

Line range hint 242-252: Add aria-label for the expand button.

The expand button already has an aria-label, but it could be more descriptive by including the medication name.

Apply this diff:

 <Button
-  aria-label="Expand Medication Request"
+  aria-label={t("expand_medication_request", { medication: medication.medication?.display })}
   variant="ghost"
   size="icon"
   className="h-8 w-8 text-gray-500 hover:text-gray-900"
   onClick={(e) => {
     e.stopPropagation();
     setExpandedMedicationIndex(index);
   }}
   disabled={disabled}
 >
   <Pencil2Icon className="h-4 w-4" />
 </Button>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74c4c8d and 75359cf.

📒 Files selected for processing (6)
  • public/locale/en.json (8 hunks)
  • src/components/Medicine/MedicationsTable.tsx (1 hunks)
  • src/components/Medicine/utils.ts (1 hunks)
  • src/components/Questionnaire/DualValueSetSelect.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (11 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • public/locale/en.json
  • src/components/Medicine/MedicationsTable.tsx
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Redirect rules - care-ohc
  • GitHub Check: Header rules - care-ohc
  • GitHub Check: Pages changed - care-ohc
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
🔇 Additional comments (5)
src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (2)

Line range hint 1-305: LGTM! Well-structured implementation with responsive design.

The component effectively handles medication statements with proper state management, event handling, and responsive layout using Collapsible for mobile views and grid layout for desktop views.


309-503: LGTM! Well-structured grid row implementation with proper accessibility.

The component effectively renders a medication statement row with:

  • Proper type definitions using MedicationStatementRequest
  • Responsive design with NotesInput for mobile and Input for desktop
  • Accessible form controls with associated labels
src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (3)

Line range hint 1-305: LGTM! Well-structured implementation with responsive design.

The component effectively handles medication requests with proper state management, event handling, and responsive layout using Collapsible for mobile views and grid layout for desktop views.


643-683: LGTM! Clean implementation of PRN reasons and additional instructions.

The DualValueSetSelect component is effectively used to handle both PRN reasons and additional instructions in a single component, improving the user experience.


745-777: LGTM! Well-structured notes implementation with responsive design.

The notes section is properly implemented with:

  • Input component for desktop view
  • NotesInput component for mobile view
  • Proper accessibility with labels

Comment on lines 57 to 63
const searchQuery = useQuery({
queryKey: ["valueset", systems[activeTab], "expand", count, search],
queryFn: query.debounced(routes.valueset.expand, {
pathParams: { system: systems[activeTab] },
body: { count, search: search + searchPostFix },
}),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for the search query.

The search query implementation should handle errors:

 const searchQuery = useQuery({
   queryKey: ["valueset", systems[activeTab], "expand", count, search],
   queryFn: query.debounced(routes.valueset.expand, {
     pathParams: { system: systems[activeTab] },
     body: { count, search: search + searchPostFix },
   }),
+  retry: 2,
+  onError: (error) => {
+    console.error('Failed to fetch value set:', error);
+    // Consider showing a user-friendly error message
+  }
 });
📝 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.

Suggested change
const searchQuery = useQuery({
queryKey: ["valueset", systems[activeTab], "expand", count, search],
queryFn: query.debounced(routes.valueset.expand, {
pathParams: { system: systems[activeTab] },
body: { count, search: search + searchPostFix },
}),
});
const searchQuery = useQuery({
queryKey: ["valueset", systems[activeTab], "expand", count, search],
queryFn: query.debounced(routes.valueset.expand, {
pathParams: { system: systems[activeTab] },
body: { count, search: search + searchPostFix },
}),
retry: 2,
onError: (error) => {
console.error('Failed to fetch value set:', error);
// Consider showing a user-friendly error message
}
});

Comment on lines +490 to +500
{/* Remove Button */}
<div className="hidden lg:flex lg:px-2 lg:py-1 items-center justify-center sticky right-0 bg-white shadow-[-12px_0_15px_-4px_rgba(0,0,0,0.15)] w-12">
<Button
variant="ghost"
size="icon"
onClick={onRemove}
disabled={disabled}
className="h-8 w-8"
>
<MinusCircledIcon className="h-4 w-4" />
</Button>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add aria-label for the remove button.

The remove button is missing an aria-label for accessibility.

Apply this diff:

 <Button
   variant="ghost"
   size="icon"
+  aria-label={t("remove_medication")}
   onClick={onRemove}
   disabled={disabled}
   className="h-8 w-8"
 >
   <MinusCircledIcon className="h-4 w-4" />
 </Button>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 43 to 51
export type MedicationStatementRequest = {
status: MedicationStatementStatus;
reason?: string;
medication: Code;
dosage_text?: string;
effective_period?: Period;
information_source?: MedicationStatementInformationSourceType;
note?: string;
};
Copy link
Member

Choose a reason for hiding this comment

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

Too many optional fields, why don't we make the information_source mandatory? also the dosage_text 🤔

@@ -0,0 +1,185 @@
import { useQuery } from "@tanstack/react-query";
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure why we need DualValueSetSelect it!

Copy link

Reminder: To add the "needs testing" label, comment "ready for testing" on this PR.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/components/Questionnaire/ValueSetSelect.tsx (2)

35-36: LGTM! Consider adding JSDoc comments.

The new props are well-typed and their names clearly indicate their purpose. Consider adding JSDoc comments to document their usage and impact on component behavior.

+ /** Hide the trigger button and render content directly */
  hideTrigger?: boolean;
+ /** Control the popover's open state externally */
  controlledOpen?: boolean;

107-135: Consider adding ARIA label for accessibility.

The conditional rendering logic is clean and maintainable. Consider adding an aria-label to improve accessibility when the trigger is hidden.

  <Command filter={() => 1}>
    <CommandInput
+     aria-label={placeholder}
      placeholder={placeholder}
      className="outline-none border-none ring-0 shadow-none"
      onValueChange={setSearch}
    />
src/components/Medicine/DualValueSetSelect.tsx (3)

19-30: LGTM! Consider enhancing type safety for labels prop.

The types are well-defined. Consider making the labels prop type more specific to ensure meaningful labels.

- labels: [string, string];
+ type TabLabel = 'instructions' | 'reasons' | string;
+ labels: [TabLabel, TabLabel];

56-101: Consider memoizing the mapped values for performance.

The trigger button implementation is clean and accessible. For better performance with large datasets, consider memoizing the mapped values.

+ const renderedValues = useMemo(
+   () =>
      values.map(
        (value, index) =>
          value && (
            <div key={value.code}>
              {/* existing implementation */}
            </div>
          ),
      ),
+   [values, onSelect]
+ );

103-116: Consider using numeric tab values for cleaner type handling.

The tabs implementation works well, but the string-number conversion could be handled more elegantly.

- value={activeTab.toString()}
- onValueChange={(value) => {
-   setActiveTab(Number(value) as TabIndex);
- }}
+ value={activeTab}
+ onValueChange={(value: TabIndex) => {
+   setActiveTab(value);
+ }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75359cf and 4785ba0.

📒 Files selected for processing (5)
  • public/locale/en.json (8 hunks)
  • src/components/Medicine/DualValueSetSelect.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (11 hunks)
  • src/components/Questionnaire/ValueSetSelect.tsx (3 hunks)
  • src/types/emr/medicationStatement.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • public/locale/en.json
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: cypress-run (1)
🔇 Additional comments (7)
src/types/emr/medicationStatement.ts (3)

32-32: Verify the impact of making fields required.

Good decision to make dosage_text and information_source required fields as suggested in the previous review. However, this is a breaking change that needs verification.

Let's verify all usages to ensure they provide these now-required fields:

#!/bin/bash
# Search for MedicationStatement object creations/assignments
ast-grep --pattern 'MedicationStatement = {
  $$$
}'

# Also check for spread operations that might miss these fields
rg -A 5 'MedicationStatement.*=.*\{.*\.\.\.'

Also applies to: 38-38


43-51: Well-structured request type!

Good separation of concerns by creating a dedicated request type that omits server-side fields (id, patient, encounter). The consistent field ordering with MedicationStatement improves maintainability.


58-58: LGTM! Consistent type definitions.

The changes to make dosage_text and information_source required are consistently applied across both MedicationStatement and MedicationStatementRead types.

Also applies to: 61-61

src/components/Questionnaire/ValueSetSelect.tsx (2)

52-52: LGTM! Clean state management implementation.

The state management cleanly handles both controlled and uncontrolled modes, maintaining the search reset behavior.

Also applies to: 64-67


69-105: LGTM! Good extraction of reusable content.

The Command component logic is cleanly extracted and reused, following DRY principles while maintaining UI consistency.

src/components/Medicine/DualValueSetSelect.tsx (2)

42-51: LGTM! Clean state management implementation.

The state management and event handling are well-implemented, with good UX considerations for tab reset.


118-136: LGTM! Clean integration with ValueSetSelect.

The ValueSetSelect integration is well-implemented, making good use of the new features for a seamless dual-selection experience.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/components/Medicine/MultiValueSetSelect.tsx (2)

74-78: Reconsider event propagation handling.

Using stopPropagation might prevent event bubbling needed by parent components. Consider using a more specific approach to handle click events.

-onClick={(e) => {
-  e.stopPropagation();
+onClick={() => {
   setActiveTab(index);
   setOpen(true);
 }}

127-134: Consider making tab auto-advance optional.

The automatic tab advancement after selection might be confusing for users who want to make multiple selections in the same category. Consider making this behavior configurable.

+interface MultiValueSetSelectProps {
+  autoAdvanceTabs?: boolean;
+  // ... existing props
+}

 onSelect={(value) => {
   options[activeTab].onSelect(value);
-  if (activeTab < options.length - 1) {
+  if (autoAdvanceTabs && activeTab < options.length - 1) {
     setActiveTab(activeTab + 1);
   } else {
     setOpen(false);
   }
 }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4785ba0 and c4f7424.

📒 Files selected for processing (2)
  • src/components/Medicine/MultiValueSetSelect.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: cypress-run (1)
🔇 Additional comments (1)
src/components/Medicine/MultiValueSetSelect.tsx (1)

19-25: Consider adding runtime validation for nullable values.

The value field in ValueSetOption is nullable (Code | null). While TypeScript provides compile-time type safety, consider adding runtime validation to ensure proper handling of null values, especially since this affects the UI state.

Let's verify how null values are handled across the codebase:

Comment on lines +51 to +148
return (
<div className="w-full">
<Popover open={open} onOpenChange={handleOpenChange}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
className={cn(
"w-full justify-start h-auto min-h-10 py-2",
"text-left",
!hasValues && "text-gray-400",
)}
>
{!hasValues ? (
<span>{options[0]?.placeholder || t("search")}</span>
) : (
<div className="flex flex-col gap-1 w-full">
{options.map(
(option, index) =>
option.value && (
<div
key={option.value.code}
className="flex items-center justify-between bg-gray-100 rounded px-2 py-1 min-w-0"
onClick={(e) => {
e.stopPropagation();
setActiveTab(index);
setOpen(true);
}}
>
<span className="text-sm truncate flex-1 mr-2">
{option.value.display}
</span>
<Button
onClick={(e) => {
e.stopPropagation();
option.onSelect(null);
}}
variant="ghost"
size="icon"
>
<X className="h-4 w-4" />
</Button>
</div>
),
)}
</div>
)}
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0 inline-block min-w-[300px]"
align="start"
sideOffset={4}
>
<Tabs
value={activeTab.toString()}
onValueChange={(value) => {
setActiveTab(Number(value));
}}
className="w-fit"
>
<TabsList className="flex">
{options.map((option, index) => (
<TabsTrigger
key={index}
value={index.toString()}
className="whitespace-nowrap px-3"
>
{option.label}
</TabsTrigger>
))}
</TabsList>
<div className="w-full">
<ValueSetSelect
system={options[activeTab].system}
value={options[activeTab].value}
onSelect={(value) => {
options[activeTab].onSelect(value);
if (activeTab < options.length - 1) {
setActiveTab(activeTab + 1);
} else {
setOpen(false);
}
}}
placeholder={options[activeTab].placeholder || t("search")}
disabled={disabled}
count={count}
searchPostFix={searchPostFix}
wrapTextForSmallScreen
hideTrigger
controlledOpen={open}
/>
</div>
</Tabs>
</PopoverContent>
</Popover>
</div>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add keyboard navigation support.

The component lacks keyboard navigation support. Users should be able to:

  • Navigate between tabs using arrow keys
  • Open/close popover using Enter/Space
  • Select/deselect values using Enter/Space

Would you like me to provide the implementation for keyboard navigation support?

Comment on lines +40 to +42
const [activeTab, setActiveTab] = useState<number>(0);
const [open, setOpen] = useState(false);
const hasValues = options.some((opt) => opt.value !== null);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add effect to sync activeTab with options array.

The activeTab state could get out of sync if the options array changes. Consider adding a useEffect hook to reset or adjust the activeTab when options change.

+import { useEffect } from "react";
+
 const [activeTab, setActiveTab] = useState<number>(0);
 const [open, setOpen] = useState(false);
 const hasValues = options.some((opt) => opt.value !== null);
+
+useEffect(() => {
+  if (activeTab >= options.length) {
+    setActiveTab(0);
+  }
+}, [options.length, activeTab]);
📝 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.

Suggested change
const [activeTab, setActiveTab] = useState<number>(0);
const [open, setOpen] = useState(false);
const hasValues = options.some((opt) => opt.value !== null);
import { useEffect } from "react";
const [activeTab, setActiveTab] = useState<number>(0);
const [open, setOpen] = useState(false);
const hasValues = options.some((opt) => opt.value !== null);
useEffect(() => {
if (activeTab >= options.length) {
setActiveTab(0);
}
}, [options.length, activeTab]);

Comment on lines +55 to +63
<Button
variant="outline"
role="combobox"
className={cn(
"w-full justify-start h-auto min-h-10 py-2",
"text-left",
!hasValues && "text-gray-400",
)}
>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve accessibility by using correct ARIA attributes.

The combobox role might not be appropriate for this multi-select implementation. Consider using listbox with aria-multiselectable="true" instead. Also, add necessary ARIA labels for better screen reader support.

 <Button
   variant="outline"
-  role="combobox"
+  role="listbox"
+  aria-multiselectable="true"
+  aria-label={t("select_medication_instructions")}
   className={cn(
     "w-full justify-start h-auto min-h-10 py-2",
     "text-left",
     !hasValues && "text-gray-400",
   )}
 >
📝 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.

Suggested change
<Button
variant="outline"
role="combobox"
className={cn(
"w-full justify-start h-auto min-h-10 py-2",
"text-left",
!hasValues && "text-gray-400",
)}
>
<Button
variant="outline"
role="listbox"
aria-multiselectable="true"
aria-label={t("select_medication_instructions")}
className={cn(
"w-full justify-start h-auto min-h-10 py-2",
"text-left",
!hasValues && "text-gray-400",
)}
>

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

Successfully merging this pull request may close these issues.

3 participants