- UniversalListPage에 externalIsLoading prop 추가 - CardTransactionDetailClient DevFill 자동입력 기능 추가 - 여러 컴포넌트 로딩 상태 처리 개선 - skeleton 컴포넌트 확장 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
'use client';
|
|
|
|
/**
|
|
* 대손추심 상세 클라이언트 컴포넌트 V2
|
|
*
|
|
* 라우팅 구조 변경: /[id], /[id]/edit, /new → /[id]?mode=view|edit, /new
|
|
* 기존 BadDebtDetail 컴포넌트 활용
|
|
*/
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
import { BadDebtDetail } from './BadDebtDetail';
|
|
import { getBadDebtById } from './actions';
|
|
import type { BadDebtRecord } from './types';
|
|
import { DetailPageSkeleton } from '@/components/ui/skeleton';
|
|
import { ErrorCard } from '@/components/ui/error-card';
|
|
import { toast } from 'sonner';
|
|
|
|
type DetailMode = 'view' | 'edit' | 'new';
|
|
|
|
interface BadDebtDetailClientV2Props {
|
|
recordId?: string;
|
|
initialMode?: DetailMode;
|
|
}
|
|
|
|
const BASE_PATH = '/ko/accounting/bad-debt-collection';
|
|
|
|
export function BadDebtDetailClientV2({ recordId, initialMode }: BadDebtDetailClientV2Props) {
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
|
|
// URL 쿼리에서 모드 결정
|
|
const modeFromQuery = searchParams.get('mode') as DetailMode | null;
|
|
const isNewMode = !recordId || recordId === 'new';
|
|
|
|
const [mode, setMode] = useState<DetailMode>(() => {
|
|
if (isNewMode) return 'new';
|
|
if (initialMode) return initialMode;
|
|
if (modeFromQuery === 'edit') return 'edit';
|
|
return 'view';
|
|
});
|
|
|
|
const [recordData, setRecordData] = useState<BadDebtRecord | null>(null);
|
|
const [isLoading, setIsLoading] = useState(!isNewMode);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// 데이터 로드
|
|
useEffect(() => {
|
|
const loadData = async () => {
|
|
if (isNewMode) {
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const result = await getBadDebtById(recordId!);
|
|
if (result) {
|
|
setRecordData(result);
|
|
} else {
|
|
setError('악성채권 정보를 찾을 수 없습니다.');
|
|
toast.error('악성채권을 불러오는데 실패했습니다.');
|
|
}
|
|
} catch (err) {
|
|
console.error('악성채권 조회 실패:', err);
|
|
setError('악성채권 정보를 불러오는 중 오류가 발생했습니다.');
|
|
toast.error('악성채권을 불러오는데 실패했습니다.');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
loadData();
|
|
}, [recordId, isNewMode]);
|
|
|
|
// URL 쿼리 변경 감지
|
|
useEffect(() => {
|
|
if (!isNewMode && modeFromQuery === 'edit') {
|
|
setMode('edit');
|
|
} else if (!isNewMode && !modeFromQuery) {
|
|
setMode('view');
|
|
}
|
|
}, [modeFromQuery, isNewMode]);
|
|
|
|
// 로딩 중
|
|
if (isLoading) {
|
|
return <DetailPageSkeleton sections={2} fieldsPerSection={6} />;
|
|
}
|
|
|
|
// 에러 발생 (view/edit 모드에서)
|
|
if (error && !isNewMode) {
|
|
return (
|
|
<ErrorCard
|
|
type="network"
|
|
title="악성채권 정보를 불러올 수 없습니다"
|
|
description={error}
|
|
tips={[
|
|
'해당 악성채권이 존재하는지 확인해주세요',
|
|
'인터넷 연결 상태를 확인해주세요',
|
|
'잠시 후 다시 시도해주세요',
|
|
]}
|
|
homeButtonLabel="목록으로 이동"
|
|
homeButtonHref={BASE_PATH}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// 등록 모드
|
|
if (mode === 'new') {
|
|
return <BadDebtDetail mode="new" />;
|
|
}
|
|
|
|
// 수정 모드
|
|
if (mode === 'edit' && recordData) {
|
|
return <BadDebtDetail mode="edit" recordId={recordId} initialData={recordData} />;
|
|
}
|
|
|
|
// 상세 보기 모드
|
|
if (mode === 'view' && recordData) {
|
|
return <BadDebtDetail mode="view" recordId={recordId} initialData={recordData} />;
|
|
}
|
|
|
|
// 데이터 없음 (should not reach here)
|
|
return (
|
|
<ErrorCard
|
|
type="not-found"
|
|
title="악성채권을 찾을 수 없습니다"
|
|
description="요청하신 악성채권 정보가 존재하지 않습니다."
|
|
homeButtonLabel="목록으로 이동"
|
|
homeButtonHref={BASE_PATH}
|
|
/>
|
|
);
|
|
}
|