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

Encounter Locations view #10969

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

Conversation

amjithtitus09
Copy link
Contributor

@amjithtitus09 amjithtitus09 commented Mar 3, 2025

Nav Bar -> Encounters -> Locations Tab

@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
    • Enhanced localization with additional English language entries for clearer messaging.
    • Redesigned encounter navigation that now directs users to an overview with separate views for patients and locations.
    • New interactive cards display detailed encounter information with visual indicators.
    • Expanded facility management capabilities with dedicated interfaces for managing and navigating locations, including search, filtering, and hierarchical navigation.
    • Introduced a tabbed interface for managing encounters, allowing navigation between patients and locations.
    • New components for location management, including a navigation bar and content display for enhanced user interaction.

@amjithtitus09 amjithtitus09 requested a review from a team as a code owner March 3, 2025 13:35
@amjithtitus09 amjithtitus09 self-assigned this Mar 3, 2025
Copy link
Contributor

coderabbitai bot commented Mar 3, 2025

Walkthrough

This pull request introduces enhancements to localization and routing functionalities. It adds multiple new key-value pairs to the JSON locale file and introduces several new components: an encounter information card, an overview component with tabbed navigation for patient and location views, and improved location management components with state and data fetching logic. Additionally, the patient routing logic has been restructured to support dynamic redirection and route parameters.

Changes

File(s) Change Summary
public/…/en.json Added multiple localization key-value pairs (e.g., "available_beds", "beds", "encounter_locations", etc.) to enhance UI text for patient and facility modules.
src/Routers/routes/PatientRoutes.tsx Updated patient routing by removing the EncounterList import, adding EncountersOverview, redirecting from /facility/:facilityId/encounters to /facility/:facilityId/encounters/patients, and introducing dynamic routes with parameters (tab and locationId).
src/components/Encounter/EncounterInfoCard.tsx
src/pages/Encounters/EncounterList.tsx
Introduced a new EncounterInfoCard component for displaying encounter details and refactored EncounterList to use this component with stricter prop requirements.
src/components/ui/sidebar/facility-nav.tsx Reformatted the "encounters" link object to a multi-line structure for improved readability without altering functionality.
src/pages/Encounters/EncountersOverview.tsx Created a new tabbed EncountersOverview component that toggles between "patients" and "locations" views, integrating dynamic routing via the raviger library.
src/pages/Facility/locations/LocationList.tsx
src/pages/Facility/locations/LocationContent.tsx
src/pages/Facility/locations/LocationNavbar.tsx
Added new location management components and a custom hook (useLocationState) for interactive navigation, state management, data fetching, and display of facility locations.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant R as Router
    participant EO as EncountersOverview
    participant EL as EncounterList
    participant LL as LocationList

    U->>R: Navigate to /facility/{facilityId}/encounters
    R->>R: Redirect to /facility/{facilityId}/encounters/patients
    R->>EO: Render EncountersOverview ("patients" tab)
    EO->>U: Display tabbed interface (patients, locations)
    U->>EO: Select "locations" tab
    EO->>R: Navigate to /facility/{facilityId}/encounters/locations
    R->>EO: Render EncountersOverview ("locations" tab)
    EO->>LL: Render LocationList component
    EO->>U: Display updated location content
Loading

Possibly related PRs

Suggested labels

needs review, tested

Poem

I'm a hopping rabbit, dancing with glee,
New keys and routes set my heart free.
Encounter cards and tabs so neat,
Location trees make the layout complete.
In lines of code, I find rhythm and rhyme—
Celebrating changes one hop at a time!
🥕 Keep on coding, it's carrot time!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
  • @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 Mar 3, 2025

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit 86b4316
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/67cc5a57388886000879c398
😎 Deploy Preview https://deploy-preview-10969.preview.ohc.network
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

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

Copy link

cloudflare-workers-and-pages bot commented Mar 3, 2025

Deploying care-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: 86b4316
Status: ✅  Deploy successful!
Preview URL: https://836f9179.care-fe.pages.dev
Branch Preview URL: https://location-view-for-patients.care-fe.pages.dev

View logs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (8)
src/components/ui/sidebar/facility-nav.tsx (1)

37-41: Consider using a location-specific icon instead of "d-patient"

The new "locations" link is currently using the "d-patient" icon, which is also used for patient-related navigation items. For better visual differentiation and user clarity, it would be better to use a more appropriate and distinct icon for locations (e.g., "d-map-pin" or something location-related if available).

    {
      name: t("locations"),
      url: `${baseUrl}/locations`,
-     icon: "d-patient",
+     icon: "d-map-pin", // or another location-specific icon
    },
src/components/Encounter/EncounterInfoCard.tsx (2)

27-53: Consider enhancing utility functions with TypeScript enums and constants.

The utility functions getStatusColor and getPriorityColor use string literals for status and priority values. Consider enhancing type safety by:

  1. Using TypeScript enums or union types for status and priority values
  2. Creating constants for the color classes to make maintenance easier
+ type EncounterStatus = 'planned' | 'in_progress' | 'completed' | 'cancelled';
+ type EncounterPriority = 'stat' | 'urgent' | 'asap' | 'routine';

