refactor(inspector-ui): wire useTranslations in ConversationTab/StatsTab/StatsCharts/TimingWaterfall (M3+M4)

- ConversationTab: t("conversationNotAvailable"), t("conversationNoMessages"), t("contextFingerprint")
- StatsTab: adds useTranslations + t("statsNoData"); LoadingCharts sub-component uses t("loadingCharts")
- StatsCharts: useTranslations for all 5 chart labels (statusDistribution, latency, totalRequests, successful, errors)
- TimingWaterfall: useTranslations for t("timingProxyOverhead") and t("timingUpstreamResponse")
This commit is contained in:
diegosouzapw
2026-05-28 17:30:04 -03:00
parent 68e2f2a2cd
commit 471a5bed00
4 changed files with 24 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
"use client";
import { useTranslations } from "next-intl";
import type { InterceptedRequest } from "@/mitm/inspector/types";
interface TimingWaterfallProps {
@@ -7,6 +8,7 @@ interface TimingWaterfallProps {
}
export function TimingWaterfall({ request }: TimingWaterfallProps) {
const t = useTranslations("trafficInspector");
const { proxyLatencyMs, upstreamLatencyMs, totalLatencyMs } = request;
const total = totalLatencyMs ?? (proxyLatencyMs ?? 0) + (upstreamLatencyMs ?? 0);
@@ -16,12 +18,12 @@ export function TimingWaterfall({ request }: TimingWaterfallProps) {
const segments: Array<{ label: string; ms: number; color: string }> = [
{
label: "Proxy overhead",
label: t("timingProxyOverhead"),
ms: proxyLatencyMs ?? 0,
color: "bg-blue-500",
},
{
label: "Upstream response",
label: t("timingUpstreamResponse"),
ms: upstreamLatencyMs ?? 0,
color: "bg-green-500",
},

View File

@@ -15,10 +15,7 @@ export function ConversationTab({ request }: ConversationTabProps) {
if (!conversation) {
return (
<div className="p-4 text-sm text-text-muted">
Conversation data not available. This may not be an LLM request or the body
could not be parsed.
</div>
<div className="p-4 text-sm text-text-muted">{t("conversationNotAvailable")}</div>
);
}
@@ -26,7 +23,7 @@ export function ConversationTab({ request }: ConversationTabProps) {
if (allTurns.length === 0) {
return (
<div className="p-4 text-sm text-text-muted">No messages found in this request.</div>
<div className="p-4 text-sm text-text-muted">{t("conversationNoMessages")}</div>
);
}
@@ -34,7 +31,7 @@ export function ConversationTab({ request }: ConversationTabProps) {
<div className="h-full overflow-auto p-3 space-y-2">
{conversation.contextKey && (
<div className="text-xs text-text-muted mb-2">
Context fingerprint:{" "}
{t("contextFingerprint")}{" "}
<span className="font-mono text-blue-400">#{conversation.contextKey.slice(0, 12)}</span>
</div>
)}

View File

@@ -10,6 +10,7 @@ import {
LineChart,
Line,
} from "recharts";
import { useTranslations } from "next-intl";
import type { InterceptedRequest } from "@/mitm/inspector/types";
interface StatsChartsProps {
@@ -17,6 +18,8 @@ interface StatsChartsProps {
}
export default function StatsCharts({ requests }: StatsChartsProps) {
const t = useTranslations("trafficInspector");
const statusDist = requests.reduce<Record<string, number>>((acc, r) => {
const key =
typeof r.status === "number" ? `${Math.floor(r.status / 100)}xx` : String(r.status);
@@ -34,7 +37,7 @@ export default function StatsCharts({ requests }: StatsChartsProps) {
<div className="h-full overflow-auto p-4 space-y-6">
<div>
<h3 className="text-xs font-medium text-text-muted uppercase tracking-wider mb-3">
Status distribution
{t("statsStatusDistribution")}
</h3>
<div style={{ height: 160 }}>
<ResponsiveContainer width="100%" height="100%">
@@ -51,7 +54,7 @@ export default function StatsCharts({ requests }: StatsChartsProps) {
{latencyData.length > 1 && (
<div>
<h3 className="text-xs font-medium text-text-muted uppercase tracking-wider mb-3">
Latency (last 50 requests)
{t("statsLatency")}
</h3>
<div style={{ height: 160 }}>
<ResponsiveContainer width="100%" height="100%">
@@ -75,13 +78,13 @@ export default function StatsCharts({ requests }: StatsChartsProps) {
<div className="grid grid-cols-3 gap-3 text-sm">
<div className="rounded border border-border bg-bg-subtle p-3">
<div className="text-2xl font-bold text-text-main">{requests.length}</div>
<div className="text-xs text-text-muted mt-1">Total requests</div>
<div className="text-xs text-text-muted mt-1">{t("statsTotalRequests")}</div>
</div>
<div className="rounded border border-border bg-bg-subtle p-3">
<div className="text-2xl font-bold text-green-400">
{requests.filter((r) => typeof r.status === "number" && r.status < 400).length}
</div>
<div className="text-xs text-text-muted mt-1">Successful</div>
<div className="text-xs text-text-muted mt-1">{t("statsSuccessful")}</div>
</div>
<div className="rounded border border-border bg-bg-subtle p-3">
<div className="text-2xl font-bold text-red-400">
@@ -92,7 +95,7 @@ export default function StatsCharts({ requests }: StatsChartsProps) {
).length
}
</div>
<div className="text-xs text-text-muted mt-1">Errors</div>
<div className="text-xs text-text-muted mt-1">{t("statsErrors")}</div>
</div>
</div>
</div>

View File

@@ -1,6 +1,7 @@
"use client";
import dynamic from "next/dynamic";
import { useTranslations } from "next-intl";
import type { InterceptedRequest } from "@/mitm/inspector/types";
interface StatsTabProps {
@@ -10,15 +11,19 @@ interface StatsTabProps {
// Recharts bundle is split via Next.js dynamic() — not included in the initial page chunk.
const StatsCharts = dynamic(() => import("./StatsCharts"), {
ssr: false,
loading: () => <div className="p-4 text-sm text-muted-foreground">Loading charts</div>,
loading: () => <LoadingCharts />,
});
function LoadingCharts() {
const t = useTranslations("trafficInspector");
return <div className="p-4 text-sm text-muted-foreground">{t("loadingCharts")}</div>;
}
export function StatsTab({ requests }: StatsTabProps) {
const t = useTranslations("trafficInspector");
if (requests.length === 0) {
return (
<div className="p-4 text-sm text-text-muted">
No requests yet. Start a session recording to capture data for stats.
</div>
<div className="p-4 text-sm text-text-muted">{t("statsNoData")}</div>
);
}
return <StatsCharts requests={requests} />;