From 62ef2b1ff91d87644de7e95941ffa4b52b04ba99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EB=B3=91=EC=B2=A0?= Date: Tue, 20 Jan 2026 19:31:07 +0900 Subject: [PATCH] =?UTF-8?q?feat(WEB):=20IntegratedDetailTemplate=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=ED=85=9C=ED=94=8C=EB=A6=BF=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20Phase=201~8=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Phase 1: 기안함(DocumentCreate) 마이그레이션 - Phase 2: 작업지시(WorkOrderCreate/Edit) 마이그레이션 - Phase 3: 출하(ShipmentCreate/Edit) 마이그레이션 - Phase 4: 사원(EmployeeForm) 마이그레이션 - Phase 5: 게시판(BoardForm) 마이그레이션 - Phase 6: 1:1문의(InquiryForm) 마이그레이션 - Phase 7: 공정(ProcessForm) 마이그레이션 - Phase 8: 수입검사/품질검사(InspectionCreate) 마이그레이션 - DetailActions에 showSave 옵션 추가 - 각 도메인별 config 파일 생성 Co-Authored-By: Claude --- ...ratedDetailTemplate-migration-checklist.md | 151 +++++++++++++++++ .../DocumentCreate/documentCreateConfig.ts | 34 ++++ .../approval/DocumentCreate/index.tsx | 152 ++++++++---------- .../board/BoardForm/boardFormConfig.ts | 36 +++++ src/components/board/BoardForm/index.tsx | 58 +++---- .../InquiryManagement/InquiryForm.tsx | 50 +++--- .../InquiryManagement/inquiryConfig.ts | 34 ++++ .../hr/EmployeeManagement/EmployeeForm.tsx | 90 ++++++----- .../hr/EmployeeManagement/employeeConfig.ts | 34 ++++ .../VacationRequestDialog.tsx | 102 ++++-------- .../ReceivingManagement/InspectionCreate.tsx | 52 +++--- .../ReceivingManagement/inspectionConfig.ts | 23 +++ .../ShipmentManagement/ShipmentCreate.tsx | 99 +++++------- .../ShipmentManagement/ShipmentEdit.tsx | 110 ++++++------- .../ShipmentManagement/shipmentConfig.ts | 30 ++++ .../process-management/ProcessForm.tsx | 57 ++++--- .../process-management/processConfig.ts | 36 +++++ .../production/WorkOrders/WorkOrderCreate.tsx | 53 +++--- .../production/WorkOrders/WorkOrderEdit.tsx | 65 +++----- .../production/WorkOrders/workOrderConfig.ts | 34 ++++ .../InspectionManagement/InspectionCreate.tsx | 56 +++---- .../InspectionManagement/inspectionConfig.ts | 21 +++ .../components/DetailActions.tsx | 17 +- .../IntegratedDetailTemplate/index.tsx | 1 + 24 files changed, 861 insertions(+), 534 deletions(-) create mode 100644 claudedocs/[IMPL-2025-01-20] IntegratedDetailTemplate-migration-checklist.md create mode 100644 src/components/approval/DocumentCreate/documentCreateConfig.ts create mode 100644 src/components/board/BoardForm/boardFormConfig.ts create mode 100644 src/components/material/ReceivingManagement/inspectionConfig.ts create mode 100644 src/components/process-management/processConfig.ts 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 ? '복제된 문서를 수정 후 상신합니다' : '새로운 문서를 작성합니다'} - -
-
-
-
-
-
- - {/* 액션 버튼 (스텝) */} -
- - - -
+ ); + }, [handlePreview, handleDelete, handleSubmit, handleSaveDraft, isPending, isEditMode]); - {/* 폼 영역 */} + // 폼 컨텐츠 렌더링 + const renderFormContent = useCallback(() => { + return (
{/* 기본 정보 */} @@ -535,27 +525,19 @@ export function DocumentCreate() { {/* 문서 유형별 폼 */} {renderDocumentTypeForm()}
+ ); + }, [basicInfo, approvalLine, references, renderDocumentTypeForm]); - {/* 하단 고정 버튼 (모바일) */} -
-
- - - -
-
- - {/* 모바일 하단 여백 */} -
+ return ( + <> + {/* 미리보기 모달 */} -
+ ); } 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 ( - - {/* 헤더 */} - - - -
- } - /> - + // ===== 폼 콘텐츠 렌더링 ===== + 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 ( - - {/* 헤더 */} - - - - - } - /> - - {/* 폼 카드 */} - + // ===== 폼 콘텐츠 렌더링 ===== + 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 && ( +
- )} -
+
+ )} -
+
{/* 사원 정보 - 프로필 사진 + 기본 정보 */} @@ -932,31 +933,7 @@ export function EmployeeForm({ - {/* 버튼 영역 */} -
- - {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({ {/* 시작일 */}
- - - - - - - - - + + setFormData(prev => ({ ...prev, startDate: e.target.value }))} + />
{/* 종료일 */}
- - - - - - - 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 ( - -
- -

{error}

- -
-
- ); - } - - return ( - -
- {/* 헤더 */} -
-
- - -

출하 등록

-
-
- - -
-
- - {/* 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]} - -
-
- - -
+ {/* 상태 배지 */} +
+ + {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 ? '수정' : '등록'}

-
- - -
-
- + // ===== 폼 콘텐츠 렌더링 ===== + 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 ( - - {/* 헤더 */} -
-
- -

- - 작업지시 등록 -

-
-
- - -
-
- -
+ // 폼 컨텐츠 렌더링 + 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}) -
-
- - -
-
- -
+ // 폼 컨텐츠 렌더링 + 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 ( - -
- {/* 헤더 */} -
-
- -

검사 등록

-
-
- - -
-
- - {/* 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} - {/* 오른쪽: 저장/등록 */} - + {/* 오른쪽: 추가액션 + 저장/등록 */} +
+ {extraActions} + {showSave && onSubmit && ( + + )} +
); } 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,