diff --git a/src/apps/review/src/lib/components/AiReviewsTable/AiReviewsTable.tsx b/src/apps/review/src/lib/components/AiReviewsTable/AiReviewsTable.tsx index d8fc9f668..ba5b7dacb 100644 --- a/src/apps/review/src/lib/components/AiReviewsTable/AiReviewsTable.tsx +++ b/src/apps/review/src/lib/components/AiReviewsTable/AiReviewsTable.tsx @@ -113,23 +113,29 @@ function getDecisionBySubmission( } /** - * Builds a list of human-readable note strings from escalations and unlock reason. + * Builds a list of human-readable note strings from escalations. * Used to display notes for Escalating, Approving/Rejecting, and Unlocking actions. */ function buildDecisionNotes( escalations?: AiReviewDecisionEscalation[], - reason?: string | null, + resourceMemberIdMapping?: Record, ): string[] { const parts: string[] = [] + const getMemberHandle = (memberId?: string | null): string => { + if (!memberId || !resourceMemberIdMapping) return '' + const resource = resourceMemberIdMapping[memberId] + return resource?.memberHandle || '' + } + escalations?.forEach(esc => { if (esc.escalationNotes) { - const by = esc.createdBy ? ` (by ${esc.createdBy})` : '' + const by = esc.createdBy ? ` (by ${getMemberHandle(esc.createdBy)})` : '' parts.push(`Escalation Note${by}: ${esc.escalationNotes}`) } if (esc.approverNotes) { - const by = esc.updatedBy ? ` (by ${esc.updatedBy})` : '' + const by = esc.updatedBy ? ` (by ${getMemberHandle(esc.updatedBy)})` : '' const prefix = esc.status === 'APPROVED' ? 'Approval Note' : esc.status === 'REJECTED' @@ -139,10 +145,6 @@ function buildDecisionNotes( } }) - if (reason) { - parts.push(`Unlock Reason: ${reason}`) - } - return parts } @@ -334,17 +336,18 @@ const AiReviewsTable: FC = props => { return 'Submission Locked - This submission won\'t be reviewed during the Review Phase.' }, [currentDecision?.submissionLocked, hasSubmitterRole]) + const resourceMemberIdMapping = challengeDetailContext.resourceMemberIdMapping + /** - * Builds the list of notes from escalations (escalationNotes, approverNotes) - * and the unlock reason. These are shown to Copilot/Manager/Admin only - * (not to submitters) so they can see why a submission was escalated, - * approved/rejected, or unlocked. + * Builds the list of notes from escalations (escalationNotes, approverNotes). + * These are shown to Copilot/Manager/Admin only (not to submitters) so they + * can see why a submission was escalated or approved/rejected. */ const decisionNotes = useMemo((): string[] => { if (!currentDecision || hasSubmitterRole) return [] - return buildDecisionNotes(currentDecision.escalations, currentDecision.reason) - }, [currentDecision, hasSubmitterRole]) + return buildDecisionNotes(currentDecision.escalations, resourceMemberIdMapping) + }, [currentDecision, hasSubmitterRole, resourceMemberIdMapping]) const hasDecisionNotes = decisionNotes.length > 0 diff --git a/src/apps/review/src/lib/components/CollapsibleAiReviewsRow/CollapsibleAiReviewsRow.tsx b/src/apps/review/src/lib/components/CollapsibleAiReviewsRow/CollapsibleAiReviewsRow.tsx index a15224bdb..5b5aa9011 100644 --- a/src/apps/review/src/lib/components/CollapsibleAiReviewsRow/CollapsibleAiReviewsRow.tsx +++ b/src/apps/review/src/lib/components/CollapsibleAiReviewsRow/CollapsibleAiReviewsRow.tsx @@ -53,25 +53,31 @@ export function normalizeDecisionStatus( } /** - * Builds a multi-line tooltip string from escalation notes, approver notes, - * and the unlock reason. Returns undefined if there are no notes at all. - * Used to show Copilot/Manager/Admin why a submission was escalated, - * approved/rejected, or unlocked. + * Builds a multi-line tooltip string from escalation notes and approver notes. + * Returns undefined if there are no notes at all. + * Used to show Copilot/Manager/Admin why a submission was escalated or + * approved/rejected. */ function buildNotesTooltip( escalations?: AiReviewDecisionEscalation[], - reason?: string | null, + resourceMemberIdMapping?: Record, ): string | undefined { const parts: string[] = [] + const getMemberHandle = (memberId?: string | null): string => { + if (!memberId || !resourceMemberIdMapping) return '' + const resource = resourceMemberIdMapping[memberId] + return resource?.memberHandle || '' + } + escalations?.forEach(esc => { if (esc.escalationNotes) { - const by = esc.createdBy ? ` (by ${esc.createdBy})` : '' + const by = esc.createdBy ? ` (by ${getMemberHandle(esc.createdBy)})` : '' parts.push(`Escalation Note${by}: ${esc.escalationNotes}`) } if (esc.approverNotes) { - const by = esc.updatedBy ? ` (by ${esc.updatedBy})` : '' + const by = esc.updatedBy ? ` (by ${getMemberHandle(esc.updatedBy)})` : '' const prefix = esc.status === 'APPROVED' ? 'Approval Note' : esc.status === 'REJECTED' @@ -81,10 +87,6 @@ function buildNotesTooltip( } }) - if (reason) { - parts.push(`Unlock Reason: ${reason}`) - } - return parts.length ? parts.join('\n') : undefined } @@ -111,16 +113,18 @@ const CollapsibleAiReviewsRow: FC = props => { [currentDecision?.status], ) + const resourceMemberIdMapping = challengeDetailContext.resourceMemberIdMapping + /** * Builds the tooltip text for the notes icon shown next to the status label. * Only shown to Copilot/Manager/Admin (not submitters). - * Covers: escalation notes, approval/rejection notes, and unlock reason. + * Covers: escalation notes and approval/rejection notes. */ const notesTooltip = useMemo((): string | undefined => { if (hasSubmitterRole || !currentDecision) return undefined - return buildNotesTooltip(currentDecision.escalations, currentDecision.reason) - }, [currentDecision, hasSubmitterRole]) + return buildNotesTooltip(currentDecision.escalations, resourceMemberIdMapping) + }, [currentDecision, hasSubmitterRole, resourceMemberIdMapping]) const [isOpen, setIsOpen] = useState(props.defaultOpen ?? false) const [portalContainer, setPortalContainer] = useState(undefined)