Files
sam-react-prod/src/components/business/construction/order-management/OrderDetailForm.tsx
유병철 a1f4c82cec fix: 프로젝트 전체 TypeScript 타입에러 408개 수정 (tsc --noEmit 0 errors)
- 공통 템플릿 타입 수정 (IntegratedDetailTemplate, UniversalListPage)
- 페이지(app/[locale]) 타입 호환성 수정 (80개)
- 재고/자재 모듈 타입 수정 (StockStatus, ReceivingManagement)
- 생산 모듈 타입 수정 (WorkOrders, WorkerScreen, WorkResults)
- 주문/출고 모듈 타입 수정 (ShipmentManagement, Orders)
- 견적/단가 모듈 타입 수정 (Quotes, Pricing)
- 건설 모듈 타입 수정 (49개, 17개 하위 모듈)
- HR 모듈 타입 수정 (CardManagement, VacationManagement 등)
- 설정 모듈 타입 수정 (PermissionManagement, AccountManagement 등)
- 게시판 모듈 타입 수정 (BoardManagement, BoardList 등)
- 회계 모듈 타입 수정 (VendorManagement, BadDebtCollection 등)
- 기타 모듈 타입 수정 (CEODashboard, clients, vehicle 등)
- 유틸/훅/API 타입 수정 (hooks, contexts, lib)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 10:07:58 +09:00

276 lines
9.0 KiB
TypeScript

