167 lines
4.9 KiB
TypeScript
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>
|
|
)
|
|
} |