This is an automated email from the ASF dual-hosted git repository.
hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git
The following commit(s) were added to refs/heads/master by this push:
new 5e4466d fix(web): keep the table page while data refreshes
5e4466d is described below
commit 5e4466d7b84838a56c439f998e6d23876fe3648b
Author: Yang Luo <[email protected]>
AuthorDate: Thu Sep 3 13:33:56 2026 +0800
fix(web): keep the table page while data refreshes
---
web/src/components/shared/data-table.tsx | 18 ++++++++++++++
web/src/pages/AgentSessionsPage.tsx | 40 ++++++++++++++++++++++++--------
2 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/web/src/components/shared/data-table.tsx
b/web/src/components/shared/data-table.tsx
index 50e32a1..e2415c5 100644
--- a/web/src/components/shared/data-table.tsx
+++ b/web/src/components/shared/data-table.tsx
@@ -216,6 +216,9 @@ export function DataTable<T>({
getPaginationRowModel: browserPaginates ? getPaginationRowModel() :
undefined,
manualSorting: serverSorted,
getRowId: (record, index) => resolveRowKey(rowKey, record, index),
+ // Pages that poll hand over a fresh array every few seconds, which would
+ // otherwise throw the reader back to page 1 mid-read.
+ autoResetPageIndex: false,
// The default filter only sees accessor values, which would silently skip
// display columns. Matching the whole record keeps a search for a nested
// field working the way a reader expects.
@@ -238,6 +241,21 @@ export function DataTable<T>({
: table.getPageCount();
const showPagination = serverPagination ? total > currentPageSize ||
currentPage > 1 : browserPaginates && total > pageSize;
+ const pageIndex = table.getState().pagination.pageIndex;
+
+ React.useEffect(() => {
+ if (browserPaginates) {
+ table.setPageIndex(0);
+ }
+ }, [browserPaginates, globalFilter, table]);
+
+ // The rows behind the current page can go away while it is open.
+ React.useEffect(() => {
+ if (browserPaginates && pageIndex > 0 && pageIndex >= pageCount) {
+ table.setPageIndex(Math.max(0, pageCount - 1));
+ }
+ }, [browserPaginates, pageCount, pageIndex, table]);
+
const goTo = (nextPage: number) => {
if (serverPagination) {
serverPagination.onChange(nextPage, serverPagination.pageSize);
diff --git a/web/src/pages/AgentSessionsPage.tsx
b/web/src/pages/AgentSessionsPage.tsx
index f457a80..49f5468 100644
--- a/web/src/pages/AgentSessionsPage.tsx
+++ b/web/src/pages/AgentSessionsPage.tsx
@@ -27,20 +27,27 @@ import {SimpleTooltip} from "@/components/ui/tooltip";
import {MessageAlert} from "@/components/ui/alert";
import {Badge} from "@/components/ui/badge";
import {Button} from "@/components/ui/button";
+import {Label} from "@/components/ui/label";
+import {Switch} from "@/components/ui/switch";
import type {Account, AgentSession} from "@/types";
export default function AgentSessionsPage({account}: {account: Account}) {
const [sessions, setSessions] = React.useState<AgentSession[]>([]);
const [error, setError] = React.useState("");
const [loading, setLoading] = React.useState(false);
+ const [autoRefresh, setAutoRefresh] = React.useState(true);
const isAdmin = Setting.isAdminUser(account);
- const load = React.useCallback(() => {
+ // The poll below runs every few seconds and must not spin the refresh button
+ // or blank the rows the operator is reading.
+ const load = React.useCallback((foreground = true) => {
if (!isAdmin) {
return;
}
- setLoading(true);
+ if (foreground) {
+ setLoading(true);
+ }
AgentBackend.getAgentSessions()
.then(res => {
if (res.status === "ok") {
@@ -51,7 +58,11 @@ export default function AgentSessionsPage({account}:
{account: Account}) {
}
})
.catch(err => setError(err.message || String(err)))
- .then(() => setLoading(false));
+ .then(() => {
+ if (foreground) {
+ setLoading(false);
+ }
+ });
}, [isAdmin]);
React.useEffect(() => {
@@ -59,10 +70,13 @@ export default function AgentSessionsPage({account}:
{account: Account}) {
return undefined;
}
- load();
- const interval = setInterval(load, 3000);
+ load(true);
+ if (!autoRefresh) {
+ return undefined;
+ }
+ const interval = setInterval(() => load(false), 3000);
return () => clearInterval(interval);
- }, [isAdmin, load]);
+ }, [autoRefresh, isAdmin, load]);
if (!isAdmin) {
return <UnauthorizedResult />;
@@ -160,10 +174,16 @@ export default function AgentSessionsPage({account}:
{account: Account}) {
emptyIcon={MessageSquare}
emptyText={i18next.t("agent:No agent sessions yet")}
toolbar={
- <Button variant="outline" size="sm" onClick={load} loading={loading}>
- <RefreshCw />
- {i18next.t("general:Refresh")}
- </Button>
+ <>
+ <Label className="text-sm font-normal">
+ <Switch checked={autoRefresh} onCheckedChange={setAutoRefresh} />
+ {i18next.t("agent:Auto refresh")}
+ </Label>
+ <Button variant="outline" size="sm" onClick={() => load(true)}
loading={loading}>
+ <RefreshCw />
+ {i18next.t("general:Refresh")}
+ </Button>
+ </>
}
/>
</PageContainer>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]