- const getStatusColor = (status: string) => {
+ const getStatusColor = (status: EncounterStatus) => {
  switch (status) {
    case "planned":
      return "bg-blue-100 text-blue-800";
    // other cases...
  }
};

- const getPriorityColor = (priority: string) => {
+ const getPriorityColor = (priority: EncounterPriority) => {
  // function body...
}

55-120: Component implements good UI/UX practices with appropriate accessors.

The component is well-structured with:

  • Proper data extraction from props
  • Conditional rendering of patient death status
  • Semantic badges with appropriate colors
  • Accessible navigation links with clear visual affordances
  • Use of i18n for all user-facing strings

One suggestion for improved internationalization:

The "View Details" text on line 113 should use the translation function:

- View Details
+ {t("view_details")}
src/pages/Facility/locations/LocationList.tsx (5)

203-209: Scroll-into-view might cause a jarring experience for large trees.
Automatically scrolling to the newly selected node is handy, but it can be disorienting when many locations expand. If performance or user flow becomes an issue, consider optimizing with a condition-based scroll or virtualization.


214-223: Consider adding pagination or dynamic loading for child locations.
The hard-coded limit: 100 might miss scenarios where more than 100 children exist. If you anticipate large sets of child locations, implement pagination or load-on-demand for better scalability.


292-342: Unify color scheme and consider accessibility for occupancy status.
The bed status styling is clear, but ensure these color contrasts meet accessibility requirements. If future theming or brand guidelines change, extracting these statuses into a shared style or theme provider is beneficial.


526-538: Evaluate necessity of useMemo in LocationSummary.
These calculations are relatively modest. If performance is not a pressing concern, you may omit useMemo. On the flip side, if you handle large facility data, continuing this optimization makes sense.


569-708: Add test coverage for the main LocationList component and subcomponents.
Given the complexity of queries, expansions, and multiple states, unit or integration tests would help maintain reliability.

Would you like a starter test suite covering these features?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between dd8c696 and 30f9c06.

📒 Files selected for processing (9)
  • public/locale/en.json (8 hunks)
  • src/Routers/AppRouter.tsx (2 hunks)
  • src/Routers/routes/LocationRoutes.tsx (1 hunks)
  • src/Routers/routes/PatientRoutes.tsx (2 hunks)
  • src/components/Encounter/EncounterInfoCard.tsx (1 hunks)
  • src/components/ui/sidebar/facility-nav.tsx (1 hunks)
  • src/pages/Encounters/EncounterList.tsx (5 hunks)
  • src/pages/Encounters/EncountersOverview.tsx (1 hunks)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Test
  • GitHub Check: cypress-run (1)
🔇 Additional comments (29)
src/Routers/routes/LocationRoutes.tsx (1)

1-11: Implementation looks good

The LocationRoutes module follows the established pattern for route definitions in the application. The route correctly passes the facilityId parameter to the LocationList component.

src/Routers/AppRouter.tsx (2)

27-27: Integration of LocationRoutes looks correct

The import for LocationRoutes follows the same pattern as other route imports.


54-54: Proper integration of LocationRoutes

The LocationRoutes are correctly merged into the main Routes object, following the established pattern for other route modules.

src/Routers/routes/PatientRoutes.tsx (3)

19-21: Redirection implementation looks good

The redirection from /facility/:facilityId/encounters to /facility/:facilityId/encounters/patients follows best practices for route restructuring. This approach ensures that existing links to the encounters page will still work correctly.


22-24:

✅ Verification successful

Verify that EncountersOverview handles tab parameter correctly

The new route passes both facilityId and tab as props to the EncountersOverview component. Ensure that this component is properly designed to handle different tab values, particularly the default "patients" tab that users will be redirected to.


🏁 Script executed:

#!/bin/bash
# Check how EncountersOverview handles the tab parameter

# Look at the implementation of EncountersOverview
fd "EncountersOverview.tsx" src/ --exec cat {} | grep -A 15 "interface" || fd "EncountersOverview.tsx" src/ --exec cat {}

Length of output: 366


EncountersOverview properly handles the tab parameter
The component correctly sets a default value of "patients" for the tab prop and processes it as expected. No changes are needed in this context.


12-12:

❓ Verification inconclusive

Verify removal of EncounterList import

The import of EncounterList has been replaced with EncountersOverview. Ensure that any code that relied on the direct import of EncounterList has been updated accordingly.


🏁 Script executed:

#!/bin/bash
# Check for other imports of EncounterList that might need updating

# Look for imports of EncounterList across the codebase
rg "import .*EncounterList" src/ --type tsx --type ts

Length of output: 83


Below is an updated review comment. It notes that while the change to import EncountersOverview in place of EncounterList looks correct in PatientRoutes.tsx, further verification should be performed (using an updated script command) to ensure no residual imports of EncounterList remain in the codebase.

Verify that all lingering references to EncounterList are removed

  • The code in src/Routers/routes/PatientRoutes.tsx now imports:
    import EncountersOverview from "@/pages/Encounters/EncountersOverview";
  • Please ensure that no other files still import or refer to EncounterList. The initial search using rg with file type flags resulted in an error. Re-run the following script to verify that no instances exist in .ts or .tsx files:
    # Re-run the search for any remaining imports of EncounterList
    rg "import .*EncounterList" src/ -g "*.tsx" -g "*.ts"
  • If this script produces no output, then the replacement is complete. Otherwise, please update any files still referencing EncounterList.
src/components/Encounter/EncounterInfoCard.tsx (2)

1-20: Well-structured imports and component organization.

The imports are well-organized with a logical grouping: third-party libraries first, followed by local utils, icons, UI components, and types.


21-25: Clean interface definition with appropriate prop types.

The interface is well-defined with clear prop types. The hideBorder optional prop with a boolean type provides flexibility for different styling contexts.

src/pages/Encounters/EncountersOverview.tsx (4)

1-9: Appropriate imports with clear organization.

The imports are well-structured with a logical grouping.


10-13: Well-defined props interface.

The interface is concise and properly defines the expected props for the component.


25-59: Well-structured component with good tab navigation pattern.

The component implements a clean tab navigation pattern with:

  • Proper value binding between tab state and UI
  • Clear navigation implementation
  • Responsive layout with appropriate styling
  • Proper use of i18n for tab labels
  • Consistent content height management

51-57:

❓ Verification inconclusive

Verify LocationList component compatibility.

Ensure the LocationList component is designed to accept and properly use the facilityId prop. Check that it's implemented to handle encounter locations and not just generic facility locations.


🏁 Script executed:

#!/bin/bash
# Check the LocationList component implementation to ensure it handles facilityId correctly
echo "Checking LocationList component implementation..."
rg -A 3 "interface.*LocationList.*Props" --type ts
rg -A 3 "function LocationList" --type ts
rg -A 5 "facilityId.*LocationList" --type ts
echo "Done."

Length of output: 1776


Verify that the correct LocationList component is tailored for encounter-specific needs

It appears that both LocationList implementations accept a facilityId prop. However, please double-check that the version imported into EncountersOverview.tsx is intentionally designed to filter or adjust for encounter locations (vs. showing generic facility locations). If the behavior isn’t encounter-specific, consider updating the logic or selecting a more appropriate component.

src/pages/Encounters/EncounterList.tsx (4)

39-42: Updated prop type signature is more appropriate.

Making facilityId a required prop is a good practice since it's essential for the component's functionality. This change makes the API more explicit about dependencies.


129-130: Updated call to buildQueryParams with correct parameter order.

This line has been properly updated to account for the changed parameter order in the buildQueryParams function.


658-664: Good refactoring to use the new EncounterInfoCard component.

The code has been properly refactored to use the new EncounterInfoCard component, which should improve consistency and maintainability. The props are correctly passed to the new component.


44-66:

✅ Verification successful

Updated parameter order in buildQueryParams function.

The function now takes facilityId as the first parameter, which is a good change since it's the primary identifier required for querying encounters.

Let's verify that all places where this function is called have been updated to match the new parameter order:


🏁 Script executed:

#!/bin/bash
# Check all usage locations of buildQueryParams to ensure they've been updated
echo "Checking buildQueryParams usage..."
rg -A 1 "buildQueryParams" --type ts
echo "Done."

Length of output: 431


Parameter Order Update Verified
The updated parameter order for buildQueryParams (placing facilityId as the first parameter) has been verified. The function call in src/pages/Encounters/EncounterList.tsx now correctly reflects this update:

  • Verified Call: buildQueryParams(facilityId, status, encounterClass, priority)

No further changes are necessary.

public/locale/en.json (9)

458-458: Appropriate translation key added for bed availability.

This translation key will be useful for showing availability stats in the locations view.


957-957: Key translation added for encounter locations feature.

This is a critical translation key for the new "Encounter Locations" tab in the EncountersOverview component.


1538-1539: Translation key for empty child locations state.

This will be shown when a location has no child locations, providing a clear user message.


1546-1546: Translation key for missing location descriptions.

This is a good fallback text when a location doesn't have a description.


1653-1653: Translation key for occupied beds metric.

This complements the "available_beds" key to show both sides of bed occupancy statistics.


1714-1714: Translation key for patient encounters tab.

This will be used for the "Patients" tab in the EncountersOverview component, providing a descriptive label.


1908-1908: Translation key for admission readiness status.

This key will likely be used to indicate patients or beds that are prepared for admission.


2104-2104: Translation key for location search functionality.

This will provide appropriate text for the search interface in the LocationList component.


2343-2343: Translation key for location totals.

This will be used to display the total count of locations, likely in a statistics view.

src/pages/Facility/locations/LocationList.tsx (4)

82-93: Watch out for potential cyclical references in location hierarchy.
While it's unlikely in a well-formed dataset, consider safeguarding getParentChain() against unexpected cycles or malformed data, which could cause infinite loops.


95-176: UseCallback dependencies look good, but verify concurrency assumptions.
Your useLocationState() logic appears solid. All callbacks correctly use functional state updates, which helps avoid stale references. However, ensure no external states or props outside of these callbacks need to be addressed as dependencies for stable concurrency.


427-432: Confirm the parent update logic from the first child only.
Reassigning parent data by reading only the first child (children?.results?.[0]) might be valid for your use case, but double-check scenarios with multiple children or missing parents to avoid unexpected behaviors.


669-671: Revisit disabling search outside selected location.
Disabling the search bar when no location is selected prevents users from quickly finding top-level locations. Confirm whether this limitation matches user needs or if global search is preferred.

Copy link

cypress bot commented Mar 3, 2025

CARE    Run #5143

Run Properties:  status check passed Passed #5143  •  git commit 86b4316b88: Encounter Locations view
Project CARE
Branch Review location-view-for-patients
Run status status check passed Passed #5143
Run duration 07m 17s
Commit git commit 86b4316b88: Encounter Locations view
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 12
View all changes introduced in this branch ↗︎

amjithtitus09 and others added 6 commits March 4, 2025 11:50
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/pages/Encounters/EncountersOverview.tsx (5)

1-8: Imports organized clearly. Consider adding potential mobile optimization imports.

The imports are well structured, separating external libraries from internal components. If this component will be used on mobile devices, you might want to consider adding a hook like useMediaQuery for responsive layout adjustments.


14-19: Component structure looks good. Consider adding error handling.

The component follows React functional component patterns correctly. The default value for the tab prop is appropriately defined.

Consider adding error handling for cases where facilityId might be invalid:

export default function EncountersOverview({
  facilityId,
  tab = "patients",
}: EncountersOverviewProps) {
  const { t } = useTranslation();
+  
+  if (!facilityId) {
+    return <div className="p-4">{t("common:error.invalidFacilityId")}</div>;
+  }

25-27: Navigation logic is correct. Consider adding state loading indication.

The navigation logic to switch between tabs is implemented correctly.

Consider adding a loading state when switching tabs to improve user experience:

onValueChange={(value) => {
+  setIsLoading(true);
  navigate(`/facility/${facilityId}/encounters/${value}`);
}}

You would need to add a useState hook at the top of the component:

const [isLoading, setIsLoading] = useState(false);

29-44: Tab UI implementation looks good. Consider accessibility improvements.

The tabs UI is well structured using the UI library components with proper translation support.

Consider enhancing accessibility by adding ARIA attributes:

<TabsList className="bg-transparent p-0 h-8" aria-label={t("encounters.tabsLabel")}>

20-56: Component rendering logic is well-structured. Consider memoization for performance.

The overall component structure and rendering logic is clean and follows React best practices.

For improved performance, especially if this component might re-render frequently, consider wrapping it with React.memo:

-export default function EncountersOverview({
+function EncountersOverview({
  facilityId,
  tab = "patients",
}: EncountersOverviewProps) {
  // Component code...
}

+export default React.memo(EncountersOverview);

This would require adding an import for React:

+import React from "react";
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 30f9c06 and 20a5c49.

📒 Files selected for processing (2)
  • src/components/ui/sidebar/facility-nav.tsx (1 hunks)
  • src/pages/Encounters/EncountersOverview.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/ui/sidebar/facility-nav.tsx
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: cypress-run (1)
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (3)
src/pages/Encounters/EncountersOverview.tsx (3)

9-12: Well-defined interface with appropriate typing.

The interface is concise and clear with proper TypeScript typing. The optional tab parameter with default value in the function definition follows best practices.


46-48: Patient tab content implementation is correct.

The patient tab content is implemented correctly, passing the required facilityId prop to the EncounterList component.


50-52: Location tab content implementation is correct.

The location tab content is implemented correctly, passing the required facilityId prop to the LocationList component.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/pages/Facility/locations/LocationList.tsx (4)

426-433: Refine parent location update logic.
The side effect updates the parent location data after fetching children but only uses the first child's parent. If multiple children exist, or if the parent reference is missing, you may end up with partial or unintended data in state. Consider explicitly verifying or clarifying which parent to use.


523-567: Display a fallback when no locations are loaded in LocationSummary.
If an API error occurs or data is unexpectedly empty, the component silently returns null. That can confuse users. Consider showing a small message or skeleton to guide users.


582-591: Add error UI for top-level location queries.
The top-level locations (allLocations) also lack explicit error handling. Neglecting isError can leave the user unaware of failed data loading. Adding a fallback or alert message is recommended.


642-707: Improve test coverage for new functionalities.
This file introduces multiple new UI features (e.g., hierarchical display, search, breadcrumbs, location summaries). To maintain code quality, add unit/integration tests to verify each component’s behavior, especially queries, expansions, and search filters.

Would you like help drafting these test cases?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 20a5c49 and 763db34.

📒 Files selected for processing (1)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: cypress-run (1)
🔇 Additional comments (1)
src/pages/Facility/locations/LocationList.tsx (1)

292-343: Validate occupancy logic for beds.
Your approach to identifying occupied beds by checking location.current_encounter is clear, but it might be prone to errors if future data structures evolve. Ensure that location.current_encounter is guaranteed to represent occupancy in all long-term scenarios, or add defensive checks for partial data.

Comment on lines 94 to 176
// Hook for location data management
function useLocationState(): LocationState & {
handleLocationSelect: (location: LocationListType) => void;
handleToggleExpand: (locationId: string) => void;
handleSearchChange: (value: string) => void;
handleLocationData: (location: LocationListType | undefined) => void;
} {
const [state, setState] = useState<LocationState>({
selectedLocationId: null,
selectedLocation: null,
expandedLocations: new Set(),
searchQuery: "",
});

const handleLocationSelect = useCallback((location: LocationListType) => {
if (!location.id) {
setState((prev) => ({
...prev,
selectedLocationId: null,
selectedLocation: null,
searchQuery: "",
}));
return;
}

const parentIds = getParentChain(location);
setState((prev) => ({
...prev,
selectedLocationId: location.id,
selectedLocation: location,
searchQuery: "",
expandedLocations: new Set([...prev.expandedLocations, ...parentIds]),
}));
}, []);

const handleToggleExpand = useCallback((locationId: string) => {
setState((prev) => {
const next = new Set(prev.expandedLocations);
if (next.has(locationId)) {
next.delete(locationId);
} else {
next.add(locationId);
}
return { ...prev, expandedLocations: next };
});
}, []);

const handleSearchChange = useCallback((value: string) => {
setState((prev) => ({ ...prev, searchQuery: value }));
}, []);

const handleLocationData = useCallback(
(location: LocationListType | undefined) => {
if (!location) return;

setState((prev) => {
if (
!prev.selectedLocation ||
location.id === prev.selectedLocationId ||
prev.selectedLocation.id === location.id
) {
return prev;
}
return {
...prev,
selectedLocation: {
...prev.selectedLocation,
parent: location,
},
};
});
},
[],
);

return {
...state,
handleLocationSelect,
handleToggleExpand,
handleSearchChange,
handleLocationData,
};
}
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

Introduce error handling for useQuery operations.
Currently, none of the useQuery calls in the useLocationState hook handle the error state. If the request fails, the user might not receive any feedback or fallback UI. Consider extending the pattern you use for isLoading to display or log errors for a more robust user experience.

Do you need a sample approach to display errors within this hook or in the associated components?

Comment on lines 211 to 223
const { data: children, isLoading } = useQuery({
queryKey: ["locations", facilityId, "children", location.id],
queryFn: query(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
parent: location.id,
limit: 100,
mode: "kind",
},
}),
enabled: isExpanded && hasChildren,
});

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

Handle query failure states for child locations.
You are handling isLoading but ignoring the scenario where isError is true. Without error feedback, data-fetch failures may leave the UI in an ambiguous state.

  const { data: children, isLoading, isError } = useQuery({
    ...
  });

+ if (isError) {
+   return (
+     <div className="p-4 text-red-500">
+       {t("failed_to_load_child_locations")}
+     </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
const { data: children, isLoading } = useQuery({
queryKey: ["locations", facilityId, "children", location.id],
queryFn: query(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
parent: location.id,
limit: 100,
mode: "kind",
},
}),
enabled: isExpanded && hasChildren,
});
const { data: children, isLoading, isError } = useQuery({
queryKey: ["locations", facilityId, "children", location.id],
queryFn: query(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
parent: location.id,
limit: 100,
mode: "kind",
},
}),
enabled: isExpanded && hasChildren,
});
if (isError) {
return (
<div className="p-4 text-red-500">
{t("failed_to_load_child_locations")}
</div>
);
}

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 (3)
src/pages/Facility/locations/LocationList.tsx (3)

94-176: ⚠️ Potential issue

Handle error states in useQuery operations consistently.

The useLocationState hook uses React Query via multiple useQuery calls, but none of them handle potential error states. This could lead to a poor user experience if API requests fail.

I recommend implementing error handling for all React Query operations to provide feedback to users when data fetching fails. Here's how you might enhance the useLocationState hook to include error state handling and provide appropriate UI fallbacks where needed.

// This would need to be applied to each useQuery call in the component
const { data: data, isLoading, isError, error } = useQuery({
  // ... existing query config
});

+ // Handle error state in the component rendering
+ if (isError) {
+   return (
+     <div className="p-4 text-red-500">
+       {t("error_loading_data")}: {error instanceof Error ? error.message : String(error)}
+     </div>
+   );
+ }

211-223: ⚠️ Potential issue

Add error handling for child location queries.

You're checking for isLoading but not handling potential query failures.

Add error state handling to provide feedback when location data can't be loaded:

- const { data: children, isLoading } = useQuery({
+ const { data: children, isLoading, isError } = useQuery({
    queryKey: ["locations", facilityId, "children", location.id],
    queryFn: query(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        parent: location.id,
        limit: 100,
        mode: "kind",
      },
    }),
    enabled: isExpanded && hasChildren,
  });

+ // Add error handling in the component rendering
+ if (isError) {
+   return (
+     <div className="text-xs text-red-500 ml-6">
+       {t("failed_to_load_child_locations")}
+     </div>
+   );
+ }

414-424: ⚠️ Potential issue

Implement error handling for SelectedLocationChildren query.

The component currently only handles the loading state but not potential query failures.

Add error state handling to provide user feedback when location data can't be loaded:

- const { data: children, isLoading } = useQuery({
+ const { data: children, isLoading, isError } = useQuery({
    queryKey: ["locations", facilityId, "children", selectedLocationId, "full"],
    queryFn: query(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        parent: selectedLocationId,
        limit: 100,
      },
    }),
    enabled: !!selectedLocationId,
  });

+ if (isError) {
+   return (
+     <Card className="col-span-full">
+       <CardContent className="p-6 text-center text-red-500">
+         {t("error_loading_child_locations")}
+       </CardContent>
+     </Card>
+   );
+ }
🧹 Nitpick comments (8)
src/pages/Facility/locations/LocationList.tsx (8)

667-675: Add debounce to search input for performance optimization.

The current implementation updates the search query on every keystroke, which could lead to excessive rendering.

Implement debouncing for the search input to improve performance:

+ import { useDebounce } from '@/hooks/useDebounce'; // Create this hook or use a library

// Inside your component:
+ const [searchInputValue, setSearchInputValue] = useState("");
+ const debouncedSearchValue = useDebounce(searchInputValue, 300);

+ // Update search query when debounced value changes
+ useEffect(() => {
+   handleSearchChange(debouncedSearchValue);
+ }, [debouncedSearchValue, handleSearchChange]);

// Then update the input:
<Input
  placeholder={t("search_by_name")}
- value={searchQuery}
- onChange={(e) => handleSearchChange(e.target.value)}
+ value={searchInputValue}
+ onChange={(e) => setSearchInputValue(e.target.value)}
  className="w-full"
  disabled={!selectedLocationId}
/>

292-343: Enhance BedCard for better accessibility and interactive feedback.

The BedCard component could benefit from improved accessibility and user interaction cues.

Consider these improvements for the BedCard component:

function BedCard({ location, facilityId }: BedCardProps) {
  const { t } = useTranslation();
  const isOccupied = !!location.current_encounter;

+ // Add click handler if location has an encounter
+ const handleCardClick = () => {
+   if (location.current_encounter) {
+     // Navigate to encounter details or show modal
+     // Example: navigate(`/facility/${facilityId}/encounter/${location.current_encounter.id}`);
+   }
+ };

  return (
    <div
+     role={isOccupied ? "button" : undefined}
+     tabIndex={isOccupied ? 0 : undefined}
+     onClick={isOccupied ? handleCardClick : undefined}
+     onKeyDown={(e) => {
+       if (isOccupied && (e.key === 'Enter' || e.key === ' ')) {
+         e.preventDefault();
+         handleCardClick();
+       }
+     }}
      className={`border rounded-lg overflow-hidden shadow-sm h-fit ${
        isOccupied ? "bg-white border-gray-200" : "bg-green-50 border-green-200"
+       }${isOccupied ? " hover:shadow-md transition-all cursor-pointer" : ""}`}
+     aria-label={`${location.name} - ${isOccupied ? t("occupied") : t("available")}`}
    >
      {/* Rest of component */}
    </div>
  );
}

605-610: Optimize the topLevelLocations filter for better performance.

The current implementation might unnecessarily re-filter the locations on each render.

Use a more explicit check and add proper memoization:

  const topLevelLocations = useMemo(() => {
    if (!allLocations?.results) return [];
    return allLocations.results.filter(
-     (loc) => !loc.parent || Object.keys(loc.parent).length === 0,
+     (loc) => !loc.parent?.id, // More explicit check for parent ID
    );
-  }, [allLocations?.results]);
+  }, [allLocations?.results?.length]); // More precise dependency

