add sitcky table cols for left and right cols

This commit is contained in:
miloschwartz
2025-11-07 18:03:33 -08:00
parent 47bcadb329
commit 3993e5b705
29 changed files with 689 additions and 620 deletions

View File

@@ -2156,5 +2156,6 @@
"checkSelectedStatus": "Check Status of Selected",
"clients": "Clients",
"accessClientSelect": "Select machine clients",
"resourceClientDescription": "Machine clients that can access this resource"
"resourceClientDescription": "Machine clients that can access this resource",
"regenerate": "Regenerate"
}

View File

@@ -35,6 +35,9 @@ export function IdpDataTable<TData, TValue>({
}}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
enableColumnVisibility={true}
stickyLeftColumn="name"
stickyRightColumn="actions"
/>
);
}

View File

@@ -96,6 +96,7 @@ export default function IdpTable({ idps }: Props) {
},
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -133,19 +134,12 @@ export default function IdpTable({ idps }: Props) {
},
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const siteRow = row.original;
return (
<div className="flex items-center">
<Link href={`/admin/idp/${siteRow.idpId}/general`}>
<Button
variant={"outline"}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -176,6 +170,14 @@ export default function IdpTable({ idps }: Props) {
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link href={`/admin/idp/${siteRow.idpId}/general`}>
<Button
variant={"outline"}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
);
}

View File

@@ -32,6 +32,9 @@ export function UsersDataTable<TData, TValue>({
searchColumn="email"
onRefresh={onRefresh}
isRefreshing={isRefreshing}
enableColumnVisibility={true}
stickyLeftColumn="username"
stickyRightColumn="actions"
/>
);
}

View File

@@ -103,6 +103,7 @@ export default function UsersTable({ users }: Props) {
},
{
accessorKey: "username",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -201,46 +202,45 @@ export default function UsersTable({ users }: Props) {
},
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const r = row.original;
return (
<>
<div className="flex items-center gap-2">
<Button
variant={"outline"}
onClick={() => {
router.push(`/admin/users/${r.id}`);
}}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="h-8 w-8 p-0"
>
<span className="sr-only">
Open menu
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setSelected(r);
setIsDeleteModalOpen(true);
}}
>
{t("delete")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="h-8 w-8 p-0"
>
<span className="sr-only">
Open menu
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setSelected(r);
setIsDeleteModalOpen(true);
}}
>
{t("delete")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
variant={"outline"}
onClick={() => {
router.push(`/admin/users/${r.id}`);
}}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</div>
);
}
}

View File

@@ -59,6 +59,9 @@ export function ApiKeysDataTable<TData, TValue>({
addButtonText={t('apiKeysAdd')}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
enableColumnVisibility={true}
stickyLeftColumn="name"
stickyRightColumn="actions"
/>
);
}

View File

@@ -88,6 +88,7 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
const columns: ColumnDef<ApiKeyRow>[] = [
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -120,19 +121,12 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
},
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const r = row.original;
return (
<div className="flex items-center gap-2">
<Link href={`/admin/api-keys/${r.id}`}>
<Button
variant={"outline"}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -162,6 +156,14 @@ export default function ApiKeysTable({ apiKeys }: ApiKeyTableProps) {
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link href={`/admin/api-keys/${r.id}`}>
<Button
variant={"outline"}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
);
}

View File

