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

Add Deletion Functionality to Locations #10571

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

Conversation

DonXavierdev
Copy link
Contributor

@DonXavierdev DonXavierdev commented Feb 12, 2025

Proposed Changes

@ohcnetwork/care-fe-code-reviewers

Screenshot

Screenshot 2025-03-06 at 2 02 50 PM

Merge Checklist

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

Summary by CodeRabbit

  • New Features
    • Introduced a delete option for locations, complete with a confirmation dialog and success notification.
    • Enhanced the integration between locations and facilities by linking facility identification to location cards.
    • Expanded localization to provide clear feedback upon successful location deletion.

@DonXavierdev DonXavierdev requested a review from a team as a code owner February 12, 2025 10:41
Copy link
Contributor

coderabbitai bot commented Feb 12, 2025

Walkthrough

This pull request updates the facility location management by passing a new facilityId prop from the LocationView to the LocationCard. The LocationCard component now supports deletion of a location through a new delete button that triggers an AlertDialog and performs a delete API call using useMutation. Additionally, a new API endpoint for deletion is added along with an entry in the localization file to display a success message upon deletion.

Changes

File(s) Change Summary
src/pages/Facility/.../LocationView.tsx, src/pages/Facility/.../LocationCard.tsx Pass new facilityId prop to LocationCard; update its component signature and integrate deletion functionality (uses useMutation, AlertDialog, toast notifications, and layout adjustments).
src/types/location/.../locationApi.ts Add new DELETE endpoint for location removal with a specific URL pattern and response type.
public/locale/en.json Add new localization entry "location_removed_successfully": "Location removed successfully" for deletion feedback.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant LocationCard
    participant AlertDialog
    participant API
    participant QueryClient

    User->>LocationCard: Click "Delete" button
    LocationCard->>AlertDialog: Open confirmation dialog
    User->>AlertDialog: Confirm deletion
    AlertDialog->>LocationCard: Send confirmation
    LocationCard->>API: Call delete endpoint (facilityId, locationId)
    API-->>LocationCard: Acknowledge deletion
    LocationCard->>QueryClient: Invalidate location query
    LocationCard->>User: Display success toast
Loading

Possibly related PRs

  • Patient Age and Facility Location Edits #10260: The changes in the main PR, which involve adding the facilityId prop to the LocationCard component, are directly related to the modifications made in the retrieved PR, where the facilityId property is also incorporated into the LocationCard component's props.
  • Feat/google maps link #10003: The changes in the main PR, which involve adding the facilityId prop to the LocationCard component, are directly related to the modifications in the retrieved PR, as both involve updates to the LocationCard component's props and functionality.
  • Location Availability display fix #10749: The changes in the main PR, which involve adding the facilityId prop to the LocationCard component, are related to the modifications in the retrieved PR that also involve the LocationCard component, specifically in how it handles location status and availability.

Suggested labels

needs review, tested

Suggested reviewers

  • rithviknishad
  • Jacobjeevan

Poem

I'm a rabbit coding with delight,
Hopping through props both day and night,
A new facilityId adds magic to the scene,
With delete buttons and dialogs so keen,
Bugs scamper away as queries refresh,
Celebrating these changes with a joyful "🥕 yes!"
CodeRabbit hops on, turning code to art!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

netlify bot commented Feb 12, 2025

Deploy Preview for care-ohc ready!

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

QR Code

Use your smartphone camera to open QR code link.

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

@DonXavierdev
Copy link
Contributor Author

Currently only locations that do not have children can be deleted. This is to prevent wanted locations from being deleted. Please Let me know if this approach should be modified.

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/pages/Facility/settings/locations/components/LocationCard.tsx (1)

134-136: Add accessibility label to delete button.

The delete button should have an aria-label to improve accessibility for screen readers.

-            <Button variant="destructive" onClick={() => onDelete?.(location)}>
+            <Button 
+              variant="destructive" 
+              onClick={() => onDelete?.(location)}
+              aria-label={t("delete_location")}
+            >
src/pages/Facility/settings/locations/LocationList.tsx (1)

