diff --git a/claudedocs/[IMPL-2025-01-20] IntegratedDetailTemplate-migration-checklist.md b/claudedocs/[IMPL-2025-01-20] IntegratedDetailTemplate-migration-checklist.md
new file mode 100644
index 00000000..de02ecb0
--- /dev/null
+++ b/claudedocs/[IMPL-2025-01-20] IntegratedDetailTemplate-migration-checklist.md
@@ -0,0 +1,151 @@
+# IntegratedDetailTemplate 마이그레이션 체크리스트
+
+> 작성일: 2025-01-20
+> 목적: 별도 디자인 사용 중인 등록/수정 페이지를 IntegratedDetailTemplate으로 통합
+
+---
+
+## 마이그레이션 완료
+
+### Phase 1 - 기안함 (2025-01-20)
+- [x] DocumentCreate (기안함 등록/수정)
+ - 파일: `src/components/approval/DocumentCreate/index.tsx`
+ - config: `src/components/approval/DocumentCreate/documentCreateConfig.ts`
+ - 특이사항: 커스텀 headerActions (미리보기, 삭제, 상신, 임시저장)
+
+### Phase 2 - 생산관리 (2025-01-20)
+- [x] WorkOrderCreate (작업지시 등록)
+ - 파일: `src/components/production/WorkOrders/WorkOrderCreate.tsx`
+ - config: `src/components/production/WorkOrders/workOrderConfig.ts`
+- [x] WorkOrderEdit (작업지시 수정)
+ - 파일: `src/components/production/WorkOrders/WorkOrderEdit.tsx`
+ - config: 동일 파일 (workOrderEditConfig)
+
+### Phase 3 - 출고관리 (2025-01-20)
+- [x] ShipmentCreate (출하 등록)
+ - 파일: `src/components/outbound/ShipmentManagement/ShipmentCreate.tsx`
+ - config: `src/components/outbound/ShipmentManagement/shipmentConfig.ts`
+- [x] ShipmentEdit (출하 수정)
+ - 파일: `src/components/outbound/ShipmentManagement/ShipmentEdit.tsx`
+ - config: 동일 파일 (shipmentEditConfig)
+
+### Phase 4 - HR (2025-01-20)
+- [x] EmployeeForm (사원 등록/수정/상세)
+ - 파일: `src/components/hr/EmployeeManagement/EmployeeForm.tsx`
+ - config: `src/components/hr/EmployeeManagement/employeeConfig.ts`
+ - 특이사항: "항목 설정" 버튼, 복잡한 섹션 구조, view 모드 지원
+
+### Phase 5 - 게시판 (2025-01-20)
+- [x] BoardForm (게시판 글쓰기/수정)
+ - 파일: `src/components/board/BoardForm/index.tsx`
+ - config: `src/components/board/BoardForm/boardFormConfig.ts`
+ - 특이사항: 동적 게시판 코드 기반
+
+### Phase 6 - 고객센터 (2025-01-20)
+- [x] InquiryForm (문의 등록/수정)
+ - 파일: `src/components/customer-center/InquiryManagement/InquiryForm.tsx`
+ - config: `src/components/customer-center/InquiryManagement/inquiryConfig.ts`
+
+### Phase 7 - 기준정보 (2025-01-20)
+- [x] ProcessForm (공정 등록/수정)
+ - 파일: `src/components/process-management/ProcessForm.tsx`
+ - config: `src/components/process-management/processConfig.ts`
+
+### Phase 8 - 자재/품질 (2025-01-20)
+- [x] InspectionCreate - 자재 (수입검사 등록)
+ - 파일: `src/components/material/ReceivingManagement/InspectionCreate.tsx`
+ - config: `src/components/material/ReceivingManagement/inspectionConfig.ts`
+- [x] InspectionCreate - 품질 (품질검사 등록)
+ - 파일: `src/components/quality/InspectionManagement/InspectionCreate.tsx`
+ - config: `src/components/quality/InspectionManagement/inspectionConfig.ts`
+
+---
+
+## 마이그레이션 제외 (특수 레이아웃)
+
+| 페이지 | 사유 |
+|--------|------|
+| CEO 대시보드 | 대시보드 (특수 레이아웃) |
+| 생산 대시보드 | 대시보드 (특수 레이아웃) |
+| 작업자 화면 | 특수 UI |
+| 설정 페이지들 | 트리 구조, 특수 레이아웃 |
+| 부서 관리 | 트리 구조 |
+| 일일보고서 | 특수 레이아웃 |
+| 미수금현황 | 특수 레이아웃 |
+| 종합분석 | 특수 레이아웃 |
+
+---
+
+## 마이그레이션 패턴
+
+### 1. Config 파일 생성/수정
+```typescript
+export const xxxCreateConfig: DetailConfig = {
+ title: '페이지 제목',
+ description: '페이지 설명',
+ icon: IconComponent,
+ basePath: '/base/path',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true, // false로 설정하면 기본 저장 버튼 숨김
+ submitLabel: '등록',
+ },
+};
+```
+
+### 2. 컴포넌트 수정
+```typescript
+// 변경 전
+import { PageLayout } from '@/components/organisms/PageLayout';
+return ...;
+
+// 변경 후
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+const renderFormContent = useCallback(() => (...), [deps]);
+return (
+
+);
+```
+
+### 3. 커스텀 버튼이 필요한 경우
+- config에서 `showSave: false` 설정
+- `headerActions` prop으로 커스텀 버튼 전달
+
+---
+
+## 검증 URL
+
+마이그레이션 완료 후 확인할 URL:
+- `/approval/draft/new` - 기안함 등록
+- `/production/work-orders/create` - 작업지시 등록
+- `/outbound/shipments/new` - 출하 등록
+- `/hr/employee-management/new` - 사원 등록
+- `/boards/notice/create` - 게시판 글쓰기
+
+---
+
+## 변경 이력
+
+| 날짜 | 작업 내용 |
+|------|----------|
+| 2025-01-20 | 기안함 마이그레이션 완료 |
+| 2025-01-20 | 작업지시 등록/수정 마이그레이션 완료 |
+| 2025-01-20 | DetailActions에 showSave 옵션 추가 |
+| 2025-01-20 | 출하 등록/수정 마이그레이션 완료 |
+| 2025-01-20 | 사원 등록/수정/상세 마이그레이션 완료 |
+| 2025-01-20 | 게시판 글쓰기/수정 마이그레이션 완료 |
+| 2025-01-20 | 1:1 문의 등록/수정 마이그레이션 완료 |
+| 2025-01-20 | 공정 등록/수정 마이그레이션 완료 |
+| 2025-01-20 | 수입검사(IQC) 등록 마이그레이션 완료 |
+| 2025-01-20 | 품질검사(PQC) 등록 마이그레이션 완료 |
diff --git a/src/components/approval/DocumentCreate/documentCreateConfig.ts b/src/components/approval/DocumentCreate/documentCreateConfig.ts
new file mode 100644
index 00000000..cdbd9463
--- /dev/null
+++ b/src/components/approval/DocumentCreate/documentCreateConfig.ts
@@ -0,0 +1,34 @@
+/**
+ * 기안 문서 작성/수정 페이지 설정
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+
+import { FileText } from 'lucide-react';
+import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
+
+export const documentCreateConfig: DetailConfig = {
+ title: '문서 작성',
+ description: '새로운 결재 문서를 작성합니다',
+ icon: FileText,
+ basePath: '/approval/draft',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false, // 커스텀 삭제 버튼 사용
+ showSave: false, // 상신/임시저장 버튼 사용
+ },
+};
+
+export const documentEditConfig: DetailConfig = {
+ ...documentCreateConfig,
+ title: '문서 수정',
+ description: '기존 결재 문서를 수정합니다',
+ // actions는 documentCreateConfig에서 상속 (커스텀 버튼 사용)
+};
+
+export const documentCopyConfig: DetailConfig = {
+ ...documentCreateConfig,
+ title: '문서 복제',
+ description: '복제된 문서를 수정 후 상신합니다',
+};
diff --git a/src/components/approval/DocumentCreate/index.tsx b/src/components/approval/DocumentCreate/index.tsx
index 6f091ff4..75d89f93 100644
--- a/src/components/approval/DocumentCreate/index.tsx
+++ b/src/components/approval/DocumentCreate/index.tsx
@@ -3,13 +3,16 @@
import { useState, useCallback, useEffect, useTransition, useRef } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { format } from 'date-fns';
-import { FileText, Trash2, Send, Save, ArrowLeft, Eye } from 'lucide-react';
-import { Loader2 } from 'lucide-react';
-import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
+import { Trash2, Send, Save, Eye, Loader2 } from 'lucide-react';
import { toast } from 'sonner';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import {
+ documentCreateConfig,
+ documentEditConfig,
+ documentCopyConfig,
+} from './documentCreateConfig';
import {
getExpenseEstimateItems,
- getEmployees,
createApproval,
createAndSubmitApproval,
getApprovalById,
@@ -18,7 +21,6 @@ import {
deleteApproval,
} from './actions';
import { Button } from '@/components/ui/button';
-import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { BasicInfoSection } from './BasicInfoSection';
import { ApprovalLineSection } from './ApprovalLineSection';
import { ReferenceSection } from './ReferenceSection';
@@ -33,7 +35,6 @@ import type {
ExpenseEstimateDocumentData,
} from '@/components/approval/DocumentDetail/types';
import type {
- DocumentType,
BasicInfo,
ApprovalPerson,
ProposalData,
@@ -416,7 +417,7 @@ export function DocumentCreate() {
approvers,
drafter,
};
- default:
+ default: {
// 이미 업로드된 파일 URL (Next.js 프록시 사용) + 새로 추가된 파일 미리보기 URL
const uploadedFileUrls = (proposalData.uploadedFiles || []).map(f =>
`/api/proxy/files/${f.id}/download`
@@ -436,6 +437,7 @@ export function DocumentCreate() {
approvers,
drafter,
};
+ }
}
}, [basicInfo, approvalLine, proposalData, expenseReportData, expenseEstimateData]);
@@ -453,75 +455,63 @@ export function DocumentCreate() {
}
};
- // 문서 로딩 중
- if (isLoadingDocument) {
+ // 현재 모드에 맞는 config 선택
+ const currentConfig = isEditMode
+ ? documentEditConfig
+ : isCopyMode
+ ? documentCopyConfig
+ : documentCreateConfig;
+
+ // 헤더 액션 버튼 렌더링
+ const renderHeaderActions = useCallback(() => {
return (
-
- );
- }
-
- return (
-
- {/* 헤더 */}
-
-
-
-
-
-
-
-
- {isEditMode ? '문서 수정' : isCopyMode ? '문서 복제' : '문서 작성'}
-
- {isEditMode ? '기존 문서를 수정합니다' : isCopyMode ? '복제된 문서를 수정 후 상신합니다' : '새로운 문서를 작성합니다'}
-
-
-
-
-
-
-
-
- {/* 액션 버튼 (스텝) */}
-
-
+ >
);
}
diff --git a/src/components/board/BoardForm/boardFormConfig.ts b/src/components/board/BoardForm/boardFormConfig.ts
new file mode 100644
index 00000000..0b1270c6
--- /dev/null
+++ b/src/components/board/BoardForm/boardFormConfig.ts
@@ -0,0 +1,36 @@
+'use client';
+
+import { FileText } from 'lucide-react';
+import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
+
+/**
+ * 게시글 등록 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const boardCreateConfig: DetailConfig = {
+ title: '게시글 등록',
+ description: '새로운 게시글을 등록합니다',
+ icon: FileText,
+ basePath: '/boards',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '등록',
+ },
+};
+
+/**
+ * 게시글 수정 페이지 Config
+ */
+export const boardEditConfig: DetailConfig = {
+ ...boardCreateConfig,
+ title: '게시글 수정',
+ description: '게시글을 수정합니다',
+ actions: {
+ ...boardCreateConfig.actions,
+ submitLabel: '저장',
+ },
+};
diff --git a/src/components/board/BoardForm/index.tsx b/src/components/board/BoardForm/index.tsx
index ed4e4782..bcf03bbd 100644
--- a/src/components/board/BoardForm/index.tsx
+++ b/src/components/board/BoardForm/index.tsx
@@ -2,6 +2,7 @@
/**
* 게시글 등록/수정 폼 컴포넌트
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
*
* 디자인 스펙 기준:
* - 페이지 타이틀: 게시글 상세
@@ -12,10 +13,10 @@
import { useState, useCallback, useRef, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { format } from 'date-fns';
-import { FileText, Upload, X, File, ArrowLeft, Save, Loader2 } from 'lucide-react';
+import { Upload, X, File, Loader2 } from 'lucide-react';
import { toast } from 'sonner';
-import { PageLayout } from '@/components/organisms/PageLayout';
-import { PageHeader } from '@/components/organisms/PageHeader';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { boardCreateConfig, boardEditConfig } from './boardFormConfig';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
@@ -212,31 +213,9 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};
- return (
-
- {/* 헤더 */}
-
-
-
- 취소
-
-
- {isSubmitting ? (
-
- ) : (
-
- )}
- {mode === 'create' ? '등록' : '수정'}
-
-
- }
- />
-
+ // ===== 폼 콘텐츠 렌더링 =====
+ const renderFormContent = useCallback(() => (
+ <>
{/* 폼 카드 */}
@@ -488,7 +467,28 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
-
+ >
+ ), [
+ boardCode, isPinned, title, content, allowComments, errors, boards,
+ isBoardsLoading, mode, initialData, attachments, existingAttachments,
+ showPinnedAlert, formatFileSize, handlePinnedChange, handleFileSelect,
+ handleRemoveFile, handleRemoveExistingFile,
+ ]);
+
+ // Config 선택 (create/edit)
+ const config = mode === 'create' ? boardCreateConfig : boardEditConfig;
+
+ return (
+
);
}
diff --git a/src/components/customer-center/InquiryManagement/InquiryForm.tsx b/src/components/customer-center/InquiryManagement/InquiryForm.tsx
index e5e3c765..4125dd3e 100644
--- a/src/components/customer-center/InquiryManagement/InquiryForm.tsx
+++ b/src/components/customer-center/InquiryManagement/InquiryForm.tsx
@@ -2,6 +2,7 @@
/**
* 1:1 문의 등록/수정 폼 컴포넌트
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
*
* 디자인 스펙:
* - 페이지 타이틀: 1:1 문의 등록 / 1:1 문의 수정
@@ -11,9 +12,9 @@
import { useState, useCallback, useRef } from 'react';
import { useRouter } from 'next/navigation';
-import { MessageSquare, Upload, X, File, ArrowLeft, Save } from 'lucide-react';
-import { PageLayout } from '@/components/organisms/PageLayout';
-import { PageHeader } from '@/components/organisms/PageHeader';
+import { Upload, X, File } from 'lucide-react';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { inquiryCreateConfig, inquiryEditConfig } from './inquiryConfig';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
@@ -134,29 +135,9 @@ export function InquiryForm({ mode, initialData }: InquiryFormProps) {
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};
- return (
-
- {/* 헤더 */}
-
-
-
- 취소
-
-
-
- {mode === 'create' ? '등록' : '수정'}
-
-
- }
- />
-
- {/* 폼 카드 */}
-
+ // ===== 폼 콘텐츠 렌더링 =====
+ const renderFormContent = useCallback(() => (
+
문의 정보
@@ -303,7 +284,22 @@ export function InquiryForm({ mode, initialData }: InquiryFormProps) {
-
+ ), [category, title, content, errors, attachments, existingAttachments, formatFileSize, handleFileSelect, handleRemoveFile, handleRemoveExistingFile]);
+
+ // Config 선택 (create/edit)
+ const config = mode === 'create' ? inquiryCreateConfig : inquiryEditConfig;
+
+ return (
+
);
}
diff --git a/src/components/customer-center/InquiryManagement/inquiryConfig.ts b/src/components/customer-center/InquiryManagement/inquiryConfig.ts
index 2f832339..25e2e1eb 100644
--- a/src/components/customer-center/InquiryManagement/inquiryConfig.ts
+++ b/src/components/customer-center/InquiryManagement/inquiryConfig.ts
@@ -1,6 +1,40 @@
+'use client';
+
import { MessageSquare } from 'lucide-react';
import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
+/**
+ * 1:1 문의 등록 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const inquiryCreateConfig: DetailConfig = {
+ title: '1:1 문의 등록',
+ description: '1:1 문의를 등록합니다',
+ icon: MessageSquare,
+ basePath: '/customer-center/qna',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '등록',
+ },
+};
+
+/**
+ * 1:1 문의 수정 페이지 Config
+ */
+export const inquiryEditConfig: DetailConfig = {
+ ...inquiryCreateConfig,
+ title: '1:1 문의 수정',
+ description: '1:1 문의를 수정합니다',
+ actions: {
+ ...inquiryCreateConfig.actions,
+ submitLabel: '저장',
+ },
+};
+
/**
* 1:1 문의 상세 페이지 Config
*
diff --git a/src/components/hr/EmployeeManagement/EmployeeForm.tsx b/src/components/hr/EmployeeManagement/EmployeeForm.tsx
index 116f2dac..0efecfbf 100644
--- a/src/components/hr/EmployeeManagement/EmployeeForm.tsx
+++ b/src/components/hr/EmployeeManagement/EmployeeForm.tsx
@@ -1,11 +1,16 @@
'use client';
-import { useState, useEffect, useRef } from 'react';
+/**
+ * 사원 등록/수정/상세 폼 컴포넌트
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+
+import { useState, useEffect, useCallback } from 'react';
import { useDaumPostcode } from '@/hooks/useDaumPostcode';
import { useRouter, useParams } from 'next/navigation';
import { toast } from 'sonner';
-import { PageLayout } from '@/components/organisms/PageLayout';
-import { PageHeader } from '@/components/organisms/PageHeader';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { employeeCreateConfig, employeeEditConfig, employeeConfig } from './employeeConfig';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -17,7 +22,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { Users, Plus, Trash2, ArrowLeft, Save, Settings, Camera, Edit } from 'lucide-react';
+import { Plus, Trash2, Settings, Camera } from 'lucide-react';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { FieldSettingsDialog } from './FieldSettingsDialog';
import type {
@@ -357,16 +362,12 @@ export function EmployeeForm({
router.push(`/${locale}/hr/employee-management`);
};
- return (
-
- {/* 헤더 + 버튼 영역 */}
-
-
- {!isViewMode && (
+ // ===== 폼 콘텐츠 렌더링 =====
+ const renderFormContent = useCallback(() => (
+ <>
+ {/* 항목 설정 버튼 */}
+ {!isViewMode && (
+
항목 설정
- )}
-
+
+ )}
-
+ >
+ ), [
+ formData, errors, isViewMode, mode, fieldSettings, showFieldSettings,
+ ranks, titles, departments, handleChange, handleSaveFieldSettings,
+ handleAddDepartmentPosition, handleRemoveDepartmentPosition,
+ handleDepartmentSelect, handlePositionSelect, openPostcode,
+ ]);
+
+ // Config 선택 (create/edit/view)
+ const getConfig = () => {
+ if (mode === 'view') return employeeConfig;
+ if (mode === 'edit') return employeeEditConfig;
+ return employeeCreateConfig;
+ };
+
+ return (
+
);
}
\ No newline at end of file
diff --git a/src/components/hr/EmployeeManagement/employeeConfig.ts b/src/components/hr/EmployeeManagement/employeeConfig.ts
index 1d9f8fd1..96b0cfa9 100644
--- a/src/components/hr/EmployeeManagement/employeeConfig.ts
+++ b/src/components/hr/EmployeeManagement/employeeConfig.ts
@@ -1,6 +1,40 @@
+'use client';
+
import { Users } from 'lucide-react';
import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
+/**
+ * 사원 등록 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const employeeCreateConfig: DetailConfig = {
+ title: '사원 등록',
+ description: '새로운 사원을 등록합니다',
+ icon: Users,
+ basePath: '/hr/employee-management',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '등록',
+ },
+};
+
+/**
+ * 사원 수정 페이지 Config
+ */
+export const employeeEditConfig: DetailConfig = {
+ ...employeeCreateConfig,
+ title: '사원 수정',
+ description: '사원 정보를 수정합니다',
+ actions: {
+ ...employeeCreateConfig.actions,
+ submitLabel: '저장',
+ },
+};
+
/**
* 사원 상세 페이지 Config
*
diff --git a/src/components/hr/VacationManagement/VacationRequestDialog.tsx b/src/components/hr/VacationManagement/VacationRequestDialog.tsx
index dc6eebff..0e292f31 100644
--- a/src/components/hr/VacationManagement/VacationRequestDialog.tsx
+++ b/src/components/hr/VacationManagement/VacationRequestDialog.tsx
@@ -1,8 +1,8 @@
'use client';
import { useState, useEffect } from 'react';
-import { format, differenceInDays } from 'date-fns';
-import { CalendarIcon, Loader2 } from 'lucide-react';
+import { differenceInDays, parseISO } from 'date-fns';
+import { Loader2 } from 'lucide-react';
import {
Dialog,
DialogContent,
@@ -11,13 +11,8 @@ import {
DialogFooter,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
-import { Calendar } from '@/components/ui/calendar';
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from '@/components/ui/popover';
import {
Select,
SelectContent,
@@ -25,7 +20,6 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { cn } from '@/lib/utils';
import type { VacationRequestFormData, LeaveType } from './types';
import { LEAVE_TYPE_LABELS } from './types';
import { getActiveEmployees, type EmployeeOption } from './actions';
@@ -48,8 +42,6 @@ export function VacationRequestDialog({
endDate: '',
vacationDays: 1,
});
- const [startDate, setStartDate] = useState();
- const [endDate, setEndDate] = useState();
const [employees, setEmployees] = useState([]);
const [isLoadingEmployees, setIsLoadingEmployees] = useState(false);
@@ -76,33 +68,31 @@ export function VacationRequestDialog({
endDate: '',
vacationDays: 1,
});
- setStartDate(undefined);
- setEndDate(undefined);
}
}, [open]);
+ // 날짜 변경 시 휴가 일수 자동 계산
useEffect(() => {
- if (startDate && endDate) {
- const days = differenceInDays(endDate, startDate) + 1;
- setFormData(prev => ({
- ...prev,
- startDate: format(startDate, 'yyyy-MM-dd'),
- endDate: format(endDate, 'yyyy-MM-dd'),
- vacationDays: days > 0 ? days : 1,
- }));
+ if (formData.startDate && formData.endDate) {
+ const start = parseISO(formData.startDate);
+ const end = parseISO(formData.endDate);
+ const days = differenceInDays(end, start) + 1;
+ if (days > 0 && days !== formData.vacationDays) {
+ setFormData(prev => ({ ...prev, vacationDays: days }));
+ }
}
- }, [startDate, endDate]);
+ }, [formData.startDate, formData.endDate, formData.vacationDays]);
const handleSave = () => {
if (!formData.employeeId) {
alert('사원을 선택해주세요.');
return;
}
- if (!startDate || !endDate) {
+ if (!formData.startDate || !formData.endDate) {
alert('휴가 기간을 선택해주세요.');
return;
}
- if (endDate < startDate) {
+ if (formData.endDate < formData.startDate) {
alert('종료일은 시작일 이후여야 합니다.');
return;
}
@@ -174,61 +164,29 @@ export function VacationRequestDialog({
{/* 시작일 */}
-
-
-
-
-
- {startDate ? format(startDate, 'yyyy-MM-dd') : '시작일 선택'}
-
-
-
-
-
-
+
+
setFormData(prev => ({ ...prev, startDate: e.target.value }))}
+ />
{/* 종료일 */}
-
-
-
-
-
- {endDate ? format(endDate, 'yyyy-MM-dd') : '종료일 선택'}
-
-
-
- startDate ? date < startDate : false}
- initialFocus
- />
-
-
+
+
setFormData(prev => ({ ...prev, endDate: e.target.value }))}
+ />
{/* 휴가 일수 (자동 계산) */}
- {startDate && endDate && (
+ {formData.startDate && formData.endDate && (
diff --git a/src/components/material/ReceivingManagement/InspectionCreate.tsx b/src/components/material/ReceivingManagement/InspectionCreate.tsx
index 351fbff1..063a6cdc 100644
--- a/src/components/material/ReceivingManagement/InspectionCreate.tsx
+++ b/src/components/material/ReceivingManagement/InspectionCreate.tsx
@@ -2,6 +2,8 @@
/**
* 수입검사 등록 (IQC) 페이지
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ *
* - 검사 대상 선택
* - 검사 정보 입력 (검사일, 검사자*, LOT번호)
* - 검사 항목 테이블 (겉모양, 두께, 폭, 길이)
@@ -10,13 +12,14 @@
import { useState, useCallback, useMemo, useEffect } from 'react';
import { useRouter } from 'next/navigation';
-import { ClipboardCheck, Calendar } from 'lucide-react';
+import { Calendar } from 'lucide-react';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { materialInspectionCreateConfig } from './inspectionConfig';
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
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 { PageLayout } from '@/components/organisms/PageLayout';
import {
Select,
SelectContent,
@@ -183,27 +186,10 @@ export function InspectionCreate({ id }: Props) {
router.push('/ko/material/receiving-management');
}, [router]);
- return (
-
-
- {/* 헤더 */}
-
-
-
-
수입검사 등록 (IQC)
-
-
-
- 취소
-
-
-
- 검사 저장
-
-
-
-
-
+ // ===== 폼 콘텐츠 렌더링 =====
+ const renderFormContent = useCallback(() => (
+ <>
+
{/* 좌측: 검사 대상 선택 */}
@@ -362,7 +348,6 @@ export function InspectionCreate({ id }: Props) {
-
{/* 성공 다이얼로그 */}
-
+ >
+ ), [
+ isLoadingTargets, inspectionTargets, selectedTargetId, inspectionDate,
+ inspector, lotNo, inspectionItems, opinion, validationErrors, showSuccess,
+ handleTargetSelect, handleJudgmentChange, handleRemarkChange, handleSuccessClose,
+ ]);
+
+ return (
+
);
}
\ No newline at end of file
diff --git a/src/components/material/ReceivingManagement/inspectionConfig.ts b/src/components/material/ReceivingManagement/inspectionConfig.ts
new file mode 100644
index 00000000..5c98055b
--- /dev/null
+++ b/src/components/material/ReceivingManagement/inspectionConfig.ts
@@ -0,0 +1,23 @@
+'use client';
+
+import { ClipboardCheck } from 'lucide-react';
+import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
+
+/**
+ * 수입검사 등록 (IQC) 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const materialInspectionCreateConfig: DetailConfig = {
+ title: '수입검사 등록',
+ description: '수입검사를 등록합니다',
+ icon: ClipboardCheck,
+ basePath: '/material/receiving-management',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '등록',
+ },
+};
diff --git a/src/components/outbound/ShipmentManagement/ShipmentCreate.tsx b/src/components/outbound/ShipmentManagement/ShipmentCreate.tsx
index e38125db..08972674 100644
--- a/src/components/outbound/ShipmentManagement/ShipmentCreate.tsx
+++ b/src/components/outbound/ShipmentManagement/ShipmentCreate.tsx
@@ -3,13 +3,11 @@
/**
* 출하 등록 페이지
* API 연동 완료 (2025-12-26)
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
*/
import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
-import { ArrowLeft, Truck, Loader2, AlertCircle } from 'lucide-react';
-import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
-import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
@@ -22,7 +20,8 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { PageLayout } from '@/components/organisms/PageLayout';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { shipmentCreateConfig } from './shipmentConfig';
import {
createShipment,
getLotOptions,
@@ -177,63 +176,10 @@ export function ShipmentCreate() {
}
}, [formData, router]);
- // 로딩 상태 표시
- if (isLoading) {
- return (
-
-
-
- );
- }
-
- // 에러 상태 표시
- if (error) {
- return (
-
-
-
- );
- }
-
- return (
-
-
- {/* 헤더 */}
-
-
-
-
- 취소
-
-
- {isSubmitting ? (
- <>
-
- 저장 중...
- >
- ) : (
- '저장'
- )}
-
-
-
-
- {/* Validation 에러 표시 */}
+ // 폼 컨텐츠 렌더링
+ const renderFormContent = useCallback(() => (
+
+ {/* Validation 에러 표시 */}
{validationErrors.length > 0 && (
@@ -428,6 +374,35 @@ export function ShipmentCreate() {
-
+ ), [formData, validationErrors, isSubmitting, lotOptions, logisticsOptions, vehicleTonnageOptions]);
+
+ // 로딩 또는 에러 상태 처리
+ if (error) {
+ return (
+
(
+
+ {error}
+
+ )}
+ />
+ );
+ }
+
+ return (
+
);
}
diff --git a/src/components/outbound/ShipmentManagement/ShipmentEdit.tsx b/src/components/outbound/ShipmentManagement/ShipmentEdit.tsx
index 91aef102..51660bbe 100644
--- a/src/components/outbound/ShipmentManagement/ShipmentEdit.tsx
+++ b/src/components/outbound/ShipmentManagement/ShipmentEdit.tsx
@@ -3,13 +3,11 @@
/**
* 출하 수정 페이지
* API 연동 완료 (2025-12-26)
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
*/
import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
-import { ArrowLeft, Truck, Loader2, AlertCircle } from 'lucide-react';
-import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
-import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
@@ -23,8 +21,8 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { PageLayout } from '@/components/organisms/PageLayout';
-import { ServerErrorPage } from '@/components/common/ServerErrorPage';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { shipmentEditConfig } from './shipmentConfig';
import {
getShipmentById,
getLogisticsOptions,
@@ -215,63 +213,23 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
}
}, [id, formData, router]);
- // 로딩 상태 표시
- if (isLoading) {
- return (
-
-
-
- );
- }
+ // 동적 config (로트번호 + 상태 표시)
+ const dynamicConfig = {
+ ...shipmentEditConfig,
+ title: detail ? `출고 수정 (${detail.lotNo})` : '출고 수정',
+ };
- // 에러 상태 표시
- if (error || !detail) {
- return (
-
- );
- }
+ // 폼 컨텐츠 렌더링
+ const renderFormContent = useCallback(() => {
+ if (!detail) return null;
- return (
-
+ return (
- {/* 헤더 */}
-
-
-
-
-
-
-
출고 수정
-
{detail.lotNo}
-
- {SHIPMENT_STATUS_LABELS[detail.status]}
-
-
-
-
- 취소
-
-
- {isSubmitting ? (
- <>
-
- 저장 중...
- >
- ) : (
- '저장'
- )}
-
-
+ {/* 상태 배지 */}
+
+
+ {SHIPMENT_STATUS_LABELS[detail.status]}
+
{/* Validation 에러 표시 */}
@@ -536,6 +494,38 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
-
+ );
+ }, [detail, formData, validationErrors, isSubmitting, logisticsOptions, vehicleTonnageOptions]);
+
+ // 에러 상태 표시
+ if (error && !isLoading) {
+ return (
+
(
+
+
+ {error || '출하 정보를 찾을 수 없습니다.'}
+
+
+ )}
+ />
+ );
+ }
+
+ return (
+
);
}
diff --git a/src/components/outbound/ShipmentManagement/shipmentConfig.ts b/src/components/outbound/ShipmentManagement/shipmentConfig.ts
index 6ac2d17f..9824624c 100644
--- a/src/components/outbound/ShipmentManagement/shipmentConfig.ts
+++ b/src/components/outbound/ShipmentManagement/shipmentConfig.ts
@@ -1,3 +1,5 @@
+'use client';
+
import { Truck } from 'lucide-react';
import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
@@ -33,3 +35,31 @@ export const shipmentConfig: DetailConfig = {
},
},
};
+
+/**
+ * 출하 등록 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const shipmentCreateConfig: DetailConfig = {
+ title: '출하 등록',
+ description: '새로운 출하를 등록합니다',
+ icon: Truck,
+ basePath: '/outbound/shipments',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '저장',
+ },
+};
+
+/**
+ * 출하 수정 페이지 Config
+ */
+export const shipmentEditConfig: DetailConfig = {
+ ...shipmentCreateConfig,
+ title: '출고 수정',
+ description: '출고 정보를 수정합니다',
+};
diff --git a/src/components/process-management/ProcessForm.tsx b/src/components/process-management/ProcessForm.tsx
index 30568a5f..dd64f7a4 100644
--- a/src/components/process-management/ProcessForm.tsx
+++ b/src/components/process-management/ProcessForm.tsx
@@ -1,8 +1,15 @@
'use client';
+/**
+ * 공정 등록/수정 폼 컴포넌트
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+
import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
-import { X, Save, Plus, Wrench, Trash2, Loader2, Pencil } from 'lucide-react';
+import { Plus, Wrench, Trash2, Pencil } from 'lucide-react';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { processCreateConfig, processEditConfig } from './processConfig';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
@@ -17,7 +24,6 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { PageLayout } from '@/components/organisms/PageLayout';
import { RuleModal } from './RuleModal';
import { toast } from 'sonner';
import type { Process, ClassificationRule, ProcessType } from '@/types/process';
@@ -190,27 +196,9 @@ export function ProcessForm({ mode, initialData }: ProcessFormProps) {
router.back();
};
- return (
-
- {/* 헤더 */}
-
-
공정 {isEdit ? '수정' : '등록'}
-
-
-
- 취소
-
-
- {isLoading ? (
-
- ) : (
-
- )}
- {isEdit ? '수정' : '등록'}
-
-
-
-
+ // ===== 폼 콘텐츠 렌더링 =====
+ const renderFormContent = useCallback(() => (
+ <>
{/* 기본 정보 */}
@@ -460,6 +448,27 @@ export function ProcessForm({ mode, initialData }: ProcessFormProps) {
onAdd={handleSaveRule}
editRule={editingRule}
/>
-
+ >
+ ), [
+ processName, processType, department, workLogTemplate, classificationRules,
+ requiredWorkers, equipmentInfo, workSteps, note, isActive, ruleModalOpen,
+ editingRule, departmentOptions, isDepartmentsLoading, handleSaveRule,
+ handleEditRule, handleDeleteRule, handleModalClose,
+ ]);
+
+ // Config 선택 (create/edit)
+ const config = isEdit ? processEditConfig : processCreateConfig;
+
+ return (
+
);
}
diff --git a/src/components/process-management/processConfig.ts b/src/components/process-management/processConfig.ts
new file mode 100644
index 00000000..b045747f
--- /dev/null
+++ b/src/components/process-management/processConfig.ts
@@ -0,0 +1,36 @@
+'use client';
+
+import { Wrench } from 'lucide-react';
+import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
+
+/**
+ * 공정 등록 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const processCreateConfig: DetailConfig = {
+ title: '공정 등록',
+ description: '새로운 공정을 등록합니다',
+ icon: Wrench,
+ basePath: '/master-data/process-management',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '등록',
+ },
+};
+
+/**
+ * 공정 수정 페이지 Config
+ */
+export const processEditConfig: DetailConfig = {
+ ...processCreateConfig,
+ title: '공정 수정',
+ description: '공정 정보를 수정합니다',
+ actions: {
+ ...processCreateConfig.actions,
+ submitLabel: '저장',
+ },
+};
diff --git a/src/components/production/WorkOrders/WorkOrderCreate.tsx b/src/components/production/WorkOrders/WorkOrderCreate.tsx
index 8c9d5bad..21cced1f 100644
--- a/src/components/production/WorkOrders/WorkOrderCreate.tsx
+++ b/src/components/production/WorkOrders/WorkOrderCreate.tsx
@@ -3,11 +3,12 @@
/**
* 작업지시 등록 페이지
* API 연동 완료 (2025-12-26)
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
*/
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useCallback } from 'react';
import { useRouter } from 'next/navigation';
-import { ArrowLeft, FileText, X, Edit2, Loader2 } from 'lucide-react';
+import { X, Edit2, FileText } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
@@ -21,13 +22,14 @@ import {
SelectValue,
} from '@/components/ui/select';
import { Alert, AlertDescription } from '@/components/ui/alert';
-import { PageLayout } from '@/components/organisms/PageLayout';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { SalesOrderSelectModal } from './SalesOrderSelectModal';
import { AssigneeSelectModal } from './AssigneeSelectModal';
import { toast } from 'sonner';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import { createWorkOrder, getProcessOptions, type ProcessOption } from './actions';
-import { PROCESS_TYPE_LABELS, type ProcessType, type SalesOrder } from './types';
+import { type SalesOrder } from './types';
+import { workOrderCreateConfig } from './workOrderConfig';
// Validation 에러 타입
interface ValidationErrors {
@@ -208,31 +210,9 @@ export function WorkOrderCreate() {
return selectedProcess?.processCode || '-';
};
- return (
-
- {/* 헤더 */}
-
-
-
-
- 취소
-
-
- {isSubmitting && }
- 등록
-
-
-
-
-
+ // 폼 컨텐츠 렌더링
+ const renderFormContent = useCallback(() => (
+
{/* Validation 에러 표시 */}
{Object.keys(validationErrors).length > 0 && (
@@ -497,6 +477,19 @@ export function WorkOrderCreate() {
/>
+ ), [mode, formData, validationErrors, processOptions, isLoadingProcesses, assigneeNames, getSelectedProcessCode]);
+
+ return (
+ <>
+
{/* 수주 선택 모달 */}
-
+ >
);
}
\ No newline at end of file
diff --git a/src/components/production/WorkOrders/WorkOrderEdit.tsx b/src/components/production/WorkOrders/WorkOrderEdit.tsx
index 430d3c59..9a29c622 100644
--- a/src/components/production/WorkOrders/WorkOrderEdit.tsx
+++ b/src/components/production/WorkOrders/WorkOrderEdit.tsx
@@ -3,12 +3,11 @@
/**
* 작업지시 수정 페이지
* WorkOrderCreate 패턴 기반
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
*/
import { useState, useEffect, useCallback } from 'react';
import { useRouter } from 'next/navigation';
-import { ArrowLeft, FileText, 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';
@@ -20,13 +19,13 @@ import {
SelectValue,
} from '@/components/ui/select';
import { Alert, AlertDescription } from '@/components/ui/alert';
-import { PageLayout } from '@/components/organisms/PageLayout';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { AssigneeSelectModal } from './AssigneeSelectModal';
-import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
import { toast } from 'sonner';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import { getWorkOrderById, updateWorkOrder, getProcessOptions, type ProcessOption } from './actions';
import type { WorkOrder } from './types';
+import { workOrderEditConfig } from './workOrderConfig';
// Validation 에러 타입
interface ValidationErrors {
@@ -199,41 +198,15 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
return selectedProcess?.processCode || '-';
};
- // 로딩 상태
- if (isLoading) {
- return
;
- }
+ // 동적 config (작업지시 번호 포함)
+ const dynamicConfig = {
+ ...workOrderEditConfig,
+ title: `작업지시 수정 ${workOrder ? `(${workOrder.workOrderNo})` : ''}`,
+ };
- if (!workOrder) {
- return null;
- }
-
- return (
-
- {/* 헤더 */}
-
-
-
-
-
-
-
- 작업지시 수정
-
-
({workOrder.workOrderNo})
-
-
-
- 취소
-
-
- {isSubmitting && }
- 저장
-
-
-
-
-
+ // 폼 컨텐츠 렌더링
+ const renderFormContent = useCallback(() => (
+
{/* Validation 에러 표시 */}
{Object.keys(validationErrors).length > 0 && (
@@ -386,6 +359,20 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
/>
+ ), [formData, validationErrors, processOptions, isLoadingProcesses, assigneeNames, getSelectedProcessCode]);
+
+ return (
+ <>
+
{/* 담당자 선택 모달 */}
-
+ >
);
}
\ No newline at end of file
diff --git a/src/components/production/WorkOrders/workOrderConfig.ts b/src/components/production/WorkOrders/workOrderConfig.ts
index 3581932b..ea9ff759 100644
--- a/src/components/production/WorkOrders/workOrderConfig.ts
+++ b/src/components/production/WorkOrders/workOrderConfig.ts
@@ -1,3 +1,5 @@
+'use client';
+
import { FileText } from 'lucide-react';
import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
@@ -28,3 +30,35 @@ export const workOrderConfig: DetailConfig = {
editLabel: '수정',
},
};
+
+/**
+ * 작업지시 등록 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const workOrderCreateConfig: DetailConfig = {
+ title: '작업지시 등록',
+ description: '새로운 작업지시를 등록합니다',
+ icon: FileText,
+ basePath: '/production/work-orders',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '등록',
+ },
+};
+
+/**
+ * 작업지시 수정 페이지 Config
+ */
+export const workOrderEditConfig: DetailConfig = {
+ ...workOrderCreateConfig,
+ title: '작업지시 수정',
+ description: '작업지시 정보를 수정합니다',
+ actions: {
+ ...workOrderCreateConfig.actions,
+ submitLabel: '저장',
+ },
+};
diff --git a/src/components/quality/InspectionManagement/InspectionCreate.tsx b/src/components/quality/InspectionManagement/InspectionCreate.tsx
index 5c9b5f1d..87b8707a 100644
--- a/src/components/quality/InspectionManagement/InspectionCreate.tsx
+++ b/src/components/quality/InspectionManagement/InspectionCreate.tsx
@@ -2,19 +2,20 @@
/**
* 검사 등록 페이지
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
* API 연동 완료 (2025-12-26)
*/
import { useState, useCallback } from 'react';
import { useRouter } from 'next/navigation';
-import { ClipboardCheck, ImageIcon, Loader2 } from 'lucide-react';
-import { Button } from '@/components/ui/button';
+import { ImageIcon } from 'lucide-react';
+import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
+import { qualityInspectionCreateConfig } from './inspectionConfig';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Alert, AlertDescription } from '@/components/ui/alert';
-import { PageLayout } from '@/components/organisms/PageLayout';
import { toast } from 'sonner';
import { createInspection } from './actions';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
@@ -89,9 +90,9 @@ export function InspectionCreate() {
}, []);
// 취소
- const handleCancel = () => {
+ const handleCancel = useCallback(() => {
router.push('/quality/inspections');
- };
+ }, [router]);
// validation 체크
const validateForm = (): boolean => {
@@ -156,33 +157,10 @@ export function InspectionCreate() {
}
};
- return (
-
-
- {/* 헤더 */}
-
-
-
-
검사 등록
-
-
-
- 취소
-
-
- {isSubmitting ? (
- <>
-
- 등록 중...
- >
- ) : (
- '검사완료'
- )}
-
-
-
-
- {/* Validation 에러 표시 */}
+ // ===== 폼 콘텐츠 렌더링 =====
+ const renderFormContent = useCallback(() => (
+
+ {/* Validation 에러 표시 */}
{validationErrors.length > 0 && (
@@ -352,6 +330,18 @@ export function InspectionCreate() {
-
+ ), [formData, inspectionItems, validationErrors, handleInputChange, handleQualityResultChange, handleMeasurementChange]);
+
+ return (
+
);
}
\ No newline at end of file
diff --git a/src/components/quality/InspectionManagement/inspectionConfig.ts b/src/components/quality/InspectionManagement/inspectionConfig.ts
index 06796d55..1610f504 100644
--- a/src/components/quality/InspectionManagement/inspectionConfig.ts
+++ b/src/components/quality/InspectionManagement/inspectionConfig.ts
@@ -1,6 +1,27 @@
+'use client';
+
import { ClipboardCheck } from 'lucide-react';
import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
+/**
+ * 품질검사 등록 페이지 Config
+ * IntegratedDetailTemplate 마이그레이션 (2025-01-20)
+ */
+export const qualityInspectionCreateConfig: DetailConfig = {
+ title: '품질검사 등록',
+ description: '품질검사를 등록합니다',
+ icon: ClipboardCheck,
+ basePath: '/quality/inspection-management',
+ fields: [],
+ actions: {
+ showBack: true,
+ showEdit: false,
+ showDelete: false,
+ showSave: true,
+ submitLabel: '등록',
+ },
+};
+
/**
* 검수관리 상세 페이지 Config
*
diff --git a/src/components/templates/IntegratedDetailTemplate/components/DetailActions.tsx b/src/components/templates/IntegratedDetailTemplate/components/DetailActions.tsx
index 575cc460..73ce2ed9 100644
--- a/src/components/templates/IntegratedDetailTemplate/components/DetailActions.tsx
+++ b/src/components/templates/IntegratedDetailTemplate/components/DetailActions.tsx
@@ -27,6 +27,7 @@ export interface DetailActionsProps {
back?: boolean;
delete?: boolean;
edit?: boolean;
+ save?: boolean;
};
/** 버튼 라벨 */
labels?: {
@@ -74,6 +75,7 @@ export function DetailActions({
back: showBack = true,
delete: showDelete = true,
edit: showEdit = true,
+ save: showSave = true,
} = showButtons;
const {
@@ -133,11 +135,16 @@ export function DetailActions({
{cancelLabel}
- {/* 오른쪽: 저장/등록 */}
-
-
- {actualSubmitLabel}
-
+ {/* 오른쪽: 추가액션 + 저장/등록 */}
+
+ {extraActions}
+ {showSave && onSubmit && (
+
+
+ {actualSubmitLabel}
+
+ )}
+
);
}
diff --git a/src/components/templates/IntegratedDetailTemplate/index.tsx b/src/components/templates/IntegratedDetailTemplate/index.tsx
index b6767d75..044e6e0a 100644
--- a/src/components/templates/IntegratedDetailTemplate/index.tsx
+++ b/src/components/templates/IntegratedDetailTemplate/index.tsx
@@ -302,6 +302,7 @@ export function IntegratedDetailTemplate>({
back: actions.showBack !== false,
delete: actions.showDelete !== false && !!onDelete,
edit: actions.showEdit !== false,
+ save: actions.showSave !== false,
}}
labels={{
back: actions.backLabel,