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 Confirmation Pop-up to Prevent Accidental Exception Deletions #10283

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@
"archived_files": "Archived Files",
"are_non_editable_fields": "are non-editable fields",
"are_you_still_watching": "Are you still watching?",
"are_you_sure": "Are you sure?",
"are_you_sure_want_to_delete": "Are you sure you want to delete {{name}}?",
"are_you_sure_want_to_delete_this_record": "Are you sure want to delete this record?",
"are_you_sure_want_to_remove": "Are you sure you want to remove {{name}} from the patient? This action cannot be undone",
Expand Down Expand Up @@ -2075,6 +2076,7 @@
"third_party_software_licenses": "Third Party Software Licenses",
"this_action_is_irreversible": "This action is irreversible. Once a file is archived it cannot be unarchived.",
"this_file_has_been_archived": "This file has been archived and cannot be unarchived.",
"this_will_permanently_remove_the_template": "This will permanently remove the template and cannot be undone",
"time": "Time",
"time_slot": "Time Slot",
"title": "Title",
Expand Down
21 changes: 18 additions & 3 deletions src/components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";

import { cn } from "@/lib/utils";
Expand Down Expand Up @@ -97,13 +98,27 @@ const AlertDialogDescription = React.forwardRef<
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName;

const alertVariants = cva("mt-2 sm:mt-0", {
variants: {
variant: {
default: "bg-white text-gray-950 dark:bg-gray-950 dark:text-gray-50",
destructive:
"bg-red-500 text-gray-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/90",
},
},
defaultVariants: {
variant: "default",
},
});

const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> &
VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
className={cn(buttonVariants(), alertVariants({ variant }), className)}
{...props}
/>
));
Expand Down
56 changes: 47 additions & 9 deletions src/pages/Scheduling/ScheduleExceptions.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { format, parseISO } from "date-fns";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

Expand All @@ -8,6 +9,18 @@ import { cn } from "@/lib/utils";
import ColoredIndicator from "@/CAREUI/display/ColoredIndicator";
import CareIcon from "@/CAREUI/icons/CareIcon";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
modamaan marked this conversation as resolved.
Show resolved Hide resolved
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";

import Loading from "@/components/Common/Loading";
Expand Down Expand Up @@ -63,6 +76,7 @@ const ScheduleExceptionItem = (
) => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const [open, setOpen] = useState(false);

