From ba8cd44a0ed2c613a5f2cee06bfc9bd0f61f21c7 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 7 Nov 2025 08:39:04 +0000
Subject: (최겸) 입찰/견적 수정사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../detail/general-contract-basic-info.tsx         | 488 +++++++++++++++++++--
 1 file changed, 461 insertions(+), 27 deletions(-)

(limited to 'lib/general-contracts/detail/general-contract-basic-info.tsx')

diff --git a/lib/general-contracts/detail/general-contract-basic-info.tsx b/lib/general-contracts/detail/general-contract-basic-info.tsx
index d891fe63..4071b2e0 100644
--- a/lib/general-contracts/detail/general-contract-basic-info.tsx
+++ b/lib/general-contracts/detail/general-contract-basic-info.tsx
@@ -16,6 +16,11 @@ 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'
+import { GENERAL_CONTRACT_SCOPES } from '@/lib/general-contracts/types'
+import { uploadContractAttachment, getContractAttachments, deleteContractAttachment, getContractAttachmentForDownload } from '../service'
+import { downloadFile } from '@/lib/file-download'
+import { FileText, Upload, Download, Trash2 } from 'lucide-react'
+import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
 
 interface ContractBasicInfoProps {
   contractId: number
@@ -38,6 +43,7 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
   const [procurementLoading, setProcurementLoading] = useState(false)
   
   const [formData, setFormData] = useState({
+    contractScope: '', // 계약확정범위
     specificationType: '',
     specificationManualText: '',
     unitPriceType: '',
@@ -83,9 +89,16 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
       projectNotAwarded: false,
       other: false,
     },
+    externalYardEntry: 'N' as 'Y' | 'N', // 사외업체 야드투입 (Y/N)
+    contractAmountReason: '', // 합의계약 미확정 사유
   })
   
   const [errors] = useState<Record<string, string>>({})
+  const [specificationFiles, setSpecificationFiles] = useState<Array<{ id: number; fileName: string; filePath: string; uploadedAt: Date }>>([])
+  const [isLoadingSpecFiles, setIsLoadingSpecFiles] = useState(false)
+  const [showSpecFileDialog, setShowSpecFileDialog] = useState(false)
+  const [unitPriceTypeOther, setUnitPriceTypeOther] = useState<string>('') // 단가 유형 '기타' 수기입력
+  const [showYardEntryConfirmDialog, setShowYardEntryConfirmDialog] = useState(false)
 
   // 계약 데이터 로드
   React.useEffect(() => {
@@ -121,7 +134,13 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
         
         setPaymentDeliveryPercent(paymentDeliveryPercentValue)
         
+        // 합의계약(AD, AW)인 경우 인도조건 기본값 설정
+        const defaultDeliveryTerm = (contractData?.type === 'AD' || contractData?.type === 'AW') 
+          ? '본 표준하도급 계약에 따름'
+          : (contractData?.deliveryTerm || '')
+        
         setFormData({
+          contractScope: contractData?.contractScope || '',
           specificationType: contractData?.specificationType || '',
           specificationManualText: contractData?.specificationManualText || '',
           unitPriceType: contractData?.unitPriceType || '',
@@ -145,10 +164,11 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
           liquidatedDamages: Boolean(contractData?.liquidatedDamages),
           liquidatedDamagesPercent: contractData?.liquidatedDamagesPercent || '',
           deliveryType: contractData?.deliveryType || '',
-          deliveryTerm: contractData?.deliveryTerm || '',
+          deliveryTerm: defaultDeliveryTerm,
           shippingLocation: contractData?.shippingLocation || '',
           dischargeLocation: contractData?.dischargeLocation || '',
           contractDeliveryDate: contractData?.contractDeliveryDate || '',
+          paymentDeliveryAdditionalText: (contractData as any)?.paymentDeliveryAdditionalText || '',
           contractEstablishmentConditions: contractEstablishmentConditions || {
             regularVendorRegistration: false,
             projectAward: false,
@@ -167,6 +187,8 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
             projectNotAwarded: false,
             other: false,
           },
+          externalYardEntry: (contractData?.externalYardEntry as 'Y' | 'N') || 'N',
+          contractAmountReason: (contractData as any)?.contractAmountReason || '',
         })
       } catch (error) {
         console.error('Error loading contract:', error)
@@ -179,6 +201,33 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
     }
   }, [contractId])
 
