This is an automated email from the ASF dual-hosted git repository.
dengliming pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new 4180a4d9 fix: preserve alert namespace on reload (#680)
4180a4d9 is described below
commit 4180a4d9b8fff9ddf49537daaa490e0c481bc547
Author: Carlos Acosta <[email protected]>
AuthorDate: Sun Sep 27 00:32:00 2026 -0500
fix: preserve alert namespace on reload (#680)
Co-authored-by: Aeontra <aeontra@localhost>
---
src/models/alert.js | 4 +-
src/models/alert.test.js | 49 ++++++++++++++++++++++++
src/routes/System/Alert/index.js | 45 +++++++++++++++++-----
src/routes/System/Alert/index.test.js | 71 +++++++++++++++++++++++++++++++++++
4 files changed, 158 insertions(+), 11 deletions(-)
diff --git a/src/models/alert.js b/src/models/alert.js
index 51332f22..02b818cd 100644
--- a/src/models/alert.js
+++ b/src/models/alert.js
@@ -96,8 +96,8 @@ export default {
},
*reload(params, { put }) {
const { fetchValue } = params;
- const { userName, currentPage, pageSize } = fetchValue;
- const payload = { userName, currentPage, pageSize };
+ const { currentPage, pageSize, namespaceId } = fetchValue;
+ const payload = { currentPage, pageSize, namespaceId };
yield put({ type: "fetch", payload });
},
},
diff --git a/src/models/alert.test.js b/src/models/alert.test.js
new file mode 100644
index 00000000..5a002628
--- /dev/null
+++ b/src/models/alert.test.js
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import model from "./alert";
+
+jest.mock("../services/api", () => ({
+ addAlertReceiver: jest.fn(),
+ deleteAlertReceivers: jest.fn(),
+ fetchAlertReport: jest.fn(),
+ getAlertReceiverDetail: jest.fn(),
+ getAlertReceivers: jest.fn(),
+ updateAlertReceiver: jest.fn(),
+}));
+jest.mock("../utils/IntlUtils", () => ({
+ getIntlContent: (key) => key,
+}));
+
+const put = (action) => ({ type: "put", action });
+
+it("reloads alert receivers with the active namespace", () => {
+ const fetchValue = {
+ currentPage: 2,
+ pageSize: 20,
+ namespaceId: "namespace-1",
+ };
+ const generator = model.effects.reload({ fetchValue }, { put });
+
+ expect(generator.next().value).toEqual(
+ put({
+ type: "fetch",
+ payload: fetchValue,
+ }),
+ );
+ expect(generator.next().done).toBe(true);
+});
diff --git a/src/routes/System/Alert/index.js b/src/routes/System/Alert/index.js
index 7a8d9d3b..cb5d48ea 100644
--- a/src/routes/System/Alert/index.js
+++ b/src/routes/System/Alert/index.js
@@ -26,12 +26,20 @@ import { Type } from "./globalData";
const DEFAULT_ALERT_TYPE = 1;
-@connect(({ alert, loading, global }) => ({
- alert,
- loading: loading.effects["alert/fetch"],
- currentNamespaceId: global.currentNamespaceId,
-}))
-export default class Alert extends Component {
+export const getAlertPageAfterDelete = (
+ total,
+ deletedCount,
+ currentPage,
+ pageSize,
+) => {
+ const remainingPages = Math.max(
+ 1,
+ Math.ceil((total - deletedCount) / pageSize),
+ );
+ return Math.min(currentPage, remainingPages);
+};
+
+export class AlertComponent extends Component {
constructor(props) {
super(props);
this.state = {
@@ -123,20 +131,31 @@ export default class Alert extends Component {
};
deleteClick = () => {
- const { dispatch } = this.props;
+ const {
+ dispatch,
+ currentNamespaceId,
+ alert: { total },
+ } = this.props;
const { currentPage, pageSize, selectedRowKeys } = this.state;
if (selectedRowKeys && selectedRowKeys.length > 0) {
+ const nextPage = getAlertPageAfterDelete(
+ total,
+ selectedRowKeys.length,
+ currentPage,
+ pageSize,
+ );
dispatch({
type: "alert/delete",
payload: {
list: selectedRowKeys,
},
fetchValue: {
- currentPage,
+ currentPage: nextPage,
pageSize,
+ namespaceId: currentNamespaceId,
},
callback: () => {
- this.setState({ selectedRowKeys: [] });
+ this.setState({ selectedRowKeys: [], currentPage: nextPage });
},
});
} else {
@@ -350,3 +369,11 @@ export default class Alert extends Component {
);
}
}
+
+const ConnectedAlert = connect(({ alert, loading, global }) => ({
+ alert,
+ loading: loading.effects["alert/fetch"],
+ currentNamespaceId: global.currentNamespaceId,
+}))(AlertComponent);
+
+export default ConnectedAlert;
diff --git a/src/routes/System/Alert/index.test.js
b/src/routes/System/Alert/index.test.js
new file mode 100644
index 00000000..fa1020a5
--- /dev/null
+++ b/src/routes/System/Alert/index.test.js
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { AlertComponent, getAlertPageAfterDelete } from "./index";
+
+jest.mock("./AddModal", () => () => null);
+jest.mock(
+ "../../../utils/AuthButton",
+ () =>
+ ({ children }) =>
+ children,
+);
+jest.mock("../../../utils/IntlUtils", () => ({
+ getCurrentLocale: jest.fn(),
+ getIntlContent: (key) => key,
+}));
+
+it("moves back when deleting the final row on a later page", () => {
+ expect(getAlertPageAfterDelete(13, 1, 2, 12)).toBe(1);
+ expect(getAlertPageAfterDelete(25, 1, 3, 12)).toBe(2);
+ expect(getAlertPageAfterDelete(30, 1, 2, 12)).toBe(2);
+});
+
+it("passes namespace and the existing page to the delete reload", () => {
+ const dispatch = jest.fn();
+ const component = new AlertComponent({
+ dispatch,
+ currentNamespaceId: "namespace-1",
+ alert: {
+ alertList: [{ id: "receiver-13" }],
+ total: 13,
+ },
+ });
+ component.state = {
+ ...component.state,
+ currentPage: 2,
+ pageSize: 12,
+ selectedRowKeys: ["receiver-13"],
+ };
+ component.setState = (update) => {
+ component.state = { ...component.state, ...update };
+ };
+
+ component.deleteClick();
+
+ const action = dispatch.mock.calls[0][0];
+ expect(action.type).toBe("alert/delete");
+ expect(action.fetchValue).toEqual({
+ currentPage: 1,
+ pageSize: 12,
+ namespaceId: "namespace-1",
+ });
+
+ action.callback();
+ expect(component.state.currentPage).toBe(1);
+ expect(component.state.selectedRowKeys).toEqual([]);
+});