feat(WEB): 입찰/계약/주문관리 기능 추가 및 견적 상세 리팩토링

- 입찰관리: 목록/상세/수정 페이지 및 목업 데이터
- 계약관리: 목록/상세/수정 페이지 구현
- 주문관리: 수주/발주 목록 및 상세 페이지 구현
- 견적 상세 폼: 섹션별 분리 및 hooks/utils 리팩토링
- 품목관리, 카테고리관리, 단가관리 기능 추가
- 현장설명회/협력업체 폼 개선
- 프린트 유틸리티 공통화 (print-utils.ts)
- 문서 모달 공통 컴포넌트 정리
- IntegratedListTemplateV2, StatCards 개선

🤖 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-05 18:59:04 +09:00
parent 4b1a3abf05
commit 386cd30bc0
145 changed files with 25782 additions and 254 deletions

View File

@@ -126,6 +126,9 @@ export interface IntegratedListTemplateV2Props<T = any> {
tableColumns: TableColumn[];
tableTitle?: string; // "전체 목록 (100개)" 같은 타이틀
// 커스텀 테이블 헤더 렌더링 (동적 컬럼용)
renderCustomTableHeader?: () => ReactNode;
// 테이블 하단 푸터 (합계 등)
tableFooter?: ReactNode;
@@ -157,6 +160,9 @@ export interface IntegratedListTemplateV2Props<T = any> {
// 개발자 메타데이터
devMetadata?: DevMetadata;
// 로딩 상태
isLoading?: boolean;
}
export function IntegratedListTemplateV2<T = any>({
@@ -181,6 +187,7 @@ export function IntegratedListTemplateV2<T = any>({
beforeTableContent,
tableColumns,
tableTitle,
renderCustomTableHeader,
tableFooter,
data,
totalCount,
@@ -199,6 +206,7 @@ export function IntegratedListTemplateV2<T = any>({
renderMobileCard,
pagination,
devMetadata,
isLoading,
}: IntegratedListTemplateV2Props<T>) {
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
@@ -275,9 +283,9 @@ export function IntegratedListTemplateV2<T = any>({
</Card>
)}
{/* 테이블 앞 컨텐츠 (계정과목명 + 저장 버튼 등) */}
{/* 테이블 앞 컨텐츠 (계정과목명 + 저장 버튼, 달력 등) */}
{beforeTableContent && (
<div className="flex items-center gap-2 py-2">
<div className="w-full py-2">
{beforeTableContent}
</div>
)}
@@ -385,25 +393,33 @@ export function IntegratedListTemplateV2<T = any>({
<Table>
<TableHeader>
<TableRow>
{showCheckbox && (
<TableHead className="w-[50px] min-w-[50px] max-w-[50px] text-center">
<Checkbox
checked={allSelected}
onCheckedChange={onToggleSelectAll}
/>
</TableHead>
{renderCustomTableHeader ? (
// 커스텀 테이블 헤더 사용 (동적 컬럼용)
renderCustomTableHeader()
) : (
// 기본 테이블 헤더
<>
{showCheckbox && (
<TableHead className="w-[50px] min-w-[50px] max-w-[50px] text-center">
<Checkbox
checked={allSelected}
onCheckedChange={onToggleSelectAll}
/>
</TableHead>
)}
{tableColumns.map((column) => {
// "actions" 컬럼은 항상 렌더링하되, 선택된 항목이 없을 때는 빈 헤더로 표시
return (
<TableHead
key={column.key}
className={column.className}
>
{column.key === "actions" && selectedItems.size === 0 ? "" : column.label}
</TableHead>
);
})}
</>
)}
{tableColumns.map((column) => {
// "actions" 컬럼은 항상 렌더링하되, 선택된 항목이 없을 때는 빈 헤더로 표시
return (
<TableHead
key={column.key}
className={column.className}
>
{column.key === "actions" && selectedItems.size === 0 ? "" : column.label}
</TableHead>
);
})}
</TableRow>
</TableHeader>
<TableBody className="[&_tr]:h-14 [&_tr]:min-h-[56px] [&_tr]:max-h-[56px]">