feat(workbench): expand SCADA monitoring and investigation presentation
This commit is contained in:
@@ -116,6 +116,9 @@ import type {
|
||||
} from "./types";
|
||||
import { getScheduledConditionIdFromAlertId } from "./utils/scheduled-condition-alerts";
|
||||
import { getOperationalNotificationSeverity } from "./utils/operational-notification-routing";
|
||||
import { useScadaAlertNotices } from "./hooks/use-scada-alert-notices";
|
||||
import { useDismissedWorkbenchAlerts } from "./hooks/use-dismissed-workbench-alerts";
|
||||
import { ScadaAlertAtmosphere, type ScadaAlertIntensity } from "./components/scada-alert-atmosphere";
|
||||
import { createAlertQueueConversationPrompt } from "./utils/scheduled-condition-prompts";
|
||||
|
||||
const WORKBENCH_LAYOUT_CSS_VARIABLES = getWorkbenchLayoutCssVariables();
|
||||
@@ -158,6 +161,7 @@ function MapWorkbenchContent({
|
||||
const [taskTickerVisible, setTaskTickerVisible] = useState(true);
|
||||
const [analysisResultsPanelOpen, setAnalysisResultsPanelOpen] = useState(false);
|
||||
const [peripheralSamplePanelOpen, setPeripheralSamplePanelOpen] = useState(false);
|
||||
const [scadaAlertIntensity, setScadaAlertIntensity] = useState<ScadaAlertIntensity>("standard");
|
||||
const [selectedConditionId, setSelectedConditionId] = useState<string | null>(null);
|
||||
const [conditionFocusRequest, setConditionFocusRequest] = useState<{
|
||||
conditionId: string;
|
||||
@@ -225,11 +229,12 @@ function MapWorkbenchContent({
|
||||
|| condition.operationalTask?.task_type === "external_work_order"
|
||||
)
|
||||
], [operationalTasks.conditions, runtimeScheduledConditions]);
|
||||
const workbenchAlerts = useMemo<WorkbenchAlert[]>(() => {
|
||||
const allWorkbenchAlerts = useMemo<WorkbenchAlert[]>(() => {
|
||||
const taskNotifications = operationalTasks.notifications.flatMap((notification) => {
|
||||
const severity = getOperationalNotificationSeverity(notification.kind);
|
||||
return severity ? [{
|
||||
id: `operational-task-notification:${notification.notification_id}`,
|
||||
notificationVersion: notification.version,
|
||||
conditionId: operationalTaskConditionId(notification.task_id),
|
||||
title: notification.title,
|
||||
description: notification.description,
|
||||
@@ -240,6 +245,20 @@ function MapWorkbenchContent({
|
||||
});
|
||||
return [...taskNotifications, ...runtimeWorkbenchAlerts];
|
||||
}, [operationalTasks.notifications, runtimeWorkbenchAlerts]);
|
||||
const { alerts: workbenchAlerts, dismissAlert } = useDismissedWorkbenchAlerts(
|
||||
allWorkbenchAlerts, projectPresentation.projectId, projectPresentation.user.name
|
||||
);
|
||||
const handleRemoveAlert = (alert: WorkbenchAlert) => {
|
||||
if (!dismissAlert(alert)) {
|
||||
showMapNotice({ tone: "warning", title: "通知未移除", message: "无法保存移除记录,请确认项目已加载且浏览器允许本地存储后重试。" });
|
||||
}
|
||||
};
|
||||
const { atmosphere: scadaAlertAtmosphere, previewAtmosphere, endAtmospherePreview, dismissAtmosphere } = useScadaAlertNotices(
|
||||
projectPresentation.projectId,
|
||||
workbenchSync.snapshot?.runtime.alertAvailability === "ready" ? runtimeWorkbenchAlerts : null,
|
||||
handleSelectAlert,
|
||||
operationalTasks.loading ? workbenchSync.snapshot?.tasks ?? null : operationalTasks.tasks
|
||||
);
|
||||
const collapseAgentPanel = agent.collapsePanel;
|
||||
const expandAgentPanel = agent.expandPanel;
|
||||
const clearActiveTool = useCallback(() => {
|
||||
@@ -550,6 +569,7 @@ function MapWorkbenchContent({
|
||||
const taskTickerAvailable = runningTickerConditions.length > 0;
|
||||
const shouldShowTaskTicker =
|
||||
!readingWorkspace?.active &&
|
||||
(isLargeScreen || activeToolId !== "geocoding") &&
|
||||
!artifactActive &&
|
||||
mobileSheet === null &&
|
||||
!conditionFeedExpanded &&
|
||||
@@ -853,6 +873,7 @@ function MapWorkbenchContent({
|
||||
return;
|
||||
}
|
||||
|
||||
setPeripheralSamplePanelOpen(false);
|
||||
openConditionWorkspaceForViewport();
|
||||
setConditionFocusRequest((current) => ({
|
||||
conditionId,
|
||||
@@ -1064,6 +1085,7 @@ function MapWorkbenchContent({
|
||||
|
||||
const toolbarPanelProps: ToolbarPanelProps = {
|
||||
geocoding,
|
||||
featureLocation: { commands: mapController, controllerState: mapControllerState, detailFeature, enabled: mapReady },
|
||||
activeToolId,
|
||||
activeMeasureModeId,
|
||||
activeMeasureUnitId,
|
||||
@@ -1257,9 +1279,12 @@ function MapWorkbenchContent({
|
||||
onRejectQuestion: agent.sessionPurpose === "task_execution" ? undefined : agent.rejectQuestion
|
||||
};
|
||||
|
||||
const conditionFeedSuspended = devPanelOpen || peripheralSamplePanelOpen || Boolean(readingWorkspace?.active);
|
||||
const mobileSheetSuspended = Boolean(readingWorkspace?.active) || (mobileSheet === "condition" && peripheralSamplePanelOpen);
|
||||
|
||||
return (
|
||||
<main
|
||||
className="relative h-[100dvh] min-h-[640px] overflow-hidden bg-slate-100 text-slate-900 tabular-nums"
|
||||
className="relative isolate h-[100dvh] min-h-[640px] overflow-hidden bg-slate-100 text-slate-900 tabular-nums"
|
||||
data-basemap-tone={basemapTone}
|
||||
style={
|
||||
{
|
||||
@@ -1269,6 +1294,7 @@ function MapWorkbenchContent({
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<ScadaAlertAtmosphere atmosphere={scadaAlertAtmosphere} onDismiss={dismissAtmosphere} onOpen={handleSelectAlert} intensity={scadaAlertIntensity} />
|
||||
<WorkbenchTopBar
|
||||
dataTime={displayedDataTime}
|
||||
modelName={
|
||||
@@ -1281,7 +1307,7 @@ function MapWorkbenchContent({
|
||||
alertDataSource={alertDataSource}
|
||||
user={projectPresentation.user}
|
||||
projectSelection={projectSelection}
|
||||
conditionFeedVisible={isLargeScreen ? shouldShowConditionFeed : mobileSheet === "condition"}
|
||||
conditionFeedVisible={!conditionFeedSuspended && (isLargeScreen ? shouldShowConditionFeed : mobileSheet === "condition")}
|
||||
timelineVisible={timelineVisible}
|
||||
taskTickerAvailable={taskTickerAvailable}
|
||||
taskTickerVisible={taskTickerVisible}
|
||||
@@ -1290,6 +1316,7 @@ function MapWorkbenchContent({
|
||||
devPanelEnabled={devPanelEnabled}
|
||||
devPanelOpen={devPanelOpen}
|
||||
onSelectAlert={handleSelectAlert}
|
||||
onRemoveAlert={handleRemoveAlert}
|
||||
onToggleConditionFeed={toggleConditionWorkspaceForViewport}
|
||||
onToggleTimeline={toggleTimelineVisibility}
|
||||
onToggleTaskTicker={() => setTaskTickerVisible((current) => !current)}
|
||||
@@ -1336,7 +1363,17 @@ function MapWorkbenchContent({
|
||||
open={peripheralSamplePanelOpen}
|
||||
onClose={() => setPeripheralSamplePanelOpen(false)}
|
||||
getAccessToken={getAccessToken}
|
||||
mapReady={mapReady}
|
||||
mapRef={mapRef}
|
||||
onAgentTrigger={handlePeripheralWorkOrderAgentTrigger}
|
||||
onOpenInvestigation={(taskId) => {
|
||||
setPeripheralSamplePanelOpen(false);
|
||||
openConditionWorkspaceForViewport();
|
||||
const conditionId = operationalTaskConditionId(taskId);
|
||||
setSelectedConditionId(conditionId);
|
||||
setConditionFocusRequest((current) => ({ conditionId, requestId: (current?.requestId ?? 0) + 1 }));
|
||||
operationalTasks.refresh();
|
||||
}}
|
||||
/>
|
||||
|
||||
<WorkbenchAgentPanels
|
||||
@@ -1470,7 +1507,7 @@ function MapWorkbenchContent({
|
||||
</WorkbenchAgentPanels>
|
||||
|
||||
{conditionFeedMounted ? (
|
||||
<div className="absolute bottom-[calc(var(--workbench-timeline-height)+1.25rem)] right-16 top-24 z-50 hidden lg:block 2xl:[--workbench-condition-width:var(--workbench-condition-width-wide)]" style={devPanelOpen || readingWorkspace?.active ? { display: "none" } : undefined}>
|
||||
<div className="pointer-events-none absolute bottom-[calc(var(--workbench-timeline-height)+1.25rem)] right-16 top-24 z-50 hidden lg:block 2xl:[--workbench-condition-width:var(--workbench-condition-width-wide)]" style={conditionFeedSuspended ? { display: "none" } : undefined}>
|
||||
<ScheduledConditionFeed
|
||||
presentation="desktop-floating"
|
||||
conditions={scheduledConditions}
|
||||
@@ -1516,10 +1553,15 @@ function MapWorkbenchContent({
|
||||
flowDataSource={flowDataSource}
|
||||
investigationFollow={investigationFollow}
|
||||
onInvestigationFollowChange={setInvestigationFollow}
|
||||
onPreviewScadaAlert={previewAtmosphere}
|
||||
onEndScadaAlertPreview={endAtmospherePreview}
|
||||
scadaAlertPreviewActive={scadaAlertAtmosphere?.preview === true}
|
||||
scadaAlertIntensity={scadaAlertIntensity}
|
||||
onScadaAlertIntensityChange={setScadaAlertIntensity}
|
||||
investigationTools={{
|
||||
task: selectedInvestigationTask, mapReady,
|
||||
charts: previewPresentation.charts.length, overlay: previewPresentation.overlay?.title ?? null,
|
||||
activity: [...presentation.activity, ...previewPresentation.activity], onSample: previewPresentation.createSample,
|
||||
onSample: previewPresentation.createSample,
|
||||
onPreview: previewPresentation.runPreview, onClear: previewPresentation.clearPreview
|
||||
}}
|
||||
onClose={() => setDevPanelOpen(false)}
|
||||
@@ -1529,11 +1571,16 @@ function MapWorkbenchContent({
|
||||
{mobileSheet === null && !readingWorkspace?.active && artifactWorkspace.surfaceMode !== "focus" ? (
|
||||
<div
|
||||
className="absolute left-3 right-3 z-30 transition-[bottom] duration-200 ease-out motion-reduce:transition-none lg:hidden"
|
||||
style={{ bottom: mobileOverlayLayout.mapControlBottom }}
|
||||
style={{
|
||||
bottom: mobileOverlayLayout.mapControlBottom,
|
||||
"--mobile-tool-available-height": `calc(100dvh - ${mobileOverlayLayout.mapControlBottom}px - 4.5rem)`
|
||||
} as CSSProperties}
|
||||
>
|
||||
<ToolbarPanel
|
||||
{...toolbarPanelProps}
|
||||
panelWidthClassName="w-full max-h-[52dvh] overflow-y-auto"
|
||||
panelWidthClassName={activeToolId === "geocoding"
|
||||
? "w-full max-h-[var(--mobile-tool-available-height)]"
|
||||
: "w-full max-h-[52dvh] overflow-y-auto"}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -1602,8 +1649,8 @@ function MapWorkbenchContent({
|
||||
|
||||
{mobileSheet ? (
|
||||
<MobileWorkbenchSheet
|
||||
className={readingWorkspace?.active ? "hidden" : undefined}
|
||||
suspended={Boolean(readingWorkspace?.active)}
|
||||
className={mobileSheetSuspended ? "hidden" : undefined}
|
||||
suspended={mobileSheetSuspended}
|
||||
label={
|
||||
mobileSheet === "agent"
|
||||
? "Agent 工作台抽屉"
|
||||
|
||||
Reference in New Issue
Block a user