'use client'; import { useState, useCallback, useMemo } from 'react'; import { useRouter } from 'next/navigation'; import { Plus, X, Eye, Stamp } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { DatePicker } from '@/components/ui/date-picker'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { QuantityInput } from '@/components/ui/quantity-input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Checkbox } from '@/components/ui/checkbox'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate'; import { handoverReportConfig } from './handoverReportConfig'; import { toast } from 'sonner'; import type { HandoverReportDetail, HandoverReportFormData, HandoverStatus, ConstructionManager, } from './types'; import { HANDOVER_STATUS_LABELS, CONSTRUCTION_PM_OPTIONS, MANAGER_OPTIONS, getEmptyHandoverReportFormData, handoverReportDetailToFormData, } from './types'; import { updateHandoverReport, deleteHandoverReport } from './actions'; import { HandoverReportDocumentModal } from './modals/HandoverReportDocumentModal'; import { ElectronicApprovalModal, type ElectronicApproval, getEmptyElectronicApproval, } from '../common'; import { formatNumber } from '@/utils/formatAmount'; interface HandoverReportDetailFormProps { mode: 'view' | 'edit'; reportId: string; initialData?: HandoverReportDetail; } export default function HandoverReportDetailForm({ mode, reportId, initialData, }: HandoverReportDetailFormProps) { const router = useRouter(); const isViewMode = mode === 'view'; const isEditMode = mode === 'edit'; // 폼 데이터 const [formData, setFormData] = useState( initialData ? handoverReportDetailToFormData(initialData) : getEmptyHandoverReportFormData() ); // 로딩 상태 const [isLoading, setIsLoading] = useState(false); // 모달 상태 const [showDocumentModal, setShowDocumentModal] = useState(false); const [showApprovalModal, setShowApprovalModal] = useState(false); // 전자결재 데이터 const [approvalData, setApprovalData] = useState( getEmptyElectronicApproval() ); // 폼 필드 변경 const handleFieldChange = useCallback( (field: keyof HandoverReportFormData, value: string | number | boolean) => { setFormData((prev) => ({ ...prev, [field]: value })); }, [] ); // 저장 핸들러 (IntegratedDetailTemplate용) const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => { try { const result = await updateHandoverReport(reportId, formData); if (result.success) { toast.success('수정이 완료되었습니다.'); router.push(`/ko/construction/project/contract/handover-report/${reportId}?mode=view`); router.refresh(); return { success: true }; } return { success: false, error: result.error || '저장에 실패했습니다.' }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : '저장에 실패했습니다.' }; } }, [router, reportId, formData]); // 삭제 핸들러 (IntegratedDetailTemplate용) const handleDelete = useCallback(async (): Promise<{ success: boolean; error?: string }> => { try { const result = await deleteHandoverReport(reportId); if (result.success) { toast.success('인수인계보고서가 삭제되었습니다.'); router.push('/ko/construction/project/contract/handover-report'); router.refresh(); return { success: true }; } return { success: false, error: result.error || '삭제에 실패했습니다.' }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : '삭제에 실패했습니다.' }; } }, [router, reportId]); // 인수인계보고서 보기 핸들러 const handleViewDocument = useCallback(() => { setShowDocumentModal(true); }, []); // 전자결재 핸들러 const handleApproval = useCallback(() => { setShowApprovalModal(true); }, []); // 전자결재 저장 const handleApprovalSave = useCallback((approval: ElectronicApproval) => { setApprovalData(approval); setShowApprovalModal(false); toast.success('전자결재 정보가 저장되었습니다.'); }, []); // 커스텀 헤더 액션 (view 모드에서 인수인계보고서 보기, 전자결재 버튼) const customHeaderActions = useMemo(() => { if (!isViewMode) return null; return ( <> ); }, [isViewMode, handleViewDocument, handleApproval]); // 공사담당자 추가 const handleAddManager = useCallback(() => { const newManager: ConstructionManager = { id: String(Date.now()), name: '', nonPerformanceReason: '', }; setFormData((prev) => ({ ...prev, constructionManagers: [...prev.constructionManagers, newManager], })); }, []); // 공사담당자 삭제 const handleRemoveManager = useCallback((managerId: string) => { setFormData((prev) => ({ ...prev, constructionManagers: prev.constructionManagers.filter((m) => m.id !== managerId), })); }, []); // 공사담당자 변경 const handleManagerChange = useCallback( (managerId: string, field: keyof ConstructionManager, value: string | boolean) => { setFormData((prev) => ({ ...prev, constructionManagers: prev.constructionManagers.map((m) => m.id === managerId ? { ...m, [field]: value } : m ), })); }, [] ); // 장비 외 실행금액 변경 const handleEquipmentCostChange = useCallback( (field: 'shippingCost' | 'highAltitudeWork' | 'publicExpense', value: number) => { setFormData((prev) => ({ ...prev, externalEquipmentCost: { ...prev.externalEquipmentCost, [field]: value, }, })); }, [] ); // 계약 ITEM 비고 변경 const handleContractItemRemarkChange = useCallback((itemId: string, remark: string) => { setFormData((prev) => ({ ...prev, contractItems: prev.contractItems.map((item) => item.id === itemId ? { ...item, remark } : item ), })); }, []); // 폼 내용 렌더링 함수 (IntegratedDetailTemplate용) const renderFormContent = () => (
{/* 인수인계 정보 */} 인수인계 정보
{/* 보고서번호 */}
handleFieldChange('reportNumber', e.target.value)} disabled={isViewMode} />
{/* 계약담당자 */}
handleFieldChange('contractManagerName', e.target.value)} disabled={isViewMode} />
{/* 거래처명 */}
handleFieldChange('partnerName', e.target.value)} disabled={isViewMode} />
{/* 현장명 */}
handleFieldChange('siteName', e.target.value)} disabled={isViewMode} />
{/* 계약일자 */}
handleFieldChange('contractDate', date)} disabled={isViewMode} />
{/* 개소 */}
handleFieldChange('totalSites', value ?? 0)} disabled={isViewMode} />
{/* 계약기간 */}
handleFieldChange('contractStartDate', date)} disabled={isViewMode} /> ~ handleFieldChange('contractEndDate', date)} disabled={isViewMode} />
{/* 계약금액 (공급가액) */}
{ const value = e.target.value.replace(/[^0-9]/g, ''); handleFieldChange('contractAmount', parseInt(value) || 0); }} disabled={isViewMode} />
{/* 공사PM */}
{/* 상태 */}
handleFieldChange('status', value as HandoverStatus)} disabled={isViewMode} className="flex gap-4" >
{/* 공사담당자 */} 공사담당자 {isEditMode && ( )} 번호 공사담당자 미이행 사유 {isEditMode && } {formData.constructionManagers.length === 0 ? ( 등록된 공사담당자가 없습니다. ) : ( formData.constructionManagers.map((manager, index) => ( {index + 1} {isEditMode ? ( ) : ( manager.name )} {isEditMode ? ( handleManagerChange(manager.id, 'nonPerformanceReason', e.target.value) } placeholder="미이행 사유 입력" /> ) : ( manager.nonPerformanceReason || '-' )} {isEditMode && ( )} )) )}
{/* 계약 ITEM */} 계약 ITEM 번호 명칭 제품 수량 비고 {formData.contractItems.length === 0 ? ( 등록된 계약 ITEM이 없습니다. ) : ( formData.contractItems.map((item) => ( {item.no} {item.name} {item.product} {formatNumber(item.quantity)} {isEditMode ? ( handleContractItemRemarkChange(item.id, e.target.value)} placeholder="비고 입력" /> ) : ( item.remark || '-' )} )) )}
{/* 상세 정보 */} 상세 정보
{/* 2차 배관 유무 / 도장 & 코킹 유무 */}
{/* 2차 배관 유무 */}
{ handleFieldChange('hasSecondaryPiping', value === 'included'); if (value !== 'included') { handleFieldChange('secondaryPipingNote', ''); } }} disabled={isViewMode} className="flex gap-4 shrink-0" >
handleFieldChange('secondaryPipingNote', e.target.value)} disabled={isViewMode || !formData.hasSecondaryPiping} placeholder="2차 배관 내용 입력" className={`flex-1 transition-opacity duration-200 ${ formData.hasSecondaryPiping ? 'opacity-100' : 'opacity-0 pointer-events-none' }`} />
{/* 도장 & 코킹 유무 */}
{ handleFieldChange('hasCoating', value === 'included'); if (value !== 'included') { handleFieldChange('coatingNote', ''); } }} disabled={isViewMode} className="flex gap-4 shrink-0" >
handleFieldChange('coatingNote', e.target.value)} disabled={isViewMode || !formData.hasCoating} placeholder="도장 & 코킹 내용 입력" className={`flex-1 transition-opacity duration-200 ${ formData.hasCoating ? 'opacity-100' : 'opacity-0 pointer-events-none' }`} />
{/* 장비 외 실행금액 */}
0} onCheckedChange={(checked) => handleEquipmentCostChange('shippingCost', checked ? 1500000 : 0) } disabled={isViewMode} />
0} onCheckedChange={(checked) => handleEquipmentCostChange('highAltitudeWork', checked ? 800000 : 0) } disabled={isViewMode} />
0} onCheckedChange={(checked) => handleEquipmentCostChange('publicExpense', checked ? 10000000 : 0) } disabled={isViewMode} />
{/* 특이사항 */}