diff --git a/src/components/templates/IntegratedListTemplateV2.tsx b/src/components/templates/IntegratedListTemplateV2.tsx index 706cb19a..b7b57e69 100644 --- a/src/components/templates/IntegratedListTemplateV2.tsx +++ b/src/components/templates/IntegratedListTemplateV2.tsx @@ -5,7 +5,7 @@ import { LucideIcon, Trash2, Plus, Loader2 } from "lucide-react"; import { DateRangeSelector } from "@/components/molecules/DateRangeSelector"; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent } from "@/components/ui/tabs"; -import { TableSkeleton, MobileCardGridSkeleton } from "@/components/ui/skeleton"; +import { TableSkeleton, MobileCardGridSkeleton, StatCardGridSkeleton, ListPageSkeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; @@ -483,6 +483,27 @@ export function IntegratedListTemplateV2({ setShowDeleteDialog(false); }; + // 로딩 시 전체 페이지 스켈레톤 표시 + // - isLoading이 true면 전체 스켈레톤 표시 (헤더, 달력, 버튼, 카드, 테이블 모두) + // - 이렇게 하면 "따닥" 현상 없이 매끄러운 로딩 경험 제공 + if (isLoading) { + return ( + + 0} + statsCount={stats?.length || 4} + tableRows={pagination.itemsPerPage || 10} + tableColumns={tableColumns.length || 6} + mobileCards={6} + /> + + ); + } + return ( {/* 페이지 헤더 */} @@ -530,11 +551,15 @@ export function IntegratedListTemplateV2({ )} {/* 통계 카드 - 태블릿/데스크톱 */} - {stats && stats.length > 0 && ( + {isLoading && stats !== undefined ? ( +
+ +
+ ) : stats && stats.length > 0 ? (
- )} + ) : null} {/* 경고 배너 (통계 카드와 검색 영역 사이) */} {alertBanner} diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx index 43c99b61..ddc6a14e 100644 --- a/src/components/ui/skeleton.tsx +++ b/src/components/ui/skeleton.tsx @@ -380,6 +380,8 @@ function StatCardGridSkeleton({ // ============================================ interface ListPageSkeletonProps { showHeader?: boolean; + showDateRange?: boolean; // 달력 + 프리셋 버튼 영역 + showCreateButton?: boolean; // 등록 버튼 showFilters?: boolean; showStats?: boolean; statsCount?: number; @@ -390,6 +392,8 @@ interface ListPageSkeletonProps { function ListPageSkeleton({ showHeader = true, + showDateRange = true, + showCreateButton = true, showFilters = true, showStats = false, statsCount = 4, @@ -398,15 +402,45 @@ function ListPageSkeleton({ mobileCards = 6, }: ListPageSkeletonProps) { return ( -
- {/* 페이지 헤더 */} +
+ {/* 페이지 헤더 (타이틀 + 설명) */} {showHeader && ( -
-
- - +
+
+
+
+
- +
+ )} + + {/* 헤더 액션 영역: 날짜 범위 선택기 + 프리셋 버튼 + 등록 버튼 */} + {(showDateRange || showCreateButton) && ( +
+ {/* 날짜 범위 선택기 (왼쪽) */} + {showDateRange && ( + <> + {/* 날짜 입력 (시작일 ~ 종료일) */} +
+
+
+
+
+ {/* 프리셋 버튼들 (당해년도, 전전월, 전월, 당월, 어제, 오늘) */} +
+
+
+
+
+
+
+
+ + )} + {/* 등록 버튼 (오른쪽 끝) */} + {showCreateButton && ( +
+ )}
)}