roles table

This commit is contained in:
Fred KISSIE
2026-03-23 21:34:44 +01:00
parent 062bec23b6
commit 294532ecbb
6 changed files with 187 additions and 152 deletions

View File

@@ -1,41 +0,0 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { DataTable } from "@app/components/ui/data-table";
import { useTranslations } from "next-intl";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
createRole?: () => void;
onRefresh?: () => void;
isRefreshing?: boolean;
}
export function RolesDataTable<TData, TValue>({
columns,
data,
createRole,
onRefresh,
isRefreshing
}: DataTableProps<TData, TValue>) {
const t = useTranslations();
return (
<DataTable
columns={columns}
data={data}
persistPageSize="roles-table"
title={t("roles")}
searchPlaceholder={t("accessRolesSearch")}
searchColumn="name"
onAdd={createRole}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
addButtonText={t("accessRolesAdd")}
enableColumnVisibility={true}
stickyLeftColumn="name"
stickyRightColumn="actions"
/>
);
}

View File

@@ -7,7 +7,13 @@ import { Button } from "@app/components/ui/button";
import { ExtendedColumnDef } from "@app/components/ui/data-table";
import { toast } from "@app/hooks/useToast";
import { Role } from "@server/db";
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
import {
ArrowDown01Icon,
ArrowUp10Icon,
ArrowUpDown,
ChevronsUpDownIcon,
MoreHorizontal
} from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import { useState, useTransition } from "react";
@@ -18,24 +24,40 @@ import {
DropdownMenuItem
} from "./ui/dropdown-menu";
import EditRoleForm from "./EditRoleForm";
import type { PaginationState } from "@tanstack/react-table";
import { ControlledDataTable } from "./ui/controlled-data-table";
import { useNavigationContext } from "@app/hooks/useNavigationContext";
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
import { useDebouncedCallback } from "use-debounce";
export type RoleRow = Role;
type RolesTableProps = {
roles: RoleRow[];
pagination: PaginationState;
rowCount: number;
};
export default function UsersTable({ roles }: RolesTableProps) {
export default function UsersTable({
roles,
pagination,
rowCount
}: RolesTableProps) {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [editingRole, setEditingRole] = useState<RoleRow | null>(null);
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
const router = useRouter();
const [isRefreshing, startTransition] = useTransition();
const {
navigate: filter,
isNavigating: isFiltering,
searchParams
} = useNavigationContext();
const [roleToRemove, setRoleToRemove] = useState<RoleRow | null>(null);
const t = useTranslations();
const [isRefreshing, startTransition] = useTransition();
const refreshData = async () => {
console.log("Data refreshed");
@@ -56,15 +78,17 @@ export default function UsersTable({ roles }: RolesTableProps) {
enableHiding: false,
friendlyName: t("name"),
header: ({ column }) => {
const nameOrder = getSortDirection("name", searchParams);
const Icon =
nameOrder === "asc"
? ArrowDown01Icon
: nameOrder === "desc"
? ArrowUp10Icon
: ChevronsUpDownIcon;
return (
<Button
variant="ghost"
onClick={() =>
column.toggleSorting(column.getIsSorted() === "asc")
}
>
<Button variant="ghost" onClick={() => toggleSort("name")}>
{t("name")}
<ArrowUpDown className="ml-2 h-4 w-4" />
<Icon className="ml-2 h-4 w-4" />
</Button>
);
}
@@ -148,6 +172,30 @@ export default function UsersTable({ roles }: RolesTableProps) {
}
];
function toggleSort(column: string) {
const newSearch = getNextSortOrder(column, searchParams);
filter({
searchParams: newSearch
});
}
const handlePaginationChange = (newPage: PaginationState) => {
searchParams.set("page", (newPage.pageIndex + 1).toString());
searchParams.set("pageSize", newPage.pageSize.toString());
filter({
searchParams
});
};
const handleSearchChange = useDebouncedCallback((query: string) => {
searchParams.set("query", query);
searchParams.delete("page");
filter({
searchParams
});
}, 300);
return (
<>
{editingRole && (
@@ -191,10 +239,18 @@ export default function UsersTable({ roles }: RolesTableProps) {
/>
)}
<RolesDataTable
<ControlledDataTable
columns={columns}
data={roles}
createRole={() => {
rows={roles}
tableId="roles-table"
searchQuery={searchParams.get("query")?.toString()}
onSearch={handleSearchChange}
onPaginationChange={handlePaginationChange}
searchPlaceholder={t("accessRolesSearch")}
addButtonText={t("accessRolesAdd")}
rowCount={rowCount}
pagination={pagination}
onAdd={() => {
setIsCreateModalOpen(true);
}}
onRefresh={() => startTransition(refreshData)}

View File

@@ -1,41 +0,0 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { DataTable } from "@app/components/ui/data-table";
import { useTranslations } from "next-intl";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
inviteUser?: () => void;
onRefresh?: () => void;
isRefreshing?: boolean;
}
export function UsersDataTable<TData, TValue>({
columns,
data,
inviteUser,
onRefresh,
isRefreshing
}: DataTableProps<TData, TValue>) {
const t = useTranslations();
return (
<DataTable
columns={columns}
data={data}
persistPageSize="users-table"
title={t("users")}
searchPlaceholder={t("accessUsersSearch")}
searchColumn="email"
onAdd={inviteUser}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
addButtonText={t("accessUserCreate")}
enableColumnVisibility={true}
stickyLeftColumn="displayUsername"
stickyRightColumn="actions"
/>
);
}

View File

@@ -1,6 +1,7 @@
"use client";
import { ColumnDef, type PaginationState } from "@tanstack/react-table";
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { Button } from "@app/components/ui/button";
import { ExtendedColumnDef } from "@app/components/ui/data-table";
import {
DropdownMenu,
@@ -8,35 +9,29 @@ import {
DropdownMenuItem,
DropdownMenuTrigger
} from "@app/components/ui/dropdown-menu";
import { Button } from "@app/components/ui/button";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useNavigationContext } from "@app/hooks/useNavigationContext";
import { useOrgContext } from "@app/hooks/useOrgContext";
import { toast } from "@app/hooks/useToast";
import { useUserContext } from "@app/hooks/useUserContext";
import { createApiClient, formatAxiosError } from "@app/lib/api";
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
import { type PaginationState } from "@tanstack/react-table";
import {
ArrowDown01Icon,
ArrowRight,
ArrowUp10Icon,
ArrowUpDown,
ChevronsUpDownIcon,
Crown,
MoreHorizontal
} from "lucide-react";
import { UsersDataTable } from "@app/components/UsersDataTable";
import { useState, useEffect, useTransition } from "react";
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
import { useOrgContext } from "@app/hooks/useOrgContext";
import { toast } from "@app/hooks/useToast";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { formatAxiosError } from "@app/lib/api";
import { createApiClient } from "@app/lib/api";
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { useUserContext } from "@app/hooks/useUserContext";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState, useTransition } from "react";
import { useDebouncedCallback } from "use-debounce";
import IdpTypeBadge from "./IdpTypeBadge";
import { ControlledDataTable } from "./ui/controlled-data-table";
import type { filter } from "d3";
import { useDebouncedCallback } from "use-debounce";
import { useNavigationContext } from "@app/hooks/useNavigationContext";
import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn";
export type UserRow = {
id: string;