From e5a841716ee97e9f28e0a7fad4d3ba23176ddf25 Mon Sep 17 00:00:00 2001 From: michael Date: Sun, 25 Jan 2026 10:39:42 -0600 Subject: [PATCH] Add categorized issue display for revision analysis When analyzing proof revisions (version > 1), SubReviewCard now displays issues in three distinct categories: Resolved Issues (green, collapsed by default), Outstanding Issues (amber), and New Issues (red). Each section is collapsible with count badges. Original mode (version 1) maintains backward compatibility with the single "Actionable Issues" list. Co-Authored-By: Claude Opus 4.5 --- frontend/components/FeedbackReport.tsx | 251 ++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 7 deletions(-) diff --git a/frontend/components/FeedbackReport.tsx b/frontend/components/FeedbackReport.tsx index 955a3a2..8ae18f8 100755 --- a/frontend/components/FeedbackReport.tsx +++ b/frontend/components/FeedbackReport.tsx @@ -241,9 +241,18 @@ const FlagIssueModal: React.FC<{ ); }; -const SubReviewCard: React.FC<{ - title: string; - review: SubReview; +// Helper function to detect revision mode (when revision data is present) +const hasRevisionData = (review: SubReview): boolean => { + return ( + (review.resolvedIssues && review.resolvedIssues.length > 0) || + (review.outstandingIssues && review.outstandingIssues.length > 0) || + (review.newIssues && review.newIssues.length > 0) + ); +}; + +const SubReviewCard: React.FC<{ + title: string; + review: SubReview; onFlag: () => void; onResolve: (issueText: string, reason: string) => void; }> = ({ title, review, onFlag, onResolve }) => { @@ -251,15 +260,39 @@ const SubReviewCard: React.FC<{ text: string; status: 'actionable' | 'resolved'; reason?: string; + category?: 'outstanding' | 'new'; // For revision mode } const [issues, setIssues] = useState([]); const [currentStatus, setCurrentStatus] = useState(review.ragStatus); const [isModalOpen, setIsModalOpen] = useState(false); const [activeIssueIndex, setActiveIssueIndex] = useState(null); - + + // Section collapse states for revision mode + const [resolvedCollapsed, setResolvedCollapsed] = useState(true); + const [outstandingCollapsed, setOutstandingCollapsed] = useState(false); + const [newCollapsed, setNewCollapsed] = useState(false); + useEffect(() => { - setIssues(review.issues.map(text => ({ text, status: 'actionable', reason: undefined }))); + if (hasRevisionData(review)) { + // Revision mode: populate from outstandingIssues and newIssues + const outstandingIssues: IssueState[] = (review.outstandingIssues || []).map(text => ({ + text, + status: 'actionable', + reason: undefined, + category: 'outstanding' as const + })); + const newIssues: IssueState[] = (review.newIssues || []).map(text => ({ + text, + status: 'actionable', + reason: undefined, + category: 'new' as const + })); + setIssues([...outstandingIssues, ...newIssues]); + } else { + // Original mode: use review.issues + setIssues(review.issues.map(text => ({ text, status: 'actionable', reason: undefined }))); + } setCurrentStatus(review.ragStatus); }, [review]); @@ -371,7 +404,211 @@ const SubReviewCard: React.FC<{ {formatFeedbackText(review.feedback)} - {currentStatus !== 'Error' && issues.length > 0 && ( + {/* Revision mode: Show categorized sections */} + {currentStatus !== 'Error' && hasRevisionData(review) && ( +
+ {/* Resolved Issues Section */} + {review.resolvedIssues && review.resolvedIssues.length > 0 && ( +
+ + {!resolvedCollapsed && ( +
    + {review.resolvedIssues.map((issueText, index) => ( +
  • + + + {issueText} + +
  • + ))} +
+ )} +
+ )} + + {/* Outstanding Issues Section */} + {issues.filter(i => i.category === 'outstanding').length > 0 && ( +
+ + {!outstandingCollapsed && ( +
    + {issues.filter(i => i.category === 'outstanding').map((issue, idx) => { + const actualIndex = issues.findIndex((i, index) => i === issue && index >= 0); + return ( +
  • + {issue.status === 'actionable' ? ( + + ) : ( + + )} +
    +
    + + {issue.text} + + {issue.status === 'actionable' ? ( + + ) : ( +
    + + +
    + Reason for resolution: + "{issue.reason}" +
    +
    + +
    + )} +
    +
    +
  • + ); + })} +
+ )} +
+ )} + + {/* New Issues Section */} + {issues.filter(i => i.category === 'new').length > 0 && ( +
+ + {!newCollapsed && ( +
    + {issues.filter(i => i.category === 'new').map((issue, idx) => { + const actualIndex = issues.findIndex((i, index) => i === issue && index >= 0); + return ( +
  • + {issue.status === 'actionable' ? ( + + ) : ( + + )} +
    +
    + + {issue.text} + + {issue.status === 'actionable' ? ( + + ) : ( +
    + + +
    + Reason for resolution: + "{issue.reason}" +
    +
    + +
    + )} +
    +
    +
  • + ); + })} +
+ )} +
+ )} + + {/* No issues state for revision mode */} + {issues.length === 0 && (!review.resolvedIssues || review.resolvedIssues.length === 0) && ( +
+ + No actionable issues found. +
+ )} +
+ )} + + {/* Original mode: Show single list (backward compatible) */} + {currentStatus !== 'Error' && !hasRevisionData(review) && issues.length > 0 && (
Actionable Issues
    @@ -418,7 +655,7 @@ const SubReviewCard: React.FC<{
)} - {currentStatus !== 'Error' && issues.length === 0 && ( + {currentStatus !== 'Error' && !hasRevisionData(review) && issues.length === 0 && (
No actionable issues found.