Fix flagging feature: blue link in Auditing, remove alert popup, show solid red flag icon

- Style Proof Name column in Auditing Flags tab as blue clickable link
- Replace browser alert() with in-app success message in flag modal that auto-closes after 2s
- Add filled prop to FlagIcon for solid red variant when flagged
- Thread flaggedItems from App → Campaigns → ProofDetailView → FeedbackReport
- Show solid red flag icon on SubReviewCard and LeadAgentSummary when agent has been flagged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael 2026-02-12 13:46:15 -06:00
parent 33c2ce5cf4
commit 9ec892b46b
5 changed files with 93 additions and 58 deletions

View file

@ -817,6 +817,7 @@ const App: React.FC = () => {
onDeleteCampaign={handleDeleteCampaign}
onFlagSubmit={handleFlagSubmit}
onResolveSubmit={handleResolveSubmit}
flaggedItems={flaggedItems}
/>;
case 'WIP Reviewer':
return <WIPReviewer dropdownOptions={dropdownOptions} msalInstance={msalInstance} />;

View file

@ -42,7 +42,7 @@ const FlagsTable: React.FC<{ items: FlaggedItem[], onNavigate: AuditingProps['on
onClick={() => onNavigate(item)}
title={`Click to view Version ${item.version} of ${item.proofName}`}
>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-black-title">{item.proofName}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-active-blue">{item.proofName}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">Version {item.version}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitter}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.submitAgency}</td>

View file

@ -1268,7 +1268,8 @@ const ProofDetailView: React.FC<{
isUploadingNewVersion: boolean;
onFlagSubmit: (flagData: Omit<FlaggedItem, 'id' | 'timestamp' | 'submitter' | 'submitAgency'>) => void;
onResolveSubmit: (resolveData: Omit<ResolvedItem, 'id' | 'timestamp' | 'submitter' | 'submitAgency'>) => void;
}> = ({ campaignName, proof, onBack, onNewVersionUpload, isUploadingNewVersion, onFlagSubmit, onResolveSubmit }) => {
flaggedItems: FlaggedItem[];
}> = ({ campaignName, proof, onBack, onNewVersionUpload, isUploadingNewVersion, onFlagSubmit, onResolveSubmit, flaggedItems }) => {
const getInitialVersionIndex = () => {
if (proof.initialVersion && proof.versions) {
@ -1596,6 +1597,9 @@ const ProofDetailView: React.FC<{
feedback={selectedVersion.feedback}
onFlagSubmit={handleFlagSubmitWrapper}
onResolveSubmit={handleResolveSubmitWrapper}
flaggedItems={flaggedItems}
proofName={proof.proofName}
version={selectedVersion.version}
/>
</div>
</div>
@ -1622,6 +1626,7 @@ interface CampaignsProps {
onDeleteCampaign: (campaignName: string) => Promise<void>;
onFlagSubmit: (flagData: Omit<FlaggedItem, 'id' | 'timestamp' | 'submitter' | 'submitAgency'>) => void;
onResolveSubmit: (resolveData: Omit<ResolvedItem, 'id' | 'timestamp' | 'submitter' | 'submitAgency'>) => void;
flaggedItems: FlaggedItem[];
}
export const Campaigns: React.FC<CampaignsProps> = ({
@ -1642,6 +1647,7 @@ export const Campaigns: React.FC<CampaignsProps> = ({
onDeleteCampaign,
onFlagSubmit,
onResolveSubmit,
flaggedItems,
}) => {
const [isModalOpen, setIsModalOpen] = useState(false);
@ -1670,6 +1676,7 @@ export const Campaigns: React.FC<CampaignsProps> = ({
isUploadingNewVersion={isUploadingNewVersion}
onFlagSubmit={onFlagSubmit}
onResolveSubmit={onResolveSubmit}
flaggedItems={flaggedItems}
/>;
}

View file

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import type { AgentReview, SubReview, RagStatus, OverallStatus } from '../types';
import type { AgentReview, SubReview, RagStatus, OverallStatus, FlaggedItem } from '../types';
import { CheckCircleIcon, ExclamationTriangleIcon, InformationCircleIcon } from './icons/StatusIcons';
import { FlagIcon } from './icons/FlagIcon';
import { XIcon } from './icons/XIcon';
@ -185,57 +185,74 @@ const FlagIssueModal: React.FC<{
if (!isOpen) return null;
const [comments, setComments] = useState('');
const [showSuccess, setShowSuccess] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSubmit(comments);
setShowSuccess(true);
setTimeout(() => {
setShowSuccess(false);
setComments('');
onClose();
}, 2000);
};
return (
<div
className="fixed inset-0 bg-slate-900/60 backdrop-blur-sm flex items-center justify-center z-50 transition-opacity duration-300"
onClick={onClose}
onClick={showSuccess ? undefined : onClose}
>
<div
className="bg-white rounded-2xl shadow-2xl p-8 w-full max-w-lg transform transition-all border border-white/20 ring-1 ring-black/5"
onClick={(e) => e.stopPropagation()}
>
<div className="flex justify-between items-start mb-6">
<div>
<h3 className="text-2xl font-bold text-primary-blue">Flag Feedback</h3>
<p className="text-slate-500 text-sm mt-1">Reporting incorrect feedback from <span className="font-semibold text-active-blue">{agentName}</span></p>
{showSuccess ? (
<div className="text-center py-8">
<CheckCircleIcon className="h-12 w-12 text-success mx-auto mb-4" />
<h3 className="text-xl font-bold text-primary-blue mb-2">Flag Submitted</h3>
<p className="text-slate-500">Thank you for your feedback on the {agentName}'s review. This has been logged for auditing.</p>
</div>
<button onClick={onClose} className="p-2 rounded-full hover:bg-slate-100 text-slate-400 hover:text-slate-600 transition-colors">
<XIcon className="h-6 w-6" />
</button>
</div>
) : (
<>
<div className="flex justify-between items-start mb-6">
<div>
<h3 className="text-2xl font-bold text-primary-blue">Flag Feedback</h3>
<p className="text-slate-500 text-sm mt-1">Reporting incorrect feedback from <span className="font-semibold text-active-blue">{agentName}</span></p>
</div>
<button onClick={onClose} className="p-2 rounded-full hover:bg-slate-100 text-slate-400 hover:text-slate-600 transition-colors">
<XIcon className="h-6 w-6" />
</button>
</div>
<form onSubmit={handleSubmit}>
<label htmlFor="flag-comments" className="block text-sm font-bold text-slate-700 mb-2">Additional Comments</label>
<textarea
id="flag-comments"
value={comments}
onChange={(e) => setComments(e.target.value)}
className="w-full p-4 border border-slate-200 rounded-xl focus:ring-2 focus:ring-red-500/30 focus:border-red-500 transition-all bg-slate-50 focus:bg-white resize-none"
rows={5}
placeholder="Please explain why this feedback is incorrect..."
/>
<div className="mt-8 flex justify-end gap-3">
<button
type="button"
onClick={onClose}
className="px-5 py-2.5 rounded-xl text-slate-600 font-semibold hover:bg-slate-100 transition-colors"
>
Cancel
</button>
<button
type="submit"
className="px-5 py-2.5 rounded-xl bg-red-600 text-white font-bold shadow-lg shadow-red-600/30 hover:bg-red-500 transition-all"
>
Submit Flag
</button>
</div>
</form>
<form onSubmit={handleSubmit}>
<label htmlFor="flag-comments" className="block text-sm font-bold text-slate-700 mb-2">Additional Comments</label>
<textarea
id="flag-comments"
value={comments}
onChange={(e) => setComments(e.target.value)}
className="w-full p-4 border border-slate-200 rounded-xl focus:ring-2 focus:ring-red-500/30 focus:border-red-500 transition-all bg-slate-50 focus:bg-white resize-none"
rows={5}
placeholder="Please explain why this feedback is incorrect..."
/>
<div className="mt-8 flex justify-end gap-3">
<button
type="button"
onClick={onClose}
className="px-5 py-2.5 rounded-xl text-slate-600 font-semibold hover:bg-slate-100 transition-colors"
>
Cancel
</button>
<button
type="submit"
className="px-5 py-2.5 rounded-xl bg-red-600 text-white font-bold shadow-lg shadow-red-600/30 hover:bg-red-500 transition-all"
>
Submit Flag
</button>
</div>
</form>
</>
)}
</div>
</div>
);
@ -255,7 +272,8 @@ const SubReviewCard: React.FC<{
review: SubReview;
onFlag: () => void;
onResolve: (issueText: string, reason: string) => void;
}> = ({ title, review, onFlag, onResolve }) => {
isFlagged?: boolean;
}> = ({ title, review, onFlag, onResolve, isFlagged }) => {
interface IssueState {
text: string;
status: 'actionable' | 'resolved';
@ -391,11 +409,11 @@ const SubReviewCard: React.FC<{
<RagStatusBadge status={currentStatus} />
<button
onClick={onFlag}
className="p-1.5 text-slate-400 rounded-lg hover:bg-red-50 hover:text-red-500 transition-colors"
title="Flag as incorrect"
aria-label="Flag this feedback as incorrect"
className={`p-1.5 rounded-lg transition-colors ${isFlagged ? 'text-red-500' : 'text-slate-400 hover:bg-red-50 hover:text-red-500'}`}
title={isFlagged ? "Flagged as incorrect" : "Flag as incorrect"}
aria-label={isFlagged ? "This feedback has been flagged" : "Flag this feedback as incorrect"}
>
<FlagIcon className="h-4 w-4" />
<FlagIcon className="h-4 w-4" filled={isFlagged} />
</button>
</div>
</div>
@ -667,7 +685,7 @@ const SubReviewCard: React.FC<{
);
};
const LeadAgentSummary: React.FC<{ status: OverallStatus, summary: string, onFlag: () => void; }> = ({ status, summary, onFlag }) => {
const LeadAgentSummary: React.FC<{ status: OverallStatus, summary: string, onFlag: () => void; isFlagged?: boolean; }> = ({ status, summary, onFlag, isFlagged }) => {
const isPassed = status === 'Passed';
let themeStyles = 'from-sky-50 to-white border-sky-100 text-primary-blue';
@ -705,10 +723,10 @@ const LeadAgentSummary: React.FC<{ status: OverallStatus, summary: string, onFla
</h3>
<button
onClick={onFlag}
className="text-slate-400 hover:text-red-500 p-2 rounded-full hover:bg-white/50 transition-colors"
title="Flag as incorrect"
className={`p-2 rounded-full transition-colors ${isFlagged ? 'text-red-500' : 'text-slate-400 hover:text-red-500 hover:bg-white/50'}`}
title={isFlagged ? "Flagged as incorrect" : "Flag as incorrect"}
>
<FlagIcon className="h-5 w-5" />
<FlagIcon className="h-5 w-5" filled={isFlagged} />
</button>
</div>
<div className={`text-lg leading-relaxed ${isPassed ? 'text-emerald-800/80' : 'text-slate-700'}`}>
@ -770,11 +788,20 @@ const FinancialPromotionSummary: React.FC<{ reason: string; summary: string }> =
};
export const FeedbackReport: React.FC<{
export const FeedbackReport: React.FC<{
feedback: AgentReview;
onFlagSubmit: (agentName: string, comments: string) => void;
onResolveSubmit: (agentName: string, issueText: string, reason: string) => void;
}> = ({ feedback, onFlagSubmit, onResolveSubmit }) => {
flaggedItems?: FlaggedItem[];
proofName?: string;
version?: number;
}> = ({ feedback, onFlagSubmit, onResolveSubmit, flaggedItems = [], proofName, version }) => {
const flaggedAgents = new Set(
flaggedItems
.filter(f => f.proofName === proofName && f.version === version)
.map(f => f.agentFlagged)
);
const [flagModalState, setFlagModalState] = useState<{ isOpen: boolean; agentName: string }>({
isOpen: false,
agentName: '',
@ -790,8 +817,6 @@ export const FeedbackReport: React.FC<{
const handleSubmitFlag = (comments: string) => {
onFlagSubmit(flagModalState.agentName, comments);
alert(`Thank you for your feedback on the ${flagModalState.agentName}'s review. This has been logged for auditing.`);
handleCloseFlagModal();
};
const agentReviews = [
@ -818,22 +843,24 @@ export const FeedbackReport: React.FC<{
summary={feedback.leadAgentSummary}
/>
) : (
<LeadAgentSummary
status={feedback.overallStatus}
summary={feedback.leadAgentSummary}
<LeadAgentSummary
status={feedback.overallStatus}
summary={feedback.leadAgentSummary}
onFlag={() => handleOpenFlagModal('Lead Agent')}
isFlagged={flaggedAgents.has('Lead Agent')}
/>
)}
<div className="grid grid-cols-1 gap-6">
{agentReviews.map(({ title, review }) => (
<SubReviewCard
<SubReviewCard
key={title}
title={`${title}`}
title={`${title}`}
review={review}
onFlag={() => handleOpenFlagModal(title)}
onResolve={(issueText, reason) => onResolveSubmit(title, issueText, reason)}
isFlagged={flaggedAgents.has(title)}
/>
))}
</div>

View file

@ -1,7 +1,7 @@
import React from 'react';
export const FlagIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>
export const FlagIcon: React.FC<React.SVGProps<SVGSVGElement> & { filled?: boolean }> = ({ filled, ...props }) => (
<svg xmlns="http://www.w3.org/2000/svg" fill={filled ? "currentColor" : "none"} viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>
<path strokeLinecap="round" strokeLinejoin="round" d="M3 3v1.5M3 21v-6m0 0l2.77-.693a9 9 0 016.208.682l.108.054a9 9 0 006.086.71l3.114-.732a48.524 48.524 0 01-.005-10.499l-3.11.732a9 9 0 01-6.085-.711l-.108-.054a9 9 0 00-6.208-.682L3 4.5M3 15V4.5" />
</svg>
);