fix(WEB): React/Next.js 보안 업데이트 및 캘린더/주문관리 개선

- 보안: next 15.5.7 → 15.5.9 (CVE-2025-55184, CVE-2025-55183, CVE-2025-67779)
- 보안: react/react-dom 19.2.1 → 19.2.3
- 캘린더: MonthView, ScheduleBar 개선
- 주문관리: 리스트/액션/타입 수정

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2026-01-06 11:03:33 +09:00
parent a938da9e22
commit eccfd959fe
9 changed files with 241 additions and 84 deletions

View File

@@ -211,6 +211,7 @@ export function getEventSegmentsForWeek(
* 이벤트 행 배치 계산 (다른 색상은 다른 행에 배치)
* - 같은 색상(작업반장)끼리만 같은 행 공유 가능
* - 다른 색상은 무조건 다른 행에 배치
* - 시작일이 빠른 색상 그룹이 위에 배치
*/
export function assignEventRows(segments: WeekEventSegment[]): Map<string, number> {
const rowMap = new Map<string, number>();
@@ -225,10 +226,18 @@ export function assignEventRows(segments: WeekEventSegment[]): Map<string, numbe
colorGroups.get(color)!.push(segment);
});
// 색상 그룹을 가장 이른 시작일 기준으로 정렬
const sortedColorGroups = Array.from(colorGroups.entries()).sort((a, b) => {
// 각 그룹에서 가장 이른 시작일 찾기
const aMinStart = Math.min(...a[1].map(s => parseISO(s.event.startDate).getTime()));
const bMinStart = Math.min(...b[1].map(s => parseISO(s.event.startDate).getTime()));
return aMinStart - bMinStart;
});
let currentBaseRow = 0;
// 각 색상 그룹별로 행 배치
colorGroups.forEach((groupSegments) => {
// 각 색상 그룹별로 행 배치 (시작일 순)
sortedColorGroups.forEach(([, groupSegments]) => {
const occupied: boolean[][] = [];
// 시작 컬럼 순으로 정렬
@@ -280,6 +289,51 @@ export function assignEventRows(segments: WeekEventSegment[]): Map<string, numbe
return rowMap;
}
/**
* 전역 이벤트 행 배치 계산 (월간 뷰 전체 기준)
* - 먼저 시작한 이벤트가 끝날 때까지 같은 행 유지
* - 나중에 시작한 이벤트는 아래 행으로 배치
*/
export function assignGlobalEventRows(events: ScheduleEvent[]): Map<string, number> {
const rowMap = new Map<string, number>();
if (events.length === 0) return rowMap;
// 모든 이벤트를 시작일 순으로 정렬 (시작일 같으면 기간 긴 것 먼저)
const sortedEvents = [...events].sort((a, b) => {
const aStart = parseISO(a.startDate).getTime();
const bStart = parseISO(b.startDate).getTime();
if (aStart !== bStart) return aStart - bStart;
const aDuration = parseISO(a.endDate).getTime() - aStart;
const bDuration = parseISO(b.endDate).getTime() - bStart;
return bDuration - aDuration;
});
// 각 행의 점유 종료일 추적
const rowEndDates: number[] = [];
sortedEvents.forEach((event) => {
const eventStart = parseISO(event.startDate).getTime();
const eventEnd = parseISO(event.endDate).getTime();
// 이 이벤트를 배치할 수 있는 가장 낮은 행 찾기
let row = 0;
while (row < rowEndDates.length) {
// 이전 이벤트가 끝났으면 배치 가능
if (rowEndDates[row] < eventStart) {
break;
}
row++;
}
// 행에 배치하고 종료일 업데이트
rowEndDates[row] = eventEnd;
rowMap.set(event.id, row);
});
return rowMap;
}
/**
* 월간 뷰에서 주 단위로 날짜 분할
*/