@@ -61,6 +61,7 @@ export default function BlueprintsTable({ blueprints, orgId }: Props) {
},
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -157,10 +158,11 @@ export default function BlueprintsTable({ blueprints, orgId }: Props) {
},
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
return (
<div className="flex">
<div className="flex justify-end">
<Button
variant="outline"
className="items-center"
@@ -187,6 +189,9 @@ export default function BlueprintsTable({ blueprints, orgId }: Props) {
title={t("blueprints")}
searchPlaceholder={t("searchBlueprintProgress")}
searchColumn="name"
enableColumnVisibility={true}
stickyLeftColumn="name"
stickyRightColumn="actions"
onAdd={() => {
router.push(`/${orgId}/settings/blueprints/create`);
}}

View File

@@ -334,6 +334,7 @@ export default function ClientsTable({
const baseColumns: ColumnDef<ClientRow>[] = [
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -531,19 +532,59 @@ export default function ClientsTable({
if (hasRowsWithoutUserId) {
baseColumns.push({
id: "actions",
header: () => <span className="p-3">{t("actions")}</span>,
enableHiding: false,
header: ({ table }) => {
const hasHideableColumns = table
.getAllColumns()
.some((column) => column.getCanHide());
if (!hasHideableColumns) {
return <span className="p-3"></span>;
}
return (
<div className="flex flex-col items-end gap-1 p-3">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-7 w-7 p-0">
<Columns className="h-4 w-4" />
<span className="sr-only">
{t("columns") || "Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>
{t("toggleColumns") || "Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{typeof column.columnDef.header ===
"string"
? column.columnDef.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
},
cell: ({ row }) => {
const clientRow = row.original;
return !clientRow.userId ? (
<div className="flex items-center">
<Link
href={`/${clientRow.orgId}/settings/clients/${clientRow.id}`}
>
<Button variant={"outline"}>
Edit
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -570,6 +611,14 @@ export default function ClientsTable({
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link
href={`/${clientRow.orgId}/settings/clients/${clientRow.id}`}
>
<Button variant={"outline"}>
Edit
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
) : null;
}
@@ -693,122 +742,6 @@ export default function ClientsTable({
</TabsList>
</div>
<div className="flex items-center gap-2 sm:justify-end">
{currentView === "user" &&
userTable
.getAllColumns()
.some((column) =>
column.getCanHide()
) && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<Columns className="mr-0 sm:mr-2 h-4 w-4" />
<span className="hidden sm:inline">
{t("columns") ||
"Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-48"
>
<DropdownMenuLabel>
{t("toggleColumns") ||
"Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{userTable
.getAllColumns()
.filter((column) =>
column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(
value
) =>
column.toggleVisibility(
!!value
)
}
>
{typeof column
.columnDef
.header ===
"string"
? column
.columnDef
.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
)}
{currentView === "machine" &&
machineTable
.getAllColumns()
.some((column) =>
column.getCanHide()
) && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<Columns className="mr-0 sm:mr-2 h-4 w-4" />
<span className="hidden sm:inline">
{t("columns") ||
"Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-48"
>
<DropdownMenuLabel>
{t("toggleColumns") ||
"Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{machineTable
.getAllColumns()
.filter((column) =>
column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(
value
) =>
column.toggleVisibility(
!!value
)
}
>
{typeof column
.columnDef
.header ===
"string"
? column
.columnDef
.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
)}
<div>
<Button
variant="outline"
@@ -828,7 +761,8 @@ export default function ClientsTable({
</CardHeader>
<CardContent>
<TabsContent value="user">
<Table>
<div className="overflow-x-auto">
<Table>
<TableHeader>
{userTable
.getHeaderGroups()
@@ -841,6 +775,15 @@ export default function ClientsTable({
.map((header) => (
<TableHead
key={header.id}
className={`whitespace-nowrap ${
header.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: header.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{header.isPlaceholder
? null
@@ -876,6 +819,15 @@ export default function ClientsTable({
key={
cell.id
}
className={`whitespace-nowrap ${
cell.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: cell.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{flexRender(
cell
@@ -900,6 +852,7 @@ export default function ClientsTable({
)}
</TableBody>
</Table>
</div>
<div className="mt-4">
<DataTablePagination
table={userTable}
@@ -910,7 +863,8 @@ export default function ClientsTable({
</div>
</TabsContent>
<TabsContent value="machine">
<Table>
<div className="overflow-x-auto">
<Table>
<TableHeader>
{machineTable
.getHeaderGroups()
@@ -923,6 +877,15 @@ export default function ClientsTable({
.map((header) => (
<TableHead
key={header.id}
className={`whitespace-nowrap ${
header.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: header.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{header.isPlaceholder
? null
@@ -958,6 +921,15 @@ export default function ClientsTable({
key={
cell.id
}
className={`whitespace-nowrap ${
cell.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: cell.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{flexRender(
cell
@@ -982,6 +954,7 @@ export default function ClientsTable({
)}
</TableBody>
</Table>
</div>
<div className="mt-4">
<DataTablePagination
table={machineTable}

View File

@@ -33,6 +33,9 @@ export function DomainsDataTable<TData, TValue>({
onAdd={onAdd}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
enableColumnVisibility={true}
stickyLeftColumn="baseDomain"
stickyRightColumn="actions"
/>
);
}

View File

@@ -137,6 +137,7 @@ export default function DomainsTable({ domains, orgId }: Props) {
const columns: ColumnDef<DomainRow>[] = [
{
accessorKey: "baseDomain",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -209,13 +210,47 @@ export default function DomainsTable({ domains, orgId }: Props) {
},
{
id: "actions",
header: () => <span className="p-3">{t("actions")}</span>,
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const domain = row.original;
const isRestarting = restartingDomains.has(domain.domainId);
return (
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="h-8 w-8 p-0"
>
<span className="sr-only">
Open menu
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<Link
className="block w-full"
href={`/${orgId}/settings/domains/${domain.domainId}`}
>
<DropdownMenuItem>
{t("viewSettings")}
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => {
setSelectedDomain(domain);
setIsDeleteModalOpen(true);
}}
>
<span className="text-red-500">
{t("delete")}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{domain.failed && (
<Button
variant="outline"
@@ -240,41 +275,6 @@ export default function DomainsTable({ domains, orgId }: Props) {
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center justify-end gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="h-8 w-8 p-0"
>
<span className="sr-only">
Open menu
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<Link
className="block w-full"
href={`/${orgId}/settings/domains/${domain.domainId}`}
>
<DropdownMenuItem>
{t("viewSettings")}
</DropdownMenuItem>
</Link>
<DropdownMenuItem
onClick={() => {
setSelectedDomain(domain);
setIsDeleteModalOpen(true);
}}
>
<span className="text-red-500">
{t("delete")}
</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
{/* <Button
variant="secondary"
size="sm"

View File

@@ -32,6 +32,9 @@ export function InvitationsDataTable<TData, TValue>({
searchColumn="email"
onRefresh={onRefresh}
isRefreshing={isRefreshing}
enableColumnVisibility={true}
stickyLeftColumn="email"
stickyRightColumn="dots"
/>
);
}

View File

@@ -69,6 +69,7 @@ export default function InvitationsTable({
const columns: ColumnDef<InvitationRow>[] = [
{
accessorKey: "email",
enableHiding: false,
header: () => (<span className="p-3">{t("email")}</span>)
},
{
@@ -91,6 +92,8 @@ export default function InvitationsTable({
},
{
id: "dots",
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const invitation = row.original;
return (
@@ -119,13 +122,13 @@ export default function InvitationsTable({
</DropdownMenu>
<Button
variant={"secondary"}
variant={"outline"}
onClick={() => {
setIsRegenerateModalOpen(true);
setSelectedInvitation(invitation);
}}
>
<span>{t("inviteRegenerate")}</span>
{t("regenerate", { fallback: "Regenerate" })}
</Button>
</div>
);

View File

@@ -33,6 +33,7 @@ export function LicenseKeysDataTable({
const columns: ColumnDef<LicenseKeyCache>[] = [
{
accessorKey: "licenseKey",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -123,9 +124,10 @@ export function LicenseKeysDataTable({
},
{
id: "delete",
header: () => <span className="p-3">{t("actions")}</span>,
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => (
<div className="flex items-center space-x-2">
<div className="flex items-center gap-2 justify-end">
<Button variant={"outline"}
onClick={() => onDelete(row.original)}
>
@@ -146,6 +148,9 @@ export function LicenseKeysDataTable({
searchColumn="licenseKey"
onAdd={onCreate}
addButtonText={t("licenseKeyAdd")}
enableColumnVisibility={true}
stickyLeftColumn="licenseKey"
stickyRightColumn="delete"
/>
);
}

View File

@@ -34,6 +34,9 @@ export function OrgApiKeysDataTable<TData, TValue>({
onRefresh={onRefresh}
isRefreshing={isRefreshing}
addButtonText={t('apiKeysAdd')}
enableColumnVisibility={true}
stickyLeftColumn="name"
stickyRightColumn="actions"
/>
);
}

View File

@@ -91,6 +91,7 @@ export default function OrgApiKeysTable({
const columns: ColumnDef<OrgApiKeyRow>[] = [
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -123,19 +124,12 @@ export default function OrgApiKeysTable({
},
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const r = row.original;
return (
<div className="flex items-center">
<Link href={`/${orgId}/settings/api-keys/${r.id}`}>
<Button
variant={"outline"}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -167,6 +161,14 @@ export default function OrgApiKeysTable({
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link href={`/${orgId}/settings/api-keys/${r.id}`}>
<Button
variant={"outline"}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
);
}

View File

@@ -28,6 +28,9 @@ export function PolicyDataTable<TData, TValue>({
searchColumn="orgId"
addButtonText={t('orgPoliciesAdd')}
onAdd={onAdd}
enableColumnVisibility={true}
stickyLeftColumn="orgId"
stickyRightColumn="actions"
/>
);
}

View File

@@ -37,34 +37,9 @@ interface Props {
export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props) {
const t = useTranslations();
const columns: ColumnDef<PolicyRow>[] = [
{
id: "dots",
cell: ({ row }) => {
const r = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">{t('openMenu')}</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
onDelete(r.orgId);
}}
>
<span className="text-red-500">{t('delete')}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
},
{
accessorKey: "orgId",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -135,14 +110,31 @@ export default function PolicyTable({ policies, onDelete, onAdd, onEdit }: Props
},
{
id: "actions",
header: () => (<span className="p-3">{t('actions')}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const policy = row.original;
return (
<div className="flex items-center">
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">{t('openMenu')}</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
onDelete(policy.orgId);
}}
>
<span className="text-red-500">{t('delete')}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
variant={"outline"}
className="ml-2"
onClick={() => onEdit(policy)}
>
{t('edit')}

View File

@@ -586,6 +586,7 @@ export default function ResourcesTable({
const proxyColumns: ColumnDef<ResourceRow>[] = [
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -748,19 +749,59 @@ export default function ResourcesTable({
},
{
id: "actions",
header: () => <span className="p-3">{t("actions")}</span>,
enableHiding: false,
header: ({ table }) => {
const hasHideableColumns = table
.getAllColumns()
.some((column) => column.getCanHide());
if (!hasHideableColumns) {
return <span className="p-3"></span>;
}
return (
<div className="flex flex-col items-end gap-1 p-3">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-7 w-7 p-0">
<Columns className="h-4 w-4" />
<span className="sr-only">
{t("columns") || "Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>
{t("toggleColumns") || "Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{typeof column.columnDef.header ===
"string"
? column.columnDef.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
},
cell: ({ row }) => {
const resourceRow = row.original;
return (
<div className="flex items-center gap-2">
<Link
href={`/${resourceRow.orgId}/settings/resources/${resourceRow.nice}`}
>
<Button variant={"outline"}>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -791,6 +832,14 @@ export default function ResourcesTable({
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link
href={`/${resourceRow.orgId}/settings/resources/${resourceRow.nice}`}
>
<Button variant={"outline"}>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
);
}
@@ -800,6 +849,7 @@ export default function ResourcesTable({
const internalColumns: ColumnDef<InternalResourceRow>[] = [
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -902,20 +952,59 @@ export default function ResourcesTable({
{
id: "actions",
header: () => <span className="p-3">{t("actions")}</span>,
enableHiding: false,
header: ({ table }) => {
const hasHideableColumns = table
.getAllColumns()
.some((column) => column.getCanHide());
if (!hasHideableColumns) {
return <span className="p-3"></span>;
}
return (
<div className="flex flex-col items-end gap-1 p-3">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-7 w-7 p-0">
<Columns className="h-4 w-4" />
<span className="sr-only">
{t("columns") || "Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>
{t("toggleColumns") || "Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{typeof column.columnDef.header ===
"string"
? column.columnDef.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
},
cell: ({ row }) => {
const resourceRow = row.original;
return (
<div className="flex items-center gap-2">
<Button
variant={"outline"}
onClick={() => {
setEditingResource(resourceRow);
setIsEditDialogOpen(true);
}}
>
{t("edit")}
</Button>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -940,6 +1029,15 @@ export default function ResourcesTable({
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
variant={"outline"}
onClick={() => {
setEditingResource(resourceRow);
setIsEditDialogOpen(true);
}}
>
{t("edit")}
</Button>
</div>
);
}
@@ -1090,122 +1188,6 @@ export default function ResourcesTable({
)}
</div>
<div className="flex items-center gap-2 sm:justify-end">
{currentView === "proxy" &&
proxyTable
.getAllColumns()
.some((column) =>
column.getCanHide()
) && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<Columns className="mr-0 sm:mr-2 h-4 w-4" />
<span className="hidden sm:inline">
{t("columns") ||
"Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-48"
>
<DropdownMenuLabel>
{t("toggleColumns") ||
"Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{proxyTable
.getAllColumns()
.filter((column) =>
column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(
value
) =>
column.toggleVisibility(
!!value
)
}
>
{typeof column
.columnDef
.header ===
"string"
? column
.columnDef
.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
)}
{currentView === "internal" &&
internalTable
.getAllColumns()
.some((column) =>
column.getCanHide()
) && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<Columns className="mr-0 sm:mr-2 h-4 w-4" />
<span className="hidden sm:inline">
{t("columns") ||
"Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-48"
>
<DropdownMenuLabel>
{t("toggleColumns") ||
"Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{internalTable
.getAllColumns()
.filter((column) =>
column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(
value
) =>
column.toggleVisibility(
!!value
)
}
>
{typeof column
.columnDef
.header ===
"string"
? column
.columnDef
.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
)}
<div>
<Button
variant="outline"
@@ -1225,7 +1207,8 @@ export default function ResourcesTable({
</CardHeader>
<CardContent>
<TabsContent value="proxy">
<Table>
<div className="overflow-x-auto">
<Table>
<TableHeader>
{proxyTable
.getHeaderGroups()
@@ -1238,6 +1221,15 @@ export default function ResourcesTable({
.map((header) => (
<TableHead
key={header.id}
className={`whitespace-nowrap ${
header.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: header.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{header.isPlaceholder
? null
@@ -1273,6 +1265,15 @@ export default function ResourcesTable({
key={
cell.id
}
className={`whitespace-nowrap ${
cell.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: cell.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{flexRender(
cell
@@ -1301,6 +1302,7 @@ export default function ResourcesTable({
)}
</TableBody>
</Table>
</div>
<div className="mt-4">
<DataTablePagination
table={proxyTable}
@@ -1311,7 +1313,8 @@ export default function ResourcesTable({
</div>
</TabsContent>
<TabsContent value="internal">
<Table>
<div className="overflow-x-auto">
<Table>
<TableHeader>
{internalTable
.getHeaderGroups()
@@ -1324,6 +1327,15 @@ export default function ResourcesTable({
.map((header) => (
<TableHead
key={header.id}
className={`whitespace-nowrap ${
header.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: header.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{header.isPlaceholder
? null
@@ -1359,6 +1371,15 @@ export default function ResourcesTable({
key={
cell.id
}
className={`whitespace-nowrap ${
cell.column.id ===
"actions"
? "sticky right-0 z-10 w-auto min-w-fit bg-card"
: cell.column.id ===
"name"
? "md:sticky md:left-0 z-10 bg-card"
: ""
}`}
>
{flexRender(
cell
@@ -1387,6 +1408,7 @@ export default function ResourcesTable({
)}
</TableBody>
</Table>
</div>
<div className="mt-4">
<DataTablePagination
table={internalTable}

View File

@@ -36,6 +36,9 @@ export function RolesDataTable<TData, TValue>({
onRefresh={onRefresh}
isRefreshing={isRefreshing}
addButtonText={t('accessRolesAdd')}
enableColumnVisibility={true}
stickyLeftColumn="name"
stickyRightColumn="actions"
/>
);
}

View File

@@ -64,6 +64,7 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
const columns: ColumnDef<RoleRow>[] = [
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -84,12 +85,13 @@ export default function UsersTable({ roles: r }: RolesTableProps) {
},
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const roleRow = row.original;
return (
<div className="flex items-center">
<div className="flex items-center gap-2 justify-end">
<Button
variant={"outline"}
disabled={roleRow.isAdmin || false}

View File

@@ -36,6 +36,9 @@ export function ShareLinksDataTable<TData, TValue>({
onRefresh={onRefresh}
isRefreshing={isRefreshing}
addButtonText={t('shareCreate')}
enableColumnVisibility={true}
stickyLeftColumn="resourceName"
stickyRightColumn="delete"
/>
);
}

View File

@@ -105,6 +105,7 @@ export default function ShareLinksTable({
const columns: ColumnDef<ShareLinkRow>[] = [
{
accessorKey: "resourceName",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -254,11 +255,12 @@ export default function ShareLinksTable({
},
{
id: "delete",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const resourceRow = row.original;
return (
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-2 justify-end">
{/* <DropdownMenu> */}
{/* <DropdownMenuTrigger asChild> */}
{/* <Button variant="ghost" className="h-8 w-8 p-0"> */}

View File

@@ -44,6 +44,8 @@ export function SitesDataTable<TData, TValue>({
}}
columnVisibility={columnVisibility}
enableColumnVisibility={enableColumnVisibility}
stickyLeftColumn="name"
stickyRightColumn="actions"
/>
);
}

View File

@@ -109,6 +109,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
const columns: ColumnDef<SiteRow>[] = [
{
accessorKey: "name",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -361,19 +362,12 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
: []),
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const siteRow = row.original;
return (
<div className="flex items-center gap-2">
<Link
href={`/${siteRow.orgId}/settings/sites/${siteRow.nice}`}
>
<Button variant={"outline"}>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -402,6 +396,14 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) {
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link
href={`/${siteRow.orgId}/settings/sites/${siteRow.nice}`}
>
<Button variant={"outline"}>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
);
}

View File

@@ -36,6 +36,9 @@ export function UsersDataTable<TData, TValue>({
onRefresh={onRefresh}
isRefreshing={isRefreshing}
addButtonText={t('accessUserCreate')}
enableColumnVisibility={true}
stickyLeftColumn="displayUsername"
stickyRightColumn="actions"
/>
);
}

View File

@@ -73,6 +73,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
const columns: ColumnDef<UserRow>[] = [
{
accessorKey: "displayUsername",
enableHiding: false,
header: ({ column }) => {
return (
<Button
@@ -133,9 +134,6 @@ export default function UsersTable({ users: u }: UsersTableProps) {
return (
<div className="flex flex-row items-center gap-2">
{userRow.isOwner && (
<Crown className="w-4 h-4 text-yellow-600" />
)}
<span>{userRow.role}</span>
</div>
);
@@ -143,21 +141,58 @@ export default function UsersTable({ users: u }: UsersTableProps) {
},
{
id: "actions",
header: () => (<span className="p-3">{t("actions")}</span>),
enableHiding: false,
header: () => <span className="p-3"></span>,
cell: ({ row }) => {
const userRow = row.original;
return (
<div className="flex items-center">
{userRow.isOwner && (
<Button
variant={"outline"}
className="ml-2"
disabled={true}
>
{t("manage")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
)}
<div className="flex items-center justify-end">
<div>
{!userRow.isOwner && (
<>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="h-8 w-8 p-0"
>
<span className="sr-only">
{t("openMenu")}
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<Link
href={`/${org?.org.orgId}/settings/access/users/${userRow.id}`}
className="block w-full"
>
<DropdownMenuItem>
{t("accessUsersManage")}
</DropdownMenuItem>
</Link>
{`${userRow.username}-${userRow.idpId}` !==
`${user?.username}-${user?.idpId}` && (
<DropdownMenuItem
onClick={() => {
setIsDeleteModalOpen(
true
);
setSelectedUser(
userRow
);
}}
>
<span className="text-red-500">
{t("accessUserRemove")}
</span>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</>
)}
</div>
{!userRow.isOwner && (
<Link
href={`/${org?.org.orgId}/settings/access/users/${userRow.id}`}
@@ -172,59 +207,6 @@ export default function UsersTable({ users: u }: UsersTableProps) {
</Button>
</Link>
)}
<>
<div>
{userRow.isOwner && (
<MoreHorizontal className="h-4 w-4 opacity-0" />
)}
{!userRow.isOwner && (
<>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="h-8 w-8 p-0"
>
<span className="sr-only">
{t("openMenu")}
</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<Link
href={`/${org?.org.orgId}/settings/access/users/${userRow.id}`}
className="block w-full"
>
<DropdownMenuItem>
{t("accessUsersManage")}
</DropdownMenuItem>
</Link>
{`${userRow.username}-${userRow.idpId}` !==
`${user?.username}-${user?.idpId}` && (
<DropdownMenuItem
onClick={() => {
setIsDeleteModalOpen(
true
);
setSelectedUser(
userRow
);
}}
>
<span className="text-red-500">
{t(
"accessUserRemove"
)}
</span>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</>
)}
</div>
</>
</div>
);
}
@@ -273,9 +255,7 @@ export default function UsersTable({ users: u }: UsersTableProps) {
}}
dialog={
<div>
<p>
{t("userQuestionOrgRemove")}
</p>
<p>{t("userQuestionOrgRemove")}</p>
<p>{t("userMessageOrgRemove")}</p>
</div>
}

View File

@@ -121,16 +121,7 @@ export default function IdpTable({ idps, orgId }: Props) {
cell: ({ row }) => {
const siteRow = row.original;
return (
<div className="flex items-center justify-end">
<Link href={`/${orgId}/settings/idp/${siteRow.idpId}/general`}>
<Button
variant={"outline"}
className="ml-2"
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
<div className="flex items-center gap-2 justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
@@ -161,6 +152,14 @@ export default function IdpTable({ idps, orgId }: Props) {
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link href={`/${orgId}/settings/idp/${siteRow.idpId}/general`}>
<Button
variant={"outline"}
>
{t("edit")}
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</Link>
</div>
);
}

View File

@@ -152,6 +152,8 @@ type DataTableProps<TData, TValue> = {
columnVisibility?: Record<string, boolean>;
enableColumnVisibility?: boolean;
persistColumnVisibility?: boolean | string;
stickyLeftColumn?: string; // Column ID or accessorKey for left sticky column
stickyRightColumn?: string; // Column ID or accessorKey for right sticky column (typically "actions")
};
export function DataTable<TData, TValue>({
@@ -171,7 +173,9 @@ export function DataTable<TData, TValue>({
defaultPageSize = 20,
columnVisibility: defaultColumnVisibility,
enableColumnVisibility = false,
persistColumnVisibility = false
persistColumnVisibility = false,
stickyLeftColumn,
stickyRightColumn
}: DataTableProps<TData, TValue>) {
const t = useTranslations();
@@ -290,6 +294,28 @@ export function DataTable<TData, TValue>({
}
};
// Helper function to check if a column should be sticky
const isStickyColumn = (columnId: string | undefined, accessorKey: string | undefined, position: "left" | "right"): boolean => {
if (position === "left" && stickyLeftColumn) {
return columnId === stickyLeftColumn || accessorKey === stickyLeftColumn;
}
if (position === "right" && stickyRightColumn) {
return columnId === stickyRightColumn || accessorKey === stickyRightColumn;
}
return false;
};
// Get sticky column classes
const getStickyClasses = (columnId: string | undefined, accessorKey: string | undefined): string => {
if (isStickyColumn(columnId, accessorKey, "left")) {
return "md:sticky md:left-0 z-10 bg-card";
}
if (isStickyColumn(columnId, accessorKey, "right")) {
return "sticky right-0 z-10 w-auto min-w-fit bg-card";
}
return "";
};
return (
<div className="container mx-auto max-w-12xl">
<Card>
@@ -329,131 +355,150 @@ export function DataTable<TData, TValue>({
)}
</div>
<div className="flex items-center gap-2 sm:justify-end">
{enableColumnVisibility &&
table
.getAllColumns()
.some((column) => column.getCanHide()) && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<Columns className="mr-0 sm:mr-2 h-4 w-4" />
<span className="hidden sm:inline">
{t("columns") || "Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="w-48"
>
<DropdownMenuLabel>
{t("toggleColumns") || "Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(
!!value
)
}
>
{typeof column.columnDef
.header === "string"
? column.columnDef
.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
)}
{onRefresh && (
<Button
variant="outline"
onClick={onRefresh}
disabled={isRefreshing}
>
<RefreshCw
className={`mr-0 sm:mr-2 h-4 w-4 ${isRefreshing ? "animate-spin" : ""}`}
/>
<span className="hidden sm:inline">
{t("refresh")}
</span>
</Button>
<div>
<Button
variant="outline"
onClick={onRefresh}
disabled={isRefreshing}
>
<RefreshCw
className={`mr-0 sm:mr-2 h-4 w-4 ${isRefreshing ? "animate-spin" : ""}`}
/>
<span className="hidden sm:inline">
{t("refresh")}
</span>
</Button>
</div>
)}
{onAdd && addButtonText && (
<Button onClick={onAdd}>
<Plus className="mr-2 h-4 w-4" />
{addButtonText}
</Button>
<div>
<Button onClick={onAdd}>
<Plus className="mr-2 h-4 w-4" />
{addButtonText}
</Button>
</div>
)}
</div>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead
key={header.id}
className="whitespace-nowrap"
>
{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}
className="whitespace-nowrap"
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
<div className="overflow-x-auto">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
const columnId = header.column.id;
const accessorKey = (header.column.columnDef as any).accessorKey as string | undefined;
const stickyClasses = getStickyClasses(columnId, accessorKey);
const isRightSticky = isStickyColumn(columnId, accessorKey, "right");
const hasHideableColumns = enableColumnVisibility &&
table.getAllColumns().some((col) => col.getCanHide());
return (
<TableHead
key={header.id}
className={`whitespace-nowrap ${stickyClasses}`}
>
{header.isPlaceholder ? null : (
isRightSticky && hasHideableColumns ? (
<div className="flex flex-col items-end pr-3">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="h-7 w-7 p-0 mb-1">
<Columns className="h-4 w-4" />
<span className="sr-only">
{t("columns") || "Columns"}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>
{t("toggleColumns") || "Toggle columns"}
</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{typeof column.columnDef.header === "string"
? column.columnDef.header
: column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
<div className="h-0 opacity-0 pointer-events-none overflow-hidden">
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</div>
</div>
) : (
flexRender(
header.column.columnDef.header,
header.getContext()
)
)
)}
</TableHead>
);
})}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results found.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={
row.getIsSelected() && "selected"
}
>
{row.getVisibleCells().map((cell) => {
const columnId = cell.column.id;
const accessorKey = (cell.column.columnDef as any).accessorKey as string | undefined;
const stickyClasses = getStickyClasses(columnId, accessorKey);
const isRightSticky = isStickyColumn(columnId, accessorKey, "right");
return (
<TableCell
key={cell.id}
className={`whitespace-nowrap ${stickyClasses} ${isRightSticky ? "text-right" : ""}`}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
);
})}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results found.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="mt-4">
<DataTablePagination
table={table}