diff --git a/src/App.tsx b/src/App.tsx index 83660fe..fd675cb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,7 @@ import ProjectPage from "@/pages/ProjectPage"; import CreateProjectPage from "@/pages/CreateProjectPage"; import SettingsLayout from "@/pages/settings/SettingsLayout"; import SettingsPage from "@/pages/settings/SettingsPage"; +import LabelsPage from "@/pages/settings/LabelsPage"; import AgentsPage from "@/pages/settings/AgentsPage"; function App() { @@ -38,6 +39,7 @@ function App() { }> } /> + } /> } /> diff --git a/src/components/AgentModal.tsx b/src/components/AgentModal.tsx index 18e5148..9a0f3e4 100644 --- a/src/components/AgentModal.tsx +++ b/src/components/AgentModal.tsx @@ -1,7 +1,7 @@ -import { useState } from "react"; -import type { Member } from "@/lib/api"; -import { updateMember, regenerateToken, revokeToken } from "@/lib/api"; +import { useState, useEffect } from "react"; +import type { Member, Label } from "@/lib/api"; +import { updateMember, regenerateToken, revokeToken, getLabels } from "@/lib/api"; interface Props { agent: Member; @@ -23,6 +23,12 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) { const [copied, setCopied] = useState(false); const [confirmRegen, setConfirmRegen] = useState(false); const [confirmRevoke, setConfirmRevoke] = useState(false); + const [allLabels, setAllLabels] = useState([]); + const [agentLabels, setAgentLabels] = useState(agent.agent_config?.labels || []); + + useEffect(() => { + getLabels().then(setAllLabels).catch(() => {}); + }, []); const handleSave = async () => { setSaving(true); @@ -32,6 +38,7 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) { name: name.trim(), agent_config: { capabilities: caps, + labels: agentLabels, chat_listen: chatListen, task_listen: taskListen, prompt: prompt.trim() || null, @@ -117,6 +124,34 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) { /> + {/* Labels */} + {allLabels.length > 0 && ( +
+ +
+ {allLabels.map((l) => ( + + ))} +
+

Для авто-назначения: лейблы задачи ∩ лейблы агента

+
+ )} +
diff --git a/src/lib/api.ts b/src/lib/api.ts index d7a751f..4f73b70 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -54,6 +54,7 @@ export interface MemberBrief { export interface AgentConfig { capabilities: string[]; + labels: string[]; chat_listen: string; task_listen: string; prompt: string | null; diff --git a/src/pages/settings/LabelsPage.tsx b/src/pages/settings/LabelsPage.tsx new file mode 100644 index 0000000..9b11ecd --- /dev/null +++ b/src/pages/settings/LabelsPage.tsx @@ -0,0 +1,67 @@ +import { useState, useEffect } from "react"; +import type { Label } from "@/lib/api"; +import { getLabels, createLabel, deleteLabel } from "@/lib/api"; + +export default function LabelsPage() { + const [labels, setLabels] = useState([]); + const [newName, setNewName] = useState(""); + const [newColor, setNewColor] = useState("#6366f1"); + + useEffect(() => { + getLabels().then(setLabels).catch(() => {}); + }, []); + + const handleCreate = async () => { + if (!newName.trim()) return; + const label = await createLabel(newName.trim(), newColor); + setLabels([...labels, label]); + setNewName(""); + }; + + return ( +
+

🏷️ Лейблы

+

Глобальные лейблы для задач и агентов. Используются для авто-назначения.

+ +
+ {labels.map((label) => ( +
+
+ + {label.name} +
+ +
+ ))} + {labels.length === 0 &&

Нет лейблов. Создайте первый.

} +
+ +
+ setNewName(e.target.value)} + onKeyDown={e => e.key === "Enter" && handleCreate()} + placeholder="Новый лейбл..." + className="flex-1 px-3 py-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg text-sm outline-none focus:border-[var(--accent)]" + /> + setNewColor(e.target.value)} + className="w-10 h-10 rounded cursor-pointer border border-[var(--border)]" + /> + +
+
+ ); +} diff --git a/src/pages/settings/SettingsLayout.tsx b/src/pages/settings/SettingsLayout.tsx index 89d8bca..ed51f6b 100644 --- a/src/pages/settings/SettingsLayout.tsx +++ b/src/pages/settings/SettingsLayout.tsx @@ -3,6 +3,7 @@ import { logout } from "@/lib/auth-client"; const MENU = [ { href: "/settings", label: "Общие", icon: "⚙️" }, + { href: "/settings/labels", label: "Лейблы", icon: "🏷️" }, { href: "/settings/agents", label: "Агенты", icon: "🤖" }, ]; diff --git a/src/pages/settings/SettingsPage.tsx b/src/pages/settings/SettingsPage.tsx index 91222e6..cbded20 100644 --- a/src/pages/settings/SettingsPage.tsx +++ b/src/pages/settings/SettingsPage.tsx @@ -1,73 +1,9 @@ -import { useState, useEffect } from "react"; -import type { Label } from "@/lib/api"; -import { getLabels, createLabel, deleteLabel } from "@/lib/api"; - export default function SettingsPage() { - const [labels, setLabels] = useState([]); - const [newName, setNewName] = useState(""); - const [newColor, setNewColor] = useState("#6366f1"); - - useEffect(() => { - getLabels().then(setLabels).catch(() => {}); - }, []); - return ( -
-

⚙️ Общие настройки

- - {/* Labels */} -
-

Лейблы

-

Глобальные лейблы для задач и агентов. Используются для авто-назначения.

-
- {labels.map((label) => ( -
-
- - {label.name} -
- -
- ))} - {labels.length === 0 &&

Нет лейблов

} -
-
- setNewName(e.target.value)} - onKeyDown={e => { - if (e.key === "Enter" && newName.trim()) { - createLabel(newName.trim(), newColor).then(l => { - setLabels([...labels, l]); - setNewName(""); - }); - } - }} - placeholder="Новый лейбл..." - className="flex-1 px-3 py-1.5 bg-[var(--bg)] border border-[var(--border)] rounded-lg text-sm outline-none" - /> - setNewColor(e.target.value)} - className="w-8 h-8 rounded cursor-pointer" - /> - -
+
+

⚙️ Общие настройки

+
+

Coming soon...

);