'use client'; /** * 제품검사 등록 페이지 * * 기획서 기반 전면 재구축: * - 기본정보 입력 * - 건축공사장, 자재유통업자, 공사시공자, 공사감리자 정보 * - 검사 정보 (검사방문요청일, 기간, 검사자, 현장주소) * - 수주 설정 정보 (수주 선택 → 규격 비교 테이블) */ import { useState, useCallback, useMemo } from 'react'; import { useRouter } from 'next/navigation'; import { Plus, Trash2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate'; import { qualityInspectionCreateConfig } from './inspectionConfig'; import { toast } from 'sonner'; import { createInspection } from './actions'; import { isOrderSpecSame, calculateOrderSummary } from './mockData'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import { OrderSelectModal } from './OrderSelectModal'; import type { InspectionFormData, OrderSettingItem, OrderSelectItem } from './types'; import { emptyConstructionSite, emptyMaterialDistributor, emptyConstructor, emptySupervisor, emptyScheduleInfo, } from './mockData'; export function InspectionCreate() { const router = useRouter(); // 폼 상태 const [formData, setFormData] = useState({ qualityDocNumber: '', siteName: '', client: '', manager: '', managerContact: '', constructionSite: { ...emptyConstructionSite }, materialDistributor: { ...emptyMaterialDistributor }, constructorInfo: { ...emptyConstructor }, supervisor: { ...emptySupervisor }, scheduleInfo: { ...emptyScheduleInfo }, orderItems: [], }); const [isSubmitting, setIsSubmitting] = useState(false); const [orderModalOpen, setOrderModalOpen] = useState(false); // ===== 수주 선택 처리 ===== const handleOrderSelect = useCallback((items: OrderSelectItem[]) => { const newOrderItems: OrderSettingItem[] = items.map((item) => ({ id: item.id, orderNumber: item.orderNumber, floor: '', symbol: '', orderWidth: 0, orderHeight: 0, constructionWidth: 0, constructionHeight: 0, changeReason: '', })); setFormData((prev) => ({ ...prev, orderItems: [...prev.orderItems, ...newOrderItems], })); }, []); // ===== 수주 항목 삭제 ===== const handleRemoveOrderItem = useCallback((itemId: string) => { setFormData((prev) => ({ ...prev, orderItems: prev.orderItems.filter((item) => item.id !== itemId), })); }, []); // ===== 폼 필드 변경 헬퍼 ===== const updateField = useCallback(( key: K, value: InspectionFormData[K] ) => { setFormData((prev) => ({ ...prev, [key]: value })); }, []); const updateNested = useCallback(( section: 'constructionSite' | 'materialDistributor' | 'constructorInfo' | 'supervisor' | 'scheduleInfo', field: string, value: string ) => { setFormData((prev) => ({ ...prev, [section]: { ...(prev[section] as unknown as Record), [field]: value, }, })); }, []); // ===== 수주 설정 요약 ===== const orderSummary = useMemo( () => calculateOrderSummary(formData.orderItems), [formData.orderItems] ); // ===== 취소 ===== const handleCancel = useCallback(() => { router.push('/quality/inspections'); }, [router]); // ===== 등록 제출 ===== const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => { // 필수 필드 검증 if (!formData.siteName.trim()) { toast.error('현장명은 필수 입력 항목입니다.'); return { success: false, error: '현장명을 입력해주세요.' }; } if (!formData.client.trim()) { toast.error('수주처는 필수 입력 항목입니다.'); return { success: false, error: '수주처를 입력해주세요.' }; } setIsSubmitting(true); try { const result = await createInspection(formData); if (result.success) { toast.success('제품검사가 등록되었습니다.'); router.push('/quality/inspections'); return { success: true }; } return { success: false, error: result.error || '등록에 실패했습니다.' }; } catch (error) { if (isNextRedirectError(error)) throw error; return { success: false, error: '등록 중 오류가 발생했습니다.' }; } finally { setIsSubmitting(false); } }, [formData, router]); // ===== 수주 설정 테이블 ===== const renderOrderTable = (items: OrderSettingItem[]) => ( No. 수주번호 층수 부호 수주 가로 수주 세로 시공 가로 시공 세로 일치 변경사유 삭제 {items.map((item, index) => { const isSame = isOrderSpecSame(item); return ( {index + 1} {item.orderNumber} {item.floor} {item.symbol} {item.orderWidth} {item.orderHeight} {item.constructionWidth} {item.constructionHeight} {isSame ? ( 일치 ) : ( 불일치 )} {item.changeReason || '-'} ); })} {items.length === 0 && ( 수주를 선택해주세요. )}
); // ===== 폼 렌더링 ===== const renderFormContent = useCallback(() => (
{/* 기본 정보 */} 기본 정보
updateField('qualityDocNumber', e.target.value)} placeholder="품질관리서 번호 입력" />
updateField('siteName', e.target.value)} placeholder="현장명 입력" />
updateField('client', e.target.value)} placeholder="수주처 입력" />
updateField('manager', e.target.value)} placeholder="담당자 입력" />
updateField('managerContact', e.target.value)} placeholder="담당자 연락처 입력" />
{/* 건축공사장 정보 */} 건축공사장 정보
updateNested('constructionSite', 'siteName', e.target.value)} placeholder="현장명 입력" />
updateNested('constructionSite', 'landLocation', e.target.value)} placeholder="대지위치 입력" />
updateNested('constructionSite', 'lotNumber', e.target.value)} placeholder="지번 입력" />
{/* 자재유통업자 정보 */} 자재유통업자 정보
updateNested('materialDistributor', 'companyName', e.target.value)} placeholder="회사명 입력" />
updateNested('materialDistributor', 'companyAddress', e.target.value)} placeholder="회사주소 입력" />
updateNested('materialDistributor', 'representativeName', e.target.value)} placeholder="대표자명 입력" />
updateNested('materialDistributor', 'phone', e.target.value)} placeholder="전화번호 입력" />
{/* 공사시공자 정보 */} 공사시공자 정보
updateNested('constructorInfo', 'companyName', e.target.value)} placeholder="회사명 입력" />
updateNested('constructorInfo', 'companyAddress', e.target.value)} placeholder="회사주소 입력" />
updateNested('constructorInfo', 'name', e.target.value)} placeholder="성명 입력" />
updateNested('constructorInfo', 'phone', e.target.value)} placeholder="전화번호 입력" />
{/* 공사감리자 정보 */} 공사감리자 정보
updateNested('supervisor', 'officeName', e.target.value)} placeholder="사무소명 입력" />
updateNested('supervisor', 'officeAddress', e.target.value)} placeholder="사무소주소 입력" />
updateNested('supervisor', 'name', e.target.value)} placeholder="성명 입력" />
updateNested('supervisor', 'phone', e.target.value)} placeholder="전화번호 입력" />
{/* 검사 정보 */} 검사 정보
updateNested('scheduleInfo', 'visitRequestDate', e.target.value)} />
updateNested('scheduleInfo', 'startDate', e.target.value)} />
updateNested('scheduleInfo', 'endDate', e.target.value)} />
updateNested('scheduleInfo', 'inspector', e.target.value)} placeholder="검사자 입력" />
{/* 현장 주소 */}
updateNested('scheduleInfo', 'sitePostalCode', e.target.value)} className="w-28" />
updateNested('scheduleInfo', 'siteAddress', e.target.value)} placeholder="주소 입력" />
updateNested('scheduleInfo', 'siteAddressDetail', e.target.value)} placeholder="상세주소 입력" />
{/* 수주 설정 정보 */}
수주 설정 정보
전체: {orderSummary.total} 일치: {orderSummary.same} 불일치: {orderSummary.changed}
{renderOrderTable(formData.orderItems)}
), [formData, orderSummary, updateField, updateNested, handleRemoveOrderItem, orderModalOpen]); // 이미 선택된 수주 ID 목록 const excludeOrderIds = useMemo( () => formData.orderItems.map((item) => item.id), [formData.orderItems] ); return ( <> ); }