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

Medicine administration UI #10289

Merged
merged 39 commits into from
Feb 4, 2025
Merged

Medicine administration UI #10289

merged 39 commits into from
Feb 4, 2025

Conversation

amjithtitus09
Copy link
Contributor

@amjithtitus09 amjithtitus09 commented Jan 30, 2025

@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

    • Expanded medication management with new screens and dialogs for administering medications.
    • Introduced API endpoints to support medication administration workflows.
    • Enhanced medication list views with improved search and tabbed interfaces.
    • Extended localization support with additional language entries and clearer error messages.
  • Style

    • Updated default button appearance to a primary style.
    • Refined text colors across various UI components for improved readability.
    • Improved date picker labels with internationalization support.

@amjithtitus09 amjithtitus09 requested a review from a team as a code owner January 30, 2025 09:40
@amjithtitus09 amjithtitus09 requested a review from bodhish January 30, 2025 09:40
Copy link
Contributor

coderabbitai bot commented Jan 30, 2025

Walkthrough

This pull request encompasses a broad set of changes across the application. Localization strings in the English JSON file have been updated with key renames and new entries. New API endpoints for medication administration and request upsert operations have been added. Several new React components and forms for managing medication administration have been introduced, along with utility functions to support time slot management. In addition, cosmetic updates to UI elements, modifications to exported functions, and refinements to type definitions and constants further enhance medication management features while removing an unused component.

Changes

File(s) Change Summary
public/locale/en.json
src/components/Questionnaire/QuestionnaireEditor.tsx
src/Resource/.../ResourceList.tsx
src/pages/Facility/.../LocationCard.tsx
src/components/Questionnaire/QuestionTypes/DateTimeQuestion.tsx
src/components/ui/date-picker.tsx
src/components/ui/date-range-picker.tsx
Updated localization: renamed keys, added new translation entries and error messages; adjusted CSS classes for text styling; added i18n support for date selections.
src/Utils/request/api.tsx
src/types/emr/medicationAdministration/medicationAdministrationApi.ts
src/types/emr/medicationRequest/medicationRequestApi.ts
API enhancements: added new endpoints for medication administration and upsert operations; updated HTTP method usage using enums.
src/components/Medicine/MedicationAdministration/AdministrationTab.tsx
src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx
src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx
src/components/Medicine/MedicationAdministration/utils.ts
Introduced new components and utility functions for managing medication administration requests, including UI dialogs, forms, and time slot handling.
src/components/Medicine/MedicationsTable.tsx
src/components/Medicine/MultiValueSetSelect.tsx
src/components/ui/button.tsx
src/components/Medicine/utils.ts
UI and function improvements: exported helper function, added disabled support in buttons, updated dosage formatting, and changed default button variant to "primary".
src/types/emr/medicationAdministration/medicationAdministration.ts
src/types/emr/medicationRequest.ts
Type definition refinements: updated dosage types, restructured medication status constants into active and inactive groups, and introduced new interfaces for medication administration requests and records.
src/components/Medicine/MedicationRequestTable/index.tsx Enhanced medication request table: added an EmptyState component, introduced state to toggle stopped medications, and integrated a tabbed interface for prescriptions and medicine administration.
src/CAREUI/display/SubHeading.tsx Removed unused SubHeading component.

Sequence Diagram(s)

sequenceDiagram
    participant UI as User Interface
    participant AT as AdministrationTab
    participant API as Medication API
    participant QC as Query Client
    UI->>AT: Request medication administrations
    AT->>API: GET /api/v1/patient/{id}/medication/administration/
    API-->>AT: Return paginated administrations
    AT->>UI: Render medication data
Loading
sequenceDiagram
    participant User
    participant Dialog as MedicineAdminDialog
    participant API as Medication API
    User->>Dialog: Open dialog and fill in form details
    Dialog->>API: POST to upsert medication administration
    API-->>Dialog: Return success response
    Dialog->>User: Display success toast & close dialog
Loading

Possibly related PRs

Suggested labels

needs testing

Suggested reviewers

  • rithviknishad
  • nihal467

Poem

Oh, I’m a rabbit with a code-filled hop,
Nibbling keys that never stop.
New endpoints bloom like carrots bright,
Components dance in pure delight.
My whiskers twitch at each bug undone—
Celebrating changes under the coding sun!
🐰✨ Happy hopping in the dev run!

Tip

🌐 Web search-backed reviews and chat
  • We have enabled web search-based reviews and chat for all users. This feature allows CodeRabbit to access the latest documentation and information on the web.
  • You can disable this feature by setting web_search: false in the knowledge_base settings.
  • Please share any feedback in the Discord discussion.
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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 30, 2025

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit aaebcf8
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/67a25d094b40f50008613345
😎 Deploy Preview https://deploy-preview-10289--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
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: 11

🔭 Outside diff range comments (3)
src/components/Questionnaire/QuestionTypes/SymptomQuestion.tsx (2)

Line range hint 291-297: Add encounterId to the query key for proper cache invalidation.

The query won't automatically refetch when the encounter changes because encounterId is not included in the query key. This could lead to stale data being displayed.

Apply this diff to ensure proper cache invalidation:

  const { data: patientSymptoms } = useQuery({
-   queryKey: ["symptoms", patientId],
+   queryKey: ["symptoms", patientId, encounterId],
    queryFn: query(symptomApi.listSymptoms, {
      pathParams: { patientId },
      queryParams: {
        encounter: encounterId,
        limit: 100,
      },
    }),
  });

Line range hint 300-311: Add missing dependencies to useEffect.

The effect's dependency array is missing updateQuestionnaireResponseCB and questionnaireResponse.question_id. This could lead to stale closures and potentially cause updates to be lost.

Apply this diff to fix the dependency array:

  useEffect(() => {
    if (patientSymptoms?.results) {
      updateQuestionnaireResponseCB(
        [
          {
            type: "symptom",
            value: patientSymptoms.results.map(convertToSymptomRequest),
          },
        ],
        questionnaireResponse.question_id,
      );
    }
-  }, [patientSymptoms]);
+  }, [patientSymptoms, updateQuestionnaireResponseCB, questionnaireResponse.question_id]);
src/components/Patient/PatientDetailsTab/index.tsx (1)

Remove redundant patientId prop

The patientId prop is redundant with the existing id prop. The codebase consistently uses id as the primary identifier, and when patientId is needed, it's derived from either id or patientData.id. To maintain consistency and avoid confusion, use only the id prop.

🔗 Analysis chain

Line range hint 13-17: Verify patientId prop usage.

The patientId prop has been added to PatientProps, but it seems redundant with the existing id prop.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if patientId and id are used differently
rg -A 5 "patientId.*=.*props" src/
rg -A 5 "id.*=.*props" src/

Length of output: 17668

🧹 Nitpick comments (14)
src/components/ui/button.tsx (1)

45-47: Consider documenting the default variant change.

Since this is a breaking change that could affect the appearance of buttons across the application, consider:

  1. Adding a comment explaining why "primary" is now the default variant
  2. Updating component documentation if it exists
  3. Communicating this change to other developers
src/components/Questionnaire/QuestionTypes/DiagnosisQuestion.tsx (1)

87-87: Consider adding error handling for invalid encounter IDs.

