-
Notifications
You must be signed in to change notification settings - Fork 626
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
base: develop
Are you sure you want to change the base?
Encounter Locations view #10969
Conversation
…location-view-for-patients
…location-view-for-patients
WalkthroughThis pull request updates multiple areas of the application. It adds nine new key-value pairs to the localization file for improved UI text related to patient management and facility operations. The routing structure is enhanced with the addition of a new Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
✅ Deploy Preview for care-ohc ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Deploying care-fe with
|
Latest commit: |
181a949
|
Status: | ✅ Deploy successful! |
Preview URL: | https://a85776b7.care-fe.pages.dev |
Branch Preview URL: | https://location-view-for-patients.care-fe.pages.dev |
There was a problem hiding this 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
andgetPriorityColor
use string literals for status and priority values. Consider enhancing type safety by:
- Using TypeScript enums or union types for status and priority values
- 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-codedlimit: 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 omituseMemo
. 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
📒 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 goodThe 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 correctThe import for LocationRoutes follows the same pattern as other route imports.
54-54
: Proper integration of LocationRoutesThe 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 goodThe 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
andtab
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 thetab
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 tsLength 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 thefacilityId
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 intoEncountersOverview.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 forbuildQueryParams
(placingfacilityId
as the first parameter) has been verified. The function call insrc/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 safeguardinggetParentChain()
against unexpected cycles or malformed data, which could cause infinite loops.
95-176
: UseCallback dependencies look good, but verify concurrency assumptions.
YouruseLocationState()
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.
CARE
|
Project |
CARE
|
Branch Review |
location-view-for-patients
|
Run status |
|
Run duration | 07m 17s |
Commit |
|
Committer | Amjith Titus |
View all properties for this run ↗︎ |
Test results | |
---|---|
|
0
|
|
0
|
|
0
|
|
0
|
|
12
|
View all changes introduced in this branch ↗︎ |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…location-view-for-patients
There was a problem hiding this 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
📒 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.
There was a problem hiding this 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 inLocationSummary
.
If an API error occurs or data is unexpectedly empty, the component silently returnsnull
. 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. NeglectingisError
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
📒 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 checkinglocation.current_encounter
is clear, but it might be prone to errors if future data structures evolve. Ensure thatlocation.current_encounter
is guaranteed to represent occupancy in all long-term scenarios, or add defensive checks for partial data.
// 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, | ||
}; | ||
} |
There was a problem hiding this comment.
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?
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, | ||
}); | ||
|
There was a problem hiding this comment.
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.
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> | |
); | |
} |
There was a problem hiding this 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 issueHandle 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 issueAdd 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 issueImplement 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
📒 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
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, | ||
}, | ||
}), | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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> | |
); | |
} |
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> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
…location-view-for-patients
There was a problem hiding this 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 suggestionRevisit error handling for child location queries.
ThisuseQuery
call manages the children of a location but omitsisError
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 suggestionAdd error handling for
SelectedLocationChildren
query.
The query for children pages also lacksisError
checks. Without them, users receive no feedback when an error occurs.
570-580
: 🛠️ Refactor suggestionImplement fallback UI for the main locations query.
You handle loading states but notisError
orerror
fromuseQuery
. 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 foruseLocationState
.
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
📒 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)
@amjithtitus09 is it ready for testing, iam seeing a lot of commit after the labelling |
@nihal467 Ready for testing |
Conflicts have been detected against the base branch. Please merge the base branch into your branch.
|
…location-view-for-patients
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
public/locale/en.json (2)
1722-1722
: Key Naming Consistency – "patient encounters"
The new key is currently defined as"patient encounters": "Patient Encounters"
, which uses a space instead of an underscore. For consistency with the majority of the keys (e.g.,"available_beds"
,"occupied_beds"
), consider renaming it to"patient_encounters"
with an underscore.
Suggested diff:-"patient encounters": "Patient Encounters", +"patient_encounters": "Patient Encounters",
2352-2352
: New Key "total_locations" – Check Capitalization Consistency
While the key"total_locations": "Total locations"
is understandable, note that similar keys such as"Encounter Locations"
use title casing. For visual consistency in the UI, consider updating the value to"Total Locations"
.
Suggested diff:-"total_locations": "Total locations", +"total_locations": "Total Locations",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
public/locale/en.json
(9 hunks)src/Routers/AppRouter.tsx
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/Routers/AppRouter.tsx
⏰ Context from checks skipped due to timeout of 90000ms (7)
- 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: Cloudflare Pages: care-fe
🔇 Additional comments (7)
public/locale/en.json (7)
461-461
: New Key "available_beds" – Clear and Consistent
The addition of"available_beds": "Available beds"
is concise and follows the standard key naming convention seen throughout the file. This key should integrate well wherever the UI displays the bed availability.
962-962
: New Key "encounter_locations" – Appropriate for the Feature
The key"encounter_locations": "Encounter Locations"
is clearly defined and directly supports the new Encounter Locations view. Ensure that all components referencing encounter locations use this exact key.
1545-1545
: New Key "no_child_locations" – Self-Explanatory and Useful
The new key"no_child_locations": "No Child Locations"
accurately conveys the absence of subordinate locations. It is consistent with similar keys in its structure and messaging.
1553-1553
: New Key "no_description" – Minimal and Clear
The introduction of"no_description": "No Description"
is straightforward. It provides a fallback for cases when descriptive text is missing, maintaining a clean UI.
1661-1661
: New Key "occupied_beds" – Informative Addition
The key"occupied_beds": "Occupied beds"
fits well with the other bed-related localization strings. Its usage should help in clearly communicating bed occupancy information to users.
1916-1916
: New Key "ready_for_admission" – Concise and Consistent
The key"ready_for_admission": "Ready For Admission"
is well formulated and clearly communicates the state. It aligns with the overall UI language and style.
2112-2112
: New Key "search_locations" – Enhances Discoverability
The addition"search_locations": "Search Locations"
is clear and enhances the user experience by providing a prompt for searching through locations. Its format and wording are consistent with similar search-related keys.
10bedicu (Root) → ICU 1 Room (Child) → Room 1 Bed (Inner Child) When a user is added only to the ICU 1 Room organization, the ICU 1 Room and its inner child, Room 1 Bed, are not visible to the user. Note: When a user is added to the 10bedicu root location, they have proper access and can view all associated child locations, including ICU 1 Room and Room 1 Bed. |
Nav Bar -> Encounters -> Locations Tab
@ohcnetwork/care-fe-code-reviewers
Merge Checklist
Summary by CodeRabbit
New Features
UI Enhancements