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]

Reply via email to