78-83: Enhance error handling to cover network errors.

The error handling only processes API errors. Consider handling network errors and other potential failure cases.

     onError: (error) => {
-      const errorData = error.cause as { errors: { msg: string }[] };
-      errorData.errors.forEach((er) => {
-        toast.error(er.msg);
-      });
+      if (error.cause && 'errors' in error.cause) {
+        const errorData = error.cause as { errors: { msg: string }[] };
+        errorData.errors.forEach((er) => {
+          toast.error(er.msg);
+        });
+      } else {
+        toast.error(t("failed_to_delete_location"));
+      }
     },
src/pages/Facility/settings/locations/LocationView.tsx (1)

99-128: Consider extracting shared deletion logic into a custom hook.

The deletion logic is duplicated between LocationList and LocationView components. Consider creating a custom hook like useLocationDelete to share this functionality.

Example implementation:

function useLocationDelete(facilityId: string, options?: { 
  parentId?: string,
  onSuccess?: () => void 
}) {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({ locationId }: { locationId: string }) =>
      mutate(locationApi.delete, {
        pathParams: { facility_id: facilityId, id: locationId },
      })({ facilityId, locationId }),
    onSuccess: () => {
      const queryKey = options?.parentId 
        ? ["locations", facilityId, options.parentId, "children"]
        : ["locations", facilityId];
      queryClient.invalidateQueries({ queryKey });
      toast.success("Location removed successfully");
      options?.onSuccess?.();
    },
    onError: (error) => {
      if (error.cause && 'errors' in error.cause) {
        const errorData = error.cause as { errors: { msg: string }[] };
        errorData.errors.forEach((er) => {
          toast.error(er.msg);
        });
      } else {
        toast.error(t("failed_to_delete_location"));
      }
    },
  });
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7d7cfd6 and 6ebc0fe.

📒 Files selected for processing (4)
  • src/pages/Facility/settings/locations/LocationList.tsx (5 hunks)
  • src/pages/Facility/settings/locations/LocationView.tsx (6 hunks)
  • src/pages/Facility/settings/locations/components/LocationCard.tsx (3 hunks)
  • src/types/location/locationApi.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: cypress-run (1)
🔇 Additional comments (2)
src/types/location/locationApi.ts (1)

31-35: LGTM! The delete endpoint follows RESTful conventions.

The delete endpoint is well-defined with appropriate path parameters and return type.

src/pages/Facility/settings/locations/LocationView.tsx (1)

122-127: Enhance error handling to cover network errors.

Similar to LocationList, the error handling should be improved to handle network errors and other failure cases.

@rithviknishad
Copy link
Member

Currently only locations that do not have children can be deleted. This is to prevent wanted locations from being deleted. Please Let me know if this approach should be modified.

Yup! Backend also rejects it anyways

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.

reviews would be applicable in multiple places too

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6ebc0fe and 89af39c.

📒 Files selected for processing (3)
  • src/pages/Facility/settings/locations/LocationList.tsx (1 hunks)
  • src/pages/Facility/settings/locations/LocationView.tsx (1 hunks)
  • src/pages/Facility/settings/locations/components/LocationCard.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/pages/Facility/settings/locations/LocationView.tsx
  • src/pages/Facility/settings/locations/LocationList.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Test
  • GitHub Check: OSSAR-Scan
  • GitHub Check: cypress-run (1)
🔇 Additional comments (2)
src/pages/Facility/settings/locations/components/LocationCard.tsx (2)

43-48: LGTM! Props interface update is well-structured.

The addition of the optional facilityId prop aligns with the component's new deletion functionality while maintaining backward compatibility.


165-166: LGTM! Layout changes are well-structured.

The use of justify-between for button placement creates a clean and balanced layout, with destructive actions (delete) on the left and navigation (view details) on the right.

Also applies to: 204-213

