fitaiProto/apps/admin/src/components/users/UserGrid.tsx
2025-11-07 20:59:56 +01:00

167 lines
4.9 KiB
TypeScript

'use client'
import React, { useState, useMemo } from 'react'
import { AgGridReact } from 'ag-grid-react'
import { ColDef } from 'ag-grid-community'
import 'ag-grid-community/styles/ag-grid.css'
import 'ag-grid-community/styles/ag-theme-alpine.css'
import { formatDate } from '@/lib/utils'
interface User {
id: string
email: string
firstName: string
lastName: string
role: string
phone?: string
createdAt: Date
client?: {
id: string
membershipType: string
membershipStatus: string
joinDate: Date
lastVisit?: Date
}
}
interface UserGridProps {
users: User[]
onUserSelect?: (user: User) => void
loading?: boolean
}
export function UserGrid({ users, onUserSelect, loading = false }: UserGridProps) {
const [selectedUser, setSelectedUser] = useState<User | null>(null)
const columnDefs: ColDef<User>[] = useMemo(() => [
{
headerName: 'Name',
valueGetter: (params) => `${params.data?.firstName} ${params.data?.lastName}`,
filter: 'agTextColumnFilter',
sortable: true,
minWidth: 150,
},
{
headerName: 'Email',
field: 'email',
filter: 'agTextColumnFilter',
sortable: true,
minWidth: 200,
},
{
headerName: 'Role',
field: 'role',
filter: 'agSetColumnFilter',
sortable: true,
cellRenderer: (params: any) => {
const roleColors = {
admin: 'bg-purple-100 text-purple-800',
trainer: 'bg-blue-100 text-blue-800',
client: 'bg-green-100 text-green-800',
}
const colorClass = roleColors[params.value as keyof typeof roleColors] || 'bg-gray-100 text-gray-800'
return `<span class="px-2 py-1 rounded text-xs font-medium ${colorClass}">${params.value}</span>`
},
minWidth: 120,
},
{
headerName: 'Phone',
field: 'phone',
filter: 'agTextColumnFilter',
sortable: true,
minWidth: 130,
},
{
headerName: 'Membership',
valueGetter: (params) => params.data?.client?.membershipType || 'N/A',
filter: 'agSetColumnFilter',
sortable: true,
cellRenderer: (params: any) => {
if (!params.value || params.value === 'N/A') return 'N/A'
const membershipColors = {
vip: 'bg-yellow-100 text-yellow-800',
premium: 'bg-blue-100 text-blue-800',
basic: 'bg-gray-100 text-gray-800',
}
const colorClass = membershipColors[params.value as keyof typeof membershipColors] || 'bg-gray-100 text-gray-800'
return `<span class="px-2 py-1 rounded text-xs font-medium ${colorClass}">${params.value}</span>`
},
minWidth: 120,
},
{
headerName: 'Status',
valueGetter: (params) => params.data?.client?.membershipStatus || 'N/A',
filter: 'agSetColumnFilter',
sortable: true,
cellRenderer: (params: any) => {
if (!params.value || params.value === 'N/A') return 'N/A'
const statusColors = {
active: 'bg-green-100 text-green-800',
inactive: 'bg-red-100 text-red-800',
suspended: 'bg-yellow-100 text-yellow-800',
}
const colorClass = statusColors[params.value as keyof typeof statusColors] || 'bg-gray-100 text-gray-800'
return `<span class="px-2 py-1 rounded text-xs font-medium ${colorClass}">${params.value}</span>`
},
minWidth: 120,
},
{
headerName: 'Join Date',
valueGetter: (params) => params.data?.client?.joinDate || params.data?.createdAt,
filter: 'agDateColumnFilter',
sortable: true,
valueFormatter: (params: any) => formatDate(new Date(params.value)),
minWidth: 120,
},
{
headerName: 'Last Visit',
valueGetter: (params) => params.data?.client?.lastVisit,
filter: 'agDateColumnFilter',
sortable: true,
valueFormatter: (params: any) => params.value ? formatDate(new Date(params.value)) : 'Never',
minWidth: 120,
},
], [])
const defaultColDef: ColDef = useMemo(() => ({
flex: 1,
resizable: true,
floatingFilter: true,
suppressMenu: true,
}), [])
const onSelectionChanged = () => {
const selectedNodes = gridRef.current?.api.getSelectedNodes()
if (selectedNodes?.length > 0) {
const user = selectedNodes[0].data
setSelectedUser(user)
onUserSelect?.(user)
}
}
const gridRef = React.useRef<AgGridReact<User>>(null)
const gridOptions = {
columnDefs,
defaultColDef,
rowData: users,
rowSelection: 'single',
onSelectionChanged,
enableRangeSelection: true,
enableCellTextSelection: true,
suppressRowClickSelection: false,
animateRows: true,
loading: loading,
pagination: true,
paginationPageSize: 20,
paginationPageSizeSelector: [10, 20, 50, 100],
}
return (
<div className="ag-theme-alpine" style={{ height: '600px', width: '100%' }}>
<AgGridReact<User> {...gridOptions} ref={gridRef} />
</div>
)
}