feat: consolidate Endpoint, MCP, A2A into tabbed Endpoints page

- Renamed sidebar 'Endpoint' to 'Endpoints', removed standalone MCP/A2A entries
- Created tabbed layout with SegmentedControl: Endpoint Proxy | MCP | A2A | API Endpoints
- Added inline ServiceToggle (online/offline status) for MCP and A2A tabs
- Created ApiEndpointsTab placeholder with Coming Soon badge
- Updated i18n in en.json and pt.json with new endpoints namespace
This commit is contained in:
diegosouzapw
2026-03-05 16:36:58 -03:00
parent 084b206ae6
commit 0f22f38f7e
5 changed files with 214 additions and 24 deletions

View File

@@ -0,0 +1,46 @@
"use client";
import { Card } from "@/shared/components";
import { useTranslations } from "next-intl";
export default function ApiEndpointsTab() {
const t = useTranslations("endpoints");
return (
<div className="p-6 max-w-5xl mx-auto space-y-6">
<Card className="p-8 text-center space-y-4">
<div className="flex items-center justify-center size-16 rounded-2xl bg-primary/10 text-primary mx-auto">
<span className="material-symbols-outlined text-[32px]">code</span>
</div>
<h2 className="text-xl font-semibold">{t("apiEndpointsTitle")}</h2>
<p className="text-sm text-text-muted max-w-md mx-auto">{t("apiEndpointsDescription")}</p>
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-amber-500/10 text-amber-500 text-sm font-medium">
<span className="material-symbols-outlined text-[18px]">construction</span>
{t("comingSoon")}
</div>
</Card>
<Card className="p-5">
<h3 className="text-sm font-semibold mb-3">{t("plannedFeatures")}</h3>
<ul className="space-y-2 text-sm text-text-muted">
<li className="flex items-center gap-2">
<span className="material-symbols-outlined text-[16px] text-primary">check_circle</span>
{t("featureRestApi")}
</li>
<li className="flex items-center gap-2">
<span className="material-symbols-outlined text-[16px] text-primary">check_circle</span>
{t("featureWebhooks")}
</li>
<li className="flex items-center gap-2">
<span className="material-symbols-outlined text-[16px] text-primary">check_circle</span>
{t("featureSwagger")}
</li>
<li className="flex items-center gap-2">
<span className="material-symbols-outlined text-[16px] text-primary">check_circle</span>
{t("featureAuth")}
</li>
</ul>
</Card>
</div>
);
}

View File

