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

@@ -1,6 +1,6 @@
'use client';
import { useState, useCallback, useMemo } from 'react';
import { useState, useCallback, useMemo, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Building2, Plus, X, Loader2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
@@ -125,6 +125,16 @@ export function VendorDetailClient({ mode, vendorId, initialData }: VendorDetail
// 새 메모 입력
const [newMemo, setNewMemo] = useState('');
// 상세/수정 모드에서 로고 목데이터 초기화
useEffect(() => {
if (initialData && !formData.logoUrl) {
setFormData(prev => ({
...prev,
logoUrl: 'https://placehold.co/750x250/3b82f6/white?text=Vendor+Logo',
}));
}
}, [initialData]);
// 필드 변경 핸들러
const handleChange = useCallback((field: string, value: string | number | boolean) => {
setFormData(prev => ({ ...prev, [field]: value }));
@@ -438,11 +448,21 @@ export function VendorDetailClient({ mode, vendorId, initialData }: VendorDetail
<div className="space-y-2">
<Label className="text-sm font-medium text-gray-700"> </Label>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">
<p className="text-sm text-gray-500">750 X 250px, 10MB PNG, JPEG, GIF</p>
{!isViewMode && (
<Button variant="outline" className="mt-2">
</Button>
{formData.logoUrl ? (
<img
src={formData.logoUrl}
alt="회사 로고"
className="max-h-[100px] max-w-[300px] object-contain mx-auto"
/>
) : (
<>
<p className="text-sm text-gray-500">750 X 250px, 10MB PNG, JPEG, GIF</p>
{!isViewMode && (
<Button variant="outline" className="mt-2">
</Button>
)}
</>
)}
</div>
</div>