SignalMod / frontend /src /pages /HubPage.tsx
Ruperth's picture
feat: localise Hub Settings and ModelBanner
035d8b3
Raw
History Blame Contribute Delete
3.86 kB
import { useMemo } from "react";
import {
Bar,
BarChart,
Cell,
Pie,
PieChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { useApp } from "../context/AppContext";
import { useTheme } from "../context/ThemeContext";
import { useI18n } from "../i18n/I18nContext";
function cssVar(name: string, fallback: string): string {
if (typeof window === "undefined") return fallback;
return (
getComputedStyle(document.documentElement).getPropertyValue(name).trim() ||
fallback
);
}
export function HubPage() {
const { hubHistory, threshold } = useApp();
const { theme } = useTheme();
const { t } = useI18n();
const colors = useMemo(
() => ({
safe: cssVar("--safe", "#2f8f3e"),
toxic: cssVar("--toxic", "#c2331f"),
accent: cssVar("--accent", "#d83d2c"),
muted: cssVar("--muted", "#6b6256"),
}),
[theme]
);
const toxic = hubHistory.filter((h) => h.score >= threshold).length;
const safe = hubHistory.length - toxic;
const pieData = [
{ name: t.hub.safe, value: safe || 1 },
{ name: t.hub.toxic, value: toxic || 0 },
];
const pieColors = [colors.safe, colors.toxic];
const barData = hubHistory.slice(0, 12).map((h, i) => ({
name: `#${i + 1}`,
score: Math.round(h.score * 100),
}));
return (
<div className="hub-page">
<h1>{t.hub.title}</h1>
<div className="hub-cards">
<div className="hub-stat">
<span className="stat-label">{t.hub.threshold}</span>
<span className="stat-value">{threshold.toFixed(2)}</span>
</div>
<div className="hub-stat">
<span className="stat-label">{t.hub.eventsLogged}</span>
<span className="stat-value">{hubHistory.length}</span>
</div>
<div className="hub-stat">
<span className="stat-label">{t.hub.toxicSession}</span>
<span className="stat-value">{toxic}</span>
</div>
</div>
<div className="hub-charts">
<div className="chart-box">
<h2>{t.hub.safeVsToxic}</h2>
<ResponsiveContainer width="100%" height={220}>
<PieChart>
<Pie data={pieData} dataKey="value" nameKey="name" cx="50%" cy="50%" outerRadius={80}>
{pieData.map((_, i) => (
<Cell key={i} fill={pieColors[i % pieColors.length]} />
))}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
</div>
<div className="chart-box">
<h2>{t.hub.recentScores}</h2>
<ResponsiveContainer width="100%" height={220}>
<BarChart data={barData}>
<XAxis dataKey="name" stroke={colors.muted} />
<YAxis stroke={colors.muted} domain={[0, 100]} />
<Tooltip />
<Bar dataKey="score" fill={colors.accent} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
<div className="hub-table-wrap">
<h2>{t.hub.recentActions}</h2>
<table className="hub-table">
<thead>
<tr>
<th>{t.hub.colUser}</th>
<th>{t.hub.colComment}</th>
<th>{t.hub.colScore}</th>
<th>{t.hub.colAction}</th>
</tr>
</thead>
<tbody>
{hubHistory.length === 0 ? (
<tr>
<td colSpan={4}>{t.hub.emptyHistory}</td>
</tr>
) : (
hubHistory.map((h, i) => (
<tr key={i}>
<td>{h.user}</td>
<td>{h.snippet}</td>
<td>{(h.score * 100).toFixed(0)}%</td>
<td>{h.action}</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
);
}