582-603: Consider implementing pagination for large location datasets.

The current implementation loads all locations at once with a fixed limit (QUERY_LIMIT), which might not scale well for facilities with many locations.

For better scalability, consider implementing pagination or virtualized lists for large datasets. You might also want to implement a more advanced search feature that uses backend filtering rather than client-side filtering:

// In the query, add pagination parameters
const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
  queryKey: ["locations", facilityId, "all"],
  queryFn: query.paginated(locationApi.list, {
    pathParams: { facility_id: facilityId },
    queryParams: {
      mine: true,
      page_size: QUERY_LIMIT,
+     // Add search term to backend query if available
+     search: searchQuery.length > 2 ? searchQuery : undefined,
    },
  }),
+ // Re-fetch when search query changes if using backend search
+ enabled: !searchQuery || searchQuery.length > 2,
});

// Then add pagination controls in the UI
+ const [page, setPage] = useState(1);
+ // Add pagination controls in the UI

612-642: Enhance responsiveness for mobile devices.

While there is some mobile responsiveness with hidden md:block for the sidebar, the overall layout could be improved for small screens.

Consider adding a mobile-friendly navigation option:

<div className="flex px-4 space-x-4 h-[calc(100vh-4rem)]">
  {/* Left sidebar - Location tree */}
  {topLevelLocations.length > 0 && (
+   <>
+     {/* Mobile view - Location selector */}
+     <div className="w-full md:hidden mb-4">
+       <div className="bg-white rounded-lg shadow p-4">
+         <label className="block text-sm font-medium mb-2">{t("select_location")}</label>
+         <select 
+           className="w-full border border-gray-300 rounded-md" 
+           value={selectedLocationId || ""}
+           onChange={(e) => {
+             const locId = e.target.value;
+             if (!locId) {
+               handleLocationSelect({ id: "", name: "" } as LocationListType);
+             } else {
+               const selectedLoc = allLocations?.results.find(l => l.id === locId);
+               if (selectedLoc) handleLocationSelect(selectedLoc);
+             }
+           }}
+         >
+           <option value="">{t("all_locations")}</option>
+           {allLocations?.results.map(loc => (
+             <option key={loc.id} value={loc.id}>{loc.name}</option>
+           ))}
+         </select>
+       </div>
+     </div>
    <div className="w-64 shadow-lg bg-white rounded-lg hidden md:block">
      {/* Existing sidebar code */}
    </div>
+   </>
  )}

596-601: Add retry logic for failed queries.

In case of network issues or API failures, it would be helpful to have automatic retry logic for important queries.

Add retry options to critical queries:

const { data: selectedChildren } = useQuery({
  queryKey: ["locations", facilityId, "children", selectedLocationId, "full"],
  queryFn: query(locationApi.list, {
    pathParams: { facility_id: facilityId },
    queryParams: {
      parent: selectedLocationId,
      limit: 100,
    },
  }),
  enabled: !!selectedLocationId,
+ retry: 3, // Retry failed queries 3 times
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), // Exponential backoff
});

1-43: Consider organizing imports for better readability.

The current import section is quite lengthy and could be better organized.

Consider grouping imports by type (React, UI components, utilities, etc.) for improved readability:

// React and third-party libraries
import { useQuery } from "@tanstack/react-query";
import React, { useCallback, useMemo, useState, useEffect } from "react";
import { useTranslation } from "react-i18next";

