feat(WEB): Phase 6 IntegratedDetailTemplate 마이그레이션 완료

Phase 6 마이그레이션 (41개 컴포넌트 완료):
- 건설/시공: 협력업체, 시공관리, 기성관리, 발주관리, 계약관리 등
- 영업: 견적관리(V2), 고객관리(V2), 수주관리
- 회계: 청구관리, 매입관리, 매출관리, 거래처관리, 악성채권 등
- 생산: 작업지시, 검수관리
- 출고: 출하관리
- 자재: 입고관리, 재고현황
- 고객센터: 문의관리, 이벤트관리, 공지관리
- 인사: 직원관리
- 설정: 권한관리

주요 변경사항:
- 34개 xxxConfig.ts 파일 생성 (설정 기반 페이지 구성)
- PageLayout/PageHeader → IntegratedDetailTemplate 통합
- 일관된 타이틀/버튼 영역 (목록, 상세, 수정, 삭제)
- 1112줄 코드 감소 (중복 제거)

프로젝트 공통화 현황 분석 문서 추가:
- 상세 페이지 62%, 목록 페이지 82% 공통화 달성
- 추가 공통화 기회 및 로드맵 정리

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-20 15:51:02 +09:00
parent 6f457b28f3
commit 61e3a0ed60
71 changed files with 4743 additions and 4402 deletions

View File

@@ -1,10 +1,15 @@
'use client';
/**
* 악성채권 추심관리 상세 페이지
* IntegratedDetailTemplate 마이그레이션 완료 (2026-01-20)
*/
import { useState, useCallback, useMemo } from 'react';
import { useDaumPostcode } from '@/hooks/useDaumPostcode';
import { useRouter } from 'next/navigation';
import { format } from 'date-fns';
import { AlertTriangle, Plus, X, FileText, Receipt, CreditCard, Upload, Download, Trash2 } from 'lucide-react';
import { Plus, X, FileText, Receipt, CreditCard, Upload, Download, Trash2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
@@ -28,14 +33,13 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
import { badDebtConfig } from './badDebtConfig';
import { toast } from 'sonner';
import type {
BadDebtRecord,
BadDebtMemo,
Manager,
AttachedFile,
CollectionStatus,
} from './types';
import {
@@ -130,10 +134,6 @@ export function BadDebtDetail({ mode, recordId, initialData }: BadDebtDetailProp
}, []);
// 네비게이션 핸들러
const handleBack = useCallback(() => {
router.push('/ko/accounting/bad-debt-collection');
}, [router]);
const handleEdit = useCallback(() => {
router.push(`/ko/accounting/bad-debt-collection/${recordId}?mode=edit`);
}, [router, recordId]);
@@ -331,31 +331,44 @@ export function BadDebtDetail({ mode, recordId, initialData }: BadDebtDetailProp
setNewAdditionalFiles(prev => prev.filter((_, i) => i !== index));
}, []);
// 헤더 버튼
const headerActions = useMemo(() => {
// 동적 config (mode에 따라 title 변경)
const dynamicConfig = useMemo(() => {
const titleMap: Record<string, string> = {
new: '악성채권 등록',
edit: '악성채권 수정',
view: '악성채권 추심관리 상세',
};
return {
...badDebtConfig,
title: titleMap[mode] || badDebtConfig.title,
};
}, [mode]);
// 커스텀 헤더 액션 (저장 확인 다이얼로그 패턴 유지)
const customHeaderActions = useMemo(() => {
if (isViewMode) {
return (
<div className="flex gap-2">
<>
<Button variant="outline" className="text-red-500 border-red-200 hover:bg-red-50" onClick={handleDelete} disabled={isLoading}>
{isLoading ? '처리중...' : '삭제'}
</Button>
<Button onClick={handleEdit} className="bg-blue-500 hover:bg-blue-600" disabled={isLoading}>
</Button>
</div>
</>
);
}
return (
<div className="flex gap-2">
<>
<Button variant="outline" onClick={handleCancel} disabled={isLoading}>
</Button>
<Button onClick={handleSave} className="bg-blue-500 hover:bg-blue-600" disabled={isLoading}>
{isLoading ? '처리중...' : (isNewMode ? '등록' : '저장')}
</Button>
</div>
</>
);
}, [isViewMode, isNewMode, isLoading, handleDelete, handleEdit, handleCancel, handleSave]);
}, [isViewMode, isNewMode, isLoading, handleDelete, handleEdit, handleCancel, handleSave, mode]);
// 입력 필드 렌더링 헬퍼
const renderField = (
@@ -387,17 +400,9 @@ export function BadDebtDetail({ mode, recordId, initialData }: BadDebtDetailProp
);
};
return (
<PageLayout>
<PageHeader
title="악성채권 추심관리 상세"
description="추심 대상 업체 정보를 표시"
icon={AlertTriangle}
actions={headerActions}
onBack={handleBack}
/>
<div className="space-y-6">
// 폼 콘텐츠 렌더링
const renderFormContent = useCallback(() => (
<div className="space-y-6">
{/* 기본 정보 */}
<Card>
<CardHeader>
@@ -956,6 +961,40 @@ export function BadDebtDetail({ mode, recordId, initialData }: BadDebtDetailProp
</CardContent>
</Card>
</div>
), [
formData,
isViewMode,
isNewMode,
newMemo,
newBusinessRegistrationFile,
newTaxInvoiceFile,
newAdditionalFiles,
handleChange,
handleAddMemo,
handleDeleteMemo,
handleManagerChange,
handleBillStatus,
handleReceivablesStatus,
handleFileDownload,
handleDeleteExistingFile,
handleAddAdditionalFile,
handleRemoveNewAdditionalFile,
openPostcode,
renderField,
]);
return (
<>
<IntegratedDetailTemplate
config={dynamicConfig}
mode={isNewMode ? 'create' : (isViewMode ? 'view' : 'edit')}
initialData={formData}
itemId={recordId}
isLoading={isLoading}
headerActions={customHeaderActions}
renderView={() => renderFormContent()}
renderForm={() => renderFormContent()}
/>
{/* 삭제 확인 다이얼로그 */}
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
@@ -1000,6 +1039,6 @@ export function BadDebtDetail({ mode, recordId, initialData }: BadDebtDetailProp
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</PageLayout>
</>
);
}

View File

@@ -0,0 +1,34 @@
import { AlertTriangle } from 'lucide-react';
import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
/**
* 악성채권 추심관리 상세 페이지 Config
*
* 참고: 이 config는 타이틀/버튼 영역만 정의
* 폼 내용은 renderView/renderForm에서 처리
*
* 특이사항:
* - view/edit/new 모드 지원
* - 저장 확인 다이얼로그 (커스텀 headerActions 사용)
* - 파일 업로드/다운로드
* - 메모 추가/삭제
*/
export const badDebtConfig: DetailConfig = {
title: '악성채권 추심관리 상세',
description: '추심 대상 업체 정보를 표시',
icon: AlertTriangle,
basePath: '/accounting/bad-debt-collection',
fields: [], // renderView/renderForm 사용으로 필드 정의 불필요
gridColumns: 2,
actions: {
showBack: true,
showDelete: true,
showEdit: true,
backLabel: '목록',
editLabel: '수정',
deleteLabel: '삭제',
cancelLabel: '취소',
saveLabel: '저장',
createLabel: '등록',
},
};