From 4e7aa0c3b931786299ffc15a60547d586f883ebf Mon Sep 17 00:00:00 2001 From: kjs Date: Mon, 2 Feb 2026 13:41:11 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=83=80=EC=9E=84=EB=9D=BC=EC=9D=B8=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 리소스가 없을 경우 스케줄의 resourceId를 기반으로 자동으로 리소스를 생성하는 기능을 추가하였습니다. - 리소스별 스케줄 그룹화를 위해 effectiveResources를 도입하여 코드의 가독성을 향상시켰습니다. - 스케줄 데이터가 없을 경우 사용자에게 적절한 메시지를 표시하도록 수정하였습니다. - useTimelineData 훅에서 불필요한 검색 조건을 제거하여 성능을 개선하였습니다. --- .../TimelineSchedulerComponent.tsx | 46 +++++++++++++------ .../hooks/useTimelineData.ts | 11 ----- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/frontend/lib/registry/components/v2-timeline-scheduler/TimelineSchedulerComponent.tsx b/frontend/lib/registry/components/v2-timeline-scheduler/TimelineSchedulerComponent.tsx index 23301657..47cf7c95 100644 --- a/frontend/lib/registry/components/v2-timeline-scheduler/TimelineSchedulerComponent.tsx +++ b/frontend/lib/registry/components/v2-timeline-scheduler/TimelineSchedulerComponent.tsx @@ -85,11 +85,31 @@ export function TimelineSchedulerComponent({ const cellWidthConfig = config.cellWidth || defaultTimelineSchedulerConfig.cellWidth!; const cellWidth = cellWidthConfig[zoomLevel] || 60; + // 리소스가 없으면 스케줄의 resourceId로 자동 생성 + const effectiveResources = useMemo(() => { + if (resources.length > 0) { + return resources; + } + + // 스케줄에서 고유한 resourceId 추출하여 자동 리소스 생성 + const uniqueResourceIds = new Set(); + schedules.forEach((schedule) => { + if (schedule.resourceId) { + uniqueResourceIds.add(schedule.resourceId); + } + }); + + return Array.from(uniqueResourceIds).map((id) => ({ + id, + name: id, // resourceId를 이름으로 사용 + })); + }, [resources, schedules]); + // 리소스별 스케줄 그룹화 const schedulesByResource = useMemo(() => { const grouped = new Map(); - resources.forEach((resource) => { + effectiveResources.forEach((resource) => { grouped.set(resource.id, []); }); @@ -99,7 +119,7 @@ export function TimelineSchedulerComponent({ list.push(schedule); } else { // 리소스가 없는 스케줄은 첫 번째 리소스에 할당 - const firstResource = resources[0]; + const firstResource = effectiveResources[0]; if (firstResource) { const firstList = grouped.get(firstResource.id); if (firstList) { @@ -110,7 +130,7 @@ export function TimelineSchedulerComponent({ }); return grouped; - }, [schedules, resources]); + }, [schedules, effectiveResources]); // 줌 레벨 변경 const handleZoomIn = useCallback(() => { @@ -132,12 +152,12 @@ export function TimelineSchedulerComponent({ // 스케줄 클릭 핸들러 const handleScheduleClick = useCallback( (schedule: ScheduleItem) => { - const resource = resources.find((r) => r.id === schedule.resourceId); + const resource = effectiveResources.find((r) => r.id === schedule.resourceId); if (resource && onScheduleClick) { onScheduleClick({ schedule, resource }); } }, - [resources, onScheduleClick] + [effectiveResources, onScheduleClick] ); // 빈 셀 클릭 핸들러 @@ -195,13 +215,13 @@ export function TimelineSchedulerComponent({ // 추가 버튼 클릭 const handleAddClick = useCallback(() => { - if (onAddSchedule && resources.length > 0) { + if (onAddSchedule && effectiveResources.length > 0) { onAddSchedule( - resources[0].id, + effectiveResources[0].id, new Date().toISOString().split("T")[0] ); } - }, [onAddSchedule, resources]); + }, [onAddSchedule, effectiveResources]); // 디자인 모드 플레이스홀더 if (isDesignMode) { @@ -250,8 +270,8 @@ export function TimelineSchedulerComponent({ ); } - // 리소스 없음 - if (resources.length === 0) { + // 리소스 없음 (스케줄도 없는 경우에만 표시) + if (effectiveResources.length === 0) { return (
-

리소스가 없습니다

-

리소스 테이블을 설정하세요

+

스케줄 데이터가 없습니다

+

스케줄 테이블에 데이터를 추가하세요

); @@ -385,7 +405,7 @@ export function TimelineSchedulerComponent({ {/* 리소스 행들 */}
- {resources.map((resource) => ( + {effectiveResources.map((resource) => (