admin/app/(dashboard)/contact-messages/page.tsx
alisaza 96603d31f7 feat(alert-modal): integrate alert modal for confirmation actions
Enhanced various components to utilize the alert modal for user confirmations before executing critical actions. This includes marking messages as read, saving notification rules, restoring reviews, sending replies, changing ticket statuses, and managing event commissions. The integration improves user experience by ensuring actions are intentional and provides clear feedback on the outcomes.
2026-09-13 10:14:18 +03:30

209 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState } from 'react'
import type { PaginationListColumnType } from '@/types'
import PaginatedList from '@/components/PaginatedList'
import PageNavbar from '@/components/layouts/PageNavbar'
import Button from '@/components/formElements/Button'
import FileCheckIcon from '@/components/icons/FileCheckIcon'
import Modal from '@/components/modals/Modal'
import AdminTableActions from '@/components/ui/AdminTableActions'
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'
import { API_ROUTES } from '@/services/config'
interface ContactMessageRow {
id: string
fullName: string
mobile: string
subject: string
message: string
readAt?: string | null
readBy?: string | null
createdAt: string
[key: string]: unknown
}
const columns: PaginationListColumnType[] = [
{
field: 'fullName',
label: 'نام',
filterable: false,
sortable: false,
},
{
field: 'mobile',
label: 'شماره تماس',
filterable: false,
sortable: false,
},
{
field: 'subject',
label: 'موضوع',
filterable: false,
sortable: false,
},
{
field: 'message',
label: 'پیام',
filterable: false,
sortable: false,
},
{
field: 'readStatus',
label: 'وضعیت',
filterable: true,
sortable: false,
type: 'select',
filterItems: [
{ code: 'unread', name: 'خوانده‌نشده' },
{ code: 'read', name: 'خوانده‌شده' },
],
},
{
field: 'createdAt',
label: 'تاریخ ثبت',
filterable: false,
sortable: true,
},
{
field: 'actions',
label: 'عملیات',
},
]
const ContactMessagesPage = () => {
const { showAlert } = useAlertModal()
const { pendingId, runAction } = useAdminMutation({ url: API_ROUTES.CONTACT_MESSAGES.ADMIN_LIST })
const [selected, setSelected] = useState<ContactMessageRow | null>(null)
const handleMarkRead = (message: ContactMessageRow) => {
showAlert('این پیام به‌عنوان خوانده‌شده علامت‌گذاری شود؟', () =>
runAction(message.id, () => axiosInstance.patch(API_ROUTES.CONTACT_MESSAGES.ADMIN_READ(message.id)), 'پیام خوانده شد')
)
}
return (
<section className="h-full w-full text-right">
<PageNavbar pageTitle="پیام‌های تماس" />
<div className="admin-page-container">
<PaginatedList
columns={columns}
url={API_ROUTES.CONTACT_MESSAGES.ADMIN_LIST}
>
{{
fullName: (_row, cellValue) => coerceToString(cellValue),
mobile: (_row, cellValue) => <span dir="ltr">{formatIranianMobile(coerceToString(cellValue))}</span>,
subject: (_row, cellValue) => coerceToString(cellValue),
message: (row, cellValue) => {
const message = row as ContactMessageRow
const body = coerceToString(cellValue)
return (
<div className="flex flex-col gap-1">
<span className="text-sm">{truncateValue(body)}</span>
{body.length > 80 ? (
<Button
className="w-fit"
size="sm"
variant="light"
onClick={() => {
setSelected(message)
}}
>
نمایش کامل
</Button>
) : null}
</div>
)
},
readStatus: (row) => {
const message = row as ContactMessageRow
const { label, chipColor } = getBooleanStatus(Boolean(message.readAt), {
true: 'خوانده‌شده',
false: 'خوانده‌نشده',
})
return (
<StatusChip
chipColor={chipColor}
label={label}
/>
)
},
createdAt: (_row, cellValue) => formatPersianDate(cellValue),
actions: (row) => {
const message = row as ContactMessageRow
return (
<AdminTableActions>
<AdminTableViewButton
label="مشاهده پیام"
mode="open-detail"
onClick={() => {
setSelected(message)
}}
/>
{message.readAt ? null : (
<Button
iconOnly
aria-label="علامت‌گذاری به‌عنوان خوانده‌شده"
isLoading={pendingId === message.id}
size="sm"
variant="light"
onClick={() => {
handleMarkRead(message)
}}
>
<FileCheckIcon className="size-5" />
</Button>
)}
</AdminTableActions>
)
},
}}
</PaginatedList>
</div>
<Modal
hideFooter
isOpen={Boolean(selected)}
size="lg"
title={selected?.subject ?? 'پیام تماس'}
onOpenChange={(open) => {
if (!open) setSelected(null)
}}
>
{selected ? (
<div className="space-y-4 text-sm">
<div>
<p className="font-bold">فرستنده</p>
<p className="mt-1">{selected.fullName}</p>
<p
className="mt-1 text-text-muted"
dir="ltr"
>
{formatIranianMobile(selected.mobile)}
</p>
</div>
<div>
<p className="font-bold">پیام</p>
<p className="mt-1 whitespace-pre-wrap leading-relaxed">{selected.message}</p>
</div>
<p className="text-text-muted">{formatPersianDate(selected.createdAt)}</p>
</div>
) : null}
</Modal>
</section>
)
}
export default ContactMessagesPage