// Icons
import {
  ArrowRight,
  Bed,
  // ... other icons
} from "lucide-react";

// UI components
import { Badge } from "@/components/ui/badge";
import {
  // ... other UI components
} from "@/components/ui/breadcrumb";

// Application components
import { CardGridSkeleton } from "@/components/Common/SkeletonLoading";
import EncounterInfoCard from "@/components/Encounter/EncounterInfoCard";

// Utilities and types
import query from "@/Utils/request/query";
import { LocationList as LocationListType } from "@/types/location/location";
import locationApi from "@/types/location/locationApi";

44-72: Consider using TypeScript enums or unions for location form types.

The current approach of using keyof typeof LocationIcon works but could be more type-safe.

Consider using a union type for location forms:

- type LocationFormType = keyof typeof LocationIcon;
+ // Define location form types as a union
+ type LocationFormType = 
+   | 'si' | 'bu' | 'wi' | 'wa' | 'lvl' | 'co' 
+   | 'ro' | 'bd' | 've' | 'ho' | 'ca' | 'rd' 
+   | 'area' | 'jdn' | 'vi';

// Then use this type consistently
const LocationIcon: Record<LocationFormType, React.FC<LucideProps>> = {
  si: Building2, // Site
  // ... other mappings
}

This approach provides better type safety and autocompletion.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 763db34 and df5be5e.

📒 Files selected for processing (3)
  • public/locale/en.json (9 hunks)
  • src/components/ui/sidebar/facility-nav.tsx (1 hunks)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/ui/sidebar/facility-nav.tsx
  • public/locale/en.json
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Redirect rules - care-ohc
  • GitHub Check: Header rules - care-ohc
  • GitHub Check: Pages changed - care-ohc
  • GitHub Check: Test
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: OSSAR-Scan
  • GitHub Check: Cloudflare Pages: care-fe

Comment on lines 582 to 591
const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
queryKey: ["locations", facilityId, "all"],
queryFn: query.paginated(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
mine: true,
page_size: QUERY_LIMIT,
},
}),
});
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 error handling for the main locations query.

The query for all locations doesn't handle potential error states.

Implement error handling for a better user experience:

- const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
+ const { 
+   data: allLocations, 
+   isLoading: isLoadingLocations,
+   isError: isErrorLocations,
+   error: locationsError
+ } = useQuery({
    queryKey: ["locations", facilityId, "all"],
    queryFn: query.paginated(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        mine: true,
        page_size: QUERY_LIMIT,
      },
    }),
  });

// Add in the appropriate section of the render function:
+ if (isErrorLocations) {
+   return (
+     <div className="flex items-center justify-center h-[calc(100vh-4rem)]">
+       <Card className="w-full max-w-md">
+         <CardContent className="p-6 text-center">
+           <div className="text-red-500 mb-2">
+             {t("failed_to_load_locations")}
+           </div>
+           <Button onClick={() => window.location.reload()}>
+             {t("retry")}
+           </Button>
+         </CardContent>
+       </Card>
+     </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
const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
queryKey: ["locations", facilityId, "all"],
queryFn: query.paginated(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
mine: true,
page_size: QUERY_LIMIT,
},
}),
});
const {
data: allLocations,
isLoading: isLoadingLocations,
isError: isErrorLocations,
error: locationsError
} = useQuery({
queryKey: ["locations", facilityId, "all"],
queryFn: query.paginated(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
mine: true,
page_size: QUERY_LIMIT,
},
}),
});
if (isErrorLocations) {
return (
<div className="flex items-center justify-center h-[calc(100vh-4rem)]">
<Card className="w-full max-w-md">
<CardContent className="p-6 text-center">
<div className="text-red-500 mb-2">
{t("failed_to_load_locations")}
</div>
<Button onClick={() => window.location.reload()}>
{t("retry")}
</Button>
</CardContent>
</Card>
</div>
);
}