+  // 사양 파일 목록 로드
+  React.useEffect(() => {
+    const loadSpecificationFiles = async () => {
+      if (!contractId || formData.specificationType !== '첨부서류 참조') return
+      
+      setIsLoadingSpecFiles(true)
+      try {
+        const attachments = await getContractAttachments(contractId)
+        const specFiles = (attachments as Array<{ id: number; fileName: string; filePath: string; documentName: string; uploadedAt: Date }>)
+          .filter(att => att.documentName === '사양 및 공급범위' || att.documentName === 'specification')
+          .map(att => ({
+            id: att.id,
+            fileName: att.fileName,
+            filePath: att.filePath,
+            uploadedAt: att.uploadedAt
+          }))
+        setSpecificationFiles(specFiles)
+      } catch (error) {
+        console.error('Error loading specification files:', error)
+      } finally {
+        setIsLoadingSpecFiles(false)
+      }
+    }
+    
+    loadSpecificationFiles()
+  }, [contractId, formData.specificationType])
+
   // Procurement 데이터 로드 함수들
   const loadPaymentTerms = React.useCallback(async () => {
     setProcurementLoading(true);
@@ -249,7 +298,16 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
       
       // 필수값 validation 체크
       const validationErrors: string[] = []
+      if (!formData.contractScope) validationErrors.push('계약확정범위')
       if (!formData.specificationType) validationErrors.push('사양')
+      // 첨부서류 참조 선택 시 사양 파일 필수 체크
+      if (formData.specificationType === '첨부서류 참조' && specificationFiles.length === 0) {
+        validationErrors.push('사양 파일')
+      }
+      // LO 계약인 경우 계약체결유효기간 필수값 체크
+      if (contract?.type === 'LO' && !contract?.validityEndDate) {
+        validationErrors.push('계약체결유효기간')
+      }
       if (!formData.paymentDelivery) validationErrors.push('납품 지급조건')
       if (!formData.currency) validationErrors.push('계약통화')
       if (!formData.paymentTerm) validationErrors.push('지불조건')
@@ -294,6 +352,35 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
 
       {/* 기본 정보 탭 */}
       <TabsContent value="basic" className="space-y-6">
+        {/* 계약확정범위 */}
+        <Card>
+          <CardHeader>
+            <CardTitle>계약확정범위</CardTitle>
+          </CardHeader>
+          <CardContent>
+            <div className="grid gap-4">
+              <div className="flex flex-col gap-2">
+                <Label htmlFor="contractScope">계약확정범위 *</Label>
+                <Select
+                  value={formData.contractScope}
+                  onValueChange={(value) => setFormData(prev => ({ ...prev, contractScope: value }))}
+                >
+                  <SelectTrigger>
+                    <SelectValue placeholder="계약확정범위 선택" />
+                  </SelectTrigger>
+                  <SelectContent>
+                    {GENERAL_CONTRACT_SCOPES.map((scope) => (
+                      <SelectItem key={scope} value={scope}>
+                        {scope}
+                      </SelectItem>
+                    ))}
+                  </SelectContent>
+                </Select>
+              </div>
+            </div>
+          </CardContent>
+        </Card>
+        
         <Card>
       {/* 보증기간 및 단가유형 */}
         <CardHeader>
@@ -509,7 +596,7 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
                   <SelectValue placeholder="사양을 선택하세요" />
                 </SelectTrigger>
                 <SelectContent>
-                  <SelectItem value="첨부파일">첨부파일</SelectItem>
+                  <SelectItem value="첨부서류 참조">첨부서류 참조</SelectItem>
                   <SelectItem value="표준사양">표준사양</SelectItem>
                   <SelectItem value="수기사양">수기사양</SelectItem>
                 </SelectContent>
@@ -520,9 +607,26 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
             </div>
             {/* 단가 */}
             <div className="flex flex-col gap-2">
-              <Label htmlFor="unitPriceType">단가 유형</Label>
+              <Label htmlFor="unitPriceType">
+                단가 유형
+                {(() => {
+                  const contractType = contract?.type as string || ''
+                  const contractCategory = contract?.category as string || ''
+                  const unitPriceContractTypes = ['UP', 'LE', 'IL', 'AL', 'OS', 'OW']
+                  const isUnitPriceRequired = contractCategory === 'unit_price' && unitPriceContractTypes.includes(contractType)
+                  return isUnitPriceRequired ? <span className="text-red-600 ml-1">*</span> : null
+                })()}
+              </Label>
               <Select value={formData.unitPriceType} onValueChange={(value) => setFormData(prev => ({ ...prev, unitPriceType: value }))}>
-                <SelectTrigger>
+                <SelectTrigger className={
+                  (() => {
+                    const contractType = contract?.type as string || ''
+                    const contractCategory = contract?.category as string || ''
+                    const unitPriceContractTypes = ['UP', 'LE', 'IL', 'AL', 'OS', 'OW']
+                    const isUnitPriceRequired = contractCategory === 'unit_price' && unitPriceContractTypes.includes(contractType)
+                    return isUnitPriceRequired && !formData.unitPriceType ? 'border-red-500' : ''
+                  })()
+                }>
                   <SelectValue placeholder="단가 유형을 선택하세요" />
                 </SelectTrigger>
                 <SelectContent>
@@ -535,6 +639,29 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
                   <SelectItem value="기타">기타</SelectItem>
                 </SelectContent>
               </Select>
+              {/* 단가 유형 '기타' 선택 시 수기입력 필드 */}
+              {formData.unitPriceType === '기타' && (
+                <div className="mt-2">
+                  <Input
+                    value={unitPriceTypeOther}
+                    onChange={(e) => setUnitPriceTypeOther(e.target.value)}
+                    placeholder="단가 유형을 수기로 입력하세요"
+                    className="mt-2"
+                    required
+                  />
+                </div>
+              )}
+              {(() => {
+                const contractType = contract?.type as string || ''
+                const contractCategory = contract?.category as string || ''
+                const unitPriceContractTypes = ['UP', 'LE', 'IL', 'AL', 'OS', 'OW']
+                const isUnitPriceRequired = contractCategory === 'unit_price' && unitPriceContractTypes.includes(contractType)
+                return isUnitPriceRequired && !formData.unitPriceType ? (
+                  <p className="text-sm text-red-600">단가 유형은 필수값입니다.</p>
+                ) : formData.unitPriceType === '기타' && !unitPriceTypeOther.trim() ? (
+                  <p className="text-sm text-red-600">단가 유형(기타)을 입력해주세요.</p>
+                ) : null
+              })()}
             </div>
             {/* 선택에 따른 폼: vertical로 출력 */}
         
@@ -552,6 +679,187 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
             </div>
           )}
 