While the implementation is correct, consider adding validation or error handling for cases where the encounterId might be invalid or empty.

 export function DiagnosisQuestion({
   encounterId,
   patientId,
   questionnaireResponse,
   updateQuestionnaireResponseCB,
   disabled,
 }: DiagnosisQuestionProps) {
+  if (!encounterId?.trim()) {
+    console.warn('Invalid or empty encounter ID provided');
+  }
   const diagnoses =
     (questionnaireResponse.values?.[0]?.value as DiagnosisRequest[]) || [];

   const { data: patientDiagnoses } = useQuery({

Also applies to: 101-101

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

81-81: LGTM! Consider documenting the encounter-specific behavior.

The implementation correctly adds encounter context to medication requests. Consider adding a comment explaining how encounter filtering affects medication requests visibility.

 interface MedicationRequestQuestionProps {
   patientId: string;
   questionnaireResponse: QuestionnaireResponse;
   updateQuestionnaireResponseCB: (
     values: ResponseValue[],
     questionId: string,
     note?: string,
   ) => void;
   disabled?: boolean;
+  /** 
+   * Encounter ID used to filter medication requests.
+   * When provided, only shows medications associated with this encounter.
+   */
   encounterId: string;
 }

Also applies to: 89-89, 99-99

src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (3)

43-48: Ensure extensibility for alternate time partitions
The hard-coded time slots could be made configurable if the application needs to adapt to different shift schedules or time block conventions in the future. Storing them in a separate configuration (or retrieving from an endpoint) might allow more flexibility.


65-85: Consider extracting visible slot calculation logic
The memoized calculation of visible time slots is rather involved and could be extracted into a custom hook or separate utility. This approach will improve readability and testability.


549-557: Validate safety checks before administering
The “Administer” button is only shown when medication.status === "active" in the current time slot. Ensure that all necessary distribution, ingestion, or cross-verification checks are performed prior to pediatric or high-risk medication administration.

src/components/Medicine/MedicationAdministration/utils.ts (1)

26-33: Simplify the dose property assignment.

The code has repetitive access to the dose quantity object, which can be simplified.

Apply this diff to reduce repetition:

-     dose: medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity && {
-       value:
-         medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity?.value,
-       unit: medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity
-         ?.unit,
-       code: medication.dosage_instruction[0]?.dose_and_rate?.dose_quantity
-         ?.unit,
+     dose: (() => {
+       const doseQuantity = medication.dosage_instruction?.[0]?.dose_and_rate?.dose_quantity;
+       return doseQuantity && {
+         value: doseQuantity.value,
+         unit: doseQuantity.unit,
+         code: doseQuantity.unit,
+       };
+     })(),
src/types/emr/medicationAdministration/medicationAdministration.ts (1)

56-96: Consider extracting common properties into a shared interface.

The MedicationAdministrationRequest and MedicationAdministrationRead interfaces share many common properties. Consider creating a base interface to reduce duplication:

interface BaseMedicationAdministration {
  status: MedicationAdministrationStatus;
  status_reason?: Code;
  medication: Code;
  occurrence_period_start: string;
  occurrence_period_end?: string;
  recorded?: string;
  note?: string;
  dosage?: {
    text?: string;
    site?: Code;
    route?: Code;
    method?: Code;
    dose?: DosageQuantity;
    rate?: Quantity;
  };
}

export interface MedicationAdministrationRequest extends BaseMedicationAdministration {
  id?: string;
  encounter: string;
  request: string;
}

export interface MedicationAdministrationRead extends BaseMedicationAdministration {
  id: string;
  encounter: string;
  request: string;
}
src/components/Medicine/MedicationRequestTable/MedicineItem.tsx (1)

23-74: Enhance accessibility with ARIA labels and better contrast.

Consider the following improvements:

  1. Add aria-label to the grid container
  2. Use semantic HTML elements for the medication name (h3 is good)
  3. Ensure text contrast meets WCAG guidelines, especially for text-muted-foreground
-    <div className="space-y-1">
+    <div className="space-y-1" role="article" aria-label={`Medication details for ${medication.medication?.display}`}>
src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1)

43-46: Prevent potential race conditions in useEffect.

The current useEffect implementation might cause race conditions if initialRequest changes rapidly. Consider adding a cleanup function:

  React.useEffect(() => {
+   let isActive = true;
-   setAdministrationRequest(initialRequest);
+   if (isActive) {
+     setAdministrationRequest(initialRequest);
+   }
+   return () => {
+     isActive = false;
+   };
  }, [initialRequest]);
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1)

181-195: Consider consolidating date/time picker logic.

The start and end time picker implementations share similar logic. Consider extracting this into a reusable component to reduce code duplication.

Also applies to: 197-220

src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1)

242-248: Consider adding loading state to submit button.

The submit button should show a loading state during mutation to prevent double submissions.

 <Button
   type="submit"
   className="bg-[#006D4C] hover:bg-[#006D4C]/90"
-  disabled={selectedMedicines.size === 0}
+  disabled={selectedMedicines.size === 0 || isSubmitting}
 >
-  {t("administer_medicines")} ({selectedMedicines.size})
+  {isSubmitting ? t("submitting") : `${t("administer_medicines")} (${selectedMedicines.size})`}
 </Button>
src/types/emr/medicationRequest.ts (1)

95-96: Track technical debt: Remove code property after backend update.

The TODO comment indicates this is a temporary change. Create a follow-up ticket to remove this property once the backend administration dosage is updated from Quantity to DosageQuantity.

Would you like me to create a tracking issue for removing this temporary code once the backend is updated?

src/components/Medicine/MedicationRequestTable/index.tsx (1)

115-219: Excellent UI/UX improvements with comprehensive features.

The UI implementation includes:

  • Clear tab separation between prescriptions and administration
  • Responsive search with instant feedback
  • Proper handling of loading and empty states
  • Horizontal scrolling for wide content

Consider adding keyboard shortcuts for common actions like searching and clearing the search input.

 <input
   type="text"
   placeholder="Search medications..."
   value={searchQuery}
   onChange={(e) => setSearchQuery(e.target.value)}
+  onKeyDown={(e) => {
+    if (e.key === 'Escape') {
+      setSearchQuery('');
+    }
+  }}
   className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
 />
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 50cb157 and 866b730.

📒 Files selected for processing (27)
  • public/locale/en.json (11 hunks)
  • src/Utils/request/api.tsx (2 hunks)
  • src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2 hunks)
  • src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/utils.ts (1 hunks)
  • src/components/Medicine/MedicationRequestTable/MedicineItem.tsx (1 hunks)
  • src/components/Medicine/MedicationRequestTable/MedicineList.tsx (1 hunks)
  • src/components/Medicine/MedicationRequestTable/index.tsx (3 hunks)
  • src/components/Medicine/MedicationsTable.tsx (1 hunks)
  • src/components/Medicine/MultiValueSetSelect.tsx (1 hunks)
  • src/components/Medicine/utils.ts (1 hunks)
  • src/components/Patient/PatientDetailsTab/index.tsx (3 hunks)
  • src/components/Patient/PatientHome.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/AllergyQuestion.tsx (3 hunks)
  • src/components/Questionnaire/QuestionTypes/DiagnosisQuestion.tsx (3 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationRequestQuestion.tsx (2 hunks)
  • src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (3 hunks)
  • src/components/Questionnaire/QuestionTypes/QuestionInput.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/SymptomQuestion.tsx (3 hunks)
  • src/components/ui/button.tsx (1 hunks)
  • src/pages/Encounters/tabs/EncounterUpdatesTab.tsx (1 hunks)
  • src/types/emr/medicationAdministration/medicationAdministration.ts (3 hunks)
  • src/types/emr/medicationAdministration/medicationAdministrationApi.ts (1 hunks)
  • src/types/emr/medicationRequest.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Cypress Tests
src/components/Patient/PatientDetailsTab/index.tsx

[error] 35-35: TypeScript error: Cannot find name 'Updates'

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (30)
src/components/Questionnaire/QuestionTypes/SymptomQuestion.tsx (2)

49-49: LGTM! The encounterId prop is correctly typed.

The addition of the required encounterId property aligns with the PR objective to filter symptoms by encounter.


285-285: LGTM! The encounterId prop is correctly destructured.

The prop is properly added to the component's parameter list and matches the interface definition.

src/components/ui/button.tsx (1)

45-45: Verify the impact of changing the default variant.

Changing the default variant from "default" to "primary" will affect all instances where the Button component is used without an explicit variant prop. This could lead to unexpected visual changes across the application.

Let's verify the Button component usage in the codebase:

✅ Verification successful

Change appears safe due to explicit variant usage pattern

The codebase demonstrates a consistent pattern where button variants are explicitly specified rather than relying on default values. This significantly reduces the risk of unintended visual changes from modifying the default variant.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all instances of Button component usage without explicit variant prop

# Search for Button component imports
echo "Button component imports:"
rg "import.*Button.*from.*components/ui/button"

# Search for Button usage without variant prop
# Note: This might have false positives but helps identify potential impacts
echo -e "\nButton usage without variant prop:"
ast-grep --pattern '<Button $$$>'

# Search for Button usage with explicit variants
echo -e "\nButton usage with explicit variants:"
ast-grep --pattern '<Button variant="$_" $$$>'

Length of output: 65794

src/components/Questionnaire/QuestionTypes/QuestionInput.tsx (2)

118-136: LGTM! Implementation aligns with PR objectives.

The conditional rendering of medication components based on encounterId effectively addresses the bug where structured responses weren't being filtered by Encounter. The implementation is clean and handles the absence of encounterId gracefully.


Line range hint 138-174: Verify if appointment type should check for encounterId.

While the implementation for allergy, symptom, diagnosis, and encounter types is consistent and well-structured, the appointment type doesn't follow the same pattern of checking for encounterId. This might be intentional, but it's worth verifying.

Let's verify if other appointment-related components in the codebase require encounterId:

src/components/Questionnaire/QuestionTypes/DiagnosisQuestion.tsx (1)

48-48: LGTM! Props interface updated correctly.

The encounterId property is properly typed as string in the interface.

src/components/Questionnaire/QuestionTypes/MedicationStatementQuestion.tsx (1)

63-63: LGTM! Implementation matches the pattern.

The changes correctly integrate the encounter context into the medication statement queries. The implementation is consistent with other components.

Also applies to: 94-94, 114-114

src/components/Questionnaire/QuestionTypes/AllergyQuestion.tsx (1)

63-63: Verify allergy data filtering behavior.

The implementation looks correct, but since allergies are critical for patient safety, we should verify that filtering by encounter doesn't hide important allergy information from other encounters.

Also applies to: 120-120, 131-131

src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (2)

152-164: Validate correct handling of older administration records
The logic accumulates the most recent occurrence date per medication. Confirm that older records aren’t mistakenly excluded from other computations if they need to be displayed or tracked.


313-314: Verify status transitions for discontinuation
When discontinuing a medication, it transitions to "ended". Verify that additional business rules (e.g., reason for discontinuation) are captured if required.

src/types/emr/medicationAdministration/medicationAdministrationApi.ts (1)

9-20: Confirm return types of upsert endpoint
The upsertMedicationAdministration definition indicates the response type as an array of MedicationAdministrationRequest objects. Verify it matches the backend's actual response (often newly updated or created resources return read objects or references).

src/components/Medicine/MedicationRequestTable/MedicineList.tsx (2)

9-14: LGTM! Well-structured props interface.

The MedicineListProps interface is well-defined with clear types and proper optional property marking.


16-40: LGTM! Clean and efficient implementation.

The component follows React best practices:

  • Proper key usage in map function
  • Controlled component pattern for checkboxes
  • Clear separation of concerns between selection and display
src/components/Medicine/MedicationAdministration/utils.ts (1)

18-19: Consider timezone handling in date formatting.

Both start and end times are set to the same moment using new Date(). This might need to account for timezone differences and duration.

Would you like me to provide an implementation that handles timezones properly?

src/components/Patient/PatientDetailsTab/index.tsx (1)

70-70: ⚠️ Potential issue

Fix TypeScript error: Updates component reference.

The pipeline is failing because the Updates component is still referenced in patientTabs but has been removed.

Apply this diff to fix the error:

  {
    route: "updates",
-   component: Updates,
+   component: QuestionnaireResponsesList,
  },

Likely invalid or redundant comment.

src/pages/Encounters/tabs/EncounterUpdatesTab.tsx (1)

49-52: LGTM! Proper prop passing to QuestionnaireResponsesList.

The addition of the patientId prop aligns with the PR objectives and maintains consistency with other components in the file.

src/types/emr/medicationAdministration/medicationAdministration.ts (1)

46-46: LGTM! Type consistency improvement.

The change from Quantity to DosageQuantity aligns the type with medication request definitions, ensuring consistent typing across the codebase.

src/components/Medicine/MedicationsTable.tsx (1)

Line range hint 22-31: LGTM! Good function extraction.

Making getFrequencyDisplay exportable improves code reusability while maintaining its functionality.

src/components/Medicine/MultiValueSetSelect.tsx (1)

58-58: LGTM! Proper propagation of disabled state.

The addition of the disabled prop to the Button component correctly propagates the disabled state from parent to child, ensuring consistent UI behavior.

src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2)

