Files
invyone/frontend/components/control/hooks/useControlMode.ts
T
DDD1542 2f398ae0b3 chore: 제어모드 IDE 작업 + v2/legacy 레지스트리 컴포넌트 폐기
- 제어모드 IDE: ControlCardPanel, control/ide/* (Canvas/LeftRail/RightRail/PanZoomStage/V3RuleNode 등), schemas, lib/api/control
- 레지스트리 정리: aggregation-widget, status-count, section-card/paper, table-list(legacy/v2), tabs-widget 폐기 → table/_shared/ 로 통합
- InvLegacyButtonConfigPanel cp 마이그레이션
- canonical data view cleanup 후속 노트
2026-05-19 21:31:03 +09:00

187 lines
7.2 KiB
TypeScript

import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
/**
* 제어 노드 16종 정의 (mockup CTRL_NODE_TYPES)
* out: 커스텀 출력 포트. 없으면 기본 [{port:'out', label:'→'}]
*/
export const CTRL_NODE_TYPES: Record<string, {
icon: string; label: string; rgb: string; cat: string;
out?: { port: string; label: string; cls: string }[];
}> = {
'timer': { icon: '⏱', label: '타이머', rgb: '0,206,201', cat: '트리거' },
'condition': { icon: '◇', label: '조건분기', rgb: '253,203,110', cat: '조건',
out: [{ port: 'yes', label: 'Y', cls: 'port-yes' }, { port: 'no', label: 'N', cls: 'port-no' }] },
'validation': { icon: '✔', label: '데이터 검증', rgb: '255,107,129', cat: '조건',
out: [{ port: 'pass', label: '✓', cls: 'port-yes' }, { port: 'fail', label: '✗', cls: 'port-no' }] },
'status-change': { icon: '🔄', label: '상태 변경', rgb: '108,92,231', cat: '액션' },
'auto-insert': { icon: '📝', label: '자동 등록', rgb: '85,239,196', cat: '액션' },
'calculation': { icon: '🧮', label: '계산/수식', rgb: '45,152,218', cat: '액션' },
'delete': { icon: '🗑', label: '삭제/보관', rgb: '255,71,87', cat: '액션' },
'document': { icon: '📄', label: '문서 생성', rgb: '162,155,254', cat: '액션' },
'approval': { icon: '✋', label: '승인/결재', rgb: '255,165,2', cat: '흐름',
out: [{ port: 'approved', label: '✓', cls: 'port-yes' }, { port: 'rejected', label: '✗', cls: 'port-no' }] },
'delay': { icon: '⏳', label: '대기/지연', rgb: '72,219,251', cat: '흐름' },
'loop': { icon: '🔁', label: '반복', rgb: '223,142,254', cat: '흐름',
out: [{ port: 'each', label: '→', cls: '' }, { port: 'done', label: '✓', cls: 'port-yes' }] },
'parallel': { icon: '🔀', label: '병렬 실행', rgb: '0,206,201', cat: '흐름' },
'merge': { icon: '⤵', label: '병합/합류', rgb: '149,175,192', cat: '흐름' },
'webhook': { icon: '🌐', label: '외부 호출', rgb: '116,185,255', cat: '연동' },
'notification': { icon: '📨', label: '알림 발송', rgb: '253,121,168', cat: '연동' },
'log': { icon: '📜', label: '로그 기록', rgb: '150,150,160', cat: '기록' },
};
interface ControlModeState {
/** 제어 모드 활성 여부 */
active: boolean;
/** 읽기 / 편집 / 실행 / 이력 모드 (선택된 카드 컨텍스트 안의 토글, v3 — IDE 4-segmented tabs) */
mode: 'view' | 'edit' | 'run' | 'history';
/** 선택된 카드 ID — 카드 클릭 시 좌측 축소 + 그 옆에 제어 패널 */
selectedCardId: string | null;
/** 활성 흐름 — FlowViewer 내부 상태 (selectedCardId 와 동기화) */
activeFlowCardId: string | null;
/** 흐름 엣지 배열 (BFS 결과) */
flowEdges: Record<string, any>[];
/** 테이블 노드 위치 */
tablePositions: Record<string, { x: number; y: number }>;
/** 규칙 빌더 — 노드 */
ruleNodes: Record<string, any>[];
/** 규칙 빌더 — 연결 */
ruleConnections: Record<string, any>[];
/** 현재 편집 중인 룰 ID */
activeRuleId: string | null;
/** 설정 팝오버 대상 노드 ID */
configNodeId: string | null;
// 액션
toggleControlMode: () => void;
setMode: (mode: 'view' | 'edit' | 'run' | 'history') => void;
setSelectedCardId: (cardId: string | null) => void;
setActiveFlowCard: (cardId: string | null) => void;
setFlowEdges: (edges: Record<string, any>[]) => void;
setTablePositions: (pos: Record<string, { x: number; y: number }>) => void;
setRuleNodes: (nodes: Record<string, any>[]) => void;
addRuleNode: (node: Record<string, any>) => void;
updateRuleNode: (nodeId: string, updates: Record<string, any>) => void;
removeRuleNode: (nodeId: string) => void;
moveRuleNode: (nodeId: string, x: number, y: number) => void;
setRuleConnections: (conns: Record<string, any>[]) => void;
addRuleConnection: (conn: Record<string, any>) => void;
removeRuleConnection: (connId: string) => void;
setActiveRuleId: (ruleId: string | null) => void;
setConfigNodeId: (nodeId: string | null) => void;
resetControlMode: () => void;
}
let _nodeSeq = 0;
export function genNodeId(prefix: string) { return `${prefix}-${++_nodeSeq}`; }
let _connSeq = 0;
export function genConnId() { return `conn-${++_connSeq}`; }
export const useControlMode = create<ControlModeState>()(
devtools(
(set) => ({
active: false,
mode: 'view',
selectedCardId: null,
activeFlowCardId: null,
flowEdges: [],
tablePositions: {},
ruleNodes: [],
ruleConnections: [],
activeRuleId: null,
configNodeId: null,
toggleControlMode: () =>
set((s) => ({
active: !s.active,
mode: 'view',
selectedCardId: null,
activeFlowCardId: null,
flowEdges: [],
tablePositions: {},
ruleNodes: [],
ruleConnections: [],
configNodeId: null,
})),
setMode: (mode) => set({ mode, configNodeId: null }),
setSelectedCardId: (cardId) =>
set({
selectedCardId: cardId,
// 카드 바꾸면 모드/룰 초기화 (각 카드는 자기 제어 컨텍스트)
mode: 'view',
activeFlowCardId: cardId,
ruleNodes: [],
ruleConnections: [],
activeRuleId: null,
configNodeId: null,
}),
setActiveFlowCard: (cardId) => set({ activeFlowCardId: cardId }),
setFlowEdges: (edges) => set({ flowEdges: edges }),
setTablePositions: (pos) => set({ tablePositions: pos }),
setRuleNodes: (nodes) => set({ ruleNodes: nodes }),
addRuleNode: (node) => set((s) => ({ ruleNodes: [...s.ruleNodes, node] })),
updateRuleNode: (nodeId, updates) =>
set((s) => ({
ruleNodes: s.ruleNodes.map((n) =>
n.id === nodeId ? { ...n, ...updates } : n
),
})),
removeRuleNode: (nodeId) =>
set((s) => ({
ruleNodes: s.ruleNodes.filter((n) => n.id !== nodeId),
ruleConnections: s.ruleConnections.filter(
(c) => c.from_node_id !== nodeId && c.to_node_id !== nodeId
),
})),
moveRuleNode: (nodeId, x, y) =>
set((s) => ({
ruleNodes: s.ruleNodes.map((n) =>
n.id === nodeId ? { ...n, x, y } : n
),
})),
setRuleConnections: (conns) => set({ ruleConnections: conns }),
addRuleConnection: (conn) =>
set((s) => ({ ruleConnections: [...s.ruleConnections, conn] })),
removeRuleConnection: (connId) =>
set((s) => ({
ruleConnections: s.ruleConnections.filter((c) => c.id !== connId),
})),
setActiveRuleId: (ruleId) => set({ activeRuleId: ruleId }),
setConfigNodeId: (nodeId) => set({ configNodeId: nodeId }),
resetControlMode: () =>
set({
active: false,
mode: 'view',
selectedCardId: null,
activeFlowCardId: null,
flowEdges: [],
tablePositions: {},
ruleNodes: [],
ruleConnections: [],
activeRuleId: null,
configNodeId: null,
}),
}),
{ name: 'control-mode-store' }
)
);