+          {/* 사양이 첨부서류 참조일 때 파일 업로드 */}
+          {formData.specificationType === '첨부서류 참조' && (
+            <div className="flex flex-col gap-2">
+              <div className="flex items-center justify-between">
+                <Label htmlFor="specificationFile">
+                  사양 파일 <span className="text-red-600">*</span>
+                </Label>
+                <Dialog open={showSpecFileDialog} onOpenChange={setShowSpecFileDialog}>
+                  <DialogTrigger asChild>
+                    <Button variant="outline" size="sm" type="button">
+                      <Upload className="h-4 w-4 mr-2" />
+                      파일 업로드
+                    </Button>
+                  </DialogTrigger>
+                  <DialogContent className="max-w-2xl">
+                    <DialogHeader>
+                      <DialogTitle>사양 파일 업로드</DialogTitle>
+                    </DialogHeader>
+                    <div className="space-y-4">
+                      <div>
+                        <Label htmlFor="file-upload">파일 선택</Label>
+                        <Input
+                          id="file-upload"
+                          type="file"
+                          onChange={async (e) => {
+                            const file = e.target.files?.[0]
+                            if (!file || !userId) return
+                            
+                            try {
+                              setIsLoadingSpecFiles(true)
+                              const result = await uploadContractAttachment(
+                                contractId,
+                                file,
+                                userId.toString(),
+                                '사양 및 공급범위'
+                              )
+                              
+                              if (result.success) {
+                                toast.success('사양 파일이 업로드되었습니다.')
+                                // 파일 목록 새로고침
+                                const attachments = await getContractAttachments(contractId)
+                                const specFiles = (attachments as Array<{ id: number; fileName: string; filePath: string; documentName: string; uploadedAt: Date }>)
+                                  .filter(att => att.documentName === '사양 및 공급범위' || att.documentName === 'specification')
+                                  .map(att => ({
+                                    id: att.id,
+                                    fileName: att.fileName,
+                                    filePath: att.filePath,
+                                    uploadedAt: att.uploadedAt
+                                  }))
+                                setSpecificationFiles(specFiles)
+                                setShowSpecFileDialog(false)
+                                e.target.value = ''
+                              } else {
+                                toast.error(result.error || '파일 업로드에 실패했습니다.')
+                              }
+                            } catch (error) {
+                              console.error('Error uploading file:', error)
+                              toast.error('파일 업로드 중 오류가 발생했습니다.')
+                            } finally {
+                              setIsLoadingSpecFiles(false)
+                            }
+                          }}
+                          disabled={isLoadingSpecFiles}
+                        />
+                      </div>
+                      
+                      {/* 업로드된 파일 목록 */}
+                      {specificationFiles.length > 0 && (
+                        <div className="space-y-2">
+                          <Label>업로드된 파일</Label>
+                          <div className="space-y-2 max-h-60 overflow-y-auto">
+                            {specificationFiles.map((file) => (
+                              <div key={file.id} className="flex items-center justify-between p-2 border rounded">
+                                <div className="flex items-center gap-2">
+                                  <FileText className="h-4 w-4 text-muted-foreground" />
+                                  <span className="text-sm">{file.fileName}</span>
+                                  <span className="text-xs text-muted-foreground">
+                                    ({new Date(file.uploadedAt).toLocaleDateString()})
+                                  </span>
+                                </div>
+                                <div className="flex items-center gap-2">
+                                  <Button
+                                    variant="ghost"
+                                    size="sm"
+                                    onClick={async () => {
+                                      try {
+                                        const fileData = await getContractAttachmentForDownload(file.id, contractId)
+                                        downloadFile(fileData.attachment?.filePath || '', fileData.attachment?.fileName || '', {
+                                          showToast: true
+                                        })
+                                      } catch (error) {
+                                        console.error('Error downloading file:', error)
+                                        toast.error('파일 다운로드 중 오류가 발생했습니다.')
+                                      }
+                                    }}
+                                  >
+                                    <Download className="h-4 w-4" />
+                                  </Button>
+                                  <Button
+                                    variant="ghost"
+                                    size="sm"
+                                    onClick={async () => {
+                                      try {
+                                        await deleteContractAttachment(file.id, contractId)
+                                        toast.success('파일이 삭제되었습니다.')
+                                        setSpecificationFiles(prev => prev.filter(f => f.id !== file.id))
+                                      } catch (error) {
+                                        console.error('Error deleting file:', error)
+                                        toast.error('파일 삭제 중 오류가 발생했습니다.')
+                                      }
+                                    }}
+                                    className="text-red-600 hover:text-red-700"
+                                  >
+                                    <Trash2 className="h-4 w-4" />
+                                  </Button>
+                                </div>
+                              </div>
+                            ))}
+                          </div>
+                        </div>
+                      )}
+                    </div>
+                  </DialogContent>
+                </Dialog>
+              </div>
+              
+              {specificationFiles.length === 0 && (
+                <p className="text-sm text-red-600">사양 파일을 업로드해주세요.</p>
+              )}
+              
+              {specificationFiles.length > 0 && (
+                <div className="space-y-2">
+                  {specificationFiles.map((file) => (
+                    <div key={file.id} className="flex items-center justify-between p-2 border rounded text-sm">
+                      <div className="flex items-center gap-2">
+                        <FileText className="h-4 w-4 text-muted-foreground" />
+                        <span>{file.fileName}</span>
+                      </div>
+                      <div className="flex items-center gap-2">
+                        <Button
+                          variant="ghost"
+                          size="sm"
+                          onClick={async () => {
+                            try {
+                              const fileData = await getContractAttachmentForDownload(file.id, contractId)
+                              downloadFile(fileData.attachment?.filePath || '', fileData.attachment?.fileName || '', {
+                                showToast: true
+                              })
+                            } catch (error) {
+                              console.error('Error downloading file:', error)
+                              toast.error('파일 다운로드 중 오류가 발생했습니다.')
+                            }
+                          }}
+                        >
+                          <Download className="h-4 w-4" />
+                        </Button>
+                        <Button
+                          variant="ghost"
+                          size="sm"
+                          onClick={async () => {
+                            try {
+                              await deleteContractAttachment(file.id, contractId)
+                              toast.success('파일이 삭제되었습니다.')
+                              setSpecificationFiles(prev => prev.filter(f => f.id !== file.id))
+                            } catch (error) {
+                              console.error('Error deleting file:', error)
+                              toast.error('파일 삭제 중 오류가 발생했습니다.')
+                            }
+                          }}
+                          className="text-red-600 hover:text-red-700"
+                        >
+                          <Trash2 className="h-4 w-4" />
+                        </Button>
+                      </div>
+                    </div>
+                  ))}
+                </div>
+              )}
+            </div>
+          )}
+
           </div>
 
           