'use client';
import { useCallback, useMemo } from 'react';
import { useRouter } from 'next/navigation';
import { Plus, Eye, Copy } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { orderConfig } from './orderConfig';
import { toast } from 'sonner';
import type { OrderDetail } from './types';
import { useOrderDetailForm } from './hooks/useOrderDetailForm';
import { updateOrder, deleteOrder } from './actions';
import { OrderInfoCard } from './cards/OrderInfoCard';
import { ContractInfoCard } from './cards/ContractInfoCard';
import { ConstructionDetailCard } from './cards/ConstructionDetailCard';
import { OrderScheduleCard } from './cards/OrderScheduleCard';
import { OrderMemoCard } from './cards/OrderMemoCard';
import { OrderDetailItemTable } from './tables/OrderDetailItemTable';
import { OrderDialogs } from './dialogs/OrderDialogs';
import { OrderDocumentModal } from './modals/OrderDocumentModal';
interface OrderDetailFormProps {
mode: 'view' | 'edit' | 'create';
orderId?: string;
initialData?: OrderDetail;
}
export default function OrderDetailForm({
mode,
orderId,
initialData,
}: OrderDetailFormProps) {
const router = useRouter();
const {
// Mode flags
isViewMode,
isEditMode,
// Form data
formData,
// Loading state
isLoading,
// Dialog states (카테고리 삭제 다이얼로그만 유지)
showCategoryDeleteDialog,
setShowCategoryDeleteDialog,
// Modal states
showDocumentModal,
setShowDocumentModal,
// Selection states
selectedItems,
addCounts,
setAddCounts,
// Calendar states
calendarDate,
selectedCalendarDate,
calendarEvents,
// Form handlers
handleFieldChange,
// CRUD handlers (복제, 발주서 보기만 유지)
handleDuplicate,
handleViewDocument,
// Category handlers
handleAddCategory,
handleCategoryChange,
handleConfirmDeleteCategory,
// Item handlers
handleAddItems,
handleDeleteSelectedItems,
handleDeleteAllItems,
handleItemChange,
// Selection handlers
handleToggleSelection,
handleToggleSelectAll,
// Calendar handlers
handleCalendarDateClick,
handleCalendarMonthChange,
} = useOrderDetailForm({ mode: mode === 'create' ? 'edit' : mode, orderId: orderId ?? '', initialData });
// 저장 핸들러 (IntegratedDetailTemplate용)
const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
try {
const result = await updateOrder(orderId ?? '', formData);
if (result.success) {
toast.success('수정이 완료되었습니다.');
router.push(`/ko/construction/order/order-management/${orderId}?mode=view`);
router.refresh();
return { success: true };
}
return { success: false, error: result.error || '저장에 실패했습니다.' };
} catch (error) {
return { success: false, error: error instanceof Error ? error.message : '저장에 실패했습니다.' };
}
}, [orderId, formData, router]);
// 삭제 핸들러 (IntegratedDetailTemplate용)
const handleDelete = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
try {
const result = await deleteOrder(orderId ?? '');
if (result.success) {
toast.success('발주가 삭제되었습니다.');
router.push('/ko/construction/order/order-management');
router.refresh();
return { success: true };
}
return { success: false, error: result.error || '삭제에 실패했습니다.' };
} catch (error) {
return { success: false, error: error instanceof Error ? error.message : '삭제에 실패했습니다.' };
}
}, [orderId, router]);
// 커스텀 헤더 액션 (view 모드에서 발주서 보기, 복제 버튼)
// 모바일: 아이콘만, sm 이상: 아이콘 + 텍스트
const customHeaderActions = useMemo(() => {
if (!isViewMode) return null;
return (
<>
<Button variant="outline" onClick={handleViewDocument} size="sm" className="md:size-default">
<Eye className="h-4 w-4 md:mr-2" />
<span className="hidden md:inline"> </span>
</Button>
<Button variant="outline" onClick={handleDuplicate} disabled={isLoading} size="sm" className="md:size-default">
<Copy className="h-4 w-4 md:mr-2" />
<span className="hidden md:inline"></span>
</Button>
</>
);
}, [isViewMode, handleViewDocument, handleDuplicate, isLoading]);
// 폼 내용 렌더링 함수 (IntegratedDetailTemplate용)
const renderFormContent = () => (
<div className="space-y-6">
{/* 발주 정보 */}
<OrderInfoCard
formData={formData}
isViewMode={isViewMode}
onFieldChange={handleFieldChange}
/>
{/* 계약 정보 */}
<ContractInfoCard
formData={formData}
isViewMode={isViewMode}
isEditMode={isEditMode}
onFieldChange={handleFieldChange}
/>
{/* 시공 상세 */}
<ConstructionDetailCard
formData={formData}
isViewMode={isViewMode}
onFieldChange={handleFieldChange}
/>
{/* 발주 스케줄 (달력) */}
<OrderScheduleCard
events={calendarEvents}
currentDate={calendarDate}
selectedDate={selectedCalendarDate}
onDateClick={handleCalendarDateClick}
onMonthChange={handleCalendarMonthChange}
/>
{/* 발주 상세 - 카테고리별 섹션 */}
{formData.orderCategories.map((category) => {
const categorySelectedItems = selectedItems.get(category.id) || new Set();
const addCount = addCounts.get(category.id) || 1;
return (
<OrderDetailItemTable
key={category.id}
category={category}
isEditMode={isEditMode}
isViewMode={isViewMode}
selectedItems={categorySelectedItems}
addCount={addCount}
onAddCountChange={(count) => {
setAddCounts((prev) => {
const newMap = new Map(prev);
newMap.set(category.id, count);
return newMap;
});
}}
onAddItems={(count) => handleAddItems(category.id, count)}
onDeleteSelectedItems={() => handleDeleteSelectedItems(category.id)}
onDeleteAllItems={() => handleDeleteAllItems(category.id)}
onCategoryChange={(field, value) =>
handleCategoryChange(category.id, field, value)
}
onItemChange={(itemId, field, value) =>
handleItemChange(category.id, itemId, field, value)
}
onToggleSelection={(itemId) => handleToggleSelection(category.id, itemId)}
onToggleSelectAll={() => handleToggleSelectAll(category.id, category.items)}
/>
);
})}
{/* 발주 카테고리 추가 버튼 */}
{isEditMode && (
<div className="flex justify-center">
<Button variant="outline" onClick={handleAddCategory}>
<Plus className="h-4 w-4 mr-2" />
</Button>
</div>
)}
{/* 비고 */}
<OrderMemoCard
memo={formData.memo}
isViewMode={isViewMode}
onMemoChange={(value) => handleFieldChange('memo', value)}
/>
</div>
);
// 동적 config 설정
// IntegratedDetailTemplate: create → "{title} 등록", view → "{title}", edit → "{title} 수정"
// view 모드에서 "발주 상세"로 표시하려면 직접 설정 필요
const dynamicConfig = {
...orderConfig,
title: isViewMode ? '발주 상세' : '발주',
};
return (
<>
<IntegratedDetailTemplate
config={dynamicConfig}
mode={mode}
initialData={{}}
itemId={orderId}
isLoading={false}
onSubmit={handleSubmit}
onDelete={isViewMode ? handleDelete : undefined}
headerActions={customHeaderActions}
renderView={() => renderFormContent()}
renderForm={() => renderFormContent()}
/>
{/* 카테고리 삭제 다이얼로그 (특수 기능) */}
<OrderDialogs
showSaveDialog={false}
onSaveDialogChange={() => {}}
onConfirmSave={() => {}}
showDeleteDialog={false}
onDeleteDialogChange={() => {}}
onConfirmDelete={() => {}}
showCategoryDeleteDialog={showCategoryDeleteDialog}
onCategoryDeleteDialogChange={setShowCategoryDeleteDialog}
onConfirmDeleteCategory={handleConfirmDeleteCategory}
isLoading={isLoading}
/>
{/* 발주서 보기 모달 (특수 기능) */}
{initialData && (
<OrderDocumentModal
open={showDocumentModal}
onOpenChange={setShowDocumentModal}
order={initialData}
/>
)}
</>
);
}