MIF_E31221222/sigap-website/app/protected/(admin-pages)/_components/users/users-table.tsx

586 lines
17 KiB
TypeScript

"use client";
import { useState, useCallback, useEffect } from "react";
import {
type ColumnDef,
type ColumnFiltersState,
type SortingState,
type VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
ArrowUpDown,
ChevronDown,
Plus,
MoreVertical,
Filter,
} 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 { Input } from "@/app/_components/ui/input";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/app/_components/ui/table";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/app/_components/ui/avatar";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/app/_components/ui/select";
import { EditUserSheet } from "./edit-user";
const data: User[] = [
{
id: "1",
email: "john@example.com",
firstName: "John",
lastName: "Doe",
avatar: "/placeholder.svg?height=40&width=40",
role: "admin",
status: "active",
lastSignedIn: "2024-02-26",
},
{
id: "2",
email: "jane@example.com",
firstName: "Jane",
lastName: "Smith",
avatar: "/placeholder.svg?height=40&width=40",
role: "staff",
status: "active",
lastSignedIn: "2024-02-25",
},
{
id: "3",
email: "michael@example.com",
firstName: "Michael",
lastName: "Johnson",
avatar: "/placeholder.svg?height=40&width=40",
role: "user",
status: "inactive",
lastSignedIn: "2024-02-20",
},
{
id: "4",
email: "sarah@example.com",
firstName: "Sarah",
lastName: "Williams",
avatar: "/placeholder.svg?height=40&width=40",
role: "staff",
status: "active",
lastSignedIn: "2024-02-24",
},
{
id: "5",
email: "david@example.com",
firstName: "David",
lastName: "Brown",
avatar: "/placeholder.svg?height=40&width=40",
role: "user",
status: "inactive",
lastSignedIn: "2024-02-15",
},
{
id: "6",
email: "emily@example.com",
firstName: "Emily",
lastName: "Davis",
avatar: "/placeholder.svg?height=40&width=40",
role: "admin",
status: "active",
lastSignedIn: "2024-02-23",
},
{
id: "7",
email: "robert@example.com",
firstName: "Robert",
lastName: "Miller",
avatar: "/placeholder.svg?height=40&width=40",
role: "user",
status: "active",
lastSignedIn: "2024-02-22",
},
{
id: "8",
email: "amanda@example.com",
firstName: "Amanda",
lastName: "Wilson",
avatar: "/placeholder.svg?height=40&width=40",
role: "staff",
status: "inactive",
lastSignedIn: "2024-02-18",
},
{
id: "9",
email: "thomas@example.com",
firstName: "Thomas",
lastName: "Moore",
avatar: "/placeholder.svg?height=40&width=40",
role: "user",
status: "active",
lastSignedIn: "2024-02-21",
},
{
id: "10",
email: "laura@example.com",
firstName: "Laura",
lastName: "Taylor",
avatar: "/placeholder.svg?height=40&width=40",
role: "staff",
status: "active",
lastSignedIn: "2024-02-19",
},
];
export type User = {
id: string;
email: string;
firstName: string;
lastName: string;
avatar: string;
role: "admin" | "staff" | "user";
status: "active" | "inactive";
lastSignedIn: string;
};
export function UsersTable() {
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
const [rowSelection, setRowSelection] = useState({});
const [isSheetOpen, setIsSheetOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [searchValue, setSearchValue] = useState("");
// Apply search filter when search value changes
useEffect(() => {
if (searchValue) {
setColumnFilters((prev) => {
// Remove existing kolomusers filter if it exists
const filtered = prev.filter((filter) => filter.id !== "kolomusers");
// Add the new search filter
return [...filtered, { id: "kolomusers", value: searchValue }];
});
} else {
// Remove the filter if search is empty
setColumnFilters((prev) =>
prev.filter((filter) => filter.id !== "kolomusers")
);
}
}, [searchValue]);
const openEditSheet = useCallback((user: User) => {
setSelectedUser(user);
setIsSheetOpen(true);
}, []);
const handleUserUpdate = (updatedUser: User) => {
// Here you would typically update the user in your data source
console.log("Updated user:", updatedUser);
// For this example, we'll just update the local state
setSelectedUser(updatedUser);
};
const handleRoleFilter = (role: string) => {
if (role === "all") {
setColumnFilters((prev) => prev.filter((filter) => filter.id !== "role"));
} else {
setColumnFilters((prev) => {
const filtered = prev.filter((filter) => filter.id !== "role");
return [...filtered, { id: "role", value: role }];
});
}
};
const handleStatusFilter = (status: string) => {
if (status === "all") {
setColumnFilters((prev) =>
prev.filter((filter) => filter.id !== "status")
);
} else {
setColumnFilters((prev) => {
const filtered = prev.filter((filter) => filter.id !== "status");
return [...filtered, { id: "status", value: status }];
});
}
};
const columns: 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: "kolomusers",
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>
);
},
},
];
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
initialState: {
pagination: {
pageSize: 5,
},
},
});
return (
<div className="w-full">
<div className="flex items-center justify-between py-4">
<div className="flex items-center">
<Input
placeholder="Search users..."
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
className="max-w-sm"
/>
</div>
<div className="flex items-center gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
Columns <ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
<Button>
<Plus className="mr-2 h-4 w-4" /> Add User
</Button>
</div>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-between space-x-2 py-4">
<div className="flex items-center gap-2">
<Select
value={table.getState().pagination.pageSize.toString()}
onValueChange={(value) => {
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="w-32">
<SelectValue placeholder="Rows per page" />
</SelectTrigger>
<SelectContent>
{[5, 10, 20, 50].map((pageSize) => (
<SelectItem key={pageSize} value={pageSize.toString()}>
{pageSize} rows
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-sm text-muted-foreground">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</span>
</div>
<div className="space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
<EditUserSheet
isOpen={isSheetOpen}
onOpenChange={setIsSheetOpen}
selectedUser={selectedUser}
onUserUpdate={handleUserUpdate}
/>
</div>
);
}