189 lines
5.2 KiB
TypeScript
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;
|
|
}
|