feat: update PlanningAgent to support SSE streaming with reasoning process display and fix UI spacing issues
This commit is contained in:
@@ -381,14 +381,14 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
|
||||
{view === "dashboard" && (
|
||||
<>
|
||||
<h2 className="text-xl font-extrabold mb-6">Overview</h2>
|
||||
<h2 className="text-xl font-extrabold mb-6">总览</h2>
|
||||
|
||||
<div className="grid grid-cols-4 gap-5 mb-8">
|
||||
{[
|
||||
{ label: "Open PRs", value: stats.pending, color: "text-yellow-600", bg: "bg-yellow-50" },
|
||||
{ label: "Merged", value: stats.passed, color: "text-green-600", bg: "bg-green-50" },
|
||||
{ label: "Rejected", value: stats.rejected, color: "text-red-600", bg: "bg-red-50" },
|
||||
{ label: "Total Issues", value: stats.totalIssues, color: "text-magenta", bg: "bg-magenta-50" },
|
||||
{ label: "打开 PR", value: stats.pending, color: "text-yellow-600", bg: "bg-yellow-50" },
|
||||
{ label: "已合并", value: stats.passed, color: "text-green-600", bg: "bg-green-50" },
|
||||
{ label: "已拒绝", value: stats.rejected, color: "text-red-600", bg: "bg-red-50" },
|
||||
{ label: "问题总数", value: stats.totalIssues, color: "text-magenta", bg: "bg-magenta-50" },
|
||||
].map((s) => (
|
||||
<div key={s.label} className={`${s.bg} p-6 border border-border rounded-2xl shadow-[0_4px_16px_rgba(0,0,0,0.04)]`}>
|
||||
<div className={`text-3xl font-extrabold ${s.color}`}>{s.value}</div>
|
||||
@@ -401,7 +401,7 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
<ProblemTrendChart history={trendHistory} loading={trendLoading} />
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-extrabold mb-4">Recent Pull Requests</h3>
|
||||
<h3 className="text-lg font-extrabold mb-4">近期 Pull Request</h3>
|
||||
<PRTable prs={prs.slice(0, 8)} loading={loading} onView={(pr) => openPRDetail(pr.id)} />
|
||||
</>
|
||||
)}
|
||||
@@ -409,7 +409,7 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
{view === "pr-list" && (
|
||||
<>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-extrabold">Pull Request List</h2>
|
||||
<h2 className="text-xl font-extrabold">Pull Request 列表</h2>
|
||||
<div className="flex gap-2">
|
||||
{(["all", "open", "merged", "closed"] as const).map((f) => (
|
||||
<button
|
||||
@@ -421,11 +421,11 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
: "border-border text-txt-muted hover:border-magenta"
|
||||
}`}
|
||||
>
|
||||
{f === "all" ? "All" : f === "open" ? "Open" : f === "merged" ? "Merged" : "Closed"}
|
||||
{f === "all" ? "全部" : f === "open" ? "打开" : f === "merged" ? "已合并" : "已关闭"}
|
||||
</button>
|
||||
))}
|
||||
<button className="btn-outline text-xs px-3 py-1.5" onClick={fetchPRs}>
|
||||
Refresh
|
||||
刷新
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -435,21 +435,21 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
|
||||
{view === "settings" && (
|
||||
<div className="max-w-xl">
|
||||
<h2 className="text-xl font-extrabold mb-6">Settings</h2>
|
||||
<h2 className="text-xl font-extrabold mb-6">设置</h2>
|
||||
<div className="card mb-4">
|
||||
<h3 className="font-bold mb-2">Webhook Configuration</h3>
|
||||
<p className="text-sm text-txt-muted mb-3">Add this URL to your Gitea repository webhook settings:</p>
|
||||
<h3 className="font-bold mb-2">Webhook 配置</h3>
|
||||
<p className="text-sm text-txt-muted mb-3">将此 URL 添加到 Gitea 仓库的 webhook 设置:</p>
|
||||
<code className="block bg-surface-muted p-3 text-sm font-mono break-all rounded-lg">
|
||||
POST {window.location.origin}/quality-api/webhook/gitea
|
||||
</code>
|
||||
</div>
|
||||
<div className="card">
|
||||
<h3 className="font-bold mb-2">Quick Notes</h3>
|
||||
<h3 className="font-bold mb-2">快速说明</h3>
|
||||
<ul className="text-sm text-txt-muted list-disc list-inside space-y-1">
|
||||
<li>Supports Gitea Push and Pull Request events</li>
|
||||
<li>Runs Pylint, Flake8, ESLint, and Bandit automatically</li>
|
||||
<li>Optional AI review using DeepSeek-V3</li>
|
||||
<li>Scan summary can be pushed to Feishu channels</li>
|
||||
<li>支持 Gitea Push 与 Pull Request 事件</li>
|
||||
<li>自动运行 Pylint、Flake8、ESLint 与 Bandit</li>
|
||||
<li>可选 AI 审查(DeepSeek-V3)</li>
|
||||
<li>扫描摘要可推送到飞书通知</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -477,7 +477,7 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
|
||||
<div className="flex flex-1 min-h-0">
|
||||
<div className="w-[260px] shrink-0 border-r border-border overflow-y-auto p-4 bg-surface-muted/35">
|
||||
<h3 className="text-xs font-bold text-txt-muted uppercase mb-3">Changed Files</h3>
|
||||
<h3 className="text-xs font-bold text-txt-muted uppercase mb-3">变更文件</h3>
|
||||
{changedFiles.map((f) => (
|
||||
<button
|
||||
key={f.filename}
|
||||
@@ -502,7 +502,7 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
{/* 代码区:完整代码 + 每行右侧缺陷标注,一行一个滚动条(参考 index copy.html) */}
|
||||
<div className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden">
|
||||
<div className="flex-1 min-h-0 overflow-auto bg-[#1e1e1e] font-mono text-[13px] leading-[1.5]">
|
||||
{fileContent ? (
|
||||
{fileContent ? (
|
||||
<div className="min-w-min">
|
||||
{(fileContent.content ?? "").split("\n").map((line, i) => {
|
||||
const lineNum = i + 1;
|
||||
@@ -573,7 +573,7 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-full min-h-[200px] text-[#6c757d] text-sm">
|
||||
{selectedFile ? "Loading file..." : "Select a file to inspect"}
|
||||
{selectedFile ? "加载文件..." : "请选择要检查的文件"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -582,21 +582,21 @@ export default function QualityGate({ view }: QualityGateProps) {
|
||||
|
||||
<div className="flex justify-end gap-3 px-6 py-4 border-t border-border shrink-0">
|
||||
<button className="btn-outline" onClick={() => setModalOpen(false)}>
|
||||
Close
|
||||
关闭
|
||||
</button>
|
||||
<button
|
||||
className="bg-red-600 text-white font-bold text-sm px-6 py-2.5 border-none cursor-pointer hover:opacity-90 disabled:opacity-50 rounded-xl"
|
||||
onClick={handleClose}
|
||||
disabled={loading || selectedPR.state !== "open"}
|
||||
>
|
||||
Reject
|
||||
拒绝
|
||||
</button>
|
||||
<button
|
||||
className="btn-magenta"
|
||||
onClick={handleMerge}
|
||||
disabled={loading || selectedPR.state !== "open"}
|
||||
>
|
||||
Approve & Merge
|
||||
批准并合并
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -616,27 +616,27 @@ function PRTable({
|
||||
onView: (pr: PRScan) => void;
|
||||
}) {
|
||||
if (loading && prs.length === 0) {
|
||||
return <div className="text-center py-8 text-txt-muted text-sm">Loading...</div>;
|
||||
return <div className="text-center py-8 text-txt-muted text-sm">加载中...</div>;
|
||||
}
|
||||
|
||||
if (prs.length === 0) {
|
||||
return <div className="text-center py-8 text-txt-muted text-sm">No PR records found</div>;
|
||||
return <div className="text-center py-8 text-txt-muted text-sm">暂无 PR 记录</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto rounded-2xl border border-border bg-white shadow-[0_6px_22px_rgba(0,0,0,0.04)]">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-surface-muted text-left">
|
||||
<tr className="bg-surface-muted text-left">
|
||||
<th className="px-4 py-3 border-b border-border font-bold">PR#</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">Title</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">Repository</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">Author</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">Branch</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">State</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">Issues</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">Created</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">Action</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">标题</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">仓库</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">作者</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">分支</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">状态</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">问题数</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">创建时间</th>
|
||||
<th className="px-4 py-3 border-b border-border font-bold">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -657,7 +657,7 @@ function PRTable({
|
||||
? "bg-green-100 text-green-700"
|
||||
: "bg-red-100 text-red-700"
|
||||
}`}>
|
||||
{pr.state === "open" ? "Open" : pr.state === "merged" ? "Merged" : "Closed"}
|
||||
{pr.state === "open" ? "打开" : pr.state === "merged" ? "已合并" : "已关闭"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
@@ -666,14 +666,14 @@ function PRTable({
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-xs text-txt-muted">
|
||||
{new Date(pr.created_at).toLocaleString("en-US")}
|
||||
{new Date(pr.created_at).toLocaleString("zh-CN")}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<button
|
||||
className="text-magenta font-bold text-xs hover:underline"
|
||||
onClick={() => onView(pr)}
|
||||
>
|
||||
Inspect →
|
||||
查看 →
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user