refactor: UniversalListPage externalIsLoading 지원 및 스켈레톤 개선

- UniversalListPage에 externalIsLoading prop 추가
- CardTransactionDetailClient DevFill 자동입력 기능 추가
- 여러 컴포넌트 로딩 상태 처리 개선
- skeleton 컴포넌트 확장

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-22 20:54:16 +09:00
parent 207520e1d6
commit 19237be4aa
71 changed files with 244 additions and 155 deletions

View File

@@ -451,7 +451,95 @@ function ListPageSkeleton({
}
// ============================================
// 12. 페이지 헤더 스켈레톤
// 12. 콘텐츠 스켈레톤 (범용 - ContentLoadingSpinner 대체용)
// ============================================
interface ContentSkeletonProps {
/** 스켈레톤 유형 */
type?: 'list' | 'detail' | 'form' | 'table' | 'cards';
/** 행/카드 개수 (default: 5) */
rows?: number;
/** 메시지 표시 (deprecated - 스켈레톤은 메시지 없음) */
text?: string;
}
function ContentSkeleton({
type = 'list',
rows = 5,
}: ContentSkeletonProps) {
switch (type) {
case 'detail':
return (
<div className="p-6 space-y-4 animate-pulse">
{/* 상세 정보 그리드 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{Array.from({ length: rows }).map((_, i) => (
<div key={i} className="space-y-2">
<Skeleton className="h-4 w-20" />
<Skeleton className="h-10 w-full rounded-md" />
</div>
))}
</div>
</div>
);
case 'form':
return (
<div className="p-6 space-y-4 animate-pulse">
{Array.from({ length: rows }).map((_, i) => (
<div key={i} className="space-y-2">
<Skeleton className="h-4 w-24" />
<Skeleton className="h-10 w-full rounded-md" />
</div>
))}
</div>
);
case 'table':
return (
<div className="p-4 animate-pulse">
<div className="space-y-3">
{Array.from({ length: rows }).map((_, i) => (
<div key={i} className="flex items-center gap-4">
<Skeleton className="h-4 w-4 rounded" />
<Skeleton className="h-4 flex-1" />
<Skeleton className="h-4 w-20" />
</div>
))}
</div>
</div>
);
case 'cards':
return (
<div className="p-4 space-y-3 animate-pulse">
{Array.from({ length: rows }).map((_, i) => (
<div key={i} className="p-4 border rounded-lg space-y-2">
<div className="flex items-center justify-between">
<Skeleton className="h-5 w-32" />
<Skeleton className="h-5 w-16 rounded-full" />
</div>
<Skeleton className="h-4 w-24" />
<Skeleton className="h-4 w-full" />
</div>
))}
</div>
);
case 'list':
default:
return (
<div className="p-4 space-y-3 animate-pulse">
{Array.from({ length: rows }).map((_, i) => (
<div key={i} className="flex items-center gap-3 py-2">
<Skeleton className="h-5 w-5 rounded" />
<Skeleton className="h-4 w-8" />
<Skeleton className="h-4 flex-1" />
<Skeleton className="h-8 w-8 rounded" />
</div>
))}
</div>
);
}
}
// ============================================
// 13. 페이지 헤더 스켈레톤
// ============================================
interface PageHeaderSkeletonProps {
showActions?: boolean;
@@ -498,4 +586,5 @@ export {
StatCardGridSkeleton,
ListPageSkeleton,
PageHeaderSkeleton,
ContentSkeleton,
};