Files
next-tjwater-frontend/src/features/workbench/workspace/workspace-model.ts
T
jiang 7a304a91a8
Generic Container CI/CD / test-build-publish (push) Successful in 1m13s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m14s
feat(workbench): dock task and chart workspaces
2026-11-12 11:42:25 +08:00

189 lines
5.2 KiB
TypeScript

import type { FeatureCollection, Geometry } from "geojson";
export const ARTIFACT_PANE_MIN_WIDTH = 600;
export const ARTIFACT_MAP_SPLIT_MIN_WIDTH = ARTIFACT_PANE_MIN_WIDTH * 2 + 1;
export const CONDITION_PANE_MIN_WIDTH = 880;
export const CONDITION_MAP_SPLIT_MIN_WIDTH = ARTIFACT_PANE_MIN_WIDTH + CONDITION_PANE_MIN_WIDTH + 1;
export type ArtifactType = "diagnosis" | "simulation" | "metric_analysis";
export type ArtifactLifecycle = "draft" | "reviewed" | "published" | "archived";
export type ArtifactViewMode = "map_split" | "focus";
export type ArtifactRequestedView = ArtifactViewMode | "map_only";
export type ArtifactMapRelation = "required" | "optional" | "none";
export type WorkbenchSurfaceMode = "default" | ArtifactRequestedView;
export type AnalysisMetric = {
label: string;
value: string;
detail: string;
tone: "neutral" | "info" | "success" | "warning" | "danger";
};
export type AnalysisChartSeries = {
name: string;
values: number[];
color: string;
type?: "line" | "bar";
};
export type AnalysisBlock =
| {
id: string;
kind: "metric-grid";
title: string;
span: 12;
metrics: AnalysisMetric[];
}
| {
id: string;
kind: "chart";
title: string;
description: string;
span: 5 | 7 | 12;
chartType: "line" | "bar";
categories: string[];
unit: string;
series: AnalysisChartSeries[];
}
| {
id: string;
kind: "process-flow";
title: string;
description: string;
span: 5 | 7 | 12;
nodes: Array<{
id: string;
label: string;
detail: string;
status: "complete" | "active" | "pending";
}>;
}
| {
id: string;
kind: "data-table";
title: string;
description: string;
span: 5 | 7 | 12;
columns: Array<{ key: string; label: string; numeric?: boolean }>;
rows: Array<Record<string, string | number>>;
}
| {
id: string;
kind: "narrative";
title: string;
span: 5 | 7 | 12;
paragraphs: string[];
};
export type AnalysisArtifactAction = {
id: string;
label: string;
description: string;
tone: "primary" | "neutral" | "warning";
};
export type AnalysisArtifact = {
id: string;
origin?: "agent" | "backend" | "demo";
type: ArtifactType;
lifecycle: ArtifactLifecycle;
revision: number;
title: string;
subtitle: string;
summary: string;
scope: string;
confidence: string;
generatedAt: string;
analyticalTimeRange: { start: string; end: string };
preferredView: ArtifactViewMode;
mapRelation: ArtifactMapRelation;
spatialOverlay?: FeatureCollection<Geometry>;
blocks: AnalysisBlock[];
sources: string[];
limitations: string[];
actions: AnalysisArtifactAction[];
};
export type ArtifactWorkspaceState = {
pendingArtifact: AnalysisArtifact | null;
activeArtifact: AnalysisArtifact | null;
requestedView: ArtifactRequestedView;
};
export function createInitialArtifactWorkspaceState(): ArtifactWorkspaceState {
return {
pendingArtifact: null,
activeArtifact: null,
requestedView: "map_split"
};
}
export function previewAnalysisArtifact(
state: ArtifactWorkspaceState,
artifact: AnalysisArtifact
): ArtifactWorkspaceState {
return { ...state, pendingArtifact: artifact };
}
export function openPendingArtifact(state: ArtifactWorkspaceState): ArtifactWorkspaceState {
if (!state.pendingArtifact) return state;
return {
pendingArtifact: null,
activeArtifact: state.pendingArtifact,
requestedView: getPreferredArtifactView(state.pendingArtifact)
};
}
export function openAnalysisArtifact(
_state: ArtifactWorkspaceState,
artifact: AnalysisArtifact
): ArtifactWorkspaceState {
return {
pendingArtifact: null,
activeArtifact: artifact,
requestedView: getPreferredArtifactView(artifact)
};
}
export function collapseActiveArtifact(state: ArtifactWorkspaceState): ArtifactWorkspaceState {
if (!state.activeArtifact) return state;
return {
...state,
pendingArtifact: state.activeArtifact,
activeArtifact: null
};
}
export function destroyPendingArtifact(state: ArtifactWorkspaceState): ArtifactWorkspaceState {
return state.pendingArtifact ? { ...state, pendingArtifact: null } : state;
}
export function destroyActiveArtifact(state: ArtifactWorkspaceState): ArtifactWorkspaceState {
return state.activeArtifact
? { ...state, activeArtifact: null, requestedView: "map_split" }
: state;
}
export function requestArtifactView(
state: ArtifactWorkspaceState,
view: ArtifactRequestedView
): ArtifactWorkspaceState {
if (!state.activeArtifact) return state;
return { ...state, requestedView: view };
}
export function resolveWorkbenchSurfaceMode(
state: ArtifactWorkspaceState,
businessWorkspaceWidth: number
): WorkbenchSurfaceMode {
const artifact = state.activeArtifact;
if (!artifact) return "default";
if (state.requestedView === "map_only") return "map_only";
if (state.requestedView === "focus") return "focus";
return businessWorkspaceWidth >= ARTIFACT_MAP_SPLIT_MIN_WIDTH ? "map_split" : "focus";
}
function getPreferredArtifactView(artifact: AnalysisArtifact): ArtifactViewMode {
return artifact.mapRelation === "none" ? "focus" : artifact.preferredView;
}