Files
lunarfront-app/packages/admin/.tanstack/tmp/ea12e527-9222bd0963a9701c5f47681c6fc339b0
Ryan Moon f359f92071 Fix members routing - restructure as directory for detail page to work
Move members.tsx to members/index.tsx so $memberId.tsx resolves as a
sibling route. Fixes member detail page not loading when navigating
from members list.
2026-03-28 13:30:30 -05:00

138 lines
4.6 KiB
Plaintext

import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { globalMemberListOptions, type MemberWithAccount } from '@/api/members'
import { usePagination } from '@/hooks/use-pagination'
import { DataTable, type Column } from '@/components/shared/data-table'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Search, Plus, MoreVertical, Pencil, Users } from 'lucide-react'
export const Route = createFileRoute('/_authenticated/members/')({
validateSearch: (search: Record<string, unknown>) => ({
page: Number(search.page) || 1,
limit: Number(search.limit) || 25,
q: (search.q as string) || undefined,
sort: (search.sort as string) || undefined,
order: (search.order as 'asc' | 'desc') || 'asc',
}),
component: MembersListPage,
})
function MembersListPage() {
const navigate = useNavigate()
const { params, setPage, setSearch, setSort } = usePagination()
const [searchInput, setSearchInput] = useState(params.q ?? '')
const { data, isLoading } = useQuery(globalMemberListOptions(params))
function handleSearchSubmit(e: React.FormEvent) {
e.preventDefault()
setSearch(searchInput)
}
const memberColumns: Column<MemberWithAccount>[] = [
{
key: 'memberNumber',
header: '#',
render: (row) => <span className="font-mono text-sm text-muted-foreground">{row.memberNumber ?? '-'}</span>,
},
{
key: 'last_name',
header: 'Name',
sortable: true,
render: (row) => <span className="font-medium">{row.firstName} {row.lastName}</span>,
},
{
key: 'email',
header: 'Email',
sortable: true,
render: (row) => row.email ?? <span className="text-muted-foreground">-</span>,
},
{
key: 'phone',
header: 'Phone',
render: (row) => row.phone ?? <span className="text-muted-foreground">-</span>,
},
{
key: 'accountName',
header: 'Account',
render: (row) => row.accountName ?? <span className="text-muted-foreground">-</span>,
},
{
key: 'status',
header: 'Status',
render: (row) => row.isMinor ? <Badge variant="secondary">Minor</Badge> : <Badge>Adult</Badge>,
},
{
key: 'actions',
header: '',
render: (row) => (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={(e) => e.stopPropagation()}>
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => navigate({ to: '/members/$memberId', params: { memberId: row.id } })}>
<Pencil className="mr-2 h-4 w-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate({ to: '/accounts/$accountId', params: { accountId: row.accountId } })}>
<Users className="mr-2 h-4 w-4" />
View Account
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
),
},
]
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold">Members</h1>
<Button onClick={() => navigate({ to: '/accounts/new' })}>
<Plus className="mr-2 h-4 w-4" />
New Member
</Button>
</div>
<form onSubmit={handleSearchSubmit} className="flex gap-2 max-w-sm">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search members..."
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
className="pl-9"
/>
</div>
<Button type="submit" variant="secondary">Search</Button>
</form>
<DataTable
columns={memberColumns}
data={data?.data ?? []}
loading={isLoading}
page={params.page}
totalPages={data?.pagination.totalPages ?? 1}
total={data?.pagination.total ?? 0}
sort={params.sort}
order={params.order}
onPageChange={setPage}
onSort={setSort}
onRowClick={(member) => navigate({ to: '/members/$memberId', params: { memberId: member.id } })}
/>
</div>
)
}