"use client"; import { useState, useEffect, useCallback } from "react"; import Card from "@/components/ui/Card"; const today = () => new Date().toISOString().split("T")[0]; type Severity = "low" | "medium" | "high" | "critical"; type BugStatus = "open" | "closed"; interface Bug { description: string; severity: Severity; status: BugStatus; } interface FormState { date: string; testsRun: string[]; bugsFound: Bug[]; codeQualityScore: number; recommendations: string[]; } const defaultForm: FormState = { date: today(), testsRun: [""], bugsFound: [], codeQualityScore: 0, recommendations: [""], }; const severityColors: Record = { low: "text-emerald-400 bg-emerald-500/10 border-emerald-500/30", medium: "text-amber-400 bg-amber-500/10 border-amber-500/30", high: "text-orange-400 bg-orange-500/10 border-orange-500/30", critical: "text-red-400 bg-red-500/10 border-red-500/30", }; export default function AmunPage() { const [form, setForm] = useState(defaultForm); const [saving, setSaving] = useState(false); const [saved, setSaved] = useState(false); const [errors, setErrors] = useState>({}); const [loadingDate, setLoadingDate] = useState(false); const [history, setHistory] = useState([]); const loadSessionForDate = useCallback(async (date: string) => { setLoadingDate(true); try { const res = await fetch(`/api/amun?date=${date}`); const data = await res.json(); if (data) { setForm({ date: data.date, testsRun: typeof data.testsRun === "string" ? JSON.parse(data.testsRun) : (data.testsRun || [""]), bugsFound: typeof data.bugsFound === "string" ? JSON.parse(data.bugsFound) : (data.bugsFound || []), codeQualityScore: data.codeQualityScore || 0, recommendations: typeof data.recommendations === "string" ? JSON.parse(data.recommendations) : (data.recommendations || [""]), }); } else { setForm({ ...defaultForm, date }); } } catch { setForm({ ...defaultForm, date }); } finally { setLoadingDate(false); } }, []); const loadHistory = useCallback(async () => { try { const res = await fetch("/api/amun?limit=10"); const data = await res.json(); if (Array.isArray(data)) { setHistory( data.map((s) => ({ date: s.date, testsRun: typeof s.testsRun === "string" ? JSON.parse(s.testsRun) : (s.testsRun || []), bugsFound: typeof s.bugsFound === "string" ? JSON.parse(s.bugsFound) : (s.bugsFound || []), codeQualityScore: s.codeQualityScore || 0, recommendations: typeof s.recommendations === "string" ? JSON.parse(s.recommendations) : (s.recommendations || []), })) ); } } catch { // ignore } }, []); useEffect(() => { loadSessionForDate(today()); loadHistory(); }, [loadSessionForDate, loadHistory]); const validate = (): boolean => { const errs: Record = {}; if (form.codeQualityScore < 0 || form.codeQualityScore > 100) { errs.codeQualityScore = "Score must be between 0 and 100"; } setErrors(errs); return Object.keys(errs).length === 0; }; const handleSave = async () => { if (!validate()) return; setSaving(true); try { const res = await fetch("/api/amun", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(form), }); if (!res.ok) { const err = await res.json(); setErrors({ submit: err.error || "Failed to save" }); } else { setSaved(true); setTimeout(() => setSaved(false), 3000); loadHistory(); } } catch { setErrors({ submit: "Network error. Please try again." }); } finally { setSaving(false); } }; const updateArrayItem = (field: "testsRun" | "recommendations", index: number, value: string) => { setForm((prev) => { const arr = [...prev[field]]; arr[index] = value; return { ...prev, [field]: arr }; }); }; const addArrayItem = (field: "testsRun" | "recommendations") => { setForm((prev) => ({ ...prev, [field]: [...prev[field], ""] })); }; const removeArrayItem = (field: "testsRun" | "recommendations", index: number) => { setForm((prev) => ({ ...prev, [field]: prev[field].filter((_, i) => i !== index) })); }; const addBug = () => { setForm((prev) => ({ ...prev, bugsFound: [...prev.bugsFound, { description: "", severity: "medium", status: "open" }], })); }; const updateBug = (index: number, field: keyof Bug, value: string) => { setForm((prev) => { const bugs = [...prev.bugsFound]; bugs[index] = { ...bugs[index], [field]: value as never }; return { ...prev, bugsFound: bugs }; }); }; const removeBug = (index: number) => { setForm((prev) => ({ ...prev, bugsFound: prev.bugsFound.filter((_, i) => i !== index) })); }; const scoreColor = (score: number) => { if (score >= 80) return "text-emerald-400"; if (score >= 60) return "text-amber-400"; if (score >= 40) return "text-orange-400"; return "text-red-400"; }; const scoreBarColor = (score: number) => { if (score >= 80) return "bg-emerald-500"; if (score >= 60) return "bg-amber-500"; if (score >= 40) return "bg-orange-500"; return "bg-red-500"; }; const openBugs = form.bugsFound.filter((b) => b.status === "open"); const closedBugs = form.bugsFound.filter((b) => b.status === "closed"); return (
{/* Header */}

⚙️ Amun Dev Agent

Development testing & code quality dashboard

{ setForm((prev) => ({ ...prev, date: e.target.value })); loadSessionForDate(e.target.value); }} className="bg-[#1a1d27] border border-[#2a2d3a] rounded-lg px-3 py-2 text-sm text-slate-300 focus:outline-none focus:border-purple-500/50" />
{errors.submit && (
{errors.submit}
)} {/* Stats Row */}

Tests Run

{form.testsRun.filter((t) => t.trim()).length}

Open Bugs

0 ? "text-red-400" : "text-emerald-400"}`}> {openBugs.length}

Code Quality

{form.codeQualityScore}%

{/* Code Quality Score */} {errors.codeQualityScore && (

{errors.codeQualityScore}

)}
setForm((p) => ({ ...p, codeQualityScore: parseInt(e.target.value) }))} className="flex-1 accent-purple-500" />
setForm((p) => ({ ...p, codeQualityScore: Math.min(100, Math.max(0, parseInt(e.target.value) || 0)), })) } className="w-16 bg-[#0f1117] border border-[#2a2d3a] rounded-lg px-2 py-1.5 text-sm text-slate-200 focus:outline-none focus:border-purple-500/50 text-center" /> %
{/* Tests Run */}
{form.testsRun.map((t, i) => (
updateArrayItem("testsRun", i, e.target.value)} placeholder={`Test ${i + 1} (e.g. unit:auth, e2e:checkout)`} className="flex-1 bg-[#0f1117] border border-[#2a2d3a] rounded-lg px-3 py-2 text-sm text-slate-200 focus:outline-none focus:border-blue-500/50 font-mono" /> {form.testsRun.length > 1 && ( )}
))}
{/* Bugs Found */}
{form.bugsFound.length === 0 && (

No bugs logged yet.

)} {form.bugsFound.map((bug, i) => (
updateBug(i, "description", e.target.value)} placeholder="Bug description" className="flex-1 bg-[#13151f] border border-[#2a2d3a] rounded-lg px-3 py-2 text-sm text-slate-200 focus:outline-none focus:border-red-500/50" />
))}
{/* Recommendations */}
{form.recommendations.map((r, i) => (
updateArrayItem("recommendations", i, e.target.value)} placeholder={`Recommendation ${i + 1}`} className="flex-1 bg-[#0f1117] border border-[#2a2d3a] rounded-lg px-3 py-2 text-sm text-slate-200 focus:outline-none focus:border-amber-500/50" /> {form.recommendations.length > 1 && ( )}
))}
{/* Session History */} {history.length > 0 && (
{history.map((s) => ( ))}
)} {/* Save Button */}
); }