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