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

Migration to useQuery: Removed PaginatedList.tsx and Update Dependent Components #10350

Merged

Conversation

rajku-dev
Copy link
Contributor

@rajku-dev rajku-dev commented Feb 1, 2025

Proposed Changes

@ohcnetwork/care-fe-code-reviewers

Merge Checklist

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

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a custom implementation for fetching and displaying questionnaire responses, enhancing responsiveness and flexibility.
    • Enhanced the encounter history component with improved loading and empty state handling.
    • Implemented a new data fetching mechanism for comments in the comment section, improving user experience during loading states.
    • Updated the patient updates section with a new data fetching strategy for better clarity and performance.
    • Removed the PaginatedList component, simplifying pagination logic across various components.
  • Bug Fixes

    • Addressed issues related to loading and empty states across multiple components.
  • Refactor

    • Transitioned to using useQuery for data fetching across multiple components, improving performance and maintainability.
    • Updated the structure of the comment model to provide more detailed user information.
    • Streamlined the pagination logic by replacing the PaginatedList component with custom implementations.

@rajku-dev rajku-dev requested a review from a team as a code owner February 1, 2025 09:09
Copy link
Contributor

coderabbitai bot commented Feb 1, 2025

Walkthrough

This pull request involves the complete removal of the PaginatedList component from the application. It replaces this component with custom implementations in various files, utilizing the useQuery hook from @tanstack/react-query and a new useQueryParams hook for managing data fetching and pagination. The changes affect components such as QuestionnaireResponsesList, EncounterHistory, ResourceCommentSection, and others, streamlining the data handling processes and enhancing loading and empty state management.

Changes

File(s) Change Summary
src/CAREUI/misc/PaginatedList.tsx Removed the PaginatedList component, including its context interface and child components.
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx Removed PaginatedList and replaced it with a custom implementation using useQuery and useQueryParams; updated loading and empty state handling with conditional rendering.
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx Replaced the PaginatedList component with direct use of useQuery for fetching encounter data; modified loading and empty state handling.
src/components/Facility/models.tsx Updated CommentModel interface by removing created_by_object and changing created_by from number to UserBareMinimum.
src/components/Resource/ResourceCommentSection.tsx Introduced useQuery for fetching comments; removed PaginatedList and implemented custom fetching logic with pagination; updated rendering logic for loading and empty states.
src/components/Patient/PatientDetailsTab/patientUpdates.tsx Replaced PaginatedList with useQuery for managing patient updates; added useQueryParams for query parameters; updated rendering logic for loading and empty states.

Possibly related PRs

Suggested labels

tested

Suggested reviewers

  • rithviknishad
  • Jacobjeevan

Poem

I'm a bouncy rabbit, hopping through code,
With new hooks and queries on this joyful road.
From useQuery magic to neat renaming fixes,
My whiskers twitch at all these clever mixes.
Celebrate the changes — it's a coding delight!
Happy hops and bytes to all, day and night!


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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

netlify bot commented Feb 1, 2025

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit 379fa05
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/67a1598f3db5b30008c8c717
😎 Deploy Preview https://deploy-preview-10350--care-ohc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/CAREUI/misc/PaginatedList.tsx (1)

74-84: useQuery usage is correctly keyed and configured.
Consider ensuring queryParams 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

📥 Commits

Reviewing files that changed from the base of the PR and between 66104a8 and 52a3fae.

📒 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 of react-query.
No issues found with this import statement.


10-10: Importing apiQuery 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: Combining UseQueryResult 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 new useQuery 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: Initializing currentPage properly.
Using initialPage ?? 1 is a straightforward defaulting approach.


71-71: Optional chaining for onPageChange 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: Renaming query to queryParams aligns with the updated PaginatedList props.
Good consistency in naming across the codebase.

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

285-285: Using queryParams for PaginatedList maintains consistency.
No issues found.

