Skip to content

Commit

Permalink
feat: added clients table to company dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
bencodes07 committed Oct 24, 2024
1 parent 2645397 commit 3db12cf
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 26 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@syncfusion/ej2-react-navigations": "^25.2.3",
"@syncfusion/ej2-react-schedule": "^25.2.3",
"@syncfusion/ej2-react-splitbuttons": "^25.2.3",
"@tanstack/react-table": "^8.20.5",
"@tweenjs/tween.js": "23.1.1",
"class-variance-authority": "^0.7.0",
"classnames": "^2.5.1",
Expand Down
2 changes: 1 addition & 1 deletion src/app/company/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function DashboardContent({ children }: { children: React.ReactNode }) {
)}
variant={active === "users" ? "default" : "ghost"}>
<Users className="mx-2" size={18} />
Users
Clients
</Button>
</Link>
<Link href={"/company/dashboard/members"}>
Expand Down
26 changes: 3 additions & 23 deletions src/app/company/dashboard/users/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";
import { Input } from "@/components/ui/input";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -16,6 +15,7 @@ import { useRouter } from "next/navigation";
import { deleteToken } from "@/lib/authActions";
import Loader from "@/components/layout/Loader";
import { useCompany } from "@/components/dashboard/CompanyContext";
import { UsersTable } from "@/components/dashboard/UsersTable";

export default function Page() {
const { user, loading, companyLoading, company } = useCompany();
Expand All @@ -40,7 +40,6 @@ export default function Page() {
{company?.getCompany.name} (ID: {company?.getCompany.id})
</h1>
<div className="flex items-center gap-x-6">
<Input className="w-[320px]" placeholder="Search"></Input>
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild className={"mr-4"}>
<Button variant="ghost" className="relative size-8 rounded-full">
Expand Down Expand Up @@ -72,27 +71,8 @@ export default function Page() {
</div>
</header>

<div className="mt-8 flex h-[600px] w-full flex-col rounded-[20px] px-12">
<div className={"mt-6 flex h-[200px] w-full items-center justify-between"}>
<div className={"flex flex-row items-center justify-center"}>
<div
className={
"flex size-[200px] items-center justify-center rounded-full bg-primary text-6xl font-medium text-foreground"
}>
{extractNameInitials(company?.getCompany.name)}
</div>
<div className={"ml-12 flex flex-col"}>
<h1 className={"text-4xl font-semibold"}>{company?.getCompany.name}</h1>
</div>
</div>
</div>
<p className={"mt-10 text-muted-foreground"}>
{company?.getCompany.description !== "" ? (
company?.getCompany.description
) : (
<i>This company has not provided a description</i>
)}
</p>
<div className="mt-8 flex h-[600px] w-full flex-col rounded-[20px] px-6">
<UsersTable />
</div>
</div>
);
Expand Down
249 changes: 249 additions & 0 deletions src/components/dashboard/UsersTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
"use client";

import * as React from "react";
import {
type ColumnDef,
type ColumnFiltersState,
type SortingState,
type 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 { Checkbox } from "@/components/ui/checkbox";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import type { User } from "@/types";
import { getAccessToken, getAllUsers } from "@/lib/authActions";

const columns: Array<ColumnDef<User>> = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")}
onCheckedChange={(value) => {
table.toggleAllPageRowsSelected(value as boolean);
}}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => {
row.toggleSelected(value as boolean);
}}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false
},
{
accessorKey: "name",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => {
column.toggleSorting(column.getIsSorted() === "asc");
}}>
Name
<ArrowUpDown className="ml-2 size-4" />
</Button>
);
}
},
{
accessorKey: "email",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => {
column.toggleSorting(column.getIsSorted() === "asc");
}}>
Email
<ArrowUpDown className="ml-2 size-4" />
</Button>
);
}
},
{
id: "actions",
enableHiding: false,
cell: ({ row }) => {
const user = row.original;

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="size-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => {
void navigator.clipboard.writeText(user.id.toString());
}}>
Copy user ID
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
}
];

export function UsersTable(): React.ReactElement {
const [users, setUsers] = React.useState<User[]>([]);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});

React.useEffect(() => {
const fetchUsers = async (): Promise<void> => {
const fetchedUsers = await getAllUsers(await getAccessToken());
setUsers(fetchedUsers);
};
void fetchUsers();
}, []);

const table = useReactTable({
data: users,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection
}
});

return (
<div className="w-full text-foreground">
<div className="flex items-center py-4">
<Input
placeholder="Filter by name..."
value={(table.getColumn("name")?.getFilterValue() as string) ?? ""}
onChange={(event) => {
table.getColumn("name")?.setFilterValue(event.target.value);
}}
className="max-w-sm"
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Columns <ChevronDown className="ml-2 size-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(Boolean(value));
}}>
{column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="rounded-md border border-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 !== 0 ? (
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-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length} row(s)
selected.
</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>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/ui/dropdown-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
Expand All @@ -62,7 +62,7 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
Expand Down
Loading

0 comments on commit 3db12cf

Please sign in to comment.