feat(WEB): 공사관리 시스템 및 CEO 대시보드 기능 확장
- 공사현장관리: 프로젝트 상세, 공정관리, 칸반보드 구현 - 이슈관리: 현장 이슈 등록/조회 기능 추가 - 근로자현황: 일별 근로자 출역 현황 페이지 추가 - 유틸리티관리: 현장 유틸리티 관리 페이지 추가 - 기성청구: 기성청구 관리 페이지 추가 - CEO 대시보드: 현황판(StatusBoardSection) 추가, 설정 다이얼로그 개선 - 발주관리: 모바일 필터 적용, 리스트 UI 개선 - 공용 컴포넌트: MobileFilter, IntegratedListTemplateV2 개선, CalendarHeader 반응형 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,61 +27,97 @@ export function CalendarHeader({
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between pb-3 border-b">
|
||||
{/* 좌측: 타이틀 + 년월 네비게이션 */}
|
||||
<div className="flex items-center gap-4">
|
||||
{titleSlot && (
|
||||
<span className="text-base font-semibold text-foreground">{titleSlot}</span>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0 hover:bg-primary/10"
|
||||
onClick={onPrevMonth}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<div className="flex flex-col gap-3 pb-3 border-b">
|
||||
{/* PC: 타이틀 + 네비게이션 | 뷰전환 + 필터 (한 줄) */}
|
||||
{/* 모바일: 타이틀 / 네비게이션 + 뷰전환 / 필터 (세 줄) */}
|
||||
|
||||
<span className="text-lg font-bold min-w-[120px] text-center">
|
||||
{formatYearMonth(currentDate)}
|
||||
</span>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0 hover:bg-primary/10"
|
||||
onClick={onNextMonth}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
{/* 1줄(모바일) / 좌측(PC): 타이틀 */}
|
||||
{titleSlot && (
|
||||
<div className="xl:hidden text-base font-semibold text-foreground">
|
||||
{titleSlot}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 우측: 뷰 전환 + 필터 */}
|
||||
<div className="flex items-center gap-3">
|
||||
{/* 뷰 전환 탭 */}
|
||||
<div className="flex rounded-md border">
|
||||
{views.map((v) => (
|
||||
<button
|
||||
key={v.value}
|
||||
onClick={() => onViewChange(v.value)}
|
||||
className={cn(
|
||||
'px-4 py-1.5 text-sm font-medium transition-colors',
|
||||
'first:rounded-l-md last:rounded-r-md',
|
||||
view === v.value
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-primary/10 text-foreground'
|
||||
)}
|
||||
{/* 2줄(모바일) / 전체(PC): 네비게이션 + 뷰전환 + 필터 */}
|
||||
<div className="flex flex-col xl:flex-row xl:items-center xl:justify-between gap-3">
|
||||
{/* 좌측: (PC에서만 타이틀) + 네비게이션 */}
|
||||
<div className="flex items-center gap-4">
|
||||
{titleSlot && (
|
||||
<span className="hidden xl:block text-base font-semibold text-foreground">
|
||||
{titleSlot}
|
||||
</span>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0 hover:bg-primary/10"
|
||||
onClick={onPrevMonth}
|
||||
>
|
||||
{v.label}
|
||||
</button>
|
||||
))}
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<span className="text-lg font-bold min-w-[120px] text-center">
|
||||
{formatYearMonth(currentDate)}
|
||||
</span>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0 hover:bg-primary/10"
|
||||
onClick={onNextMonth}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 모바일: 뷰 전환 탭 (네비게이션 옆) */}
|
||||
<div className="flex xl:hidden rounded-md border">
|
||||
{views.map((v) => (
|
||||
<button
|
||||
key={v.value}
|
||||
onClick={() => onViewChange(v.value)}
|
||||
className={cn(
|
||||
'px-4 py-1.5 text-sm font-medium transition-colors',
|
||||
'first:rounded-l-md last:rounded-r-md',
|
||||
view === v.value
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-primary/10 text-foreground'
|
||||
)}
|
||||
>
|
||||
{v.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 필터 슬롯 */}
|
||||
{filterSlot && <div className="flex items-center gap-2">{filterSlot}</div>}
|
||||
{/* 우측(PC만): 뷰 전환 + 필터 */}
|
||||
<div className="hidden xl:flex items-center gap-3">
|
||||
<div className="flex rounded-md border">
|
||||
{views.map((v) => (
|
||||
<button
|
||||
key={v.value}
|
||||
onClick={() => onViewChange(v.value)}
|
||||
className={cn(
|
||||
'px-4 py-1.5 text-sm font-medium transition-colors',
|
||||
'first:rounded-l-md last:rounded-r-md',
|
||||
view === v.value
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'hover:bg-primary/10 text-foreground'
|
||||
)}
|
||||
>
|
||||
{v.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{filterSlot && <div className="flex items-center gap-2">{filterSlot}</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3줄(모바일만): 필터 */}
|
||||
{filterSlot && (
|
||||
<div className="flex xl:hidden items-center gap-2">{filterSlot}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user