Auftragsbuch: Konzept-Ansicht fuer IDE-Projekte (KONZEPT.md inline)
IDE-Vorbereiten-Karten bekommen einen 'Konzept'-Knopf: MC2 holt KONZEPT.md aus dem vorbereiteten Gitea-Repo (Gitea-Raw-API + Auth aus git-credentials) und rendert es direkt im Auftragsbuch (leichter MD-Renderer lib/markdown.tsx). So liest der Commander das ausgearbeitete Konzept in der GUI, ohne das Repo aufzumachen (User-Wunsch: 'lesen gehoert in MC2, feilen in die IDE'). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,10 @@ import { useEffect, useState } from "react"
|
||||
import {
|
||||
Inbox, Check, X, GitBranch, FileDiff, Loader2, AlertTriangle, Sparkles,
|
||||
ChevronDown, ChevronRight, Clock, ClipboardCheck, Hammer, RefreshCw, Layers, Lightbulb, Send, HelpCircle,
|
||||
RotateCcw, Square, ArrowUp, ArrowDown, Code2,
|
||||
RotateCcw, Square, ArrowUp, ArrowDown, Code2, FileText, GitFork,
|
||||
} from "lucide-react"
|
||||
import { api, type AuftragItem, type IdeenErgebnis, type IdeenItem, type IdeenProjekt, type SkillKandidat } from "@/lib/api"
|
||||
import { api, type AuftragItem, type IdeenErgebnis, type IdeenItem, type IdeenKonzept, type IdeenProjekt, type SkillKandidat } from "@/lib/api"
|
||||
import { Markdown } from "@/lib/markdown"
|
||||
import { useAuftragsbuch, useIdeen, useIdeenLog, useQueryClient, qk } from "@/lib/queries"
|
||||
import { useDialog } from "@/lib/useDialog"
|
||||
import { cn } from "@/lib/utils"
|
||||
@@ -167,6 +168,7 @@ function IdeenSection() {
|
||||
const [sending, setSending] = useState(false)
|
||||
const [busy, setBusy] = useState<Record<string, boolean>>({})
|
||||
const [openLog, setOpenLog] = useState<string | null>(null)
|
||||
const [openKonzept, setOpenKonzept] = useState<string | null>(null)
|
||||
const [antworten, setAntworten] = useState<Record<string, string>>({})
|
||||
|
||||
const items = data?.items ?? []
|
||||
@@ -329,6 +331,15 @@ function IdeenSection() {
|
||||
{busy[it.id] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <X className="h-3.5 w-3.5" />}
|
||||
</button>
|
||||
)}
|
||||
{it.welt === "ide" && (it.status === "running" || it.status === "done") && (
|
||||
<button
|
||||
onClick={() => setOpenKonzept(openKonzept === it.id ? null : it.id)}
|
||||
title="Konzept ansehen (das ausgearbeitete KONZEPT.md)"
|
||||
className={cn("flex shrink-0 items-center gap-1 rounded-md px-1.5 py-1 text-[10px] font-bold transition-colors cursor-pointer",
|
||||
openKonzept === it.id ? "text-violet-300" : "text-violet-300/70 hover:text-violet-200")}>
|
||||
<FileText className="h-3.5 w-3.5" /> Konzept
|
||||
</button>
|
||||
)}
|
||||
{canOpen && (
|
||||
<button
|
||||
onClick={() => setOpenLog(isOpen ? null : it.id)}
|
||||
@@ -339,6 +350,8 @@ function IdeenSection() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{openKonzept === it.id && <KonzeptPanel id={it.id} />}
|
||||
|
||||
{it.status === "running" && it.notiz && (
|
||||
<p className="-mt-1 ml-1 text-[11px] italic leading-relaxed text-sky-200/80" title="Jüngste Zwischenmeldung des Workers (Heartbeat)">
|
||||
„{it.notiz}“
|
||||
@@ -551,6 +564,57 @@ function ProjektGruppe({ projekt, karten, zeile }: {
|
||||
)
|
||||
}
|
||||
|
||||
// Konzept eines vorbereiteten IDE-Projekts: holt KONZEPT.md aus dem Gitea-Repo und
|
||||
// rendert es direkt (User-Wunsch 20.07.: „lesen gehört in MC2"). Läuft die Vorbereitung
|
||||
// noch, gibt es evtl. noch kein Repo/Konzept — dann ehrlicher Hinweis + Nachlade-Button.
|
||||
function KonzeptPanel({ id }: { id: string }) {
|
||||
const [data, setData] = useState<IdeenKonzept | null>(null)
|
||||
const [fehler, setFehler] = useState(false)
|
||||
const [versuch, setVersuch] = useState(0)
|
||||
useEffect(() => {
|
||||
let weg = false
|
||||
setData(null); setFehler(false)
|
||||
api<IdeenKonzept>(`/api/ideen/${id}/konzept`)
|
||||
.then((r) => { if (!weg) setData(r) })
|
||||
.catch(() => { if (!weg) setFehler(true) })
|
||||
return () => { weg = true }
|
||||
}, [id, versuch])
|
||||
return (
|
||||
<div className="ml-2 -mt-1">
|
||||
<div className="rounded-lg border border-violet-500/25 bg-violet-500/[0.04] p-3">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<p className="flex items-center gap-1.5 text-[10px] font-bold uppercase tracking-wide text-violet-300">
|
||||
<FileText className="h-3 w-3" /> Konzept
|
||||
</p>
|
||||
{data?.repo && (
|
||||
<a href={`https://git.tobisniceshomelab.ddnsfree.com/${data.repo}`}
|
||||
className="flex items-center gap-1 text-[10px] text-violet-300/70 hover:text-violet-200">
|
||||
<GitFork className="h-3 w-3" /> {data.repo}
|
||||
</a>
|
||||
)}
|
||||
<button onClick={() => setVersuch((v) => v + 1)} title="Neu laden"
|
||||
className="ml-auto rounded p-0.5 text-muted-foreground/50 hover:text-foreground cursor-pointer">
|
||||
<RefreshCw className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
{fehler ? (
|
||||
<p className="text-[11px] text-red-400">Konzept nicht ladbar — später nochmal versuchen.</p>
|
||||
) : data === null ? (
|
||||
<p className="text-[10px] text-muted-foreground animate-pulse">Konzept wird geladen …</p>
|
||||
) : data.konzept ? (
|
||||
<div className="max-h-[28rem] overflow-y-auto pr-1">
|
||||
<Markdown text={data.konzept} />
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-[11px] text-muted-foreground italic">
|
||||
{data.error || "Noch kein Konzept — das Projekt wird gerade vorbereitet."}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Ergebnis einer FERTIGEN Queue-Aufgabe: die Abschluss-Zusammenfassung des Workers
|
||||
// (kanban_complete). Lazy — wird erst beim Aufklappen geladen, ändert sich nie mehr.
|
||||
function ErgebnisPanel({ id }: { id: string }) {
|
||||
|
||||
Reference in New Issue
Block a user