From d7260dcdf587d6f6e8400ba51ff9975cbeb23989 Mon Sep 17 00:00:00 2001
From: xiaoxing0135 <706015750@qq.com>
Date: Sun, 17 May 2026 00:26:18 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20add=20Feedback=20module=20=E2=80=94=20s?=
=?UTF-8?q?tructured=20user=20feedback=20with=20GUI=20+=20API?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- FeedbackPage.tsx: new page with type/module/title/description/email form
- 4 feedback types: Bug Report, Feature Request, Question, Feedback
- 12 module categories for precise issue routing
- Saves to localStorage; POST to /feedback in desktop app
- Thank-you screen after submission
- API: POST /feedback endpoint saves to workspace/feedback/*.json
- Navigation: Feedback link in App.tsx header
- Route registered in main.tsx
---
runtime/api/main.py | 20 +++
runtime/web/src/App.tsx | 8 +-
runtime/web/src/main.tsx | 2 +
runtime/web/src/pages/FeedbackPage.tsx | 195 +++++++++++++++++++++++++
4 files changed, 224 insertions(+), 1 deletion(-)
create mode 100644 runtime/web/src/pages/FeedbackPage.tsx
diff --git a/runtime/api/main.py b/runtime/api/main.py
index 4697242..1783969 100644
--- a/runtime/api/main.py
+++ b/runtime/api/main.py
@@ -14,6 +14,7 @@
from runtime.api.deps import Kernel
from runtime.api.models import CatalogResponse, RunCreateText, RunCreated, RunStatus as RunStatusModel
from runtime.api.parsers import parse_path, parse_text, parse_url
+from runtime.config.settings import get_settings
app = FastAPI(title="Test-Agent Runtime", version=__version__)
app.add_middleware(
@@ -128,6 +129,25 @@ def report(run_id: str) -> JSONResponse:
return JSONResponse(res)
+@app.post("/feedback")
+def submit_feedback(payload: dict) -> dict:
+ """Accept user feedback from desktop/web UI. Logs to workspace/feedback/."""
+ import json as _json
+ from datetime import datetime, timezone
+
+ fb_dir = get_settings().workspace_dir / "feedback"
+ fb_dir.mkdir(parents=True, exist_ok=True)
+ entry = {
+ **payload,
+ "received_at": datetime.now(timezone.utc).isoformat(),
+ }
+ ts = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
+ fname = fb_dir / f"feedback-{ts}.json"
+ fname.write_text(_json.dumps(entry, indent=2, ensure_ascii=False), encoding="utf-8")
+ logger.info("feedback saved: {}", fname)
+ return {"status": "ok", "saved_to": str(fname)}
+
+
def _run_in_background(run_id: str, decision) -> None:
try:
summary = _kernel.execute_sync(run_id, decision)
diff --git a/runtime/web/src/App.tsx b/runtime/web/src/App.tsx
index c2bb3e4..650797d 100644
--- a/runtime/web/src/App.tsx
+++ b/runtime/web/src/App.tsx
@@ -1,5 +1,5 @@
import { Outlet, NavLink } from "react-router-dom";
-import { Beaker, Upload, BookOpen, Settings, Stethoscope } from "lucide-react";
+import { Beaker, Upload, BookOpen, Settings, Stethoscope, MessageSquare } from "lucide-react";
export default function App() {
return (
@@ -37,6 +37,12 @@ export default function App() {
Settings
+
+ (isActive ? "font-semibold" : "")}>
+
+ Feedback
+
+
diff --git a/runtime/web/src/main.tsx b/runtime/web/src/main.tsx
index 7c9209d..9b5218e 100644
--- a/runtime/web/src/main.tsx
+++ b/runtime/web/src/main.tsx
@@ -9,6 +9,7 @@ import ReportPage from "./pages/ReportPage";
import CatalogPage from "./pages/CatalogPage";
import SettingsPage from "./pages/SettingsPage";
import DoctorPage from "./pages/DoctorPage";
+import FeedbackPage from "./pages/FeedbackPage";
import "./index.css";
const queryClient = new QueryClient({
@@ -27,6 +28,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
} />
} />
} />
+ } />
} />
diff --git a/runtime/web/src/pages/FeedbackPage.tsx b/runtime/web/src/pages/FeedbackPage.tsx
new file mode 100644
index 0000000..4c4955c
--- /dev/null
+++ b/runtime/web/src/pages/FeedbackPage.tsx
@@ -0,0 +1,195 @@
+import { useState, FormEvent } from "react";
+import { MessageSquare, Send, Check, AlertCircle } from "lucide-react";
+
+const MODULES = [
+ "Upload / Run",
+ "Report / Results",
+ "Catalog / Skills",
+ "Settings / Config",
+ "Doctor / Check",
+ "CLI / tagent",
+ "Desktop App",
+ "Install / Setup",
+ "Documentation",
+ "Expert (specify)",
+ "Skill (specify)",
+ "Other",
+];
+
+const TYPES = ["Bug Report", "Feature Request", "Question", "Feedback"];
+
+interface FeedbackEntry {
+ id: string;
+ type: string;
+ module: string;
+ title: string;
+ description: string;
+ email: string;
+ timestamp: string;
+}
+
+function saveFeedback(entry: FeedbackEntry) {
+ const existing = JSON.parse(localStorage.getItem("tagent_feedback") || "[]");
+ existing.push(entry);
+ localStorage.setItem("tagent_feedback", JSON.stringify(existing));
+}
+
+export default function FeedbackPage() {
+ const [type, setType] = useState("Bug Report");
+ const [module, setModule] = useState("");
+ const [title, setTitle] = useState("");
+ const [desc, setDesc] = useState("");
+ const [email, setEmail] = useState("");
+ const [sent, setSent] = useState(false);
+ const [error, setError] = useState("");
+
+ const submit = (e: FormEvent) => {
+ e.preventDefault();
+ if (!module || !title || !desc) {
+ setError("Please fill in module, title, and description.");
+ return;
+ }
+
+ const entry: FeedbackEntry = {
+ id: `fb-${Date.now()}`,
+ type,
+ module,
+ title,
+ description: desc,
+ email,
+ timestamp: new Date().toISOString(),
+ };
+
+ saveFeedback(entry);
+
+ // Also try to submit to GitHub Issues if in desktop app
+ if ((window as any).electronAPI?.isElectron) {
+ try {
+ const body = `## ${type}: ${title}\n\n**Module**: ${module}\n**Email**: ${email || "N/A"}\n\n### Description\n${desc}\n\n---\n*Submitted via Test-Agent Desktop v1.32.0*`;
+ fetch("http://localhost:8800/feedback", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ type, module, title, body }),
+ }).catch(() => {});
+ } catch {}
+ }
+
+ setSent(true);
+ setError("");
+ };
+
+ if (sent) {
+ return (
+
+
+
Thank you!
+
Your feedback has been saved. We review every submission.
+
+
+ );
+ }
+
+ return (
+
+
+ Feedback
+
+
+ {error && (
+
+ )}
+
+
+
+
+ Feedback is stored locally and reviewed regularly.
+ For urgent issues, open a{" "}
+
+ GitHub Issue
+ .
+
+
+ );
+}