diff --git a/package.json b/package.json index 7857124..76bc0c8 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@tailwindcss/aspect-ratio": "^0.4.2", "@tanstack/react-query": "^5.28.4", "@tanstack/react-router": "^1.20.4", + "@tanstack/react-table": "^8.15.3", "@tanstack/router-devtools": "^1.20.4", "@tanstack/router-vite-plugin": "^1.20.2", "@types/react-lazy-load-image-component": "^1.6.3", diff --git a/src/components/data-table.tsx b/src/components/data-table.tsx new file mode 100644 index 0000000..8328f6e --- /dev/null +++ b/src/components/data-table.tsx @@ -0,0 +1,240 @@ +"use client"; + +import * as React from "react"; +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +interface DataTableProps { + data: TData[] +} + +const columns: ColumnDef[] = [ + { + accessorKey: "id", + header: "ID", + cell: ({ row }) => ( +
{row.getValue("id")}
+ ), + }, + { + accessorKey: "email", + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row }) =>
{row.getValue("email")}
, + }, + { + accessorKey: "role", + header: () =>
Role
, + cell: ({ row }) => { + return ( +
{row.getValue("role")}
+ ); + }, + }, + { + id: "actions", + enableHiding: false, + cell: (/* { row } */ ) => { + // const users = row.original; + + return ( + + + + + + Role + ADMIN + CUSTOMER + + Actions + Delete + + + ); + }, + }, +]; + +export function DataTable({ data }: DataTableProps) { + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + 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, + }, + }); + + return ( +
+
+ + table.getColumn("email")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ + +
+
+
+ ); +} diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx new file mode 100644 index 0000000..7f3502f --- /dev/null +++ b/src/components/ui/table.tsx @@ -0,0 +1,117 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+ + +)) +Table.displayName = "Table" + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableHeader.displayName = "TableHeader" + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableBody.displayName = "TableBody" + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0", + className + )} + {...props} + /> +)) +TableFooter.displayName = "TableFooter" + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableRow.displayName = "TableRow" + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableHead.displayName = "TableHead" + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableCell.displayName = "TableCell" + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableCaption.displayName = "TableCaption" + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index ea97e66..7c75d6d 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -26,6 +26,7 @@ import { Route as AuthLoginImport } from './routes/_auth/login' import { Route as AdminDashboardIndexImport } from './routes/_admin/dashboard/index' import { Route as AuthenticatedProductProductIdImport } from './routes/_authenticated/product.$productId' import { Route as AuthenticatedCategoryCategoryNameImport } from './routes/_authenticated/category.$categoryName' +import { Route as AdminDashboardUsersImport } from './routes/_admin/dashboard/users' import { Route as AdminDashboardUploadImport } from './routes/_admin/dashboard/upload' import { Route as AdminDashboardProductsImport } from './routes/_admin/dashboard/products' import { Route as AdminDashboardUpdateProductIdImport } from './routes/_admin/dashboard/update.$productId' @@ -111,6 +112,11 @@ const AuthenticatedCategoryCategoryNameRoute = getParentRoute: () => AuthenticatedRoute, } as any) +const AdminDashboardUsersRoute = AdminDashboardUsersImport.update({ + path: '/dashboard/users', + getParentRoute: () => AdminRoute, +} as any) + const AdminDashboardUploadRoute = AdminDashboardUploadImport.update({ path: '/dashboard/upload', getParentRoute: () => AdminRoute, @@ -193,6 +199,10 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AdminDashboardUploadImport parentRoute: typeof AdminImport } + '/_admin/dashboard/users': { + preLoaderRoute: typeof AdminDashboardUsersImport + parentRoute: typeof AdminImport + } '/_authenticated/category/$categoryName': { preLoaderRoute: typeof AuthenticatedCategoryCategoryNameImport parentRoute: typeof AuthenticatedImport @@ -222,6 +232,7 @@ export const routeTree = rootRoute.addChildren([ AdminRoute.addChildren([ AdminDashboardProductsRoute, AdminDashboardUploadRoute, + AdminDashboardUsersRoute, AdminDashboardIndexRoute, AdminDashboardProductProductIdRoute, AdminDashboardUpdateProductIdRoute, diff --git a/src/routes/_admin/dashboard/index.tsx b/src/routes/_admin/dashboard/index.tsx index 14a32be..29446f8 100644 --- a/src/routes/_admin/dashboard/index.tsx +++ b/src/routes/_admin/dashboard/index.tsx @@ -32,17 +32,19 @@ function Dashboard() {
- - -
- -
- Users - Manage user roles and access + + + +
+ +
+ Users + Manage user roles and access +
-
- - + + +
diff --git a/src/routes/_admin/dashboard/users.tsx b/src/routes/_admin/dashboard/users.tsx new file mode 100644 index 0000000..85c90c9 --- /dev/null +++ b/src/routes/_admin/dashboard/users.tsx @@ -0,0 +1,34 @@ +import { DataTable } from "@/components/data-table"; +import Loading from "@/components/loading"; +import { config } from "@/lib/config"; +import { createFileRoute } from "@tanstack/react-router"; +import axios from "axios"; +import useSWR from "swr"; + +export const Route = createFileRoute("/_admin/dashboard/users")({ + component: Users, +}); + +async function getUsers(url: string, session: string) { + const { data } = await axios.get(`${config.SERVER_API_URL}/v1/${url}`, { + headers: { + Authorization: `Bearer ${session}`, + }, + }); + return data.users; +} + +function Users() { + const { session } = Route.useRouteContext(); + const { data, isLoading } = useSWR(["user/all", session], ([url, session]) => + getUsers(url, session) + ); + + if (isLoading) return ; + + return ( +
+ +
+ ); +}