diff --git a/package.json b/package.json
index 7d935ac..bfbe8fc 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/app/company/dashboard/layout.tsx b/src/app/company/dashboard/layout.tsx
index 167d2a9..648b11c 100644
--- a/src/app/company/dashboard/layout.tsx
+++ b/src/app/company/dashboard/layout.tsx
@@ -82,7 +82,7 @@ function DashboardContent({ children }: { children: React.ReactNode }) {
)}
variant={active === "users" ? "default" : "ghost"}>
- Users
+ Clients
diff --git a/src/app/company/dashboard/users/page.tsx b/src/app/company/dashboard/users/page.tsx
index a3363c5..fc32624 100644
--- a/src/app/company/dashboard/users/page.tsx
+++ b/src/app/company/dashboard/users/page.tsx
@@ -1,5 +1,4 @@
"use client";
-import { Input } from "@/components/ui/input";
import {
DropdownMenu,
DropdownMenuContent,
@@ -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();
@@ -40,7 +40,6 @@ export default function Page() {
{company?.getCompany.name} (ID: {company?.getCompany.id})
-
-
-
-
-
- {extractNameInitials(company?.getCompany.name)}
-
-
-
{company?.getCompany.name}
-
-
-
-
- {company?.getCompany.description !== "" ? (
- company?.getCompany.description
- ) : (
- This company has not provided a description
- )}
-
+
+
);
diff --git a/src/components/dashboard/UsersTable.tsx b/src/components/dashboard/UsersTable.tsx
new file mode 100644
index 0000000..a3df39a
--- /dev/null
+++ b/src/components/dashboard/UsersTable.tsx
@@ -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> = [
+ {
+ id: "select",
+ header: ({ table }) => (
+ {
+ table.toggleAllPageRowsSelected(value as boolean);
+ }}
+ aria-label="Select all"
+ />
+ ),
+ cell: ({ row }) => (
+ {
+ row.toggleSelected(value as boolean);
+ }}
+ aria-label="Select row"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => {
+ return (
+
+ );
+ }
+ },
+ {
+ accessorKey: "email",
+ header: ({ column }) => {
+ return (
+
+ );
+ }
+ },
+ {
+ id: "actions",
+ enableHiding: false,
+ cell: ({ row }) => {
+ const user = row.original;
+
+ return (
+
+
+
+
+
+ Actions
+ {
+ void navigator.clipboard.writeText(user.id.toString());
+ }}>
+ Copy user ID
+
+
+
+ );
+ }
+ }
+];
+
+export function UsersTable(): React.ReactElement {
+ const [users, setUsers] = React.useState([]);
+ const [sorting, setSorting] = React.useState([]);
+ const [columnFilters, setColumnFilters] = React.useState([]);
+ const [columnVisibility, setColumnVisibility] = React.useState({});
+ const [rowSelection, setRowSelection] = React.useState({});
+
+ React.useEffect(() => {
+ const fetchUsers = async (): Promise => {
+ 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 (
+
+
+ {
+ table.getColumn("name")?.setFilterValue(event.target.value);
+ }}
+ className="max-w-sm"
+ />
+
+
+
+
+
+ {table
+ .getAllColumns()
+ .filter((column) => column.getCanHide())
+ .map((column) => {
+ return (
+ {
+ column.toggleVisibility(Boolean(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 !== 0 ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+ ))}
+
+ ))
+ ) : (
+
+
+ No results.
+
+
+ )}
+
+
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length} row(s)
+ selected.
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
index 03b9b11..f51a87b 100644
--- a/src/components/ui/dropdown-menu.tsx
+++ b/src/components/ui/dropdown-menu.tsx
@@ -45,7 +45,7 @@ const DropdownMenuSubContent = React.forwardRef<
>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+Table.displayName = "Table";
+
+const TableHeader = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+TableHeader.displayName = "TableHeader";
+
+const TableBody = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+TableBody.displayName = "TableBody";
+
+const TableFooter = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+ tr]:last:border-b-0", className)} {...props} />
+ )
+);
+TableFooter.displayName = "TableFooter";
+
+const TableRow = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+TableRow.displayName = "TableRow";
+
+const TableHead = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+ |
+ )
+);
+TableHead.displayName = "TableHead";
+
+const TableCell = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+ |
+ )
+);
+TableCell.displayName = "TableCell";
+
+const TableCaption = React.forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
+);
+TableCaption.displayName = "TableCaption";
+
+export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };
diff --git a/src/lib/authActions.ts b/src/lib/authActions.ts
index a00b4e0..7a96d87 100644
--- a/src/lib/authActions.ts
+++ b/src/lib/authActions.ts
@@ -93,6 +93,24 @@ export async function getUser(accessToken?: string): Promise {
}
}
+export async function getAllUsers(accessToken?: string): Promise {
+ const response = await fetch(process.env.FRONTEND_DOMAIN + "/api/user/getAll", {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json;charset=UTF-8",
+ Authorization: "Bearer " + accessToken ?? cookies().get("accessToken")?.value
+ },
+ body: undefined
+ });
+ if (response.ok) {
+ return (await response.json()) as User[];
+ } else if (response.status === 403 || response.status === 500 || response.status === 401) {
+ return [];
+ } else {
+ throw new Error("There was a problem fetching the user: " + response.statusText + " " + response.status);
+ }
+}
+
export async function getAccessToken() {
return cookies().get("accessToken")?.value;
}