Comment on lines 167 to 203
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
className={cn(buttonVariants({ variant: "destructive" }))}
>
<CareIcon icon="l-trash" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("remove")} {location.name}
</AlertDialogTitle>
<AlertDialogDescription>
{t("are_you_sure_want_to_delete", {
name: location.name,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
onClick={() =>
removeLocation({
pathParams: {
facility_id: facilityId,
id: location.id,
},
})
}
className={cn(buttonVariants({ variant: "destructive" }))}
>
{t("remove")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 13, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent deletion of locations with children.

Based on the PR comments, locations with children should not be deletable. The UI should reflect this restriction.

Add a check for location.has_children and disable the delete button accordingly:

-            <AlertDialog>
+            {!location.has_children ? (
+             <AlertDialog>
              <AlertDialogTrigger asChild>
                <Button
                  className={cn(buttonVariants({ variant: "destructive" }))}
                >
                  <CareIcon icon="l-trash" />
                </Button>
              </AlertDialogTrigger>
              <AlertDialogContent>
                <AlertDialogHeader>
                  <AlertDialogTitle>
                    {t("remove")} {location.name}
                  </AlertDialogTitle>
                  <AlertDialogDescription>
                    {t("are_you_sure_want_to_delete", {
                      name: location.name,
                    })}
                  </AlertDialogDescription>
                </AlertDialogHeader>
                <AlertDialogFooter>
                  <AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
                  <AlertDialogAction
                    onClick={() =>
                      removeLocation({
                        pathParams: {
                          facility_id: facilityId,
                          id: location.id,
                        },
                      })
                    }
                    className={cn(buttonVariants({ variant: "destructive" }))}
                  >
                    {t("remove")}
                  </AlertDialogAction>
                </AlertDialogFooter>
              </AlertDialogContent>
             </AlertDialog>
+            ) : (
+             <Button
+               className={cn(buttonVariants({ variant: "destructive" }))}
+               disabled
+               title={t("cannot_delete_location_with_children")}
+             >
+               <CareIcon icon="l-trash" />
+             </Button>
+            )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
className={cn(buttonVariants({ variant: "destructive" }))}
>
<CareIcon icon="l-trash" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("remove")} {location.name}
</AlertDialogTitle>
<AlertDialogDescription>
{t("are_you_sure_want_to_delete", {
name: location.name,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
onClick={() =>
removeLocation({
pathParams: {
facility_id: facilityId,
id: location.id,
},
})
}
className={cn(buttonVariants({ variant: "destructive" }))}
>
{t("remove")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{!location.has_children ? (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
className={cn(buttonVariants({ variant: "destructive" }))}
>
<CareIcon icon="l-trash" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("remove")} {location.name}
</AlertDialogTitle>
<AlertDialogDescription>
{t("are_you_sure_want_to_delete", {
name: location.name,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
onClick={() =>
removeLocation({
pathParams: {
facility_id: facilityId,
id: location.id,
},
})
}
className={cn(buttonVariants({ variant: "destructive" }))}
>
{t("remove")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
) : (
<Button
className={cn(buttonVariants({ variant: "destructive" }))}
disabled
title={t("cannot_delete_location_with_children")}
>
<CareIcon icon="l-trash" />
</Button>
)}

Copy link
Contributor

@Jacobjeevan Jacobjeevan Feb 14, 2025

Choose a reason for hiding this comment

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

☝️ @DonXavierdev Don't show the trash button if location has children

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines 167 to 203
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
className={cn(buttonVariants({ variant: "destructive" }))}
>
<CareIcon icon="l-trash" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("remove")} {location.name}
</AlertDialogTitle>
<AlertDialogDescription>
{t("are_you_sure_want_to_delete", {
name: location.name,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
onClick={() =>
removeLocation({
pathParams: {
facility_id: facilityId,
id: location.id,
},
})
}
className={cn(buttonVariants({ variant: "destructive" }))}
>
{t("remove")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Copy link
Contributor

@Jacobjeevan Jacobjeevan Feb 14, 2025

Choose a reason for hiding this comment

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

☝️ @DonXavierdev Don't show the trash button if location has children

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

59-69: ⚠️ Potential issue

Add error handling for failed deletions.

While the success case is well-handled with query invalidation and toast notification, there's no error handling for failed deletions.

Add error handling to provide feedback when deletion fails:

 const { mutate: removeLocation } = useMutation({
   mutationFn: mutate(locationApi.delete, {
     pathParams: { facility_id: facilityId, id: location.id },
   }),
   onSuccess: () => {
     queryClient.invalidateQueries({
       queryKey: ["locations", facilityId],
     });
     toast.success("Location removed successfully");
   },
+  onError: (error) => {
+    toast.error(error instanceof Error ? error.message : "Failed to remove location");
+  },
 });
🧹 Nitpick comments (2)
src/pages/Facility/settings/locations/components/LocationCard.tsx (2)

67-67: Internationalize the success message.

The success message is hardcoded. Use the translation function to maintain consistency with the rest of the internationalized text.

-      toast.success("Location removed successfully");
+      toast.success(t("location_removed_successfully"));

170-174: Improve accessibility for the delete button.

The delete button lacks proper accessibility attributes for screen readers.

 <Button
   className={cn(buttonVariants({ variant: "destructive" }))}
+  aria-label={t("delete_location", { name: location.name })}
 >
-  <CareIcon icon="l-trash" />
+  <CareIcon icon="l-trash" aria-hidden="true" />
+  <span className="sr-only">{t("delete_location", { name: location.name })}</span>
 </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 89af39c and c7e14b0.

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

1-48: LGTM! Import statements and interface changes are well-organized.

The new imports and Props interface update are correctly implemented to support the delete functionality.

🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: Do not shadow the global "Map" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

59-69: ⚠️ Potential issue

Add error and loading state handling to the deletion mutation.

While the success case is well-handled, the mutation implementation needs error handling and loading state management.

+ const [isDeleting, setIsDeleting] = useState(false);
  const { mutate: removeLocation } = useMutation({
    mutationFn: mutate(locationApi.delete, {
      pathParams: { facility_id: facilityId, id: location.id },
    }),
    onSuccess: () => {
      queryClient.invalidateQueries({
        queryKey: ["locations", facilityId],
      });
      toast.success(t("location_removed_successfully"));
+     setIsDeleting(false);
    },
+   onError: (error) => {
+     toast.error(error instanceof Error ? error.message : t("failed_to_remove_location"));
+     setIsDeleting(false);
+   },
+   onMutate: () => {
+     setIsDeleting(true);
+   },
  });
🧹 Nitpick comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

167-205: Add loading state to the delete button and enhance dialog text.

The delete button implementation needs loading state indication and more descriptive confirmation text.

  {!location.has_children && (
    <AlertDialog>
      <AlertDialogTrigger asChild>
        <Button
          className={cn(buttonVariants({ variant: "destructive" }))}
+         disabled={isDeleting}
        >
-         <CareIcon icon="l-trash" />
+         {isDeleting ? (
+           <CareIcon icon="l-spinner" className="animate-spin" />
+         ) : (
+           <CareIcon icon="l-trash" />
+         )}
        </Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>
            {t("remove")} {location.name}
          </AlertDialogTitle>
          <AlertDialogDescription>
-           {t("are_you_sure_want_to_delete", {
-             name: location.name,
-           })}
+           {t("location_delete_confirmation", {
+             name: location.name,
+             type: getLocationFormLabel(location.form),
+           })}
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
          <AlertDialogAction
            onClick={() =>
              removeLocation({
                pathParams: {
                  facility_id: facilityId,
                  id: location.id,
                },
              })
            }
            className={cn(buttonVariants({ variant: "destructive" }))}
+           disabled={isDeleting}
          >
-           {t("remove")}
+           {isDeleting ? t("removing") : t("remove")}
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c7e14b0 and 79bd9a7.

📒 Files selected for processing (2)
  • public/locale/en.json (1 hunks)
  • src/pages/Facility/settings/locations/components/LocationCard.tsx (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Test
  • GitHub Check: OSSAR-Scan
  • GitHub Check: cypress-run (1)
🔇 Additional comments (2)
public/locale/en.json (1)

1254-1254: New Localization Entry for Delete Confirmation

The newly added key "location_removed_successfully": "Location removed successfully" fits well with the new deletion functionality. It follows the existing pattern in the file and provides clear feedback upon successful deletion of a location.

src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

1-1: LGTM! Well-organized imports and type definitions.

The new imports are logically grouped and all necessary dependencies for the deletion functionality are included. The Props interface is correctly updated with the optional facilityId.

Also applies to: 18-18, 22-34, 36-36, 39-41, 47-47

@nihal467
Copy link
Member

nihal467 commented Feb 18, 2025

image

  • When all the child locations are already deleted, the parent locations is still not showing the delete button

Note: Check whether a location that is currently in use should be restricted from being deleted.

@DonXavierdev
Copy link
Contributor Author

  • When all the child locations are already deleted, the parent locations is still not showing the delete button

@rithviknishad Can you check if this is backend issue

@rithviknishad
Copy link
Member

Can you check if this is backend issue

@DonXavierdev yes this is a backend issue. has_children is never being unset after deleting location. You can file an issue in the backend repo.

@DonXavierdev
Copy link
Contributor Author

Check whether a location that is currently in use should be restricted from being deleted.

@nihal467 What should i do here . should i remove the delete button when location has an active label ?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

59-69: 🛠️ Refactor suggestion

Add error handling for failed deletions.

While the success case is well-handled with query invalidation and toast notification, there's no error handling for failed deletions.

Add error handling to provide feedback when deletion fails:

 const { mutate: removeLocation } = useMutation({
   mutationFn: mutate(locationApi.delete, {
     pathParams: { facility_id: facilityId, id: location.id },
   }),
   onSuccess: () => {
     queryClient.invalidateQueries({
       queryKey: ["locations", facilityId],
     });
     toast.success(t("location_removed_successfully"));
   },
+  onError: (error) => {
+    toast.error(error instanceof Error ? error.message : t("failed_to_remove_location"));
+  },
 });
🧹 Nitpick comments (2)
src/pages/Facility/settings/locations/components/LocationCard.tsx (2)

170-172: Use destructive variant for delete button.

The delete button should visually communicate its destructive nature. The current "white" variant doesn't sufficiently indicate that this is a destructive action.

-                  <Button className={cn(buttonVariants({ variant: "white" }))}>
+                  <Button className={cn(buttonVariants({ variant: "destructive" }))}>
                     <CareIcon icon="l-trash" />
                   </Button>

189-194: Consider optimizing the removeLocation call.

The pathParams are redundant as they are already defined in the mutationFn. You can simplify the onClick handler.

-                      onClick={() =>
-                        removeLocation({
-                          pathParams: {
-                            facility_id: facilityId,
-                            id: location.id,
-                          },
-                        })
-                      }
+                      onClick={() => removeLocation()}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa2c02 and da625f0.

📒 Files selected for processing (4)
  • public/locale/en.json (1 hunks)
  • src/pages/Facility/settings/locations/LocationView.tsx (1 hunks)
  • src/pages/Facility/settings/locations/components/LocationCard.tsx (3 hunks)
  • src/types/location/locationApi.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/pages/Facility/settings/locations/LocationView.tsx
  • src/types/location/locationApi.ts
  • public/locale/en.json
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Test
  • GitHub Check: cypress-run (1)
🔇 Additional comments (5)
src/pages/Facility/settings/locations/components/LocationCard.tsx (5)

167-203: The delete button implementation correctly prevents deletion of locations with children.

The implementation appropriately hides the delete button for locations with children, aligning with the requirements discussed in the PR comments.


47-47: Make facilityId prop non-optional.

Based on the previous review comments, since the LocationCard is only used on two pages that both provide the facilityId, it should be a required prop as implemented.


1-41: Imports are appropriate for the new functionality.

The added imports for useMutation, AlertDialog components, and CareIcon are necessary for implementing the deletion functionality.

🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: Do not shadow the global "Map" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)


166-166: Layout change from flex-end to justify-between is appropriate.

The change from right-aligned to justify-between positioning accommodates the new delete button well.


67-67:

✅ Verification successful

Verify the translation key exists.

Ensure that the "location_removed_successfully" translation key exists in all relevant locale files to avoid displaying untranslated text to users.


🏁 Script executed:

#!/bin/bash
# Check if the translation key exists in the locale files
grep -r "location_removed_successfully" public/locale/

Length of output: 144


Translation Key Confirmed

I verified that the translation key "location_removed_successfully" exists in the English locale file (public/locale/en.json). If your project later supports additional locales, please ensure the same key is present there to prevent any untranslated text.

@github-actions github-actions bot removed the merge conflict pull requests with merge conflict label Mar 3, 2025
@nihal467
Copy link
Member

nihal467 commented Mar 4, 2025

image

  • As shown in the screenshot, locations that are active have an "Unavailable" badge. The delete button for these locations should remain hidden until they are marked as "Available.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

1-1: 🛠️ Refactor suggestion

Add error handling for deletion failures.

The code implements the deletion functionality but lacks error handling when the API call fails. Users would not receive any feedback if the deletion encounters an error.

Add error handling to provide feedback when deletion fails:

const { mutate: removeLocation } = useMutation({
  mutationFn: mutate(locationApi.delete, {
    pathParams: { facility_id: facilityId, id: location.id },
  }),
  onSuccess: () => {
    queryClient.invalidateQueries({
      queryKey: ["locations", facilityId],
    });
    toast.success(t("location_removed_successfully"));
  },
+  onError: (error) => {
+    toast.error(error instanceof Error ? error.message : t("failed_to_remove_location"));
+  },
});

Also applies to: 18-18, 57-69

🧹 Nitpick comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

170-172: Use destructive variant for delete button.

The delete button currently uses the "white" variant which doesn't visually communicate its destructive nature. Consider using the "destructive" variant to make it clear to users that this is a deletion action.

-<Button className={cn(buttonVariants({ variant: "white" }))}>
+<Button className={cn(buttonVariants({ variant: "destructive", size: "icon" }))}>
  <CareIcon icon="l-trash" />
</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 da625f0 and 3a8e8b4.

📒 Files selected for processing (1)
  • src/pages/Facility/settings/locations/components/LocationCard.tsx (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Test
  • GitHub Check: lint
  • GitHub Check: cypress-run (1)
  • GitHub Check: CodeQL-Build
  • GitHub Check: OSSAR-Scan
🔇 Additional comments (7)
src/pages/Facility/settings/locations/components/LocationCard.tsx (7)

47-47: Update the facilityId prop to be required.

Based on the previous comment thread, it was confirmed that this component is only used in locations where facilityId is available, so making it required (non-optional) is appropriate.


167-203: Conditional rendering for delete button is correctly implemented.

The code correctly implements the conditions from the PR requirements:

  1. It prevents deletion of locations with children (!location.has_children)
  2. It prevents deletion of locations currently in use (!location.current_encounter)

This implementation aligns with both frontend and backend requirements as discussed in the PR comments.


165-166: Change to justify-between is appropriate for the new layout.

The modification to use justify-between instead of the previous layout is appropriate for the new UI that includes both the delete button and view details button.


50-55: Update is consistent with the component's usage.

The addition of facilityId to the component parameters is consistent with the described changes in the AI summary, where this prop is now passed from the LocationView component.


36-36: Import change to include buttonVariants is appropriate.

The import now includes buttonVariants which is used for styling buttons in the AlertDialog, making this a necessary and appropriate change.


67-67:

✅ Verification successful

Verify translation key exists.

Ensure that the "location_removed_successfully" translation key exists in the localization files for all supported languages.


🏁 Script executed:

#!/bin/bash
# Check for the translation key in localization files

echo "Checking for 'location_removed_successfully' translation key..."
# Look for the key in locale files
fd -e json -t f "en.json" | xargs rg -l "location_removed_successfully"

Length of output: 234


Translation Key Verified
The translation key location_removed_successfully was successfully located in public/locale/en.json. Please ensure that if additional locales are supported (e.g., French, Spanish, etc.), this key is also defined in their corresponding localization files.


22-22:

❓ Verification inconclusive

Verify CareIcon integration.

Ensure that the CareIcon component correctly renders the trash icon. Verify that the "l-trash" icon exists in the icon library and displays as expected.

Also applies to: 170-172


🏁 Script executed:

#!/bin/bash
# Check if the l-trash icon is defined in the icon library

echo "Checking for l-trash icon in the CareIcon component..."
# Look for l-trash in icon definitions
fd -t f "CareIcon" | xargs rg -l "l-trash"

Length of output: 159


Attention: Verify the l-trash Icon Integration in CareIcon

  • Automated scans did not return any evidence of an l-trash definition in the CareIcon component.
  • Please manually verify that the icon library includes a definition for l-trash so that the CareIcon component correctly renders the trash icon.
  • Confirm the integration in both the file src/pages/Facility/settings/locations/components/LocationCard.tsx at line 22 and at lines 170–172.

@rithviknishad
Copy link
Member

not followed merge checklist

@rithviknishad
Copy link
Member

rithviknishad commented Mar 5, 2025

@DonXavierdev update the PR's body

@DonXavierdev
Copy link
Contributor Author

@rithviknishad
Screenshot 2025-03-05 at 12 15 06 PM

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.

image

Not consistent with the button placement.

@DonXavierdev DonXavierdev requested a review from Jacobjeevan March 6, 2025 08:34
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

59-69: 🛠️ Refactor suggestion

Add error handling for failed deletions.

While the success case is well-handled with query invalidation and toast notification, there's no error handling for failed deletions.

Add error handling to provide feedback when deletion fails:

 const { mutate: removeLocation } = useMutation({
   mutationFn: mutate(locationApi.delete, {
     pathParams: { facility_id: facilityId, id: location.id },
   }),
   onSuccess: () => {
     queryClient.invalidateQueries({
       queryKey: ["locations", facilityId],
     });
     toast.success(t("location_removed_successfully"));
   },
+  onError: (error) => {
+    toast.error(error instanceof Error ? error.message : "Failed to remove location");
+  },
 });
🧹 Nitpick comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

167-209: Implementation correctly restricts deletion.

The code properly implements restrictions on location deletion based on PR requirements - locations with children or currently in use cannot be deleted.

Minor improvements:

  1. The Button variant is "white" but would be more consistent with destructive actions if changed to "destructive":
 <Button
-  className={cn(buttonVariants({ variant: "white" }))}
+  className={cn(buttonVariants({ variant: "destructive" }))}
 >
   <CareIcon icon="l-trash" />
 </Button>
  1. The unnecessary div wrapper can be removed:
-            {!location.has_children && !location.current_encounter && (
-              <div>
-                <AlertDialog>
+            {!location.has_children && !location.current_encounter && (
+              <AlertDialog>
                 <AlertDialogTrigger asChild>
                   <Button
                     className={cn(buttonVariants({ variant: "white" }))}
                   >
                     <CareIcon icon="l-trash" />
                   </Button>
                 </AlertDialogTrigger>
                 <AlertDialogContent>
                   <AlertDialogHeader>
                     <AlertDialogTitle>
                       {t("remove")} {location.name}
                     </AlertDialogTitle>
                     <AlertDialogDescription>
                       {t("are_you_sure_want_to_delete", {
                         name: location.name,
                       })}
                     </AlertDialogDescription>
                   </AlertDialogHeader>
                   <AlertDialogFooter>
                     <AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
                     <AlertDialogAction
                       onClick={() =>
                         removeLocation({
                           pathParams: {
                             facility_id: facilityId,
                             id: location.id,
                           },
                         })
                       }
                       className={cn(
                         buttonVariants({ variant: "destructive" }),
                       )}
                     >
                       {t("remove")}
                     </AlertDialogAction>
                   </AlertDialogFooter>
                 </AlertDialogContent>
-                </AlertDialog>
-              </div>
+              </AlertDialog>
             )}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3a8e8b4 and 76b3f66.

📒 Files selected for processing (1)
  • src/pages/Facility/settings/locations/components/LocationCard.tsx (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Test
  • GitHub Check: lint
  • GitHub Check: CodeQL-Build
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
🔇 Additional comments (2)
src/pages/Facility/settings/locations/components/LocationCard.tsx (2)

210-220: View details button correctly positioned.

The view details button is properly positioned with ml-auto to ensure it appears on the right side of the flex container, maintaining good UI layout when the delete button is not present.


47-47: Correctly added facilityId parameter.

The facilityId parameter is properly defined as a required string in the Props interface, which is appropriate since it's needed for the delete operation.

Copy link
Member

@yash-learner yash-learner left a comment

Choose a reason for hiding this comment

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

LGTM

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

github-actions bot commented Mar 7, 2025

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

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/pages/Facility/settings/locations/components/LocationCard.tsx (1)

46-56: 🛠️ Refactor suggestion

Add error handling for failed deletions.

While the success case is well-handled with query invalidation and toast notification, there's no error handling for failed deletions. Users won't receive feedback if the location removal fails.

const { mutate: removeLocation } = useMutation({
  mutationFn: mutate(locationApi.delete, {
    pathParams: { facility_id: facilityId, id: location.id },
  }),
  onSuccess: () => {
    queryClient.invalidateQueries({
      queryKey: ["locations", facilityId],
    });
    toast.success(t("location_removed_successfully"));
  },
+  onError: (error) => {
+    toast.error(error instanceof Error ? error.message : t("failed_to_remove_location"));
+  },
});
🧹 Nitpick comments (4)
src/pages/Facility/settings/locations/components/LocationCard.tsx (4)

124-128: Use consistent variant for destructive actions.

The delete button uses the "white" variant, but it's a destructive action. For better UX, consider using the "destructive" variant or at least a consistent visual style for all delete-related buttons.

<Button
-  className={cn(buttonVariants({ variant: "white" }))}
+  className={cn(buttonVariants({ variant: "destructive" }))}
>
  <CareIcon icon="l-trash" />
</Button>

127-128: Improve accessibility for icon-only buttons.

The delete button only contains an icon without text, which might not be accessible to all users. Add an aria-label for screen readers.

<Button
  className={cn(buttonVariants({ variant: "white" }))}
+  aria-label={t("remove_location")}
>
  <CareIcon icon="l-trash" />
</Button>

144-151: Optimize onClick handler with useCallback.

The onClick handler is recreated on every render. Consider using useCallback to optimize performance, especially for complex UIs with many cards.

+const handleDelete = useCallback(() => {
+  removeLocation({
+    pathParams: {
+      facility_id: facilityId,
+      id: location.id,
+    },
+  });
+}, [facilityId, location.id, removeLocation]);

// Then in the JSX:
<AlertDialogAction
-  onClick={() =>
-    removeLocation({
-      pathParams: {
-        facility_id: facilityId,
-        id: location.id,
-      },
-    })
-  }
+  onClick={handleDelete}
  className={cn(
    buttonVariants({ variant: "destructive" }),
  )}
>

Don't forget to add the import:

-import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { useCallback } from "react";

1-5: Order imports alphabetically.

For better code organization, consider ordering imports alphabetically within their groups.

import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ChevronRight, Folder, FolderOpen, PenLine } from "lucide-react";
import { Link } from "raviger";
+import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 76b3f66 and 7bd89ea.

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

120-162: Good work on implementing deletion restrictions.

You've correctly implemented the business logic to prevent deletion of locations that:

  1. Have child locations (!location.has_children)
  2. Are currently in use (!location.current_encounter)

This aligns with the requirements discussed in the PR comments and ensures data integrity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changes required merge conflict pull requests with merge conflict
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Delete/Archive Button for Facility Locations
6 participants