233 lines
7.3 KiB
TypeScript
233 lines
7.3 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo } from "react";
|
|
import { type ColumnDef } from "@tanstack/react-table";
|
|
import { ArrowUpDown, Filter, MoreVertical } from "lucide-react";
|
|
|
|
import { Button } from "@/app/_components/ui/button";
|
|
import { Checkbox } from "@/app/_components/ui/checkbox";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuCheckboxItem,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/app/_components/ui/dropdown-menu";
|
|
import {
|
|
Avatar,
|
|
AvatarFallback,
|
|
AvatarImage,
|
|
} from "@/app/_components/ui/avatar";
|
|
|
|
import { User } from "@/types/user";
|
|
|
|
interface ColumnOptions {
|
|
openEditSheet: (user: User) => void;
|
|
handleRoleFilter: (role: string) => void;
|
|
handleStatusFilter: (status: string) => void;
|
|
}
|
|
|
|
export const useColumns = ({
|
|
openEditSheet,
|
|
handleRoleFilter,
|
|
handleStatusFilter,
|
|
}: ColumnOptions) => {
|
|
// Use useMemo to prevent unnecessary re-creation of columns array
|
|
const columns = useMemo<ColumnDef<User>[]>(
|
|
() => [
|
|
{
|
|
id: "select",
|
|
header: ({ table }) => (
|
|
<Checkbox
|
|
checked={
|
|
table.getIsAllPageRowsSelected() ||
|
|
(table.getIsSomePageRowsSelected() && "indeterminate")
|
|
}
|
|
onCheckedChange={(value) =>
|
|
table.toggleAllPageRowsSelected(!!value)
|
|
}
|
|
aria-label="Select all"
|
|
/>
|
|
),
|
|
cell: ({ row }) => (
|
|
<Checkbox
|
|
checked={row.getIsSelected()}
|
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
|
aria-label="Select row"
|
|
/>
|
|
),
|
|
enableSorting: false,
|
|
enableHiding: false,
|
|
},
|
|
{
|
|
accessorKey: "usersColoumn",
|
|
header: ({ column }) => {
|
|
return (
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() =>
|
|
column.toggleSorting(column.getIsSorted() === "asc")
|
|
}
|
|
>
|
|
Users
|
|
<ArrowUpDown className="ml-2 h-4 w-4" />
|
|
</Button>
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
const user = row.original;
|
|
return (
|
|
<div className="flex items-center space-x-4">
|
|
<Avatar>
|
|
<AvatarImage
|
|
src={user.avatar}
|
|
alt={`${user.firstName} ${user.lastName}`}
|
|
/>
|
|
<AvatarFallback>
|
|
{user.firstName[0]}
|
|
{user.lastName[0]}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<div className="font-medium">
|
|
{user.firstName} {user.lastName}
|
|
</div>
|
|
<div className="text-sm text-muted-foreground">
|
|
{user.email}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
filterFn: (row, id, filterValue) => {
|
|
const searchTerm = filterValue.toLowerCase();
|
|
const user = row.original;
|
|
return (
|
|
user.firstName.toLowerCase().includes(searchTerm) ||
|
|
user.lastName.toLowerCase().includes(searchTerm) ||
|
|
user.email.toLowerCase().includes(searchTerm)
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: "role",
|
|
header: ({ column }) => {
|
|
return (
|
|
<div className="flex items-center space-x-2">
|
|
<span>Role</span>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
|
<Filter className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>Filter by Role</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem onClick={() => handleRoleFilter("all")}>
|
|
All
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => handleRoleFilter("admin")}>
|
|
Admin
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => handleRoleFilter("staff")}>
|
|
Staff
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => handleRoleFilter("user")}>
|
|
User
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: "status",
|
|
header: ({ column }) => {
|
|
return (
|
|
<div className="flex items-center space-x-2">
|
|
<span>Status</span>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
|
<Filter className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>Filter by Status</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem onClick={() => handleStatusFilter("all")}>
|
|
All
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => handleStatusFilter("active")}
|
|
>
|
|
Active
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => handleStatusFilter("inactive")}
|
|
>
|
|
Inactive
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
);
|
|
},
|
|
cell: ({ row }) => {
|
|
const status = row.getValue("status") as string;
|
|
return (
|
|
<div
|
|
className={`capitalize ${status === "active" ? "text-green-600" : "text-red-600"}`}
|
|
>
|
|
{status}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: "lastSignedIn",
|
|
header: "Last Sign In",
|
|
},
|
|
{
|
|
id: "actions",
|
|
cell: ({ row }) => {
|
|
const user = row.original;
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="h-8 w-8 p-0">
|
|
<span className="sr-only">Open menu</span>
|
|
<MoreVertical className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
<DropdownMenuItem
|
|
onClick={() => navigator.clipboard.writeText(user.id)}
|
|
>
|
|
Copy user ID
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem onClick={() => openEditSheet(user)}>
|
|
Edit user
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="text-red-600">
|
|
Delete user
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
},
|
|
},
|
|
],
|
|
[openEditSheet, handleRoleFilter, handleStatusFilter]
|
|
);
|
|
|
|
return columns;
|
|
};
|