'use client'; /** * 검사 상세/수정 페이지 * API 연동 완료 (2025-12-26) * IntegratedDetailTemplate 마이그레이션 완료 (2026-01-20) */ import { useState, useCallback, useEffect, useMemo } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { Printer, Paperclip, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { NumberInput } from '@/components/ui/number-input'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate'; import { inspectionConfig } from './inspectionConfig'; import { ServerErrorPage } from '@/components/common/ServerErrorPage'; import { toast } from 'sonner'; import { getInspectionById, updateInspection } from './actions'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import { judgmentColorMap, judgeMeasurement } from './mockData'; import type { Inspection, InspectionItem, QualityCheckItem, MeasurementItem } from './types'; interface InspectionDetailProps { id: string; } export function InspectionDetail({ id }: InspectionDetailProps) { const router = useRouter(); const searchParams = useSearchParams(); const isEditMode = searchParams.get('mode') === 'edit'; // 검사 데이터 상태 const [inspection, setInspection] = useState(null); const [isLoading, setIsLoading] = useState(true); // 수정 폼 상태 const [editReason, setEditReason] = useState(''); const [inspectionItems, setInspectionItems] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); // validation 에러 상태 const [validationErrors, setValidationErrors] = useState([]); // API로 검사 데이터 로드 const loadInspection = useCallback(async () => { setIsLoading(true); try { const result = await getInspectionById(id); if (result.success && result.data) { setInspection(result.data); setInspectionItems(result.data.items || []); } else { toast.error(result.error || '검사 데이터를 불러오는데 실패했습니다.'); } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[InspectionDetail] loadInspection error:', error); toast.error('검사 데이터 로드 중 오류가 발생했습니다.'); } finally { setIsLoading(false); } }, [id]); // 컴포넌트 마운트 시 데이터 로드 useEffect(() => { loadInspection(); }, [loadInspection]); // 품질 검사 항목 결과 변경 (양호/불량) const handleQualityResultChange = useCallback((itemId: string, result: '양호' | '불량') => { setInspectionItems(prev => prev.map(item => { if (item.id === itemId && item.type === 'quality') { return { ...item, result, judgment: result === '양호' ? '적합' : '부적합', } as QualityCheckItem; } return item; })); // 입력 시 에러 클리어 setValidationErrors([]); }, []); // 측정 항목 값 변경 const handleMeasurementChange = useCallback((itemId: string, value: string) => { setInspectionItems(prev => prev.map(item => { if (item.id === itemId && item.type === 'measurement') { const measuredValue = parseFloat(value) || 0; const judgment = judgeMeasurement(item.spec, measuredValue); return { ...item, measuredValue, judgment, } as MeasurementItem; } return item; })); // 입력 시 에러 클리어 setValidationErrors([]); }, []); // 목록으로 const handleBack = () => { router.push('/quality/inspections'); }; // 수정 모드 진입 const handleEditMode = () => { router.push(`/quality/inspections/${id}?mode=edit`); }; // 수정 취소 const handleCancelEdit = () => { router.push(`/quality/inspections/${id}`); }; // validation 체크 const validateForm = (): boolean => { const errors: string[] = []; // 필수 필드: 수정 사유 if (!editReason.trim()) { errors.push('수정 사유는 필수 입력 항목입니다.'); } // 검사 항목 validation inspectionItems.forEach((item, index) => { if (item.type === 'quality') { const qualityItem = item as QualityCheckItem; if (!qualityItem.result) { errors.push(`${index + 1}. ${item.name}: 결과를 선택해주세요.`); } } else if (item.type === 'measurement') { const measurementItem = item as MeasurementItem; if (measurementItem.measuredValue === undefined || measurementItem.measuredValue === null) { errors.push(`${index + 1}. ${item.name}: 측정값을 입력해주세요.`); } } }); setValidationErrors(errors); return errors.length === 0; }; // 수정 완료 const handleSubmitEdit = async () => { // validation 체크 if (!validateForm()) { return; } setIsSubmitting(true); try { const result = await updateInspection(id, { items: inspectionItems, remarks: editReason, }); if (result.success) { toast.success('검사가 수정되었습니다.'); router.push(`/quality/inspections/${id}`); } else { toast.error(result.error || '검사 수정에 실패했습니다.'); } } catch (error) { if (isNextRedirectError(error)) throw error; console.error('[InspectionDetail] handleSubmitEdit error:', error); toast.error('검사 수정 중 오류가 발생했습니다.'); } finally { setIsSubmitting(false); } }; // 수정 사유 변경 핸들러 const handleEditReasonChange = (value: string) => { setEditReason(value); // 입력 시 에러 클리어 if (validationErrors.length > 0) { setValidationErrors([]); } }; // 성적서 출력 const handlePrintReport = () => { // TODO: 성적서 출력 기능 console.log('Print Report'); }; // 저장 핸들러 (IntegratedDetailTemplate용) const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => { // validation 체크 if (!validateForm()) { return { success: false, error: '입력 내용을 확인해주세요.' }; } try { const result = await updateInspection(id, { items: inspectionItems, remarks: editReason, }); if (result.success) { toast.success('검사가 수정되었습니다.'); router.push(`/quality/inspections/${id}`); return { success: true }; } return { success: false, error: result.error || '검사 수정에 실패했습니다.' }; } catch (error) { if (isNextRedirectError(error)) throw error; return { success: false, error: '검사 수정 중 오류가 발생했습니다.' }; } }, [id, inspectionItems, editReason, router, validateForm]); // 모드 결정 const mode = isEditMode ? 'edit' : 'view'; // 동적 config (모드에 따른 타이틀 변경) const dynamicConfig = useMemo(() => { if (isEditMode) { return { ...inspectionConfig, title: '검사 수정', }; } return inspectionConfig; }, [isEditMode]); // 커스텀 헤더 액션 (view 모드에서 성적서 버튼) const customHeaderActions = useMemo(() => { if (isEditMode) return null; return ( ); }, [isEditMode]); // View 모드 폼 내용 렌더링 const renderViewContent = () => { if (!inspection) return null; return (
{/* 검사 정보 */} 검사 정보

