Replace entire Barclays colour palette (navy #1A2142, lime #C3FB5A, violet #7A0FF9) with Oliver brand tokens: black #1A1A1A, gold #FFCB05, orange #FF5C00, azure #0487B6, sky #5DF5EA, grey #EFEFEF, green #09821F. - Switch font from Inter/Barclays Effra to Arial (system font) - Add new Oliver logo asset (BAR-ModComms-logo-v4.png) - Sidebar: black background, new logo, azure active state - Hero: orange "Intelligent Review" text, hide AI-Powered tagline - Hide ChecksOverview on Home page per Oliver design - Toast notification: orange background with black text - All tables: sky headers, alternating white/grey rows - Campaign badges: gold "In Progress", green "Completed" - Analytics: grey KPI cards, sky accent on Key Insight, oliver trend colours - All buttons: azure fill, pill-shaped (rounded-full) - All tabs/toggles/dropdowns: azure accent colour - Update HTML title to "Mod Comms - Intelligent Review" - Default border radius set to 10px Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
219 lines
No EOL
13 KiB
TypeScript
Executable file
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-oliver-sky">
|
|
<tr>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Version</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submitter</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submit Agency</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agent Flagged</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">User Comments</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black 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-oliver-grey cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}`}
|
|
onClick={() => onNavigate(item)}
|
|
title={`Click to view Version ${item.version} of ${item.proofName}`}
|
|
>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-azure">{item.proofName}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">Version {item.version}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitter}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitAgency}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.agentFlagged}</td>
|
|
<td className="px-6 py-4 text-sm text-oliver-black">
|
|
<div className="max-w-xs break-words" title={item.comments}>
|
|
{item.comments || <span className="italic text-oliver-black/60">No comment</span>}
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{formatDate(item.timestamp)}</td>
|
|
</tr>
|
|
)) : (
|
|
<tr>
|
|
<td colSpan={7} className="text-center py-10 text-oliver-black/60">
|
|
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-oliver-sky">
|
|
<tr>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Version</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submitter</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submit Agency</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agent</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Agent Issue</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">User Comments</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black 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-oliver-grey cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}`}
|
|
onClick={() => onNavigate(item)}
|
|
title={`Click to view Version ${item.version} of ${item.proofName}`}
|
|
>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-azure">{item.proofName}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">Version {item.version}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitter}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitAgency}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.agent}</td>
|
|
<td className="px-6 py-4 text-sm text-oliver-black">
|
|
<div className="max-w-xs break-words" title={item.issue}>{item.issue}</div>
|
|
</td>
|
|
<td className="px-6 py-4 text-sm text-oliver-black">
|
|
<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-oliver-black">{formatDate(item.timestamp)}</td>
|
|
</tr>
|
|
)) : (
|
|
<tr>
|
|
<td colSpan={8} className="text-center py-10 text-oliver-black/60">
|
|
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-oliver-sky">
|
|
<tr>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Name</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Proof Version</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submitter</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Submit Agency</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black uppercase tracking-wider">Error Summary</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-oliver-black 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-oliver-grey cursor-pointer ${index % 2 === 0 ? 'bg-white' : 'bg-oliver-grey'}`}
|
|
onClick={() => onNavigate(item)}
|
|
title={`Click to view Version ${item.version} of ${item.proofName}`}
|
|
>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-semibold text-oliver-azure">{item.proofName}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">Version {item.version}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitter}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-oliver-black">{item.submitAgency}</td>
|
|
<td className="px-6 py-4 text-sm text-oliver-black">
|
|
<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-oliver-black">{formatDate(item.timestamp)}</td>
|
|
</tr>
|
|
)) : (
|
|
<tr>
|
|
<td colSpan={6} className="text-center py-10 text-oliver-black/60">
|
|
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-white">
|
|
<header className="mb-8">
|
|
<h1 className="text-3xl lg:text-4xl font-semibold text-oliver-black">Auditing</h1>
|
|
<p className="text-base lg:text-lg text-oliver-black 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-oliver-azure text-oliver-azure'
|
|
: 'border-transparent text-oliver-black hover:text-oliver-azure 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-oliver-azure text-oliver-azure'
|
|
: 'border-transparent text-oliver-black hover:text-oliver-azure 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-oliver-azure text-oliver-azure'
|
|
: 'border-transparent text-oliver-black hover:text-oliver-azure 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>
|
|
);
|
|
}; |