feat: refocus quota and admin ops mutations
Some checks failed
StoryForge CI / Baseline checks (push) Has been cancelled
StoryForge CI / Backend tests (push) Has been cancelled
StoryForge CI / Web tests (push) Has been cancelled

This commit is contained in:
kris
2026-04-04 11:34:21 +08:00
parent 6f1e56daca
commit 7888797663
3 changed files with 57 additions and 8 deletions

View File

@@ -311,3 +311,9 @@
- 管理员在切换“覆盖目标”后,会自动回到 `管理员配置台 -> 覆盖与审计`,直接进入当前目标的审计区。
- 系统主 Agent 历史回滚、系统平台策略历史回滚完成后,会自动回到 `管理员配置台 -> Agent 治理`,方便连续调整系统默认策略。
- 前端回归新增了这三条管理员治理落点断言,锁住“改完就能继续治理”的交互。
### 额度与管理员运维动作回跳补齐
- `租户额度` 保存后,现在会自动回到 `额度` 工作区的策略区域,不再只留一条成功提示。
- `运维扫描 / 事件审计 / 修复计划生成 / 修复计划审计` 完成后,会统一回到 `管理员配置台 -> 运维审计`,方便连续处理下一条事件。
- 前端回归新增了这批动作的 refocus 断言,并锁住了 `credits-quota-anchor``admin-ops-anchor` 两个工作区锚点。

View File