17-18: LGTM! Props interface updated correctly.

The changes to make encounter optional and add required patientId improve component reusability while maintaining type safety.


128-141: LGTM! API parameters updated appropriately.

The changes to use patientId directly and make encounter conditional align with the interface changes and PR objectives.

src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1)

45-48: Consider adding validation for lastAdministeredDate.

The component accepts lastAdministeredDate as an optional string but doesn't validate its format before using it with new Date(). This could lead to runtime errors with invalid dates.

src/components/Patient/PatientHome.tsx (1)

139-139: LGTM! Consistent prop passing.

The addition of the patientId prop enables child components to access the patient identifier, which is necessary for the medication administration functionality.

src/Utils/request/api.tsx (1)

657-663: LGTM! Well-structured API endpoint.

The new medication administration endpoint follows the established API patterns:

  • RESTful path structure
  • Consistent with other patient-related endpoints
  • Proper typing with PaginatedResponse
src/components/Medicine/MedicationRequestTable/index.tsx (2)

21-50: Well-structured EmptyState component with good UX.

The EmptyState component is well-designed with:

  • Clear prop interface
  • Conditional messaging based on search state
  • Consistent styling with the UI theme

65-101: Clean implementation of medication status management.

Good implementation of medication status handling:

  • Separate queries for active and stopped medications
  • Efficient state management with showStopped
  • Clear separation of concerns in data fetching
public/locale/en.json (4)

752-752: LGTM! Comprehensive status keys for medication administration workflow.

The added status keys provide clear and concise states for tracking medication administration:

  • "draft": For in-progress administration records
  • "ended": For completed administration cycles
  • "in_progress": For active administration
  • "not_done": For skipped or cancelled administration
  • "prescribed": For medications that are prescribed but not yet administered
  • "taken": For successfully administered medications

Also applies to: 902-902, 1071-1071, 1386-1386, 1585-1585, 1985-1985


759-759: LGTM! Clear action and search labels.

The added keys provide clear labels for medication-related actions:

  • "edit_administration": For editing medication administration records
  • "search_medicine": For medicine search functionality

Also applies to: 1810-1810


1137-1137: LGTM! Appropriate messages for various states.

The added keys provide clear messages for:

  • Past time administration confirmation
  • Empty states for diagnoses and symptoms

Also applies to: 1322-1322, 1373-1373


Line range hint 752-1985: Verify consistency in terminology.

Ensure consistent usage of terms across the application:

  1. "medicine" vs "medication"
  2. "administration" vs "administered"
✅ Verification successful

Terminology usage is consistent and follows healthcare standards

The codebase demonstrates intentional and consistent terminology usage:

  • Backend/API layer uses formal "medication" terminology aligned with healthcare standards (FHIR)
  • Frontend/UI layer uses more user-friendly "medicine" terminology for better user experience
  • "Administration" related terms are used consistently across all layers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for inconsistent terminology usage
echo "Checking for terminology consistency..."
rg -i "medic(ine|ation)" 
rg -i "administ(ration|ered|er)"

Length of output: 71229

Comment on lines 203 to 214
if (administrations?.results?.length) {
const sortedAdmins = [...administrations.results].sort(
(a, b) =>
new Date(b.occurrence_period_start).getTime() -
new Date(a.occurrence_period_start).getTime(),
);

if (!lastModifiedDate) {
setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
}
}
}, [administrations]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential stale lastModifiedDate issue
Since lastModifiedDate is only set when no previous date exists, any new administration record after the component mounts won’t update this state (line 210). If tracking the latest modification time is important, consider removing the if (!lastModifiedDate) condition.

- if (!lastModifiedDate) {
+ // Always re-check if the new admin is newer
  setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
📝 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
if (administrations?.results?.length) {
const sortedAdmins = [...administrations.results].sort(
(a, b) =>
new Date(b.occurrence_period_start).getTime() -
new Date(a.occurrence_period_start).getTime(),
);
if (!lastModifiedDate) {
setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
}
}
}, [administrations]);
if (administrations?.results?.length) {
const sortedAdmins = [...administrations.results].sort(
(a, b) =>
new Date(b.occurrence_period_start).getTime() -
new Date(a.occurrence_period_start).getTime(),
);
// Always re-check if the new admin is newer
setLastModifiedDate(new Date(sortedAdmins[0].occurrence_period_start));
}
}, [administrations]);

Comment on lines +16 to +19
path: "/api/v1/patient/{patientId}/medication/administration/upsert/",
method: HttpMethod.POST,
TRes: Type<MedicationAdministrationRequest[]>,
},
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

Support partial failures
Consider whether the upsert endpoint should handle partial errors in case some medication administration requests fail, while others succeed. Returning a more detailed response with success/failure statuses may improve transparency.

Comment on lines +8 to +13
if (dose_range) {
return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
} else if (dose_quantity) {
return `${dose_quantity.value} ${dose_quantity.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}`;
return "";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle missing or invalid dosage data
The logic gracefully checks for dose_range or dose_quantity. However, if dose_range.low.value or .high.value is absent or invalid, this may cause runtime errors. A more robust guard or fallback could help prevent potential crashes.

  if (dose_range) {
+   if (!dose_range.low?.value || !dose_range.low?.unit || !dose_range.high?.value || !dose_range.high?.unit) {
+     return "";
+   }
    return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
  } else if (dose_quantity) {
+   if (!dose_quantity.value || !dose_quantity.unit) {
+     return "";
+   }
    return `${dose_quantity.value} ${dose_quantity.unit.display}`;
  }
📝 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
if (dose_range) {
return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
} else if (dose_quantity) {
return `${dose_quantity.value} ${dose_quantity.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}`;
return "";
if (dose_range) {
if (!dose_range.low?.value || !dose_range.low?.unit || !dose_range.high?.value || !dose_range.high?.unit) {
return "";
}
return `${dose_range.low.value}${dose_range.low.unit.display} - ${dose_range.high.value}${dose_range.high.unit.display}`;
} else if (dose_quantity) {
if (!dose_quantity.value || !dose_quantity.unit) {
return "";
}
return `${dose_quantity.value} ${dose_quantity.unit.display}`;
}
return "";

Comment on lines +23 to +25
site: medication.dosage_instruction[0]?.site,
route: medication.dosage_instruction[0]?.route,
method: medication.dosage_instruction[0]?.method,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add bounds checking for dosage_instruction array access.

The code directly accesses dosage_instruction[0] without checking if the array is empty, which could lead to runtime errors.

Apply this diff to add safety checks:

    dosage: {
-     site: medication.dosage_instruction[0]?.site,
-     route: medication.dosage_instruction[0]?.route,
-     method: medication.dosage_instruction[0]?.method,
+     site: medication.dosage_instruction?.[0]?.site,
+     route: medication.dosage_instruction?.[0]?.route,
+     method: medication.dosage_instruction?.[0]?.method,
📝 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
site: medication.dosage_instruction[0]?.site,
route: medication.dosage_instruction[0]?.route,
method: medication.dosage_instruction[0]?.method,
site: medication.dosage_instruction?.[0]?.site,
route: medication.dosage_instruction?.[0]?.route,
method: medication.dosage_instruction?.[0]?.method,

Comment on lines 44 to 45
{formatDosage(medication.dosage_instruction[0])}
</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for dosage_instruction array access.

Direct access to medication.dosage_instruction[0] could throw if the array is empty. Consider using optional chaining:

-            {formatDosage(medication.dosage_instruction[0])}
+            {formatDosage(medication.dosage_instruction?.[0])}
📝 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
{formatDosage(medication.dosage_instruction[0])}
</p>
{formatDosage(medication.dosage_instruction?.[0])}
</p>

});
};

console.log("patientId", patientId);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove debug console.log statement.

Remove the debug console.log statement before merging to production.

-  console.log("patientId", patientId);

Comment on lines 113 to 136
<div className="space-y-2">
<Label>{t("status")}</Label>
<Select
value={administrationRequest.status}
onValueChange={(value: MedicationAdministrationStatus) =>
onChange({ ...administrationRequest, status: value })
}
disabled={
!!administrationRequest.id &&
administrationRequest.status !== "in_progress"
}
>
<SelectTrigger>
<SelectValue placeholder={t("select_status")} />
</SelectTrigger>
<SelectContent>
{MEDICATION_ADMINISTRATION_STATUS.map((status) => (
<SelectItem key={status} value={status}>
{t(status)}
</SelectItem>
))}
</SelectContent>
</Select>
</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

Consider adding status transition validation.

