From 535de26b9cf3242c04543d6891d78352b9593a60 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Tue, 11 Nov 2025 09:22:58 +0000 Subject: (최겸) 구매 수정사항 개발 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/general-contract-basic-info.tsx | 28 +- .../main/create-general-contract-dialog.tsx | 5 + .../main/general-contract-update-sheet.tsx | 20 +- .../general-contract-approval-request-dialog.tsx | 1312 ------------- .../detail/general-contract-basic-info.tsx | 1250 ------------- .../general-contract-communication-channel.tsx | 362 ---- .../detail/general-contract-detail.tsx | 186 -- .../detail/general-contract-documents.tsx | 383 ---- .../detail/general-contract-field-service-rate.tsx | 288 --- .../detail/general-contract-info-header.tsx | 211 --- .../detail/general-contract-items-table.tsx | 602 ------ .../detail/general-contract-location.tsx | 480 ----- .../detail/general-contract-offset-details.tsx | 314 ---- .../general-contract-subcontract-checklist.tsx | 610 ------ .../main/create-general-contract-dialog.tsx | 413 ----- .../main/general-contract-update-sheet.tsx | 401 ---- .../main/general-contracts-table-columns.tsx | 571 ------ .../general-contracts-table-toolbar-actions.tsx | 124 -- .../main/general-contracts-table.tsx | 217 --- lib/general-contracts_old/service.ts | 1933 -------------------- lib/general-contracts_old/types.ts | 125 -- lib/general-contracts_old/validation.ts | 82 - lib/mail/templates/risks-notification.hbs | 2 +- lib/pq/service.ts | 89 +- lib/pq/table/copy-pq-list-dialog.tsx | 2 +- lib/rfq-last/attachment/vendor-response-table.tsx | 2 +- lib/rfq-last/quotation-compare-view.tsx | 46 +- lib/rfq-last/table/rfq-table-columns.tsx | 2 +- lib/rfq-last/table/rfq-table.tsx | 2 +- lib/rfq-last/validations.ts | 2 +- .../vendor/batch-update-conditions-dialog.tsx | 112 +- lib/rfq-last/vendor/rfq-vendor-table.tsx | 56 +- lib/rfq-last/vendor/send-rfq-dialog.tsx | 8 +- lib/site-visit/client-site-visit-wrapper.tsx | 16 +- lib/site-visit/vendor-info-sheet.tsx | 66 +- lib/site-visit/vendor-info-view-dialog.tsx | 22 +- .../rfq-history-table/rfq-history-table.tsx | 4 +- lib/vendors/table/request-pq-dialog.tsx | 67 +- 38 files changed, 317 insertions(+), 10098 deletions(-) delete mode 100644 lib/general-contracts_old/detail/general-contract-approval-request-dialog.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-basic-info.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-communication-channel.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-detail.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-documents.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-field-service-rate.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-info-header.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-items-table.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-location.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-offset-details.tsx delete mode 100644 lib/general-contracts_old/detail/general-contract-subcontract-checklist.tsx delete mode 100644 lib/general-contracts_old/main/create-general-contract-dialog.tsx delete mode 100644 lib/general-contracts_old/main/general-contract-update-sheet.tsx delete mode 100644 lib/general-contracts_old/main/general-contracts-table-columns.tsx delete mode 100644 lib/general-contracts_old/main/general-contracts-table-toolbar-actions.tsx delete mode 100644 lib/general-contracts_old/main/general-contracts-table.tsx delete mode 100644 lib/general-contracts_old/service.ts delete mode 100644 lib/general-contracts_old/types.ts delete mode 100644 lib/general-contracts_old/validation.ts (limited to 'lib') diff --git a/lib/general-contracts/detail/general-contract-basic-info.tsx b/lib/general-contracts/detail/general-contract-basic-info.tsx index 4071b2e0..fb08da87 100644 --- a/lib/general-contracts/detail/general-contract-basic-info.tsx +++ b/lib/general-contracts/detail/general-contract-basic-info.tsx @@ -53,18 +53,19 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { 작업후: { enabled: false, period: 0, maxPeriod: 0 }, 기타: { enabled: false, period: 0, maxPeriod: 0 }, }, - contractAmount: null as number | null, + contractAmount: null, currency: 'KRW', linkedPoNumber: '', linkedBidNumber: '', notes: '', // 개별 JSON 필드들 (스키마에 맞게) - paymentBeforeDelivery: {} as any, + paymentBeforeDelivery: {}, paymentDelivery: '', // varchar 타입 - paymentAfterDelivery: {} as any, + paymentDeliveryAdditionalText: '', + paymentAfterDelivery: {}, paymentTerm: '', taxType: '', - liquidatedDamages: false as boolean, + liquidatedDamages: false, liquidatedDamagesPercent: '', deliveryType: '', deliveryTerm: '', @@ -89,7 +90,7 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { projectNotAwarded: false, other: false, }, - externalYardEntry: 'N' as 'Y' | 'N', // 사외업체 야드투입 (Y/N) + externalYardEntry: 'N', // 사외업체 야드투입 (Y/N) contractAmountReason: '', // 합의계약 미확정 사유 }) @@ -309,7 +310,10 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { validationErrors.push('계약체결유효기간') } if (!formData.paymentDelivery) validationErrors.push('납품 지급조건') - if (!formData.currency) validationErrors.push('계약통화') + // 계약확정범위가 '단가' 또는 '물량(실적)'이 아닌 경우에만 계약통화 필수값 체크 + if (formData.contractScope !== '단가' && formData.contractScope !== '물량(실적)' && !formData.currency) { + validationErrors.push('계약통화') + } if (!formData.paymentTerm) validationErrors.push('지불조건') if (!formData.taxType) validationErrors.push('세금조건') @@ -1375,7 +1379,7 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) {
- {contract?.type === 'AD' || contract?.type === 'AW' ? ( + {(contract?.type === 'AD' || contract?.type === 'AW') || formData.contractScope === '단가' || formData.contractScope === '물량(실적)' ? (
- + setFormData(prev => ({ ...prev, currency: e.target.value }))} placeholder="계약통화를 입력하세요" - className={errors.currency ? 'border-red-500' : ''} + className={`${errors.currency ? 'border-red-500' : ''} ${formData.contractScope === '단가' || formData.contractScope === '물량(실적)' ? 'bg-gray-50' : ''}`} + disabled={formData.contractScope === '단가' || formData.contractScope === '물량(실적)'} /> - {errors.currency && ( + {errors.currency && formData.contractScope !== '단가' && formData.contractScope !== '물량(실적)' && (

계약통화는 필수값입니다.

)}
diff --git a/lib/general-contracts/main/create-general-contract-dialog.tsx b/lib/general-contracts/main/create-general-contract-dialog.tsx index 168b8cbc..04f70834 100644 --- a/lib/general-contracts/main/create-general-contract-dialog.tsx +++ b/lib/general-contracts/main/create-general-contract-dialog.tsx @@ -205,6 +205,11 @@ export function CreateGeneralContractDialog() { onChange={(e) => setForm(prev => ({ ...prev, name: e.target.value }))} placeholder="계약명을 입력하세요" /> + {form.type === 'SC' && ( +

+ 납품예정 품목 및 수량을 명기하세요. 납품 품목 또는 작업 내용은 구체적으로 작성하되, 수량(물량)이 정확하지 않을 경우, 상호협의하에 변경 가능하며, 수량(물량) 등은 개별계약(PO)시 명기하세요 +

+ )}
diff --git a/lib/general-contracts/main/general-contract-update-sheet.tsx b/lib/general-contracts/main/general-contract-update-sheet.tsx index 18095516..074558ec 100644 --- a/lib/general-contracts/main/general-contract-update-sheet.tsx +++ b/lib/general-contracts/main/general-contract-update-sheet.tsx @@ -46,8 +46,8 @@ const updateContractSchema = z.object({ name: z.string().min(1, "계약명을 입력해주세요"), startDate: z.string().optional(), // AD, LO, OF 계약인 경우 선택사항 endDate: z.string().optional(), // AD, LO, OF 계약인 경우 선택사항 - validityEndDate: z.string().optional(), // LO 계약인 경우에만 필수값으로 처리 - contractScope: z.string().min(1, "계약확정범위를 선택해주세요"), + validityEndDate: z.string().optional(), + contractScope: z.string().optional(), notes: z.string().optional(), linkedRfqOrItb: z.string().optional(), linkedPoNumber: z.string().optional(), @@ -70,15 +70,6 @@ const updateContractSchema = z.object({ }) } } - - // LO 계약인 경우 계약체결유효기간 필수값 체크 - if (data.type === 'LO' && !data.validityEndDate) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "LO 계약의 경우 계약체결유효기간은 필수 항목입니다", - path: ["validityEndDate"], - }) - } }) type UpdateContractFormData = z.infer @@ -355,10 +346,7 @@ export function GeneralContractUpdateSheet({ name="validityEndDate" render={({ field }) => ( - - 유효기간종료일 - {form.watch('type') === 'LO' && *} - + 유효기간종료일 @@ -373,7 +361,7 @@ export function GeneralContractUpdateSheet({ name="contractScope" render={({ field }) => ( - 계약확정범위 * + 계약확정범위 { - const file = e.target.files?.[0] - if (file) handleFileUpload(file) - }} - /> -

- Word 문서(.doc, .docx) 파일만 업로드 가능합니다. -

-
- - {/* ContractDocuments 컴포넌트 사용 */} - {/*
- - -
*/} - - {uploadedFile && ( -
-
- - 업로드 완료 -
-

{uploadedFile.name}

-
- )} - - - - -
- - -
- - - {/* 4단계: PDF 미리보기 */} - - - - - - PDF 미리보기 - - - - {!generatedPdfUrl ? ( -
- -
- ) : ( -
-
-
- - PDF 생성 완료 -
-
- -
-
-

생성된 PDF

-
- - -
-
- - {/* PDF 미리보기 영역 */} -
- {isPdfPreviewVisible ? ( - <> -
- -
-
- - ) : ( -
-
- -

미리보기 버튼을 클릭하여 PDF를 확인하세요

-
-
- )} -
-
-
- )} - - - -
- - -
- - - - - )} \ No newline at end of file diff --git a/lib/general-contracts_old/detail/general-contract-basic-info.tsx b/lib/general-contracts_old/detail/general-contract-basic-info.tsx deleted file mode 100644 index d891fe63..00000000 --- a/lib/general-contracts_old/detail/general-contract-basic-info.tsx +++ /dev/null @@ -1,1250 +0,0 @@ -'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(null) - const userId = session.data?.user?.id ? Number(session.data.user.id) : null - - // 독립적인 상태 관리 - const [paymentDeliveryPercent, setPaymentDeliveryPercent] = useState('') - - // Procurement 데이터 상태들 - const [paymentTermsOptions, setPaymentTermsOptions] = useState>([]) - const [incotermsOptions, setIncotermsOptions] = useState>([]) - const [shippingPlaces, setShippingPlaces] = useState>([]) - const [destinationPlaces, setDestinationPlaces] = useState>([]) - 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>({}) - - // 계약 데이터 로드 - 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 ( - - - 계약 기본 정보 - - - - - 기본 정보 - 지급/인도 조건 - 추가 조건 - 계약첨부문서 - - - {/* 기본 정보 탭 */} - - - {/* 보증기간 및 단가유형 */} - - 보증기간 및 단가유형 - - - {/* 3그리드: 보증기간, 사양, 단가 */} -
- {/* 보증기간 */} -
- -
-
- setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 납품후: { - ...prev.warrantyPeriod.납품후, - enabled: e.target.checked - } - } - }))} - className="rounded" - /> - -
- {formData.warrantyPeriod.납품후?.enabled && ( -
- setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 납품후: { - ...prev.warrantyPeriod.납품후, - period: parseInt(e.target.value) || 0 - } - } - }))} - className="w-20 h-8 text-sm" - /> - 개월, 최대 - setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 납품후: { - ...prev.warrantyPeriod.납품후, - maxPeriod: parseInt(e.target.value) || 0 - } - } - }))} - className="w-20 h-8 text-sm" - /> - 개월 -
- )} - -
- setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 인도후: { - ...prev.warrantyPeriod.인도후, - enabled: e.target.checked - } - } - }))} - className="rounded" - /> - -
- {formData.warrantyPeriod.인도후?.enabled && ( -
- setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 인도후: { - ...prev.warrantyPeriod.인도후, - period: parseInt(e.target.value) || 0 - } - } - }))} - className="w-20 h-8 text-sm" - /> - 개월, 최대 - setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 인도후: { - ...prev.warrantyPeriod.인도후, - maxPeriod: parseInt(e.target.value) || 0 - } - } - }))} - className="w-20 h-8 text-sm" - /> - 개월 -
- )} - -
- setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 작업후: { - ...prev.warrantyPeriod.작업후, - enabled: e.target.checked - } - } - }))} - className="rounded" - /> - -
- {formData.warrantyPeriod.작업후?.enabled && ( -
- setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 작업후: { - ...prev.warrantyPeriod.작업후, - period: parseInt(e.target.value) || 0 - } - } - }))} - className="w-20 h-8 text-sm" - /> - 개월, 최대 - setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 작업후: { - ...prev.warrantyPeriod.작업후, - maxPeriod: parseInt(e.target.value) || 0 - } - } - }))} - className="w-20 h-8 text-sm" - /> - 개월 -
- )} - -
- setFormData(prev => ({ - ...prev, - warrantyPeriod: { - ...prev.warrantyPeriod, - 기타: { - ...prev.warrantyPeriod.기타, - enabled: e.target.checked - } - } - }))} - className="rounded" - /> - -
-
-
- {/* 사양 */} -
- - - {errors.specificationType && ( -

사양은 필수값입니다.

- )} -
- {/* 단가 */} -
- - -
- {/* 선택에 따른 폼: vertical로 출력 */} - - - {/* 사양이 수기사양일 때 매뉴얼 텍스트 */} - {formData.specificationType === '수기사양' && ( -
- -