@@ -4615,7 +4615,7 @@ function renderAdminOpsPanel() {
const overview = appState.adminOpsOverview;
if (!overview) {
return `
<div class="panel pad" style="margin-top:18px;">
<div class="panel pad" id="admin-ops-anchor" style="margin-top:18px;">
<div class="panel-head"><div><h3>运维与审计 Agent</h3><div class="panel-subtitle">仅平台最高权限用户可见。</div></div></div>
<div class="task-item"><h4>尚未拉到概览</h4><p>刷新后会自动读取失败任务、集成健康和待审事件。</p></div>
${renderAdminFixRunsPanel()}
@@ -4626,7 +4626,7 @@ function renderAdminOpsPanel() {
const incidents = safeArray(overview.incidents).slice(0, 6);
const audits = safeArray(overview.recent_audits).slice(0, 5);
return `
<div class="panel pad" style="margin-top:18px;">
<div class="panel pad" id="admin-ops-anchor" style="margin-top:18px;">
<div class="panel-head">
<div>
<h3>运维与审计 Agent</h3>
@@ -5749,6 +5749,15 @@ function focusTrackingWorkspace() {
});
}
function focusCreditsWorkspace(anchorId = "credits-quota-anchor") {
setScreen("credits");
renderAll();
window.requestAnimationFrame(() => {
(document.getElementById(anchorId) || document.querySelector('[data-screen="credits"] .mobile-flow-focus-card, [data-screen="credits"] .panel'))
?.scrollIntoView({ behavior: "smooth", block: "start" });
});
}
function focusProductionDetailTab(tabValue) {
appState.productionDetailTab = tabValue;
setScreen("production");
@@ -5803,6 +5812,16 @@ function focusReviewWorkspace(reviewId = "") {
});
}
function focusAdminOpsWorkspace(anchorId = "admin-ops-anchor") {
appState.adminWorkbenchTab = "ops";
setScreen("admin-workbench");
renderAll();
window.requestAnimationFrame(() => {
(document.getElementById(anchorId) || document.querySelector('[data-screen="admin-workbench"] .mobile-flow-focus-card, [data-screen="admin-workbench"] .panel'))
?.scrollIntoView({ behavior: "smooth", block: "start" });
});
}
function renderDiscoveryOverviewSection({ selected, selectedProject, importedSources, tracked, topVideos, reports, latestVideos, currentPlatformLabel, topVideoBatchResult }) {
return `
<div class="layout-grid grid-main">
@@ -7485,7 +7504,7 @@ function renderCreditsScreen() {
</div>
<div class="layout-grid grid-main" style="margin-top:18px;">
<div class="side-stack">
<div class="panel pad">
<div class="panel pad" id="credits-quota-anchor">
<div class="panel-head"><div><h3>当前额度策略</h3><div class="panel-subtitle">先让用户能看懂“还剩多少、风险在哪、下一步怎么做”。</div></div></div>
<div class="list">
<div class="task-item">
@@ -10034,7 +10053,7 @@ function openTenantQuotaAction() {
});
rememberAction("租户额度已更新", "当前项目的预算与配额已经保存。", "green", saved);
await loadAgentControlSurfaces(project.id);
renderAll();
focusCreditsWorkspace("credits-quota-anchor");
}
});
}
@@ -10284,6 +10303,7 @@ function openBenchmarkLinkAction(defaults = {}) {
async function scanAdminOpsAction() {
if (!isSuperAdmin()) throw new Error("只有平台管理者才能调用运维 Agent。");
setBusy(true, "运维 Agent 正在扫描故障事件...");
let shouldRefocus = false;
try {
const payload = await storyforgeFetch("/v2/admin/ops/incidents/scan", {
method: "POST",
@@ -10291,9 +10311,14 @@ async function scanAdminOpsAction() {
});
rememberAction("运维扫描已完成", `本轮共归集 ${formatNumber(payload.count)} 条故障事件。`, payload.count ? "orange" : "green", payload);
await loadAgentControlSurfaces(getOneLinerProjectId());
shouldRefocus = true;
} finally {
setBusy(false, "");
renderAll();
if (shouldRefocus) {
focusAdminOpsWorkspace("admin-ops-anchor");
} else {
renderAll();
}
}
}
@@ -10352,7 +10377,7 @@ function openAdminIncidentReviewAction(incidentId) {
});
rememberAction("审计结果已保存", `事件「${saved.title}」已更新为 ${saved.status}`, "green", saved);
await loadAgentControlSurfaces(getOneLinerProjectId());
renderAll();
focusAdminOpsWorkspace("admin-ops-anchor");
}
});
}
@@ -10388,7 +10413,7 @@ function openAdminRepairPlanAction(incidentId) {
});
rememberAction("修复计划已生成", `已为事件「${incident.title}」生成 repair plan。`, "green", saved);
await loadAgentControlSurfaces(getOneLinerProjectId());
renderAll();
focusAdminOpsWorkspace("admin-ops-anchor");
}
});
}
@@ -10490,7 +10515,7 @@ function openAdminFixRunAuditAction(runId) {
});
rememberAction("修复计划已审计", `修复计划 ${runId} 已更新为 ${saved.audit_status || values.reviewStatus}`, "green", saved);
await loadAgentControlSurfaces(getOneLinerProjectId());
renderAll();
focusAdminOpsWorkspace("admin-ops-anchor");
}
});
}

View File

@@ -1017,6 +1017,24 @@ test("review actions return to the review workspace with the saved review in foc
assert.match(APP, /data-review-id="\$\{escapeHtml\(review\.id\)\}"/);
});
test("quota and admin ops mutations refocus the user into the most relevant workbench area", () => {
const quota = extractBetween(APP, "function openTenantQuotaAction()", "function openCreateAssistantAction()");
const scanOps = extractBetween(APP, "async function scanAdminOpsAction()", "function openAdminIncidentReviewAction(incidentId)");
const reviewIncident = extractBetween(APP, "function openAdminIncidentReviewAction(incidentId)", "function openAdminRepairPlanAction(incidentId)");
const repairPlan = extractBetween(APP, "function openAdminRepairPlanAction(incidentId)", "function openAdminFixRunDetailAction(runId)");
const auditFixRun = extractBetween(APP, "function openAdminFixRunAuditAction(runId)", "function openJobDetailAction(jobId)");
assert.match(APP, /function focusCreditsWorkspace\(anchorId = "credits-quota-anchor"\)/);
assert.match(APP, /function focusAdminOpsWorkspace\(anchorId = "admin-ops-anchor"\)/);
assert.match(APP, /id="credits-quota-anchor"/);
assert.match(APP, /id="admin-ops-anchor"/);
assert.match(quota, /focusCreditsWorkspace\("credits-quota-anchor"\)/);
assert.match(scanOps, /focusAdminOpsWorkspace\("admin-ops-anchor"\)/);
assert.match(reviewIncident, /focusAdminOpsWorkspace\("admin-ops-anchor"\)/);
assert.match(repairPlan, /focusAdminOpsWorkspace\("admin-ops-anchor"\)/);
assert.match(auditFixRun, /focusAdminOpsWorkspace\("admin-ops-anchor"\)/);
});
test("assistant actions return to the playbook workspace with the saved assistant in focus", () => {
const createAssistant = extractBetween(APP, "function openCreateAssistantAction()", "function openEditAssistantAction(assistantId = \"\")");
const editAssistant = extractBetween(APP, "function openEditAssistantAction(assistantId = \"\")", "function openAnalyzeSelectedAccountAction()");