feat(04-02): adicionar tab Sugestões (OpenClaw) em /skills com badge contador
- Tab "Sugestões" na view switcher da página /skills - Renderiza OpenClawPanel com lista de skills emergentes pendentes - Badge amarelo mostra contagem de sugestões pendentes (atualiza a cada 5min) - Query /api/openclaw/suggestions já conectada ao PatternDetector backend Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d4af26e59d
commit
9165ee4c48
|
|
@ -6,9 +6,10 @@
|
||||||
- Phase 1: submodules, tabelas, Neo4j, ReferenceParser
|
- Phase 1: submodules, tabelas, Neo4j, ReferenceParser
|
||||||
- Phase 2: SkillEngine, API REST, Monaco Editor, /skills, autocomplete, Marketplace, versionamento Git-like
|
- Phase 2: SkillEngine, API REST, Monaco Editor, /skills, autocomplete, Marketplace, versionamento Git-like
|
||||||
- Phase 3: miroflow_service.py, bridge TS + KG logging, MiroFlowControl.tsx + tab Científico
|
- Phase 3: miroflow_service.py, bridge TS + KG logging, MiroFlowControl.tsx + tab Científico
|
||||||
|
- Phase 4: PatternDetector (cron 1h), OpenClaw routes (suggestions/patterns/accept/reject), tab Sugestões em /skills + badge contador
|
||||||
|
|
||||||
## In Progress
|
## In Progress
|
||||||
- (nenhum — iniciar Phase 4)
|
- (nenhum — Phase 4 concluída, iniciar Phase 5)
|
||||||
|
|
||||||
## Roadmap Evolution
|
## Roadmap Evolution
|
||||||
- Phase 7 added: Skill Fabric Expandido (compiladores, sandbox, 3 editor modes, validation pipeline)
|
- Phase 7 added: Skill Fabric Expandido (compiladores, sandbox, 3 editor modes, validation pipeline)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { BrowserFrame } from "@/components/Browser/BrowserFrame";
|
import { BrowserFrame } from "@/components/Browser/BrowserFrame";
|
||||||
|
import OpenClawPanel from "@/components/OpenClawPanel";
|
||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
@ -143,7 +144,7 @@ export default function Skills() {
|
||||||
const [historyOpen, setHistoryOpen] = useState(false);
|
const [historyOpen, setHistoryOpen] = useState(false);
|
||||||
const [historySkill, setHistorySkill] = useState<Skill | null>(null);
|
const [historySkill, setHistorySkill] = useState<Skill | null>(null);
|
||||||
|
|
||||||
const [view, setView] = useState<"minhas" | "marketplace">("minhas");
|
const [view, setView] = useState<"minhas" | "marketplace" | "sugestoes">("minhas");
|
||||||
const [mktSearch, setMktSearch] = useState("");
|
const [mktSearch, setMktSearch] = useState("");
|
||||||
const [mktTag, setMktTag] = useState("all");
|
const [mktTag, setMktTag] = useState("all");
|
||||||
const [importedId, setImportedId] = useState<string | null>(null);
|
const [importedId, setImportedId] = useState<string | null>(null);
|
||||||
|
|
@ -187,6 +188,12 @@ export default function Skills() {
|
||||||
enabled: view === "marketplace",
|
enabled: view === "marketplace",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { data: sugData } = useQuery<{ suggestions: unknown[]; total: number }>({
|
||||||
|
queryKey: ["/api/openclaw/suggestions"],
|
||||||
|
refetchInterval: 5 * 60 * 1000,
|
||||||
|
});
|
||||||
|
const pendingSuggestionsCount = sugData?.total ?? 0;
|
||||||
|
|
||||||
// ── Mutations ──────────────────────────────────────────────────────────────
|
// ── Mutations ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const saveMutation = useMutation({
|
const saveMutation = useMutation({
|
||||||
|
|
@ -416,6 +423,17 @@ export default function Skills() {
|
||||||
>
|
>
|
||||||
<Store className="w-3.5 h-3.5" /> Biblioteca
|
<Store className="w-3.5 h-3.5" /> Biblioteca
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setView("sugestoes")}
|
||||||
|
className={`px-3 py-1.5 text-xs flex items-center gap-1.5 transition-colors relative ${view === "sugestoes" ? "bg-[#c89b3c] text-black font-medium" : "text-white/60 hover:text-white hover:bg-white/5"}`}
|
||||||
|
>
|
||||||
|
<Sparkles className="w-3.5 h-3.5" /> Sugestões
|
||||||
|
{pendingSuggestionsCount > 0 && view !== "sugestoes" && (
|
||||||
|
<span className="absolute -top-1 -right-1 w-4 h-4 rounded-full bg-yellow-400 text-black text-[9px] font-bold flex items-center justify-center">
|
||||||
|
{pendingSuggestionsCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{view === "minhas" && (
|
{view === "minhas" && (
|
||||||
<Button size="sm" onClick={openNew} className="bg-[#c89b3c] hover:bg-[#d4a94a] text-black">
|
<Button size="sm" onClick={openNew} className="bg-[#c89b3c] hover:bg-[#d4a94a] text-black">
|
||||||
|
|
@ -669,6 +687,13 @@ export default function Skills() {
|
||||||
)}
|
)}
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</>)}
|
</>)}
|
||||||
|
|
||||||
|
{/* ── VIEW: SUGESTÕES (OpenClaw) ──────────────────────────────────────── */}
|
||||||
|
{view === "sugestoes" && (
|
||||||
|
<div className="flex-1 min-h-0">
|
||||||
|
<OpenClawPanel />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Edit / Create Dialog ────────────────────────────────────────────── */}
|
{/* ── Edit / Create Dialog ────────────────────────────────────────────── */}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue