- 등록(?mode=new), 상세(?mode=view), 수정(?mode=edit) URL 패턴 일괄 적용
- 중복 패턴 제거: /edit?mode=edit → ?mode=edit (16개 파일)
- 제목 일관성: {기능} 등록/상세/수정 패턴 적용
- 검수 체크리스트 문서 추가 (79개 페이지)
- UniversalListPage, IntegratedDetailTemplate 공통 컴포넌트 개선
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
145 lines
5.0 KiB
TypeScript
145 lines
5.0 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useCallback, useEffect } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { toast } from 'sonner';
|
|
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
|
|
import type { DetailMode } from '@/components/templates/IntegratedDetailTemplate/types';
|
|
import { depositDetailConfig } from './depositDetailConfig';
|
|
import type { DepositRecord } from './types';
|
|
import {
|
|
getDepositById,
|
|
createDeposit,
|
|
updateDeposit,
|
|
deleteDeposit,
|
|
getVendors,
|
|
getBankAccounts,
|
|
} from './actions';
|
|
import { useDevFill, generateDepositData } from '@/components/dev';
|
|
|
|
// ===== Props =====
|
|
interface DepositDetailClientV2Props {
|
|
depositId?: string;
|
|
initialMode?: DetailMode;
|
|
}
|
|
|
|
export default function DepositDetailClientV2({
|
|
depositId,
|
|
initialMode = 'view',
|
|
}: DepositDetailClientV2Props) {
|
|
const router = useRouter();
|
|
const [mode, setMode] = useState<DetailMode>(initialMode);
|
|
const [deposit, setDeposit] = useState<DepositRecord | null>(null);
|
|
const [isLoading, setIsLoading] = useState(initialMode !== 'create');
|
|
|
|
// ===== DevFill: 자동 입력 기능 =====
|
|
useDevFill('deposit', useCallback(async () => {
|
|
if (initialMode === 'create') {
|
|
// 거래처 및 계좌 목록 가져오기
|
|
const [vendorResult, bankAccountResult] = await Promise.all([
|
|
getVendors(),
|
|
getBankAccounts(),
|
|
]);
|
|
const vendors = vendorResult.success ? vendorResult.data : undefined;
|
|
const bankAccounts = bankAccountResult.success ? bankAccountResult.data : undefined;
|
|
|
|
const mockData = generateDepositData({ vendors, bankAccounts });
|
|
setDeposit(mockData as unknown as DepositRecord);
|
|
toast.success('입금 데이터가 자동 입력되었습니다.');
|
|
}
|
|
}, [initialMode]));
|
|
|
|
// ===== 데이터 로드 =====
|
|
useEffect(() => {
|
|
const loadDeposit = async () => {
|
|
if (depositId && initialMode !== 'create') {
|
|
setIsLoading(true);
|
|
const result = await getDepositById(depositId);
|
|
if (result.success && result.data) {
|
|
setDeposit(result.data);
|
|
} else {
|
|
toast.error(result.error || '입금 내역을 불러오는데 실패했습니다.');
|
|
}
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
loadDeposit();
|
|
}, [depositId, initialMode]);
|
|
|
|
// ===== 저장/등록 핸들러 =====
|
|
const handleSubmit = useCallback(
|
|
async (formData: Record<string, unknown>): Promise<{ success: boolean; error?: string }> => {
|
|
const submitData = depositDetailConfig.transformSubmitData?.(formData) || formData;
|
|
|
|
if (!submitData.vendorId) {
|
|
toast.error('거래처를 선택해주세요.');
|
|
return { success: false, error: '거래처를 선택해주세요.' };
|
|
}
|
|
|
|
const result =
|
|
mode === 'create'
|
|
? await createDeposit(submitData as Partial<DepositRecord>)
|
|
: await updateDeposit(depositId!, submitData as Partial<DepositRecord>);
|
|
|
|
if (result.success) {
|
|
toast.success(mode === 'create' ? '입금 내역이 등록되었습니다.' : '입금 내역이 수정되었습니다.');
|
|
router.push('/ko/accounting/deposits');
|
|
return { success: true };
|
|
} else {
|
|
toast.error(result.error || '저장에 실패했습니다.');
|
|
return { success: false, error: result.error };
|
|
}
|
|
},
|
|
[mode, depositId, router]
|
|
);
|
|
|
|
// ===== 삭제 핸들러 =====
|
|
const handleDelete = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
|
|
if (!depositId) return { success: false, error: 'ID가 없습니다.' };
|
|
|
|
const result = await deleteDeposit(depositId);
|
|
if (result.success) {
|
|
toast.success('입금 내역이 삭제되었습니다.');
|
|
router.push('/ko/accounting/deposits');
|
|
return { success: true };
|
|
} else {
|
|
toast.error(result.error || '삭제에 실패했습니다.');
|
|
return { success: false, error: result.error };
|
|
}
|
|
}, [depositId, router]);
|
|
|
|
// ===== 모드 변경 핸들러 =====
|
|
const handleModeChange = useCallback(
|
|
(newMode: DetailMode) => {
|
|
if (newMode === 'edit' && depositId) {
|
|
router.push(`/ko/accounting/deposits/${depositId}?mode=edit`);
|
|
} else {
|
|
setMode(newMode);
|
|
}
|
|
},
|
|
[depositId, router]
|
|
);
|
|
|
|
// 타이틀 동적 설정
|
|
// IntegratedDetailTemplate: create → "{title} 등록", view → "{title}", edit → "{title} 수정"
|
|
// view 모드에서 "입금 상세"로 표시하려면 직접 설정 필요
|
|
const dynamicConfig = {
|
|
...depositDetailConfig,
|
|
title: mode === 'view' ? '입금 상세' : '입금',
|
|
};
|
|
|
|
return (
|
|
<IntegratedDetailTemplate
|
|
config={dynamicConfig as Parameters<typeof IntegratedDetailTemplate>[0]['config']}
|
|
mode={mode}
|
|
initialData={deposit as unknown as Record<string, unknown> | undefined}
|
|
itemId={depositId}
|
|
isLoading={isLoading}
|
|
onSubmit={handleSubmit}
|
|
onDelete={handleDelete}
|
|
onModeChange={handleModeChange}
|
|
buttonPosition="top"
|
|
/>
|
|
);
|
|
}
|