Skip to content

Commit

Permalink
Add questionnaire delete confirmation
Browse files Browse the repository at this point in the history
  • Loading branch information
olimsaidov committed Nov 1, 2024
1 parent f8a7733 commit 6194273
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 14 deletions.
66 changes: 66 additions & 0 deletions aidbox-forms-smart-launch/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions aidbox-forms-smart-launch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.1",
Expand Down
31 changes: 31 additions & 0 deletions aidbox-forms-smart-launch/src/components/indefinite-progress.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Progress } from "@/ui/progress.jsx";
import { useEffect, useRef, useState } from "react";

export const IndefiniteProgress = ({ active }) => {
const [value, setValue] = useState(0);
const prevActive = useRef(false);

useEffect(() => {
if (active && !prevActive.current) {
const interval = setInterval(() => {
setValue((prev) => {
if (prev >= 99.8) return prev; // Safeguard to avoid reaching 100

// Calculate a smaller increment as the value gets closer to 100
const progressIncrease = (100 - prev) / 30; // Smaller divisor means faster slowing as it approaches 100
return prev + progressIncrease;
});
}, 100);

return () => clearInterval(interval);
}

if (!active && prevActive.current) {
setValue(100);
}

prevActive.current = active;
}, [active]);

return <Progress value={value} />;
};
62 changes: 48 additions & 14 deletions aidbox-forms-smart-launch/src/pages/questionnaires.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/ui/dropdown-menu";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/ui/alert-dialog";
import {
ChevronDown,
Copy,
Expand Down Expand Up @@ -167,27 +177,21 @@ export const Questionnaires = () => {

const deleteQuestionnaireMutation = useMutation({
mutationFn: (questionnaire) => deleteQuestionnaire(client, questionnaire),
onMutate: async (questionnaire) => {
onSuccess: async (_, questionnaire) => {
await queryClient.cancelQueries({ queryKey: currentQueryKey });
const previousData = queryClient.getQueryData(currentQueryKey);

queryClient.setQueryData(currentQueryKey, (data) => ({
...data,
entry: data.entry?.filter((x) => x.resource.id !== questionnaire.id),
}));

return { previousData };
},
onSuccess: () => {
toast({
title: "Questionnaire deleted",
description: `Questionnaire deleted successfully`,
});
},

onError: (err, newTodo, context) => {
queryClient.setQueryData(currentQueryKey, context.previousData);

toast({
variant: "destructive",
title: "Delete questionnaire",
Expand All @@ -208,6 +212,7 @@ export const Questionnaires = () => {
})) || [];

const [previewingQuestionnaire, setPreviewingQuestionnaire] = useState(null);
const [deletingQuestionnaire, setDeletingQuestionnaire] = useState(null);

const columns = [
{
Expand All @@ -233,6 +238,7 @@ export const Questionnaires = () => {
(createQuestionnaireMutation.isPending &&
createQuestionnaireMutation.variables.questionnaire.id ===
questionnaire.id) ||
questionnaire.id === deleteQuestionnaireMutation.variables?.id ||
questionnaire.id === undefined; // optimistically importing questionnaire

return loading ? (
Expand Down Expand Up @@ -262,12 +268,12 @@ export const Questionnaires = () => {
onClick={() => setPreviewingQuestionnaire(questionnaire.id)}
>
<Eye />
View questionnaire
Preview
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link to={`/questionnaires/${questionnaire.id}`}>
<Edit />
Edit questionnaire
Edit
</Link>
</DropdownMenuItem>
{source === "library" && (
Expand All @@ -280,18 +286,16 @@ export const Questionnaires = () => {
}}
>
<Import />
Import questionnaire
Import
</DropdownMenuItem>
)}
{source !== "library" && (
<DropdownMenuItem
className="text-destructive focus:text-destructive"
onClick={() => {
deleteQuestionnaireMutation.mutate(questionnaire);
}}
onClick={() => setDeletingQuestionnaire(questionnaire)}
>
<Trash2 />
Delete questionnaire
Delete
</DropdownMenuItem>
)}
<DropdownMenuItem
Expand Down Expand Up @@ -346,6 +350,36 @@ export const Questionnaires = () => {

<Pagination currentPage={currentPage} totalPages={totalPages} />

<AlertDialog
onOpenChange={(open) => {
if (!open) {
setDeletingQuestionnaire(null);
}
}}
open={!!deletingQuestionnaire}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. Proceeding will permanently delete
this questionnaire from the system.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
deleteQuestionnaireMutation.mutate(deletingQuestionnaire);
setDeletingQuestionnaire(null);
}}
>
Continue
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>

<Dialog
onOpenChange={(open) => {
if (!open) {
Expand Down
97 changes: 97 additions & 0 deletions aidbox-forms-smart-launch/src/ui/alert-dialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"

import { cn } from "@/lib/utils"
import { buttonVariants } from "@/ui/button"

const AlertDialog = AlertDialogPrimitive.Root

const AlertDialogTrigger = AlertDialogPrimitive.Trigger

const AlertDialogPortal = AlertDialogPrimitive.Portal

const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref} />
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName

const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props} />
</AlertDialogPortal>
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName

const AlertDialogHeader = ({
className,
...props
}) => (
<div
className={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
{...props} />
)
AlertDialogHeader.displayName = "AlertDialogHeader"

const AlertDialogFooter = ({
className,
...props
}) => (
<div
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...props} />
)
AlertDialogFooter.displayName = "AlertDialogFooter"

const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title ref={ref} className={cn("text-lg font-semibold", className)} {...props} />
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName

const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props} />
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName

const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName

const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
{...props} />
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName

export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}
21 changes: 21 additions & 0 deletions aidbox-forms-smart-launch/src/ui/progress.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from "react"
import * as ProgressPrimitive from "@radix-ui/react-progress"

import { cn } from "@/lib/utils"

const Progress = React.forwardRef(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
className
)}
{...props}>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }} />
</ProgressPrimitive.Root>
))
Progress.displayName = ProgressPrimitive.Root.displayName

export { Progress }

0 comments on commit 6194273

Please sign in to comment.