const { mutate: deleteException, isPending } = useMutation({
mutationFn: mutate(scheduleApis.exceptions.delete, {
Expand Down Expand Up @@ -112,15 +126,39 @@ const ScheduleExceptionItem = (
</span>
</div>
</div>
<Button
variant="secondary"
size="sm"
disabled={isPending}
onClick={() => deleteException(undefined)}
>
<CareIcon icon="l-minus-circle" className="text-base" />
<span className="ml-2">{t("remove")}</span>
</Button>
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger asChild>
<Button variant="secondary" size="sm" disabled={isPending}>
<CareIcon icon="l-minus-circle" className="text-base" />
<span className="ml-2">{t("remove")}</span>
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("are_you_sure")}</AlertDialogTitle>
<AlertDialogDescription>
<Alert variant="destructive" className="mt-4">
<AlertTitle>{t("warning")}</AlertTitle>
<AlertDescription>
{t("this_will_permanently_remove_the_template")}
</AlertDescription>
</Alert>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={() => {
deleteException();
setOpen(false);
}}
>
{t("confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
{/* TODO: Add this information */}
{/* <div className="px-4 py-2">
Expand Down
86 changes: 72 additions & 14 deletions src/pages/Scheduling/components/CreateScheduleTemplateSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { isAfter, isBefore, parse } from "date-fns";
import { ArrowRightIcon } from "lucide-react";
import { useQueryParams } from "raviger";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Trans } from "react-i18next";
Expand All @@ -15,6 +16,18 @@ import WeekdayCheckbox, {
DayOfWeek,
} from "@/CAREUI/interactive/WeekdayCheckbox";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { DatePicker } from "@/components/ui/date-picker";
import {
Expand Down Expand Up @@ -235,6 +248,9 @@ export default function CreateScheduleTemplateSheet({
);
};

const [openDialog, setOpenDialog] = useState(false);
const [removeIndex, setRemoveIndex] = useState<number | null>(null);

return (
<Sheet
open={qParams.sheet === "create_template"}
Expand Down Expand Up @@ -351,21 +367,63 @@ export default function CreateScheduleTemplateSheet({
{form.watch(`availabilities.${index}.name`)}
</span>
</div>
<Button
type="button"
variant="secondary"
size="sm"
className="text-gray-600 hover:text-gray-900"
onClick={() => {
const availabilities =
form.getValues("availabilities");
availabilities.splice(index, 1);
form.setValue("availabilities", availabilities);
}}
<AlertDialog
open={openDialog}
onOpenChange={setOpenDialog}
>
<CareIcon icon="l-trash" className="text-base" />
<span className="ml-2">{t("remove")}</span>
</Button>
<AlertDialogTrigger asChild>
<Button
type="button"
variant="secondary"
size="sm"
className="text-gray-600 hover:text-gray-900"
modamaan marked this conversation as resolved.
Show resolved Hide resolved
onClick={() => {
setRemoveIndex(index);
setOpenDialog(true);
}}
>
<CareIcon icon="l-trash" className="text-base" />
<span className="ml-2">{t("remove")}</span>
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("are_you_sure")}
</AlertDialogTitle>
<AlertDialogDescription>
<Alert variant="destructive" className="mt-4">
<AlertTitle>{t("warning")}</AlertTitle>
<AlertDescription>
{t(
"this_will_permanently_remove_the_template",
)}
</AlertDescription>
</Alert>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
Copy link
Contributor

Choose a reason for hiding this comment

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

Reset the state on both cancel and confirm.

<AlertDialogAction
variant={"destructive"}
onClick={() => {
const availabilities =
form.getValues("availabilities");
if (removeIndex !== null) {
availabilities.splice(removeIndex, 1);
form.setValue(
"availabilities",
availabilities,
);
}
setOpenDialog(false);
}}
>
{t("confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>

<div className="grid grid-cols-2 gap-x-6 gap-y-4">
Expand Down
109 changes: 90 additions & 19 deletions src/pages/Scheduling/components/EditScheduleTemplateSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ import WeekdayCheckbox, {
DayOfWeek,
} from "@/CAREUI/interactive/WeekdayCheckbox";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { DatePicker } from "@/components/ui/date-picker";
import {
Expand Down Expand Up @@ -126,6 +138,7 @@ const ScheduleTemplateEditor = ({
}) => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const [isDialogOpen, setDialogOpen] = useState(false);

const templateFormSchema = z
.object({
Expand Down Expand Up @@ -250,16 +263,44 @@ const ScheduleTemplateEditor = ({
</div>

<div className="flex justify-end gap-2">
<Button
type="button"
variant="outline"
onClick={() => deleteTemplate()}
disabled={isProcessing}
size="sm"
>
<Trash2Icon />
{isDeleting ? t("deleting") : t("delete")}
</Button>
<AlertDialog open={isDialogOpen} onOpenChange={setDialogOpen}>
<AlertDialogTrigger asChild>
<Button
type="button"
variant="outline"
disabled={isProcessing}
size="sm"
>
<Trash2Icon />
{isDeleting ? t("deleting") : t("delete")}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("are_you_sure")}</AlertDialogTitle>
<AlertDialogDescription>
<Alert variant="destructive" className="mt-4">
<AlertTitle>{t("warning")}</AlertTitle>
<AlertDescription>
{t("this_will_permanently_remove_the_template")}
</AlertDescription>
</Alert>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={() => {
deleteTemplate();
setDialogOpen(false);
}}
>
{t("confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<Button
variant="primary"
type="submit"
Expand Down Expand Up @@ -289,6 +330,7 @@ const AvailabilityEditor = ({
}) => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const [openDialog, setOpenDialog] = useState(false);

const { mutate: deleteAvailability, isPending: isDeleting } = useMutation({
mutationFn: mutate(scheduleApis.templates.availabilities.delete, {
Expand Down Expand Up @@ -351,15 +393,44 @@ const AvailabilityEditor = ({
</span>
</div>

<Button
variant="ghost"
size="icon"
onClick={() => deleteAvailability()}
disabled={isDeleting}
className="text-red-600 hover:text-red-700 hover:bg-red-50"
>
<CareIcon icon="l-trash" className="text-lg" />
</Button>
<AlertDialog open={openDialog} onOpenChange={setOpenDialog}>
<AlertDialogTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setOpenDialog(true)}
disabled={isDeleting}
className="text-red-600 hover:text-red-700 hover:bg-red-50"
Comment on lines +399 to +403
Copy link
Member

Choose a reason for hiding this comment

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

let's not modify classNames of buttons. looses the point of having variants and consistent designs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

should i remove this effect?

District.Admin._.CARE.-red-hover.2025-02-05.19-33-02.mp4

Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the overriding classes as Rithvik suggested. Use variants if necessary.

>
<CareIcon icon="l-trash" className="text-lg" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("are_you_sure")}</AlertDialogTitle>
<AlertDialogDescription>
<Alert variant="destructive" className="mt-4">
<AlertTitle>{t("warning")}</AlertTitle>
<AlertDescription>
{t("this_will_permanently_remove_the_template")}
</AlertDescription>
</Alert>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={() => {
deleteAvailability();
setOpenDialog(false);
}}
>
{t("confirm")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>

<div className="space-y-4">
Expand Down
Loading