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 1c4f696b1 fix(ai): keep the active conversation open when deletion
fails (#5211)
1c4f696b1 is described below
commit 1c4f696b11d7760c739365a3e989c046d19f5a4a
Author: 健康宝 <[email protected]>
AuthorDate: Fri Oct 9 11:24:46 2026 +0800
fix(ai): keep the active conversation open when deletion fails (#5211)
The modal ended the open conversation whenever the requested id set
contained it, without looking
at `failed`; on a failed delete the caller navigated away from a
conversation that still exists and
skipped the list refresh. Treat the active conversation as deleted only
when it was requested and
did not fail. Covers the single and the batch delete path.
---
.../pages/ai/components/ConversationListModal.tsx | 8 +++-
.../__tests__/ConversationListModal.test.tsx | 47 ++++++++++++++++++++++
2 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/web/src/pages/ai/components/ConversationListModal.tsx
b/web/src/pages/ai/components/ConversationListModal.tsx
index c91eccabe..0132d2063 100644
--- a/web/src/pages/ai/components/ConversationListModal.tsx
+++ b/web/src/pages/ai/components/ConversationListModal.tsx
@@ -231,7 +231,13 @@ const ConversationListPanel = ({
} else {
message.error(t('ai.list.deleteFailed'));
}
- if (activeConversationId != null && ids.includes(activeConversationId)) {
+ // Only a delete that SUCCEEDED ends the open conversation: deletion may
have failed, and then
+ // the conversation is still on screen, so the caller must stay put and
the list still refresh.
+ const activeDeleted =
+ activeConversationId != null &&
+ ids.includes(activeConversationId) &&
+ !failed.includes(activeConversationId);
+ if (activeDeleted) {
onActiveDeleted?.();
return;
}
diff --git
a/web/src/pages/ai/components/__tests__/ConversationListModal.test.tsx
b/web/src/pages/ai/components/__tests__/ConversationListModal.test.tsx
index a3e4be132..d0c66bd99 100644
--- a/web/src/pages/ai/components/__tests__/ConversationListModal.test.tsx
+++ b/web/src/pages/ai/components/__tests__/ConversationListModal.test.tsx
@@ -470,6 +470,53 @@ describe('ConversationListModal', () => {
expect(deleteMock).toHaveBeenCalledWith(7);
});
+ it('keepsTheOpenConversationWhenItsDeleteFailsTest', async () => {
+ const user = userEvent.setup();
+ listMock
+ .mockResolvedValueOnce(page([conversation(7, '检查集群状态')], 1, 1))
+ .mockResolvedValue(page([conversation(7, '检查集群状态')], 1, 1));
+ deleteMock.mockRejectedValue(new Error('workspace is busy'));
+ const { onActiveDeleted } = renderModal({ activeConversationId: 7 });
+ await screen.findByText('当前会话');
+ const listCallsBefore = listMock.mock.calls.length;
+
+ await user.click(screen.getByTestId('ai-conversation-row-delete-7'));
+ await confirmDelete(user);
+
+ await waitFor(() => expect(deleteMock).toHaveBeenCalledWith(7));
+ // The conversation is still on screen, so the caller must not be sent
away from it.
+ expect(await screen.findByText('删除失败,请稍后重试')).toBeInTheDocument();
+ expect(onActiveDeleted).not.toHaveBeenCalled();
+ // The failed delete still refreshes the list rather than returning early.
+ await waitFor(() =>
expect(listMock.mock.calls.length).toBeGreaterThan(listCallsBefore));
+ });
+
+ it('keepsTheOpenConversationWhenOnlyItsBatchDeleteFailsTest', async () => {
+ const user = userEvent.setup();
+ listMock
+ .mockResolvedValueOnce(
+ page([conversation(7, '检查集群状态'), conversation(8, '另一个会话')], 2, 1),
+ )
+ .mockResolvedValue(page([conversation(7, '检查集群状态')], 1, 1));
+ deleteMock.mockImplementation(async (conversationId) => {
+ if (conversationId === 7) throw new Error('workspace is busy');
+ });
+ const { onActiveDeleted } = renderModal({ activeConversationId: 7 });
+ await screen.findByText('当前会话');
+ const listCallsBefore = listMock.mock.calls.length;
+
+ const checkboxes = screen.getAllByRole('checkbox');
+ await user.click(checkboxes[1]);
+ await user.click(checkboxes[2]);
+ await user.click(screen.getByTestId('ai-conversation-selected-delete'));
+ await confirmDelete(user);
+
+ await waitFor(() => expect(deleteMock).toHaveBeenCalledTimes(2));
+ expect(await screen.findByText('已删除 1 条会话,1 条删除失败')).toBeInTheDocument();
+ expect(onActiveDeleted).not.toHaveBeenCalled();
+ await waitFor(() =>
expect(listMock.mock.calls.length).toBeGreaterThan(listCallsBefore));
+ });
+
it('stepsBackAPageWhenTheDeleteEmptiesItTest', async () => {
const user = userEvent.setup();
listMock.mockImplementation(async (params) =>