fix(workbench): suppress unconfigured demo alerts

The not_configured branch previously promoted mock task failures into critical alerts. Return an explicit empty alert source so the exception badge reflects only actionable backend state.
This commit is contained in:
2026-09-24 15:13:14 +08:00
parent 7e969e7e8c
commit a5e5acb03b
5 changed files with 24 additions and 11 deletions
@@ -142,7 +142,7 @@ export function WorkbenchTopBar({
open={openMenu === "alerts"} open={openMenu === "alerts"}
onOpenChange={createMenuOpenChange("alerts")} onOpenChange={createMenuOpenChange("alerts")}
alerts={alerts} alerts={alerts}
sourceLabel={alertDataSource === "mock" ? "工况演示回退" : "后端运行态"} sourceLabel={getAlertSourceLabel(alertDataSource)}
conditionFeedVisible={conditionFeedVisible} conditionFeedVisible={conditionFeedVisible}
onSelectAlert={onSelectAlert} onSelectAlert={onSelectAlert}
onToggleConditionFeed={onToggleConditionFeed} onToggleConditionFeed={onToggleConditionFeed}
@@ -239,7 +239,7 @@ export function WorkbenchTopBar({
open={openMenu === "compact-alerts"} open={openMenu === "compact-alerts"}
onOpenChange={createMenuOpenChange("compact-alerts")} onOpenChange={createMenuOpenChange("compact-alerts")}
alerts={alerts} alerts={alerts}
sourceLabel={alertDataSource === "mock" ? "工况演示回退" : "后端运行态"} sourceLabel={getAlertSourceLabel(alertDataSource)}
conditionFeedVisible={conditionFeedVisible} conditionFeedVisible={conditionFeedVisible}
onSelectAlert={onSelectAlert} onSelectAlert={onSelectAlert}
onToggleConditionFeed={onToggleConditionFeed} onToggleConditionFeed={onToggleConditionFeed}
@@ -261,6 +261,12 @@ export function WorkbenchTopBar({
); );
} }
function getAlertSourceLabel(source: WorkbenchAlertDataSource) {
if (source === "backend") return "后端运行态";
if (source === "mock") return "工况演示回退";
return "未配置告警源";
}
function getAnalysisOriginLabel(origin: AnalysisArtifact["origin"]) { function getAnalysisOriginLabel(origin: AnalysisArtifact["origin"]) {
if (origin === "backend") return "后端分析运行"; if (origin === "backend") return "后端分析运行";
if (origin === "demo") return "演示成果"; if (origin === "demo") return "演示成果";
@@ -66,7 +66,7 @@ describe("workbench runtime data", () => {
expect(getMsUntilNextHeaderDataTimeTick(new Date(2026, 6, 22, 15, 43, 30))).toBe(90_000); expect(getMsUntilNextHeaderDataTimeTick(new Date(2026, 6, 22, 15, 43, 30))).toBe(90_000);
}); });
it("keeps mock conditions and shows no work orders when backend storage is not configured", () => { it("keeps mock conditions without inventing alerts when backend storage is not configured", () => {
const resolved = resolveWorkbenchRuntimeData([mockCondition], { const resolved = resolveWorkbenchRuntimeData([mockCondition], {
availability: "not_configured", availability: "not_configured",
source: "none", source: "none",
@@ -86,7 +86,8 @@ describe("workbench runtime data", () => {
expect(resolved.runtimeDataSource).toBe("mock"); expect(resolved.runtimeDataSource).toBe("mock");
expect(resolved.scheduledConditions).toEqual([mockCondition]); expect(resolved.scheduledConditions).toEqual([mockCondition]);
expect(resolved.workbenchAlerts).toHaveLength(1); expect(resolved.workbenchAlerts).toEqual([]);
expect(resolved.alertDataSource).toBe("none");
expect(resolved.scenarioDataSource).toBe("mock"); expect(resolved.scenarioDataSource).toBe("mock");
expect(resolved.artifactDataSource).toBe("mock"); expect(resolved.artifactDataSource).toBe("mock");
expect(resolved.workOrderDataSource).toBe("none"); expect(resolved.workOrderDataSource).toBe("none");
@@ -132,9 +133,8 @@ describe("workbench runtime data", () => {
expect(resolved.runtimeDataSource).toBe("mock"); expect(resolved.runtimeDataSource).toBe("mock");
expect(resolved.scheduledConditions).toEqual([mockCondition]); expect(resolved.scheduledConditions).toEqual([mockCondition]);
expect(resolved.workbenchAlerts).not.toEqual([backendAlert]); expect(resolved.workbenchAlerts).toEqual([]);
expect(resolved.workbenchAlerts[0]?.id).toBe("scheduled-condition-alert-mock-condition"); expect(resolved.alertDataSource).toBe("none");
expect(resolved.alertDataSource).toBe("mock");
expect(resolved.scenarioDataSource).toBe("mock"); expect(resolved.scenarioDataSource).toBe("mock");
expect(resolved.workOrderDataSource).toBe("none"); expect(resolved.workOrderDataSource).toBe("none");
@@ -11,7 +11,6 @@ import {
} from "../data/scheduled-conditions"; } from "../data/scheduled-conditions";
import { WORKBENCH_SCENARIOS } from "../data/workbench-session"; import { WORKBENCH_SCENARIOS } from "../data/workbench-session";
import type { ScheduledConditionItem } from "../types"; import type { ScheduledConditionItem } from "../types";
import { createScheduledConditionAlerts } from "../utils/scheduled-condition-alerts";
import type { WorkbenchSyncState } from "./use-workbench-sync"; import type { WorkbenchSyncState } from "./use-workbench-sync";
const HEADER_DATA_TIME_STEP_MINUTES = 5; const HEADER_DATA_TIME_STEP_MINUTES = 5;
@@ -106,8 +105,8 @@ export function resolveWorkbenchRuntimeData(
scheduledConditions: [...fallbackConditions, ...resolvedWorkOrders], scheduledConditions: [...fallbackConditions, ...resolvedWorkOrders],
workbenchAlerts: backendAlertsConfigured workbenchAlerts: backendAlertsConfigured
? backendSnapshot!.alerts ? backendSnapshot!.alerts
: createScheduledConditionAlerts(fallbackConditions), : [],
alertDataSource: backendAlertsConfigured ? "backend" as const : "mock" as const, alertDataSource: backendAlertsConfigured ? "backend" as const : "none" as const,
runtimeDataSource: "mock" as const, runtimeDataSource: "mock" as const,
scenarios: useBackendScenarios ? backendSnapshot!.scenarios : WORKBENCH_SCENARIOS, scenarios: useBackendScenarios ? backendSnapshot!.scenarios : WORKBENCH_SCENARIOS,
scenarioDataSource: useBackendScenarios ? "analysis_runs" as const : "mock" as const, scenarioDataSource: useBackendScenarios ? "analysis_runs" as const : "mock" as const,
+1 -1
View File
@@ -44,7 +44,7 @@ export type WorkbenchRuntimeDataSource =
| "scheduled_tasks" | "scheduled_tasks"
| "operational_tasks"; | "operational_tasks";
export type WorkbenchAlertDataSource = "mock" | "backend"; export type WorkbenchAlertDataSource = "mock" | "backend" | "none";
export type WorkbenchScenarioDataSource = "mock" | "analysis_runs"; export type WorkbenchScenarioDataSource = "mock" | "analysis_runs";
@@ -264,6 +264,14 @@ test("审批动作展示中文参数和证据,viewer 不可审批,冲突后
}; };
await page.goto("/"); await page.goto("/");
const alertTrigger = page.getByRole("button", {
name: "查看异常处置面板,当前 0 条待复核工况"
}).first();
await expect(alertTrigger).toBeVisible();
await alertTrigger.click();
await expect(page.getByText("当前无待复核工况", { exact: true }).first()).toBeVisible();
await expect(page.getByText("来源:未配置告警源", { exact: true })).toBeVisible();
await page.keyboard.press("Escape");
const reconnectNotice = page.locator("[data-sonner-toast]").filter({ const reconnectNotice = page.locator("[data-sonner-toast]").filter({
hasText: "业务同步正在重连" hasText: "业务同步正在重连"
}); });