admin/app/(dashboard)/bookings/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

149 lines
3.9 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 { formatPersonName, coerceToString } from '@/helpers'
import { formatIranianMobile, formatPersianDate } from '@/lib/formatters'
import { BOOKING_STATUS_FILTER_ITEMS, getBookingStatus } from '@/constants/status'
import { API_ROUTES } from '@/services/config'
interface BookingUser {
id: string
mobile: string
firstName: string | null
lastName: string | null
}
interface BookingEvent {
id: string
title: string
startsAt: string
status: string
organizerId: string
}
const columns: PaginationListColumnType[] = [
{
field: 'bookingCode',
label: 'کد رزرو',
filterable: false,
sortable: false,
type: 'text',
},
{
field: 'userId',
label: 'مهمان',
filterable: true,
sortable: false,
type: 'text',
},
{
field: 'eventId',
label: 'رویداد',
filterable: true,
sortable: false,
type: 'text',
},
{
field: 'status',
label: 'وضعیت',
filterable: true,
sortable: true,
type: 'select',
filterItems: BOOKING_STATUS_FILTER_ITEMS,
},
{
field: 'checkedInAt',
label: 'چک‌این',
filterable: false,
sortable: false,
type: 'date',
},
{
field: 'createdAt',
label: 'تاریخ ثبت',
filterable: false,
sortable: true,
type: 'date',
},
{
field: 'actions',
label: 'عملیات',
},
]
// Platform-wide, read-only admin bookings list. No write actions here by
// design — cancel/check-in/refund stay on their existing flows; the view
// button jumps to the guest's user-detail page (رزروها tab).
const BookingsPage = () => {
return (
<section className="h-full w-full text-right">
<PageNavbar pageTitle="رزروها" />
<div className="admin-page-container">
<PaginatedList
columns={columns}
url={API_ROUTES.BOOKINGS.ADMIN_LIST}
>
{{
userId: (row) => {
const user = row.user as BookingUser | undefined
if (!user) return '—'
const name = formatPersonName(user.firstName ?? undefined, user.lastName ?? undefined)
return (
<div className="flex flex-col gap-1">
<span>{name}</span>
<span
className="text-xs text-tertiary-300"
dir="ltr"
>
{formatIranianMobile(user.mobile)}
</span>
</div>
)
},
eventId: (row) => {
const event = row.event as BookingEvent | undefined
return event?.title ?? '—'
},
status: (_row, cellValue) => {
const { label, chipColor } = getBookingStatus(coerceToString(cellValue))
return (
<StatusChip
chipColor={chipColor}
label={label}
/>
)
},
checkedInAt: (_row, cellValue) => formatPersianDate(cellValue),
createdAt: (_row, cellValue) => formatPersianDate(cellValue),
actions: (row) => {
const user = row.user as BookingUser | undefined
if (!user) return '—'
return (
<AdminTableViewButton
label="مشاهده کاربر"
mode="navigate"
to={APP_ROUTES.USER_DETAIL(user.id)}
/>
)
},
}}
</PaginatedList>
</div>
</section>
)
}
export default BookingsPage