feat(web): NetX workbench, UME-only UI, i18n, and header status

- Replace sidebar app with workbench home and module tabs (openOrFocusModule)

- Remove legacy pages; add module registry, shared tab channel, WEB.md

- Add zh/en i18n, global ToastProvider, restore oclaw bridge pill in header

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-27 11:18:30 +08:00
parent 664775927b
commit 282dbe75af
33 changed files with 1631 additions and 1677 deletions

View file

@ -1,9 +1,4 @@
import type {
AiAnalyzeHistoryResponse,
AlarmQueryResponse,
Batch,
Diagnostics,
ImportHistoryItem,
IntegrationStatus,
UmeAlarmItem,
UmeAlarmSubscriptionStatus,
@ -30,77 +25,8 @@ export const apiPost = async <T,>(path: string, body: unknown): Promise<T> => {
return data as T;
};
export const apiPostForm = async <T,>(path: string, form: FormData): Promise<T> => {
const res = await fetch(path, {
method: "POST",
headers: { accept: "application/json" },
body: form,
});
const text = await res.text();
const data = text ? JSON.parse(text) : {};
if (!res.ok) throw new Error(String((data as { detail?: string }).detail || `${res.status} ${path}`));
return data as T;
};
export const apiDelete = async <T,>(path: string): Promise<T> => {
const res = await fetch(path, {
method: "DELETE",
headers: { accept: "application/json" },
});
const text = await res.text();
const data = text ? JSON.parse(text) : {};
if (!res.ok) throw new Error(String((data as { detail?: string }).detail || `${res.status} ${path}`));
return data as T;
};
export const fetchBatches = () => apiGet<{ items: Batch[] }>("/v1/batches?limit=30");
export const fetchIntegrationStatus = () => apiGet<IntegrationStatus>("/v1/integrations/status");
export const fetchJobs = () =>
apiGet<{ items: Array<{ id: number; kind: string; file_name: string; batch_id?: string | null; ok: boolean; summary: string; created_at: string }> }>(
"/v1/jobs?limit=20",
).then((x) => {
const items: ImportHistoryItem[] = (x.items || []).map((j) => ({
kind: (j.kind as ImportHistoryItem["kind"]) || "alarms",
ts_ms: Date.parse(j.created_at) || Date.now(),
file_name: j.file_name || "",
batch_id: j.batch_id || undefined,
ok: Boolean(j.ok),
summary: j.summary || "",
}));
return { items };
});
export const fetchDiagnostics = (batchId: string) =>
apiGet<Diagnostics>(`/v1/diagnostics?batch_id=${encodeURIComponent(batchId)}`);
export const fetchAiAnalyzeHistory = (params: { batchId?: string; page: number; pageSize: number }) => {
const p = new URLSearchParams();
if (params.batchId) p.set("batch_id", params.batchId);
p.set("page", String(Math.max(1, Number(params.page || 1))));
p.set("page_size", String(Math.max(1, Math.min(100, Number(params.pageSize || 20)))));
return apiGet<AiAnalyzeHistoryResponse>(`/v1/ap/history?${p.toString()}`);
};
export const fetchAlarms = (params: {
batchId: string;
alarmCode: string;
neName: string;
severity: string;
page: number;
pageSize: number;
}) => {
const p = new URLSearchParams();
if (params.batchId) p.set("batch_id", params.batchId);
if (params.alarmCode) p.set("alarm_code", params.alarmCode);
if (params.neName) p.set("ne_name", params.neName);
if (params.severity) p.set("severity", params.severity);
p.set("page", String(Math.max(1, Number(params.page || 1))));
p.set("page_size", String(Math.max(1, Math.min(200, Number(params.pageSize || 80)))));
return apiGet<AlarmQueryResponse>(`/v1/alarms?${p.toString()}`);
};
export const fetchUmeAlarmSubscriptionStatus = () =>
apiGet<UmeAlarmSubscriptionStatus>("/v1/ume/alarm-subscription/status");
@ -123,6 +49,7 @@ export const fetchUmeSyncStatus = (params: { page: number; pageSize: number }) =
p.set("page_size", String(Math.max(1, Math.min(200, Number(params.pageSize || 20)))));
return apiGet<UmeSyncStatusResponse>(`/v1/ume/sync/status?${p.toString()}`);
};
export const fetchUmeTokenStatus = () => apiGet<UmeTokenStatus>("/v1/ume/token/status");
export const refreshUmeToken = () => apiPost<UmeTokenStatus>("/v1/ume/token/refresh", {});
export const disconnectUmeToken = () => apiPost<UmeTokenStatus>("/v1/ume/token/disconnect", {});
@ -132,7 +59,9 @@ export const fetchUmeNe = (params: { keyword: string; page: number; pageSize: nu
if (params.keyword) p.set("keyword", params.keyword);
p.set("page", String(Math.max(1, Number(params.page || 1))));
p.set("page_size", String(Math.max(1, Math.min(500, Number(params.pageSize || 50)))));
return apiGet<{ total: number; page: number; page_size: number; items: UmeNeItem[] }>(`/v1/ume/inventory/ne?${p.toString()}`);
return apiGet<{ total: number; page: number; page_size: number; items: UmeNeItem[] }>(
`/v1/ume/inventory/ne?${p.toString()}`,
);
};
export const fetchUmeCurrentAlarms = (params: {
@ -150,28 +79,7 @@ export const fetchUmeCurrentAlarms = (params: {
if (params.keyword) p.set("keyword", params.keyword);
p.set("page", String(Math.max(1, Number(params.page || 1))));
p.set("page_size", String(Math.max(1, Math.min(500, Number(params.pageSize || 50)))));
return apiGet<{ total: number; page: number; page_size: number; items: UmeAlarmItem[] }>(`/v1/ume/alarms?${p.toString()}`);
};
export const fetchUmeHistoryAlarms = (params: {
severity: string;
neId: string;
keyword: string;
timeFrom: string;
timeTo: string;
page: number;
pageSize: number;
}) => {
const p = new URLSearchParams();
if (params.severity) p.set("severity", params.severity);
if (params.neId) p.set("ne_id", params.neId);
if (params.keyword) p.set("keyword", params.keyword);
if (params.timeFrom) p.set("time_from", params.timeFrom);
if (params.timeTo) p.set("time_to", params.timeTo);
p.set("page", String(Math.max(1, Number(params.page || 1))));
p.set("page_size", String(Math.max(1, Math.min(500, Number(params.pageSize || 50)))));
return apiGet<{ total: number; page: number; page_size: number; items: UmeAlarmItem[] }>(
`/v1/ume/alarms/history?${p.toString()}`,
`/v1/ume/alarms?${p.toString()}`,
);
};