The status selection logic should validate whether the transition from one status to another is valid according to the medication administration workflow.

 <Select
   value={administrationRequest.status}
   onValueChange={(value: MedicationAdministrationStatus) =>
-    onChange({ ...administrationRequest, status: value })
+    onChange({
+      ...administrationRequest,
+      status: validateStatusTransition(administrationRequest.status, value)
+        ? value
+        : administrationRequest.status,
+    })
   }
   disabled={
     !!administrationRequest.id &&
     administrationRequest.status !== "in_progress"
   }
 >

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

Comment on lines 159 to 165
const now = new Date().toISOString().slice(0, 16);
onChange({
...administrationRequest,
occurrence_period_start: now,
occurrence_period_end: now,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential timezone issue with date handling.

Using toISOString().slice(0, 16) might lead to timezone-related issues. Consider using a more robust date formatting approach.

-const now = new Date().toISOString().slice(0, 16);
+const now = new Date().toISOString();

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

Comment on lines 61 to 71
const { mutate: upsertAdministrations } = useMutation({
mutationFn: mutate(
medicationAdministrationApi.upsertMedicationAdministration,
{
pathParams: { patientId: patientId },
},
),
onSuccess: () => {
onOpenChange(false);
},
});
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

Consider adding error handling for mutation failures.

The mutation success case is handled, but there's no error handling. Consider adding error handling and user feedback.

 const { mutate: upsertAdministrations } = useMutation({
   mutationFn: mutate(
     medicationAdministrationApi.upsertMedicationAdministration,
     {
       pathParams: { patientId: patientId },
     },
   ),
   onSuccess: () => {
     onOpenChange(false);
   },
+  onError: (error) => {
+    // Handle error and show user feedback
+    console.error('Failed to update administrations:', error);
+    // Consider adding a toast notification here
+  },
 });
📝 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 { mutate: upsertAdministrations } = useMutation({
mutationFn: mutate(
medicationAdministrationApi.upsertMedicationAdministration,
{
pathParams: { patientId: patientId },
},
),
onSuccess: () => {
onOpenChange(false);
},
});
const { mutate: upsertAdministrations } = useMutation({
mutationFn: mutate(
medicationAdministrationApi.upsertMedicationAdministration,
{
pathParams: { patientId: patientId },
},
),
onSuccess: () => {
onOpenChange(false);
},
onError: (error) => {
// Handle error and show user feedback
console.error('Failed to update administrations:', error);
// Consider adding a toast notification here
},
});

Comment on lines 116 to 130
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

const administrations = Array.from(selectedMedicines).map((id) => {
return administrationRequests[id];
});

upsertAdministrations({
datapoints: administrations,
});

onOpenChange(false);
setSelectedMedicines(new Set());
setAdministrationRequests({});
};
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 validation before submission.

The submit handler should validate all administration requests before submission to ensure data integrity.

 const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
   e.preventDefault();

   const administrations = Array.from(selectedMedicines).map((id) => {
     return administrationRequests[id];
   });

+  // Validate all administrations before submission
+  const invalidAdministrations = administrations.filter(
+    (admin) => !isValidAdministrationRequest(admin)
+  );
+  if (invalidAdministrations.length > 0) {
+    // Handle invalid administrations
+    return;
+  }

   upsertAdministrations({
     datapoints: administrations,
   });

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

Copy link

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

Deploying care-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: aaebcf8
Status: ✅  Deploy successful!
Preview URL: https://49e9c5b0.care-fe.pages.dev
Branch Preview URL: https://medicine-administration-ui.care-fe.pages.dev

View logs

Copy link

cypress bot commented Jan 30, 2025

CARE    Run #4611

Run Properties:  status check passed Passed #4611  •  git commit aaebcf892c: Medicine administration UI
Project CARE
Branch Review medicine-administration-ui
Run status status check passed Passed #4611
Run duration 06m 06s
Commit git commit aaebcf892c: Medicine administration UI
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 10
View all changes introduced in this branch ↗︎

@amjithtitus09
Copy link
Contributor Author

@nihal467 Needs testing

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

♻️ Duplicate comments (1)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1)

51-67: ⚠️ Potential issue

Fix time validation logic.

The current implementation has several issues:

  1. Uses authored_on instead of encounter start time for validation
  2. Lacks proper timezone handling
  3. Doesn't properly validate against future times
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b33beaf and 8b09293.

📒 Files selected for processing (1)
  • src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Test
  • GitHub Check: OSSAR-Scan
  • GitHub Check: cypress-run (1)
  • GitHub Check: CodeQL-Build
  • GitHub Check: OSSAR-Scan
🔇 Additional comments (4)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (4)

29-35: Add encounterStartTime to props interface.

The props interface is missing the encounterStartTime prop which is required to implement the time validation requirements.


157-174: Add status transition validation.

The status selection should validate whether the transition from one status to another is valid according to the medication administration workflow.


219-236: Add constraints to DateTimePicker components.

Both DateTimePicker components should have min/max constraints to prevent invalid selections.

Also applies to: 238-259


187-217: Fix timezone handling in radio group time update.

When setting current time for non-past administrations, ensure consistent timezone handling.

Comment on lines 69 to 84
const handleStartTimeChange = (newTime: string) => {
const date = new Date(newTime);
const error = validateDateTime(date, true);

if (error) {
setStartTimeError(error);
return;
}

setStartTimeError("");
onChange({
...administrationRequest,
occurrence_period_start: newTime,
occurrence_period_end: newTime,
});
};
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 timezone handling to time change handlers.

The time change handlers need to handle timezones consistently to prevent timezone-related bugs.

Apply this diff to fix the handlers:

 const handleStartTimeChange = (newTime: string) => {
+  // Convert to user's timezone for validation
+  const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
+  const localDate = utcToZonedTime(new Date(newTime), userTz);
-  const date = new Date(newTime);
-  const error = validateDateTime(date, true);
+  const error = validateDateTime(localDate, true);

   if (error) {
     setStartTimeError(error);
     return;
   }

   setStartTimeError("");
+  // Convert back to UTC for storage
+  const utcTime = zonedTimeToUtc(localDate, userTz);
   onChange({
     ...administrationRequest,
-    occurrence_period_start: newTime,
-    occurrence_period_end: newTime,
+    occurrence_period_start: utcTime.toISOString(),
+    occurrence_period_end: utcTime.toISOString(),
   });
 };

Also applies to: 86-100

@nihal467
Copy link
Member

nihal467 commented Feb 3, 2025

image

  • Add the name of the user who administrated the prescription
  • change the date and time component to use the device default one

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: 5

♻️ Duplicate comments (3)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (3)

40-47: ⚠️ Potential issue

Add encounter start time to props interface.

Per PR objectives, users should be restricted from selecting administration dates before encounter start time. The interface is missing this crucial prop.


64-80: ⚠️ Potential issue

Update datetime validation to use encounter start time and handle timezones.

The current validation has two issues:

  1. Uses authored_on instead of encounter start time
  2. Lacks timezone handling which could lead to inconsistencies

257-287: ⚠️ Potential issue

Fix timezone handling for current time.

When setting current time for non-past administrations, ensure consistent timezone handling.

🧹 Nitpick comments (1)
src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1)

62-75: Add input validation to prevent runtime errors.

The isTimeInSlot function should validate its inputs to handle edge cases gracefully.

 function isTimeInSlot(
   date: Date,
   slot: { date: Date; start: string; end: string },
 ): boolean {
+  if (!(date instanceof Date) || !(slot.date instanceof Date)) {
+    return false;
+  }
+  
+  const timeFormat = /^([0-1]?[0-9]|2[0-3]):([0-5][0-9])$/;
+  if (!timeFormat.test(slot.start) || !timeFormat.test(slot.end)) {
+    return false;
+  }
+
   const slotStartDate = new Date(slot.date);
   const [startHour] = slot.start.split(":").map(Number);
   const [endHour] = slot.end.split(":").map(Number);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8b09293 and 0f7f513.

📒 Files selected for processing (5)
  • public/locale/en.json (17 hunks)
  • src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx
  • public/locale/en.json
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx

[error] 122-122: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 137-137: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 137-137: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 146-146: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: cypress-run (1)
🔇 Additional comments (4)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1)

227-244: Consider adding status transition validation.

The status selection logic should validate whether the transition from one status to another is valid according to the medication administration workflow.

src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1)

172-180: 🛠️ Refactor suggestion

Add validation before submitting administrations.

The form submission should validate the administration requests before sending them to the server.

 const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
   e.preventDefault();
   const administrations = Array.from(selectedMedicines).map(
     (id) => administrationRequests[id],
   );
+  
+  // Validate all administrations
+  const invalidAdministrations = administrations.filter(admin => {
+    const { occurrence_period_start, occurrence_period_end } = admin;
+    return !occurrence_period_start || 
+           !occurrence_period_end || 
+           new Date(occurrence_period_end) <= new Date(occurrence_period_start);
+  });
+  
+  if (invalidAdministrations.length > 0) {
+    toast.error(t("invalid_administration_times"));
+    return;
+  }
+
   upsertAdministrations({
     datapoints: administrations,
   });
 };

Likely invalid or redundant comment.

src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (2)

529-554: LGTM! Time slot navigation is well implemented.

The implementation correctly handles:

  • Prevention of scrolling before prescription date
  • Smooth transition between time slots
  • Date changes when crossing day boundaries

555-583: LGTM! Administration request handling is comprehensive.

The implementation correctly handles both new and existing administrations, including proper preservation of notes and other fields when editing.

