feat: 타임라인 스케줄러 컴포넌트 리소스 처리 개선
- 리소스가 없을 경우 스케줄의 resourceId를 기반으로 자동으로 리소스를 생성하는 기능을 추가하였습니다. - 리소스별 스케줄 그룹화를 위해 effectiveResources를 도입하여 코드의 가독성을 향상시켰습니다. - 스케줄 데이터가 없을 경우 사용자에게 적절한 메시지를 표시하도록 수정하였습니다. - useTimelineData 훅에서 불필요한 검색 조건을 제거하여 성능을 개선하였습니다.
This commit is contained in:
parent
4447911892
commit
4e7aa0c3b9
|
|
@ -85,11 +85,31 @@ export function TimelineSchedulerComponent({
|
||||||
const cellWidthConfig = config.cellWidth || defaultTimelineSchedulerConfig.cellWidth!;
|
const cellWidthConfig = config.cellWidth || defaultTimelineSchedulerConfig.cellWidth!;
|
||||||
const cellWidth = cellWidthConfig[zoomLevel] || 60;
|
const cellWidth = cellWidthConfig[zoomLevel] || 60;
|
||||||
|
|
||||||
|
// 리소스가 없으면 스케줄의 resourceId로 자동 생성
|
||||||
|
const effectiveResources = useMemo(() => {
|
||||||
|
if (resources.length > 0) {
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 스케줄에서 고유한 resourceId 추출하여 자동 리소스 생성
|
||||||
|
const uniqueResourceIds = new Set<string>();
|
||||||
|
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 schedulesByResource = useMemo(() => {
|
||||||
const grouped = new Map<string, ScheduleItem[]>();
|
const grouped = new Map<string, ScheduleItem[]>();
|
||||||
|
|
||||||
resources.forEach((resource) => {
|
effectiveResources.forEach((resource) => {
|
||||||
grouped.set(resource.id, []);
|
grouped.set(resource.id, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -99,7 +119,7 @@ export function TimelineSchedulerComponent({
|
||||||
list.push(schedule);
|
list.push(schedule);
|
||||||
} else {
|
} else {
|
||||||
// 리소스가 없는 스케줄은 첫 번째 리소스에 할당
|
// 리소스가 없는 스케줄은 첫 번째 리소스에 할당
|
||||||
const firstResource = resources[0];
|
const firstResource = effectiveResources[0];
|
||||||
if (firstResource) {
|
if (firstResource) {
|
||||||
const firstList = grouped.get(firstResource.id);
|
const firstList = grouped.get(firstResource.id);
|
||||||
if (firstList) {
|
if (firstList) {
|
||||||
|
|
@ -110,7 +130,7 @@ export function TimelineSchedulerComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
return grouped;
|
return grouped;
|
||||||
}, [schedules, resources]);
|
}, [schedules, effectiveResources]);
|
||||||
|
|
||||||
// 줌 레벨 변경
|
// 줌 레벨 변경
|
||||||
const handleZoomIn = useCallback(() => {
|
const handleZoomIn = useCallback(() => {
|
||||||
|
|
@ -132,12 +152,12 @@ export function TimelineSchedulerComponent({
|
||||||
// 스케줄 클릭 핸들러
|
// 스케줄 클릭 핸들러
|
||||||
const handleScheduleClick = useCallback(
|
const handleScheduleClick = useCallback(
|
||||||
(schedule: ScheduleItem) => {
|
(schedule: ScheduleItem) => {
|
||||||
const resource = resources.find((r) => r.id === schedule.resourceId);
|
const resource = effectiveResources.find((r) => r.id === schedule.resourceId);
|
||||||
if (resource && onScheduleClick) {
|
if (resource && onScheduleClick) {
|
||||||
onScheduleClick({ schedule, resource });
|
onScheduleClick({ schedule, resource });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[resources, onScheduleClick]
|
[effectiveResources, onScheduleClick]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 빈 셀 클릭 핸들러
|
// 빈 셀 클릭 핸들러
|
||||||
|
|
@ -195,13 +215,13 @@ export function TimelineSchedulerComponent({
|
||||||
|
|
||||||
// 추가 버튼 클릭
|
// 추가 버튼 클릭
|
||||||
const handleAddClick = useCallback(() => {
|
const handleAddClick = useCallback(() => {
|
||||||
if (onAddSchedule && resources.length > 0) {
|
if (onAddSchedule && effectiveResources.length > 0) {
|
||||||
onAddSchedule(
|
onAddSchedule(
|
||||||
resources[0].id,
|
effectiveResources[0].id,
|
||||||
new Date().toISOString().split("T")[0]
|
new Date().toISOString().split("T")[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [onAddSchedule, resources]);
|
}, [onAddSchedule, effectiveResources]);
|
||||||
|
|
||||||
// 디자인 모드 플레이스홀더
|
// 디자인 모드 플레이스홀더
|
||||||
if (isDesignMode) {
|
if (isDesignMode) {
|
||||||
|
|
@ -250,8 +270,8 @@ export function TimelineSchedulerComponent({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 리소스 없음
|
// 리소스 없음 (스케줄도 없는 경우에만 표시)
|
||||||
if (resources.length === 0) {
|
if (effectiveResources.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="w-full flex items-center justify-center bg-muted/10 rounded-lg"
|
className="w-full flex items-center justify-center bg-muted/10 rounded-lg"
|
||||||
|
|
@ -259,8 +279,8 @@ export function TimelineSchedulerComponent({
|
||||||
>
|
>
|
||||||
<div className="text-center text-muted-foreground">
|
<div className="text-center text-muted-foreground">
|
||||||
<Calendar className="h-8 w-8 mx-auto mb-2" />
|
<Calendar className="h-8 w-8 mx-auto mb-2" />
|
||||||
<p className="text-sm font-medium">리소스가 없습니다</p>
|
<p className="text-sm font-medium">스케줄 데이터가 없습니다</p>
|
||||||
<p className="text-xs mt-1">리소스 테이블을 설정하세요</p>
|
<p className="text-xs mt-1">스케줄 테이블에 데이터를 추가하세요</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -385,7 +405,7 @@ export function TimelineSchedulerComponent({
|
||||||
|
|
||||||
{/* 리소스 행들 */}
|
{/* 리소스 행들 */}
|
||||||
<div>
|
<div>
|
||||||
{resources.map((resource) => (
|
{effectiveResources.map((resource) => (
|
||||||
<ResourceRow
|
<ResourceRow
|
||||||
key={resource.id}
|
key={resource.id}
|
||||||
resource={resource}
|
resource={resource}
|
||||||
|
|
|
||||||
|
|
@ -94,17 +94,6 @@ export function useTimelineData(
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 10000,
|
size: 10000,
|
||||||
autoFilter: true,
|
autoFilter: true,
|
||||||
search: {
|
|
||||||
// 표시 범위 내의 스케줄만 조회
|
|
||||||
[fieldMapping.startDate]: {
|
|
||||||
value: toDateString(viewEndDate),
|
|
||||||
operator: "lte",
|
|
||||||
},
|
|
||||||
[fieldMapping.endDate]: {
|
|
||||||
value: toDateString(viewStartDate),
|
|
||||||
operator: "gte",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue