From 4d531f877a2ab1ddb7c40f1f953a20cf5292fa41 Mon Sep 17 00:00:00 2001 From: chloe_choi Date: Sat, 28 Mar 2026 11:29:46 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=ED=99=94=EC=9D=B4=ED=8A=B8=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20=EA=B8=80=EB=9E=98=EC=8A=A4=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/app/global.css | 228 ++++++++++++------ .../dashboard/components/AudioPlayer.tsx | 2 +- .../dashboard/components/IntroCard.tsx | 2 +- .../components/MeetingExpandedCard.tsx | 4 +- .../dashboard/components/OngoingMeetings.tsx | 2 +- .../dashboard/components/RecentMeetings.tsx | 2 +- client/features/dashboard/pages/Summary.tsx | 14 +- .../meeting/components/MeetingRoomContent.tsx | 10 +- client/features/schedule/pages/Schedule.tsx | 4 +- client/shared/adapters/meeting.ts | 22 +- client/shared/components/Layout.tsx | 14 +- client/shared/components/MeetingHeader.tsx | 16 +- client/shared/components/Sidebar.tsx | 14 +- 13 files changed, 223 insertions(+), 111 deletions(-) diff --git a/client/app/global.css b/client/app/global.css index 5837000..0d40b79 100644 --- a/client/app/global.css +++ b/client/app/global.css @@ -12,43 +12,43 @@ * A different format will require also updating the theme in tailwind.config.ts. */ :root { - --background: 270 30% 98%; - --foreground: 270 30% 15%; + --background: 270 20% 96%; + --foreground: 270 40% 12%; --card: 0 0% 100%; - --card-foreground: 270 30% 15%; + --card-foreground: 270 40% 12%; --popover: 0 0% 100%; - --popover-foreground: 270 30% 15%; + --popover-foreground: 270 40% 12%; --primary: 270 84% 56%; --primary-foreground: 0 0% 100%; - --secondary: 270 30% 92%; - --secondary-foreground: 270 84% 56%; + --secondary: 270 25% 90%; + --secondary-foreground: 270 84% 50%; - --muted: 270 15% 75%; - --muted-foreground: 270 20% 40%; + --muted: 270 15% 70%; + --muted-foreground: 270 25% 35%; --accent: 270 84% 56%; --accent-foreground: 0 0% 100%; - --destructive: 0 84% 60%; + --destructive: 0 84% 55%; --destructive-foreground: 0 0% 100%; - --border: 270 30% 90%; - --input: 270 20% 96%; + --border: 270 30% 85%; + --input: 270 20% 92%; --ring: 270 84% 56%; --radius: 0.75rem; - --sidebar-background: 270 30% 98%; - --sidebar-foreground: 270 30% 15%; + --sidebar-background: 0 0% 100%; + --sidebar-foreground: 270 40% 12%; --sidebar-primary: 270 84% 56%; --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 270 30% 94%; - --sidebar-accent-foreground: 270 84% 50%; - --sidebar-border: 270 30% 90%; + --sidebar-accent: 270 30% 92%; + --sidebar-accent-foreground: 270 84% 45%; + --sidebar-border: 270 25% 88%; --sidebar-ring: 270 84% 56%; } @@ -92,41 +92,41 @@ } .light { - --background: 270 30% 98%; - --foreground: 270 30% 15%; + --background: 270 20% 96%; + --foreground: 270 40% 12%; --card: 0 0% 100%; - --card-foreground: 270 30% 15%; + --card-foreground: 270 40% 12%; --popover: 0 0% 100%; - --popover-foreground: 270 30% 15%; + --popover-foreground: 270 40% 12%; --primary: 270 84% 56%; --primary-foreground: 0 0% 100%; - --secondary: 270 30% 92%; - --secondary-foreground: 270 84% 56%; + --secondary: 270 25% 90%; + --secondary-foreground: 270 84% 50%; - --muted: 270 15% 75%; - --muted-foreground: 270 20% 40%; + --muted: 270 15% 70%; + --muted-foreground: 270 25% 35%; --accent: 270 84% 56%; --accent-foreground: 0 0% 100%; - --destructive: 0 84% 60%; + --destructive: 0 84% 55%; --destructive-foreground: 0 0% 100%; - --border: 270 30% 90%; - --input: 270 20% 96%; + --border: 270 30% 85%; + --input: 270 20% 92%; --ring: 270 84% 56%; - --sidebar-background: 270 30% 98%; - --sidebar-foreground: 270 30% 15%; + --sidebar-background: 0 0% 100%; + --sidebar-foreground: 270 40% 12%; --sidebar-primary: 270 84% 56%; --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 270 30% 94%; - --sidebar-accent-foreground: 270 84% 50%; - --sidebar-border: 270 30% 90%; + --sidebar-accent: 270 30% 92%; + --sidebar-accent-foreground: 270 84% 45%; + --sidebar-border: 270 25% 88%; --sidebar-ring: 270 84% 56%; } } @@ -146,7 +146,7 @@ } body.light { - background: linear-gradient(145deg, hsl(270, 40%, 97%) 0%, hsl(0, 0%, 100%) 40%, hsl(330, 30%, 97%) 100%); + background: linear-gradient(145deg, hsl(270, 25%, 94%) 0%, hsl(270, 15%, 96%) 40%, hsl(330, 20%, 95%) 100%); background-attachment: fixed; } @@ -182,73 +182,163 @@ } } -/* ── Design Token Utility Classes ── */ +/* ══════════════════════════════════════════ + Design Token System (로그인 카드 스타일 기반) + ══════════════════════════════════════════ */ -/* 모달 오버레이 */ -.om-modal-overlay { - @apply fixed inset-0 z-50 flex items-center justify-center p-4; +/* ── 카드 ── */ +.om-card { + @apply rounded-2xl transition-all duration-300; } - -.om-modal-overlay.dark { - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(8px); +.dark .om-card { + @apply bg-gradient-to-br from-purple-900/40 via-pink-900/20 to-black/60 + border border-purple-500/30 hover:border-purple-500/60 + backdrop-blur-xl shadow-lg shadow-purple-500/10; } - -.om-modal-overlay.light { - background: rgba(120, 80, 160, 0.15); - backdrop-filter: blur(8px); +.light .om-card { + @apply bg-white border border-purple-200/60 hover:border-purple-300 + shadow-md hover:shadow-lg; + box-shadow: + 0 4px 16px rgba(147, 51, 234, 0.06), + 0 1px 3px rgba(147, 51, 234, 0.04); +} +.light .om-card:hover { + box-shadow: + 0 8px 30px rgba(147, 51, 234, 0.1), + 0 2px 6px rgba(147, 51, 234, 0.06); } -/* 모달 카드 — light: 글래스모피즘 */ +/* ── 모달 카드 ── */ .om-modal-card { @apply rounded-2xl transition-all duration-300; } - .dark .om-modal-card { - @apply bg-gradient-to-br from-purple-900/60 via-purple-950/80 to-pink-900/40 - border border-purple-500/25 backdrop-blur-xl + @apply bg-gradient-to-br from-purple-900/60 via-pink-900/30 to-black/70 + border border-purple-500/30 backdrop-blur-xl shadow-2xl shadow-purple-500/20; } - .light .om-modal-card { - @apply bg-white/95 backdrop-blur-xl - border border-purple-200/80 - shadow-2xl shadow-purple-300/30; + @apply bg-white backdrop-blur-xl border border-purple-200/70; box-shadow: - 0 25px 50px -12px rgba(147, 51, 234, 0.15), - 0 0 0 1px rgba(192, 132, 252, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.6); + 0 25px 60px -12px rgba(147, 51, 234, 0.15), + 0 8px 24px rgba(147, 51, 234, 0.06), + 0 0 0 1px rgba(192, 132, 252, 0.08); +} + +/* ── 인풋 ── */ +.om-input { + @apply w-full px-4 py-3 rounded-xl transition-all duration-200; +} +.dark .om-input { + @apply border border-purple-500/30 bg-white/5 backdrop-blur-sm + text-white placeholder-white/40 + focus:bg-white/10 focus:border-purple-500/60 focus:ring-2 focus:ring-purple-500/30; } +.light .om-input { + @apply border-2 border-purple-300/60 bg-purple-50 + text-purple-950 placeholder-purple-400/60 + focus:bg-white focus:border-purple-500 focus:ring-2 focus:ring-purple-300/40 + shadow-sm; +} + +/* ── 라벨 ── */ +.om-label { + @apply text-sm font-semibold flex items-center gap-2; +} +.dark .om-label { @apply text-white/90; } +.light .om-label { @apply text-purple-900; } + +/* ── 아이콘 (보조) ── */ +.om-icon { @apply w-4 h-4; } +.dark .om-icon { @apply text-purple-400; } +.light .om-icon { @apply text-purple-600; } -/* 그래디언트 버튼 — Primary CTA */ +/* ── 제목 ── */ +.om-title { + @apply text-2xl font-bold; +} +.dark .om-title { @apply text-white; } +.light .om-title { @apply text-purple-950; } + +.om-subtitle { + @apply text-sm; +} +.dark .om-subtitle { @apply text-white/60; } +.light .om-subtitle { @apply text-purple-600; } + +/* ── 구분선 ── */ +.om-divider { + @apply h-px; +} +.dark .om-divider { @apply bg-purple-500/20; } +.light .om-divider { @apply bg-purple-200; } + +/* ── 보조 텍스트/링크 ── */ +.om-text-link { + @apply font-semibold transition-colors; +} +.dark .om-text-link { @apply text-purple-300 hover:text-pink-300; } +.light .om-text-link { @apply text-purple-600 hover:text-purple-800; } + +/* ── 그래디언트 버튼 — Primary CTA ── */ .om-btn-primary { @apply bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white font-semibold rounded-xl shadow-lg shadow-purple-500/30 - hover:shadow-xl + hover:shadow-xl hover:shadow-purple-500/50 transition-all duration-300 - disabled:opacity-50 disabled:cursor-not-allowed; + disabled:opacity-50 disabled:cursor-not-allowed + flex items-center justify-center gap-2; } -/* 그래디언트 텍스트 */ -.om-gradient-text { - @apply bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent; +/* ── 세컨더리 버튼 ── */ +.om-btn-secondary { + @apply rounded-xl font-medium transition-all duration-200; +} +.dark .om-btn-secondary { + @apply border border-purple-500/30 text-white/70 + hover:bg-purple-500/20 hover:text-white; +} +.light .om-btn-secondary { + @apply border-2 border-purple-200/70 text-purple-700 + hover:bg-purple-50 hover:border-purple-300 + hover:shadow-sm; } -/* 텍스트 색상 (CSS 변수 기반 — dark/light 자동 대응) */ -.om-text-primary { - color: hsl(var(--foreground)); +/* ── 섹션 패널 ── */ +.om-panel { + @apply rounded-2xl p-6 transition-all duration-300; +} +.dark .om-panel { + @apply bg-gradient-to-br from-purple-900/30 via-purple-950/20 to-pink-900/10 + border border-purple-500/20 backdrop-blur-xl; +} +.light .om-panel { + @apply bg-white border border-purple-200/60 + shadow-md; + box-shadow: + 0 4px 16px rgba(147, 51, 234, 0.05), + 0 1px 3px rgba(147, 51, 234, 0.03); } -.om-text-secondary { - color: hsl(var(--muted-foreground)); +/* ── 뱃지 ── */ +.om-badge { + @apply px-2.5 py-1 rounded-full text-xs font-semibold; } +.dark .om-badge { @apply bg-purple-500/20 text-purple-300; } +.light .om-badge { @apply bg-purple-100 text-purple-700 border border-purple-200/50; } -.om-text-muted { - color: hsl(var(--muted-foreground) / 0.7); +/* ── 그래디언트 텍스트 ── */ +.om-gradient-text { + @apply bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent; } +/* ── 텍스트 색상 (CSS 변수 기반) ── */ +.om-text-primary { color: hsl(var(--foreground)); } +.om-text-secondary { color: hsl(var(--muted-foreground)); } +.om-text-muted { color: hsl(var(--muted-foreground) / 0.7); } + @keyframes blob { 0%, 100% { transform: translate(0, 0) scale(1); } 33% { transform: translate(30px, -50px) scale(1.1); } diff --git a/client/features/dashboard/components/AudioPlayer.tsx b/client/features/dashboard/components/AudioPlayer.tsx index a0151db..458a056 100644 --- a/client/features/dashboard/components/AudioPlayer.tsx +++ b/client/features/dashboard/components/AudioPlayer.tsx @@ -25,7 +25,7 @@ function formatTime(seconds: number): string { function formatDateTime(dateStr: string): string { if (!dateStr) return "-"; const d = new Date(dateStr); - return d.toLocaleString("ko-KR", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" }); + return d.toLocaleString("ko-KR", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZone: "Asia/Seoul" }); } function formatDuration(startedAt: string, endedAt: string): string { diff --git a/client/features/dashboard/components/IntroCard.tsx b/client/features/dashboard/components/IntroCard.tsx index 5accfb2..f281373 100644 --- a/client/features/dashboard/components/IntroCard.tsx +++ b/client/features/dashboard/components/IntroCard.tsx @@ -6,7 +6,7 @@ interface IntroCardProps { export default function IntroCard({ onCreateMeeting }: IntroCardProps) { return ( -
+
diff --git a/client/features/dashboard/components/MeetingExpandedCard.tsx b/client/features/dashboard/components/MeetingExpandedCard.tsx index bfe937a..3df99b7 100644 --- a/client/features/dashboard/components/MeetingExpandedCard.tsx +++ b/client/features/dashboard/components/MeetingExpandedCard.tsx @@ -159,7 +159,7 @@ export default function MeetingExpandedCard({ const isRegenerating = minutesData?.status === "REGENERATING" || regenerateMutation.isPending; return ( -
+
@@ -383,7 +383,7 @@ export default function MeetingExpandedCard({ } const [, type, startMs, , speaker, text] = match; const startDate = new Date(Number(startMs)); - const time = startDate.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit", second: "2-digit" }); + const time = startDate.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit", second: "2-digit", timeZone: "Asia/Seoul" }); const isVoice = type === "VOICE"; return (
diff --git a/client/features/dashboard/components/OngoingMeetings.tsx b/client/features/dashboard/components/OngoingMeetings.tsx index 06358c0..34e9d5d 100644 --- a/client/features/dashboard/components/OngoingMeetings.tsx +++ b/client/features/dashboard/components/OngoingMeetings.tsx @@ -114,7 +114,7 @@ export default function OngoingMeetings() { return (
diff --git a/client/features/dashboard/pages/Summary.tsx b/client/features/dashboard/pages/Summary.tsx index 6f125a3..7501163 100644 --- a/client/features/dashboard/pages/Summary.tsx +++ b/client/features/dashboard/pages/Summary.tsx @@ -93,7 +93,7 @@ export default function Summary() { return ( <> -
+
{!expandedMeeting && (
@@ -104,7 +104,7 @@ export default function Summary() { 모든 회의 기록을 한눈에 관리하고 확인하세요

-
+
{([ { id: "all", label: "모든 회의" }, { id: "scheduled", label: "예정된 회의" }, @@ -116,8 +116,8 @@ export default function Summary() { onClick={() => setActiveTab(tab.id)} className={`px-5 py-2.5 rounded-lg font-semibold transition-all duration-200 ${ activeTab === tab.id - ? "dark:bg-purple-600 dark:text-white light:bg-gradient-to-r light:from-purple-600 light:to-purple-700 light:text-white light:shadow-lg light:shadow-purple-300/40" - : "dark:bg-purple-500/10 dark:text-white/70 light:bg-white/60 light:text-purple-700 light:border light:border-purple-300/30 dark:hover:bg-purple-500/20 light:hover:bg-purple-100/70" + ? "dark:bg-purple-600 dark:text-white light:bg-purple-600 light:text-white light:shadow-md light:shadow-purple-400/30" + : "dark:bg-purple-500/10 dark:text-white/70 light:bg-white light:text-purple-600 light:border light:border-purple-300/60 light:shadow-sm dark:hover:bg-purple-500/20 light:hover:bg-purple-50 light:hover:border-purple-400" }`} > {tab.label} @@ -133,7 +133,7 @@ export default function Summary() {