This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new eeee8ac9 fix(ui): P0 frontend fixes - gzip, delete confirm, audit 
search debounce, StrictMode (#1453)
eeee8ac9 is described below

commit eeee8ac979d59e687ab60d2835c9dbdaa2f4c7cf
Author: zhaohai <[email protected]>
AuthorDate: Mon Aug 10 20:59:18 2026 +0800

    fix(ui): P0 frontend fixes - gzip, delete confirm, audit search debounce, 
StrictMode (#1453)
    
    * fix: batch of small backend validation and export fixes (#1103, #1105, 
#1233, #1241, #1242)
    
    * fix(rocketmq): audit failure must not fail an already-delivered message
    
    * fix(rocketmq): tolerate missing broker tables in dashboard overview
    
    * fix(ai): close error response stream in LLM client
    
    * fix(ai): enforce stream timeout and avoid pipe deadlock in CLI agent
    
    * fix(cluster): keep write/read queue counts in sync on partial updates
    
    * fix(ai): persist maxTokens and temperature across restarts
    
    * fix(ai): bound the LLM gateway thread pool to avoid thread exhaustion
    
    * fix(dlq): tolerate single-queue scan failures during resend collection
    
    * fix(topic): validate required topic on message send
    
    * fix(api): return 400 for invalid request parameters
    
    * fix(topic): require newTTL on TTL update
    
    * fix(topic): require positive queue counts on topic creation
    
    * fix(metrics): validate nested query in datasource queries
    
    * chore: keep only this change on top of the batch branch
    
    ---------
    
    Co-authored-by: yyqdbngt <[email protected]>
    
    * fix(web): gzip serving, delete confirmations, audit search debounce, 
StrictMode remount fix
    
    - nginx: enable gzip for text assets so the ~1MB entry bundle is no
      longer served uncompressed in the Docker deployment
    - GroupManagement: reset mountedRef on remount; under StrictMode the
      cleanup cleared it and nothing restored it, so the remounted list
      load never applied and the page stayed loading forever in dev
    - alerts/settings: require Popconfirm before deleting an alert rule or
      a data source, matching the confirmation convention used elsewhere
    - audit: debounce the free-text search (300ms) so the record list is
      not re-fetched on every keystroke
    
    ---------
    
    Co-authored-by: yyqdbngt <[email protected]>
    Co-authored-by: yyqdbngt <[email protected]>
---
 web/nginx.conf                           |  8 ++++++++
 web/src/pages/ops/alerts.tsx             | 27 +++++++++++++++++----------
 web/src/pages/ops/audit.tsx              | 12 ++++++++++--
 web/src/pages/settings/index.tsx         | 18 ++++++++++--------
 web/src/pages/studio/GroupManagement.tsx |  3 +++
 5 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/web/nginx.conf b/web/nginx.conf
index 11b9786d..41833596 100644
--- a/web/nginx.conf
+++ b/web/nginx.conf
@@ -2,6 +2,14 @@ server {
     listen 80;
     server_name _;
 
+    # Compress text assets; the entry JS bundle is ~1MB uncompressed.
+    gzip on;
+    gzip_comp_level 5;
+    gzip_min_length 1024;
+    gzip_proxied any;
+    gzip_vary on;
+    gzip_types text/plain text/css application/javascript application/json 
image/svg+xml;
+
     # RESOLVER is injected by 15-resolver.sh (podman network gateway only).
     # Variable proxy_pass re-resolves on every request, so a recreated
     # rocketmq-server container with a new IP is picked up without restarting
diff --git a/web/src/pages/ops/alerts.tsx b/web/src/pages/ops/alerts.tsx
index 62b8a890..65e93c73 100644
--- a/web/src/pages/ops/alerts.tsx
+++ b/web/src/pages/ops/alerts.tsx
@@ -31,6 +31,7 @@ import {
   Checkbox,
   Flex,
   message,
+  Popconfirm,
   theme,
 } from 'antd';
 import type { ColumnsType, TableRowSelection } from 'antd/es/table/interface';
@@ -274,17 +275,23 @@ const AlertsPage = () => {
           >
             {t('common.edit')}
           </Button>
-          <Button
-            size="small"
-            icon={<Trash size={14} />}
-            danger
-            loading={actionId === `delete-${record.id}`}
-            disabled={isActionRunning}
-            style={{ borderColor: '#ff4d4f', color: '#ff4d4f' }}
-            onClick={() => void handleDelete(record)}
+          <Popconfirm
+            title={t('common.areYouSureToDelete')}
+            onConfirm={() => void handleDelete(record)}
+            okText={t('common.confirm')}
+            cancelText={t('common.cancel')}
           >
-            {t('common.delete')}
-          </Button>
+            <Button
+              size="small"
+              icon={<Trash size={14} />}
+              danger
+              loading={actionId === `delete-${record.id}`}
+              disabled={isActionRunning}
+              style={{ borderColor: '#ff4d4f', color: '#ff4d4f' }}
+            >
+              {t('common.delete')}
+            </Button>
+          </Popconfirm>
         </Flex>
       ),
     },
diff --git a/web/src/pages/ops/audit.tsx b/web/src/pages/ops/audit.tsx
index 68a82473..d3f3b710 100644
--- a/web/src/pages/ops/audit.tsx
+++ b/web/src/pages/ops/audit.tsx
@@ -89,6 +89,7 @@ const AuditPage: React.FC = () => {
   const [loading, setLoading] = useState(true);
   const [refreshKey, setRefreshKey] = useState(0);
   const [searchText, setSearchText] = useState('');
+  const [debouncedSearchText, setDebouncedSearchText] = useState('');
   const [selectedType, setSelectedType] = useState<string | 
undefined>(undefined);
   const [selectedResourceType, setSelectedResourceType] = useState<string | 
undefined>(undefined);
   const [selectedClusterId, setSelectedClusterId] = useState<string | 
undefined>(undefined);
@@ -115,6 +116,13 @@ const AuditPage: React.FC = () => {
     };
   }, [refreshKey]);
 
+  // Debounce free-text search so the record list is not re-fetched on every
+  // keystroke; typing pauses for 300ms before the query hits the server.
+  useEffect(() => {
+    const timer = window.setTimeout(() => setDebouncedSearchText(searchText), 
300);
+    return () => window.clearTimeout(timer);
+  }, [searchText]);
+
   useEffect(() => {
     let cancelled = false;
 
@@ -122,7 +130,7 @@ const AuditPage: React.FC = () => {
       page,
       pageSize,
       ...buildAuditFilter(
-        searchText,
+        debouncedSearchText,
         selectedType,
         selectedResourceType,
         selectedClusterId,
@@ -148,7 +156,7 @@ const AuditPage: React.FC = () => {
   }, [
     page,
     pageSize,
-    searchText,
+    debouncedSearchText,
     selectedType,
     selectedResourceType,
     selectedClusterId,
diff --git a/web/src/pages/settings/index.tsx b/web/src/pages/settings/index.tsx
index 64bb0bb0..cc9d9b55 100644
--- a/web/src/pages/settings/index.tsx
+++ b/web/src/pages/settings/index.tsx
@@ -26,6 +26,7 @@ import {
   Input,
   InputNumber,
   Modal,
+  Popconfirm,
   Radio,
   Select,
   Space,
@@ -447,15 +448,16 @@ export const DataSourceTab = () => {
           >
             编辑
           </Button>
-          <Button
-            type="link"
-            size="small"
-            danger
-            icon={<DeleteOutlined />}
-            onClick={() => void handleDelete(record)}
+          <Popconfirm
+            title="确定要删除该数据源吗?"
+            onConfirm={() => void handleDelete(record)}
+            okText="确定"
+            cancelText="取消"
           >
-            删除
-          </Button>
+            <Button type="link" size="small" danger icon={<DeleteOutlined />}>
+              删除
+            </Button>
+          </Popconfirm>
         </Space>
       ),
     },
diff --git a/web/src/pages/studio/GroupManagement.tsx 
b/web/src/pages/studio/GroupManagement.tsx
index 4459e2d1..f3c01ee1 100644
--- a/web/src/pages/studio/GroupManagement.tsx
+++ b/web/src/pages/studio/GroupManagement.tsx
@@ -106,6 +106,9 @@ const GroupManagementPage = () => {
   }, [t]);
 
   useEffect(() => {
+    // Reset on (re)mount: under StrictMode the previous cleanup has already
+    // cleared the flag, and without this the remounted load never applies.
+    mountedRef.current = true;
     const timeoutId = window.setTimeout(() => {
       void loadGroups();
     });

Reply via email to