mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
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:
@@ -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",
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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} />;
|
||||
|
||||
Reference in New Issue
Block a user