feat(WEB): 공사관리 리스트 공통화 및 캘린더/포맷터 기능 개선

공사관리 리스트 공통화:
- 입찰/계약/견적/인수인계/이슈/품목/노무/현장/파트너/단가/기성/현장브리핑/구조검토/유틸리티/작업자현황 리스트 공통 포맷터 적용
- 중복 포맷팅 로직 제거 (-530줄)

캘린더 기능 개선:
- CEODashboard CalendarSection 기능 확장
- ScheduleCalendar DayCell/MonthView/WeekView 개선
- ui/calendar 컴포넌트 기능 추가

유틸리티 개선:
- date.ts 날짜 유틸 함수 추가
- formatAmount.ts 금액 포맷 함수 추가

신규 추가:
- useListHandlers 훅 추가
- src/constants/ 디렉토리 추가
- 포맷터 공통화 계획 문서 추가
- SAM ERP/MES 정체성 분석 문서 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-05 17:38:38 +09:00
parent 2639724f9f
commit 32d6e3bbbd
36 changed files with 852 additions and 530 deletions

View File

@@ -10,7 +10,10 @@ interface DayCellProps {
isCurrentMonth: boolean;
isToday: boolean;
isSelected: boolean;
isWeekend: boolean;
isSaturday: boolean;
isSunday: boolean;
isHoliday?: boolean;
holidayName?: string | null;
isPast: boolean;
badge?: DayBadge;
onClick: (date: Date) => void;
@@ -28,18 +31,24 @@ export function DayCell({
isCurrentMonth,
isToday,
isSelected,
isWeekend,
isSaturday,
isSunday,
isHoliday = false,
holidayName,
isPast,
badge,
onClick,
}: DayCellProps) {
const dayNumber = format(date, 'd');
const badgeColor = badge?.color || 'red';
const isRedDay = isSunday || isHoliday; // 일요일 또는 공휴일 → 빨간색
const isBlueDay = isSaturday && !isHoliday; // 토요일(공휴일 아닌 경우) → 파란색
return (
<button
type="button"
onClick={() => onClick(date)}
title={holidayName || undefined}
className={cn(
'relative w-full h-8 flex items-center justify-center',
'text-sm font-medium transition-colors rounded-md',
@@ -48,10 +57,14 @@ export function DayCell({
isCurrentMonth ? 'text-foreground' : 'text-muted-foreground/40',
// 지난 일자 - 더 명확한 회색 (현재 월에서만)
isPast && isCurrentMonth && !isToday && !isSelected && 'text-gray-400',
// 주말 색상 (지난 일자가 아닌 경우만)
isWeekend && isCurrentMonth && !isPast && 'text-red-500',
// 지난 주말 - 연한 색상
isWeekend && isCurrentMonth && isPast && !isToday && !isSelected && 'text-red-300',
// 일요일/공휴일 색상 (지난 일자가 아닌 경우만) → 빨간색
isRedDay && isCurrentMonth && !isPast && 'text-red-500',
// 토요일 색상 (지난 일자가 아닌 경우만) → 파란색
isBlueDay && isCurrentMonth && !isPast && 'text-blue-500',
// 지난 일요일/공휴일 - 연한 색상
isRedDay && isCurrentMonth && isPast && !isToday && !isSelected && 'text-red-300',
// 지난 토요일 - 연한 파란색
isBlueDay && isCurrentMonth && isPast && !isToday && !isSelected && 'text-blue-300',
// 오늘 - 굵은 글씨 (외곽선은 부모 셀에 적용) - 보라색
isToday && !isSelected && 'font-bold text-purple-600',
// 선택됨 - 배경색 하이라이트 - 보라색

View File

@@ -19,7 +19,8 @@ import {
getEventsForDate,
getBadgeForDate,
} from './utils';
import { getDay } from 'date-fns';
import { getDay, format } from 'date-fns';
import { isHoliday, getHolidayName } from '@/constants/calendarEvents';
/**
* 월간 뷰 컴포넌트
@@ -65,9 +66,14 @@ export function MonthView({
{/* 요일 헤더 */}
<div className="grid grid-cols-7 mb-1">
{weekdayHeaders.map((day, index) => {
const isWeekend =
(weekStartsOn === 0 && (index === 0 || index === 6)) ||
(weekStartsOn === 1 && (index === 5 || index === 6));
// 일요일: weekStartsOn=0이면 index 0, weekStartsOn=1이면 index 6
const isSunday =
(weekStartsOn === 0 && index === 0) ||
(weekStartsOn === 1 && index === 6);
// 토요일: weekStartsOn=0이면 index 6, weekStartsOn=1이면 index 5
const isSaturday =
(weekStartsOn === 0 && index === 6) ||
(weekStartsOn === 1 && index === 5);
return (
<div
@@ -75,7 +81,8 @@ export function MonthView({
className={cn(
'text-center text-xs font-semibold py-2',
'text-muted-foreground',
isWeekend && 'text-red-400'
isSunday && 'text-red-400',
isSaturday && 'text-blue-400'
)}
>
{day}
@@ -168,7 +175,11 @@ function WeekRow({
{/* 날짜 셀들 */}
{weekDays.map((date, colIndex) => {
const dayOfWeek = getDay(date);
const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
const isSaturday = dayOfWeek === 6;
const isSunday = dayOfWeek === 0;
const dateStr = format(date, 'yyyy-MM-dd');
const isHolidayDate = isHoliday(dateStr);
const holidayName = getHolidayName(dateStr);
const badge = getBadgeForDate(badges, date);
const hiddenCount = hiddenEventCounts.get(colIndex) || 0;
const dayEvents = getEventsForDate(events, date);
@@ -201,7 +212,10 @@ function WeekRow({
isCurrentMonth={isCurrMonth}
isToday={isToday}
isSelected={isSelected}
isWeekend={isWeekend}
isSaturday={isSaturday}
isSunday={isSunday}
isHoliday={isHolidayDate}
holidayName={holidayName}
isPast={isPast}
badge={badge}
onClick={onDateClick}

View File

@@ -17,6 +17,7 @@ import {
} from './utils';
import { format, getDay } from 'date-fns';
import { ko } from 'date-fns/locale';
import { isHoliday, getHolidayName } from '@/constants/calendarEvents';
/**
* 주간 뷰 컴포넌트
@@ -81,7 +82,13 @@ export function WeekView({
<div className="grid grid-cols-7 mb-1">
{weekDays.map((date) => {
const dayOfWeek = getDay(date);
const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
const isSaturday = dayOfWeek === 6;
const isSunday = dayOfWeek === 0;
const dateStr = format(date, 'yyyy-MM-dd');
const isHolidayDate = isHoliday(dateStr);
const holidayName = getHolidayName(dateStr);
const isRedDay = isSunday || isHolidayDate; // 일요일 또는 공휴일 → 빨간색
const isBlueDay = isSaturday && !isHolidayDate; // 토요일(공휴일 아닌 경우) → 파란색
const isToday = checkIsToday(date);
return (
@@ -91,11 +98,13 @@ export function WeekView({
'text-center py-2',
'flex flex-col items-center'
)}
title={holidayName || undefined}
>
<span
className={cn(
'text-xs font-semibold text-muted-foreground',
isWeekend && 'text-red-400'
isRedDay && 'text-red-400',
isBlueDay && 'text-blue-400'
)}
>
{format(date, 'E', { locale: ko })}
@@ -103,7 +112,8 @@ export function WeekView({
<span
className={cn(
'text-lg font-bold mt-0.5',
isWeekend && 'text-red-500',
isRedDay && 'text-red-500',
isBlueDay && 'text-blue-500',
isToday && 'bg-primary text-primary-foreground rounded-full w-8 h-8 flex items-center justify-center'
)}
>