Show city, gender, wallet balance, and guest/host/admin userType with matching filters while keeping identity status for host verification.
199 lines
5.4 KiB
TypeScript
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
|