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 243a7a691 fix(lite): let users inspect every LiteTopic session (#5242)
243a7a691 is described below

commit 243a7a691808718763e8feef3a94c476d1114933
Author: coder999o <[email protected]>
AuthorDate: Fri Oct 9 11:31:13 2026 +0800

    fix(lite): let users inspect every LiteTopic session (#5242)
    
    `handleViewSessions` always opened `sessionIds[0]`, so a row holding 
several sessions gave users no
    way to look at the others even though the table shows the count. The drawer 
now keeps the row's
    session list and renders a searchable selector when there is more than one, 
reusing the existing
    fetch and request-ownership guard; closing the drawer bumps the request id 
and clears the selection.
---
 web/src/pages/studio/LiteTopic.tsx                | 26 +++++++++++++++++++++--
 web/src/pages/studio/__tests__/LiteTopic.test.tsx | 26 +++++++++++++++++++++++
 2 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/web/src/pages/studio/LiteTopic.tsx 
b/web/src/pages/studio/LiteTopic.tsx
index b00a4b947..8061af752 100644
--- a/web/src/pages/studio/LiteTopic.tsx
+++ b/web/src/pages/studio/LiteTopic.tsx
@@ -141,6 +141,8 @@ const LiteTopicPage: React.FC = () => {
   const [sessionDrawerOpen, setSessionDrawerOpen] = useState(false);
   const [sessionData, setSessionData] = useState<LiteTopicSession | 
null>(null);
   const [sessionLoading, setSessionLoading] = useState(false);
+  const [availableSessionIds, setAvailableSessionIds] = useState<string[]>([]);
+  const [selectedSessionId, setSelectedSessionId] = useState<string>();
 
   // Extend TTL modal
   const [extendTTLModalOpen, setExtendTTLModalOpen] = useState(false);
@@ -278,8 +280,10 @@ const LiteTopicPage: React.FC = () => {
     void fetchData(patternFilter || undefined, namespace, { clear: true });
   };
 
-  const handleViewSessions = async (sessionId: string) => {
+  const handleViewSessions = async (sessionId: string, sessionIds?: string[]) 
=> {
     const requestId = ++sessionRequestId.current;
+    if (sessionIds) setAvailableSessionIds(sessionIds);
+    setSelectedSessionId(sessionId);
     setSessionDrawerOpen(true);
     setSessionLoading(true);
     setSessionData(null);
@@ -451,7 +455,7 @@ const LiteTopicPage: React.FC = () => {
               icon={<Eye size={14} />}
               onClick={(e) => {
                 e.stopPropagation();
-                handleViewSessions(record.sessionIds![0]);
+                void handleViewSessions(record.sessionIds![0], 
record.sessionIds);
               }}
             >
               {t('liteTopic.viewSessions')}
@@ -870,11 +874,29 @@ const LiteTopicPage: React.FC = () => {
         width={680}
         open={sessionDrawerOpen}
         onClose={() => {
+          sessionRequestId.current += 1;
           setSessionDrawerOpen(false);
+          setSessionLoading(false);
           setSessionData(null);
+          setAvailableSessionIds([]);
+          setSelectedSessionId(undefined);
         }}
         destroyOnHidden
       >
+        {availableSessionIds.length > 1 && (
+          <Select
+            aria-label={t('liteTopic.sessionId')}
+            showSearch
+            optionFilterProp="label"
+            value={selectedSessionId}
+            options={availableSessionIds.map((sessionId) => ({
+              value: sessionId,
+              label: sessionId,
+            }))}
+            onChange={(sessionId) => void handleViewSessions(sessionId)}
+            style={{ width: '100%', marginBottom: 16 }}
+          />
+        )}
         {renderSessionContent()}
       </Drawer>
 
diff --git a/web/src/pages/studio/__tests__/LiteTopic.test.tsx 
b/web/src/pages/studio/__tests__/LiteTopic.test.tsx
index 0dc12a554..8f160e368 100644
--- a/web/src/pages/studio/__tests__/LiteTopic.test.tsx
+++ b/web/src/pages/studio/__tests__/LiteTopic.test.tsx
@@ -156,6 +156,32 @@ describe('LiteTopic Page', () => {
     
expect(within(popProgressLabel.parentElement!).getByText('96%')).toBeInTheDocument();
   });
 
+  it('allows viewing every session linked to the same LiteTopic row', async () 
=> {
+    apiMocks.queryLiteTopicList.mockResolvedValue([
+      { namespace: 'default', topicPattern: 'order-*', sessionIds: 
['session-1', 'session-2'] },
+    ]);
+    apiMocks.queryLiteTopicSession.mockImplementation(async (sessionId: 
string) => ({
+      sessionId,
+      clientId: sessionId === 'session-1' ? 'first-client' : 'second-client',
+      totalMessages: 100,
+      consumedMessages: 0,
+    }));
+    const user = userEvent.setup();
+    renderPage();
+
+    await user.click(await screen.findByText('查看会话'));
+    expect(await screen.findByText('first-client')).toBeInTheDocument();
+
+    await user.click(screen.getByRole('combobox', { name: '会话 ID' }));
+    await user.click(
+      await screen.findByText('session-2', { selector: 
'.ant-select-item-option-content' }),
+    );
+
+    expect(apiMocks.queryLiteTopicSession).toHaveBeenCalledWith('session-2');
+    expect(await screen.findByText('second-client')).toBeInTheDocument();
+    expect(screen.queryByText('first-client')).not.toBeInTheDocument();
+  });
+
   it('keeps the latest session detail when an earlier request resolves last', 
async () => {
     const firstSession = createDeferred<{
       sessionId: string;

Reply via email to