- 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>
147 lines
No EOL
8.7 KiB
TypeScript
Executable file
147 lines
No EOL
8.7 KiB
TypeScript
Executable file
import React, { useEffect, useState } from 'react';
|
|
import { UploadIcon } from './icons/UploadIcon';
|
|
import { TrendingUpIcon } from './icons/TrendingUpIcon';
|
|
import { BugIcon } from './icons/BugIcon';
|
|
import { ClockIcon } from './icons/ClockIcon';
|
|
import { LightbulbIcon } from './icons/LightbulbIcon';
|
|
import { ExclamationTriangleIcon } from './icons/StatusIcons';
|
|
import apiService, { AnalyticsResponse } from '../services/apiService';
|
|
|
|
// Agent performance is still static for now - would need separate API
|
|
const agentPerformance = [
|
|
{ name: 'Legal Agent', passRate: 85, avgIssues: 1.2, trend: 'up' },
|
|
{ name: 'Brand Agent', passRate: 68, avgIssues: 2.5, trend: 'down' },
|
|
{ name: 'Channel Best Practices Agent', passRate: 92, avgIssues: 0.8, trend: 'up' },
|
|
{ name: 'Channel Tech Specs Agent', passRate: 71, avgIssues: 1.9, trend: 'stable' },
|
|
];
|
|
|
|
const UpArrow = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={3} stroke="currentColor" className="h-4 w-4"><path strokeLinecap="round" strokeLinejoin="round" d="M4.5 15.75l7.5-7.5 7.5 7.5" /></svg>;
|
|
const DownArrow = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={3} stroke="currentColor" className="h-4 w-4"><path strokeLinecap="round" strokeLinejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /></svg>;
|
|
const StableLine = () => <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={3} stroke="currentColor" className="h-4 w-4"><path strokeLinecap="round" strokeLinejoin="round" d="M3.75 12h16.5" /></svg>;
|
|
|
|
const TrendIndicator: React.FC<{ trend: 'up' | 'down' | 'stable' }> = ({ trend }) => {
|
|
if (trend === 'up') {
|
|
return <div className="flex items-center gap-1.5 text-success"><UpArrow/> Improving</div>;
|
|
}
|
|
if (trend === 'down') {
|
|
return <div className="flex items-center gap-1.5 text-error"><DownArrow/> Declining</div>;
|
|
}
|
|
return <div className="flex items-center gap-1.5 text-grey-700"><StableLine/> Stable</div>;
|
|
};
|
|
|
|
export const Analytics: React.FC = () => {
|
|
const [analytics, setAnalytics] = useState<AnalyticsResponse | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const loadAnalytics = async () => {
|
|
try {
|
|
const data = await apiService.getAnalytics();
|
|
setAnalytics(data);
|
|
} catch (error) {
|
|
console.error('Failed to load analytics:', error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
loadAnalytics();
|
|
}, []);
|
|
|
|
// Calculate stats from API data
|
|
// Exclude errors from denominator since they weren't successfully reviewed
|
|
const reviewedCount = analytics
|
|
? (analytics.passed || 0) + (analytics.failed || 0) + (analytics.legal_review || 0)
|
|
: 0;
|
|
const passRate = reviewedCount > 0
|
|
? Math.round((analytics!.passed / reviewedCount) * 100)
|
|
: 0;
|
|
|
|
const stats = [
|
|
{ name: 'Proofs Reviewed', value: analytics?.total_reviews?.toString() || '0', icon: UploadIcon },
|
|
{ name: 'Pass Rate', value: `${passRate}%`, icon: TrendingUpIcon },
|
|
{ name: 'Failed Reviews', value: analytics?.failed?.toString() || '0', icon: BugIcon },
|
|
{ name: 'Analysis Errors', value: analytics?.errors?.toString() || '0', icon: ExclamationTriangleIcon },
|
|
{ name: 'Legal Review Required', value: analytics?.legal_review?.toString() || '0', icon: ClockIcon },
|
|
];
|
|
|
|
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">Performance Analytics</h1>
|
|
<p className="text-base lg:text-lg text-primary-blue mt-1">Overall usage and performance statistics for the tool.</p>
|
|
</header>
|
|
|
|
{/* Stats Cards */}
|
|
<section>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-6">
|
|
{stats.map((stat) => {
|
|
const Icon = stat.icon;
|
|
return (
|
|
<div key={stat.name} className="bg-grey-100 rounded-[10px] p-6 flex items-start border-2 border-grey-300 transition-all hover:shadow-lg hover:border-active-blue">
|
|
<div className="p-3 rounded-full bg-white text-black-title mr-4 flex-shrink-0">
|
|
<Icon className="h-9 w-9" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-grey-900">{stat.name}</p>
|
|
<p className="text-3xl font-bold text-black-title mt-1">{stat.value}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</section>
|
|
|
|
{/* AI Performance Summary */}
|
|
<section className="mt-10">
|
|
<h2 className="text-2xl font-bold text-primary-blue mb-4">AI Performance Summary</h2>
|
|
<div className="bg-white border-2 border-electric-violet text-black-title p-6 rounded-[10px] shadow-md flex items-start gap-4">
|
|
<div className="flex-shrink-0">
|
|
<LightbulbIcon className="h-7 w-7 text-electric-violet" />
|
|
</div>
|
|
<div>
|
|
<p className="font-semibold text-primary-blue">Key Insight (Last 7 Days):</p>
|
|
<p className="mt-1 text-black-title">
|
|
A sharp decline in Best Practice adherence has been noted, primarily driven by proofs from the <strong>Barclays Q4 Social</strong> campaign. The Brand Guardian agent also shows a declining performance trend, suggesting a potential need for updated brand guideline training or proof review.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Agent Performance Table */}
|
|
<section className="mt-10">
|
|
<h2 className="text-2xl font-bold text-primary-blue mb-4">Agent Performance (Last 7 Days)</h2>
|
|
<div className="bg-white rounded-[10px] shadow-md overflow-hidden border border-grey-300">
|
|
<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">Agent Name</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Pass Rate</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Avg. Issues per Proof</th>
|
|
<th scope="col" className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Performance Trend</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-grey-300">
|
|
{agentPerformance.map((agent, index) => (
|
|
<tr key={agent.name} className={index % 2 === 0 ? 'bg-white' : 'bg-grey-100'}>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-black-title">{agent.name}</td>
|
|
<td className={`px-6 py-4 whitespace-nowrap text-sm font-semibold text-black-title`}>
|
|
<div className="flex items-center">
|
|
<span className={`h-2.5 w-2.5 rounded-full mr-3 ${agent.passRate >= 80 ? 'bg-success' : agent.passRate < 70 ? 'bg-error' : 'bg-warning'}`}></span>
|
|
{agent.passRate}%
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-black-title">{agent.avgIssues}</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
|
<TrendIndicator trend={agent.trend as 'up' | 'down' | 'stable'} />
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
);
|
|
}; |