129 lines
4.9 KiB
TypeScript
129 lines
4.9 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { Eye, Wrench, Save, FolderOpen } from 'lucide-react';
|
|
import { useControlMode } from './hooks/useControlMode';
|
|
import { getBusinessRuleList, getBusinessRuleInfo, insertBusinessRule, updateBusinessRule } from '@/lib/api/businessRule';
|
|
import { toast } from 'sonner';
|
|
|
|
interface ControlToolbarProps {
|
|
dashboardId: string;
|
|
}
|
|
|
|
export function ControlToolbar({ dashboardId }: ControlToolbarProps) {
|
|
const { mode, setMode, ruleNodes, ruleConnections, activeRuleId, setActiveRuleId, setRuleNodes, setRuleConnections } = useControlMode();
|
|
const [ruleList, setRuleList] = useState<Record<string, any>[]>([]);
|
|
const [showRuleList, setShowRuleList] = useState(false);
|
|
|
|
// ★ 편집 모드 진입 시 기존 규칙 목록 로드
|
|
useEffect(() => {
|
|
if (mode !== 'edit') return;
|
|
getBusinessRuleList(dashboardId)
|
|
.then((res) => setRuleList(res?.list ?? res?.data ?? []))
|
|
.catch(() => setRuleList([]));
|
|
}, [mode, dashboardId]);
|
|
|
|
// ★ 기존 규칙 로드 → 편집 상태 복원
|
|
const handleLoadRule = useCallback(async (ruleId: string) => {
|
|
try {
|
|
const detail = await getBusinessRuleInfo(ruleId);
|
|
if (!detail) { toast.error('규칙을 찾을 수 없습니다'); return; }
|
|
setRuleNodes(detail.nodes ?? []);
|
|
setRuleConnections(detail.connections ?? []);
|
|
setActiveRuleId(ruleId);
|
|
setShowRuleList(false);
|
|
toast.success(`"${detail.name ?? ruleId}" 로드됨`);
|
|
} catch {
|
|
toast.error('규칙 로드 실패');
|
|
}
|
|
}, [setRuleNodes, setRuleConnections, setActiveRuleId]);
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
const data = {
|
|
name: `규칙 ${new Date().toLocaleString('ko-KR')}`,
|
|
nodes: ruleNodes,
|
|
connections: ruleConnections,
|
|
};
|
|
if (activeRuleId) {
|
|
await updateBusinessRule(activeRuleId, data);
|
|
toast.success('규칙 저장됨');
|
|
} else {
|
|
const result = await insertBusinessRule(dashboardId, data);
|
|
if (result?.rule_id) setActiveRuleId(result.rule_id);
|
|
toast.success('규칙 생성됨');
|
|
}
|
|
} catch {
|
|
toast.error('저장 실패');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="ctrl-toolbar">
|
|
<span style={{ fontWeight: 700, color: 'var(--ctrl-cyan)', marginRight: '.5rem' }}>⚡ 제어 모드</span>
|
|
<div className="ctrl-toolbar-mode">
|
|
<button
|
|
className={`ctrl-mode-btn${mode === 'view' ? ' on' : ''}`}
|
|
onClick={() => setMode('view')}
|
|
>
|
|
<Eye size={12} style={{ marginRight: 3 }} />
|
|
읽기
|
|
</button>
|
|
<button
|
|
className={`ctrl-mode-btn${mode === 'edit' ? ' on' : ''}`}
|
|
onClick={() => setMode('edit')}
|
|
>
|
|
<Wrench size={12} style={{ marginRight: 3 }} />
|
|
편집
|
|
</button>
|
|
</div>
|
|
{mode === 'edit' && (
|
|
<div style={{ marginLeft: 'auto', display: 'flex', gap: '.3rem', position: 'relative' }}>
|
|
{/* ★ 기존 규칙 로드 버튼 */}
|
|
<button className="ctrl-mode-btn" onClick={() => setShowRuleList(!showRuleList)}>
|
|
<FolderOpen size={12} style={{ marginRight: 3 }} />
|
|
불러오기{ruleList.length > 0 ? ` (${ruleList.length})` : ''}
|
|
</button>
|
|
{/* ★ 규칙 목록 드롭다운 */}
|
|
{showRuleList && ruleList.length > 0 && (
|
|
<div style={{
|
|
position: 'absolute', top: '100%', right: 0, marginTop: 4,
|
|
background: 'var(--ctrl-glass-strong, rgba(17,16,42,.65))',
|
|
border: '1px solid var(--ctrl-glass-border, rgba(var(--v5-primary-rgb),.12))',
|
|
borderRadius: 8, padding: '.3rem', minWidth: 200, zIndex: 100,
|
|
backdropFilter: 'blur(20px) saturate(1.4)',
|
|
}}>
|
|
{ruleList.map((rule) => {
|
|
const id = rule.rule_id ?? rule.RULE_ID;
|
|
const name = rule.name ?? rule.NAME ?? id;
|
|
const isActive = id === activeRuleId;
|
|
return (
|
|
<button
|
|
key={id}
|
|
onClick={() => handleLoadRule(id)}
|
|
style={{
|
|
display: 'block', width: '100%', textAlign: 'left',
|
|
padding: '.3rem .5rem', borderRadius: 6, border: 'none',
|
|
background: isActive ? 'rgba(var(--v5-cyan-rgb),.12)' : 'transparent',
|
|
color: isActive ? 'var(--ctrl-cyan)' : 'var(--v5-text-sec)',
|
|
fontSize: '.55rem', cursor: 'pointer',
|
|
}}
|
|
>
|
|
{name}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
{ruleNodes.length > 0 && (
|
|
<button className="ctrl-mode-btn" onClick={handleSave}>
|
|
<Save size={12} style={{ marginRight: 3 }} />
|
|
규칙 저장
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|