Files
sam-react-prod/src/stores/useCalendarScheduleStore.ts
유병철 6604695674 feat(WEB): CEO 대시보드 섹션 분리 및 캘린더/거래처 관리 기능 추가
- CEO 대시보드 섹션별 컴포넌트 분리 (건설/생산/매출/매입/미출하/출근)
- LazySection 지연 로딩 패턴 적용
- DashboardSettingsDialog 섹션 표시/순서 설정 확장
- 캘린더 관리 페이지 신규 추가 (settings/calendar-management)
- useCalendarScheduleStore Zustand 스토어 추가
- CalendarHeader 일정 추가/관리 기능 강화
- 거래처 관리 상세 화면 개선 (VendorDetail/VendorDetailClient)
- 카드 관리 상세 화면 리팩토링
- FormField 기능 확장

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:38:20 +09:00

123 lines
4.2 KiB
TypeScript

/**
* 달력 일정 Zustand Store
*
* 달력관리(CalendarManagement)에서 등록한 공휴일/세무일정/회사일정을
* 프로젝트 전체 ScheduleCalendar, DatePicker 등에 전파하는 공유 스토어.
*
* 데이터 흐름:
* 1. ScheduleCalendar 마운트 시 해당 연도 fetchSchedules(year) 호출
* 2. API 응답 → schedulesByYear에 캐시
* 3. calendarEvents.ts 유틸 함수(isHoliday 등)가 스토어에서 읽음
* 4. API 미응답 시 하드코딩된 calendarEvents.ts 상수를 폴백으로 사용
*/
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import type { CalendarSchedule } from '@/components/hr/CalendarManagement/types';
import { getCalendarSchedules } from '@/components/hr/CalendarManagement/actions';
interface CalendarScheduleStore {
/** 연도별 캐시된 일정 */
schedulesByYear: Record<number, CalendarSchedule[]>;
/** 연도별 로딩 완료 여부 */
loadedYears: Record<number, boolean>;
/** 연도별 로딩 중 여부 */
loadingYears: Record<number, boolean>;
/** 연도별 일정 조회 (캐시 히트 시 API 호출 안 함) */
fetchSchedules: (year: number) => Promise<CalendarSchedule[]>;
/** 이미 가져온 데이터를 스토어에 직접 설정 (추가 API 호출 없음) */
setSchedulesForYear: (year: number, schedules: CalendarSchedule[]) => void;
/** 특정 연도 캐시 무효화 (달력관리에서 등록/수정/삭제 후 호출) */
invalidateYear: (year: number) => void;
}
export const useCalendarScheduleStore = create<CalendarScheduleStore>()(
devtools(
(set, get) => ({
schedulesByYear: {},
loadedYears: {},
loadingYears: {},
fetchSchedules: async (year: number) => {
const { loadedYears, loadingYears, schedulesByYear } = get();
// 이미 로드됨
if (loadedYears[year]) {
return schedulesByYear[year] || [];
}
// 이미 로딩 중 (중복 호출 방지)
if (loadingYears[year]) {
return schedulesByYear[year] || [];
}
set(
(state) => ({
loadingYears: { ...state.loadingYears, [year]: true },
}),
false,
'fetchSchedules/start'
);
try {
const result = await getCalendarSchedules(year);
const schedules = result.success && result.data ? result.data : [];
set(
(state) => ({
schedulesByYear: { ...state.schedulesByYear, [year]: schedules },
loadedYears: { ...state.loadedYears, [year]: true },
loadingYears: { ...state.loadingYears, [year]: false },
}),
false,
'fetchSchedules/success'
);
return schedules;
} catch {
// API 실패 시에도 loaded 처리 → 같은 연도 반복 호출 방지
// (폴백 상수 데이터 사용, 달력관리에서 등록 시 invalidateYear로 재시도)
set(
(state) => ({
schedulesByYear: { ...state.schedulesByYear, [year]: [] },
loadedYears: { ...state.loadedYears, [year]: true },
loadingYears: { ...state.loadingYears, [year]: false },
}),
false,
'fetchSchedules/error'
);
return [];
}
},
setSchedulesForYear: (year: number, schedules: CalendarSchedule[]) => {
set(
(state) => ({
schedulesByYear: { ...state.schedulesByYear, [year]: schedules },
loadedYears: { ...state.loadedYears, [year]: true },
loadingYears: { ...state.loadingYears, [year]: false },
}),
false,
'setSchedulesForYear'
);
},
invalidateYear: (year: number) => {
set(
(state) => {
const newSchedules = { ...state.schedulesByYear };
const newLoaded = { ...state.loadedYears };
delete newSchedules[year];
delete newLoaded[year];
return { schedulesByYear: newSchedules, loadedYears: newLoaded };
},
false,
'invalidateYear'
);
},
}),
{ name: 'CalendarScheduleStore' }
)
);