diff --git a/src/components/ConfirmAction.tsx b/src/components/ConfirmAction.tsx new file mode 100644 index 0000000..25a122d --- /dev/null +++ b/src/components/ConfirmAction.tsx @@ -0,0 +1,58 @@ +"use client"; + +import type { ReactNode } from "react"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; + +type ConfirmActionProps = { + trigger: ReactNode; + title: string; + description: ReactNode; + confirmLabel?: string; + cancelLabel?: string; + onConfirm: () => unknown | Promise; + disabled?: boolean; +}; + +export default function ConfirmAction({ + trigger, + title, + description, + confirmLabel = "حذف", + cancelLabel = "انصراف", + onConfirm, + disabled = false, +}: ConfirmActionProps) { + return ( + + + {trigger} + + + + {title} + {description} + + + {cancelLabel} + void onConfirm()} + > + {confirmLabel} + + + + + ); +} diff --git a/src/components/NotificationsBell.tsx b/src/components/NotificationsBell.tsx index f9bdc39..49096c5 100644 --- a/src/components/NotificationsBell.tsx +++ b/src/components/NotificationsBell.tsx @@ -1,6 +1,7 @@ "use client"; import { Bell, CheckCheck, Loader2, Trash2 } from "lucide-react"; +import ConfirmAction from "@/components/ConfirmAction"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; @@ -33,15 +34,21 @@ function NotificationItem({ )} >
- + onDelete(notification)} + trigger={ + + } + /> ) : null} - + trigger={ + + } + />
diff --git a/src/views/AdminBlogCategories.tsx b/src/views/AdminBlogCategories.tsx index 784dac5..9289eb4 100644 --- a/src/views/AdminBlogCategories.tsx +++ b/src/views/AdminBlogCategories.tsx @@ -3,6 +3,7 @@ import { useMemo, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Edit3, Loader2, Plus, RotateCcw, Trash2 } from "lucide-react"; +import ConfirmAction from "@/components/ConfirmAction"; import { useAuth } from "@/contexts/AuthContext"; import { api } from "@/lib/api"; import type * as Types from "@/lib/types"; @@ -119,7 +120,6 @@ export default function AdminBlogCategories() { }; const deleteCategory = async (category: Types.AdminCategorySchema) => { - if (!window.confirm(`دسته‌بندی «${category.name}» حذف شود؟`)) return; try { await api.deleteCategory(category.id); toast({ title: "دسته‌بندی حذف شد", variant: "success" }); @@ -197,9 +197,16 @@ export default function AdminBlogCategories() { {canDelete ? ( - + deleteCategory(category)} + trigger={ + + } + /> ) : null}
diff --git a/src/views/AdminBlogEditor.tsx b/src/views/AdminBlogEditor.tsx index 3862c10..12f6900 100644 --- a/src/views/AdminBlogEditor.tsx +++ b/src/views/AdminBlogEditor.tsx @@ -3,6 +3,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { AlertTriangle, ArrowLeft, ArrowRight, FolderUp, ImageUp, Loader2, Save, Send, Trash2 } from "lucide-react"; import { useRouter } from "next/navigation"; +import ConfirmAction from "@/components/ConfirmAction"; import Markdown from "@/components/Markdown"; import MarkdownEditor, { type MarkdownDirectionMode } from "@/components/MarkdownEditor"; import { useAuth } from "@/contexts/AuthContext"; @@ -384,10 +385,18 @@ export default function AdminBlogEditor({ postId }: Props) {
{post?.featured_image || post?.absolute_featured_image_url ? ( - + + } + /> ) : null} {canDelete ? ( - + deleteTag(tag)} + trigger={ + + } + /> ) : null}
diff --git a/src/views/AdminCoupons.tsx b/src/views/AdminCoupons.tsx index 1414b51..9aa24de 100644 --- a/src/views/AdminCoupons.tsx +++ b/src/views/AdminCoupons.tsx @@ -4,6 +4,7 @@ import * as React from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Edit3, Plus, Trash2 } from "lucide-react"; import AdminDateTimeField from "@/components/AdminDateTimeField"; +import ConfirmAction from "@/components/ConfirmAction"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; @@ -160,9 +161,17 @@ export default function AdminCoupons() { - + deleteMutation.mutate(item.id)} + disabled={deleteMutation.isPending} + trigger={ + + } + /> diff --git a/src/views/AdminEventForm.tsx b/src/views/AdminEventForm.tsx index ba2a121..7251c21 100644 --- a/src/views/AdminEventForm.tsx +++ b/src/views/AdminEventForm.tsx @@ -4,6 +4,7 @@ import * as React from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { ImagePlus, Trash2, Upload } from "lucide-react"; import AdminDateTimeField from "@/components/AdminDateTimeField"; +import ConfirmAction from "@/components/ConfirmAction"; import Markdown from "@/components/Markdown"; import MarkdownEditor from "@/components/MarkdownEditor"; import ProgressiveImage from "@/components/ProgressiveImage"; @@ -354,9 +355,17 @@ export default function AdminEventForm({ mode }: { mode: Mode }) {
{item.title} - + galleryDeleteMutation.mutate(item.id)} + disabled={galleryDeleteMutation.isPending} + trigger={ + + } + />
))} diff --git a/src/views/AdminMetaOptions.tsx b/src/views/AdminMetaOptions.tsx index eb9e9ef..3a1f271 100644 --- a/src/views/AdminMetaOptions.tsx +++ b/src/views/AdminMetaOptions.tsx @@ -3,6 +3,7 @@ import * as React from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Edit3, Plus, Trash2 } from "lucide-react"; +import ConfirmAction from "@/components/ConfirmAction"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; @@ -150,15 +151,23 @@ export default function AdminMetaOptions({ kind }: { kind: Kind }) { - + + } + /> diff --git a/src/views/Profile.tsx b/src/views/Profile.tsx index 1652c25..2206ac2 100644 --- a/src/views/Profile.tsx +++ b/src/views/Profile.tsx @@ -19,6 +19,7 @@ import { } from "lucide-react"; import AsyncSearchableCombobox from "@/components/AsyncSearchableCombobox"; import BlogThumbnail from "@/components/BlogThumbnail"; +import ConfirmAction from "@/components/ConfirmAction"; import Markdown from "@/components/Markdown"; import { Helmet } from "@/lib/helmet"; import { Link, Navigate } from "@/lib/router"; @@ -395,26 +396,32 @@ export default function Profile() { ) : null} {me?.profile_picture ? ( - { event.preventDefault(); event.stopPropagation(); - void onDeletePicture(); }} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); event.stopPropagation(); - void onDeletePicture(); } }} className="absolute bottom-1 right-1 flex h-9 w-9 items-center justify-center rounded-full bg-destructive text-destructive-foreground shadow-lg" aria-label="حذف تصویر پروفایل" > - + + } + /> ) : null} );