{inspection.inspectionNo}

{inspection.inspectionType}

{inspection.inspectionDate || '-'}

{inspection.result && ( {inspection.result} )} {!inspection.result && '-'}

{inspection.itemName}

{inspection.lotNo}

{inspection.processName}

{inspection.inspector || '-'}

{/* 검사 결과 데이터 */} 검사 결과 데이터 항목명 기준(Spec) 측정값/결과 판정 {inspection.items.map((item) => ( {item.name} {item.spec} {item.type === 'quality' ? (item as QualityCheckItem).result || '-' : `${(item as MeasurementItem).measuredValue || '-'} ${(item as MeasurementItem).unit}` } {item.judgment || '-'} ))} {inspection.items.length === 0 && ( 검사 데이터가 없습니다. )}
{/* 종합 의견 */} 종합 의견

{inspection.opinion || '의견이 없습니다.'}

{/* 첨부 파일 */} 첨부 파일 {inspection.attachments && inspection.attachments.length > 0 ? (
{inspection.attachments.map((file) => ( ))}
) : (

첨부 파일이 없습니다.

)}
); }; // Edit 모드 폼 내용 렌더링 const renderFormContent = () => { if (!inspection) return null; return (
{/* Validation 에러 표시 */} {validationErrors.length > 0 && (
⚠️
입력 내용을 확인해주세요 ({validationErrors.length}개 오류)
    {validationErrors.map((error, index) => (
  • {error}
  • ))}
)} {/* 검사 개요 (수정 불가) */} 검사 개요 (수정 불가)
{/* 수정 사유 */} 수정 사유 (필수 )