/** * CEO 대시보드 targeted refetch 시스템 * * CUD 발생 시 sessionStorage + CustomEvent로 대시보드 섹션별 갱신 트리거 * * 도메인→섹션 매핑은 동적 레지스트리 패턴: * - 공통 ERP 도메인은 여기서 직접 등록 * - 테넌트 전용 도메인은 각 모듈에서 registerDashboardDomain()으로 자기 등록 */ // 대시보드 섹션 키 (useCEODashboard의 refetchMap과 1:1 매핑) export type DashboardSectionKey = | 'dailyReport' | 'receivable' | 'debtCollection' | 'monthlyExpense' | 'cardManagement' | 'statusBoard' | 'salesStatus' | 'purchaseStatus' | 'dailyProduction' | 'unshipped' | 'construction' | 'dailyAttendance' | 'entertainment' | 'welfare'; // 동적 도메인→섹션 레지스트리 const domainSectionRegistry = new Map(); /** * 도메인→섹션 매핑 등록 (각 모듈에서 호출) * * @example * // 생산 모듈 초기화 시 * registerDashboardDomain('production', ['statusBoard', 'dailyProduction']); * * // 건설 모듈 초기화 시 * registerDashboardDomain('construction', ['statusBoard', 'construction']); */ export function registerDashboardDomain(domain: string, sections: DashboardSectionKey[]): void { domainSectionRegistry.set(domain, sections); } // ===== 공통 ERP 도메인 (테넌트 무관, 항상 등록) ===== registerDashboardDomain('deposit', ['dailyReport', 'receivable']); registerDashboardDomain('withdrawal', ['dailyReport', 'monthlyExpense']); registerDashboardDomain('sales', ['dailyReport', 'salesStatus', 'receivable']); registerDashboardDomain('purchase', ['dailyReport', 'purchaseStatus', 'monthlyExpense']); registerDashboardDomain('badDebt', ['debtCollection', 'receivable']); registerDashboardDomain('expectedExpense', ['monthlyExpense']); registerDashboardDomain('bill', ['dailyReport', 'receivable']); registerDashboardDomain('giftCertificate', ['entertainment', 'cardManagement']); registerDashboardDomain('journalEntry', ['entertainment', 'welfare', 'monthlyExpense']); registerDashboardDomain('order', ['statusBoard', 'salesStatus']); registerDashboardDomain('stock', ['statusBoard']); registerDashboardDomain('schedule', ['statusBoard']); registerDashboardDomain('client', ['statusBoard']); registerDashboardDomain('leave', ['statusBoard', 'dailyAttendance']); registerDashboardDomain('approval', ['statusBoard']); registerDashboardDomain('attendance', ['statusBoard', 'dailyAttendance']); registerDashboardDomain('shipment', ['statusBoard', 'unshipped']); // ===== 테넌트 전용 도메인 (각 모듈에서 등록 — 현재는 하위 호환을 위해 여기서도 등록) ===== // TODO: Phase 1 완료 후 각 모듈의 초기화 코드로 이동 registerDashboardDomain('production', ['statusBoard', 'dailyProduction']); registerDashboardDomain('construction', ['statusBoard', 'construction']); const STORAGE_KEY = 'dashboard:stale-sections'; const EVENT_NAME = 'dashboard:invalidate'; /** * CUD 성공 후 호출 — 해당 도메인이 영향 주는 대시보드 섹션을 stale 처리 */ export function invalidateDashboard(domain: string): void { const sections = domainSectionRegistry.get(domain); if (!sections || sections.length === 0) return; // 1. sessionStorage에 stale 섹션 저장 (navigation 사이 유지) try { const existing = sessionStorage.getItem(STORAGE_KEY); const current: string[] = existing ? JSON.parse(existing) : []; const merged = Array.from(new Set([...current, ...sections])); sessionStorage.setItem(STORAGE_KEY, JSON.stringify(merged)); } catch { // sessionStorage 접근 불가 시 무시 } // 2. CustomEvent 발행 (대시보드가 마운트 중이면 즉시 처리) if (typeof window !== 'undefined') { window.dispatchEvent( new CustomEvent(EVENT_NAME, { detail: { sections } }), ); } } /** * 대시보드 마운트 시 호출 — stale 섹션 읽고 클리어 */ export function consumeStaleSections(): DashboardSectionKey[] { try { const raw = sessionStorage.getItem(STORAGE_KEY); if (!raw) return []; sessionStorage.removeItem(STORAGE_KEY); return JSON.parse(raw) as DashboardSectionKey[]; } catch { return []; } } /** CustomEvent 이름 (리스너 등록용) */ export const DASHBOARD_INVALIDATE_EVENT = EVENT_NAME;