admin/app/(dashboard)/users/page.tsx
alisaza 280eb14ee6 feat(users): expand admin users table with city, gender, wallet, and roles
Show city, gender, wallet balance, and guest/host/admin userType with
matching filters while keeping identity status for host verification.
2026-09-07 11:29:29 +03:30

199 lines
5.4 KiB
TypeScript

'use client'
import { useEffect, useMemo, useState } from 'react'
import { coerceToString } from '@/helpers'
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 axiosInstance from '@/config/axios'
import { APP_ROUTES } from '@/constants/routes'
import {
getIdentityStatus,
getUserAccountStatus,
getUserGender,
getUserType,
IDENTITY_STATUS_FILTER_ITEMS,
USER_ACCOUNT_STATUS_FILTER_ITEMS,
USER_GENDER_FILTER_ITEMS,
USER_TYPE_FILTER_ITEMS,
} from '@/constants/status'
import { formatToman } from '@/features/admin-users/adminUserDetailUi'
import { formatIranianMobile, formatPersianDate } from '@/lib/formatters'
import { API_ROUTES } from '@/services/config'
interface NamedEntity {
id: number | string
name: string
}
interface UserListRow {
id: string
cityName?: string | null
[key: string]: unknown
}
interface FilterOption {
code: string
name: string
}
const toSelectFilterItems = (items: NamedEntity[]): FilterOption[] => items.map((item) => ({ code: String(item.id), name: item.name }))
const extractApiList = <T,>(raw: unknown): T[] => {
if (Array.isArray(raw)) return raw as T[]
if (typeof raw === 'object' && raw !== null && 'data' in raw) {
const data = raw.data
if (Array.isArray(data)) return data as T[]
}
return []
}
const UsersPage = () => {
const [cityFilterItems, setCityFilterItems] = useState<FilterOption[]>([])
useEffect(() => {
const loadCities = async () => {
try {
const citiesRes = await axiosInstance.get(API_ROUTES.GEOGRAPHY.ALL_CITIES)
setCityFilterItems(toSelectFilterItems(extractApiList<NamedEntity>(citiesRes.data)))
} catch {
setCityFilterItems([])
}
}
void loadCities()
}, [])
const columns: PaginationListColumnType[] = useMemo(
() => [
{
field: 'firstName',
label: 'نام',
filterable: true,
type: 'text',
sortable: false,
},
{
field: 'lastName',
label: 'نام خانوادگی',
filterable: true,
type: 'text',
sortable: false,
},
{
field: 'mobile',
label: 'موبایل',
filterable: true,
type: 'text',
sortable: true,
},
{
field: 'cityId',
label: 'شهر',
filterable: true,
type: 'select',
sortable: false,
filterItems: cityFilterItems,
},
{
field: 'gender',
label: 'جنسیت',
filterable: true,
type: 'select',
sortable: false,
filterItems: USER_GENDER_FILTER_ITEMS,
},
{
field: 'userType',
label: 'نقش',
filterable: true,
type: 'select',
sortable: false,
filterItems: USER_TYPE_FILTER_ITEMS,
},
{
field: 'walletBalance',
label: 'موجودی کیف پول',
filterable: true,
type: 'inputFromTo',
sortable: false,
},
{
field: 'status',
label: 'وضعیت حساب',
filterable: true,
type: 'select',
sortable: true,
filterItems: USER_ACCOUNT_STATUS_FILTER_ITEMS,
},
{
field: 'identityStatus',
label: 'احراز هویت',
filterable: true,
type: 'select',
sortable: false,
filterItems: IDENTITY_STATUS_FILTER_ITEMS,
},
{
field: 'createdAt',
label: 'تاریخ ثبت نام',
filterable: true,
sortable: true,
type: 'dateFromTo',
},
{
field: 'lastLoginAt',
label: 'آخرین ورود',
filterable: true,
sortable: true,
type: 'dateFromTo',
},
{
field: 'actions',
label: 'عملیات',
},
],
[cityFilterItems]
)
return (
<section className="h-full w-full text-right">
<PageNavbar pageTitle="کاربران" />
<div className="admin-page-container">
<PaginatedList
columns={columns}
url={API_ROUTES.USERS.ADMIN_LIST}
>
{{
mobile: (_row, cellValue) => formatIranianMobile(cellValue),
cityId: (row) => (row as UserListRow).cityName || '—',
gender: (_row, cellValue) => <StatusChip {...getUserGender(coerceToString(cellValue))} />,
userType: (_row, cellValue) => <StatusChip {...getUserType(coerceToString(cellValue))} />,
walletBalance: (_row, cellValue) => formatToman(cellValue),
status: (_row, cellValue) => <StatusChip {...getUserAccountStatus(coerceToString(cellValue))} />,
identityStatus: (_row, cellValue) => <StatusChip {...getIdentityStatus(coerceToString(cellValue))} />,
lastLoginAt: (_row, cellValue) => formatPersianDate(cellValue),
createdAt: (_row, cellValue) => formatPersianDate(cellValue),
actions: (row) => (
<AdminTableViewButton
label="مشاهده کاربر"
mode="navigate"
to={APP_ROUTES.USER_DETAIL(String(row.id))}
/>
),
}}
</PaginatedList>
</div>
</section>
)
}
export default UsersPage