diff --git a/app/(dashboard)/contact-messages/page.tsx b/app/(dashboard)/contact-messages/page.tsx index 8813031..444a8ad 100644 --- a/app/(dashboard)/contact-messages/page.tsx +++ b/app/(dashboard)/contact-messages/page.tsx @@ -13,6 +13,7 @@ import AdminTableViewButton from '@/components/ui/AdminTableViewButton' import StatusChip from '@/components/ui/StatusChip' import axiosInstance from '@/config/axios' import useAdminMutation from '@/hooks/useAdminMutation' +import useAlertModal from '@/hooks/useAlertModal' import { coerceToString } from '@/helpers' import { formatIranianMobile, formatPersianDate, truncateValue } from '@/lib/formatters' import { getBooleanStatus } from '@/constants/status' @@ -79,9 +80,16 @@ const columns: PaginationListColumnType[] = [ ] const ContactMessagesPage = () => { + const { showAlert } = useAlertModal() const { pendingId, runAction } = useAdminMutation({ url: API_ROUTES.CONTACT_MESSAGES.ADMIN_LIST }) const [selected, setSelected] = useState(null) + const handleMarkRead = (message: ContactMessageRow) => { + showAlert('این پیام به‌عنوان خوانده‌شده علامت‌گذاری شود؟', () => + runAction(message.id, () => axiosInstance.patch(API_ROUTES.CONTACT_MESSAGES.ADMIN_READ(message.id)), 'پیام خوانده شد') + ) + } + return (
@@ -150,13 +158,9 @@ const ContactMessagesPage = () => { isLoading={pendingId === message.id} size="sm" variant="light" - onClick={() => - void runAction( - message.id, - () => axiosInstance.patch(API_ROUTES.CONTACT_MESSAGES.ADMIN_READ(message.id)), - 'پیام خوانده شد' - ) - } + onClick={() => { + handleMarkRead(message) + }} > diff --git a/app/(dashboard)/notifications/_components/NotificationRulesPanel.test.tsx b/app/(dashboard)/notifications/_components/NotificationRulesPanel.test.tsx index eaa87b7..77c620a 100644 --- a/app/(dashboard)/notifications/_components/NotificationRulesPanel.test.tsx +++ b/app/(dashboard)/notifications/_components/NotificationRulesPanel.test.tsx @@ -16,7 +16,12 @@ vi.mock('@/config/axios', () => ({ patch: axiosMocks.patch, }, })) +const showAlert = vi.fn((_message: string, onConfirm?: () => unknown) => { + void onConfirm?.() +}) + vi.mock('@/lib/toast', () => ({ addToast: vi.fn() })) +vi.mock('@/hooks/useAlertModal', () => ({ default: () => ({ showAlert }) })) vi.mock('@/components/formElements/Input', () => ({ default: ({ description, @@ -109,6 +114,9 @@ describe('NotificationRulesPanel', () => { }) beforeEach(() => { + showAlert.mockImplementation((_message: string, onConfirm?: () => unknown) => { + void onConfirm?.() + }) axiosMocks.get.mockReset() axiosMocks.patch.mockReset() axiosMocks.get.mockResolvedValue({ diff --git a/app/(dashboard)/notifications/_components/NotificationRulesPanel.tsx b/app/(dashboard)/notifications/_components/NotificationRulesPanel.tsx index 4b6e539..1eb170f 100644 --- a/app/(dashboard)/notifications/_components/NotificationRulesPanel.tsx +++ b/app/(dashboard)/notifications/_components/NotificationRulesPanel.tsx @@ -10,6 +10,7 @@ import Button from '@/components/formElements/Button' import Input from '@/components/formElements/Input' import AdminState from '@/components/feedback/AdminState' import axiosInstance from '@/config/axios' +import useAlertModal from '@/hooks/useAlertModal' import { unwrapApiData, type ApiSuccessBody } from '@/services/apiResponse' import { API_ROUTES } from '@/services/config' import { extractServerErrorDetail } from '@/services/errorHandler' @@ -52,6 +53,7 @@ const isDirtyDraft = (current: RuleDraft, saved: RuleDraft | undefined) => Boolean(saved) && JSON.stringify(current) !== JSON.stringify(saved) const NotificationRulesPanel = () => { + const { showAlert } = useAlertModal() const [rules, setRules] = useState([]) const [savedDrafts, setSavedDrafts] = useState>({}) const [loading, setLoading] = useState(true) @@ -111,6 +113,12 @@ const NotificationRulesPanel = () => { } } + const confirmSave = (rule: Rule) => { + showAlert(`تغییرات قاعدهٔ «${rule.displayName}» ذخیره شود؟`, () => { + void save(rule) + }) + } + return (
@@ -142,7 +150,9 @@ const NotificationRulesPanel = () => { rule={rule} saving={saving === rule.eventKey} onChange={change} - onSave={() => void save(rule)} + onSave={() => { + confirmSave(rule) + }} /> ))}
diff --git a/app/(dashboard)/reviews/page.tsx b/app/(dashboard)/reviews/page.tsx index ddf5c99..7e9c602 100644 --- a/app/(dashboard)/reviews/page.tsx +++ b/app/(dashboard)/reviews/page.tsx @@ -115,7 +115,9 @@ const ReviewsPage = () => { } const handleRestore = (row: ReviewRow) => { - void runAction(row.id, () => axiosInstance.patch(API_ROUTES.REVIEWS.ADMIN_RESTORE(row.id)), 'نظر بازگردانده شد') + showAlert('این نظر دوباره در نمایش عمومی قرار گیرد؟', () => + runAction(row.id, () => axiosInstance.patch(API_ROUTES.REVIEWS.ADMIN_RESTORE(row.id)), 'نظر بازگردانده شد') + ) } const handleDelete = (row: ReviewRow) => { diff --git a/app/(dashboard)/support-tickets/[id]/page.tsx b/app/(dashboard)/support-tickets/[id]/page.tsx index 7c646ad..7fd2e0d 100644 --- a/app/(dashboard)/support-tickets/[id]/page.tsx +++ b/app/(dashboard)/support-tickets/[id]/page.tsx @@ -58,15 +58,39 @@ const AdminSupportTicketDetail = () => { } } + const confirmSend = () => { + if (!reply.trim()) return + + showAlert('این پاسخ ثبت و پیامک اطلاع‌رسانی برای کاربر ارسال شود؟', () => { + void send() + }) + } + + const confirmStatusChange = (nextStatus: string) => { + if (!ticket || nextStatus === ticket.status) return + + if (nextStatus === 'closed') { + showAlert( + 'این تیکت بسته شود؟ کاربر دیگر نمی‌تواند پیام بفرستد و در صورت بستن توسط ادمین، خودش نمی‌تواند دوباره باز کند.', + () => { + void changeStatus('closed') + }, + undefined, + { dangerAccept: true } + ) + + return + } + + const label = SUPPORT_ADMIN_STATUS_LABELS[nextStatus as keyof typeof SUPPORT_ADMIN_STATUS_LABELS] ?? nextStatus + + showAlert(`وضعیت تیکت به «${label}» تغییر کند؟`, () => { + void changeStatus(nextStatus) + }) + } + const confirmClose = () => { - showAlert( - 'این تیکت بسته شود؟ کاربر دیگر نمی‌تواند پیام بفرستد و در صورت بستن توسط ادمین، خودش نمی‌تواند دوباره باز کند.', - () => { - void changeStatus('closed') - }, - undefined, - { dangerAccept: true } - ) + confirmStatusChange('closed') } return ( @@ -104,7 +128,9 @@ const AdminSupportTicketDetail = () => {