From 208f4d08e53c19cd219fffce8c309282125c33d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EB=B3=91=EC=B2=A0?= Date: Thu, 22 Jan 2026 22:08:10 +0900 Subject: [PATCH] =?UTF-8?q?feat(WEB):=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=97=A4=EB=8D=94=20=EC=98=81?= =?UTF-8?q?=EC=97=AD=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ListPageSkeleton에 showDateRange, showCreateButton props 추가 - 페이지 타이틀, 날짜 범위 선택기, 프리셋 버튼, 등록 버튼 스켈레톤 구현 - Skeleton 컴포넌트 대신 직접 div 사용하여 색상 가시성 개선 - IntegratedListTemplateV2에서 새 props 전달 Co-Authored-By: Claude Opus 4.5 --- .../templates/IntegratedListTemplateV2.tsx | 31 ++++++++++-- src/components/ui/skeleton.tsx | 48 ++++++++++++++++--- 2 files changed, 69 insertions(+), 10 deletions(-) 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 && ( +
+ )}
)}