if (!date) return "";
try {
const dateObj = new Date(date);
if (isNaN(dateObj.getTime())) return "";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace unsafe isNaN with Number.isNaN.

Using global isNaN is unsafe as it attempts type coercion. Use Number.isNaN instead for more predictable behavior.

-      if (isNaN(dateObj.getTime())) return "";
+      if (Number.isNaN(dateObj.getTime())) return "";

-    if (isNaN(hours) || isNaN(minutes)) return;
+    if (Number.isNaN(hours) || Number.isNaN(minutes)) return;

-    if (isNaN(currentDate.getTime())) return;
+    if (Number.isNaN(currentDate.getTime())) return;

Also applies to: 137-137, 146-146

🧰 Tools
🪛 Biome (1.9.4)

[error] 122-122: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

Comment on lines +329 to +334
type="time"
className="w-[150px]"
value={formatTime(administrationRequest.occurrence_period_start)}
onChange={(e) => handleTimeChange(e, true)}
disabled={!isPastTime || !!administrationRequest.id}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add time input constraints.

The time inputs should have min/max constraints to prevent invalid selections:

  1. Prevent selection before encounter start time
  2. Prevent selection of future times
 <Input
   type="time"
   className="w-[150px]"
   value={formatTime(administrationRequest.occurrence_period_start)}
   onChange={(e) => handleTimeChange(e, true)}
   disabled={!isPastTime || !!administrationRequest.id}
+  min={format(new Date(medication.encounter_start_time), 'HH:mm')}
+  max={format(new Date(), 'HH:mm')}
 />

Also applies to: 385-394

Comment on lines +768 to +781
<MedicineAdminSheet
open={isSheetOpen}
onOpenChange={(open) => {
setIsSheetOpen(open);
if (!open) {
refetchAdministrations();
}
}}
medications={medications}
lastAdministeredDates={lastAdministeredDetails?.dates}
patientId={patientId}
encounterId={encounterId}
/>
</div>
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

Add date and time validation constraints to MedicineAdminSheet.

  • The current implementation does not enforce restrictions on date selection—administration dates can be chosen without checking if they are before the encounter start or in the future.
  • No validation logic was found in MedicineAdminForm (or related components) to prevent selecting dates prior to the encounter start or dates beyond the current time.
  • Update the MedicineAdminSheet component (or its underlying date picker logic) to enforce these constraints as required.
🔗 Analysis chain

Add date and time validation constraints.

As per requirements, add validation to prevent:

  1. Selecting administration dates before encounter start time
  2. Selecting future times
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if date/time validation is implemented in MedicineAdminForm

# Search for date/time validation in MedicineAdminForm
ast-grep --pattern 'function MedicineAdminForm($$$) {
  $$$
  const [date, setDate] = useState($$$);
  $$$
}'

# Search for any existing validation logic
rg -A 5 'setDate|onChange.*date'

Length of output: 21892

Comment on lines +637 to +743
{lastModifiedDate && (
<div className="text-xs text-[#6b7280]">
{t("last_modified")}{" "}
{formatDistanceToNow(lastModifiedDate)} {t("ago")}
</div>
)}
</div>
<div className="flex justify-end items-center bg-gray-50 rounded">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400 mr-2"
onClick={handlePreviousSlot}
disabled={!canGoBack}
title={
!canGoBack ? t("cannot_go_before_prescription_date") : ""
}
>
<CareIcon icon="l-angle-left" className="h-4 w-4" />
</Button>
</div>
</div>
{visibleSlots.map((slot) => (
<TimeSlotHeader
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
slot={slot}
isCurrentSlot={isTimeInSlot(currentDate, slot)}
isEndSlot={slot.date.getTime() === currentDate.getTime()}
/>
))}
<div className="flex justify-start items-center px-1 bg-gray-50">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400"
onClick={handleNextSlot}
disabled={isTimeInSlot(currentDate, visibleSlots[3])}
>
<CareIcon icon="l-angle-right" className="h-4 w-4" />
</Button>
</div>
</div>

{/* Main content with borders */}
<div className="col-span-full grid grid-cols-subgrid divide-x divide-[#e5e7eb] border-l border-r">
{/* Headers */}
<div className="p-4 font-medium text-sm border-t bg-[#F3F4F6] text-secondary-700">
{t("medicine")}:
</div>
{visibleSlots.map((slot, i) => (
<div
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
className="p-4 font-semibold text-xs text-center border-t relative bg-[#F3F4F6] text-secondary-700"
>
{i === endSlotIndex &&
slot.date.getTime() === currentDate.getTime() && (
<div className="absolute top-0 left-1/2 -translate-y-1/2 -translate-x-1/2">
<div className="h-2 w-2 rounded-full bg-blue-500" />
</div>
)}
{slot.label}
</div>
))}
<div className="border-t bg-[#F3F4F6]" />

{/* Medication rows */}
{medications?.map((medication) => (
<MedicationRow
key={medication.id}
medication={medication}
visibleSlots={visibleSlots}
currentDate={currentDate}
administrations={administrations?.results}
onAdminister={handleAdminister}
onEditAdministration={handleEditAdministration}
onDiscontinue={handleDiscontinue}
/>
))}
</div>
</div>

