From 9eafb1f316c29e0c68bfac8cdf52a474f814f3e5 Mon Sep 17 00:00:00 2001 From: Justin Gasper Date: Wed, 12 Aug 2026 20:59:04 +1000 Subject: [PATCH] PM-5857: Label support team replies What was broken Support staff and ticket raiser replies displayed only their handles, making their roles difficult to distinguish. Root cause The ticket conversation rendered reply handles without using the existing author and ticket-owner IDs to identify support-authored responses. What was changed Append (Support Team) to replies authored by someone other than the ticket owner. Ticket-owner replies remain unchanged, including when the owner also holds the Support Team role. Any added/updated tests Added TicketDetailPage coverage that verifies the suffix appears for support replies and not for ticket-owner replies. --- .../ticket-details/TicketDetailPage.spec.tsx | 41 +++++++++++++++++++ .../pages/ticket-details/TicketDetailPage.tsx | 3 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/apps/support/src/pages/ticket-details/TicketDetailPage.spec.tsx b/src/apps/support/src/pages/ticket-details/TicketDetailPage.spec.tsx index 66b321d20..c512433e8 100644 --- a/src/apps/support/src/pages/ticket-details/TicketDetailPage.spec.tsx +++ b/src/apps/support/src/pages/ticket-details/TicketDetailPage.spec.tsx @@ -242,6 +242,47 @@ describe('TicketDetailPage reply access', () => { .toBeUndefined() }) + it('identifies support team replies without labelling the ticket owner', () => { + mockUseSWR.mockReturnValue({ + data: { + ...closedTicket, + responseCount: 2, + responses: [{ + createdAt: '2026-08-07T01:30:00.000Z', + id: 'response-owner', + markdown: 'Member follow-up.', + readBy: [], + userHandle: 'ticket-owner', + userId: '12345', + }, { + createdAt: '2026-08-07T01:45:00.000Z', + id: 'response-support', + markdown: 'Support follow-up.', + readBy: [], + userHandle: 'support-agent', + userId: '67890', + }], + }, + error: undefined, + isValidating: false, + mutate: mockMutate, + }) + + render() + + const ownerReply = screen.getByText('Member follow-up.') + .closest('article') + const supportReply = screen.getByText('Support follow-up.') + .closest('article') + + expect(ownerReply?.textContent) + .toContain('ticket-owner') + expect(ownerReply?.textContent) + .not.toContain('(Support Team)') + expect(supportReply?.textContent) + .toContain('support-agent (Support Team)') + }) + it('requires non-owner support staff to assign an open ticket before replying', () => { mockProfile = { roles: ['Topcoder Support Team'], diff --git a/src/apps/support/src/pages/ticket-details/TicketDetailPage.tsx b/src/apps/support/src/pages/ticket-details/TicketDetailPage.tsx index 4df066db3..fcf8c7aa7 100644 --- a/src/apps/support/src/pages/ticket-details/TicketDetailPage.tsx +++ b/src/apps/support/src/pages/ticket-details/TicketDetailPage.tsx @@ -43,7 +43,7 @@ import { import styles from './TicketDetailPage.module.scss' /** - * Renders the original request followed by ascending replies and authorized actions. + * Renders the original request followed by ascending, role-labelled replies and authorized actions. * * @returns support ticket detail page. * @throws Does not throw; request failures are shown with recovery actions. @@ -297,6 +297,7 @@ export const TicketDetailPage: FC = () => { color={response.userHandleColor} handle={response.userHandle} /> + {response.userId !== data.memberUserId && ' (Support Team)'}