Adds column sorting to Nginx tables

This commit is contained in:
DOLBA3B
2026-05-05 21:12:04 +02:00
parent 34374cbe09
commit ff066d5626
5 changed files with 117 additions and 10 deletions
+29 -2
View File
@@ -1,3 +1,5 @@
import { IconArrowsSort, IconChevronDown, IconChevronUp } from "@tabler/icons-react";
import { flexRender } from "@tanstack/react-table";
import type { TableLayoutProps } from "src/components";
function TableHeader<T>(props: TableLayoutProps<T>) {
@@ -11,9 +13,34 @@ function TableHeader<T>(props: TableLayoutProps<T>) {
{headerGroup.headers.map((header: any) => {
const { column } = header;
const { className } = (column.columnDef.meta as any) ?? {};
const canSort = column.getCanSort();
const sortDir = column.getIsSorted();
const headerContent = header.isPlaceholder
? null
: typeof column.columnDef.header === "string"
? column.columnDef.header
: flexRender(column.columnDef.header, header.getContext());
const sortIcon = canSort ? (
sortDir === "asc" ? (
<IconChevronUp size={14} className="ms-1" />
) : sortDir === "desc" ? (
<IconChevronDown size={14} className="ms-1" />
) : (
<IconArrowsSort size={14} className="ms-1 opacity-50" />
)
) : null;
return (
<th key={header.id} className={className}>
{typeof column.columnDef.header === "string" ? `${column.columnDef.header}` : null}
<th
key={header.id}
className={className}
onClick={canSort ? column.getToggleSortingHandler() : undefined}
style={canSort ? { cursor: "pointer", userSelect: "none" } : undefined}
>
{headerContent}
{sortIcon}
</th>
);
})}
+20 -2
View File
@@ -1,6 +1,12 @@
import { IconDotsVertical, IconEdit, IconPower, IconTrash } from "@tabler/icons-react";
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { useMemo } from "react";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
type SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useMemo, useState } from "react";
import type { DeadHost } from "src/api/backend";
import {
CertificateFormatter,
@@ -29,6 +35,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
() => [
columnHelper.accessor((row: any) => row.owner, {
id: "owner",
enableSorting: false,
cell: (info: any) => {
const value = info.getValue();
return <GravatarFormatter url={value ? value.avatar : ""} name={value ? value.name : ""} />;
@@ -40,6 +47,11 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
columnHelper.accessor((row: any) => row, {
id: "domainNames",
header: intl.formatMessage({ id: "column.source" }),
sortingFn: (a, b) => {
const aVal = a.original.domainNames?.[0] ?? "";
const bVal = b.original.domainNames?.[0] ?? "";
return aVal.localeCompare(bVal);
},
cell: (info: any) => {
const value = info.getValue();
return <DomainsFormatter domains={value.domainNames} createdOn={value.createdOn} />;
@@ -47,6 +59,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
}),
columnHelper.accessor((row: any) => row.certificate, {
id: "certificate",
enableSorting: false,
header: intl.formatMessage({ id: "column.ssl" }),
cell: (info: any) => {
return <CertificateFormatter certificate={info.getValue()} />;
@@ -128,10 +141,15 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
[columnHelper, onDelete, onEdit, onDisableToggle],
);
const [sorting, setSorting] = useState<SortingState>([]);
const tableInstance = useReactTable<DeadHost>({
columns,
data,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
rowCount: data.length,
meta: {
isFetching,
+26 -2
View File
@@ -1,6 +1,12 @@
import { IconDotsVertical, IconEdit, IconPower, IconTrash } from "@tabler/icons-react";
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { useMemo } from "react";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
type SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useMemo, useState } from "react";
import type { ProxyHost } from "src/api/backend";
import {
AccessListFormatter,
@@ -30,6 +36,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
() => [
columnHelper.accessor((row: any) => row.owner, {
id: "owner",
enableSorting: false,
cell: (info: any) => {
const value = info.getValue();
return <GravatarFormatter url={value ? value.avatar : ""} name={value ? value.name : ""} />;
@@ -41,6 +48,11 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
columnHelper.accessor((row: any) => row, {
id: "domainNames",
header: intl.formatMessage({ id: "column.source" }),
sortingFn: (a, b) => {
const aVal = a.original.domainNames?.[0] ?? "";
const bVal = b.original.domainNames?.[0] ?? "";
return aVal.localeCompare(bVal);
},
cell: (info: any) => {
const value = info.getValue();
return <DomainsFormatter domains={value.domainNames} createdOn={value.createdOn} />;
@@ -49,6 +61,11 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
columnHelper.accessor((row: any) => row, {
id: "forwardHost",
header: intl.formatMessage({ id: "column.destination" }),
sortingFn: (a, b) => {
const aVal = `${a.original.forwardHost}:${a.original.forwardPort}`;
const bVal = `${b.original.forwardHost}:${b.original.forwardPort}`;
return aVal.localeCompare(bVal);
},
cell: (info: any) => {
const value = info.getValue();
return `${value.forwardScheme}://${value.forwardHost}:${value.forwardPort}`;
@@ -56,6 +73,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
}),
columnHelper.accessor((row: any) => row.certificate, {
id: "certificate",
enableSorting: false,
header: intl.formatMessage({ id: "column.ssl" }),
cell: (info: any) => {
return <CertificateFormatter certificate={info.getValue()} />;
@@ -63,6 +81,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
}),
columnHelper.accessor((row: any) => row.accessList, {
id: "accessList",
enableSorting: false,
header: intl.formatMessage({ id: "column.access" }),
cell: (info: any) => {
return <AccessListFormatter access={info.getValue()} />;
@@ -144,10 +163,15 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
[columnHelper, onEdit, onDisableToggle, onDelete],
);
const [sorting, setSorting] = useState<SortingState>([]);
const tableInstance = useReactTable<ProxyHost>({
columns,
data,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
rowCount: data.length,
meta: {
isFetching,
@@ -1,6 +1,12 @@
import { IconDotsVertical, IconEdit, IconPower, IconTrash } from "@tabler/icons-react";
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { useMemo } from "react";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
type SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useMemo, useState } from "react";
import type { RedirectionHost } from "src/api/backend";
import {
CertificateFormatter,
@@ -29,6 +35,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
() => [
columnHelper.accessor((row: any) => row.owner, {
id: "owner",
enableSorting: false,
cell: (info: any) => {
const value = info.getValue();
return <GravatarFormatter url={value ? value.avatar : ""} name={value ? value.name : ""} />;
@@ -40,6 +47,11 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
columnHelper.accessor((row: any) => row, {
id: "domainNames",
header: intl.formatMessage({ id: "column.source" }),
sortingFn: (a, b) => {
const aVal = a.original.domainNames?.[0] ?? "";
const bVal = b.original.domainNames?.[0] ?? "";
return aVal.localeCompare(bVal);
},
cell: (info: any) => {
const value = info.getValue();
return <DomainsFormatter domains={value.domainNames} createdOn={value.createdOn} />;
@@ -68,6 +80,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
}),
columnHelper.accessor((row: any) => row.certificate, {
id: "certificate",
enableSorting: false,
header: intl.formatMessage({ id: "column.ssl" }),
cell: (info: any) => {
return <CertificateFormatter certificate={info.getValue()} />;
@@ -149,10 +162,15 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
[columnHelper, onEdit, onDisableToggle, onDelete],
);
const [sorting, setSorting] = useState<SortingState>([]);
const tableInstance = useReactTable<RedirectionHost>({
columns,
data,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
rowCount: data.length,
meta: {
isFetching,
+22 -2
View File
@@ -1,6 +1,12 @@
import { IconDotsVertical, IconEdit, IconPower, IconTrash } from "@tabler/icons-react";
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { useMemo } from "react";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
type SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useMemo, useState } from "react";
import type { Stream } from "src/api/backend";
import {
CertificateFormatter,
@@ -29,6 +35,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
() => [
columnHelper.accessor((row: any) => row.owner, {
id: "owner",
enableSorting: false,
cell: (info: any) => {
const value = info.getValue();
return <GravatarFormatter url={value ? value.avatar : ""} name={value ? value.name : ""} />;
@@ -40,6 +47,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
columnHelper.accessor((row: any) => row, {
id: "incomingPort",
header: intl.formatMessage({ id: "column.incoming-port" }),
sortingFn: (a, b) => (a.original.incomingPort ?? 0) - (b.original.incomingPort ?? 0),
cell: (info: any) => {
const value = info.getValue();
return <ValueWithDateFormatter value={value.incomingPort} createdOn={value.createdOn} />;
@@ -48,6 +56,11 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
columnHelper.accessor((row: any) => row, {
id: "forwardHttpCode",
header: intl.formatMessage({ id: "column.destination" }),
sortingFn: (a, b) => {
const aVal = `${a.original.forwardingHost}:${a.original.forwardingPort}`;
const bVal = `${b.original.forwardingHost}:${b.original.forwardingPort}`;
return aVal.localeCompare(bVal);
},
cell: (info: any) => {
const value = info.getValue();
return `${value.forwardingHost}:${value.forwardingPort}`;
@@ -55,6 +68,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
}),
columnHelper.accessor((row: any) => row, {
id: "tcpForwarding",
enableSorting: false,
header: intl.formatMessage({ id: "column.protocol" }),
cell: (info: any) => {
const value = info.getValue();
@@ -76,6 +90,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
}),
columnHelper.accessor((row: any) => row.certificate, {
id: "certificate",
enableSorting: false,
header: intl.formatMessage({ id: "column.ssl" }),
cell: (info: any) => {
return <CertificateFormatter certificate={info.getValue()} />;
@@ -157,10 +172,15 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
[columnHelper, onEdit, onDisableToggle, onDelete],
);
const [sorting, setSorting] = useState<SortingState>([]);
const tableInstance = useReactTable<Stream>({
columns,
data,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
rowCount: data.length,
meta: {
isFetching,