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.
209 lines
6.4 KiB
TypeScript
209 lines
6.4 KiB
TypeScript
'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
|