summaryrefslogtreecommitdiff
path: root/lib/general-contracts_old/detail/general-contract-basic-info.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/general-contracts_old/detail/general-contract-basic-info.tsx')
-rw-r--r--lib/general-contracts_old/detail/general-contract-basic-info.tsx1250
1 files changed, 1250 insertions, 0 deletions
diff --git a/lib/general-contracts_old/detail/general-contract-basic-info.tsx b/lib/general-contracts_old/detail/general-contract-basic-info.tsx
new file mode 100644
index 00000000..d891fe63
--- /dev/null
+++ b/lib/general-contracts_old/detail/general-contract-basic-info.tsx
@@ -0,0 +1,1250 @@
+'use client'
+
+import React, { useState } from 'react'
+import { useSession } from 'next-auth/react'
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
+import { Textarea } from '@/components/ui/textarea'
+import { Button } from '@/components/ui/button'
+import { Save, LoaderIcon } from 'lucide-react'
+import { updateContractBasicInfo, getContractBasicInfo } from '../service'
+import { toast } from 'sonner'
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
+import { GeneralContract } from '@/db/schema'
+import { ContractDocuments } from './general-contract-documents'
+import { getPaymentTermsForSelection, getIncotermsForSelection, getPlaceOfShippingForSelection, getPlaceOfDestinationForSelection } from '@/lib/procurement-select/service'
+import { TAX_CONDITIONS, getTaxConditionName } from '@/lib/tax-conditions/types'
+
+interface ContractBasicInfoProps {
+ contractId: number
+}
+
+export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
+ const session = useSession()
+ const [isLoading, setIsLoading] = useState(false)
+ const [contract, setContract] = useState<GeneralContract | null>(null)
+ const userId = session.data?.user?.id ? Number(session.data.user.id) : null
+
+ // 독립적인 상태 관리
+ const [paymentDeliveryPercent, setPaymentDeliveryPercent] = useState('')
+
+ // Procurement 데이터 상태들
+ const [paymentTermsOptions, setPaymentTermsOptions] = useState<Array<{code: string, description: string}>>([])
+ const [incotermsOptions, setIncotermsOptions] = useState<Array<{code: string, description: string}>>([])
+ const [shippingPlaces, setShippingPlaces] = useState<Array<{code: string, description: string}>>([])
+ const [destinationPlaces, setDestinationPlaces] = useState<Array<{code: string, description: string}>>([])
+ const [procurementLoading, setProcurementLoading] = useState(false)
+
+ const [formData, setFormData] = useState({
+ specificationType: '',
+ specificationManualText: '',
+ unitPriceType: '',
+ warrantyPeriod: {
+ 납품후: { enabled: false, period: 0, maxPeriod: 0 },
+ 인도후: { enabled: false, period: 0, maxPeriod: 0 },
+ 작업후: { enabled: false, period: 0, maxPeriod: 0 },
+ 기타: { enabled: false, period: 0, maxPeriod: 0 },
+ },
+ contractAmount: null as number | null,
+ currency: 'KRW',
+ linkedPoNumber: '',
+ linkedBidNumber: '',
+ notes: '',
+ // 개별 JSON 필드들 (스키마에 맞게)
+ paymentBeforeDelivery: {} as any,
+ paymentDelivery: '', // varchar 타입
+ paymentAfterDelivery: {} as any,
+ paymentTerm: '',
+ taxType: '',
+ liquidatedDamages: false as boolean,
+ liquidatedDamagesPercent: '',
+ deliveryType: '',
+ deliveryTerm: '',
+ shippingLocation: '',
+ dischargeLocation: '',
+ contractDeliveryDate: '',
+ contractEstablishmentConditions: {
+ regularVendorRegistration: false,
+ projectAward: false,
+ ownerApproval: false,
+ other: false,
+ },
+ interlockingSystem: '',
+ mandatoryDocuments: {
+ technicalDataAgreement: false,
+ nda: false,
+ basicCompliance: false,
+ safetyHealthAgreement: false,
+ },
+ contractTerminationConditions: {
+ standardTermination: false,
+ projectNotAwarded: false,
+ other: false,
+ },
+ })
+
+ const [errors] = useState<Record<string, string>>({})
+
+ // 계약 데이터 로드
+ React.useEffect(() => {
+ const loadContract = async () => {
+ try {
+ console.log('Loading contract with ID:', contractId)
+ const contractData = await getContractBasicInfo(contractId)
+ console.log('Contract data received:', contractData)
+ setContract(contractData as GeneralContract)
+
+ // JSON 필드들 파싱 (null 체크) - 스키마에 맞게 개별 필드로 접근
+ const paymentBeforeDelivery = (contractData?.paymentBeforeDelivery && typeof contractData.paymentBeforeDelivery === 'object') ? contractData.paymentBeforeDelivery as any : {}
+ const paymentAfterDelivery = (contractData?.paymentAfterDelivery && typeof contractData.paymentAfterDelivery === 'object') ? contractData.paymentAfterDelivery as any : {}
+ const warrantyPeriod = (contractData?.warrantyPeriod && typeof contractData.warrantyPeriod === 'object') ? contractData.warrantyPeriod as any : {}
+ const contractEstablishmentConditions = (contractData?.contractEstablishmentConditions && typeof contractData.contractEstablishmentConditions === 'object') ? contractData.contractEstablishmentConditions as any : {}
+ const mandatoryDocuments = (contractData?.mandatoryDocuments && typeof contractData.mandatoryDocuments === 'object') ? contractData.mandatoryDocuments as any : {}
+ const contractTerminationConditions = (contractData?.contractTerminationConditions && typeof contractData.contractTerminationConditions === 'object') ? contractData.contractTerminationConditions as any : {}
+
+ // paymentDelivery에서 퍼센트와 타입 분리
+ const paymentDeliveryValue = contractData?.paymentDelivery || ''
+ let paymentDeliveryType = ''
+ let paymentDeliveryPercentValue = ''
+
+ if (paymentDeliveryValue.includes('%')) {
+ const match = paymentDeliveryValue.match(/(\d+)%\s*(.+)/)
+ if (match) {
+ paymentDeliveryPercentValue = match[1]
+ paymentDeliveryType = match[2]
+ }
+ } else {
+ paymentDeliveryType = paymentDeliveryValue
+ }
+
+ setPaymentDeliveryPercent(paymentDeliveryPercentValue)
+
+ setFormData({
+ specificationType: contractData?.specificationType || '',
+ specificationManualText: contractData?.specificationManualText || '',
+ unitPriceType: contractData?.unitPriceType || '',
+ warrantyPeriod: warrantyPeriod || {
+ 납품후: { enabled: false, period: 0, maxPeriod: 0 },
+ 인도후: { enabled: false, period: 0, maxPeriod: 0 },
+ 작업후: { enabled: false, period: 0, maxPeriod: 0 },
+ 기타: { enabled: false, period: 0, maxPeriod: 0 },
+ },
+ contractAmount: contractData?.contractAmount || null,
+ currency: contractData?.currency || 'KRW',
+ linkedPoNumber: contractData?.linkedPoNumber || '',
+ linkedBidNumber: contractData?.linkedBidNumber || '',
+ notes: contractData?.notes || '',
+ // 개별 JSON 필드들
+ paymentBeforeDelivery: paymentBeforeDelivery || {} as any,
+ paymentDelivery: paymentDeliveryType, // 분리된 타입만 저장
+ paymentAfterDelivery: paymentAfterDelivery || {} as any,
+ paymentTerm: contractData?.paymentTerm || '',
+ taxType: contractData?.taxType || '',
+ liquidatedDamages: Boolean(contractData?.liquidatedDamages),
+ liquidatedDamagesPercent: contractData?.liquidatedDamagesPercent || '',
+ deliveryType: contractData?.deliveryType || '',
+ deliveryTerm: contractData?.deliveryTerm || '',
+ shippingLocation: contractData?.shippingLocation || '',
+ dischargeLocation: contractData?.dischargeLocation || '',
+ contractDeliveryDate: contractData?.contractDeliveryDate || '',
+ contractEstablishmentConditions: contractEstablishmentConditions || {
+ regularVendorRegistration: false,
+ projectAward: false,
+ ownerApproval: false,
+ other: false,
+ },
+ interlockingSystem: contractData?.interlockingSystem || '',
+ mandatoryDocuments: mandatoryDocuments || {
+ technicalDataAgreement: false,
+ nda: false,
+ basicCompliance: false,
+ safetyHealthAgreement: false,
+ },
+ contractTerminationConditions: contractTerminationConditions || {
+ standardTermination: false,
+ projectNotAwarded: false,
+ other: false,
+ },
+ })
+ } catch (error) {
+ console.error('Error loading contract:', error)
+ toast.error('계약 정보를 불러오는 중 오류가 발생했습니다.')
+ }
+ }
+
+ if (contractId) {
+ loadContract()
+ }
+ }, [contractId])
+
+ // Procurement 데이터 로드 함수들
+ const loadPaymentTerms = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getPaymentTermsForSelection();
+ setPaymentTermsOptions(data);
+ } catch (error) {
+ console.error("Failed to load payment terms:", error);
+ toast.error("결제조건 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ const loadIncoterms = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getIncotermsForSelection();
+ setIncotermsOptions(data);
+ } catch (error) {
+ console.error("Failed to load incoterms:", error);
+ toast.error("운송조건 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ const loadShippingPlaces = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getPlaceOfShippingForSelection();
+ setShippingPlaces(data);
+ } catch (error) {
+ console.error("Failed to load shipping places:", error);
+ toast.error("선적지 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ const loadDestinationPlaces = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getPlaceOfDestinationForSelection();
+ setDestinationPlaces(data);
+ } catch (error) {
+ console.error("Failed to load destination places:", error);
+ toast.error("하역지 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ // 컴포넌트 마운트 시 procurement 데이터 로드
+ React.useEffect(() => {
+ loadPaymentTerms();
+ loadIncoterms();
+ loadShippingPlaces();
+ loadDestinationPlaces();
+ }, [loadPaymentTerms, loadIncoterms, loadShippingPlaces, loadDestinationPlaces]);
+ const handleSaveContractInfo = async () => {
+ if (!userId) {
+ toast.error('사용자 정보를 찾을 수 없습니다.')
+ return
+ }
+ try {
+ setIsLoading(true)
+
+ // 필수값 validation 체크
+ const validationErrors: string[] = []
+ if (!formData.specificationType) validationErrors.push('사양')
+ if (!formData.paymentDelivery) validationErrors.push('납품 지급조건')
+ if (!formData.currency) validationErrors.push('계약통화')
+ if (!formData.paymentTerm) validationErrors.push('지불조건')
+ if (!formData.taxType) validationErrors.push('세금조건')
+
+ if (validationErrors.length > 0) {
+ toast.error(`다음 필수 항목을 입력해주세요: ${validationErrors.join(', ')}`)
+ return
+ }
+
+ // paymentDelivery와 paymentDeliveryPercent 합쳐서 저장
+ const dataToSave = {
+ ...formData,
+ paymentDelivery: (formData.paymentDelivery === 'L/C' || formData.paymentDelivery === 'T/T') && paymentDeliveryPercent
+ ? `${paymentDeliveryPercent}% ${formData.paymentDelivery}`
+ : formData.paymentDelivery
+ }
+
+ await updateContractBasicInfo(contractId, dataToSave, userId as number)
+ toast.success('계약 정보가 저장되었습니다.')
+ } catch (error) {
+ console.error('Error saving contract info:', error)
+ toast.error('계약 정보 저장 중 오류가 발생했습니다.')
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ return (
+ <Card className="w-full">
+ <CardHeader>
+ <CardTitle>계약 기본 정보</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <Tabs defaultValue="basic" className="w-full">
+ <TabsList className="grid w-full grid-cols-4 h-auto overflow-x-auto">
+ <TabsTrigger value="basic" className="text-xs px-2 py-2 whitespace-nowrap">기본 정보</TabsTrigger>
+ <TabsTrigger value="conditions" className="text-xs px-2 py-2 whitespace-nowrap">지급/인도 조건</TabsTrigger>
+ <TabsTrigger value="additional" className="text-xs px-2 py-2 whitespace-nowrap">추가 조건</TabsTrigger>
+ <TabsTrigger value="documents" className="text-xs px-2 py-2 whitespace-nowrap">계약첨부문서</TabsTrigger>
+ </TabsList>
+
+ {/* 기본 정보 탭 */}
+ <TabsContent value="basic" className="space-y-6">
+ <Card>
+ {/* 보증기간 및 단가유형 */}
+ <CardHeader>
+ <CardTitle>보증기간 및 단가유형</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-4">
+ {/* 3그리드: 보증기간, 사양, 단가 */}
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
+ {/* 보증기간 */}
+ <div className="flex flex-col gap-2">
+ <Label htmlFor="warrantyPeriod">품질/하자 보증기간</Label>
+ <div className="space-y-3">
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="warrantyAfterDelivery"
+ checked={formData.warrantyPeriod.납품후?.enabled || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 납품후: {
+ ...prev.warrantyPeriod.납품후,
+ enabled: e.target.checked
+ }
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="warrantyAfterDelivery" className="text-sm">납품 후</Label>
+ </div>
+ {formData.warrantyPeriod.납품후?.enabled && (
+ <div className="ml-6 flex items-center space-x-2">
+ <Input
+ type="number"
+ placeholder="보증기간"
+ value={formData.warrantyPeriod.납품후?.period || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 납품후: {
+ ...prev.warrantyPeriod.납품후,
+ period: parseInt(e.target.value) || 0
+ }
+ }
+ }))}
+ className="w-20 h-8 text-sm"
+ />
+ <span className="text-xs text-muted-foreground">개월, 최대</span>
+ <Input
+ type="number"
+ placeholder="최대"
+ value={formData.warrantyPeriod.납품후?.maxPeriod || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 납품후: {
+ ...prev.warrantyPeriod.납품후,
+ maxPeriod: parseInt(e.target.value) || 0
+ }
+ }
+ }))}
+ className="w-20 h-8 text-sm"
+ />
+ <span className="text-xs text-muted-foreground">개월</span>
+ </div>
+ )}
+
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="warrantyAfterHandover"
+ checked={formData.warrantyPeriod.인도후?.enabled || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 인도후: {
+ ...prev.warrantyPeriod.인도후,
+ enabled: e.target.checked
+ }
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="warrantyAfterHandover" className="text-sm">인도 후</Label>
+ </div>
+ {formData.warrantyPeriod.인도후?.enabled && (
+ <div className="ml-6 flex items-center space-x-2">
+ <Input
+ type="number"
+ placeholder="보증기간"
+ value={formData.warrantyPeriod.인도후?.period || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 인도후: {
+ ...prev.warrantyPeriod.인도후,
+ period: parseInt(e.target.value) || 0
+ }
+ }
+ }))}
+ className="w-20 h-8 text-sm"
+ />
+ <span className="text-xs text-muted-foreground">개월, 최대</span>
+ <Input
+ type="number"
+ placeholder="최대"
+ value={formData.warrantyPeriod.인도후?.maxPeriod || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 인도후: {
+ ...prev.warrantyPeriod.인도후,
+ maxPeriod: parseInt(e.target.value) || 0
+ }
+ }
+ }))}
+ className="w-20 h-8 text-sm"
+ />
+ <span className="text-xs text-muted-foreground">개월</span>
+ </div>
+ )}
+
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="warrantyAfterWork"
+ checked={formData.warrantyPeriod.작업후?.enabled || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 작업후: {
+ ...prev.warrantyPeriod.작업후,
+ enabled: e.target.checked
+ }
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="warrantyAfterWork" className="text-sm">작업 후</Label>
+ </div>
+ {formData.warrantyPeriod.작업후?.enabled && (
+ <div className="ml-6 flex items-center space-x-2">
+ <Input
+ type="number"
+ placeholder="보증기간"
+ value={formData.warrantyPeriod.작업후?.period || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 작업후: {
+ ...prev.warrantyPeriod.작업후,
+ period: parseInt(e.target.value) || 0
+ }
+ }
+ }))}
+ className="w-20 h-8 text-sm"
+ />
+ <span className="text-xs text-muted-foreground">개월, 최대</span>
+ <Input
+ type="number"
+ placeholder="최대"
+ value={formData.warrantyPeriod.작업후?.maxPeriod || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 작업후: {
+ ...prev.warrantyPeriod.작업후,
+ maxPeriod: parseInt(e.target.value) || 0
+ }
+ }
+ }))}
+ className="w-20 h-8 text-sm"
+ />
+ <span className="text-xs text-muted-foreground">개월</span>
+ </div>
+ )}
+
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="warrantyOther"
+ checked={formData.warrantyPeriod.기타?.enabled || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ warrantyPeriod: {
+ ...prev.warrantyPeriod,
+ 기타: {
+ ...prev.warrantyPeriod.기타,
+ enabled: e.target.checked
+ }
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="warrantyOther" className="text-sm">기타/미적용</Label>
+ </div>
+ </div>
+ </div>
+ {/* 사양 */}
+ <div className="flex flex-col gap-2">
+ <Label htmlFor="specificationType">사양 <span className="text-red-600">*</span></Label>
+ <Select value={formData.specificationType} onValueChange={(value) => setFormData(prev => ({ ...prev, specificationType: value }))}>
+ <SelectTrigger className={errors.specificationType ? 'border-red-500' : ''}>
+ <SelectValue placeholder="사양을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="첨부파일">첨부파일</SelectItem>
+ <SelectItem value="표준사양">표준사양</SelectItem>
+ <SelectItem value="수기사양">수기사양</SelectItem>
+ </SelectContent>
+ </Select>
+ {errors.specificationType && (
+ <p className="text-sm text-red-600">사양은 필수값입니다.</p>
+ )}
+ </div>
+ {/* 단가 */}
+ <div className="flex flex-col gap-2">
+ <Label htmlFor="unitPriceType">단가 유형</Label>
+ <Select value={formData.unitPriceType} onValueChange={(value) => setFormData(prev => ({ ...prev, unitPriceType: value }))}>
+ <SelectTrigger>
+ <SelectValue placeholder="단가 유형을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="자재개별단가">자재개별단가</SelectItem>
+ <SelectItem value="서비스용역단가">서비스용역단가</SelectItem>
+ <SelectItem value="프로젝트단가">프로젝트단가</SelectItem>
+ <SelectItem value="지역별단가">지역별단가</SelectItem>
+ <SelectItem value="직무직급단가">직무직급단가</SelectItem>
+ <SelectItem value="단계별단가">단계별단가</SelectItem>
+ <SelectItem value="기타">기타</SelectItem>
+ </SelectContent>
+ </Select>
+ </div>
+ {/* 선택에 따른 폼: vertical로 출력 */}
+
+
+ {/* 사양이 수기사양일 때 매뉴얼 텍스트 */}
+ {formData.specificationType === '수기사양' && (
+ <div className="flex flex-col gap-2">
+ <Label htmlFor="specificationManualText">사양 매뉴얼 텍스트</Label>
+ <Textarea
+ value={formData.specificationManualText}
+ onChange={(e) => setFormData(prev => ({ ...prev, specificationManualText: e.target.value }))}
+ placeholder="사양 매뉴얼 텍스트를 입력하세요"
+ rows={3}
+ />
+ </div>
+ )}
+
+ </div>
+
+
+ </CardContent>
+ </Card>
+ </TabsContent>
+
+ {/* 지급/인도 조건 탭 */}
+ <TabsContent value="conditions" className="space-y-6">
+ <Card>
+ <CardHeader>
+ <CardTitle>Payment & Delivery Conditions (지급/인도 조건)</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-6">
+ <div className="grid grid-cols-5 gap-6">
+ {/* 납품 전 지급조건 */}
+ <div className="space-y-4">
+ <Label className="text-base font-medium">납품 전</Label>
+ <div className="space-y-3">
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="apBond"
+ checked={formData.paymentBeforeDelivery.apBond || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ apBond: e.target.checked
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="apBond" className="text-sm">AP Bond & Performance Bond</Label>
+ <Input
+ type="number"
+ min="0"
+ placeholder="%"
+ className="w-16"
+ value={formData.paymentBeforeDelivery.apBondPercent || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ apBondPercent: e.target.value
+ }
+ }))}
+ disabled={!formData.paymentBeforeDelivery.apBond}
+ />
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="drawingSubmission"
+ checked={formData.paymentBeforeDelivery.drawingSubmission || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ drawingSubmission: e.target.checked
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="drawingSubmission" className="text-sm">도면제출</Label>
+ <Input
+ type="number"
+ min="0"
+ placeholder="%"
+ className="w-16"
+ value={formData.paymentBeforeDelivery.drawingSubmissionPercent || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ drawingSubmissionPercent: e.target.value
+ }
+ }))}
+ disabled={!formData.paymentBeforeDelivery.drawingSubmission}
+ />
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="materialPurchase"
+ checked={formData.paymentBeforeDelivery.materialPurchase || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ materialPurchase: e.target.checked
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="materialPurchase" className="text-sm">소재구매 문서</Label>
+ <Input
+ type="number"
+ min="0"
+ placeholder="%"
+ className="w-16"
+ value={formData.paymentBeforeDelivery.materialPurchasePercent || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ materialPurchasePercent: e.target.value
+ }
+ }))}
+ disabled={!formData.paymentBeforeDelivery.materialPurchase}
+ />
+ </div>
+ </div>
+ </div>
+
+ {/* 납품 지급조건 */}
+ <div className="space-y-4">
+ <Label className="text-base font-medium">납품</Label>
+ <div className="space-y-3">
+ <div className="space-y-2">
+ <Label htmlFor="paymentDelivery">납품 지급조건 <span className="text-red-600">*</span></Label>
+ <Select value={formData.paymentDelivery} onValueChange={(value) => setFormData(prev => ({ ...prev, paymentDelivery: value }))}>
+ <SelectTrigger className={errors.paymentDelivery ? 'border-red-500' : ''}>
+ <SelectValue placeholder="납품 지급조건을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="L/C">L/C</SelectItem>
+ <SelectItem value="T/T">T/T</SelectItem>
+ <SelectItem value="거래명세서 기반 정기지급조건">거래명세서 기반 정기지급조건</SelectItem>
+ <SelectItem value="작업 및 입고 검사 완료">작업 및 입고 검사 완료</SelectItem>
+ <SelectItem value="청구내역서 제출 및 승인">청구내역서 제출 및 승인</SelectItem>
+ <SelectItem value="정규금액 월 단위 정산(지정일 지급)">정규금액 월 단위 정산(지정일 지급)</SelectItem>
+ </SelectContent>
+ </Select>
+ {/* L/C 또는 T/T 선택 시 퍼센트 입력 필드 */}
+ {(formData.paymentDelivery === 'L/C' || formData.paymentDelivery === 'T/T') && (
+ <div className="flex items-center gap-2 mt-2">
+ <Input
+ type="number"
+ min="0"
+ value={paymentDeliveryPercent}
+ onChange={(e) => setPaymentDeliveryPercent(e.target.value)}
+ placeholder="퍼센트"
+ className="w-20 h-8 text-sm"
+ />
+ <span className="text-sm text-gray-600">%</span>
+ </div>
+ )}
+ {errors.paymentDelivery && (
+ <p className="text-sm text-red-600">납품 지급조건은 필수값입니다.</p>
+ )}
+ </div>
+ </div>
+ </div>
+
+ {/* 납품 외 지급조건 */}
+ <div className="space-y-4">
+ <Label className="text-base font-medium">납품 외</Label>
+ <div className="space-y-3">
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="commissioning"
+ checked={formData.paymentAfterDelivery.commissioning || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ commissioning: e.target.checked
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="commissioning" className="text-sm">Commissioning 완료</Label>
+ <Input
+ type="number"
+ min="0"
+ placeholder="%"
+ className="w-16"
+ value={formData.paymentAfterDelivery.commissioningPercent || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ commissioningPercent: e.target.value
+ }
+ }))}
+ disabled={!formData.paymentAfterDelivery.commissioning}
+ />
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="finalDocument"
+ checked={formData.paymentAfterDelivery.finalDocument || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ finalDocument: e.target.checked
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="finalDocument" className="text-sm">최종문서 승인</Label>
+ <Input
+ type="number"
+ min="0"
+ placeholder="%"
+ className="w-16"
+ value={formData.paymentAfterDelivery.finalDocumentPercent || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ finalDocumentPercent: e.target.value
+ }
+ }))}
+ disabled={!formData.paymentAfterDelivery.finalDocument}
+ />
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="other"
+ checked={formData.paymentAfterDelivery.other || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ other: e.target.checked
+ }
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="other" className="text-sm">기타</Label>
+ <Input
+ type="text"
+ placeholder="기타 조건을 입력하세요"
+ className="w-48"
+ value={formData.paymentAfterDelivery.otherText || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ otherText: e.target.value
+ }
+ }))}
+ disabled={!formData.paymentAfterDelivery.other}
+ />
+ </div>
+ </div>
+ </div>
+
+ {/* 지불조건 */}
+ <div className="space-y-4">
+ <Label className="text-base font-medium">지불조건</Label>
+ <div className="space-y-3">
+ <div className="space-y-2">
+ <Label htmlFor="paymentTerm">지불조건 <span className="text-red-600">*</span></Label>
+ <Select
+ value={formData.paymentTerm}
+ onValueChange={(value) => setFormData(prev => ({ ...prev, paymentTerm: value }))}
+ >
+ <SelectTrigger className={errors.paymentTerm ? 'border-red-500' : ''}>
+ <SelectValue placeholder="지불조건을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {paymentTermsOptions.length > 0 ? (
+ paymentTermsOptions.map((option) => (
+ <SelectItem key={option.code} value={option.code}>
+ {option.code} {option.description && `(${option.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
+ {errors.paymentTerm && (
+ <p className="text-sm text-red-600">지불조건은 필수값입니다.</p>
+ )}
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="taxType">세금조건 <span className="text-red-600">*</span></Label>
+ <Select
+ value={formData.taxType}
+ onValueChange={(value) => setFormData(prev => ({ ...prev, taxType: value }))}
+ >
+ <SelectTrigger className={errors.taxType ? 'border-red-500' : ''}>
+ <SelectValue placeholder="세금조건을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {TAX_CONDITIONS.map((condition) => (
+ <SelectItem key={condition.code} value={condition.code}>
+ {condition.name}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ {errors.taxType && (
+ <p className="text-sm text-red-600">세금조건은 필수값입니다.</p>
+ )}
+ </div>
+ </div>
+ </div>
+
+ {/* 클레임금액 */}
+ <div className="space-y-4">
+ <Label className="text-base font-medium">클레임금액</Label>
+ <div className="space-y-3">
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="liquidatedDamages"
+ checked={formData.liquidatedDamages || false}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ liquidatedDamages: e.target.checked
+ }))}
+ className="rounded"
+ />
+ <Label htmlFor="liquidatedDamages" className="text-sm">지체상금</Label>
+ <Input
+ type="number"
+ min="0"
+ placeholder="%"
+ className="w-16"
+ value={formData.liquidatedDamagesPercent || ''}
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ liquidatedDamagesPercent: e.target.value
+ }))}
+ disabled={!formData.liquidatedDamages}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+
+ {/* 인도조건 섹션 */}
+ <div className="mt-8">
+ <h3 className="text-lg font-semibold mb-4">인도조건</h3>
+ <div className="grid grid-cols-5 gap-6">
+ {/* 납기종류 */}
+ <div className="space-y-4">
+ <div className="space-y-3">
+ <div className="space-y-2">
+ <Label htmlFor="deliveryType">납기종류</Label>
+ <Select value={formData.deliveryType} onValueChange={(value) => setFormData(prev => ({ ...prev, deliveryType: value }))}>
+ <SelectTrigger>
+ <SelectValue placeholder="납기종류를 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="단일납기">단일납기</SelectItem>
+ <SelectItem value="분할납기">분할납기</SelectItem>
+ <SelectItem value="구간납기">구간납기</SelectItem>
+ </SelectContent>
+ </Select>
+ </div>
+ </div>
+ </div>
+
+ {/* 인도조건 */}
+ <div className="space-y-4">
+ <div className="space-y-3">
+ <div className="space-y-2">
+ <Label htmlFor="deliveryTerm">인도조건</Label>
+ <Select
+ value={formData.deliveryTerm}
+ onValueChange={(value) => setFormData(prev => ({ ...prev, deliveryTerm: value }))}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="인도조건을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {incotermsOptions.length > 0 ? (
+ incotermsOptions.map((option) => (
+ <SelectItem key={option.code} value={option.code}>
+ {option.code} {option.description && `(${option.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
+ </div>
+ </div>
+ </div>
+
+ {/* 선적지 */}
+ <div className="space-y-4">
+ <div className="space-y-3">
+ <div className="space-y-2">
+ <Label htmlFor="shippingLocation">선적지</Label>
+ <Select
+ value={formData.shippingLocation}
+ onValueChange={(value) => setFormData(prev => ({ ...prev, shippingLocation: value }))}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="선적지를 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {shippingPlaces.length > 0 ? (
+ shippingPlaces.map((place) => (
+ <SelectItem key={place.code} value={place.code}>
+ {place.code} {place.description && `(${place.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
+ </div>
+ </div>
+ </div>
+
+ {/* 하역지 */}
+ <div className="space-y-4">
+ <div className="space-y-3">
+ <div className="space-y-2">
+ <Label htmlFor="dischargeLocation">하역지</Label>
+ <Select
+ value={formData.dischargeLocation}
+ onValueChange={(value) => setFormData(prev => ({ ...prev, dischargeLocation: value }))}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="하역지를 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {destinationPlaces.length > 0 ? (
+ destinationPlaces.map((place) => (
+ <SelectItem key={place.code} value={place.code}>
+ {place.code} {place.description && `(${place.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
+ </div>
+ </div>
+ </div>
+
+ {/* 계약납기일 */}
+ <div className="space-y-4">
+ <div className="space-y-3">
+ <div className="space-y-2">
+ <Label htmlFor="contractDeliveryDate">계약납기일</Label>
+ <Input
+ type="date"
+ value={formData.contractDeliveryDate}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractDeliveryDate: e.target.value }))}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+ </TabsContent>
+
+ {/* 추가 조건 탭 */}
+ <TabsContent value="additional" className="space-y-6">
+ <Card>
+ <CardHeader>
+ <CardTitle>Additional Conditions (추가조건)</CardTitle>
+ </CardHeader>
+ <CardContent className="space-y-6">
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+ <div className="space-y-2">
+ <Label htmlFor="contractAmount">계약금액 (자동계산)</Label>
+ <Input
+ type="text"
+ value={contract?.contractAmount ? new Intl.NumberFormat('ko-KR').format(Number(contract.contractAmount)) : '품목정보 없음'}
+ readOnly
+ className="bg-gray-50"
+ placeholder="품목정보에서 자동 계산됩니다"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="currency">계약통화 <span className="text-red-600">*</span></Label>
+ <Input
+ type="text"
+ value={formData.currency}
+ onChange={(e) => setFormData(prev => ({ ...prev, currency: e.target.value }))}
+ placeholder="계약통화를 입력하세요"
+ className={errors.currency ? 'border-red-500' : ''}
+ />
+ {errors.currency && (
+ <p className="text-sm text-red-600">계약통화는 필수값입니다.</p>
+ )}
+ </div>
+
+ {/* 계약성립조건 */}
+ <div className="space-y-4 col-span-2">
+ <Label className="text-base font-medium">계약성립조건</Label>
+ <div className="space-y-3">
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="regularVendorRegistration"
+ checked={formData.contractEstablishmentConditions.regularVendorRegistration}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractEstablishmentConditions: { ...prev.contractEstablishmentConditions, regularVendorRegistration: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="regularVendorRegistration">정규업체 등록(실사 포함) 시</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="projectAward"
+ checked={formData.contractEstablishmentConditions.projectAward}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractEstablishmentConditions: { ...prev.contractEstablishmentConditions, projectAward: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="projectAward">프로젝트 수주 시</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="ownerApproval"
+ checked={formData.contractEstablishmentConditions.ownerApproval}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractEstablishmentConditions: { ...prev.contractEstablishmentConditions, ownerApproval: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="ownerApproval">선주 승인 시</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="establishmentOther"
+ checked={formData.contractEstablishmentConditions.other}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractEstablishmentConditions: { ...prev.contractEstablishmentConditions, other: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="establishmentOther">기타</Label>
+ </div>
+ </div>
+ </div>
+
+ {/* 연동제적용 */}
+ <div className="space-y-4">
+ <Label className="text-base font-medium">연동제적용</Label>
+ <div className="space-y-2">
+ <Select value={formData.interlockingSystem} onValueChange={(value) => setFormData(prev => ({ ...prev, interlockingSystem: value }))}>
+ <SelectTrigger>
+ <SelectValue placeholder="연동제적용을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="Y">Y</SelectItem>
+ <SelectItem value="N">N</SelectItem>
+ </SelectContent>
+ </Select>
+ </div>
+ </div>
+
+ {/* 필수문서동의 */}
+ {/* <div className="space-y-4">
+ <Label className="text-base font-medium">필수문서동의</Label>
+ <div className="space-y-3">
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="technicalDataAgreement"
+ checked={formData.mandatoryDocuments.technicalDataAgreement}
+ onChange={(e) => setFormData(prev => ({ ...prev, mandatoryDocuments: { ...prev.mandatoryDocuments, technicalDataAgreement: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="technicalDataAgreement">기술자료제공동의서</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="nda"
+ checked={formData.mandatoryDocuments.nda}
+ onChange={(e) => setFormData(prev => ({ ...prev, mandatoryDocuments: { ...prev.mandatoryDocuments, nda: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="nda">비밀유지계약서(NDA)</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="basicCompliance"
+ checked={formData.mandatoryDocuments.basicCompliance}
+ onChange={(e) => setFormData(prev => ({ ...prev, mandatoryDocuments: { ...prev.mandatoryDocuments, basicCompliance: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="basicCompliance">기본준수서약서</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="safetyHealthAgreement"
+ checked={formData.mandatoryDocuments.safetyHealthAgreement}
+ onChange={(e) => setFormData(prev => ({ ...prev, mandatoryDocuments: { ...prev.mandatoryDocuments, safetyHealthAgreement: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="safetyHealthAgreement">안전보건관리 약정서</Label>
+ </div>
+ </div>
+ </div> */}
+
+ {/* 계약해지조건 */}
+ <div className="space-y-4">
+ <Label className="text-base font-medium">계약해지조건</Label>
+ <div className="space-y-3">
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="standardTermination"
+ checked={formData.contractTerminationConditions.standardTermination}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractTerminationConditions: { ...prev.contractTerminationConditions, standardTermination: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="standardTermination">표준 계약해지조건</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="projectNotAwarded"
+ checked={formData.contractTerminationConditions.projectNotAwarded}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractTerminationConditions: { ...prev.contractTerminationConditions, projectNotAwarded: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="projectNotAwarded">프로젝트 미수주 시</Label>
+ </div>
+ <div className="flex items-center space-x-2">
+ <input
+ type="checkbox"
+ id="terminationOther"
+ checked={formData.contractTerminationConditions.other}
+ onChange={(e) => setFormData(prev => ({ ...prev, contractTerminationConditions: { ...prev.contractTerminationConditions, other: e.target.checked } }))}
+ className="rounded"
+ />
+ <Label htmlFor="terminationOther">기타</Label>
+ </div>
+ </div>
+ </div>
+
+ <div className="space-y-2 col-span-2">
+ <Label htmlFor="notes">비고</Label>
+ <Textarea
+ value={formData.notes}
+ onChange={(e) => setFormData(prev => ({ ...prev, notes: e.target.value }))}
+ placeholder="비고사항을 입력하세요"
+ rows={4}
+ />
+ </div>
+ </div>
+ </CardContent>
+ </Card>
+ </TabsContent>
+
+
+ {/* 계약첨부문서 탭 */}
+ <TabsContent value="documents" className="space-y-6">
+ <ContractDocuments
+ contractId={contractId}
+ userId={userId?.toString() || "1"}
+ />
+ </TabsContent>
+ </Tabs>
+
+ {/* 저장 버튼 */}
+ <div className="flex justify-end mt-6 pt-4 border-t border-gray-200">
+ <Button
+ onClick={handleSaveContractInfo}
+ disabled={isLoading}
+ className="flex items-center gap-2"
+ >
+ {isLoading ? (
+ <LoaderIcon className="w-4 h-4 animate-spin" />
+ ) : (
+ <Save className="w-4 h-4" />
+ )}
+ 계약 정보 저장
+ </Button>
+ </div>
+ </CardContent>
+ </Card>
+ )
+}