import { useCallback, useEffect, useState } from "react"; import { Alert, Anchor, Button, Card, Group, Table, Text, TextInput, Title, } from "@mantine/core"; import { IconAlertCircle, IconCheck, IconExternalLink, IconRefresh, IconUpload, } from "@tabler/icons-react"; import { ApiError, LANGFLOW_URL, api, type FlowDefinition, type FlowVersion, } from "../api"; import FlowHelp from "./FlowHelp"; import StatusBadge from "./StatusBadge"; export default function AdminTab() { const [flows, setFlows] = useState([]); const [versions, setVersions] = useState>({}); const [langflowId, setLangflowId] = useState(""); const [importName, setImportName] = useState(""); const [error, setError] = useState(""); const [info, setInfo] = useState(""); const load = useCallback(async () => { try { const list = await api.listFlows(); setFlows(list); const all: Record = {}; for (const f of list) { all[f.id] = await api.listVersions(f.id); } setVersions(all); } catch (e) { setError(e instanceof ApiError ? `${e.status}: ${e.message}` : String(e)); } }, []); useEffect(() => { void load(); }, [load]); async function onImport() { setError(""); setInfo(""); try { const result = await api.importFlow( langflowId, importName.trim() ? importName.trim() : undefined, ); setInfo( `Importiert: ${result.definition.name} (Version ${result.version.version_no})`, ); setLangflowId(""); setImportName(""); await load(); } catch (e) { setError(e instanceof ApiError ? `${e.status}: ${e.message}` : String(e)); } } async function onDeploy(flowId: string, versionNo: number) { setError(""); setInfo(""); try { const version = await api.deployVersion(flowId, versionNo); setInfo(`Deployed: Version ${version.version_no} (${version.lfx_flow_id})`); await load(); } catch (e) { setError(e instanceof ApiError ? `${e.status}: ${e.message}` : String(e)); } } async function onSync() { setError(""); setInfo(""); try { const r = await api.syncFlows(); const parts = [ `Langflow: ${r.pulled.length}`, `+${r.added.length}`, `~${r.updated.length}`, `-${r.deleted.length}`, ].join(" "); const errs = Object.entries(r.errors) .map(([f, e]) => `${f}: ${e}`) .join("; "); setInfo(`Sync ${parts}${errs ? ` — Fehler: ${errs}` : ""}`); await load(); } catch (e) { setError(e instanceof ApiError ? `${e.status}: ${e.message}` : String(e)); } } return (
Flows & Versionen {error && ( } mb="md"> {error} )} {info && ( } mb="md"> {info} )} Flows werden visuell in Langflow entworfen:{" "} Langflow öffnen setLangflowId(e.target.value)} style={{ flex: 1 }} styles={{ input: { fontFamily: "monospace" } }} /> setImportName(e.target.value)} /> Holt alle Flows aus Langflow nach backend/flows und deployed sie danach automatisch. {flows.length === 0 ? ( Keine Flows vorhanden. ) : ( Flow Version Status Autor {flows.map((f) => { const list = versions[f.id] ?? []; const latest = list[list.length - 1]; if (!latest) { return ( {f.name}{" "} {f.source_path ? `(${f.source_path})` : "(API)"} – – ); } const live = [...list] .reverse() .find((v) => v.deployed_at && v.id !== latest.id); return ( {f.name}{" "} {f.source_path ? `(${f.source_path})` : "(API)"} v{latest.version_no} {live && ( {" "} (live: v{live.version_no}) )} {latest.deployed_at ? ( ) : ( )} {latest.author || "–"} ); })}
)}
); }