@@ -664,6 +972,37 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
                       disabled={!formData.paymentBeforeDelivery.materialPurchase}
                     />
                   </div>
+                  <div className="flex items-center space-x-2">
+                    <input
+                      type="checkbox"
+                      id="additionalConditionBefore"
+                      checked={formData.paymentBeforeDelivery.additionalCondition || false}
+                      onChange={(e) => setFormData(prev => ({ 
+                        ...prev, 
+                        paymentBeforeDelivery: { 
+                          ...prev.paymentBeforeDelivery, 
+                          additionalCondition: e.target.checked 
+                        } 
+                      }))}
+                      className="rounded"
+                    />
+                    <Label htmlFor="additionalConditionBefore" className="text-sm">추가조건</Label>
+                    <Input
+                      type="number"
+                      min="0"
+                      placeholder="%"
+                      className="w-16"
+                      value={formData.paymentBeforeDelivery.additionalConditionPercent || ''}
+                      onChange={(e) => setFormData(prev => ({
+                        ...prev,
+                        paymentBeforeDelivery: {
+                          ...prev.paymentBeforeDelivery,
+                          additionalConditionPercent: e.target.value
+                        }
+                      }))}
+                      disabled={!formData.paymentBeforeDelivery.additionalCondition}
+                    />
+                  </div>
                 </div>
               </div>
 