Comment on lines 477 to 521
function Breadcrumbs({
location,
onSelect,
t,
}: {
location: LocationListType;
onSelect: (location: LocationListType) => void;
t: (key: string) => string;
}) {
const items = [];
let current: LocationListType | undefined = location;

while (current) {
items.unshift(current);
current = current.parent;
}

return (
<Breadcrumb>
<BreadcrumbList>
{items.map((item, index) => (
<React.Fragment key={item.id}>
{index > 0 && <BreadcrumbSeparator />}
<BreadcrumbItem>
<BreadcrumbLink
className={`hover:text-primary cursor-pointer ${
index === items.length - 1 ? "font-medium text-primary" : ""
}`}
onClick={() =>
onSelect(
index === 0
? ({ id: "", name: "" } as LocationListType)
: item,
)
}
>
{index === 0 ? t("locations") : item.name}
</BreadcrumbLink>
</BreadcrumbItem>
</React.Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
);
}
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

Fix the type casting in Breadcrumbs when selecting the root level.

The current implementation creates an incomplete LocationListType object when navigating to the root level.

The code is casting an incomplete object to LocationListType, which might cause runtime errors. Consider creating a proper root location object:

- onClick={() =>
-   onSelect(
-     index === 0
-       ? ({ id: "", name: "" } as LocationListType)
-       : item,
-   )
- }
+ onClick={() => {
+   if (index === 0) {
+     // Create a root-level location object with all required properties
+     const rootLocation: LocationListType = {
+       id: "",
+       name: t("locations"),
+       form: "si", // Default form
+       has_children: true,
+       // Add other required properties with default values
+       parent: undefined,
+       availability_status: "available",
+       description: "",
+       facility: facility_id,
+     };
+     onSelect(rootLocation);
+   } else {
+     onSelect(item);
+   }
+ }}

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/pages/Facility/locations/LocationList.tsx (3)

225-264: 🛠️ Refactor suggestion

Revisit error handling for child location queries.
This useQuery call manages the children of a location but omits isError checks. Previously, reviewers suggested displaying an error state to enhance user feedback.

  const { data: children, isLoading } = useQuery({
    ...
+   isError,
+   error
  });
+ if (isError) {
+   return (
+     <div className="p-4 text-red-500">
+       {t("failed_to_load_child_locations")}
+     </div>
+   );
+ }

434-460: 🛠️ Refactor suggestion

Add error handling for SelectedLocationChildren query.
The query for children pages also lacks isError checks. Without them, users receive no feedback when an error occurs.


570-580: 🛠️ Refactor suggestion

Implement fallback UI for the main locations query.
You handle loading states but not isError or error from useQuery. Returning an error message or a retry option would provide clarity if loading fails.

🧹 Nitpick comments (11)
public/locale/en.json (10)

458-458: Ensure consistent casing across localization entries.
"Available beds" is capitalized differently than entries like "Available Beds" or "Ready For Admission." Consider adjusting for a uniform user-facing experience.


959-959: Maintain uniform casing for consistency.
"Encounter Locations" uses Title Case, which is fine, but verify similar entries (e.g., "Search Locations" vs "search locations") to keep them consistent throughout.


1532-1532: Check phrasing alignment.
"No Child Locations" is Title Cased. Compare with related entries (e.g., "No locations" vs "No Child Locations") and choose a standard approach.


1550-1550: Consider matching capitalization style with other "No..." entries.
Currently: "No Description" is Title Cased. If you prefer sentence case (e.g., "No description"), align it accordingly.


1570-1570: Align case style with "No Child Locations."
"No locations" has a lowercase second word, whereas "No Child Locations" is Title Cased. Consider standardizing.


1658-1658: Apply consistent casing to match "Ordered" or "Lowercase" usage.
"Occupied beds" is partially Title Cased. Consider "Occupied Beds" if that aligns better with other headings.


1720-1720: Avoid spaces in the localization key.
Currently, the key is "patient encounters". Most other keys are underscored (e.g., "encounter_locations"). Using "patient_encounters" might ensure consistency.


1913-1913: Confirm adherence to conventional casing.
"Ready For Admission" uses Title Case. If your design system prefers sentence case, consider "Ready for admission."


2119-2119: Casing consistency.
"Search Locations" is Title Cased. Ensure it aligns with related keys (e.g., "search_locations" or "Search locations") for uniform appearance.


2349-2349: Standardize capitalization for user-facing text.
"Total locations" might be changed to "Total Locations," matching the style of "Encounter Locations" and others.

src/pages/Facility/locations/LocationList.tsx (1)

1-689: Consider unit tests for useLocationState.
This hook’s logic (e.g., handleSearchChange, handlePageChange) is critical to location management. Adding tests can improve confidence in future refactorings.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between df5be5e and 71806ca.

📒 Files selected for processing (2)
  • public/locale/en.json (9 hunks)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Redirect rules - care-ohc
  • GitHub Check: Header rules - care-ohc
  • GitHub Check: Pages changed - care-ohc
  • GitHub Check: cypress-run (1)

@nihal467
Copy link
Member

nihal467 commented Mar 4, 2025

@amjithtitus09 is it ready for testing, iam seeing a lot of commit after the labelling

@amjithtitus09
Copy link
Contributor Author

@nihal467 Ready for testing

@github-actions github-actions bot added the merge conflict pull requests with merge conflict label Mar 5, 2025
Copy link

github-actions bot commented Mar 5, 2025

Conflicts have been detected against the base branch. Please merge the base branch into your branch.
cc: @amjithtitus09

See: https://docs.ohc.network/docs/contributing#how-to-resolve-merge-conflicts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/pages/Facility/locations/LocationList.tsx (3)

226-237: 🛠️ Refactor suggestion

Add error handling for child location queries.

The current implementation only handles the loading state but doesn't account for query failures, which may leave users without proper feedback.

- const { data: children, isLoading } = useQuery({
+ const { data: children, isLoading, isError } = useQuery({
    queryKey: ["locations", facilityId, "children", location.id],
    queryFn: query.paginated(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        parent: location.id,
        mode: "kind",
      },
      pageSize: 100,
    }),
    enabled: isExpanded, // Only fetch when the node is expanded
  });

+ // Add after line 238, before the return statement
+ if (isError) {
+   return (
+     <div className="pl-2 py-1 text-sm text-red-500">
+       {t("failed_to_load_child_locations")}
+     </div>
+   );
+ }

615-625: 🛠️ Refactor suggestion

Add error handling for the main locations query.

The query for all locations doesn't handle potential error states.

- const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
+ const { 
+   data: allLocations, 
+   isLoading: isLoadingLocations,
+   isError: isErrorLocations,
+ } = useQuery({
    queryKey: ["locations", facilityId, "all"],
    queryFn: query.paginated(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        mine: true,
        mode: "kind",
      },
      pageSize: 100,
    }),
  });

+ // Add in the return JSX, as a condition before rendering the main content
+ if (isErrorLocations) {
+   return (
+     <div className="flex items-center justify-center h-[calc(100vh-4rem)]">
+       <Card className="w-full max-w-md">
+         <CardContent className="p-6 text-center">
+           <div className="text-red-500 mb-2">
+             {t("failed_to_load_locations")}
+           </div>
+           <Button onClick={() => window.location.reload()}>
+             {t("retry")}
+           </Button>
+         </CardContent>
+       </Card>
+     </div>
+   );
+ }

435-455: 🛠️ Refactor suggestion

Handle error state for location children query.

Similar to the tree node query, this query doesn't handle error states, which could lead to a confusing user experience if the API request fails.

- const { data: children, isLoading } = useQuery({
+ const { data: children, isLoading, isError } = useQuery({
    queryKey: [
      "locations",
      facilityId,
      "children",
      selectedLocationId,
      "full",
      currentPage,
      searchQuery,
    ],
    queryFn: query(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        parent: selectedLocationId,
        limit: ITEMS_PER_PAGE,
        offset: (currentPage - 1) * ITEMS_PER_PAGE,
        name: searchQuery,
      },
    }),
    enabled: !!selectedLocationId,
  });
  
+ // Add after line 464, before the isLoading check
+ if (isError) {
+   return (
+     <Card className="col-span-full">
+       <CardContent className="p-6 text-center text-red-500">
+         {t("failed_to_load_child_locations")}
+       </CardContent>
+     </Card>
+   );
+ }
🧹 Nitpick comments (4)
src/pages/Facility/locations/LocationList.tsx (4)

349-354: Add null check before rendering EncounterInfoCard.

While isOccupied checks for the existence of location.current_encounter, there's no explicit null check before passing it to the EncounterInfoCard component.

- <EncounterInfoCard
-   encounter={location.current_encounter}
-   facilityId={facilityId}
-   hideBorder={true}
- />
+ {location.current_encounter && (
+   <EncounterInfoCard
+     encounter={location.current_encounter}
+     facilityId={facilityId}
+     hideBorder={true}
+   />
+ )}

617-625: Consider implementing pagination or lazy loading for large facility locations.

The current implementation fetches 100 top-level locations at once, which might cause performance issues for large facilities.

Consider implementing a more efficient data loading strategy such as:

  1. Reducing the initial page size and implementing lazy loading
  2. Using pagination for top-level locations similar to how you've done it for child locations
  3. Implementing a virtual list for better performance with large data sets

479-493: Use destructuring for cleaner array reduction.

The current code could be simplified using object destructuring in the reducer callback.

  const { bedLocations, nonBedLocations } = children.results.reduce(
-    (acc, location) => {
+    ({ bedLocations, nonBedLocations }, location) => {
      if (location.form === "bd") {
-        acc.bedLocations.push(location);
+        bedLocations.push(location);
      } else {
-        acc.nonBedLocations.push(location);
+        nonBedLocations.push(location);
      }
-      return acc;
+      return { bedLocations, nonBedLocations };
    },
    {
      bedLocations: [] as LocationListType[],
      nonBedLocations: [] as LocationListType[],
    },
  );

600-728: The component lacks a loading indicator for the initial render.

Users might be confused if the component shows nothing before locations are loaded.

Consider adding a loading indicator for the initial state:

  return (
+   <>
+     {isLoadingLocations && !allLocations && (
+       <div className="flex items-center justify-center h-[calc(100vh-4rem)]">
+         <div className="h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-gray-600" />
+       </div>
+     )}
+     {(!isLoadingLocations || allLocations) && (
      <div className="flex px-4 space-x-4 min-h-[100vh]">
        {/* Rest of the component */}
      </div>
+     )}
+   </>
  );
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 056cabf and 4e69990.

📒 Files selected for processing (2)
  • public/locale/en.json (10 hunks)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • public/locale/en.json
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Redirect rules - care-ohc
  • GitHub Check: Header rules - care-ohc
  • GitHub Check: Pages changed - care-ohc
  • GitHub Check: Test
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: OSSAR-Scan
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (2)
src/pages/Facility/locations/LocationList.tsx (2)

582-588: Fix the type casting in Breadcrumbs when selecting the root level.

The current implementation creates an incomplete LocationListType object when navigating to the root level.

The code is casting an incomplete object to LocationListType, which might cause runtime errors. Consider creating a proper root location object with all required properties.


307-358: Consider sorting bed cards based on availability status.

According to the PR comments, there was feedback to sort beds to ensure availability of the top bed is always displayed.

You might want to implement sorting logic at the parent component level (SelectedLocationChildren) before passing the data to BedCard:

  // In SelectedLocationChildren component, line ~480
  // Sort bedLocations to show available beds first
+ bedLocations.sort((a, b) => {
+   // If a is available and b is occupied, a comes first
+   if (!a.current_encounter && b.current_encounter) return -1;
+   // If a is occupied and b is available, b comes first
+   if (a.current_encounter && !b.current_encounter) return 1;
+   // Otherwise maintain the original order
+   return 0;
+ });

@amjithtitus09
Copy link
Contributor Author

image

  • A user can access the parent location through breadcrumbs from a child location, even if they only have permission to view the child location.

@nihal467 This is not a bug since the user will be able to see only the child locations they have access to even in the parent's view

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/pages/Facility/locations/LocationList.tsx (4)

226-236: 🛠️ Refactor suggestion

Implement error handling for the location tree query.

The query for fetching child locations in LocationTreeNode only handles the loading state but doesn't handle potential errors.

- const { data: children, isLoading } = useQuery({
+ const { data: children, isLoading, isError } = useQuery({
    queryKey: ["locations", facilityId, "children", location.id],
    queryFn: query.paginated(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        parent: location.id,
        mode: "kind",
      },
      pageSize: 100,
    }),
    enabled: isExpanded, // Only fetch when the node is expanded
  });

+ // Add this inside the return after line 247
+ if (isError) {
+   return (
+     <div className="text-red-500 text-sm pl-4">
+       {t("failed_to_load_child_locations")}
+     </div>
+   );
+ }

435-455: 🛠️ Refactor suggestion

Handle query failure states for child locations.

You are handling isLoading but ignoring the scenario where isError is true. Without error feedback, data-fetch failures may leave the UI in an ambiguous state.

- const { data: children, isLoading } = useQuery({
+ const { data: children, isLoading, isError } = useQuery({
    queryKey: [
      "locations",
      facilityId,
      "children",
      selectedLocationId,
      "full",
      currentPage,
      searchQuery,
    ],
    queryFn: query(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        parent: selectedLocationId,
        limit: ITEMS_PER_PAGE,
        offset: (currentPage - 1) * ITEMS_PER_PAGE,
        name: searchQuery,
      },
    }),
    enabled: !!selectedLocationId,
  });

+ // Add this after isLoading check on line 465
+ if (isError) {
+   return (
+     <div className="p-4 text-red-500">
+       {t("failed_to_load_child_locations")}
+     </div>
+   );
+ }

615-625: 🛠️ Refactor suggestion

Add error handling for the main locations query.

The query for all locations doesn't handle potential error states.

- const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
+ const { 
+   data: allLocations, 
+   isLoading: isLoadingLocations,
+   isError: isErrorLocations,
+   error: locationsError
+ } = useQuery({
    queryKey: ["locations", facilityId, "all"],
    queryFn: query.paginated(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        mine: true,
        mode: "kind",
      },
      pageSize: 100,
    }),
  });

// Add in the appropriate section of the render function:
+ if (isErrorLocations) {
+   return (
+     <div className="flex items-center justify-center h-[calc(100vh-4rem)]">
+       <Card className="w-full max-w-md">
+         <CardContent className="p-6 text-center">
+           <div className="text-red-500 mb-2">
+             {t("failed_to_load_locations")}
+           </div>
+           <Button onClick={() => window.location.reload()}>
+             {t("retry")}
+           </Button>
+         </CardContent>
+       </Card>
+     </div>
+   );
+ }

583-588: ⚠️ Potential issue

Fix the type casting in Breadcrumbs when selecting the root level.

The current implementation creates an incomplete LocationListType object when navigating to the root level.

- onClick={() =>
-   onSelect(
-     index === 0
-       ? ({ id: "", name: "" } as LocationListType)
-       : item,
-   )
- }
+ onClick={() => {
+   if (index === 0) {
+     // Create a root-level location object with all required properties
+     const rootLocation: LocationListType = {
+       id: "",
+       name: t("locations"),
+       form: "si", // Default form
+       has_children: true,
+       // Add other required properties with default values
+       parent: undefined,
+       availability_status: "available",
+       description: "",
+       facility: facilityId,
+     };
+     onSelect(rootLocation);
+   } else {
+     onSelect(item);
+   }
+ }}
🧹 Nitpick comments (4)
src/pages/Facility/locations/LocationList.tsx (4)

679-683: Add translation for search placeholder.

The placeholder string "search_by_name" should have a proper translation key.

            <div className="w-full sm:w-72 shrink-0">
              <Input
-               placeholder={t("search_by_name")}
+               placeholder={t("search_locations")}
                value={searchQuery}
                onChange={(e) => handleSearchChange(e.target.value)}
                className="w-full"
                disabled={!selectedLocationId}
              />

342-347: Add more meaningful content for available beds.

