Address review: tidy pagination handling and comments

Let useTableQueryState accept a full TablePaginationConfig (only defaultPageSize
is read) so UsersTable passes its pagination prop straight through instead of
extracting and casting it. Drop the redundant pagination: false from the tables
that just want it disabled (that is already the default). Clarify the comment on
the Last Seen online-only default to mention the onlyOnline prop, and explain why
onChange only uses the first sorter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 4.8 committed 2026-06-29 22:26:39 +00:00
1 parent 8cacdc5112
commit 1e7b8f08cf
9 files changed
+19 -17

No files matched your search

+1 -1
View File
@@ -208,7 +208,7 @@ export default function HostsPage() {
const [showNumbers, setShowNumbers] = useState(false);
const [showCpuModel, setShowCpuModel] = useState(false);
const tableState = useTableQueryState<lib.HostDetails>({
namespace: "host", defaultSortKey: "name", pagination: false,
namespace: "host", defaultSortKey: "name",
});
const nameSearch = useColumnSearch<lib.HostDetails>(host => host.name, "Search hosts");
@@ -31,7 +31,7 @@ export default function InstanceList(props: InstanceListProps) {
let [hosts] = useHosts();
const [systems] = useSystems();
const tableState = useTableQueryState<lib.InstanceDetails>({
namespace: "instance", defaultSortKey: "name", pagination: false,
namespace: "instance", defaultSortKey: "name",
});
const nameSearch = useColumnSearch<lib.InstanceDetails>(instance => instance.name, "Search instances");
+2 -2
View File
@@ -511,10 +511,10 @@ export default function ModsPage() {
let [mods] = useMods();
let [modPacks] = useModPacks();
const modPackTable = useTableQueryState<lib.ModPack>({
namespace: "modPack", defaultSortKey: "name", pagination: false,
namespace: "modPack", defaultSortKey: "name",
});
const modTable = useTableQueryState<lib.ModInfo>({
namespace: "mod", defaultSortKey: "title", pagination: false,
namespace: "mod", defaultSortKey: "title",
});
function actions(mod: lib.ModInfo) {
@@ -27,7 +27,7 @@ export default function PluginsPage() {
let navigate = useNavigate();
let [pluginList, setPluginList] = useState<PluginWebApi[]>([]);
const tableState = useTableQueryState<PluginRow>({
namespace: "plugin", defaultSortKey: "name", pagination: false,
namespace: "plugin", defaultSortKey: "name",
});
const nameSearch = useColumnSearch<PluginRow>(
plugin => (plugin.info ? plugin.info.title : plugin.meta.name), "Search plugins"
+1 -1
View File
@@ -65,7 +65,7 @@ export default function RolesPage() {
let account = useAccount();
let navigate = useNavigate();
const [roles] = useRoles();
const tableState = useTableQueryState<lib.Role>({ namespace: "role", pagination: false });
const tableState = useTableQueryState<lib.Role>({ namespace: "role" });
const nameSearch = useColumnSearch<lib.Role>(role => role.name, "Search roles");
return <PageLayout nav={[{ name: "Roles" }]}>
+1 -1
View File
@@ -206,7 +206,7 @@ export default function SavesList(props: { instance: lib.InstanceDetails }) {
let [starting, setStarting] = useState(false);
let [uploadingFiles, setUploadingFiles] = useState<File[]>([]);
const tableState = useTableQueryState<lib.SaveDetails>({
namespace: "save", defaultSortKey: "mtimeMs", defaultSortOrder: "descend", pagination: false,
namespace: "save", defaultSortKey: "mtimeMs", defaultSortOrder: "descend",
});
let hostOffline = ["unassigned", "unknown"].includes(props.instance.status!);
@@ -40,7 +40,7 @@ export default function UserViewPage() {
let [roles] = useRoles();
let [form] = Form.useForm();
const statsTable = useTableQueryState<[number, lib.PlayerStats]>({
namespace: "stats", defaultSortKey: "instance", pagination: false,
namespace: "stats", defaultSortKey: "instance",
});
let [rolesDirty, setRolesDirty] = useState<boolean>(false);
let [banReasonDirty, setBanReasonDirty] = useState<boolean>(false);
@@ -1,5 +1,5 @@
import React from "react";
import { Table, Tag } from "antd";
import { Table, Tag, type TablePaginationConfig } from "antd";
import { SearchOutlined } from "@ant-design/icons";
import { useNavigate } from "react-router-dom";
@@ -23,7 +23,7 @@ export interface UsersTableProps {
/** Show only players that are currently online, works with instanceId to show online of a particular instance */
onlyOnline?: boolean;
/** Ant Design pagination prop. Pass `false` to disable pagination. */
pagination?: false | object;
pagination?: false | TablePaginationConfig;
/** Ant Design size prop */
size?: "small" | "middle" | "large";
}
@@ -36,12 +36,11 @@ export default function UsersTable(
const [roles, rolesSynced] = useRoles();
const [users, usersSynced] = useUsers();
const navigate = useNavigate();
const callerPagination = pagination && typeof pagination === "object" ? pagination : undefined;
const defaultPageSize = (callerPagination as { defaultPageSize?: number })?.defaultPageSize ?? 50;
const tableState = useTableQueryState<lib.UserDetails>({
namespace: "user",
defaultSortKey: "name",
pagination: pagination === false ? false : { defaultPageSize },
// Default to 50 per page when the caller does not specify a pagination config.
pagination: pagination === false ? false : (pagination ?? { defaultPageSize: 50 }),
filterCodecs: { name: userFilterCodec },
});
@@ -168,7 +167,8 @@ export default function UsersTable(
key: "lastSeen",
filterMultiple: false,
filters: durationFilters,
// Default to online-only when nothing for this column is in the URL yet.
// With the onlyOnline prop set, seed this column's filter to "online" until the
// URL provides one, so the table starts out showing only online players.
filteredValue: tableState.filteredValue("lastSeen") ?? (onlyOnline ? ["online"] : null),
onFilter: (value: any, record: lib.UserDetails) => onFilterDuration(value, record, calculateLastSeen),
sorter: (a: lib.UserDetails, b: lib.UserDetails) => sortLastSeen(a, b, instanceId, instanceId),
@@ -187,7 +187,7 @@ export default function UsersTable(
tablePagination = false;
} else {
// Controlled current/pageSize from the URL win over the caller's display props.
tablePagination = { ...displayPagination, ...callerPagination, ...(tableState.pagination || {}) };
tablePagination = { ...displayPagination, ...pagination, ...(tableState.pagination || {}) };
}
return (
@@ -15,8 +15,8 @@ export interface TableQueryStateOptions {
defaultSortKey?: string;
/** Direction of the default sort. Default "ascend". */
defaultSortOrder?: SortOrder;
/** Pass an object to persist pagination; omit/false for `pagination={false}` tables. */
pagination?: false | { defaultPageSize?: number };
/** Pagination config to persist pagination; only `defaultPageSize` is read (omit for `pagination={false}`). */
pagination?: false | TablePaginationConfig;
/** Per-column-key codecs to persist a bundled filter value as separate readable params. */
filterCodecs?: Record<string, FilterCodec>;
}
@@ -59,6 +59,8 @@ export default function useTableQueryState<T>(options: TableQueryStateOptions):
return {
onChange(pagination, tableFilters, sorter) {
// Only single-column sort is persisted; antd passes an array when several
// columns are sorted, so take the first (primary) sorter.
const single = Array.isArray(sorter) ? sorter[0] : sorter;
const order = single && single.order ? single.order : undefined;
const columnKey = order ? String(single.columnKey ?? single.field ?? "") : undefined;