- 공통 템플릿 타입 수정 (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>
276 lines
9.0 KiB
TypeScript
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}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
} |