import React, { useEffect, useMemo, useRef, useState } from "react"; export default function UseCaseTab({ owner, repo }) { const [useCases, setUseCases] = useState([]); const [selectedId, setSelectedId] = useState(""); const [useCase, setUseCase] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [draftTitle, setDraftTitle] = useState("New Use Case"); const [message, setMessage] = useState(""); const messagesEndRef = useRef(null); const canUse = useMemo(() => Boolean(owner && repo), [owner, repo]); const spec = useCase?.spec || {}; function scrollToBottom() { requestAnimationFrame(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }); } async function loadUseCases() { if (!canUse) return; setError(""); try { const res = await fetch(`/api/repos/${owner}/${repo}/use-cases`); if (!res.ok) throw new Error(`Failed to list use cases (${res.status})`); const data = await res.json(); const list = data.use_cases || []; setUseCases(list); // auto select active or first const active = list.find((x) => x.is_active); const nextId = active?.use_case_id || list[0]?.use_case_id || ""; if (!selectedId && nextId) setSelectedId(nextId); } catch (e) { setError(e?.message || "Failed to load use cases"); } } async function loadUseCase(id) { if (!canUse || !id) return; setError(""); try { const res = await fetch(`/api/repos/${owner}/${repo}/use-cases/${id}`); if (!res.ok) throw new Error(`Failed to load use case (${res.status})`); const data = await res.json(); setUseCase(data.use_case || null); scrollToBottom(); } catch (e) { setError(e?.message || "Failed to load use case"); } } useEffect(() => { loadUseCases(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [owner, repo]); useEffect(() => { if (!selectedId) return; loadUseCase(selectedId); // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedId]); async function createUseCase() { if (!canUse) return; setBusy(true); setError(""); try { const res = await fetch(`/api/repos/${owner}/${repo}/use-cases`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: draftTitle || "New Use Case" }), }); if (!res.ok) { const txt = await res.text().catch(() => ""); throw new Error(`Create failed (${res.status}) ${txt}`); } const data = await res.json(); const id = data?.use_case?.use_case_id; await loadUseCases(); if (id) setSelectedId(id); setDraftTitle("New Use Case"); } catch (e) { setError(e?.message || "Create failed"); } finally { setBusy(false); } } async function sendMessage() { if (!canUse || !selectedId) return; const msg = (message || "").trim(); if (!msg) return; setBusy(true); setError(""); // optimistic UI: append user message immediately setUseCase((prev) => { if (!prev) return prev; const next = { ...prev }; next.messages = Array.isArray(next.messages) ? [...next.messages] : []; next.messages.push({ role: "user", content: msg, ts: new Date().toISOString() }); return next; }); setMessage(""); scrollToBottom(); try { const res = await fetch( `/api/repos/${owner}/${repo}/use-cases/${selectedId}/chat`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: msg }), } ); if (!res.ok) { const txt = await res.text().catch(() => ""); throw new Error(`Chat failed (${res.status}) ${txt}`); } const data = await res.json(); setUseCase(data.use_case || null); await loadUseCases(); scrollToBottom(); } catch (e) { setError(e?.message || "Chat failed"); } finally { setBusy(false); } } async function finalizeUseCase() { if (!canUse || !selectedId) return; setBusy(true); setError(""); try { const res = await fetch( `/api/repos/${owner}/${repo}/use-cases/${selectedId}/finalize`, { method: "POST" } ); if (!res.ok) { const txt = await res.text().catch(() => ""); throw new Error(`Finalize failed (${res.status}) ${txt}`); } const data = await res.json(); setUseCase(data.use_case || null); await loadUseCases(); alert( "Use Case finalized and marked active.\n\nA Markdown export was saved in the repo workspace .gitpilot/context/use_cases/." ); } catch (e) { setError(e?.message || "Finalize failed"); } finally { setBusy(false); } } const activeId = useCases.find((x) => x.is_active)?.use_case_id; return (
Use Case
Guided chat to clarify requirements and produce a versioned spec.
setDraftTitle(e.target.value)} placeholder="New use case title..." style={styles.titleInput} disabled={!canUse || busy} />
{error ?
{error}
: null}
Use Cases
{useCases.length === 0 ? (
No use cases yet. Create one with New.
) : ( useCases.map((uc) => ( )) )}
Guided Chat
{Array.isArray(useCase?.messages) && useCase.messages.length ? ( useCase.messages.map((m, idx) => (
{m.role === "user" ? "You" : "Assistant"}
{m.content}
)) ) : (
Select a use case and start chatting. You can paste structured info like:
{`Summary: ...
Problem: ...
Users: ...
Requirements:
- ...
Acceptance Criteria:
- ...`}
                
)}