{stoppedMedications?.results?.length > 0 && (
<div
className="p-4 border-t border-[#e5e7eb] flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</Card>
<ScrollBar orientation="horizontal" />
</ScrollArea>
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

Implement column and row freezing for better usability.

As per requirements, implement:

  1. Freeze medicine column when scrolling horizontally
  2. Freeze time frame row when scrolling vertically
-      <ScrollArea className="w-full whitespace-nowrap rounded-md">
+      <div className="w-full rounded-md relative">
+        <div className="sticky top-0 z-20 bg-white">
+          {/* Frozen time frame row */}
+          <div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px]">
+            {/* Time frame header content */}
+          </div>
+        </div>
+        <ScrollArea className="w-full whitespace-nowrap">
           <Card className="w-full border-none shadow-none min-w-[640px]">
-            <div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px]">
+            <div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px] relative">
+              <div className="sticky left-0 z-10 bg-white">
+                {/* Frozen medicine column */}
+              </div>
📝 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
<ScrollArea className="w-full whitespace-nowrap rounded-md">
<Card className="w-full border-none shadow-none min-w-[640px]">
<div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px]">
{/* Top row without vertical borders */}
<div className="col-span-full grid grid-cols-subgrid">
<div className="flex items-center justify-between p-4 bg-gray-50 border-t border-gray-50">
<div className="flex items-center gap-2 whitespace-break-spaces">
{lastModifiedDate && (
<div className="text-xs text-[#6b7280]">
{t("last_modified")}{" "}
{formatDistanceToNow(lastModifiedDate)} {t("ago")}
</div>
)}
</div>
<div className="flex justify-end items-center bg-gray-50 rounded">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400 mr-2"
onClick={handlePreviousSlot}
disabled={!canGoBack}
title={
!canGoBack ? t("cannot_go_before_prescription_date") : ""
}
>
<CareIcon icon="l-angle-left" className="h-4 w-4" />
</Button>
</div>
</div>
{visibleSlots.map((slot) => (
<TimeSlotHeader
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
slot={slot}
isCurrentSlot={isTimeInSlot(currentDate, slot)}
isEndSlot={slot.date.getTime() === currentDate.getTime()}
/>
))}
<div className="flex justify-start items-center px-1 bg-gray-50">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400"
onClick={handleNextSlot}
disabled={isTimeInSlot(currentDate, visibleSlots[3])}
>
<CareIcon icon="l-angle-right" className="h-4 w-4" />
</Button>
</div>
</div>
{/* Main content with borders */}
<div className="col-span-full grid grid-cols-subgrid divide-x divide-[#e5e7eb] border-l border-r">
{/* Headers */}
<div className="p-4 font-medium text-sm border-t bg-[#F3F4F6] text-secondary-700">
{t("medicine")}:
</div>
{visibleSlots.map((slot, i) => (
<div
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
className="p-4 font-semibold text-xs text-center border-t relative bg-[#F3F4F6] text-secondary-700"
>
{i === endSlotIndex &&
slot.date.getTime() === currentDate.getTime() && (
<div className="absolute top-0 left-1/2 -translate-y-1/2 -translate-x-1/2">
<div className="h-2 w-2 rounded-full bg-blue-500" />
</div>
)}
{slot.label}
</div>
))}
<div className="border-t bg-[#F3F4F6]" />
{/* Medication rows */}
{medications?.map((medication) => (
<MedicationRow
key={medication.id}
medication={medication}
visibleSlots={visibleSlots}
currentDate={currentDate}
administrations={administrations?.results}
onAdminister={handleAdminister}
onEditAdministration={handleEditAdministration}
onDiscontinue={handleDiscontinue}
/>
))}
</div>
</div>
{stoppedMedications?.results?.length > 0 && (
<div
className="p-4 border-t border-[#e5e7eb] flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</Card>
<ScrollBar orientation="horizontal" />
</ScrollArea>
<div className="w-full rounded-md relative">
<div className="sticky top-0 z-20 bg-white">
{/* Frozen time frame row */}
<div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px]">
{/* Time frame header content */}
</div>
</div>
<ScrollArea className="w-full whitespace-nowrap">
<Card className="w-full border-none shadow-none min-w-[640px]">
<div className="grid grid-cols-[minmax(200px,2fr),repeat(4,minmax(140px,1fr)),40px] relative">
<div className="sticky left-0 z-10 bg-white">
{/* Frozen medicine column */}
</div>
{/* Top row without vertical borders */}
<div className="col-span-full grid grid-cols-subgrid">
<div className="flex items-center justify-between p-4 bg-gray-50 border-t border-gray-50">
<div className="flex items-center gap-2 whitespace-break-spaces">
{lastModifiedDate && (
<div className="text-xs text-[#6b7280]">
{t("last_modified")}{" "}
{formatDistanceToNow(lastModifiedDate)} {t("ago")}
</div>
)}
</div>
<div className="flex justify-end items-center bg-gray-50 rounded">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400 mr-2"
onClick={handlePreviousSlot}
disabled={!canGoBack}
title={
!canGoBack ? t("cannot_go_before_prescription_date") : ""
}
>
<CareIcon icon="l-angle-left" className="h-4 w-4" />
</Button>
</div>
</div>
{visibleSlots.map((slot) => (
<TimeSlotHeader
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
slot={slot}
isCurrentSlot={isTimeInSlot(currentDate, slot)}
isEndSlot={slot.date.getTime() === currentDate.getTime()}
/>
))}
<div className="flex justify-start items-center px-1 bg-gray-50">
<Button
variant="outline"
size="icon"
className="h-8 w-8 text-gray-400"
onClick={handleNextSlot}
disabled={isTimeInSlot(currentDate, visibleSlots[3])}
>
<CareIcon icon="l-angle-right" className="h-4 w-4" />
</Button>
</div>
</div>
{/* Main content with borders */}
<div className="col-span-full grid grid-cols-subgrid divide-x divide-[#e5e7eb] border-l border-r">
{/* Headers */}
<div className="p-4 font-medium text-sm border-t bg-[#F3F4F6] text-secondary-700">
{t("medicine")}:
</div>
{visibleSlots.map((slot, i) => (
<div
key={`${format(slot.date, "yyyy-MM-dd")}-${slot.start}`}
className="p-4 font-semibold text-xs text-center border-t relative bg-[#F3F4F6] text-secondary-700"
>
{i === endSlotIndex &&
slot.date.getTime() === currentDate.getTime() && (
<div className="absolute top-0 left-1/2 -translate-y-1/2 -translate-x-1/2">
<div className="h-2 w-2 rounded-full bg-blue-500" />
</div>
)}
{slot.label}
</div>
))}
<div className="border-t bg-[#F3F4F6]" />
{/* Medication rows */}
{medications?.map((medication) => (
<MedicationRow
key={medication.id}
medication={medication}
visibleSlots={visibleSlots}
currentDate={currentDate}
administrations={administrations?.results}
onAdminister={handleAdminister}
onEditAdministration={handleEditAdministration}
onDiscontinue={handleDiscontinue}
/>
))}
</div>
</div>
{stoppedMedications?.results?.length > 0 && (
<div
className="p-4 border-t border-[#e5e7eb] flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</Card>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>

@nihal467
Copy link
Member

nihal467 commented Feb 3, 2025

image

  • when we are trying to type past the start and end time in the medicine administration, the frontend is throwing error

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

♻️ Duplicate comments (5)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (5)

142-142: ⚠️ Potential issue

Replace unsafe isNaN with Number.isNaN.

Using global isNaN is unsafe as it attempts type coercion. Use Number.isNaN instead for more predictable behavior.

-      if (isNaN(dateObj.getTime())) return "";
+      if (Number.isNaN(dateObj.getTime())) return "";

-    if (isNaN(hours) || isNaN(minutes)) return;
+    if (Number.isNaN(hours) || Number.isNaN(minutes)) return;

-    if (isNaN(currentDate.getTime())) return;
+    if (Number.isNaN(currentDate.getTime())) return;

Also applies to: 157-157, 166-166

🧰 Tools
🪛 Biome (1.9.4)

[error] 142-142: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


269-284: ⚠️ Potential issue

Fix timezone handling for current time.

When setting current time for non-past administrations, ensure consistent timezone handling to prevent timezone-related bugs.

 if (newValue === "no") {
-  const now = new Date().toISOString();
+  // Convert local time to UTC for storage
+  const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
+  const now = zonedTimeToUtc(new Date(), userTz).toISOString();
   onChange({
     ...administrationRequest,
     occurrence_period_start: now,
     occurrence_period_end: now,
   });
 }

325-338: ⚠️ Potential issue

Add date constraints to Calendar components.

Both Calendar components should have min/max constraints to prevent invalid selections:

  1. Prevent selection before encounter start time
  2. Prevent selection of future dates
 <Calendar
   mode="single"
   selected={...}
   onSelect={...}
   initialFocus
+  disabled={(date) => {
+    const now = new Date();
+    const encounterStart = new Date(encounterStartTime);
+    return date < encounterStart || date > now;
+  }}
 />

Also applies to: 381-394


341-346: ⚠️ Potential issue

Add time input constraints.

The time inputs should have min/max constraints to prevent invalid selections:

  1. Prevent selection before encounter start time
  2. Prevent selection of future times
 <Input
   type="time"
   className="w-[150px]"
   value={formatTime(administrationRequest.occurrence_period_start)}
   onChange={(e) => handleTimeChange(e, true)}
   disabled={!isPastTime || !!administrationRequest.id}
+  min={format(new Date(encounterStartTime), 'HH:mm')}
+  max={format(new Date(), 'HH:mm')}
 />

Also applies to: 396-406


239-256: 🛠️ Refactor suggestion

Consider adding status transition validation.

The status selection logic should validate whether the transition from one status to another is valid according to the medication administration workflow.

 <Select
   value={administrationRequest.status}
   onValueChange={(value: MedicationAdministrationStatus) =>
-    onChange({ ...administrationRequest, status: value })
+    onChange({
+      ...administrationRequest,
+      status: validateStatusTransition(administrationRequest.status, value)
+        ? value
+        : administrationRequest.status,
+    })
   }
   disabled={
     !!administrationRequest.id &&
     administrationRequest.status !== "in_progress"
   }
 >
🧹 Nitpick comments (3)
src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (2)

68-70: Ensure color contrast meets WCAG guidelines.

The text color text-rose-500 for the "as needed" indicator might not provide sufficient contrast for accessibility. Consider using a darker shade or adding additional visual indicators.

-              <span className="text-sm text-rose-500">
+              <span className="text-sm text-rose-700 font-medium">

105-291: Well-structured component with room for UI improvements.

The component is well-implemented with proper error handling, form validation, and state management. Consider implementing the remaining UI improvements for better usability:

  1. Sticky headers and columns for better scrolling experience
  2. Consistent row heights
  3. Collapsed view for medicine administration beyond a certain count
src/components/Medicine/MedicationRequestTable/index.tsx (1)

102-109: Enhance medication search functionality.

The current search only matches against medication names. Consider including additional searchable fields for better usability.

 const displayedMedications = medications.filter(
   (med: MedicationRequestRead) => {
     if (!searchQuery.trim()) return true;
     const searchTerm = searchQuery.toLowerCase().trim();
     const medicationName = med.medication?.display?.toLowerCase() || "";
-    return medicationName.includes(searchTerm);
+    const dosageInstruction = med.dosageInstruction?.[0]?.text?.toLowerCase() || "";
+    const route = med.dosageInstruction?.[0]?.route?.display?.toLowerCase() || "";
+    return (
+      medicationName.includes(searchTerm) ||
+      dosageInstruction.includes(searchTerm) ||
+      route.includes(searchTerm)
+    );
   },
 );
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0f7f513 and 57bacc0.

📒 Files selected for processing (4)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1 hunks)
  • src/components/Medicine/MedicationRequestTable/index.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx

[error] 142-142: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 157-157: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 157-157: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 166-166: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Test
  • GitHub Check: CodeQL-Build
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: OSSAR-Scan
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (9)
src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (4)

1-46: LGTM! Well-structured imports and interfaces.

The imports are logically organized, and the interfaces are comprehensive and type-safe.


178-186: Add validation before submission.

The submit handler should validate all administration requests before submission to ensure data integrity.


246-263: Implement sticky headers and columns for better usability.

As per requirements, the medicine column should be frozen when scrolling right, and the time frame row should be frozen when scrolling down.


276-280: 🛠️ Refactor suggestion

Remove redundant event handlers in the submit button.

The button already has type="submit" and the form has an onSubmit handler, making the onClick handler redundant and potentially unsafe due to type casting.

               <Button
                 type="submit"
                 className="bg-[#006D4C] hover:bg-[#006D4C]/90"
                 disabled={
                   selectedMedicines.size === 0 || isPending || !isAllFormsValid
                 }
-                onClick={(e) => {
-                  e.preventDefault();
-                  handleSubmit(e as any);
-                }}
               >

Likely invalid or redundant comment.

src/components/Medicine/MedicationRequestTable/index.tsx (2)

21-50: Well-structured EmptyState component!

The component is well-designed with clear props and good defaults, following React best practices and maintaining consistent UI patterns.


67-93: Add error handling for medication queries.

Both active and stopped medication queries lack error handling, which could lead to silent failures.

Add error handling to both queries:

 const { data: activeMedications, isLoading: loadingActive } = useQuery({
   queryKey: ["medication_requests_active", patientId],
   queryFn: query(medicationRequestApi.list, {
     pathParams: { patientId: patientId },
     queryParams: {
       encounter: encounterId,
       limit: 100,
       status: ["active", "on-hold", "draft", "unknown"].join(","),
     },
   }),
   enabled: !!patientId,
+  onError: (error) => {
+    console.error('Failed to fetch active medications:', error);
+  },
 });

 const { data: stoppedMedications, isLoading: loadingStopped } = useQuery({
   queryKey: ["medication_requests_stopped", patientId],
   queryFn: query(medicationRequestApi.list, {
     pathParams: { patientId: patientId },
     queryParams: {
       encounter: encounterId,
       limit: 100,
       status: ["ended", "completed", "cancelled", "entered_in_error"].join(","),
     },
   }),
   enabled: !!patientId,
+  onError: (error) => {
+    console.error('Failed to fetch stopped medications:', error);
+  },
 });
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (3)

258-267: LGTM!

The notes input implementation looks good and should fix the notes update issue mentioned in the PR objectives.


40-48: ⚠️ Potential issue

Add encounter start time to props interface.

Per PR objectives, users should be restricted from selecting administration dates before encounter start time. The encounterStartTime is missing from the props interface.

 interface MedicineAdminFormProps {
   medication: MedicationRequestRead;
   lastAdministeredDate?: string;
   lastAdministeredBy?: string;
   administrationRequest: MedicationAdministrationRequest;
   onChange: (request: MedicationAdministrationRequest) => void;
   formId: string;
   isValid?: (valid: boolean) => void;
+  encounterStartTime: string;
 }

Likely invalid or redundant comment.


66-82: ⚠️ Potential issue

Update datetime validation to use encounter start time.

The validation currently uses authored_on instead of encounter start time, which doesn't align with the PR objectives.

 const validateDateTime = (date: Date, isStartTime: boolean): string => {
   const now = new Date();
-  const authoredOn = new Date(medication.authored_on);
+  const encounterStart = new Date(encounterStartTime);
   const startTime = new Date(administrationRequest.occurrence_period_start);

   if (date > now) {
     return t(
       isStartTime ? "start_time_future_error" : "end_time_future_error",
     );
   }

   if (isStartTime) {
-    return date < authoredOn ? t("start_time_before_authored_error") : "";
+    return date < encounterStart ? t("start_time_before_encounter_error") : "";
   }

   return date < startTime ? t("end_time_before_start_error") : "";
 };

Likely invalid or redundant comment.

Comment on lines +184 to +207
<ScrollArea className="h-[calc(100vh-16rem)]">
<div className="min-w-[800px]">
<div className="p-2">
<MedicationsTable medications={displayedMedications} />
</div>
{!!stoppedMedications?.results?.length && (
<div
className="p-4 flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
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

Consider enhancing scroll behavior based on reported issues.

Based on the reported issues in the PR, the scroll implementation needs improvements:

  • Medicine column should be frozen when scrolling right
  • Time frame row should be frozen when scrolling down

Consider using CSS sticky positioning or a specialized table component that supports frozen columns and rows. For example:

-<ScrollArea className="h-[calc(100vh-16rem)]">
+<ScrollArea className="h-[calc(100vh-16rem)] relative">
   <div className="min-w-[800px]">
-    <div className="p-2">
+    <div className="p-2 relative">
+      <div className="sticky left-0 z-10 bg-white">
+        {/* Medicine column */}
+      </div>
+      <div className="sticky top-0 z-10 bg-white">
+        {/* Time frame row */}
+      </div>
       <MedicationsTable medications={displayedMedications} />
     </div>
📝 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
<ScrollArea className="h-[calc(100vh-16rem)]">
<div className="min-w-[800px]">
<div className="p-2">
<MedicationsTable medications={displayedMedications} />
</div>
{!!stoppedMedications?.results?.length && (
<div
className="p-4 flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
<ScrollArea className="h-[calc(100vh-16rem)] relative">
<div className="min-w-[800px]">
<div className="p-2 relative">
<div className="sticky left-0 z-10 bg-white">
{/* Medicine column */}
</div>
<div className="sticky top-0 z-10 bg-white">
{/* Time frame row */}
</div>
<MedicationsTable medications={displayedMedications} />
</div>
{!!stoppedMedications?.results?.length && (
<div
className="p-4 flex items-center gap-2 cursor-pointer hover:bg-gray-50"
onClick={() => setShowStopped(!showStopped)}
>
<CareIcon
icon={showStopped ? "l-eye-slash" : "l-eye"}
className="h-4 w-4"
/>
<span className="text-sm underline">
{showStopped ? t("hide") : t("show")}{" "}
{`${stoppedMedications?.results?.length} ${t("stopped")}`}{" "}
{t("prescriptions")}
</span>
</div>
)}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>

@nihal467
Copy link
Member

nihal467 commented Feb 4, 2025

LGTM

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

♻️ Duplicate comments (4)
src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (2)

52-63: ⚠️ Potential issue

Add error handling and validation to mutation.

Based on the PR requirements:

  1. Add error handling to show user-friendly error messages
  2. Add time validation to prevent future dates
 const { mutate: upsertAdministration, isPending } = useMutation({
   mutationFn: mutate(
     medicationAdministrationApi.upsertMedicationAdministration,
     {
       pathParams: { patientId: patientId },
     },
   ),
   onSuccess: () => {
     onOpenChange(false);
     toast.success(t("medication_administration_saved"));
   },
+  onError: (error) => {
+    toast.error(t("medication_administration_error"));
+  },
 });

65-69: ⚠️ Potential issue

Add time validation before submission.

Based on the PR requirements, add validation to:

  1. Prevent future dates
  2. Validate against encounter start time
 const handleSubmit = () => {
+  const now = new Date();
+  const administrationDate = new Date(administrationRequest.administeredAt);
+  
+  if (administrationDate > now) {
+    toast.error(t("future_administration_not_allowed"));
+    return;
+  }
+  
   upsertAdministration({
     datapoints: [administrationRequest],
   });
 };
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (2)

325-342: ⚠️ Potential issue

Update Calendar components to use encounter start time.

Both Calendar components use authored_on instead of encounter start time for date constraints.

 <Calendar
   mode="single"
   selected={...}
   onSelect={...}
   initialFocus
   disabled={(date) => {
     const now = new Date();
-    const encounterStart = new Date(medication.authored_on);
+    const encounterStart = new Date(encounterStartTime);
     return date < encounterStart || date > now;
   }}
 />

Also applies to: 386-403


346-351: ⚠️ Potential issue

Add time input constraints.

The time inputs should have min/max constraints to prevent invalid selections.

 <Input
   type="time"
   className="w-[150px]"
   value={formatTime(administrationRequest.occurrence_period_start)}
   onChange={(e) => handleTimeChange(e, true)}
   disabled={!isPastTime || !!administrationRequest.id}
+  min={format(new Date(encounterStartTime), 'HH:mm')}
+  max={format(new Date(), 'HH:mm')}
 />

Also applies to: 407-416

🧹 Nitpick comments (2)
src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (1)

51-91: Consider optimizing date calculations.

The utility functions are well-structured, but the date calculations could be more efficient by:

  1. Caching the date objects instead of creating new ones for each comparison.
  2. Using timestamps for comparisons instead of Date objects.
 function getAdministrationsForTimeSlot(
   administrations: MedicationAdministration[],
   medicationId: string,
   slotDate: Date,
   start: string,
   end: string,
 ): MedicationAdministration[] {
+  const [startHour] = start.split(":").map(Number);
+  const [endHour] = end.split(":").map(Number);
+  const slotStartDate = new Date(slotDate);
+  const slotEndDate = new Date(slotDate);
+  slotStartDate.setHours(startHour, 0, 0, 0);
+  slotEndDate.setHours(endHour, 0, 0, 0);
+  const startTimestamp = slotStartDate.getTime();
+  const endTimestamp = slotEndDate.getTime();
   return administrations.filter((admin) => {
-    const adminDate = new Date(admin.occurrence_period_start);
-    const slotStartDate = new Date(slotDate);
-    const slotEndDate = new Date(slotDate);
-    const [startHour] = start.split(":").map(Number);
-    const [endHour] = end.split(":").map(Number);
-    slotStartDate.setHours(startHour, 0, 0, 0);
-    slotEndDate.setHours(endHour, 0, 0, 0);
+    const adminTimestamp = new Date(admin.occurrence_period_start).getTime();
     return (
       admin.request === medicationId &&
-      adminDate >= slotStartDate &&
-      adminDate < slotEndDate
+      adminTimestamp >= startTimestamp &&
+      adminTimestamp < endTimestamp
     );
   });
 }
src/types/emr/medicationRequest/medicationRequestApi.ts (1)

11-15: Consider adding JSDoc comments for the upsert endpoint.

The endpoint looks good but would benefit from documentation explaining:

  1. When it creates vs updates records
  2. The expected request payload structure
  3. Why it returns an array instead of a single record
+/**
+ * Upserts medication requests.
+ * Creates a new request if no ID is provided, otherwise updates existing request.
+ * @returns Array of created/updated medication requests
+ */
 upsert: {
   path: "/api/v1/patient/{patientId}/medication/request/upsert/",
   method: HttpMethod.POST,
   TRes: Type<MedicationRequestRead[]>,
 },
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 57bacc0 and aaebcf8.

📒 Files selected for processing (10)
  • public/locale/en.json (18 hunks)
  • src/CAREUI/display/SubHeading.tsx (0 hunks)
  • src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1 hunks)
  • src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx (1 hunks)
  • src/components/Questionnaire/QuestionTypes/DateTimeQuestion.tsx (2 hunks)
  • src/components/ui/date-picker.tsx (2 hunks)
  • src/components/ui/date-range-picker.tsx (2 hunks)
  • src/types/emr/medicationRequest/medicationRequestApi.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/CAREUI/display/SubHeading.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/Medicine/MedicationAdministration/MedicineAdminSheet.tsx
  • public/locale/en.json
🧰 Additional context used
📓 Learnings (1)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (1)
Learnt from: rithviknishad
PR: ohcnetwork/care_fe#10289
File: src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx:325-338
Timestamp: 2025-02-04T07:25:30.683Z
Learning: Calendar components in medication administration forms should include date constraints using the `disabled` prop to prevent selection of dates before encounter start time and future dates.
🪛 Biome (1.9.4)
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx

[error] 142-142: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 157-157: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 157-157: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 166-166: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: cypress-run (1)
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (18)
src/components/Medicine/MedicationAdministration/AdministrationTab.tsx (5)

93-124: Well-structured interfaces!

The component interfaces are well-defined with clear prop types and good separation of concerns.


252-285: Improve accessibility for medication administration records.

The clickable div should be a button for better accessibility. Also, consider adding aria-label for the note icon.


745-758: Add date and time validation constraints.

As per requirements, add validation to prevent:

  1. Selecting administration dates before encounter start time
  2. Selecting future times

614-720: Implement column and row freezing for better usability.

As per requirements, implement:

  1. Freeze medicine column when scrolling horizontally
  2. Freeze time frame row when scrolling vertically

495-503: Add error handling for discontinue mutation.

The discontinue mutation lacks error handling for failed requests.

src/types/emr/medicationRequest/medicationRequestApi.ts (2)

1-1: LGTM! Good use of HttpMethod enum.

Using HttpMethod enum instead of string literals provides better type safety.


5-10: LGTM! Consistent use of HttpMethod enum.

The endpoint structure follows RESTful conventions and uses the HttpMethod enum consistently.

src/components/Medicine/MedicationAdministration/MedicineAdminDialog.tsx (3)

24-42: LGTM! Well-structured props interface.

Props are well-typed and provide all necessary data for medicine administration.


43-50: LGTM! Clean state management.

Good use of React hooks for state management and synchronization with initial data.


71-111: LGTM! Clean and accessible dialog implementation.

The UI implementation:

  • Follows design system conventions
  • Handles loading and validation states
  • Uses translations consistently
src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx (2)

40-48: ⚠️ Potential issue

Add encounterStartTime to props interface.

The component uses medication.authored_on for time validation, but according to PR objectives, users should be restricted from selecting administration dates before encounter start time.

 interface MedicineAdminFormProps {
   medication: MedicationRequestRead;
   lastAdministeredDate?: string;
   lastAdministeredBy?: string;
   administrationRequest: MedicationAdministrationRequest;
   onChange: (request: MedicationAdministrationRequest) => void;
   formId: string;
   isValid?: (valid: boolean) => void;
+  encounterStartTime: string;
 }
⛔ Skipped due to learnings
Learnt from: rithviknishad
PR: ohcnetwork/care_fe#10289
File: src/components/Medicine/MedicationAdministration/MedicineAdminForm.tsx:325-338
Timestamp: 2025-02-04T07:25:30.683Z
Learning: Calendar components in medication administration forms should include date constraints using the `disabled` prop to prevent selection of dates before encounter start time and future dates.

66-82: ⚠️ Potential issue

Fix time validation logic.

The validation has two issues:

  1. Uses authored_on instead of encounter start time
  2. Lacks timezone handling
 const validateDateTime = (date: Date, isStartTime: boolean): string => {
+  // Convert to user's timezone for comparison
+  const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
+  const localDate = utcToZonedTime(date, userTz);
   const now = new Date();
-  const authoredOn = new Date(medication.authored_on);
+  const encounterStart = utcToZonedTime(new Date(encounterStartTime), userTz);
   const startTime = new Date(administrationRequest.occurrence_period_start);

   if (date > now) {
     return t(
       isStartTime ? "start_time_future_error" : "end_time_future_error",
     );
   }

   if (isStartTime) {
-    return date < authoredOn ? t("start_time_before_authored_error") : "";
+    return date < encounterStart ? t("start_time_before_encounter_error") : "";
   }

   return date < startTime ? t("end_time_before_start_error") : "";
 };

Likely invalid or redundant comment.

src/components/ui/date-picker.tsx (1)

2-2: LGTM! Import added for internationalization.

The i18next import is correctly added for localization support.

src/components/ui/date-range-picker.tsx (2)

2-2: LGTM! Import added for internationalization.

The i18next import is correctly added for localization support.


55-55: Verify date range restrictions for medicine history.

While the localization change is good, based on the PR comments about preventing scrolling beyond medicine start date, consider adding validation to the date range selection.

Consider adding a minDate prop and passing it to the Calendar component:

 export function DateRangePicker({
   date,
   onChange,
   className,
+  minDate?: Date,
 }: DateRangePickerProps) {
   return (
     // ...
     <Calendar
       mode="range"
       selected={date}
       onSelect={onChange}
+      fromDate={minDate}
     />
     // ...
   );
 }
src/components/Questionnaire/QuestionTypes/DateTimeQuestion.tsx (3)

2-2: LGTM! Import added for internationalization.

The i18next import is correctly added for localization support.


108-108: LGTM! Text localized correctly.

The date format and localization are implemented correctly.


66-84: Add time validation for medicine administration.

The time change handler should include validation to prevent selecting times before encounter start and future times.

Consider adding time validation:

 const handleTimeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
   const [hours, minutes] = event.target.value.split(":").map(Number);
   if (isNaN(hours) || isNaN(minutes)) return;

   const date = currentValue || new Date();
   date.setHours(hours);
   date.setMinutes(minutes);

+  const now = new Date();
+  const encounterStartTime = /* get from context/props */;
+  if (date < encounterStartTime || date > now) {
+    // Show error message or prevent invalid selection
+    return;
+  }

   updateQuestionnaireResponseCB(
     [{
       type: "dateTime",
       value: date.toISOString(),
     }],
     questionnaireResponse.question_id,
     questionnaireResponse.note,
   );
 };
🧰 Tools
🪛 Biome (1.9.4)

[error] 68-68: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)


[error] 68-68: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

Comment on lines +138 to +150
const formatTime = (date: string | undefined) => {
if (!date) return "";
try {
const dateObj = new Date(date);
if (isNaN(dateObj.getTime())) return "";
return `${dateObj.getHours().toString().padStart(2, "0")}:${dateObj
.getMinutes()
.toString()
.padStart(2, "0")}`;
} catch {
return "";
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace unsafe isNaN with Number.isNaN.

The code uses the global isNaN function which attempts type coercion. Use Number.isNaN instead for more predictable behavior.

 const formatTime = (date: string | undefined) => {
   if (!date) return "";
   try {
     const dateObj = new Date(date);
-    if (isNaN(dateObj.getTime())) return "";
+    if (Number.isNaN(dateObj.getTime())) return "";
     return `${dateObj.getHours().toString().padStart(2, "0")}:${dateObj
       .getMinutes()
       .toString()
       .padStart(2, "0")}`;
   } catch {
     return "";
   }
 };

 const handleTimeChange = (
   event: React.ChangeEvent<HTMLInputElement>,
   isStartTime: boolean,
 ) => {
   const [hours, minutes] = event.target.value.split(":").map(Number);
-  if (isNaN(hours) || isNaN(minutes)) return;
+  if (Number.isNaN(hours) || Number.isNaN(minutes)) return;

   const dateStr = isStartTime
     ? administrationRequest.occurrence_period_start
     : administrationRequest.occurrence_period_end;

   if (!dateStr) return;

   const currentDate = new Date(dateStr);
-  if (isNaN(currentDate.getTime())) return;
+  if (Number.isNaN(currentDate.getTime())) return;

Also applies to: 152-182

🧰 Tools
🪛 Biome (1.9.4)

[error] 142-142: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.

(lint/suspicious/noGlobalIsNan)

@@ -46,7 +47,7 @@ export function DatePicker({ date, onChange, disabled }: DatePickerProps) {
<span className="hidden sm:block">{format(date, "PPP")}</span>
</>
) : (
<span>Pick a date</span>
<span>{t("pick_a_date")}</span>
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 Medicine Administration Date Restrictions in DatePicker

The date picker currently uses the prop as-is (disabled={disabled}) with no extra logic to prevent selecting dates before the encounter start or future dates. There is also no custom function like validateMedicineAdminDate in the repository. These validations are necessary as per the PR and retrieved learnings.

  • Location affected: src/components/ui/date-picker.tsx — the disabled prop on <Calendar> should be enhanced to include medicine administration date validations.
  • Action required: Update the disabled prop with validation logic (e.g., compare against encounter start time and current date).
🔗 Analysis chain

Verify time selection restrictions for medicine administration.

While the localization change is good, based on the PR comments, this component needs additional validation to restrict users from:

  1. Selecting administration dates before encounter start time
  2. Selecting future dates

Consider implementing these validations through the disabled prop. Example implementation:

 export function DatePicker({ date, onChange, disabled }: DatePickerProps) {
+  const validateMedicineAdminDate = (date: Date) => {
+    const now = new Date();
+    const encounterStartTime = /* get from context/props */;
+    return date < encounterStartTime || date > now;
+  };
   return (
     // ...
     <Calendar
       mode="single"
       selected={date}
       onSelect={onChange}
-      disabled={disabled}
+      disabled={(date) => disabled?.(date) || validateMedicineAdminDate(date)}
     />
     // ...
   );
 }

I'm going to run a couple of shell scripts to inspect the src/components/ui/date-picker.tsx file and search for any implementation of medicine administration date restrictions (e.g. custom logic added to the disabled prop) to verify whether the recommended changes have been applied.

Below is the first script that extracts the code around the <Calendar component usage (which is where the disabled prop should be augmented):

And here’s another script that searches the repository for any implementation reference (like a function named validateMedicineAdminDate) that might indicate the medicine administration validations have been added:

Once these scripts run, we’ll be able to confirm whether the disabled-date validations for medicine administration—preventing selection of dates before the encounter start time and future dates—are implemented as suggested.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts or web queries found


Script:

#!/bin/bash
echo "----- Extracting Calendar component block from src/components/ui/date-picker.tsx -----"
sed -n '/<Calendar/,/\/>/p' src/components/ui/date-picker.tsx

Length of output: 481


Script:

#!/bin/bash
echo "----- Searching for 'validateMedicineAdminDate' references in the repository -----"
rg "validateMedicineAdminDate" .

Length of output: 213

@rithviknishad rithviknishad merged commit 5d9273d into develop Feb 4, 2025
22 checks passed
@rithviknishad rithviknishad deleted the medicine-administration-ui branch February 4, 2025 18:54
Copy link

github-actions bot commented Feb 4, 2025

@amjithtitus09 Your efforts have helped advance digital healthcare and TeleICU systems. 🚀 Thank you for taking the time out to make CARE better. We hope you continue to innovate and contribute; your impact is immense! 🙌

rithviknishad added a commit that referenced this pull request Feb 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants