feat(WEB): 리스트 페이지 UI 레이아웃 표준화

- 공통 레이아웃 패턴 적용: [달력] → [프리셋] → [검색창] → [버튼들]
- beforeTableContent → headerActions + createButton 마이그레이션
- DateRangeSelector extraActions prop 활용하여 검색창 통합
- PricingListClient 테이블 행 클릭 → 상세 이동 기능 추가
- 회계 관련 페이지 (입금/출금/매입/매출/어음/카드/예상지출 등) 정리
- 건설 관련 페이지 검색 영역 정리
- 부모 메뉴 리다이렉트 컴포넌트 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-26 22:04:36 +09:00
parent ff93ab7fa2
commit 1f6b592b9f
65 changed files with 1974 additions and 503 deletions

View File

@@ -38,7 +38,6 @@ import {
type FilterFieldConfig,
type FilterValues,
} from '@/components/templates/UniversalListPage';
import { DateRangeSelector } from '@/components/molecules/DateRangeSelector';
import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard';
import { VacationGrantDialog } from './VacationGrantDialog';
import { VacationRequestDialog } from './VacationRequestDialog';
@@ -587,48 +586,40 @@ export function VacationManagement() {
}
}, [mainTab, handleApproveClick, handleRejectClick]);
// ===== 헤더 액션 (DateRangeSelector + 버튼들) =====
// ===== 헤더 액션 (탭별 버튼들만 - DateRangeSelector와 검색창은 공통 옵션 사용) =====
const headerActions = useCallback(({ selectedItems: selected }: { selectedItems: Set<string>; onClearSelection?: () => void; onRefresh?: () => void }) => (
<>
<DateRangeSelector
startDate={startDate}
endDate={endDate}
onStartDateChange={setStartDate}
onEndDateChange={setEndDate}
/>
<div className="ml-auto flex gap-2">
{/* 탭별 액션 버튼 */}
{mainTab === 'grant' && (
<Button onClick={() => setGrantDialogOpen(true)}>
<Plus className="h-4 w-4 mr-2" />
</Button>
)}
<div className="flex items-center gap-2">
{/* 탭별 액션 버튼 */}
{mainTab === 'grant' && (
<Button onClick={() => setGrantDialogOpen(true)}>
<Plus className="h-4 w-4 mr-2" />
</Button>
)}
{mainTab === 'request' && (
<>
{/* 버튼 순서: 승인 → 거절 → 휴가신청 (휴가신청 버튼 위치 고정) */}
{selected.size > 0 && (
<>
<Button variant="default" onClick={() => handleApproveClick(selected)}>
<Check className="h-4 w-4 mr-2" />
</Button>
<Button variant="destructive" onClick={() => handleRejectClick(selected)}>
<X className="h-4 w-4 mr-2" />
</Button>
</>
)}
<Button onClick={() => setRequestDialogOpen(true)}>
<Plus className="h-4 w-4 mr-2" />
</Button>
</>
)}
</div>
</>
), [startDate, endDate, mainTab, handleApproveClick, handleRejectClick]);
{mainTab === 'request' && (
<>
{/* 버튼 순서: 승인 → 거절 → 휴가신청 (휴가신청 버튼 위치 고정) */}
{selected.size > 0 && (
<>
<Button variant="default" onClick={() => handleApproveClick(selected)}>
<Check className="h-4 w-4 mr-2" />
</Button>
<Button variant="destructive" onClick={() => handleRejectClick(selected)}>
<X className="h-4 w-4 mr-2" />
</Button>
</>
)}
<Button onClick={() => setRequestDialogOpen(true)}>
<Plus className="h-4 w-4 mr-2" />
</Button>
</>
)}
</div>
), [mainTab, handleApproveClick, handleRejectClick]);
// ===== filterConfig 기반 통합 필터 시스템 =====
const filterConfig: FilterFieldConfig[] = useMemo(() => [
@@ -693,6 +684,15 @@ export function VacationManagement() {
columns: tableColumns,
// 공통 패턴: dateRangeSelector
dateRangeSelector: {
enabled: true,
startDate,
endDate,
onStartDateChange: setStartDate,
onEndDateChange: setEndDate,
},
tabs: tabs,
defaultTab: mainTab,