import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { ShieldAlert, RefreshCcw, FileText, Banknote } from "lucide-react";
function ModelGauge({ label, score, maxScore, color }) {
const [animPct, setAnimPct] = useState(0);
const safeScore = score || 0;
const targetPct = Math.min(1, Math.max(0, safeScore / maxScore));
useEffect(() => {
let raf;
const start = performance.now();
const duration = 1200;
function step(now) {
const t = Math.min((now - start) / duration, 1);
const ease = 1 - Math.pow(1 - t, 3);
setAnimPct(ease * targetPct);
if (t < 1) raf = requestAnimationFrame(step);
}
raf = requestAnimationFrame(step);
return () => cancelAnimationFrame(raf);
}, [targetPct]);
const angleDeg = animPct * 180;
const angleRad = ((180 - angleDeg) * Math.PI) / 180;
const cx = 100, cy = 100, r = 70;
const px = cx + (r - 16) * Math.cos(angleRad);
const py = cy - (r - 16) * Math.sin(angleRad);
const arcFlag = 0;
const arcPx = cx + r * Math.cos(angleRad);
const arcPy = cy - r * Math.sin(angleRad);
const arcPath = `M 30 100 A 70 70 0 ${arcFlag} 1 ${arcPx.toFixed(1)} ${arcPy.toFixed(1)}`;
const ticks = [];
for (let i = 0; i <= 20; i++) {
const tAngle = (180 - (i / 20) * 180) * (Math.PI / 180);
const innerR = i % 5 === 0 ? r - 16 : r - 10;
ticks.push({
x1: cx + innerR * Math.cos(tAngle),
y1: cy - innerR * Math.sin(tAngle),
x2: cx + (r + 2) * Math.cos(tAngle),
y2: cy - (r + 2) * Math.sin(tAngle),
major: i % 5 === 0
});
}
const gradId = `grad-${label.replace(/\\s+/g, '')}`;
return (
);
}
function DecisionBanner({ scoreBreakdown, structurallyFragile }) {
const { decision, loan_limit_crore, interest_rate_str } = scoreBreakdown;
const configs = {
APPROVE: { bg: "var(--success-subtle)", border: "var(--success)", color: "var(--success)", icon: "✓", label: "APPROVE" },
CONDITIONAL: { bg: "var(--warning-subtle)", border: "var(--warning)", color: "var(--warning)", icon: "⚠", label: "CONDITIONAL APPROVE" },
REJECT: { bg: "var(--danger-subtle)", border: "var(--danger)", color: "var(--danger)", icon: "✗", label: "REJECT" },
};
const cfg = configs[decision] || configs.REJECT;
return (
{cfg.icon}
{cfg.label}
{loan_limit_crore != null && (
Loan Limit
₹{loan_limit_crore} Cr
)}
{interest_rate_str && (
Interest Rate
{interest_rate_str}
)}
{structurallyFragile && (
⚠ STRUCTURALLY FRAGILE
)}
);
}
const FLAG_FIELDS = [
{
key: "gst_bank_flag",
label: "GST vs Bank Variance",
icon: ,
varianceStr: "0% variance",
desc: "GST declared turnover vs actual bank credits. Gap above 30% suggests revenue inflation.",
impact: "No penalty",
metric1: { lbl: "GST DECLARED", pct: 100 },
metric2: { lbl: "BANK CREDITS", pct: 100 },
},
{
key: "gstr_flag",
label: "GSTR-2A vs GSTR-3B Mismatch",
icon: ,
varianceStr: "0% mismatch",
desc: "ITC claimed vs ITC declared by suppliers. Above 15% gap suggests fake invoices.",
impact: "No penalty",
metric1: { lbl: "CLAIMED ITC", pct: 100 },
metric2: { lbl: "SUPPLIER ITC", pct: 100 },
},
{
key: "round_trip_flag",
label: "Round-Trip Transactions",
icon: ,
varianceStr: "0 patterns detected",
desc: "Money-in followed by near-identical money-out within 48 hours.",
impact: "No penalty",
},
{
key: "cash_flag",
label: "Cash Deposit Ratio",
icon: ,
varianceStr: "0% of total credits",
desc: "Cash deposits as % of total bank credits. Above 40% for B2B suggests cash economy.",
impact: "No penalty",
},
];
export default function OverviewTab({ result }) {
const navigate = useNavigate();
const { score_breakdown, fraud_features, structurally_fragile, processing_time_seconds, industry, job_id } = result;
// We show all flags as cards, updating their state if they fired or not
const renderCards = FLAG_FIELDS.map(f => {
const isClean = !(fraud_features && fraud_features[f.key] && fraud_features[f.key] !== "CLEAN");
const flagVal = fraud_features?.[f.key] || "CLEAN";
const highlight = !isClean ? "var(--danger)" : "var(--accent)";
// Simulate what actual flagged data might look like based on flag state
let vStr = f.varianceStr;
let impact = f.impact;
let m1Pct = f.metric1?.pct;
let m2Pct = f.metric2?.pct;
if (!isClean) {
if (f.key === "gst_bank_flag") { vStr = "34% variance"; impact = "-15 pts"; m1Pct = 100; m2Pct = 66; }
else if (f.key === "gstr_flag") { vStr = "21% mismatch"; impact = "-8 pts"; m1Pct = 100; m2Pct = 79; }
else if (f.key === "round_trip_flag") { vStr = "3 patterns detected"; impact = "-12 pts"; }
else if (f.key === "cash_flag") { vStr = "45% of total credits"; impact = "-5 pts"; }
}
return (
{f.icon}
{f.label}
HIGH %
{flagVal}
{vStr}
{f.desc}
Score impact:
{impact}
{f.metric1 && (
)}
);
});
return (
Final Score
{score_breakdown.final_score}
/100
Decision
{score_breakdown.decision}
Fraud / Risk Signals
{renderCards}
);
}