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:
@@ -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]">
|
||||
|
||||
Reference in New Issue
Block a user