Files
invyone/frontend/stores/dashboardStore.ts
T
gbpark e70267f738
Build & Deploy to K8s / build-and-deploy (push) Failing after 1m14s
feat: SCADA 데모 음성 인식 + 경고 버튼 디자인 통일
- 음성 인식 (scada-demo/js/voice.js) — 한국어 발화 → 키워드 매핑 → INVYONE_UI.select()
  · 사이드바 마이크 버튼 + transcript 라벨, 매칭 시 청록 펄스
  · Chrome/Edge HTTPS 환경 (운영 siflex.invyone.com OK)
- 경고시스템/다중경고 버튼을 음성 인식과 동일 톤
  · 🚨 emoji → SVG 삼각형 아이콘, voice-btn 패턴 (다크 솔리드 + 컬러 액센트)
  · 정적 (반짝 펄스 애니메이션 제거)
- client.ts stash pop conflict 정리 (DEV_TENANT_HOST + 도메인 정리 통합)
- ui.js 다중 경고 시연 wiring + scada 작업 노트 2건
- 기타 syncthing 보류분 batch (대시보드/레이아웃/로그인 layout 정리)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:39:43 +09:00

119 lines
3.6 KiB
TypeScript

import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
interface DashEditTarget {
id: string;
name: string;
icon: string;
is_personal: boolean;
}
interface DashboardState {
dashboards: Record<string, any>[];
activeDashboardId: string | null;
cards: Record<string, any>[];
editMode: boolean;
loading: boolean;
createOpen: boolean;
/** 수정 모달 대상 — null 이면 모달 닫힘. CreateDashboardModal 을 mode='edit' 로 재사용. */
editTarget: DashEditTarget | null;
setDashboards: (dashboards: Record<string, any>[]) => void;
setActiveDashboard: (id: string | null) => void;
setCards: (cards: Record<string, any>[]) => void;
addCard: (card: Record<string, any>) => void;
updateCard: (cardId: string, updates: Record<string, any>) => void;
removeCard: (cardId: string) => void;
toggleEditMode: () => void;
setEditMode: (on: boolean) => void;
setLoading: (loading: boolean) => void;
addDashboard: (dashboard: Record<string, any>) => void;
updateDashboardInList: (id: string, updates: Record<string, any>) => void;
removeDashboard: (id: string) => void;
openCreate: () => void;
closeCreate: () => void;
openEdit: (target: DashEditTarget) => void;
closeEdit: () => void;
libOpen: boolean;
openLib: () => void;
closeLib: () => void;
}
export const useDashboardStore = create<DashboardState>()(
devtools(
(set) => ({
dashboards: [],
activeDashboardId: null,
cards: [],
editMode: false,
loading: false,
createOpen: false,
editTarget: null,
libOpen: false,
setDashboards: (dashboards) => set({ dashboards }),
setActiveDashboard: (id) =>
set((s) =>
s.activeDashboardId === id
? { activeDashboardId: id, editMode: false }
: { activeDashboardId: id, editMode: false, cards: [] }
),
setCards: (cards) => set({ cards }),
addCard: (card) => set((s) => ({ cards: [...s.cards, card] })),
updateCard: (cardId, updates) =>
set((s) => ({
cards: s.cards.map((c) =>
(c.card_id ?? c.CARD_ID) === cardId ? { ...c, ...updates } : c
),
})),
removeCard: (cardId) =>
set((s) => ({
cards: s.cards.filter((c) => (c.card_id ?? c.CARD_ID) !== cardId),
})),
toggleEditMode: () => set((s) => ({ editMode: !s.editMode })),
setEditMode: (on) => set({ editMode: on }),
setLoading: (loading) => set({ loading }),
addDashboard: (dashboard) =>
set((s) => ({ dashboards: [...s.dashboards, dashboard] })),
updateDashboardInList: (id, updates) =>
set((s) => ({
dashboards: s.dashboards.map((d) =>
(d.dashboard_id ?? d.DASHBOARD_ID) === id ? { ...d, ...updates } : d
),
})),
removeDashboard: (id) =>
set((s) => {
const filtered = s.dashboards.filter(
(d) => (d.dashboard_id ?? d.DASHBOARD_ID) !== id
);
const isActive = s.activeDashboardId === id;
return {
dashboards: filtered,
activeDashboardId: isActive
? (filtered[0]?.dashboard_id ?? filtered[0]?.DASHBOARD_ID ?? null)
: s.activeDashboardId,
};
}),
openCreate: () => set({ createOpen: true, editTarget: null }),
closeCreate: () => set({ createOpen: false }),
openEdit: (target) => set({ editTarget: target, createOpen: false }),
closeEdit: () => set({ editTarget: null }),
openLib: () => set({ libOpen: true }),
closeLib: () => set({ libOpen: false }),
}),
{ name: 'dashboard-store' }
)
);