feat
ci-front / build (push) Successful in 2m26s

This commit is contained in:
2026-04-04 19:49:37 +03:00
parent 69ff617c30
commit c6a9907822
7 changed files with 171 additions and 123 deletions
@@ -33,14 +33,14 @@ export const Sidebar: React.FC<SidebarProps> = ({
const [showTokenModal, setShowTokenModal] = useState(false);
const [showGraphs, setShowGraphs] = useState(false);
const [expandedAgents, setExpandedAgents] = useState<Set<string>>(
new Set(agents.map((a) => a.name)),
new Set(agents.map((a) => a.label)),
);
const toggleAgent = (name: string) => {
const toggleAgent = (label: string) => {
setExpandedAgents((prev) => {
const next = new Set(prev);
if (next.has(name)) next.delete(name);
else next.add(name);
if (next.has(label)) next.delete(label);
else next.add(label);
return next;
});
};
@@ -50,8 +50,8 @@ export const Sidebar: React.FC<SidebarProps> = ({
const query = searchQuery.toLowerCase();
return agents.filter(
(agent) =>
agent.name.toLowerCase().includes(query) ||
agent.services.some((s) => s.name.toLowerCase().includes(query)),
agent.label.toLowerCase().includes(query) ||
agent.services.some((s) => s.toLowerCase().includes(query)),
);
}, [agents, searchQuery]);
@@ -61,25 +61,25 @@ export const Sidebar: React.FC<SidebarProps> = ({
agents.forEach((agent) => {
nodes.push({
id: agent.name,
name: agent.name,
id: agent.label,
name: agent.label,
type: "agent" as const,
val: 8,
description: `Агент: ${agent.name}`,
description: `Агент: ${agent.label}`,
});
agent.services.forEach((service) => {
const serviceId = `${agent.name}-${service.name}`;
const serviceId = `${agent.label}-${service}`;
nodes.push({
id: serviceId,
name: service.name,
name: service,
type: "service" as const,
val: 12,
description: `Сервис: ${service.name} (${service.status})`,
description: `Сервис: ${service}`,
});
links.push({
source: agent.name,
source: agent.label,
target: serviceId,
type: "hosts",
});
@@ -239,10 +239,10 @@ export const Sidebar: React.FC<SidebarProps> = ({
) : (
<div className="space-y-1">
{filteredAgents.map((agent) => {
const isExpanded = expandedAgents.has(agent.name);
const isExpanded = expandedAgents.has(agent.label);
return (
<div
key={agent.name}
key={agent.label}
className="rounded-lg border overflow-hidden transition-all group"
style={{
backgroundColor: "var(--bg-secondary)",
@@ -252,7 +252,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
{/* Agent header — кликабельный для сворачивания */}
<div
className="flex items-center gap-2 px-3 py-2 cursor-pointer hover:opacity-80 transition-opacity"
onClick={() => toggleAgent(agent.name)}
onClick={() => toggleAgent(agent.label)}
>
<span style={{ color: "var(--text-muted)" }}>
{isExpanded ? (
@@ -269,13 +269,11 @@ export const Sidebar: React.FC<SidebarProps> = ({
className="text-sm font-medium flex-1 truncate"
style={{ color: "var(--text-primary)" }}
>
{agent.name}
{agent.label}
</span>
{/* Статус-индикатор агента (сколько сервисов запущено) */}
{/* Статус-индикатор агента (количество сервисов) */}
<div className="flex items-center gap-1">
{agent.services.filter(
(s) => s.status === "running",
).length > 0 && (
{agent.services.length > 0 && (
<span
className="w-2 h-2 rounded-full"
style={{ backgroundColor: "#4ade80" }}
@@ -285,12 +283,7 @@ export const Sidebar: React.FC<SidebarProps> = ({
className="text-[10px]"
style={{ color: "var(--text-muted)" }}
>
{
agent.services.filter(
(s) => s.status === "running",
).length
}
/{agent.services.length}
{agent.services.length}
</span>
</div>
{/* Кнопка удаления — появляется при наведении */}
@@ -299,10 +292,10 @@ export const Sidebar: React.FC<SidebarProps> = ({
e.stopPropagation();
if (
window.confirm(
`Удалить агента "${agent.name}"?`,
`Удалить агента "${agent.label}"?`,
)
) {
removeAgent(agent.name);
removeAgent(agent.label);
}
}}
className="opacity-0 group-hover:opacity-100 p-1 rounded transition-all flex-shrink-0"
@@ -336,49 +329,32 @@ export const Sidebar: React.FC<SidebarProps> = ({
style={{ borderColor: "var(--border)" }}
>
{agent.services.map((service) => {
const isRunning = service.status === "running";
const isError = service.status === "error";
const isStopped =
service.status === "stopped" || !isRunning;
return (
<div
key={service.name}
key={service}
className="flex items-center justify-between py-1"
>
<span
className="text-xs"
style={{ color: "var(--text-secondary)" }}
>
{service.name}
{service}
</span>
{/* Status indicator */}
<div className="flex items-center gap-1.5">
<span
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
style={{
backgroundColor: isRunning
? "#4ade80"
: isError
? "#f87171"
: "#555",
backgroundColor: "#4ade80",
}}
/>
<span
className="text-[10px] font-medium"
style={{
color: isRunning
? "#4ade80"
: isError
? "#f87171"
: "#777",
color: "#4ade80",
}}
>
{isRunning
? "run"
: isError
? "err"
: "stop"}
run
</span>
</div>
</div>
@@ -12,30 +12,22 @@ interface AgentState {
const mockAgents: AgentInfo[] = [
{
name: "agent-core-01",
label: "agent-core-01",
token: "tok_a1b2c3d4e5f6g7h8",
services: [
{ name: "postgres", status: "running" },
{ name: "redis", status: "running" },
{ name: "log-collector", status: "running" },
],
services: ["postgres", "redis", "log-collector"],
connected_at: "2026-04-04 15:25:09",
},
{
name: "agent-worker-02",
label: "agent-worker-02",
token: "tok_x9y8z7w6v5u4t3s2",
services: [
{ name: "celery-worker", status: "running" },
{ name: "flower", status: "stopped" },
],
services: ["celery-worker", "flower"],
connected_at: "2026-04-04 15:25:09",
},
{
name: "agent-monitor-03",
label: "agent-monitor-03",
token: "tok_m1n2o3p4q5r6s7t8",
services: [
{ name: "prometheus", status: "running" },
{ name: "grafana", status: "running" },
{ name: "alertmanager", status: "stopped" },
],
services: ["prometheus", "grafana", "alertmanager"],
connected_at: "2026-04-04 15:25:09",
},
];
@@ -59,6 +51,6 @@ export const useAgentStore = create<AgentState>()((set, get) => ({
},
removeAgent: (name: string) => {
set({ agents: get().agents.filter((a) => a.name !== name) });
set({ agents: get().agents.filter((a) => a.label !== name) });
},
}));