-
Notifications
You must be signed in to change notification settings - Fork 553
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
Migration to useQuery: Removed PaginatedList.tsx
and Update Dependent Components
#10350
Migration to useQuery: Removed PaginatedList.tsx
and Update Dependent Components
#10350
Conversation
WalkthroughThis pull request involves the complete removal of the Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 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. |
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: 1
🧹 Nitpick comments (1)
src/CAREUI/misc/PaginatedList.tsx (1)
74-84
:useQuery
usage is correctly keyed and configured.
Consider ensuringqueryParams
is a stable reference to avoid unintentional re-fetches if the object shape changes.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/CAREUI/misc/PaginatedList.tsx
(8 hunks)src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
(1 hunks)src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: cypress-run (1)
🔇 Additional comments (14)
src/CAREUI/misc/PaginatedList.tsx (12)
1-1
: Well-adopted use ofreact-query
.
No issues found with this import statement.
10-10
: ImportingapiQuery
is consistent with the new data-fetching logic.
No concerns.
11-15
: Imported types reflect actual usage.
All imports (ApiRoute
,PaginatedResponse
,QueryParams
) appear necessary and properly utilized.
20-25
: CombiningUseQueryResult
with custom fields is appropriate.
Merging the query result with extra pagination metadata is a neat way to centralize context.
39-52
:Props<TItem>
interface is well-structured.
All prop definitions align with the newuseQuery
approach and provide clear optional fields.
60-65
: Destructuring is clear and matches the refactored API.
No issues found; parameter naming and defaults look good.
67-67
: InitializingcurrentPage
properly.
UsinginitialPage ?? 1
is a straightforward defaulting approach.
71-71
: Optional chaining foronPageChange
is safe and concise.
No further concerns.
112-112
:WhenEmpty
logic is direct and minimalistic.
The condition for skipping render when loading or non-empty is correct for an "empty" placeholder.Also applies to: 114-114
124-124
:WhenLoading
gracefully handles the loading state.
Using!isLoading
to skip rendering is concise.Also applies to: 126-126
140-140
:Refresh
button usage is coherent.
Disabling while loading and the spinner class usage are consistent with best practices.Also applies to: 147-147, 151-151
203-203
:Paginator
skip-while-loading is reasonable.
This prevents extraneous UI interactions until data is ready.Also applies to: 205-205
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (1)
24-24
: Renamingquery
toqueryParams
aligns with the updatedPaginatedList
props.
Good consistency in naming across the codebase.src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (1)
285-285
: UsingqueryParams
forPaginatedList
maintains consistency.
No issues found.
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 (1)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (1)
303-331
: Consider extracting loading and empty states to reusable components.The loading and empty states implementation is good, but these patterns might be reused across the application.
Consider creating reusable components:
// src/components/Common/States/LoadingState.tsx export const LoadingState = ({ count = 3 }) => ( <div className="grid gap-5"> <CardListSkeleton count={count} /> </div> ); // src/components/Common/States/EmptyState.tsx export const EmptyState = ({ message }: { message: string }) => ( <Card className="p-6"> <div className="text-lg font-medium text-gray-500">{message}</div> </Card> );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: cypress-run (1)
- GitHub Check: OSSAR-Scan
🔇 Additional comments (4)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (4)
1-1
: LGTM! Import changes align with migration objectives.The new imports for
useQuery
anduseFilters
are appropriate for the component's migration fromPaginatedList
to a directuseQuery
implementation.Also applies to: 11-14
Line range hint
44-279
: LGTM! Helper functions and sub-components are well-structured.The utility functions and sub-components maintain clear separation of concerns and handle complex data transformations effectively.
286-300
: LGTM! Query implementation follows best practices.The useQuery implementation:
- Includes all necessary dependencies in queryKey
- Uses debounced query for performance
- Correctly calculates pagination offset
281-284
: Verify the cache blacklist configuration.The
questionnaire
parameter is blacklisted from cache. Please verify if this is intentional and document the reasoning.✅ Verification successful
Cache Blacklist Configuration Verified
- The only instance of blacklisting
"questionnaire"
is found insrc/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
, suggesting it was intentionally added for this component.- This intentional exclusion likely prevents caching of dynamic query parameters to ensure the freshest data is fetched.
- No similar cache blacklist patterns for
"questionnaire"
exist elsewhere in the codebase, which further supports the specificity of this configuration.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for other instances of cache blacklisting to understand the pattern rg -A 2 "cacheBlacklist.*questionnaire" --type tsLength of output: 322
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 (1)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (1)
44-92
: Consider simplifying the render logic.While the implementation is functional, the render logic could be improved:
- Multiple nested
div
elements could be flattened- Conditional rendering could be simplified
- Empty state could be extracted into a reusable component
Here's a suggested refactor:
- <div className="mt-8"> - <div> - {encountersLoading ? ( - <div> - <div className="grid gap-5"> - <CardListSkeleton count={3} /> - </div> - </div> + <div className="mt-8"> + {encountersLoading ? ( + <div className="grid gap-5"> + <CardListSkeleton count={3} /> + </div> + ) : !encountersFetching && encounterData?.results?.length === 0 ? ( + <EmptyEncounterState + facilityId={facilityId} + patientId={patientId} + /> + ) : ( + <ul className="grid gap-4"> + {encounterData?.results?.map((encounter) => ( + <li key={encounter.id} className="w-full"> + <EncounterCard encounter={encounter} /> + </li> + ))} + <div className="flex w-full items-center justify-center"> + <Pagination totalCount={encounterData?.count || 0} /> + </div> + </ul> + )} + </div>And create a new component for the empty state:
const EmptyEncounterState = ({ facilityId, patientId }: { facilityId: string; patientId: string }) => { const { t } = useTranslation(); return ( <div className="p-2"> <div className="h-full space-y-2 rounded-lg bg-white px-7 py-12 border border-secondary-300"> <div className="flex w-full items-center justify-center text-lg text-secondary-600"> {t("no_consultation_history")} </div> <div className="flex w-full items-center justify-center pt-4"> <Button variant="outline_primary" asChild> <Link href={`/facility/${facilityId}/patient/${patientId}/consultation`}> <span className="flex w-full items-center justify-start gap-2"> <CareIcon icon="l-chat-bubble-user" className="text-xl" /> {t("add_consultation")} </span> </Link> </Button> </div> </div> </div> ); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: cypress-run (1)
🔇 Additional comments (3)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (3)
1-16
: LGTM! Well-organized imports.The imports are logically grouped and include all necessary dependencies for the new implementation using
useQuery
.
23-26
: Resolve inconsistent limit values.There's a mismatch between the limit values:
useFilters
is configured withlimit: 15
- The actual query uses
limit: 5
(line 37)This inconsistency could lead to confusion. Consider aligning these values or documenting why they differ.
28-41
: LGTM! Well-implemented query configuration.The implementation follows React Query best practices:
- Proper query key structure for cache management
- Debounced API calls to prevent request spam
- Comprehensive loading states extraction
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/components/Patient/PatientDetailsTab/EncounterHistory.tsx (5)
28-41
: Consider these improvements to the query configuration.
- The limit value (5) is duplicated in both
useFilters
and query parameters. Consider extracting it to a constant.- The offset calculation could be moved to a constant for better readability.
- Consider adding error handling for the debounced query.
Here's a suggested refactor:
+const ITEMS_PER_PAGE = 5; + const { qParams, Pagination, resultsPerPage } = useFilters({ - limit: 5, + limit: ITEMS_PER_PAGE, cacheBlacklist: ["encounterHistory"], }); +const currentOffset = ((qParams.page ?? 1) - 1) * resultsPerPage; + const { data: encounterData, isFetching: encountersFetching, isLoading: encountersLoading, + error, } = useQuery({ queryKey: ["encountersHistory", patientId, qParams], queryFn: query.debounced(routes.encounter.list, { queryParams: { patient: patientId, - limit: 5, - offset: ((qParams.page ?? 1) - 1) * resultsPerPage, + limit: ITEMS_PER_PAGE, + offset: currentOffset, }, }), + retry: 2, });
44-52
: Simplify the nested div structure in the loading state.The loading state contains unnecessary nested divs that can be simplified.
-<div> - <div className="grid gap-5"> +<div className="grid gap-5"> <CardListSkeleton count={3} /> - </div> </div>
80-82
: Remove duplicate key prop in EncounterCard.The
key
prop is unnecessarily duplicated on both theli
andEncounterCard
elements.<li key={encounter.id} className="w-full"> - <EncounterCard key={encounter.id} encounter={encounter} /> + <EncounterCard encounter={encounter} /> </li>
54-77
: Consider extracting the empty state to a separate component.The empty state UI logic is quite complex and could be extracted into a reusable component, especially if similar empty states are used elsewhere in the application.
Example:
const EmptyEncounterState = ({ facilityId, patientId }: { facilityId: string; patientId: string }) => { const { t } = useTranslation(); return ( <div className="p-2"> <div className="h-full space-y-2 rounded-lg bg-white px-7 py-12 border border-secondary-300"> {/* ... rest of the empty state JSX ... */} </div> </div> ); };
1-92
: Well-implemented migration to useQuery!The migration from
PaginatedList
touseQuery
has been executed effectively, with proper handling of different states and clean integration with theuseFilters
hook. The component maintains good separation of concerns and follows React best practices.Consider these architectural improvements for the future:
- Extract reusable UI components for empty and loading states
- Implement error boundaries or error states for failed queries
- Consider implementing infinite scroll instead of pagination for a better user experience
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: cypress-run (1)
🔇 Additional comments (1)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (1)
1-26
: LGTM! Clean imports and hook configuration.The imports are well-organized and the
useFilters
hook is properly configured with appropriate pagination limits and cache settings.
…keleton loaders for Comments
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: 1
🧹 Nitpick comments (4)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (3)
23-26
: Extract magic number and fix cache key inconsistency.Two issues to address:
- The limit value of 5 is hardcoded and repeated later in the query params. Consider extracting it to a constant.
- The cacheBlacklist uses "encounterHistory" while the query key uses "encountersHistory" (plural). This inconsistency might affect caching behavior.
+const ENCOUNTERS_PER_PAGE = 5; + const { qParams, Pagination, resultsPerPage } = useFilters({ - limit: 5, - cacheBlacklist: ["encounterHistory"], + limit: ENCOUNTERS_PER_PAGE, + cacheBlacklist: ["encountersHistory"], });
28-42
: Use the extracted constant for consistency.The query configuration looks good, with proper key structure and debounced API calls. However, the limit value is hardcoded again.
queryFn: query.debounced(routes.encounter.list, { queryParams: { patient: patientId, - limit: 5, + limit: ENCOUNTERS_PER_PAGE, offset: ((qParams.page ?? 1) - 1) * resultsPerPage, }, }),
79-83
: Remove duplicate key prop.The key prop is unnecessarily duplicated on both the
li
andEncounterCard
elements.{encounterData?.results?.map((encounter) => ( <li key={encounter.id} className="w-full"> - <EncounterCard key={encounter.id} encounter={encounter} /> + <EncounterCard encounter={encounter} /> </li> ))}src/components/Resource/ResourceCommentSection.tsx (1)
24-43
: LGTM! Query configuration follows best practices.The implementation correctly:
- Uses array-based query key for proper cache invalidation
- Implements pagination with offset calculation
- Uses debounced query function
Consider extracting the magic number
15
into a constant:+const COMMENTS_PER_PAGE = 15; + const { qParams, Pagination, resultsPerPage } = useFilters({ - limit: 15, + limit: COMMENTS_PER_PAGE, cacheBlacklist: ["resourceComments"], }); const { data: resourceComments, isFetching: commentsFetching, isLoading: commentsLoading, refetch: refetchComments, } = useQuery({ queryKey: [routes.getResourceComments.path, id, qParams], queryFn: query.debounced(routes.getResourceComments, { queryParams: { - limit: 15, + limit: COMMENTS_PER_PAGE, offset: ((qParams.page ?? 1) - 1) * resultsPerPage, }, pathParams: { id }, }), });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/components/Facility/models.tsx
(1 hunks)src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
(1 hunks)src/components/Resource/ResourceCommentSection.tsx
(2 hunks)
🧰 Additional context used
📓 Learnings (1)
src/components/Resource/ResourceCommentSection.tsx (1)
Learnt from: AdityaJ2305
PR: ohcnetwork/care_fe#10345
File: src/components/Resource/ResourceDetailsUpdate.tsx:132-145
Timestamp: 2025-01-31T22:13:06.153Z
Learning: The migration from useTanStackQueryInstead to useQuery in React components requires:
1. Updating types to use UseQueryResult instead of custom hook return type
2. Replacing loading with isLoading property
3. Using queryKey array for cache management
4. Using queryFn with query utility function
🔇 Additional comments (6)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (2)
1-17
: LGTM! Well-organized imports.The imports are logically grouped and all dependencies are necessary for the component's functionality.
44-92
: LGTM! Well-structured render logic.The component handles all states effectively:
- Loading state with skeleton UI
- Empty state with clear user action
- List rendering with semantic HTML
- Proper pagination integration
src/components/Facility/models.tsx (1)
44-44
: Verify the impact of CommentModel changes.The type change from
number
toUserBareMinimum
forcreated_by
is a breaking change that might affect other components usingCommentModel
.Run the following script to find all usages of
CommentModel
:src/components/Resource/ResourceCommentSection.tsx (3)
1-1
: LGTM! Imports and hook usage align with migration requirements.The changes correctly implement the migration requirements from the learnings:
- Using
useQuery
from@tanstack/react-query
- Adding loading state components
- Using the
useFilters
hook for paginationAlso applies to: 10-12
86-111
: LGTM! UI states are handled appropriately.The implementation correctly handles:
- Loading state with skeleton UI
- Empty state with user-friendly message
- List rendering with pagination
120-144
: LGTM! Comment component implementation is clean and complete.The component correctly:
- Uses the updated
CommentModel
withUserBareMinimum
type- Formats comment text and timestamps
- Displays user information with avatar
Ready for review! |
PaginatedList
to useQueryPaginatedList.tsx
and Update Dependent Components
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
Outdated
Show resolved
Hide resolved
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
Outdated
Show resolved
Hide resolved
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 (2)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2)
291-296
: 🛠️ Refactor suggestionRestructure query key for better cache management.
The current flat array structure for query keys could be improved.
- queryKey: [ - routes.getQuestionnaireResponses.path, - patientId, - qParams, - encounter?.id, - ], + queryKey: [ + 'questionnaireResponses', + { + path: routes.getQuestionnaireResponses.path, + patientId, + encounterId: encounter?.id, + ...qParams + } + ],
297-297
: 🛠️ Refactor suggestionRemove unnecessary debouncing.
The debounced query function is unnecessary here as the data is fetched based on pagination changes, not user input.
- queryFn: query.debounced(routes.getQuestionnaireResponses, { + queryFn: () => query(routes.getQuestionnaireResponses, {
🧹 Nitpick comments (5)
src/components/Patient/PatientDetailsTab/patientUpdates.tsx (2)
30-43
: Use ofuseQuery
for patient updates
Consider adding explicit error handling to gracefully handle query failures. This can improve user experience if the request fails.Example diff to show an error message:
+ const { + data: patientUpdatesData, + isFetching: patientUpdatesFetching, + isLoading: patientUpdatesLoading, + isError, + error, + } = useQuery({...}); ... + if (isError) { + return <div className="p-4 text-red-500">Error: {error.message}</div>; + }
77-119
: Mapping overpatientUpdatesData.results
Implementation is clear. A minor accessibility enhancement could be to providearia-label
props on icons for screen readers.Example diff:
<CareIcon icon="l-file-alt" + aria-label="Update icon" className="mt-1 h-5 w-5 text-gray-500" />
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (1)
44-91
: UI rendering of encounter history
Filtering, loading state, and empty state logic appear correct. Consider an error state foruseQuery
failures to inform users.src/components/Resource/ResourceCommentSection.tsx (1)
73-114
: Rendering comments and pagination
The logic is straightforward. Consider an error state to display a helpful message if fetching fails.src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (1)
308-340
: Consider extracting loading and empty states into separate components.The JSX structure could be more maintainable by extracting the loading and empty states into separate components.
+ const LoadingState = ({ count }: { count: number }) => ( + <div className="grid gap-5"> + <CardListSkeleton count={count} /> + </div> + ); + const EmptyState = () => ( + <Card className="p-6"> + <div className="text-lg font-medium text-gray-500"> + {t("no_questionnaire_responses")} + </div> + </Card> + ); return ( <div className="mt-4 gap-4"> <div className="max-w-full"> - {questionnaireLoading ? ( - <div className="grid gap-5"> - <CardListSkeleton count={resultsPerPage} /> - </div> - ) : ( + {questionnaireLoading ? <LoadingState count={resultsPerPage} /> : ( <div> {!questionnaireFetching && questionnarieResponses?.results?.length === 0 ? ( - <Card className="p-6"> - <div className="text-lg font-medium text-gray-500"> - {t("no_questionnaire_responses")} - </div> - </Card> + <EmptyState />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/CAREUI/misc/PaginatedList.tsx
(0 hunks)src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
(2 hunks)src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
(1 hunks)src/components/Patient/PatientDetailsTab/patientUpdates.tsx
(3 hunks)src/components/Resource/ResourceCommentSection.tsx
(2 hunks)
💤 Files with no reviewable changes (1)
- src/CAREUI/misc/PaginatedList.tsx
🔇 Additional comments (21)
src/components/Patient/PatientDetailsTab/patientUpdates.tsx (6)
1-1
: Import ofuseQuery
from @tanstack/react-query
No issues.
12-13
: Import ofuseFilters
Looks good.
15-15
: Import ofquery
Implementation is standard.
25-28
: Use ofuseFilters
with limit=7
The pagination limit is a design choice; no immediate concerns.
58-64
: Loading skeleton rendered for updates
The skeleton usage is appropriate, providing clear feedback when data is loading.
65-76
: Empty state handling for no updates
The conditionalpatientUpdatesData?.results?.length === 0
is correctly used to show a fallback message.src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (5)
1-1
: Import ofuseQuery
No concerns.
9-9
: Import ofCardListSkeleton
This import is straightforward.
13-14
: Import ofuseFilters
No issues.
16-16
: Import ofquery
All good here.
23-27
: Use ofuseFilters
with limit=5
The chosen page size of 5 is valid.src/components/Resource/ResourceCommentSection.tsx (7)
1-1
: Import ofuseQuery
Implementation looks fine.
10-10
: Import ofCardListSkeleton
No concerns.
11-11
: Blank line
No action needed.
12-12
: Import ofuseFilters
No issues.
15-15
: Import ofquery
Works as intended.
24-27
:useFilters
with limit=15
Pagination limit is a design choice; no issues.
76-79
: Comment submission logic
This repeats a previous review suggestion to wrap the operations in a try-catch block. The same feedback applies.src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (3)
1-15
: LGTM! Clean import organization.The imports are well-structured and properly grouped by type (external libraries, UI components, utilities).
58-273
: LGTM! Well-structured helper functions and components.The helper functions and components are well-implemented with proper type safety, error handling, and clear separation of concerns.
281-284
: LGTM! Good use of useFilters hook.The pagination configuration with cacheBlacklist is well-implemented for handling dynamic questionnaire data.
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: 1
🧹 Nitpick comments (2)
src/components/Patient/PatientDetailsTab/patientUpdates.tsx (1)
102-111
: Use Link component instead of navigate.Replace the Button with Link component for better routing practices.
-<Button - variant="outline" - onClick={() => { - navigate( - `/facility/${facilityId}/patient/${patientId}/encounter/${update.encounter}/questionnaire_response/${update.id}`, - ); - }} -> - {t("view")} -</Button> +<Button asChild variant="outline"> + <Link + href={`/facility/${facilityId}/patient/${patientId}/encounter/${update.encounter}/questionnaire_response/${update.id}`} + > + {t("view")} + </Link> +</Button>src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (1)
289-291
: Extract pagination constants.Define page size as a constant to avoid magic numbers and improve maintainability.
+const ITEMS_PER_PAGE = 15; + const { data: questionnaireResponses, isLoading } = useQuery({ queryKey: ["questionnaireResponses", patientId, qParams], queryFn: query(routes.getQuestionnaireResponses, { pathParams: { patientId }, queryParams: { encounter: encounter?.id, - limit: 15, - offset: ((qParams.page ?? 1) - 1) * 15, + limit: ITEMS_PER_PAGE, + offset: ((qParams.page ?? 1) - 1) * ITEMS_PER_PAGE, }, }), }); // ... later in the code ... <PaginationComponent cPage={qParams.page} - defaultPerPage={15} + defaultPerPage={ITEMS_PER_PAGE} data={{ totalCount: questionnaireResponses?.count ?? 0 }} onChange={(page) => setQueryParams({ page })} />Also applies to: 329-333
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
(2 hunks)src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
(1 hunks)src/components/Patient/PatientDetailsTab/patientUpdates.tsx
(3 hunks)src/components/Resource/ResourceCommentSection.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/Patient/PatientDetailsTab/patientUpdates.tsx (1)
Learnt from: AdityaJ2305
PR: ohcnetwork/care_fe#10345
File: src/components/Resource/ResourceDetailsUpdate.tsx:132-145
Timestamp: 2025-01-31T22:13:06.153Z
Learning: The migration from useTanStackQueryInstead to useQuery in React components requires:
1. Updating types to use UseQueryResult instead of custom hook return type
2. Replacing loading with isLoading property
3. Using queryKey array for cache management
4. Using queryFn with query utility function
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: cypress-run (1)
🔇 Additional comments (3)
src/components/Resource/ResourceCommentSection.tsx (1)
64-69
: Add keyboard accessibility for comment submission.The comment submission should be accessible via keyboard.
<Textarea name="comment" placeholder={t("type_your_comment")} value={commentBox} onChange={(e) => setCommentBox(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' && e.metaKey) { + submitComment(); + } + }} />src/components/Patient/PatientDetailsTab/patientUpdates.tsx (1)
28-41
: Add error handling and optimize query configuration.The query implementation should handle errors and be optimized for better performance.
const { data: patientUpdatesData, isFetching: patientUpdatesFetching, isLoading: patientUpdatesLoading, + isError, + error, } = useQuery({ queryKey: ["patientUpdates", patientId, qParams], queryFn: query(routes.getQuestionnaireResponses, { queryParams: { limit: 7, offset: ((qParams.page ?? 1) - 1) * 7, }, pathParams: { patientId }, }), + staleTime: 5 * 60 * 1000, // Cache data for 5 minutes + retry: 2, // Retry failed requests twice });src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (1)
284-294
: Fix typo in query key and improve naming.The variable name has a typo ("questionnarieResponses") and should be consistent with other query keys.
-const { data: questionnarieResponses, isLoading } = useQuery({ - queryKey: ["questionnaireResponses", patientId, qParams], +const { data: questionnaireResponses, isLoading } = useQuery({ + queryKey: ["questionnaireResponses", patientId, qParams],
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.
lgtm, few minor things
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
Outdated
Show resolved
Hide resolved
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/components/Resource/ResourceCommentSection.tsx (3)
29-38
: Add error handling for the query.Consider handling the error state to improve user experience when comments fail to load.
const { data: resourceComments, isLoading } = useQuery({ queryKey: ["resourceComments", id, qParams], queryFn: query(routes.getResourceComments, { queryParams: { limit: 15, offset: ((qParams.page ?? 1) - 1) * 15, }, pathParams: { id }, }), + onError: (error) => { + toast.error(t("failed_to_load_comments")); + console.error("Failed to load comments:", error); + }, });
50-59
: Add loading state handling for comment submission.Consider disabling the submit button while the comment is being posted to prevent duplicate submissions.
+ const [isSubmitting, setIsSubmitting] = useState(false); + const submitComment = () => { if (!/\S+/.test(commentBox)) { toast.error(t("comment_min_length")); return; } + setIsSubmitting(true); addComment({ comment: commentBox, + }, { + onSettled: () => setIsSubmitting(false), }); setCommentBox(""); };
91-114
: Improve accessibility of the comments list.Add ARIA labels to improve screen reader experience.
- <ul> + <ul aria-label={t("comments_list")}> {resourceComments?.results?.map((comment) => ( - <li key={comment.id} className="w-full"> + <li key={comment.id} className="w-full" role="article"> <Comment {...comment} /> </li> ))}src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2)
284-294
: Add error handling for the query.Consider handling the error state to improve user experience when responses fail to load.
- const { data: questionnarieResponses, isLoading } = useQuery({ + const { data: questionnarieResponses, isLoading, error } = useQuery({ queryKey: ["questionnaireResponses", patientId, qParams], queryFn: query(routes.getQuestionnaireResponses, { pathParams: { patientId }, queryParams: { encounter: encounter?.id, limit: 15, offset: ((qParams.page ?? 1) - 1) * 15, }, }), + onError: (error) => { + toast.error(t("failed_to_load_responses")); + console.error("Failed to load responses:", error); + }, });
312-337
: Improve accessibility of the responses list.Add ARIA labels to improve screen reader experience.
- <ul className="grid gap-4"> + <ul className="grid gap-4" aria-label={t("questionnaire_responses_list")}> {questionnarieResponses?.results?.map( (item: QuestionnaireResponse) => ( - <li key={item.id} className="w-full"> + <li key={item.id} className="w-full" role="article"> <ResponseCard key={item.id} item={item} /> </li> ), )}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
(2 hunks)src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
(1 hunks)src/components/Patient/PatientDetailsTab/patientUpdates.tsx
(3 hunks)src/components/Resource/ResourceCommentSection.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/components/Patient/PatientDetailsTab/patientUpdates.tsx
- src/components/Patient/PatientDetailsTab/EncounterHistory.tsx
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: OSSAR-Scan
- GitHub Check: cypress-run (1)
🔇 Additional comments (1)
src/components/Resource/ResourceCommentSection.tsx (1)
64-75
: Add keyboard accessibility for comment submission.The comment submission should be accessible via keyboard.
<Textarea name="comment" placeholder={t("type_your_comment")} value={commentBox} onChange={(e) => setCommentBox(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' && e.metaKey) { + submitComment(); + } + }} /> <div className="flex w-full justify-end mt-2"> <Button variant="primary" onClick={submitComment} + disabled={isSubmitting} > {t("post_your_comment")} </Button> </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.
nice work
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.
The limits on each page seem like random 🤔 Let's use RESULTS_PER_PAGE_LIMIT from constants instead (maybe have EncounterHistory as the exception here).
Otherwise LGTM.
made the changes as requested. |
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 (1)
src/components/Resource/ResourceCommentSection.tsx (1)
66-71
: 🛠️ Refactor suggestionAdd keyboard accessibility for comment submission.
Enable comment submission using keyboard shortcuts for better accessibility.
<Textarea name="comment" placeholder={t("type_your_comment")} value={commentBox} onChange={(e) => setCommentBox(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' && e.metaKey) { + submitComment(); + } + }} />
🧹 Nitpick comments (2)
src/components/Resource/ResourceCommentSection.tsx (2)
52-61
: Add validation for maximum comment length.Add a character limit to prevent excessively long comments.
const submitComment = () => { if (!/\S+/.test(commentBox)) { toast.error(t("comment_min_length")); return; } + if (commentBox.length > 1000) { + toast.error(t("comment_max_length")); + return; + } addComment({ comment: commentBox, }); setCommentBox(""); };
74-77
: Add loading state to the submit button.Show loading state during comment submission to prevent duplicate submissions.
-<Button variant="primary" onClick={submitComment}> +<Button + variant="primary" + onClick={submitComment} + disabled={isSubmitting} +> - {t("post_your_comment")} + {isSubmitting ? t("posting_comment") : t("post_your_comment")} </Button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
(2 hunks)src/components/Patient/PatientDetailsTab/patientUpdates.tsx
(3 hunks)src/components/Resource/ResourceCommentSection.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/Patient/PatientDetailsTab/patientUpdates.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: cypress-run (1)
🔇 Additional comments (2)
src/components/Resource/ResourceCommentSection.tsx (1)
128-152
: LGTM! Well-structured comment display component.The component effectively displays comment content, user information, and timestamps.
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (1)
62-277
: LGTM! Well-structured helper components.The helper components effectively handle different response types and formatting requirements.
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
Show resolved
Hide resolved
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 (3)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (3)
1-7
: Consider organizing imports by category.Consider grouping imports into categories for better maintainability:
- External dependencies
- Internal utilities/helpers
- Components
- Types
- Constants
import { useQuery } from "@tanstack/react-query"; import { t } from "i18next"; import { useQueryParams } from "raviger"; import { useTranslation } from "react-i18next"; - -import { cn } from "@/lib/utils"; - + import { Badge } from "@/components/ui/badge"; import { Card } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import PaginationComponent from "@/components/Common/Pagination"; import { CardListSkeleton } from "@/components/Common/SkeletonLoading"; + +import { cn } from "@/lib/utils"; +import routes from "@/Utils/request/api"; +import query from "@/Utils/request/query"; +import { formatDateTime, properCase } from "@/Utils/utils"; import { RESULTS_PER_PAGE_LIMIT } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import query from "@/Utils/request/query"; -import { formatDateTime, properCase } from "@/Utils/utils";
287-288
: Consider using a more strict type for page parameter.The current type allows undefined, but we always default to 1. Consider making it non-optional.
- const [qParams, setQueryParams] = useQueryParams<{ page?: number }>(); + const [qParams, setQueryParams] = useQueryParams<{ page: number }>({ page: 1 });
320-322
: Remove duplicate key prop in ResponseCard rendering.The key prop is specified both on the list item and the ResponseCard component.
<li key={item.id} className="w-full"> - <ResponseCard key={item.id} item={item} /> + <ResponseCard item={item} /> </li>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: cypress-run (1)
🔇 Additional comments (2)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2)
62-280
: LGTM! Well-structured helper functions and components.The helper functions and components are well-organized, with clear separation of concerns and proper type safety.
289-299
: 🛠️ Refactor suggestionAdd error handling to the query.
The query is missing error handling which could lead to silent failures.
- const { data: questionnarieResponses, isLoading } = useQuery({ + const { data: questionnaireResponses, isLoading } = useQuery({ queryKey: ["questionnaireResponses", patientId, qParams], queryFn: query(routes.getQuestionnaireResponses, { pathParams: { patientId }, queryParams: { encounter: encounter?.id, limit: RESULTS_PER_PAGE_LIMIT, offset: ((qParams.page ?? 1) - 1) * RESULTS_PER_PAGE_LIMIT, }, }), + onError: (error) => { + console.error("Failed to fetch questionnaire responses:", error); + toast.error(t("failed_to_fetch_questionnaire_responses")); + }, });Likely invalid or redundant comment.
LGTM |
@rajku-dev Your efforts have helped advance digital healthcare and TeleICU systems. 🚀 Thank you for taking the time out to make CARE better. We hope you continue to innovate and contribute; your impact is immense! 🙌 |
Proposed Changes
PaginatedList.tsx
and update dependents components to useuseQuery
along withuseFilters
hook@ohcnetwork/care-fe-code-reviewers
Merge Checklist
Summary by CodeRabbit
Summary by CodeRabbit
New Features
PaginatedList
component, simplifying pagination logic across various components.Bug Fixes
Refactor
useQuery
for data fetching across multiple components, improving performance and maintainability.PaginatedList
component with custom implementations.