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:
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
34
src/components/accounting/BadDebtCollection/badDebtConfig.ts
Normal file
34
src/components/accounting/BadDebtCollection/badDebtConfig.ts
Normal 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: '등록',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user