admin/app/(dashboard)/payments/page.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

197 lines
5.1 KiB
TypeScript

'use client'
import type { PaginationListColumnType } from '@/types'
import PaginatedList from '@/components/PaginatedList'
import StatusChip from '@/components/ui/StatusChip'
import PageNavbar from '@/components/layouts/PageNavbar'
import AdminTableViewButton from '@/components/ui/AdminTableViewButton'
import { APP_ROUTES } from '@/constants/routes'
import { formatCurrency, formatPersonName, coerceToString } from '@/helpers'
import { formatIranianMobile, formatPersianDate } from '@/lib/formatters'
import { getPaymentMethod, getPaymentStatus, PAYMENT_METHOD_FILTER_ITEMS, PAYMENT_STATUS_FILTER_ITEMS } from '@/constants/status'
import { API_ROUTES } from '@/services/config'
interface PaymentUser {
id: string
mobile: string
firstName: string | null
lastName: string | null
}
const columns: PaginationListColumnType[] = [
{
field: 'paymentCode',
label: 'کد پرداخت',
filterable: false,
sortable: false,
type: 'text',
},
{
field: 'user',
label: 'کاربر',
filterable: false,
sortable: false,
},
{
field: 'eventTitle',
label: 'رویداد',
filterable: false,
sortable: false,
type: 'text',
},
{
field: 'bookingCode',
label: 'کد رزرو',
filterable: false,
sortable: false,
type: 'text',
},
{
field: 'method',
label: 'روش پرداخت',
filterable: true,
sortable: false,
type: 'select',
filterItems: PAYMENT_METHOD_FILTER_ITEMS,
},
{
field: 'gatewayProvider',
label: 'درگاه',
filterable: false,
sortable: false,
type: 'text',
},
{
field: 'gatewayTrackingId',
label: 'شناسه درگاه',
filterable: false,
sortable: false,
type: 'text',
},
{
field: 'gatewayRefId',
label: 'RRN درگاه',
filterable: false,
sortable: false,
type: 'text',
},
{
field: 'status',
label: 'وضعیت',
filterable: true,
sortable: true,
type: 'select',
filterItems: PAYMENT_STATUS_FILTER_ITEMS,
},
{
field: 'totalAmount',
label: 'مبلغ کل',
filterable: false,
sortable: false,
},
{
field: 'paidAt',
label: 'تاریخ پرداخت',
filterable: false,
sortable: true,
type: 'date',
},
{
field: 'createdAt',
label: 'تاریخ ثبت',
filterable: true,
sortable: true,
type: 'dateFromTo',
},
{
field: 'actions',
label: 'عملیات',
},
]
const PaymentsPage = () => {
return (
<section className="h-full w-full text-right">
<PageNavbar pageTitle="پرداخت‌ها" />
<div className="admin-page-container">
<PaginatedList
columns={columns}
url={API_ROUTES.PAYMENTS.ADMIN_LIST}
>
{{
user: (row) => {
const user = row.user as PaymentUser | undefined
if (!user) return '—'
const name = formatPersonName(user.firstName ?? undefined, user.lastName ?? undefined)
return (
<div className="flex flex-col">
<span>{name}</span>
<span
className="text-xs text-tertiary-300"
dir="ltr"
>
{formatIranianMobile(user.mobile)}
</span>
</div>
)
},
method: (row, cellValue) => {
const provider = coerceToString(row.gatewayProvider)
if (provider) {
return (
<StatusChip
chipColor="default"
label={`درگاه (${provider})`}
/>
)
}
return <StatusChip {...getPaymentMethod(coerceToString(cellValue))} />
},
gatewayProvider: (_row, cellValue) => coerceToString(cellValue) || '—',
gatewayTrackingId: (_row, cellValue) => (
<span
className="font-mono text-xs"
dir="ltr"
>
{coerceToString(cellValue) || '—'}
</span>
),
gatewayRefId: (_row, cellValue) => (
<span
className="font-mono text-xs"
dir="ltr"
>
{coerceToString(cellValue) || '—'}
</span>
),
status: (_row, cellValue) => <StatusChip {...getPaymentStatus(coerceToString(cellValue))} />,
totalAmount: (_row, cellValue) => formatCurrency(Number(cellValue ?? 0)),
paidAt: (_row, cellValue) => formatPersianDate(cellValue),
createdAt: (_row, cellValue) => formatPersianDate(cellValue),
actions: (row) => {
const user = row.user as PaymentUser | undefined
if (!user) return '—'
return (
<AdminTableViewButton
label="مشاهده کاربر"
mode="navigate"
to={APP_ROUTES.USER_DETAIL(user.id)}
/>
)
},
}}
</PaginatedList>
</div>
</section>
)
}
export default PaymentsPage