src/CAREUI/misc/PaginatedList.tsx Outdated Show resolved Hide resolved
@rajku-dev rajku-dev mentioned this pull request Feb 1, 2025
6 tasks
src/CAREUI/misc/PaginatedList.tsx Outdated Show resolved Hide resolved
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 (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

📥 Commits

Reviewing files that changed from the base of the PR and between 52a3fae and 881aadb.

📒 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 and useFilters are appropriate for the component's migration from PaginatedList to a direct useQuery 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 in src/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 ts

Length of output: 322

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

  1. Multiple nested div elements could be flattened
  2. Conditional rendering could be simplified
  3. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 881aadb and 6c28f88.

📒 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 with limit: 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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (5)

28-41: Consider these improvements to the query configuration.

  1. The limit value (5) is duplicated in both useFilters and query parameters. Consider extracting it to a constant.
  2. The offset calculation could be moved to a constant for better readability.
  3. 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 the li and EncounterCard 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 to useQuery has been executed effectively, with proper handling of different states and clean integration with the useFilters hook. The component maintains good separation of concerns and follows React best practices.

Consider these architectural improvements for the future:

  1. Extract reusable UI components for empty and loading states
  2. Implement error boundaries or error states for failed queries
  3. Consider implementing infinite scroll instead of pagination for a better user experience
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6c28f88 and d54168f.

📒 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.

@rajku-dev
Copy link
Contributor Author

rajku-dev commented Feb 2, 2025

questionnaire response
image

encounter history
image
image

Edit

resource comments
image

patientUpdates
image

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (3)

23-26: Extract magic number and fix cache key inconsistency.

Two issues to address:

  1. The limit value of 5 is hardcoded and repeated later in the query params. Consider extracting it to a constant.
  2. 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 and EncounterCard 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

📥 Commits

Reviewing files that changed from the base of the PR and between d54168f and bca1bf5.

📒 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 to UserBareMinimum for created_by is a breaking change that might affect other components using CommentModel.

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:

  1. Using useQuery from @tanstack/react-query
  2. Adding loading state components
  3. Using the useFilters hook for pagination

Also 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 with UserBareMinimum type
  • Formats comment text and timestamps
  • Displays user information with avatar

@rajku-dev
Copy link
Contributor Author

Ready for review!

@rajku-dev rajku-dev changed the title Migrate PaginatedList to useQuery Migration to useQuery: Removed PaginatedList.tsx and Update Dependent Components Feb 2, 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: 2

♻️ Duplicate comments (2)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (2)

291-296: 🛠️ Refactor suggestion

Restructure 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 suggestion

Remove 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 of useQuery 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 over patientUpdatesData.results
Implementation is clear. A minor accessibility enhancement could be to provide aria-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 for useQuery 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

📥 Commits

Reviewing files that changed from the base of the PR and between b627460 and 52e2e2d.

📒 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 of useQuery from @tanstack/react-query
No issues.


12-13: Import of useFilters
Looks good.


15-15: Import of query
Implementation is standard.


25-28: Use of useFilters 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 conditional patientUpdatesData?.results?.length === 0 is correctly used to show a fallback message.

src/components/Patient/PatientDetailsTab/EncounterHistory.tsx (5)

1-1: Import of useQuery
No concerns.


9-9: Import of CardListSkeleton
This import is straightforward.


13-14: Import of useFilters
No issues.


16-16: Import of query
All good here.


23-27: Use of useFilters with limit=5
The chosen page size of 5 is valid.

src/components/Resource/ResourceCommentSection.tsx (7)

1-1: Import of useQuery
Implementation looks fine.


10-10: Import of CardListSkeleton
No concerns.


11-11: Blank line
No action needed.


12-12: Import of useFilters
No issues.


15-15: Import of query
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.

src/components/Resource/ResourceCommentSection.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a7118e and c3f1fb6.

📒 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],

src/components/Resource/ResourceCommentSection.tsx Outdated Show resolved Hide resolved
Copy link
Member

@rithviknishad rithviknishad left a 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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/components/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

📥 Commits

Reviewing files that changed from the base of the PR and between c3f1fb6 and e205f00.

📒 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>

Copy link
Member

@rithviknishad rithviknishad left a comment

Choose a reason for hiding this comment

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

nice work

Copy link
Contributor

@Jacobjeevan Jacobjeevan left a 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.

@rajku-dev
Copy link
Contributor Author

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.

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 (1)
src/components/Resource/ResourceCommentSection.tsx (1)

66-71: 🛠️ Refactor suggestion

Add 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

📥 Commits

Reviewing files that changed from the base of the PR and between e205f00 and febc096.

📒 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.

@rajku-dev rajku-dev requested a review from Jacobjeevan February 4, 2025 00:04
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 (3)
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx (3)

1-7: Consider organizing imports by category.

Consider grouping imports into categories for better maintainability:

  1. External dependencies
  2. Internal utilities/helpers
  3. Components
  4. Types
  5. 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

📥 Commits

Reviewing files that changed from the base of the PR and between febc096 and 379fa05.

📒 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 suggestion

Add 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.

@nihal467
Copy link
Member

nihal467 commented Feb 4, 2025

LGTM

@nihal467 nihal467 added tested reviewed reviewed by a core member and removed needs testing labels Feb 4, 2025
@rithviknishad rithviknishad merged commit 8941715 into ohcnetwork:develop Feb 4, 2025
15 checks passed
Copy link

github-actions bot commented Feb 4, 2025

@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! 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
reviewed reviewed by a core member tested
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants