modcomms/frontend/components/Auditing.tsx
michael 532d7541d6 Implement Barclays design system UI update
- Update Tailwind config with new color tokens (primary-blue, active-blue,
  electric-violet, lime, grey-100/300/700/900, success, warning, error)
- Add Inter font from Google Fonts as Barclays Effra alternative
- Update Sidebar with primary-blue background and white active state
- Update Hero with electric-violet accent and pill-shaped buttons
- Update all tables with lime (#C3FB5A) header backgrounds
- Implement alternating row colors (white/grey-100) on tables
- Update status badges: In Progress (amber), Completed (green)
- Update tabs with active-blue underline styling
- Apply 10px border radius to cards and containers
- Update button styling to pill-shaped with active-blue
- Update input/dropdown borders to grey-700 with 2px
- Update selected state highlighting to info-light (#E7F0FB)
- Update FeedbackReport RAG status colors to new design system

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:50:46 -06:00

219 lines
No EOL
13 KiB
TypeScript
Executable file

import React, { useState } from 'react';
import type { FlaggedItem, ResolvedItem, ErrorItem } from '../types';
interface AuditingProps {
flaggedItems: FlaggedItem[];
resolvedItems: ResolvedItem[];
errorItems: ErrorItem[];
onNavigate: (item: { campaignName: string, proofName: string, version: number }) => void;
}
const formatDate = (isoString: string) => {
try {
return new Date(isoString).toLocaleDateString('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric',
});
} catch (e) {
return 'Invalid Date';
}
};
const FlagsTable: React.FC<{ items: FlaggedItem[], onNavigate: AuditingProps['onNavigate'] }> = ({ items, onNavigate }) => (
<div className="overflow-x-auto">
<table className="min-w-full">
<thead className="bg-lime">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Version</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submitter</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submit Agency</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agent Flagged</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">User Comments</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Date</th>
</tr>
</thead>
<tbody className="divide-y divide-grey-300">
{items.length > 0 ? items.map((item, index) => (
<tr
key={item.id}
className={`hover:bg-info-light cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}`}
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 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>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.agentFlagged}</td>
<td className="px-6 py-4 text-sm text-black-title">
<div className="max-w-xs break-words" title={item.comments}>
{item.comments || <span className="italic text-grey-700">No comment</span>}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{formatDate(item.timestamp)}</td>
</tr>
)) : (
<tr>
<td colSpan={7} className="text-center py-10 text-grey-700">
There are currently no flagged items to audit.
</td>
</tr>
)}
</tbody>
</table>
</div>
);
const ResolutionsTable: React.FC<{ items: ResolvedItem[], onNavigate: AuditingProps['onNavigate'] }> = ({ items, onNavigate }) => (
<div className="overflow-x-auto">
<table className="min-w-full">
<thead className="bg-lime">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Version</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submitter</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submit Agency</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agent</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agent Issue</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">User Comments</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Date</th>
</tr>
</thead>
<tbody className="divide-y divide-grey-300">
{items.length > 0 ? items.map((item, index) => (
<tr
key={item.id}
className={`hover:bg-info-light cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}`}
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 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>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{item.agent}</td>
<td className="px-6 py-4 text-sm text-black-title">
<div className="max-w-xs break-words" title={item.issue}>{item.issue}</div>
</td>
<td className="px-6 py-4 text-sm text-black-title">
<div className="max-w-xs break-words" title={item.resolution}>{item.resolution}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{formatDate(item.timestamp)}</td>
</tr>
)) : (
<tr>
<td colSpan={8} className="text-center py-10 text-grey-700">
There are currently no resolved items to audit.
</td>
</tr>
)}
</tbody>
</table>
</div>
);
const ErrorsTable: React.FC<{ items: ErrorItem[], onNavigate: AuditingProps['onNavigate'] }> = ({ items, onNavigate }) => (
<div className="overflow-x-auto">
<table className="min-w-full">
<thead className="bg-lime">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Name</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Proof Version</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submitter</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Submit Agency</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Error Summary</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Date</th>
</tr>
</thead>
<tbody className="divide-y divide-grey-300">
{items.length > 0 ? items.map((item, index) => (
<tr
key={item.id}
className={`hover:bg-info-light cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}`}
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 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>
<td className="px-6 py-4 text-sm text-black-title">
<div className="max-w-md break-words" title={item.errorSummary}>
{item.errorSummary}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{formatDate(item.timestamp)}</td>
</tr>
)) : (
<tr>
<td colSpan={6} className="text-center py-10 text-grey-700">
There are currently no analysis errors to audit.
</td>
</tr>
)}
</tbody>
</table>
</div>
);
export const Auditing: React.FC<AuditingProps> = ({ flaggedItems, resolvedItems, errorItems, onNavigate }) => {
const [activeTab, setActiveTab] = useState<'Flags' | 'Resolutions' | 'Errors'>('Flags');
return (
<div className="p-4 sm:p-6 lg:p-8 h-full bg-grey-100">
<header className="mb-8">
<h1 className="text-3xl lg:text-4xl font-bold text-primary-blue">Auditing</h1>
<p className="text-base lg:text-lg text-primary-blue mt-1">Review and investigate all user-flagged feedback.</p>
</header>
<div className="mb-6 border-b border-grey-300">
<nav className="-mb-px flex space-x-6" aria-label="Tabs">
<button
onClick={() => setActiveTab('Flags')}
className={`whitespace-nowrap py-3 px-1 border-b-2 font-semibold text-sm transition-colors ${
activeTab === 'Flags'
? 'border-active-blue text-active-blue'
: 'border-transparent text-black-title hover:text-active-blue hover:border-grey-300'
}`}
aria-current={activeTab === 'Flags' ? 'page' : undefined}
>
Flags
</button>
<button
onClick={() => setActiveTab('Resolutions')}
className={`whitespace-nowrap py-3 px-1 border-b-2 font-semibold text-sm transition-colors ${
activeTab === 'Resolutions'
? 'border-active-blue text-active-blue'
: 'border-transparent text-black-title hover:text-active-blue hover:border-grey-300'
}`}
aria-current={activeTab === 'Resolutions' ? 'page' : undefined}
>
Resolutions
</button>
<button
onClick={() => setActiveTab('Errors')}
className={`whitespace-nowrap py-3 px-1 border-b-2 font-semibold text-sm transition-colors ${
activeTab === 'Errors'
? 'border-active-blue text-active-blue'
: 'border-transparent text-black-title hover:text-active-blue hover:border-grey-300'
}`}
aria-current={activeTab === 'Errors' ? 'page' : undefined}
>
Errors
</button>
</nav>
</div>
<section>
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
{activeTab === 'Flags' && <FlagsTable items={flaggedItems} onNavigate={onNavigate} />}
{activeTab === 'Resolutions' && <ResolutionsTable items={resolvedItems} onNavigate={onNavigate} />}
{activeTab === 'Errors' && <ErrorsTable items={errorItems} onNavigate={onNavigate} />}
</div>
</section>
</div>
);
};