@@ -1,7 +1,141 @@
"use client";
import { useState, useEffect, useCallback } from "react";
import { SegmentedControl } from "@/shared/components";
import { getMachineId } from "@/shared/utils/machine";
import EndpointPageClient from "./EndpointPageClient";
import McpDashboardPage from "../mcp/page";
import A2ADashboardPage from "../a2a/page";
import ApiEndpointsTab from "./ApiEndpointsTab";
import { useTranslations } from "next-intl";
export default async function EndpointPage() {
const machineId = await getMachineId();
return <EndpointPageClient machineId={machineId} />;
type ServiceStatus = {
online: boolean;
loading: boolean;
};
function ServiceToggle({
label,
status,
onRefresh,
}: {
label: string;
status: ServiceStatus;
onRefresh: () => void;
}) {
return (
<div className="flex items-center gap-2 ml-auto">
<button
onClick={onRefresh}
className="flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium transition-all border"
style={{
borderColor: status.loading
? "var(--color-border)"
: status.online
? "rgba(34,197,94,0.3)"
: "rgba(239,68,68,0.3)",
background: status.loading
? "transparent"
: status.online
? "rgba(34,197,94,0.1)"
: "rgba(239,68,68,0.1)",
color: status.loading
? "var(--color-text-muted)"
: status.online
? "rgb(34,197,94)"
: "rgb(239,68,68)",
}}
>
<span
className="inline-block w-2 h-2 rounded-full"
style={{
background: status.loading
? "var(--color-text-muted)"
: status.online
? "rgb(34,197,94)"
: "rgb(239,68,68)",
animation: status.online ? "pulse 2s infinite" : "none",
}}
/>
{status.loading ? "..." : status.online ? "Online" : "Offline"}
</button>
</div>
);
}
export default function EndpointPage() {
const [activeTab, setActiveTab] = useState("endpoint-proxy");
const t = useTranslations("endpoints");
const [mcpStatus, setMcpStatus] = useState<ServiceStatus>({ online: false, loading: true });
const [a2aStatus, setA2aStatus] = useState<ServiceStatus>({ online: false, loading: true });
const refreshMcpStatus = useCallback(async () => {
setMcpStatus((prev) => ({ ...prev, loading: true }));
try {
const res = await fetch("/api/mcp/status");
if (res.ok) {
const data = await res.json();
setMcpStatus({ online: !!data.online, loading: false });
} else {
setMcpStatus({ online: false, loading: false });
}
} catch {
setMcpStatus({ online: false, loading: false });
}
}, []);
const refreshA2aStatus = useCallback(async () => {
setA2aStatus((prev) => ({ ...prev, loading: true }));
try {
const res = await fetch("/api/a2a/status");
if (res.ok) {
const data = await res.json();
setA2aStatus({ online: data.status === "ok", loading: false });
} else {
setA2aStatus({ online: false, loading: false });
}
} catch {
setA2aStatus({ online: false, loading: false });
}
}, []);
useEffect(() => {
const load = () => {
void refreshMcpStatus();
void refreshA2aStatus();
};
load();
const interval = setInterval(load, 30000);
return () => clearInterval(interval);
}, [refreshMcpStatus, refreshA2aStatus]);
return (
<div className="flex flex-col gap-6">
<div className="flex flex-wrap items-center gap-3">
<SegmentedControl
options={[
{ value: "endpoint-proxy", label: t("tabProxy"), icon: "api" },
{ value: "mcp", label: "MCP", icon: "hub" },
{ value: "a2a", label: "A2A", icon: "group_work" },
{ value: "api-endpoints", label: t("tabApiEndpoints"), icon: "code" },
]}
value={activeTab}
onChange={setActiveTab}
/>
{activeTab === "mcp" && (
<ServiceToggle label="MCP" status={mcpStatus} onRefresh={refreshMcpStatus} />
)}
{activeTab === "a2a" && (
<ServiceToggle label="A2A" status={a2aStatus} onRefresh={refreshA2aStatus} />
)}
</div>
{activeTab === "endpoint-proxy" && <EndpointPageClient machineId="" />}
{activeTab === "mcp" && <McpDashboardPage />}
{activeTab === "a2a" && <A2ADashboardPage />}
{activeTab === "api-endpoints" && <ApiEndpointsTab />}
</div>
);
}

View File

@@ -74,9 +74,7 @@
"translator": "Translator",
"docs": "Docs",
"issues": "Issues",
"endpoint": "Endpoint",
"mcp": "MCP",
"a2a": "A2A",
"endpoints": "Endpoints",
"apiManager": "API Manager",
"logs": "Logs",
"auditLog": "Audit Log",
@@ -128,12 +126,8 @@
"cliToolsDescription": "Configure CLI tools",
"home": "Home",
"homeDescription": "Welcome to OmniRoute",
"endpoint": "Endpoint",
"endpointDescription": "API endpoint configuration",
"mcp": "MCP Management",
"mcpDescription": "Monitor MCP server process, tools, and operational controls",
"a2a": "A2A Management",
"a2aDescription": "Monitor Agent2Agent status, tasks, and streaming activity",
"endpoint": "Endpoints",
"endpointDescription": "Manage proxy endpoints, MCP, A2A, and API endpoints",
"settings": "Settings",
"settingsDescription": "Manage your preferences",
"openaiCompatible": "OpenAI Compatible",
@@ -821,6 +815,18 @@
"a2aQuickStartStep2": "Send JSON-RPC requests to `POST /a2a` using `message/send` or `message/stream`.",
"a2aQuickStartStep3": "Track and control tasks using `tasks/get` and `tasks/cancel`."
},
"endpoints": {
"tabProxy": "Endpoint Proxy",
"tabApiEndpoints": "API Endpoints",
"apiEndpointsTitle": "API Endpoints",
"apiEndpointsDescription": "Backend API endpoints that can be consumed by other applications and services. This section will list all available REST APIs with documentation and testing capabilities.",
"comingSoon": "Coming Soon",
"plannedFeatures": "Planned Features",
"featureRestApi": "REST API endpoint catalog with interactive documentation",
"featureWebhooks": "Webhook configuration and event subscriptions",
"featureSwagger": "OpenAPI / Swagger spec auto-generation",
"featureAuth": "API key and OAuth scope management per endpoint"
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",
"activate": "activate",

View File

@@ -74,9 +74,7 @@
"translator": "Tradutor",
"docs": "Documentos",
"issues": "Problemas",
"endpoint": "Ponto final",
"mcp": "MCP",
"a2a": "A2A",
"endpoints": "Endpoints",
"apiManager": "Gerenciador de APIs",
"logs": "Registros",
"auditLog": "Registro de auditoria",
@@ -128,12 +126,8 @@
"cliToolsDescription": "Configurar ferramentas CLI",
"home": "Página inicial",
"homeDescription": "Bem-vindo ao OmniRoute",
"endpoint": "Ponto final",
"endpointDescription": "Configuração de endpoint de API",
"mcp": "MCP Management",
"mcpDescription": "Monitor MCP server process, tools, and operational controls",
"a2a": "A2A Management",
"a2aDescription": "Monitor Agent2Agent status, tasks, and streaming activity",
"endpoint": "Endpoints",
"endpointDescription": "Gerenciar endpoints proxy, MCP, A2A e endpoints de API",
"settings": "Configurações",
"settingsDescription": "Gerencie suas preferências",
"openaiCompatible": "Compatível com OpenAI",
@@ -821,6 +815,18 @@
"a2aQuickStartStep2": "Send JSON-RPC requests to `POST /a2a` using `message/send` or `message/stream`.",
"a2aQuickStartStep3": "Track and control tasks using `tasks/get` and `tasks/cancel`."
},
"endpoints": {
"tabProxy": "Endpoint Proxy",
"tabApiEndpoints": "Endpoints de API",
"apiEndpointsTitle": "Endpoints de API",
"apiEndpointsDescription": "Endpoints de API backend que podem ser consumidos por outras aplicações e serviços. Esta seção listará todas as APIs REST disponíveis com documentação e testes.",
"comingSoon": "Em Breve",
"plannedFeatures": "Funcionalidades Planejadas",
"featureRestApi": "Catálogo de endpoints REST API com documentação interativa",
"featureWebhooks": "Configuração de webhooks e assinaturas de eventos",
"featureSwagger": "Geração automática de specs OpenAPI / Swagger",
"featureAuth": "Gestão de chaves API e escopos OAuth por endpoint"
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",
"activate": "activate",

View File

@@ -14,9 +14,7 @@ import { useTranslations } from "next-intl";
// Nav items use i18n keys resolved inside the component
const navItemDefs = [
{ href: "/dashboard", i18nKey: "home", icon: "home", exact: true },
{ href: "/dashboard/endpoint", i18nKey: "endpoint", icon: "api" },
{ href: "/dashboard/mcp", i18nKey: "mcp", icon: "hub" },
{ href: "/dashboard/a2a", i18nKey: "a2a", icon: "group_work" },
{ href: "/dashboard/endpoint", i18nKey: "endpoints", icon: "api" },
{ href: "/dashboard/api-manager", i18nKey: "apiManager", icon: "vpn_key" },
{ href: "/dashboard/providers", i18nKey: "providers", icon: "dns" },
{ href: "/dashboard/combos", i18nKey: "combos", icon: "layers" },