Spaces:
Sleeping
Sleeping
File size: 6,735 Bytes
743409d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import { AlertTriangle, CheckCircle, XCircle, AlertCircle, Sparkles } from 'lucide-react';
import { AnalysisData } from '../types/analysis';
import { Progress } from './ui/progress';
import { Badge } from './ui/badge';
import { StatisticsBar } from './StatisticsBar';
interface OverviewPanelProps {
data: AnalysisData;
}
export function OverviewPanel({ data }: OverviewPanelProps) {
const getSeverityBadgeClass = (severity: string) => {
switch (severity) {
case 'critical':
return 'bg-red-500/10 text-red-400 border border-red-500/20';
case 'high':
return 'bg-orange-500/10 text-orange-400 border border-orange-500/20';
case 'medium':
return 'bg-amber-500/10 text-amber-400 border border-amber-500/20';
case 'low':
return 'bg-indigo-500/10 text-indigo-400 border border-indigo-500/20';
default:
return 'bg-slate-500/10 text-slate-400 border border-slate-500/20';
}
};
const getRiskIcon = () => {
switch (data.riskLevel) {
case 'critical':
return <XCircle className="w-10 h-10 text-rose-500" />;
case 'high':
return <AlertTriangle className="w-10 h-10 text-orange-500 animate-bounce" />;
case 'medium':
return <AlertCircle className="w-10 h-10 text-amber-500" />;
default:
return <CheckCircle className="w-10 h-10 text-emerald-500" />;
}
};
const getRiskColor = () => {
switch (data.riskLevel) {
case 'critical':
return 'from-rose-500/10 via-rose-500/5 to-transparent border-rose-500/30';
case 'high':
return 'from-orange-500/10 via-orange-500/5 to-transparent border-orange-500/30';
case 'medium':
return 'from-amber-500/10 via-amber-500/5 to-transparent border-amber-500/30';
default:
return 'from-emerald-500/10 via-emerald-500/5 to-transparent border-emerald-500/30';
}
};
const severityCounts = data.darkPatterns.reduce((acc, pattern) => {
acc[pattern.severity] = (acc[pattern.severity] || 0) + 1;
return acc;
}, {} as Record<string, number>);
const avgConfidence = data.darkPatterns.length > 0
? Math.round(data.darkPatterns.reduce((sum, p) => sum + p.confidence, 0) / data.darkPatterns.length)
: 0;
return (
<div className="space-y-6">
{/* Statistics Bar */}
<StatisticsBar
totalPatterns={data.darkPatterns.length}
criticalCount={severityCounts['critical'] || 0}
complianceScore={data.complianceReport.cfpbAlignment}
avgConfidence={avgConfidence}
/>
{/* Overall Risk Score */}
<div className={`p-6 rounded-2xl border bg-gradient-to-br ${getRiskColor()} relative overflow-hidden`}>
<div className="absolute top-0 right-0 w-48 h-48 bg-indigo-500/5 rounded-full blur-3xl pointer-events-none" />
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-4">
<div className="flex items-center gap-4">
<div className="p-1 bg-slate-950/20 rounded-xl border border-white/5">
{getRiskIcon()}
</div>
<div>
<h3 className="text-xl font-extrabold text-slate-100 flex items-center gap-2">
Platform Trust Score: {data.overallScore}/100
</h3>
<p className="text-xs text-slate-400 mt-0.5">
Calculated Risk Rating:{' '}
<span className="font-extrabold uppercase text-indigo-400">
{data.riskLevel}
</span>
</p>
</div>
</div>
<div className="text-left sm:text-right bg-slate-950/20 px-4 py-2.5 rounded-xl border border-white/5">
<div className="text-2xl font-extrabold text-white leading-none">
{data.darkPatterns.length}
</div>
<div className="text-[10px] uppercase font-bold text-slate-400 tracking-wider mt-1">
Violations Found
</div>
</div>
</div>
<Progress value={data.overallScore} className="h-2.5 bg-slate-900 border border-white/5" />
</div>
<div className="grid md:grid-cols-2 gap-6">
{/* Pattern Types Detected */}
<div className="bg-slate-900/30 rounded-2xl p-6 border border-border">
<h4 className="font-bold text-sm text-slate-200 mb-4 flex items-center gap-2">
<Sparkles className="w-4 h-4 text-indigo-400" />
Detected Deceptive Categories
</h4>
{data.darkPatterns.length > 0 ? (
<div className="flex flex-wrap gap-2">
{Array.from(new Set(data.darkPatterns.map((p) => p.type))).map((type) => {
const pattern = data.darkPatterns.find((p) => p.type === type);
return (
<Badge
key={type}
variant="outline"
className={`${getSeverityBadgeClass(pattern?.severity || 'low')} text-[10px] font-bold px-3 py-1 rounded-md`}
>
{type}
</Badge>
);
})}
</div>
) : (
<p className="text-xs text-slate-500">No deceptive categories detected. Fully compliant!</p>
)}
</div>
{/* Quick Summary */}
<div className="bg-slate-900/30 rounded-2xl p-6 border border-border">
<h4 className="font-bold text-sm text-slate-200 mb-3">🤖 Explainable AI (XAI) Summary</h4>
<p className="text-xs text-slate-400 leading-relaxed">
{data.darkPatterns.length > 0 ? (
<>
This interface contains <strong>{data.darkPatterns.length} dark patterns</strong> flagged by the NLP vector classifiers.
Common patterns include <strong>{Array.from(new Set(data.darkPatterns.map(p => p.type))).slice(0, 3).join(', ')}</strong>.
The platform trust rating of <strong>{data.overallScore}/100</strong> indicates a{' '}
<span className="text-indigo-400 font-semibold">{data.riskLevel} risk</span> of regulatory non-compliance with the Consumer Financial Protection Bureau (CFPB) guidelines.
</>
) : (
<>
This interface is clean! The NLP model scanned all textual and layout layers and did not detect any
cognitive friction, forced continuity, hidden costs, or urgency tactics. The trust score of{' '}
<strong>{data.overallScore}/100</strong> indicates alignment with CFPB consumer protection policies.
</>
)}
</p>
</div>
</div>
</div>
);
} |