GrantForge Bot
Deploy sha-9a5957fcdef15b7e2623f8b147cda6026475aee0 — source build (no GHCR)
3a3734f
Raw
History Blame Contribute Delete
3.64 kB
import React, { useEffect, useState } from "react";
interface TrustSummary {
platform_score: number;
overall_score: number;
level: string;
components: Record<string, number>;
recommendations: string[];
}
const LEVEL_LABELS: Record<string, string> = {
excellent: "Doskonaly",
good: "Dobry",
fair: "Umiarkowany",
needs_attention: "Wymaga uwagi",
};
const COMPONENT_LABELS: Record<string, string> = {
link_precision: "Precyzja linkow",
snapshot_richness: "Bogactwo snapshotow",
eurlex: "EUR-Lex",
msp_grounding: "Grounding MSP",
monitoring: "Monitoring prawa",
};
export const TrustCenterPanel: React.FC = () => {
const [summary, setSummary] = useState<TrustSummary | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/trust/summary")
.then((r) => {
if (!r.ok) throw new Error("Nie udalo sie pobrac podsumowania zaufania");
return r.json();
})
.then(setSummary)
.catch((e) => setError(e.message))
.finally(() => setLoading(false));
}, []);
if (loading) {
return (
<div className="rounded-lg border border-slate-200 bg-white p-6 shadow-sm">
<p className="text-sm text-slate-500">Ladowanie centrum zaufania...</p>
</div>
);
}
if (error || !summary) {
return (
<div className="rounded-lg border border-red-200 bg-red-50 p-6 text-red-700">
{error || "Brak danych"}
</div>
);
}
const score = summary.overall_score ?? summary.platform_score;
const levelKey = summary.level || "fair";
return (
<div className="rounded-lg border border-slate-200 bg-white p-6 shadow-sm space-y-6">
<div className="flex flex-wrap items-center justify-between gap-4">
<div>
<h2 className="text-lg font-semibold text-slate-900">Centrum zaufania</h2>
<p className="text-sm text-slate-500">Ocena jakosci zrodel i regulaminow platformy</p>
</div>
<div className="text-right">
<div className="text-3xl font-bold text-slate-900">{score}</div>
<div className="text-xs uppercase tracking-wide text-slate-500">/ 100</div>
<div className="mt-1 inline-block rounded-full bg-slate-100 px-3 py-1 text-xs font-medium text-slate-700">
{LEVEL_LABELS[levelKey] || levelKey}
</div>
</div>
</div>
<div className="space-y-3">
<h3 className="text-sm font-medium text-slate-700">Skladowe</h3>
{Object.entries(summary.components || {}).map(([key, value]) => {
const pct = Math.min(100, Math.max(0, (value / 40) * 100));
return (
<div key={key}>
<div className="mb-1 flex justify-between text-xs text-slate-600">
<span>{COMPONENT_LABELS[key] || key}</span>
<span>{value.toFixed(1)} pkt</span>
</div>
<div className="h-2 w-full overflow-hidden rounded-full bg-slate-100">
<div
className="h-full rounded-full bg-emerald-500 transition-all"
style={{ width: `${pct}%` }}
/>
</div>
</div>
);
})}
</div>
<div>
<h3 className="mb-2 text-sm font-medium text-slate-700">Rekomendacje</h3>
<ul className="list-disc space-y-1 pl-5 text-sm text-slate-600">
{(summary.recommendations || []).map((rec, i) => (
<li key={i}>{rec}</li>
))}
</ul>
</div>
</div>
);
};
export default TrustCenterPanel;