Empty beds display minimal content. Consider adding action buttons or more details for better usability.

        {!location.current_encounter ? (
          <div className="flex flex-col items-center justify-center py-4 h-auto">
            <p className="text-sm text-gray-600 mb-3">
              {t("ready_for_admission")}
            </p>
+           <Button size="sm" className="mt-2">
+             {t("admit_patient")}
+           </Button>
          </div>
        ) : (

415-431: Simplify props interface for SelectedLocationChildren.

The component defines its props interface inline, making it harder to reuse. Consider extracting this to a named interface type.

+interface SelectedLocationChildrenProps {
+  facilityId: string;
+  selectedLocationId: string;
+  onSelect: (location: LocationListType) => void;
+  searchQuery: string;
+  onLocationData: (location: LocationListType | undefined) => void;
+  currentPage: number;
+  onPageChange: (page: number) => void;
+}

function SelectedLocationChildren({
  facilityId,
  selectedLocationId,
  onSelect,
  searchQuery,
  onLocationData,
  currentPage,
  onPageChange,
-}: {
-  facilityId: string;
-  selectedLocationId: string;
-  onSelect: (location: LocationListType) => void;
-  searchQuery: string;
-  onLocationData: (location: LocationListType | undefined) => void;
-  currentPage: number;
-  onPageChange: (page: number) => void;
-}) {
+}: SelectedLocationChildrenProps) {

480-493: Consider sorting beds by availability for better user experience.

Beds are currently displayed in the order they're returned from the API. Consider sorting them to show available beds first, as mentioned in the PR comments.

  // Group locations by type (bed vs non-bed)
  const { bedLocations, nonBedLocations } = children.results.reduce(
    (acc, location) => {
      if (location.form === "bd") {
        acc.bedLocations.push(location);
      } else {
        acc.nonBedLocations.push(location);
      }
      return acc;
    },
    {
      bedLocations: [] as LocationListType[],
      nonBedLocations: [] as LocationListType[],
    },
  );

+ // Sort beds by availability (available first)
+ bedLocations.sort((a, b) => {
+   // If a is available and b is not, a comes first
+   if (!a.current_encounter && b.current_encounter) return -1;
+   // If b is available and a is not, b comes first
+   if (a.current_encounter && !b.current_encounter) return 1;
+   // Otherwise maintain original order
+   return 0;
+ });
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4e69990 and 103ece6.

📒 Files selected for processing (1)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Test
  • GitHub Check: cypress-run (1)

Comment on lines 406 to 413
function ChildLocationCard(props: ChildLocationCardProps) {
const isBed = props.location.form === "bd";
return isBed ? (
<BedCard {...props} onClick={undefined} />
) : (
<LocationCard {...props} />
);
}
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

Fix onClick propagation in ChildLocationCard for beds.

The current implementation nullifies onClick for beds, breaking the ability to select them in the location hierarchy.

function ChildLocationCard(props: ChildLocationCardProps) {
  const isBed = props.location.form === "bd";
  return isBed ? (
-   <BedCard {...props} onClick={undefined} />
+   <BedCard {...props} onClick={props.onClick} />
  ) : (
    <LocationCard {...props} />
  );
}
📝 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
function ChildLocationCard(props: ChildLocationCardProps) {
const isBed = props.location.form === "bd";
return isBed ? (
<BedCard {...props} onClick={undefined} />
) : (
<LocationCard {...props} />
);
}
function ChildLocationCard(props: ChildLocationCardProps) {
const isBed = props.location.form === "bd";
return isBed ? (
<BedCard {...props} onClick={props.onClick} />
) : (
<LocationCard {...props} />
);
}

Comment on lines 307 to 358
function BedCard({ location, facilityId }: BedCardProps) {
const { t } = useTranslation();
const isOccupied = !!location.current_encounter;

return (
<div
className={`border rounded-lg overflow-hidden shadow-sm h-full ${
isOccupied ? "bg-white border-gray-200" : "bg-green-50 border-green-200"
}`}
>
<div
className={`px-4 py-3 flex justify-between items-center ${
isOccupied
? "bg-blue-50 border-b border-blue-100"
: "bg-green-100 border-b border-green-200"
}`}
>
<div className="flex items-center">
<Bed
className={`h-4 w-4 mr-2 ${isOccupied ? "text-blue-600" : "text-green-600"}`}
/>
<span className="font-medium">{location.name}</span>
</div>
<div
className={`text-xs px-2 py-1 rounded-full ${
isOccupied
? "bg-blue-100 text-blue-800"
: "bg-green-200 text-green-800"
}`}
>
{isOccupied ? t("occupied") : t("available")}
</div>
</div>

<div>
{!location.current_encounter ? (
<div className="flex flex-col items-center justify-center py-4 h-auto">
<p className="text-sm text-gray-600 mb-3">
{t("ready_for_admission")}
</p>
</div>
) : (
<EncounterInfoCard
encounter={location.current_encounter}
facilityId={facilityId}
hideBorder={true}
/>
)}
</div>
</div>
);
}
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

The BedCard component should handle onClick events for beds.

The BedCard component receives an onClick prop but it doesn't use it. This means beds cannot be selected by clicking, although they're displayed as selectable items in the ChildLocationCard hierarchy.

  return (
    <div
+     onClick={onClick}
      className={`border rounded-lg overflow-hidden shadow-sm h-full ${
        isOccupied ? "bg-white border-gray-200" : "bg-green-50 border-green-200"
-     }`}
+     } cursor-pointer hover:shadow-md transition-shadow`}
    >
      <div
        className={`px-4 py-3 flex justify-between items-center ${
📝 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
function BedCard({ location, facilityId }: BedCardProps) {
const { t } = useTranslation();
const isOccupied = !!location.current_encounter;
return (
<div
className={`border rounded-lg overflow-hidden shadow-sm h-full ${
isOccupied ? "bg-white border-gray-200" : "bg-green-50 border-green-200"
}`}
>
<div
className={`px-4 py-3 flex justify-between items-center ${
isOccupied
? "bg-blue-50 border-b border-blue-100"
: "bg-green-100 border-b border-green-200"
}`}
>
<div className="flex items-center">
<Bed
className={`h-4 w-4 mr-2 ${isOccupied ? "text-blue-600" : "text-green-600"}`}
/>
<span className="font-medium">{location.name}</span>
</div>
<div
className={`text-xs px-2 py-1 rounded-full ${
isOccupied
? "bg-blue-100 text-blue-800"
: "bg-green-200 text-green-800"
}`}
>
{isOccupied ? t("occupied") : t("available")}
</div>
</div>
<div>
{!location.current_encounter ? (
<div className="flex flex-col items-center justify-center py-4 h-auto">
<p className="text-sm text-gray-600 mb-3">
{t("ready_for_admission")}
</p>
</div>
) : (
<EncounterInfoCard
encounter={location.current_encounter}
facilityId={facilityId}
hideBorder={true}
/>
)}
</div>
</div>
);
}
function BedCard({ location, facilityId, onClick }: BedCardProps) {
const { t } = useTranslation();
const isOccupied = !!location.current_encounter;
return (
<div
onClick={onClick}
className={`border rounded-lg overflow-hidden shadow-sm h-full ${
isOccupied ? "bg-white border-gray-200" : "bg-green-50 border-green-200"
} cursor-pointer hover:shadow-md transition-shadow`}
>
<div
className={`px-4 py-3 flex justify-between items-center ${
isOccupied
? "bg-blue-50 border-b border-blue-100"
: "bg-green-100 border-b border-green-200"
}`}
>
<div className="flex items-center">
<Bed
className={`h-4 w-4 mr-2 ${isOccupied ? "text-blue-600" : "text-green-600"}`}
/>
<span className="font-medium">{location.name}</span>
</div>
<div
className={`text-xs px-2 py-1 rounded-full ${
isOccupied
? "bg-blue-100 text-blue-800"
: "bg-green-200 text-green-800"
}`}
>
{isOccupied ? t("occupied") : t("available")}
</div>
</div>
<div>
{!location.current_encounter ? (
<div className="flex flex-col items-center justify-center py-4 h-auto">
<p className="text-sm text-gray-600 mb-3">
{t("ready_for_admission")}
</p>
</div>
) : (
<EncounterInfoCard
encounter={location.current_encounter}
facilityId={facilityId}
hideBorder={true}
/>
)}
</div>
</div>
);
}

@nihal467
Copy link
Member

nihal467 commented Mar 6, 2025

LGTM, other than the below mentioned issue

URL : https://deploy-preview-10969.preview.ohc.network/facility/3c8fcc42-cd22-4f81-96ab-08c0309a4dbb/encounters/locations

image

  • Fix the highlight for the selected room on the left side.

const topLevelLocations = allLocations?.results || [];

return (
<div className="flex px-4 space-x-4 min-h-[100vh]">
Copy link
Member

Choose a reason for hiding this comment

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

min-h-full??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image
@bodhish This happens when min-h-full or no height mentioned. We need it to cover the full height right?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/pages/Facility/locations/LocationNavbar.tsx (2)

85-96: 🛠️ Refactor suggestion

Add error handling for the location children query.

You're handling the loading state but not error scenarios. If the query fails, users won't receive any feedback.

- const { data: children, isLoading } = useQuery({
+ const { data: children, isLoading, isError } = useQuery({
    queryKey: ["locations", facilityId, "children", location.id],
    queryFn: query.paginated(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        parent: location.id,
        mode: "kind",
      },
      pageSize: 100,
    }),
    enabled: isExpanded,
  });

+ // Add after the return statement's opening
+ if (isError) {
+   return (
+     <div className="space-y-1">
+       <div className={cn(
+         "flex items-center py-1 px-2 rounded-md cursor-pointer",
+         isSelected && "bg-blue-100 text-blue-800",
+       )}
+       style={{ paddingLeft: `${level * 1}rem` }}>
+         <div className="flex items-center flex-1 text-sm gap-2 text-red-500">
+           <Icon className="h-4 w-4" />
+           <span className="truncate">{location.name}</span>
+           <span className="text-xs">{t("error_loading")}</span>
+         </div>
+       </div>
+     </div>
+   );
+ }

173-183: 🛠️ Refactor suggestion

Add error handling for the main locations query.

The query for all locations doesn't handle potential error states.

- const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
+ const { 
+   data: allLocations, 
+   isLoading: isLoadingLocations,
+   isError: isErrorLocations 
+ } = useQuery({
    queryKey: ["locations", facilityId, "all"],
    queryFn: query.paginated(locationApi.list, {
      pathParams: { facility_id: facilityId },
      queryParams: {
        mine: true,
        mode: "kind",
      },
      pageSize: 100,
    }),
  });

// Add before return statement:
+ if (isErrorLocations) {
+   return (
+     <div className="w-64 shadow-lg bg-white rounded-lg hidden md:block p-4">
+       <h2 className="text-lg font-semibold text-red-500">{t("error_loading_locations")}</h2>
+     </div>
+   );
+ }
src/pages/Facility/locations/LocationList.tsx (1)

136-142: 🛠️ Refactor suggestion

Add error handling for location detail query.

The location detail query doesn't handle potential error states. If the API request fails, users won't receive any feedback.

- const { data: locationDetail } = useQuery({
+ const { data: locationDetail, isError, error } = useQuery({
    queryKey: ["location", facilityId, locationId],
    queryFn: query(locationApi.get, {
      pathParams: { facility_id: facilityId, id: locationId },
    }),
    enabled: !!locationId,
  });

+ // Add to the component, before the return:
+ if (isError && locationId) {
+   console.error("Failed to load location details:", error);
+   // Optionally show an error message to the user
+ }
🧹 Nitpick comments (6)
src/pages/Encounters/EncountersOverview.tsx (1)

31-31: Fix className whitespace.

There's an extra leading space in the className that should be removed.

- <div className=" w-fit px-4 py-2 rounded-lg">
+ <div className="w-fit px-4 py-2 rounded-lg">
src/pages/Facility/locations/LocationList.tsx (1)

145-155: Potential dependency issue in useEffect.

The effect depends on handleLocationSelect which could change between renders, potentially causing unnecessary updates or infinite loops.

- }, [locationDetail, handleLocationSelect]);
+ }, [locationDetail, locationId]);

Alternatively, use a more stable approach by storing the transformation logic in a ref or by memoizing the handleLocationSelect function with more stable dependencies.

src/pages/Facility/locations/LocationContent.tsx (4)

1-22: Add error handling for query failures
Currently, this code handles loading states (via isLoading) but does not appear to check for or display errors from the useQuery hook. Consider handling the isError or error state for improved user experience and troubleshooting.

 const { data: children, isLoading, isError, error } = useQuery({
   ...
 });

 return (
   <>
-    {isLoading ? (
+    {isLoading ? (
       ...
     ) : !children?.results?.length ? (
       ...
+    ) : isError ? (
+      <Card>
+        <CardContent className="p-6 text-center text-red-500">
+          {error instanceof Error ? error.message : "An unknown error occurred"}
+        </CardContent>
+      </Card>
     ) : (
       ...
     )}
   </>
 );

68-124: Improve color accessibility for occupancy states
Consider verifying color contrast (e.g., text on green background) to ensure that users with visual impairments can easily distinguish available/occupied states.


126-175: Enhance accessibility for clickable cards
Using a clickable <div> may hinder keyboard accessibility and screen reader usage. Consider semantic HTML by using a <button> or a role-based element to make the card accessible.

-function LocationCard({ location, onClick }: LocationCardProps) {
+function LocationCard({ location, onClick }: LocationCardProps) {
   ...

-  return (
-    <div
-      className="border rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow cursor-pointer"
-      onClick={onClick}
-    >
+  return (
+    <button
+      type="button"
+      className="border rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow cursor-pointer text-left"
+      onClick={onClick}
+      aria-label={`Open location: ${location.name}`}
+    >
       ...
-    </div>
+    </button>
   );
 }

242-398: Optional pagination & search refinements
The current logic for pagination and search is functional. As an optional improvement, consider lazy-loading large datasets or using virtualization if the number of location items grows large to enhance performance.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 103ece6 and b92e3e5.

📒 Files selected for processing (5)
  • src/Routers/routes/PatientRoutes.tsx (2 hunks)
  • src/pages/Encounters/EncountersOverview.tsx (1 hunks)
  • src/pages/Facility/locations/LocationContent.tsx (1 hunks)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
  • src/pages/Facility/locations/LocationNavbar.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
🔇 Additional comments (9)
src/pages/Encounters/EncountersOverview.tsx (1)

15-19: LGTM! Good design with optional props and defaults.

The component is structured well with appropriate default values, making it flexible for different usage scenarios.

src/Routers/routes/PatientRoutes.tsx (1)

19-35: LGTM! Clean routing implementation.

The routing changes effectively support the new tabbed navigation pattern, with good use of the Redirect component and proper parameter passing.

src/pages/Facility/locations/LocationNavbar.tsx (1)

192-192:

❓ Verification inconclusive

Consider accessibility on mobile devices.

The component is hidden on mobile (hidden md:block), but there's no alternative navigation method visible for small screens.


🏁 Script executed:

#!/bin/bash
# Check if there's mobile navigation elsewhere in the codebase
echo "Searching for mobile location navigation components..."
rg -A 3 -B 3 "LocationNavbar|location.*navigation" --type tsx | grep -i "mobile\|sm:\|md:\|lg:"

Length of output: 244


Below is an updated shell script to re-run our search using a glob pattern instead of the unrecognized file type. This script looks for any components or code hints that suggest an alternative mobile navigation (for example, components named “MobileNavigation” or keywords like “nav” combined with “mobile”) across TSX files:

#!/bin/bash
echo "Searching TSX files for potential mobile navigation alternative components..."
rg -A 3 -B 3 "MobileNavigation" -g "*.tsx"

echo "Searching TSX files for usage of navigation components with mobile qualifiers..."
rg -A 3 -B 3 "nav.*mobile|mobile.*nav|menu" -g "*.tsx" | grep -i "mobile\|sm:\|md:\|lg:"

Please run this script to gather further details on whether a mobile-friendly alternative exists. Once verified:

  • If an alternative mobile navigation component is found: You can confirm that the hidden LocationNavbar is intentional and acceptable.
  • If no alternative is found: Consider revisiting the design to ensure mobile users have an accessible navigation option.

**

src/pages/Facility/locations/LocationList.tsx (2)

158-158: Reconsider using min-h-[100vh] for layout.

Using min-h-[100vh] might cause unnecessary scrolling. Consider using a more appropriate height constraint.

- <div className="flex px-4 space-x-4 min-h-[100vh]">
+ <div className="flex px-4 space-x-4 h-full">

148-152:

✅ Verification successful

Verify assumption about location children.

When transforming location detail to list format, you're hardcoding has_children: false, which might be incorrect if the location actually has children.


🏁 Script executed:

#!/bin/bash
# Check if LocationDetail type includes information about children
echo "Checking LocationDetail type definition..."
rg "interface LocationDetail" --type ts -A 20
echo "Checking for API responses that might include child information..."
rg "children|has_children" --type ts | grep -i "location.*detail"

Length of output: 1594


Confirmed: Default mapping for location children is appropriate.

Analysis shows that the LocationDetail type (located in src/types/location/location.ts) does not include any children information, whereas the LocationList type defines the has_children field as a boolean. Given that the detail view lacks children-related data, defaulting has_children to false when converting a detail to a list is correct for now. If future API changes provide children information within the detail view, this transformation logic should be revisited.

src/pages/Facility/locations/LocationContent.tsx (4)

41-66: Use of fallback icon is good
Returning Building2 as a fallback icon when form is missing or unrecognized is a safe approach that prevents runtime errors.


177-190: ChildLocationCard logic looks clear
The conditional render for beds vs. non-bed locations is straightforward. It's easy to maintain and extend.


192-229: Breadcrumb generation is well-structured
Building the hierarchy in reverse and rendering using BreadcrumbItem is concise and clear. No concerns found here.


231-241: Interfaces are well-defined
Defining explicit prop interfaces clarifies component expectations, improving readability and maintainability.

@github-actions github-actions bot added the merge conflict pull requests with merge conflict label Mar 6, 2025
Copy link

github-actions bot commented Mar 6, 2025

Conflicts have been detected against the base branch. Please merge the base branch into your branch.
cc: @amjithtitus09

See: https://docs.ohc.network/docs/contributing#how-to-resolve-merge-conflicts

@github-actions github-actions bot removed the merge conflict pull requests with merge conflict label Mar 7, 2025
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: 4

🧹 Nitpick comments (5)
src/pages/Facility/locations/LocationContent.tsx (2)

73-105: Consider extracting occupant-specific styling into constants.

Although the bed occupancy styling is clear, extracting these style-related strings into constants or a small helper would improve readability and reduce the risk of typos. It also makes it easier to manage the color schemes if they need to change.


317-374: Consider extracting location grouping logic into a helper function.

The inline IIFE is clear but somewhat lengthy. Extracting this grouping logic into a small standalone function (e.g., groupBedAndNonBedLocations(results: LocationList[]) => { bedLocations; nonBedLocations }) could improve clarity and reusability.

src/pages/Facility/locations/LocationNavbar.tsx (3)

1-31: Well-organized imports, but consider optimizing icon imports.

The imports are well-structured with external dependencies first, followed by internal imports. However, you're importing 17 individual icons from 'lucide-react', which could impact bundle size.

Consider using a more selective approach:

-import {
-  Bed,
-  Building2,
-  ChevronDown,
-  ChevronRight,
-  Home,
-  Hotel,
-  LandPlot,
-  Layers,
-  LayoutGrid,
-  MapPin,
-  Route,
-  Split,
-  Store,
-  Truck,
-  Users,
-} from "lucide-react";
+// Import only the icons actually used in the component
+import { Bed, Building2, ChevronDown, ChevronRight, Home, Hotel } from "lucide-react";
+import { LandPlot, Layers, LayoutGrid, MapPin, Route } from "lucide-react";
+import { Split, Store, Truck, Users } from "lucide-react";

Or consider using a dynamic import strategy if your bundler supports it.


191-193: Improve responsive design for the sidebar.

The navigation sidebar is currently completely hidden on mobile screens (hidden md:block). Consider implementing a collapsible/expandable mobile version instead of hiding it completely.

- <div className="w-64 shadow-lg bg-white rounded-lg hidden md:block">
+ <div className={cn(
+   "shadow-lg bg-white rounded-lg transition-all duration-300",
+   "w-64 md:w-64", // Control width based on screen size
+   "fixed md:static", // Fixed position on mobile, static on desktop
+   "top-0 bottom-0 left-0 z-50", // Position for mobile
+   isMobileMenuOpen ? "translate-x-0" : "-translate-x-full md:translate-x-0" // Slide in/out on mobile
+ )}>
+   <button 
+     className="absolute -right-10 top-4 bg-white p-2 rounded-r-md shadow-md md:hidden"
+     onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
+     aria-label={isMobileMenuOpen ? t("close_menu") : t("open_menu")}
+   >
+     {isMobileMenuOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
+   </button>
    <div className="p-4">

You would need to add state for isMobileMenuOpen and import additional icons.


196-216: Consider adding pagination for large location lists.

You're fetching up to 100 locations with a single request, but there's no handling for cases where there might be more than 100 locations. The ScrollArea component helps with displaying many items, but true pagination would be more efficient.

You could implement a "Load more" button that appears when there are more locations to load:

      <ScrollArea>
        <div className="p-2">
          {isLoadingLocations ? (
            <div className="p-4">
              <CardGridSkeleton count={3} />
            </div>
          ) : (
+           <>
              {topLevelLocations.map((location) => (
                <LocationTreeNode
                  key={location.id}
                  location={location}
                  selectedLocationId={selectedLocationId}
                  onSelect={onLocationSelect}
                  expandedLocations={expandedLocations}
                  onToggleExpand={onToggleExpand}
                  facilityId={facilityId}
                />
              ))}
+             {allLocations?.next && (
+               <Button
+                 variant="outline"
+                 className="w-full mt-2"
+                 onClick={() => fetchNextPage()}
+                 disabled={isFetchingNextPage}
+               >
+                 {isFetchingNextPage ? t("loading") : t("load_more")}
+               </Button>
+             )}
+           </>
          )}
        </div>
      </ScrollArea>

You would need to update the query to use useInfiniteQuery instead of useQuery.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0df532a and f4e5e14.

📒 Files selected for processing (3)
  • src/pages/Facility/locations/LocationContent.tsx (1 hunks)
  • src/pages/Facility/locations/LocationList.tsx (1 hunks)
  • src/pages/Facility/locations/LocationNavbar.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/Facility/locations/LocationList.tsx
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: cypress-run (1)
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (1)
src/pages/Facility/locations/LocationContent.tsx (1)

62-66: Great fallback approach for unknown location forms.

Returning a default icon if an unrecognized form is passed ensures that the UI continues to function gracefully. This approach helps avoid errors when the codebase grows and more forms are introduced.

Comment on lines +255 to +277
const { data: children, isLoading } = useQuery({
queryKey: [
"locations",
facilityId,
"children",
selectedLocationId,
"kind",
"full",
currentPage,
searchQuery,
],
queryFn: query(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
parent: selectedLocationId || undefined,
limit: ITEMS_PER_PAGE,
offset: (currentPage - 1) * ITEMS_PER_PAGE,
name: searchQuery || undefined,
...(selectedLocationId ? {} : { mine: true, mode: "kind" }),
},
}),
enabled: true,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for network or server failures.

Currently, the code checks only isLoading. If the query fails (isError), users might not see an explicit error message. Consider adding a fallback UI or toast notification to handle failures gracefully.

+ const { data: children, isLoading, isError } = useQuery({
    ...
+   onError: (error) => {
+     // Provide a user-friendly error message
+     console.error("Failed to fetch locations:", error);
+   },
  });

+ if (isError) {
+   return (
+     <div className="p-6 text-center text-red-500">
+       {t("failed_to_load_locations")}
+     </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
const { data: children, isLoading } = useQuery({
queryKey: [
"locations",
facilityId,
"children",
selectedLocationId,
"kind",
"full",
currentPage,
searchQuery,
],
queryFn: query(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
parent: selectedLocationId || undefined,
limit: ITEMS_PER_PAGE,
offset: (currentPage - 1) * ITEMS_PER_PAGE,
name: searchQuery || undefined,
...(selectedLocationId ? {} : { mine: true, mode: "kind" }),
},
}),
enabled: true,
});
const { data: children, isLoading, isError } = useQuery({
queryKey: [
"locations",
facilityId,
"children",
selectedLocationId,
"kind",
"full",
currentPage,
searchQuery,
],
queryFn: query(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
parent: selectedLocationId || undefined,
limit: ITEMS_PER_PAGE,
offset: (currentPage - 1) * ITEMS_PER_PAGE,
name: searchQuery || undefined,
...(selectedLocationId ? {} : { mine: true, mode: "kind" }),
},
}),
enabled: true,
onError: (error) => {
// Provide a user-friendly error message
console.error("Failed to fetch locations:", error);
},
});
if (isError) {
return (
<div className="p-6 text-center text-red-500">
{t("failed_to_load_locations")}
</div>
);
}

Comment on lines +85 to +96
const { data: children, isLoading } = useQuery({
queryKey: ["locations", facilityId, "children", location.id, "kind"],
queryFn: query.paginated(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
parent: location.id,
mode: "kind",
},
pageSize: 100,
}),
enabled: isExpanded,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling to the location query.

The query for fetching child locations lacks error handling. If the API request fails, there's no feedback to the user.

Enhance the query implementation to handle errors:

-  const { data: children, isLoading } = useQuery({
+  const { data: children, isLoading, error } = useQuery({
     queryKey: ["locations", facilityId, "children", location.id, "kind"],
     queryFn: query.paginated(locationApi.list, {
       pathParams: { facility_id: facilityId },
       queryParams: {
         parent: location.id,
         mode: "kind",
       },
       pageSize: 100,
     }),
     enabled: isExpanded,
   });

Then update the UI to display an error message if the request fails:

{isExpanded && error && (
  <div className="pl-2 text-red-500 text-sm">
    {t("failed_to_load_locations")}
  </div>
)}

Comment on lines +173 to +183
const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
queryKey: ["locations", facilityId, "mine", "kind"],
queryFn: query.paginated(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
mine: true,
mode: "kind",
},
pageSize: 100,
}),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling to the main locations query.

Similar to the child locations query, the main query for all locations lacks error handling.

Enhance the query implementation to handle errors:

-  const { data: allLocations, isLoading: isLoadingLocations } = useQuery({
+  const { data: allLocations, isLoading: isLoadingLocations, error: locationsError } = useQuery({
     queryKey: ["locations", facilityId, "mine", "kind"],
     queryFn: query.paginated(locationApi.list, {
       pathParams: { facility_id: facilityId },
       queryParams: {
         mine: true,
         mode: "kind",
       },
       pageSize: 100,
     }),
   });

Then update the UI to display an error message if the request fails:

   <ScrollArea>
     <div className="p-2">
       {isLoadingLocations ? (
         <div className="p-4">
           <CardGridSkeleton count={3} />
         </div>
+      ) : locationsError ? (
+        <div className="p-4 text-red-500">
+          {t("failed_to_load_locations")}
+        </div>
       ) : (
         topLevelLocations.map((location) => (
           <LocationTreeNode
📝 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 { data: allLocations, isLoading: isLoadingLocations } = useQuery({
queryKey: ["locations", facilityId, "mine", "kind"],
queryFn: query.paginated(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
mine: true,
mode: "kind",
},
pageSize: 100,
}),
});
const { data: allLocations, isLoading: isLoadingLocations, error: locationsError } = useQuery({
queryKey: ["locations", facilityId, "mine", "kind"],
queryFn: query.paginated(locationApi.list, {
pathParams: { facility_id: facilityId },
queryParams: {
mine: true,
mode: "kind",
},
pageSize: 100,
}),
});
<ScrollArea>
<div className="p-2">
{isLoadingLocations ? (
<div className="p-4">
<CardGridSkeleton count={3} />
</div>
) : locationsError ? (
<div className="p-4 text-red-500">
{t("failed_to_load_locations")}
</div>
) : (
topLevelLocations.map((location) => (
<LocationTreeNode key={location.id} location={location} />
))
)}
</div>
</ScrollArea>

Comment on lines +99 to +135
<div className="space-y-1">
<div
className={cn(
"flex items-center py-1 px-2 rounded-md cursor-pointer hover:bg-gray-100",
isSelected && "bg-blue-100 text-blue-800",
)}
style={{ paddingLeft: `${level * 1}rem` }}
>
{hasChildren ? (
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={(e) => {
e.stopPropagation();
onToggleExpand(location.id);
}}
>
{isLoading ? (
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
) : isExpanded ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
</Button>
) : (
<span className="w-6" />
)}
<div
className="flex items-center flex-1 text-sm gap-2"
onClick={() => onSelect(location)}
>
<Icon className="h-4 w-4" />
<span className="truncate">{location.name}</span>
</div>
</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

Improve accessibility and replace inline styles.

The current implementation has a few accessibility and styling issues:

  1. The clickable elements lack proper ARIA attributes
  2. Using inline styles for indentation is less maintainable
  3. The click handler is attached to a div instead of a button

Replace inline styles with CSS classes and improve accessibility:

 <div className="space-y-1">
   <div
     className={cn(
-      "flex items-center py-1 px-2 rounded-md cursor-pointer hover:bg-gray-100",
+      "flex items-center py-1 px-2 rounded-md cursor-pointer hover:bg-gray-100 location-node",
       isSelected && "bg-blue-100 text-blue-800",
+      `pl-${level * 4}`
     )}
-    style={{ paddingLeft: `${level * 1}rem` }}
+    role="treeitem"
+    aria-expanded={hasChildren ? isExpanded : undefined}
+    aria-selected={isSelected}
   >
     {hasChildren ? (
       <Button
         variant="ghost"
         size="icon"
         className="h-6 w-6"
+        aria-label={isExpanded ? t("collapse") : t("expand")}
         onClick={(e) => {
           e.stopPropagation();
           onToggleExpand(location.id);
         }}
       >
         {isLoading ? (
           <div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
         ) : isExpanded ? (
           <ChevronDown className="h-4 w-4" />
         ) : (
           <ChevronRight className="h-4 w-4" />
         )}
       </Button>
     ) : (
       <span className="w-6" />
     )}
-    <div
-      className="flex items-center flex-1 text-sm gap-2"
+    <button
+      type="button"
+      className="flex items-center flex-1 text-sm gap-2 bg-transparent border-none"
       onClick={() => onSelect(location)}
+      aria-label={t("select_location", { name: location.name })}
     >
       <Icon className="h-4 w-4" />
       <span className="truncate">{location.name}</span>
-    </div>
+    </button>
   </div>

Then add the corresponding CSS classes to your stylesheet or use Tailwind's arbitrary values.

📝 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
<div className="space-y-1">
<div
className={cn(
"flex items-center py-1 px-2 rounded-md cursor-pointer hover:bg-gray-100",
isSelected && "bg-blue-100 text-blue-800",
)}
style={{ paddingLeft: `${level * 1}rem` }}
>
{hasChildren ? (
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={(e) => {
e.stopPropagation();
onToggleExpand(location.id);
}}
>
{isLoading ? (
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
) : isExpanded ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
</Button>
) : (
<span className="w-6" />
)}
<div
className="flex items-center flex-1 text-sm gap-2"
onClick={() => onSelect(location)}
>
<Icon className="h-4 w-4" />
<span className="truncate">{location.name}</span>
</div>
</div>
<div className="space-y-1">
<div
className={cn(
"flex items-center py-1 px-2 rounded-md cursor-pointer hover:bg-gray-100 location-node",
isSelected && "bg-blue-100 text-blue-800",
`pl-${level * 4}`
)}
role="treeitem"
aria-expanded={hasChildren ? isExpanded : undefined}
aria-selected={isSelected}
>
{hasChildren ? (
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
aria-label={isExpanded ? t("collapse") : t("expand")}
onClick={(e) => {
e.stopPropagation();
onToggleExpand(location.id);
}}
>
{isLoading ? (
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-gray-600" />
) : isExpanded ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
</Button>
) : (
<span className="w-6" />
)}
<button
type="button"
className="flex items-center flex-1 text-sm gap-2 bg-transparent border-none"
onClick={() => onSelect(location)}
aria-label={t("select_location", { name: location.name })}
>
<Icon className="h-4 w-4" />
<span className="truncate">{location.name}</span>
</button>
</div>

@github-actions github-actions bot added the merge conflict pull requests with merge conflict label Mar 9, 2025
Copy link

github-actions bot commented Mar 9, 2025

Conflicts have been detected against the base branch. Please merge the base branch into your branch.
cc: @amjithtitus09

See: https://docs.ohc.network/docs/contributing#how-to-resolve-merge-conflicts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge conflict pull requests with merge conflict needs review needs testing
Projects
Status: Triage
Development

Successfully merging this pull request may close these issues.

3 participants