From 6395f4d032dc3fa9d4b8fe3b8f1c150d8e6d300b Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Mon, 16 Mar 2026 09:17:59 +0900 Subject: [PATCH] Implement Card Pulse Animation and UI Enhancements - Added a new pulse animation for screen cards to enhance visual feedback. - Updated the background of the screen management list for improved aesthetics. - Refined the search input styling for better integration with the overall UI. - Enhanced screen card hover effects with dynamic glow based on screen type. - Adjusted layout spacing for a more consistent user experience. Made-with: Cursor --- .../admin/screenMng/screenMngList/page.tsx | 121 +++++++++++------- frontend/app/globals.css | 15 +++ 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx b/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx index e6bbf480..2978e025 100644 --- a/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx +++ b/frontend/app/(main)/admin/screenMng/screenMngList/page.tsx @@ -299,16 +299,16 @@ export default function ScreenManagementPage() { ) : ( -
+
{/* 카드 뷰 상단: 검색 + 카운트 */} -
+
setSearchTerm(e.target.value)} - className="pl-9 h-9 rounded-xl bg-muted/30 dark:bg-muted/30 border-border/50 dark:border-border/50 focus:bg-background focus:ring-2 focus:ring-primary/30 transition-colors" + className="pl-9 h-9 rounded-xl bg-card dark:bg-card border-border/50 shadow-sm focus:bg-card focus:ring-2 focus:ring-primary/30 transition-colors" /> {searchTerm && (
- {filteredScreens.map((screen) => ( -
handleScreenSelect(screen)} - onDoubleClick={() => handleDesignScreen(screen)} - > - {/* 좌측 타입별 컬러 바 */} -
-
- {/* 상단: 이름 + 호버 편집 */} -
-
-
{screen.screenName}
-
{screen.screenCode}
-
- 편집 -
- {/* 설명 (있으면) */} - {screen.description && ( -
{screen.description}
+ {filteredScreens.map((screen) => { + const screenType = (screen as { screenType?: string }).screenType || "form"; + const isSelected = selectedScreen?.screenId === screen.screenId; + const isRecentlyModified = screen.updatedDate && (Date.now() - new Date(screen.updatedDate).getTime()) < 7 * 24 * 60 * 60 * 1000; + + const typeColorClass = screenType === "grid" + ? "from-primary to-primary/20" + : screenType === "dashboard" + ? "from-warning to-warning/20" + : "from-success to-success/20"; + + const glowClass = screenType === "grid" + ? "hover:shadow-[0_8px_24px_-4px_rgba(0,0,0,0.1),0_0_20px_hsl(var(--primary)/0.1)] dark:hover:shadow-[0_8px_24px_-4px_rgba(0,0,0,0.4),0_0_24px_hsl(var(--primary)/0.15)]" + : screenType === "dashboard" + ? "hover:shadow-[0_8px_24px_-4px_rgba(0,0,0,0.1),0_0_20px_hsl(var(--warning)/0.1)] dark:hover:shadow-[0_8px_24px_-4px_rgba(0,0,0,0.4),0_0_24px_hsl(var(--warning)/0.12)]" + : "hover:shadow-[0_8px_24px_-4px_rgba(0,0,0,0.1),0_0_20px_hsl(var(--success)/0.1)] dark:hover:shadow-[0_8px_24px_-4px_rgba(0,0,0,0.4),0_0_24px_hsl(var(--success)/0.12)]"; + + const badgeBgClass = screenType === "grid" + ? "bg-primary/8 dark:bg-primary/15 text-primary" + : screenType === "dashboard" + ? "bg-warning/8 dark:bg-warning/15 text-warning" + : "bg-success/8 dark:bg-success/15 text-success"; + + return ( +
handleScreenSelect(screen)} + onDoubleClick={() => handleDesignScreen(screen)} + > + {/* 좌측 그라데이션 액센트 바 */} +
+ {isSelected && ( +
)} - {/* 중단: 메타 정보 */} -
- - - {screen.tableLabel || screen.tableName || "—"} - -
- {/* 하단: 타입 칩 + 날짜 */} -
- - {(screen as { screenType?: string }).screenType === "grid" ? "그리드" : (screen as { screenType?: string }).screenType === "dashboard" ? "대시보드" : "폼"} - - - {screen.updatedDate ? new Date(screen.updatedDate).toLocaleDateString("ko-KR", { month: "2-digit", day: "2-digit" }) : screen.createdDate ? new Date(screen.createdDate).toLocaleDateString("ko-KR", { month: "2-digit", day: "2-digit" }) : ""} - +
+ {/* Row 1: 이름 + 타입 뱃지 */} +
+
{screen.screenName}
+ + {screenType === "grid" ? "그리드" : screenType === "dashboard" ? "대시보드" : "폼"} + +
+ {/* Row 2: 스크린 코드 */} +
{screen.screenCode}
+ {/* Row 3: 테이블 칩 + 메타 */} +
+ + + {screen.tableLabel || screen.tableName || "—"} + +
+ {/* Row 4: 날짜 + 수정 상태 */} +
+ + {screen.updatedDate ? new Date(screen.updatedDate).toLocaleDateString("ko-KR", { year: "numeric", month: "2-digit", day: "2-digit" }) : screen.createdDate ? new Date(screen.createdDate).toLocaleDateString("ko-KR", { year: "numeric", month: "2-digit", day: "2-digit" }) : ""} + + {isRecentlyModified && ( + + + 수정됨 + + )} +
-
- ))} + ); + })}
{filteredScreens.length === 0 && (
diff --git a/frontend/app/globals.css b/frontend/app/globals.css index b3dbab89..8bbfd108 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -427,6 +427,21 @@ select { border-spacing: 0 !important; } +/* ===== 카드 펄스 도트 애니메이션 ===== */ +@keyframes screen-card-pulse { + 0%, 100% { opacity: 0; transform: scale(1); } + 50% { opacity: 0.35; transform: scale(2); } +} +.screen-card-pulse-dot::after { + content: ''; + position: absolute; + inset: -3px; + border-radius: 50%; + background: hsl(var(--success)); + opacity: 0; + animation: screen-card-pulse 2.5s ease-in-out infinite; +} + /* ===== 저장 테이블 막대기 애니메이션 ===== */ @keyframes saveBarDrop { 0% {