@@ -678,26 +1017,26 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
                         <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>
+                        {/* Payment term 검색 옵션들 */}
+                        {paymentTermsOptions.map((term) => (
+                          <SelectItem key={term.code} value={term.code}>
+                            {term.code} - {term.description}
+                          </SelectItem>
+                        ))}
+                        <SelectItem value="납품완료일로부터 60일 이내 지급">납품완료일로부터 60일 이내 지급</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">
+                    {/* 추가조건 선택 시 수기 입력 필드 */}
+                    {formData.paymentDelivery === '추가조건' && (
+                      <div className="mt-2">
                         <Input
-                          type="number"
-                          min="0"
-                          value={paymentDeliveryPercent}
-                          onChange={(e) => setPaymentDeliveryPercent(e.target.value)}
-                          placeholder="퍼센트"
-                          className="w-20 h-8 text-sm"
+                          type="text"
+                          value={formData.paymentDeliveryAdditionalText || ''}
+                          onChange={(e) => setFormData(prev => ({ ...prev, paymentDeliveryAdditionalText: e.target.value }))}
+                          placeholder="추가조건을 입력하세요"
+                          className="w-full"
                         />
-                        <span className="text-sm text-gray-600">%</span>
                       </div>
                     )}
                     {errors.paymentDelivery && (
@@ -1036,13 +1375,34 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
             <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="품목정보에서 자동 계산됩니다"
-                />
+                {contract?.type === 'AD' || contract?.type === 'AW' ? (
+                  <div className="space-y-2">
+                    <Input
+                      type="text"
+                      value="미확정"
+                      readOnly
+                      className="bg-gray-50"
+                    />
+                    <div className="flex flex-col gap-2">
+                      <Label htmlFor="contractAmountReason">미확정 사유</Label>
+                      <Textarea
+                        id="contractAmountReason"
+                        value={formData.contractAmountReason}
+                        onChange={(e) => setFormData(prev => ({ ...prev, contractAmountReason: e.target.value }))}
+                        placeholder="계약금액 미확정 사유를 입력하세요"
+                        rows={3}
+                      />
+                    </div>
+                  </div>
+                ) : (
+                  <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>
@@ -1058,6 +1418,80 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
                 )}
               </div>
 
+              {/* 사외업체 야드투입 */}
+              <div className="space-y-4 col-span-2">
+                <Label className="text-base font-medium">사외업체 야드투입</Label>
+                <div className="flex items-center space-x-4">
+                  <div className="flex items-center space-x-2">
+                    <input
+                      type="radio"
+                      id="yardEntryYes"
+                      name="externalYardEntry"
+                      value="Y"
+                      checked={formData.externalYardEntry === 'Y'}
+                      onChange={(e) => setFormData(prev => ({ ...prev, externalYardEntry: 'Y' as 'Y' | 'N' }))}
+                      className="rounded"
+                    />
+                    <Label htmlFor="yardEntryYes">Y</Label>
+                  </div>
+                  <div className="flex items-center space-x-2">
+                    <input
+                      type="radio"
+                      id="yardEntryNo"
+                      name="externalYardEntry"
+                      value="N"
+                      checked={formData.externalYardEntry === 'N'}
+                      onChange={(e) => {
+                        // 이전 값이 'Y'였고 'N'으로 변경하는 경우 팝업 표시
+                        if (formData.externalYardEntry === 'Y') {
+                          setShowYardEntryConfirmDialog(true)
+                        } else {
+                          setFormData(prev => ({ ...prev, externalYardEntry: 'N' as 'Y' | 'N' }))
+                        }
+                      }}
+                      className="rounded"
+                    />
+                    <Label htmlFor="yardEntryNo">N</Label>
+                  </div>
+                </div>
+                {/* 사외업체 야드투입 'N' 선택 시 확인 팝업 */}
+                <Dialog open={showYardEntryConfirmDialog} onOpenChange={setShowYardEntryConfirmDialog}>
+                  <DialogContent>
+                    <DialogHeader>
+                      <DialogTitle>안전필수사항 확인</DialogTitle>
+                    </DialogHeader>
+                    <div className="space-y-4">
+                      <p className="text-sm text-muted-foreground">
+                        안전필수사항으로 사내작업여부를 재확인 바랍니다.
+                      </p>
+                      <div className="flex justify-end gap-2">
+                        <Button
+                          variant="outline"
+                          onClick={() => {
+                            setShowYardEntryConfirmDialog(false)
+                            // 라디오 버튼을 다시 'Y'로 되돌림
+                            const yardEntryYesRadio = document.getElementById('yardEntryYes') as HTMLInputElement
+                            if (yardEntryYesRadio) {
+                              yardEntryYesRadio.checked = true
+                            }
+                          }}
+                        >
+                          취소
+                        </Button>
+                        <Button
+                          onClick={() => {
+                            setFormData(prev => ({ ...prev, externalYardEntry: 'N' as 'Y' | 'N' }))
+                            setShowYardEntryConfirmDialog(false)
+                          }}
+                        >
+                          확인
+                        </Button>
+                      </div>
+                    </div>
+                  </DialogContent>
+                </Dialog>
+              </div>
+
               {/* 계약성립조건 */}
               <div className="space-y-4 col-span-2">
                 <Label className="text-base font-medium">계약성립조건</Label>
-- 
cgit v1.2.3

