From b404072b3b4b236bcb2e8f969664ba03a7de8da8 Mon Sep 17 00:00:00 2001 From: admin <1297598740@qq.com> Date: Fri, 30 Jan 2026 14:12:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=95=E5=85=A5=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E3=80=81=E5=BF=AB=E9=80=9F=E6=93=8D=E4=BD=9C=E3=80=81=E6=B6=88?= =?UTF-8?q?=E8=B4=B9=E8=B6=8B=E5=8A=BF=E5=9B=BE=E8=A1=A8=E3=80=81=E5=81=A5?= =?UTF-8?q?=E5=BA=B7=E8=AF=84=E5=88=86=E5=8D=A1=E7=AD=89=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E6=96=B0UI=E7=BB=84=E4=BB=B6=E5=8F=8A=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E6=9B=B4=E6=96=B0=E5=BC=80=E5=8F=91=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8F=98=E9=87=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 2 +- implementation_plan.md | 57 + .../account/AccountForm/AccountForm.css | 241 ++-- .../CreateFirstAccountModal.css | 124 +- .../ai/VoiceInputModal/VoiceInputModal.css | 451 ++++--- src/components/charts/SpendingTrendChart.css | 31 + src/components/charts/SpendingTrendChart.tsx | 132 +- .../common/Navigation/Navigation.css | 278 ++-- .../ContributionHeatmap.css | 143 +- .../DailyInsightCard/DailyInsightCard.css | 344 ++--- .../home/HealthScoreCard/HealthScoreCard.css | 86 ++ .../home/HealthScoreCard/HealthScoreCard.tsx | 67 + .../HealthScoreModal/HealthScoreModal.css | 86 +- .../home/QuickActionsBox/QuickActionsBox.css | 73 + .../home/QuickActionsBox/QuickActionsBox.tsx | 41 + .../ledger/LedgerSelector/LedgerSelector.css | 309 ++--- .../TransactionForm/TransactionForm.css | 255 ++-- .../TransactionList/TransactionList.css | 156 +-- src/pages/Home/Home.css | 1192 +++-------------- src/pages/Home/Home.tsx | 614 ++------- 20 files changed, 1846 insertions(+), 2836 deletions(-) create mode 100644 implementation_plan.md create mode 100644 src/components/charts/SpendingTrendChart.css create mode 100644 src/components/home/HealthScoreCard/HealthScoreCard.css create mode 100644 src/components/home/HealthScoreCard/HealthScoreCard.tsx create mode 100644 src/components/home/QuickActionsBox/QuickActionsBox.css create mode 100644 src/components/home/QuickActionsBox/QuickActionsBox.tsx diff --git a/.env.development b/.env.development index 9ffe781..3f6b173 100644 --- a/.env.development +++ b/.env.development @@ -1 +1 @@ -VITE_API_BASE_URL=http://localhost:2612/api/v1 +VITE_API_BASE_URL=https://bk.swalktech.top/api/v1 diff --git a/implementation_plan.md b/implementation_plan.md new file mode 100644 index 0000000..03cf50e --- /dev/null +++ b/implementation_plan.md @@ -0,0 +1,57 @@ +# Implementation Plan - Home Page Modernization (Bento Grid) + +## 目标 +重构首页布局,采用现代化的 **Bento Grid (便当盒网格)** 设计理念,结合 "Ultra Premium Glass" 视觉风格,打造极具科技感与通透感的仪表盘界面。 + +## 设计理念 +1. **Bento Grid 布局**: 采用统一的网格系统,将不同功能的模块(净资产、图表、快捷操作等)像便当盒一样规整而灵活地排列。 +2. **层级重构**: 打破原有的从上到下流式布局,增强模块间的关联性和视觉张力。 +3. **视觉升级**: + - **Hero Area**: 强化 "每日洞察" 和 "净资产" 的核心地位。 + - **交互感**: 每个 Grid Item 都是一个独立的玻璃容器,拥有独立的悬停动效。 + - **信息密度**: 优化空间利用率,减少无意义的留白,增加信息呈现的精致度。 + +## 改造方案 + +### 1. 结构重构 (`src/pages/Home/Home.tsx`) +废弃原有的 `
` 堆叠结构,引入统一的 `.home-bento-grid` 容器。 + +**网格规划 (Desktop 4列布局):** +- **Row 1**: + - `DailyInsightCard` -> 跨 3 列 (主要视觉焦点) + - `HealthScore` (新封装) -> 跨 1 列 (右上角状态展示) +- **Row 2**: + - `NetWorthCard` -> 跨 2 列 (主要资产展示) + - `AssetsCard` -> 1 列 + - `LiabilitiesCard` -> 1 列 +- **Row 3**: + - `SpendingTrendChart` -> 跨 3 列 (宽屏图表) + - `QuickActions` (重组) -> 1 列 (垂直排列或网格排列) +- **Row 4**: + - `RecentTransactions` -> 跨 2 列 + - `ContributionHeatmap` -> 跨 2 列 (或移动到底部作为页脚装饰) + +*(具体排列会根据响应式断点自动调整)* + +### 2. 样式升级 (`src/pages/Home/Home.css`) +- **Grid Container**: 使用 `display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px;` +- **Glass Tiles**: 所有卡片统一使用 `.bento-card` 基础类,具备: + - 高级磨砂玻璃背景 (`backdrop-filter: blur(24px)`) + - 细腻的边框 (`1px solid rgba(255,255,255,0.4)`) + - 深度阴影与悬浮微动效 + +### 3. 组件优化 +- **DailyInsightCard**: 调整为更紧凑的横向布局,适应 Grid。 +- **QuickActions**: 从 "3个大卡片" 优化为 "工具箱(Toolbox)" 样式,占用更小空间但更精致。 +- **Header**: 简化顶部 Greeting 区域,使其与 Grid 融为一体。 + +## 涉及文件 +- `src/pages/Home/Home.tsx`: 结构重写 +- `src/pages/Home/Home.css`: 样式重写 +- `src/components/home/DailyInsightCard/DailyInsightCard.css`: 适配 Grid +- `src/components/home/ContributionHeatmap/ContributionHeatmap.css`: 样式微调 + +## 预期效果 +- **现代化**: 类似 iOS Widget 或 Linear 的仪表盘风格。 +- **美观**: 统一的光影与圆角语言。 +- **响应式**: 平滑适配 桌面 -> 平板 -> 手机。 diff --git a/src/components/account/AccountForm/AccountForm.css b/src/components/account/AccountForm/AccountForm.css index e86a8ff..e5fe48b 100644 --- a/src/components/account/AccountForm/AccountForm.css +++ b/src/components/account/AccountForm/AccountForm.css @@ -1,5 +1,5 @@ /** - * AccountForm Component Styles + * AccountForm Component Styles - Ultra Premium Glass */ .account-form { @@ -8,48 +8,52 @@ height: 100%; padding: 0; background-color: var(--color-bg); - border-radius: 16px; + border-radius: 20px; max-width: 500px; margin: 0 auto; overflow: hidden; + box-shadow: + 0 20px 50px rgba(0, 0, 0, 0.1), + 0 0 0 1px rgba(0, 0, 0, 0.05); } .account-form__content { flex: 1; overflow-y: auto; - padding: 1.5rem; + padding: 1.75rem; display: flex; flex-direction: column; - gap: 1.25rem; + gap: 1.5rem; -webkit-overflow-scrolling: touch; } -/* Spacer removal - handled by flex layout now */ - /* Form actions */ .account-form__actions { flex-shrink: 0; display: flex; gap: 1rem; - padding: 1rem 1.5rem 1.5rem 1.5rem; - background-color: var(--color-bg); - border-top: 1px solid var(--color-border); + padding: 1.25rem 1.75rem; + background-color: rgba(255, 255, 255, 0.5); + border-top: 1px solid rgba(0, 0, 0, 0.05); + backdrop-filter: blur(10px); z-index: 10; } .account-form__title { margin: 0 0 0.5rem 0; - font-size: 1.25rem; - font-weight: 600; - color: var(--color-text); + font-family: 'Outfit', sans-serif; + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); text-align: center; + letter-spacing: -0.02em; } /* Field styles */ .account-form__field { display: flex; flex-direction: column; - gap: 0.5rem; + gap: 0.6rem; } .account-form__field--half { @@ -58,203 +62,241 @@ .account-form__row { display: flex; - gap: 1rem; + gap: 1.25rem; } .account-form__label { - font-size: 0.875rem; - font-weight: 500; - color: var(--color-text); + font-size: 0.9rem; + font-weight: 600; + color: var(--text-secondary); + margin-left: 2px; } .account-form__required { - color: var(--color-error); + color: #ef4444; } .account-form__input { - padding: 0.75rem 1rem; - border: 1px solid var(--color-border); - border-radius: 8px; + padding: 0.85rem 1rem; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 12px; font-size: 1rem; - background-color: var(--color-bg); - color: var(--color-text); - transition: border-color 0.2s ease; + background-color: rgba(255, 255, 255, 0.5); + color: var(--text-primary); + transition: all 0.2s ease; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.02); } .account-form__input:focus { outline: none; - border-color: var(--color-primary); + border-color: var(--accent-primary); + background-color: #fff; + box-shadow: 0 0 0 3px rgba(var(--accent-primary-rgb, 59, 130, 246), 0.15); } .account-form__input--error { - border-color: var(--color-error); + border-color: #ef4444; + background-color: rgba(239, 68, 68, 0.02); } .account-form__input--number { flex: 1; + font-family: 'Outfit', sans-serif; + font-weight: 600; } .account-form__input:disabled { opacity: 0.6; cursor: not-allowed; + background-color: rgba(0, 0, 0, 0.03); } .account-form__error { - font-size: 0.75rem; - color: var(--color-error); + font-size: 0.8rem; + color: #ef4444; + margin-left: 2px; + animation: slideDownFade 0.2s ease-out; } .account-form__hint { - font-size: 0.75rem; - color: var(--color-text-secondary); + font-size: 0.8rem; + color: var(--text-tertiary); + margin-left: 2px; } /* Input group for currency + amount */ .account-form__input-group { display: flex; - gap: 0.5rem; + gap: 0.75rem; } .account-form__currency-select { - padding: 0.75rem; - border: 1px solid var(--color-border); - border-radius: 8px; - font-size: 0.875rem; - background-color: var(--color-bg); - color: var(--color-text); + padding: 0.85rem; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 12px; + font-size: 0.95rem; + font-weight: 600; + background-color: rgba(255, 255, 255, 0.5); + color: var(--text-primary); cursor: pointer; - min-width: 80px; + min-width: 90px; + transition: all 0.2s ease; } .account-form__currency-select:focus { outline: none; - border-color: var(--color-primary); -} - -.account-form__currency-select option { - background-color: var(--bg-elevated); - color: var(--text-primary); + border-color: var(--accent-primary); + background-color: #fff; } /* Account type grid */ .account-form__type-grid { display: grid; grid-template-columns: repeat(3, 1fr); - gap: 0.5rem; + gap: 0.75rem; } .account-form__type-btn { display: flex; flex-direction: column; align-items: center; - gap: 0.25rem; - padding: 0.75rem 0.5rem; - border: 1px solid var(--color-border); - border-radius: 8px; - background-color: var(--color-bg); + gap: 0.5rem; + padding: 1rem 0.5rem; + border: 1px solid rgba(0, 0, 0, 0.06); + border-radius: 16px; + background-color: rgba(255, 255, 255, 0.4); cursor: pointer; - transition: all 0.2s ease; + transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); } .account-form__type-btn:hover { - border-color: var(--color-primary); + border-color: var(--accent-primary); + background-color: rgba(255, 255, 255, 0.8); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } .account-form__type-btn--selected { - border-color: var(--color-primary); - background-color: rgba(24, 144, 255, 0.1); + border-color: var(--accent-primary); + background-color: rgba(var(--accent-primary-rgb, 59, 130, 246), 0.08); + box-shadow: 0 0 0 2px rgba(var(--accent-primary-rgb, 59, 130, 246), 0.1); +} + +.account-form__type-btn--selected .account-form__type-icon { + transform: scale(1.1); + color: var(--accent-primary); +} + +.account-form__type-btn--selected .account-form__type-label { + color: var(--accent-primary); + font-weight: 600; } .account-form__type-btn:disabled { opacity: 0.6; cursor: not-allowed; + transform: none; } .account-form__type-icon { - font-size: 1.5rem; + font-size: 1.75rem; + color: var(--text-secondary); + transition: all 0.3s ease; } .account-form__type-label { - font-size: 0.75rem; - color: var(--color-text); + font-size: 0.8rem; + color: var(--text-secondary); + transition: all 0.2s ease; } /* Icon grid */ .account-form__icon-grid { display: flex; flex-wrap: wrap; - gap: 0.5rem; + gap: 0.75rem; } .account-form__icon-btn { - width: 40px; - height: 40px; + width: 44px; + height: 44px; display: flex; align-items: center; justify-content: center; - border: 1px solid var(--color-border); - border-radius: 8px; - background-color: var(--color-bg); - font-size: 1.25rem; + border: 1px solid rgba(0, 0, 0, 0.06); + border-radius: 12px; + background-color: rgba(255, 255, 255, 0.4); + font-size: 1.4rem; cursor: pointer; - transition: all 0.2s ease; + transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); } .account-form__icon-btn:hover { - border-color: var(--color-primary); + border-color: var(--accent-primary); + transform: scale(1.1) rotate(5deg); + background-color: white; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); } .account-form__icon-btn--selected { - border-color: var(--color-primary); - background-color: rgba(24, 144, 255, 0.1); + border-color: var(--accent-primary); + background-color: rgba(var(--accent-primary-rgb, 59, 130, 246), 0.1); + transform: scale(1.1); + box-shadow: 0 0 0 2px rgba(var(--accent-primary-rgb, 59, 130, 246), 0.15); } -.account-form__icon-btn:disabled { - opacity: 0.6; - cursor: not-allowed; -} - - - .account-form__btn { flex: 1; - padding: 0.875rem 1.5rem; + padding: 1rem 1.5rem; border: none; - border-radius: 8px; + border-radius: 14px; font-size: 1rem; - font-weight: 500; + font-weight: 600; cursor: pointer; transition: all 0.2s ease; + letter-spacing: 0.01em; +} + +.account-form__btn--cancel { + background-color: rgba(0, 0, 0, 0.05); + color: var(--text-secondary); + border: 1px solid transparent; +} + +.account-form__btn--cancel:hover:not(:disabled) { + background-color: rgba(0, 0, 0, 0.08); + color: var(--text-primary); +} + +.account-form__btn--submit { + background: var(--accent-primary); + color: #fff; + box-shadow: 0 4px 12px rgba(var(--accent-primary-rgb, 59, 130, 246), 0.3); +} + +.account-form__btn--submit:hover:not(:disabled) { + filter: brightness(1.1); + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(var(--accent-primary-rgb, 59, 130, 246), 0.4); } .account-form__btn:disabled { opacity: 0.6; cursor: not-allowed; -} - -.account-form__btn--cancel { - background-color: var(--color-bg-secondary); - color: var(--color-text); - border: 1px solid var(--color-border); -} - -.account-form__btn--cancel:hover:not(:disabled) { - background-color: var(--color-border); -} - -.account-form__btn--submit { - background-color: var(--color-primary); - color: #fff; -} - -.account-form__btn--submit:hover:not(:disabled) { - background-color: var(--color-primary-hover); + box-shadow: none; } /* Responsive styles */ @media (max-width: 480px) { .account-form { - padding: 1rem; + padding: 0; + max-width: 100%; + height: 100vh; + border-radius: 0; + } + + .account-form__content { + padding: 1.5rem; } .account-form__type-grid { @@ -267,6 +309,7 @@ } .account-form__actions { - flex-direction: column-reverse; + padding: 1rem 1.5rem; + padding-bottom: max(1rem, env(safe-area-inset-bottom)); } } \ No newline at end of file diff --git a/src/components/account/CreateFirstAccountModal/CreateFirstAccountModal.css b/src/components/account/CreateFirstAccountModal/CreateFirstAccountModal.css index 180f453..2bca559 100644 --- a/src/components/account/CreateFirstAccountModal/CreateFirstAccountModal.css +++ b/src/components/account/CreateFirstAccountModal/CreateFirstAccountModal.css @@ -1,127 +1,127 @@ +/** + * CreateFirstAccountModal Styles - Ultra Premium Deep Glass + */ + .create-first-account-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; - background-color: rgba(22, 22, 26, 0.6); - /* Slightly dark overlay */ + background-color: rgba(0, 0, 0, 0.65); display: flex; justify-content: center; align-items: center; z-index: 1000; - backdrop-filter: blur(8px); - /* Stronger blur for focus */ - animation: fadeIn 0.4s ease-out; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + animation: fadeIn 0.5s ease-out; } .create-first-account-modal { - /* Modern Card Style */ - background: #ffffff; - border-radius: 24px; + /* Ultra Premium Deep Glass */ + background: rgba(30, 30, 40, 0.85); + /* Dark transparent bg */ + border-radius: 32px; padding: 3rem 2.5rem; width: 90%; - max-width: 420px; + max-width: 440px; text-align: center; position: relative; box-shadow: - 0 20px 25px -5px rgba(0, 0, 0, 0.1), - 0 10px 10px -5px rgba(0, 0, 0, 0.04); + 0 40px 100px -20px rgba(0, 0, 0, 0.6), + inset 0 1px 0 0 rgba(255, 255, 255, 0.15); border: 1px solid rgba(255, 255, 255, 0.1); - /* Subtle border */ overflow: hidden; - animation: scaleIn 0.3s cubic-bezier(0.16, 1, 0.3, 1); -} - -/* Dark mode support if root has dark class, or rely on variables */ -@media (prefers-color-scheme: dark) { - .create-first-account-modal { - background: #1e1e24; - /* Dark card bg */ - border: 1px solid rgba(255, 255, 255, 0.05); - } - - .create-first-account-modal h2 { - color: #f3f4f6 !important; - } - - .create-first-account-modal p { - color: #9ca3af !important; - } + animation: scaleIn 0.5s cubic-bezier(0.19, 1, 0.22, 1); + color: white; } /* Decorative glow */ .create-first-account-modal::before { content: ''; position: absolute; - top: -60px; + top: -80px; left: 50%; transform: translateX(-50%); - width: 140px; - height: 140px; - background: var(--accent-primary); - filter: blur(70px); - opacity: 0.15; + width: 200px; + height: 200px; + background: radial-gradient(circle, var(--accent-primary) 0%, transparent 70%); + filter: blur(60px); + opacity: 0.25; border-radius: 50%; pointer-events: none; } .modal-icon { - width: 72px; - height: 72px; - margin: 0 auto 1.5rem; - background: var(--bg-hover); + width: 80px; + height: 80px; + margin: 0 auto 1.75rem; + background: rgba(255, 255, 255, 0.08); + /* Transparent styling */ color: var(--accent-primary); - border-radius: 20px; + border-radius: 24px; display: flex; align-items: center; justify-content: center; - transition: transform 0.3s ease; + transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + border: 1px solid rgba(255, 255, 255, 0.05); + backdrop-filter: blur(10px); + box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.2); } .create-first-account-modal:hover .modal-icon { - transform: scale(1.05) rotate(-5deg); + transform: scale(1.1) rotate(-8deg); + background: rgba(255, 255, 255, 0.12); + color: white; + /* Icon glows white on hover */ + border-color: rgba(255, 255, 255, 0.2); } .create-first-account-modal h2 { - font-size: 1.5rem; - font-weight: 700; - color: var(--text-primary); + font-family: 'Outfit', sans-serif; + font-size: 1.75rem; + font-weight: 800; + color: white; + /* Always white for deep glass */ margin-bottom: 0.75rem; - line-height: 1.3; + line-height: 1.2; + letter-spacing: -0.02em; } .create-first-account-modal p { - font-size: 1rem; - color: var(--text-secondary); + font-size: 1.05rem; + color: rgba(255, 255, 255, 0.7); + /* Subtle white text */ line-height: 1.6; - margin-bottom: 2rem; + margin-bottom: 2.25rem; + max-width: 90%; + margin-left: auto; + margin-right: auto; } .create-btn { width: 100%; - padding: 1rem; + padding: 1.1rem; background: var(--accent-primary); color: white; border: none; - border-radius: 16px; - font-size: 1rem; - font-weight: 600; + border-radius: 18px; + font-size: 1.05rem; + font-weight: 700; display: flex; align-items: center; justify-content: center; gap: 0.75rem; cursor: pointer; - transition: all 0.2s ease; - box-shadow: 0 4px 6px -1px var(--shadow-color); + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + box-shadow: 0 10px 25px -5px rgba(var(--accent-primary-rgb, 59, 130, 246), 0.4); } .create-btn:hover { - filter: brightness(1.05); - /* Slightly brighter */ - background: var(--accent-primary-hover); - transform: translateY(-2px); - box-shadow: 0 10px 15px -3px var(--shadow-color); + filter: brightness(1.1); + transform: translateY(-3px) scale(1.02); + box-shadow: 0 20px 40px -10px rgba(var(--accent-primary-rgb, 59, 130, 246), 0.5); } .create-btn:active { @@ -141,7 +141,7 @@ @keyframes scaleIn { from { opacity: 0; - transform: scale(0.95) translateY(10px); + transform: scale(0.9) translateY(20px); } to { diff --git a/src/components/ai/VoiceInputModal/VoiceInputModal.css b/src/components/ai/VoiceInputModal/VoiceInputModal.css index b7b6919..b617c72 100644 --- a/src/components/ai/VoiceInputModal/VoiceInputModal.css +++ b/src/components/ai/VoiceInputModal/VoiceInputModal.css @@ -1,7 +1,6 @@ /** * Voice Input Modal Styles - * AI 语音输入浮窗样式 - * Feature: ui-visual-redesign + * AI 语音输入浮窗样式 - Ultra Premium Glass */ .voice-input-overlay { @@ -10,46 +9,75 @@ left: 0; right: 0; bottom: 0; - background: var(--overlay-color); + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); display: flex; align-items: center; justify-content: center; z-index: 1000; - animation: fadeIn var(--duration-fast) var(--ease-out); + animation: fadeIn 0.3s ease-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } } .voice-input-modal { - background: var(--bg-elevated); - border-radius: var(--radius-xl); - padding: var(--space-8); + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border-radius: 32px; + padding: 2.5rem 2rem; width: 90%; - max-width: 400px; + max-width: 420px; position: relative; - animation: scaleIn var(--duration-normal) var(--ease-bounce); - border: 1px solid var(--border-color); - box-shadow: 0 16px 48px var(--shadow-color); + animation: scaleIn 0.4s cubic-bezier(0.19, 1, 0.22, 1); + border: 1px solid rgba(255, 255, 255, 0.6); + box-shadow: + 0 20px 60px -10px rgba(0, 0, 0, 0.2), + inset 0 0 0 1px rgba(255, 255, 255, 0.5); +} + +@keyframes scaleIn { + from { + transform: scale(0.9) translateY(20px); + opacity: 0; + } + + to { + transform: scale(1) translateY(0); + opacity: 1; + } } .voice-input-close { position: absolute; - top: var(--space-4); - right: var(--space-4); - width: 32px; - height: 32px; + top: 1.5rem; + right: 1.5rem; + width: 36px; + height: 36px; border: none; - background: transparent; - color: var(--text-muted); + background: rgba(0, 0, 0, 0.05); + color: var(--text-secondary); cursor: pointer; - border-radius: var(--radius-md); + border-radius: 50%; display: flex; align-items: center; justify-content: center; - transition: all var(--duration-fast) var(--ease-in-out); + transition: all 0.2s ease; } .voice-input-close:hover { - background: var(--bg-hover); + background: rgba(0, 0, 0, 0.1); color: var(--text-primary); + transform: rotate(90deg); } .voice-input-close svg { @@ -61,8 +89,8 @@ display: flex; flex-direction: column; align-items: center; - gap: var(--space-6); - padding: var(--space-4) 0; + gap: 1.5rem; + padding: 1rem 0; } .voice-input-content.result { @@ -71,23 +99,26 @@ /* 录音按钮 */ .recording-button { - width: 80px; - height: 80px; + width: 88px; + height: 88px; border-radius: 50%; border: none; - background: linear-gradient(135deg, var(--accent-primary), var(--accent-primary-hover)); + background: linear-gradient(135deg, #f59e0b, #d97706); + /* Amber gradient */ color: white; cursor: pointer; display: flex; align-items: center; justify-content: center; - transition: all var(--duration-fast) var(--ease-in-out); - box-shadow: 0 4px 16px rgba(233, 69, 96, 0.4); + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + box-shadow: 0 10px 30px rgba(245, 158, 11, 0.4); + position: relative; + z-index: 10; } .recording-button:hover { - transform: scale(1.05); - box-shadow: 0 6px 24px rgba(233, 69, 96, 0.5); + transform: scale(1.1); + box-shadow: 0 15px 40px rgba(245, 158, 11, 0.5); } .recording-button:active { @@ -95,17 +126,22 @@ } .recording-button svg { - width: 32px; - height: 32px; + width: 36px; + height: 36px; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); } .recording-button.recording { - animation: recordingPulse 1.5s ease-in-out infinite; + background: linear-gradient(135deg, #ef4444, #dc2626); + /* Red for recording */ + box-shadow: 0 10px 30px rgba(239, 68, 68, 0.4); + animation: recordingPulse 2s ease-in-out infinite; } .recording-hint { color: var(--text-secondary); - font-size: var(--text-sm); + font-size: 0.95rem; + font-weight: 500; } /* 录音指示器 */ @@ -114,21 +150,26 @@ display: flex; align-items: center; justify-content: center; + height: 120px; + width: 120px; } .recording-ring { position: absolute; - width: 80px; - height: 80px; - border: 2px solid var(--accent-primary); + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + border: 2px solid rgba(239, 68, 68, 0.3); border-radius: 50%; - animation: ringExpand 1.5s ease-out infinite; + animation: ringExpand 2s ease-out infinite; + pointer-events: none; } .recording-status { - color: var(--accent-primary); - font-size: var(--text-base); - font-weight: var(--font-medium); + color: #ef4444; + font-size: 1rem; + font-weight: 600; + margin-top: 1rem; } /* 处理中指示器 */ @@ -142,127 +183,267 @@ .processing-status { color: var(--text-secondary); - font-size: var(--text-base); + font-size: 1rem; + font-weight: 500; } /* 转录文本 */ .transcribed-text { display: flex; - gap: var(--space-2); + gap: 0.75rem; width: 100%; } .transcribed-text .input { flex: 1; + background: rgba(255, 255, 255, 0.5); + border: 1px solid rgba(0, 0, 0, 0.1); + padding: 0.75rem 1rem; + border-radius: 12px; + font-family: inherit; + font-size: 0.95rem; + transition: all 0.2s; +} + +.transcribed-text .input:focus { + background: white; + border-color: #f59e0b; + box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.1); + outline: none; } /* AI 响应 */ .ai-response { display: flex; flex-direction: column; - gap: var(--space-4); + gap: 1.25rem; + width: 100%; } .ai-message { color: var(--text-primary); - font-size: var(--text-base); - line-height: var(--leading-relaxed); + font-size: 1rem; + line-height: 1.6; + background: rgba(245, 158, 11, 0.1); + padding: 1rem; + border-radius: 16px; + border-top-left-radius: 4px; } /* 确认卡片 */ .confirmation-card { - background: var(--bg-tertiary); - border-radius: var(--radius-lg); - padding: var(--space-4); - border: 1px solid var(--border-color); + background: white; + border-radius: 20px; + padding: 1.25rem; + border: 1px solid rgba(0, 0, 0, 0.06); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04); } .confirmation-row { display: flex; justify-content: space-between; align-items: center; - padding: var(--space-2) 0; + padding: 0.85rem 0; } .confirmation-row:not(:last-child) { - border-bottom: 1px solid var(--divider-color); + border-bottom: 1px solid rgba(0, 0, 0, 0.05); } .confirmation-label { color: var(--text-secondary); - font-size: var(--text-sm); + font-size: 0.9rem; + font-weight: 500; } .confirmation-value { color: var(--text-primary); - font-weight: var(--font-medium); + font-weight: 600; } .confirmation-value.expense { - color: var(--color-expense); + color: #ef4444; } .confirmation-value.income { - color: var(--color-income); + color: #10b981; } -.confirmation-value.amount { - font-size: var(--text-lg); - font-weight: var(--font-bold); +.confirmation-input-wrapper { + display: flex; + align-items: center; + gap: 4px; +} + +.currency-prefix { + font-weight: 700; + font-size: 1.2rem; + color: var(--text-primary); +} + +.confirmation-input { + background: rgba(0, 0, 0, 0.03); + border: 1px solid transparent; + border-radius: 8px; + padding: 6px 10px; + color: var(--text-primary); + font-size: 1rem; + transition: all 0.2s; +} + +.confirmation-input:focus { + background: white; + border-color: #f59e0b; + box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.1); + outline: none; +} + +.confirmation-input.amount-input { + width: 110px; + font-weight: 800; + font-size: 1.4rem; + text-align: right; + font-family: 'Outfit', sans-serif; + padding: 4px 8px; + background: transparent; +} + +.confirmation-input.note-input { + width: 100%; +} + +.note-input-wrapper { + display: flex; + gap: 8px; + width: 100%; + align-items: center; +} + +.note-voice-btn { + width: 36px; + height: 36px; + border-radius: 10px; + background: rgba(0, 0, 0, 0.05); + border: none; + color: var(--text-secondary); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s; + flex-shrink: 0; +} + +.note-voice-btn:hover { + background: rgba(245, 158, 11, 0.1); + color: #f59e0b; +} + +.note-voice-btn.recording { + background: #ef4444; + color: white; + animation: recordingPulse 1.5s infinite; } /* 追问 */ .follow-up-question { - color: var(--accent-info); - font-size: var(--text-sm); - padding: var(--space-3); - background: rgba(93, 173, 226, 0.1); - border-radius: var(--radius-md); + color: #3b82f6; + font-size: 0.9rem; + padding: 1rem; + background: rgba(59, 130, 246, 0.08); + border-radius: 16px; + border: 1px solid rgba(59, 130, 246, 0.1); } /* 错误信息 */ .error-message { - color: var(--accent-error); - font-size: var(--text-sm); - padding: var(--space-3); - background: rgba(255, 107, 107, 0.1); - border-radius: var(--radius-md); + color: #ef4444; + font-size: 0.9rem; + padding: 0.75rem; + background: rgba(239, 68, 68, 0.08); + border-radius: 12px; text-align: center; + width: 100%; } /* 操作按钮 */ .voice-input-actions { display: flex; - gap: var(--space-3); - margin-top: var(--space-4); + gap: 1rem; + margin-top: 1rem; + width: 100%; } .voice-input-actions .btn { flex: 1; + padding: 0.85rem; + border-radius: 16px; + border: none; + font-weight: 600; + font-size: 1rem; + cursor: pointer; + transition: all 0.2s; +} + +.btn-secondary { + background: rgba(0, 0, 0, 0.05); + color: var(--text-primary); +} + +.btn-secondary:hover { + background: rgba(0, 0, 0, 0.08); +} + +.btn-primary { + background: linear-gradient(135deg, #f59e0b, #d97706); + color: white; + box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(245, 158, 11, 0.4); +} + +.btn-ghost { + background: transparent; + color: #f59e0b; + border: none; + font-weight: 600; + cursor: pointer; } /* 动画关键帧 */ @keyframes recordingPulse { - 0%, 100% { + 0% { transform: scale(1); - opacity: 1; + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); } - 50% { - transform: scale(1.1); - opacity: 0.8; + + 70% { + transform: scale(1.05); + box-shadow: 0 0 0 15px rgba(239, 68, 68, 0); + } + + 100% { + transform: scale(1); + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } } @keyframes ringExpand { 0% { - width: 80px; - height: 80px; - opacity: 1; + width: 88px; + height: 88px; + opacity: 0.8; + border-width: 4px; } + 100% { - width: 140px; - height: 140px; + width: 160px; + height: 160px; opacity: 0; + border-width: 0px; } } @@ -270,112 +451,6 @@ @media (max-width: 480px) { .voice-input-modal { width: 95%; - padding: var(--space-6); + padding: 1.5rem; } - - .recording-button { - width: 70px; - height: 70px; - } - - .recording-button svg { - width: 28px; - height: 28px; - } -} - -/* 确认卡片编辑样式 */ -.confirmation-input-wrapper { - display: flex; - align-items: center; - gap: var(--space-1); -} - -.currency-prefix { - color: var(--text-primary); - font-weight: var(--font-bold); - font-size: var(--text-lg); -} - -.confirmation-input { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - padding: var(--space-2) var(--space-3); - color: var(--text-primary); - font-size: var(--text-base); - transition: all var(--duration-fast) var(--ease-in-out); -} - -.confirmation-input:focus { - outline: none; - border-color: var(--accent-primary); - box-shadow: 0 0 0 2px rgba(233, 69, 96, 0.2); -} - -.confirmation-input.amount-input { - width: 120px; - font-weight: var(--font-bold); - font-size: var(--text-lg); - text-align: right; -} - -/* 隐藏数字输入框的上下箭头 */ -.confirmation-input.amount-input::-webkit-outer-spin-button, -.confirmation-input.amount-input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; -} - -.confirmation-input.amount-input[type=number] { - -moz-appearance: textfield; -} - -.note-row { - flex-direction: column; - align-items: flex-start !important; - gap: var(--space-2); -} - -.note-input-wrapper { - display: flex; - align-items: center; - gap: var(--space-2); - width: 100%; -} - -.confirmation-input.note-input { - flex: 1; - width: 100%; -} - -.note-voice-btn { - width: 36px; - height: 36px; - border: none; - background: var(--bg-secondary); - color: var(--text-secondary); - border-radius: var(--radius-md); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all var(--duration-fast) var(--ease-in-out); - flex-shrink: 0; -} - -.note-voice-btn:hover { - background: var(--bg-hover); - color: var(--accent-primary); -} - -.note-voice-btn.recording { - background: var(--accent-primary); - color: white; - animation: recordingPulse 1.5s ease-in-out infinite; -} - -.note-voice-btn svg { - width: 16px; - height: 16px; -} +} \ No newline at end of file diff --git a/src/components/charts/SpendingTrendChart.css b/src/components/charts/SpendingTrendChart.css new file mode 100644 index 0000000..65c6a1d --- /dev/null +++ b/src/components/charts/SpendingTrendChart.css @@ -0,0 +1,31 @@ +/* Spending Trend Chart - Bento Style */ +.spending-trend-container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} + +.chart-header { + margin-bottom: 0.5rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.chart-title { + font-size: 0.95rem; + font-weight: 700; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.chart-subtitle { + font-size: 0.8rem; + font-weight: 500; + color: var(--text-tertiary); + background: rgba(0, 0, 0, 0.04); + padding: 4px 10px; + border-radius: 99px; +} \ No newline at end of file diff --git a/src/components/charts/SpendingTrendChart.tsx b/src/components/charts/SpendingTrendChart.tsx index 54e1db3..159d705 100644 --- a/src/components/charts/SpendingTrendChart.tsx +++ b/src/components/charts/SpendingTrendChart.tsx @@ -3,6 +3,7 @@ import ReactECharts from 'echarts-for-react'; import type { Transaction } from '../../types'; import { formatCurrency } from '../../utils/format'; import { toLocalDateString } from '../../utils/dateUtils'; +import './SpendingTrendChart.css'; interface SpendingTrendChartProps { transactions: Transaction[]; @@ -38,29 +39,36 @@ export const SpendingTrendChart: React.FC = ({ }, [transactions, days]); const option = { + title: { + show: false + }, tooltip: { trigger: 'axis', - backgroundColor: 'rgba(255, 255, 255, 0.9)', - borderColor: '#e2e8f0', + backgroundColor: 'rgba(255, 255, 255, 0.85)', + borderColor: 'rgba(255, 255, 255, 0.5)', + borderWidth: 1, + padding: [12, 16], textStyle: { - color: '#1e293b' + color: '#1e293b', + fontFamily: 'Outfit, sans-serif' }, + extraCssText: 'backdrop-filter: blur(12px); box-shadow: 0 8px 20px -4px rgba(0, 0, 0, 0.1); border-radius: 16px;', formatter: (params: any) => { const item = params[0]; return ` -
${item.name}
+
${item.name}
- - 支出 - ${formatCurrency(item.value, 'CNY')} + + 支出 + ${formatCurrency(item.value, 'CNY')}
`; } }, grid: { - left: '3%', - right: '4%', - bottom: '3%', + left: '2%', + right: '2%', + bottom: '5%', top: '10%', containLabel: true }, @@ -68,12 +76,16 @@ export const SpendingTrendChart: React.FC = ({ type: 'category', data: chartData.map(d => d.date), axisLine: { - lineStyle: { - color: '#94a3b8' - } + show: false }, axisTick: { show: false + }, + axisLabel: { + color: '#94a3b8', + fontFamily: 'Outfit, sans-serif', + fontWeight: 500, + margin: 12 } }, yAxis: { @@ -85,9 +97,10 @@ export const SpendingTrendChart: React.FC = ({ } }, axisLabel: { - color: '#94a3b8', + color: '#cbd5e1', + fontFamily: 'Outfit, sans-serif', formatter: (value: number) => { - if (value >= 1000) return `${(value / 1000).toFixed(1)}k`; + if (value >= 1000) return `${(value / 1000).toFixed(0)}k`; return value; } } @@ -95,77 +108,56 @@ export const SpendingTrendChart: React.FC = ({ series: [ { data: chartData.map(d => d.amount), - type: 'bar', // Changed to bar for cleaner daily comparison, or line for trend - smooth: true, - name: '支出', + type: 'bar', + // Smooth rounded bars itemStyle: { color: { type: 'linear', - x: 0, - y: 0, - x2: 0, - y2: 1, + x: 0, y: 0, x2: 0, y2: 1, colorStops: [ - { offset: 0, color: '#f59e0b' }, // Amber 500 - { offset: 1, color: '#d97706' } // Amber 600 + { offset: 0, color: '#f59e0b' }, + { offset: 1, color: 'rgba(245, 158, 11, 0.3)' } ] }, - borderRadius: [4, 4, 0, 0] + borderRadius: [6, 6, 0, 0] }, + barMaxWidth: 24, + animationDuration: 1500, + animationEasing: 'elasticOut', showBackground: true, backgroundStyle: { - color: 'rgba(241, 245, 249, 0.5)' - }, - animationDuration: 1000, - animationEasing: 'cubicOut', - markPoint: { - data: [ - { type: 'max', name: 'Max' }, - { type: 'min', name: 'Min' } - ], - symbol: 'pin', - symbolSize: 40, - label: { - color: '#fff', - fontSize: 10, - formatter: '{c}' - }, - itemStyle: { - color: '#ef4444' // Red pin for expense - } - }, - markLine: { - data: [{ type: 'average', name: 'Avg' }], - precision: 0, - lineStyle: { - color: '#94a3b8', - type: 'dashed' - }, - label: { - position: 'insideEndTop', - formatter: '日均: {c}' - } + color: 'rgba(0, 0, 0, 0.02)', + borderRadius: [6, 6, 0, 0] } + }, + // Line overlay for aesthetics + { + data: chartData.map(d => d.amount), + type: 'line', + smooth: true, + symbol: 'none', + lineStyle: { + color: '#fbbf24', + width: 3, + shadowColor: 'rgba(251, 191, 36, 0.4)', + shadowBlur: 10 + }, + z: 2 } ] }; return ( -
-
-

- 近7日支出趋势 -

+
+
+ 七日支出趋势 + 近7天
- +
); }; diff --git a/src/components/common/Navigation/Navigation.css b/src/components/common/Navigation/Navigation.css index 212e238..9661d28 100644 --- a/src/components/common/Navigation/Navigation.css +++ b/src/components/common/Navigation/Navigation.css @@ -3,9 +3,11 @@ */ /* CSS Variables for navigation width */ - --nav-width: 220px; - --nav-width-collapsed: 70px; - --nav-transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1); +:root { + --nav-width: 240px; + --nav-width-collapsed: 80px; + --nav-transition: 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); + /* Bouncy spring-like */ } /* Hidden state for mobile scroll */ @@ -14,10 +16,10 @@ } .navigation { - background: var(--glass-panel-bg); + background: rgba(255, 255, 255, 0.7); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); - border-top: 1px solid var(--glass-border); + border-top: 1px solid rgba(255, 255, 255, 0.5); position: fixed; bottom: 0; left: 0; @@ -25,9 +27,9 @@ z-index: 100; box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.05); padding-bottom: env(safe-area-inset-bottom); + transition: transform 0.3s ease; } - .navigation-toggle { display: none; } @@ -38,7 +40,7 @@ align-items: center; list-style: none; margin: 0; - padding: var(--spacing-sm) 0; + padding: 0.75rem 0; max-width: 500px; margin: 0 auto; } @@ -54,13 +56,14 @@ flex-direction: column; align-items: center; justify-content: center; - padding: var(--spacing-xs); + padding: 0.5rem; text-decoration: none; - color: var(--color-text-secondary); - transition: all 0.2s ease; - border-radius: var(--radius-lg); - min-width: 60px; + color: var(--text-tertiary); + transition: all 0.25s cubic-bezier(0.2, 0.8, 0.2, 1); + border-radius: 16px; + min-width: 64px; position: relative; + gap: 4px; } .navigation-link:active { @@ -68,63 +71,59 @@ } .navigation-link:hover { - color: var(--accent-primary); - background-color: var(--bg-hover); + color: var(--text-primary); + background-color: rgba(255, 255, 255, 0.5); transform: translateY(-2px); } - .navigation-link:focus { outline: none; - box-shadow: 0 0 0 2px var(--color-primary-light); } .navigation-link--active { - color: var(--color-primary); - background-color: var(--color-primary-lighter); + color: var(--accent-primary); } -/* Active indicator dot */ -.navigation-link--active::after { +.navigation-link--active::before { content: ''; position: absolute; - top: 4px; - right: 4px; - width: 6px; - height: 6px; - background-color: var(--color-accent); - border-radius: 50%; - box-shadow: 0 0 8px var(--color-accent); + top: 0; + left: 50%; + transform: translateX(-50%); + width: 32px; + height: 4px; + background: var(--accent-primary); + border-radius: 0 0 4px 4px; + box-shadow: 0 4px 12px rgba(var(--accent-rgb), 0.4); + display: none; + /* Only for desktop sidebar usually, but hiding here */ } +/* Icon Styles */ .navigation-icon { display: flex; align-items: center; justify-content: center; - margin-bottom: 2px; + margin-bottom: 0; flex-shrink: 0; - transition: transform 0.2s ease; + transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); color: currentColor; - /* Use currentColor to inherit exact color from parent */ -} - -.nav-icon-svg { - display: block; - /* Remove extra space from inline-block */ - stroke-width: 2px; + width: 24px; + height: 24px; } .navigation-link--active .navigation-icon { - transform: scale(1.1); + transform: translateY(-2px); + filter: drop-shadow(0 4px 8px rgba(var(--accent-rgb), 0.3)); } .navigation-label { - font-size: 0.7rem; + font-size: 0.75rem; font-weight: 600; text-align: center; white-space: nowrap; - overflow: hidden; - transition: opacity var(--nav-transition), width var(--nav-transition); + letter-spacing: 0.02em; + transition: opacity 0.2s; } /* Desktop/Tablet - Side Navigation */ @@ -137,37 +136,35 @@ min-width: var(--nav-width); height: 100%; position: relative; - /* Use relative positioning within flex container */ - border-right: none; - padding: 2rem 1rem; - transition: width var(--nav-transition), min-width var(--nav-transition), transform var(--nav-transition); - background: var(--glass-bg); - /* Glass sidebar */ - backdrop-filter: blur(20px); + border-right: 1px solid rgba(255, 255, 255, 0.4); + padding: 2.5rem 1.25rem; + transition: width var(--nav-transition), min-width var(--nav-transition); + background: rgba(255, 255, 255, 0.6); + /* Translucent sidebar */ + backdrop-filter: blur(24px); z-index: 50; overflow-y: auto; - /* Allow sidebar to scroll if items overflow vertical height */ - box-shadow: 1px 0 0 0 var(--glass-border), 5px 0 25px -5px rgba(0, 0, 0, 0.03); + box-shadow: 5px 0 30px -10px rgba(0, 0, 0, 0.05); + border-top: none; } - /* Reset hidden state for sidebar (never hide) */ + /* Reset hidden state for sidebar */ .navigation--hidden { transform: none; } - /* Toggle button - Desktop only */ + /* Toggle button */ .navigation-toggle { display: flex; align-items: center; justify-content: center; - width: 40px; - height: 40px; - margin: 0 auto 1.5rem auto; - /* Centered margin */ - background: transparent; - border: 1px solid transparent; - border-radius: 50%; - /* Circle toggle */ + width: 36px; + height: 36px; + margin: 0 0 2rem auto; + /* Align right */ + background: rgba(255, 255, 255, 0.5); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 10px; cursor: pointer; transition: all 0.2s ease; flex-shrink: 0; @@ -175,18 +172,17 @@ } .navigation-toggle:hover { - background: var(--bg-hover); - color: var(--color-primary); - border-color: var(--border-color); - box-shadow: var(--shadow-sm); + background: white; + color: var(--text-primary); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + transform: scale(1.05); } .navigation-list { flex-direction: column; justify-content: flex-start; padding: 0; - gap: 0.5rem; - /* Consistent gap */ + gap: 0.75rem; max-width: none; width: 100%; } @@ -200,94 +196,77 @@ flex-direction: row; justify-content: flex-start; width: 100%; - padding: 0.875rem 1rem; + padding: 1rem 1.25rem; min-width: auto; margin: 0; - border-radius: 12px; - /* Smooth corners */ + border-radius: 16px; gap: 1rem; color: var(--text-secondary); - /* Default muted color */ - font-weight: 500; + font-weight: 600; + background: transparent; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } - /* Hover State - Subtle background change */ + /* Hover State */ .navigation-link:hover { - background-color: var(--bg-secondary); + background: rgba(255, 255, 255, 0.6); color: var(--text-primary); - transform: none; - /* Remove bounce */ + transform: translateX(4px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03); } - /* Active State - High contrast & Indicator */ /* Active State - Premium Capsule Style */ .navigation-link--active { - background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.15) 0%, rgba(var(--accent-rgb), 0.05) 100%); - color: var(--accent-primary); - font-weight: 600; - position: relative; - overflow: hidden; - box-shadow: 0 0 15px rgba(var(--accent-rgb), 0.15); - /* Soft Glow */ - border: 1px solid rgba(var(--accent-rgb), 0.1); + background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%); + color: white; + box-shadow: 0 8px 20px -4px rgba(var(--accent-rgb), 0.4); + border: none; + } + + .navigation-link--active:hover { + transform: translateX(4px) scale(1.02); + background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%); + color: white; } - /* Remove Left Bar */ .navigation-link--active::before { display: none; } - /* Remove Dot */ - .navigation-link--active::after { - display: none; - } - /* Icon Styles */ .navigation-icon { - display: flex; - justify-content: center; - align-items: center; - width: 24px; - height: 24px; + width: 22px; + height: 22px; margin: 0; - color: inherit; - /* Inherit color from parent link */ - opacity: 0.7; - /* Slightly dimmed by default */ - transition: opacity 0.2s; + opacity: 0.8; } - .navigation-link:hover .navigation-icon, .navigation-link--active .navigation-icon { opacity: 1; - /* Full opacity on valid states */ transform: none; - /* No scaling */ + filter: none; } .navigation-label { font-size: 0.95rem; - /* Slightly larger text */ text-align: left; white-space: nowrap; opacity: 1; - transition: opacity 0.2s; } - /* Collapsed state - Desktop only */ + /* Collapsed state */ .navigation--collapsed { width: var(--nav-width-collapsed); - padding: 2rem 0.5rem; - /* Adjust padding when collapsed */ + padding: 2.5rem 0.75rem; } .navigation--collapsed .navigation-toggle { - margin-bottom: 2rem; + margin: 0 auto 2rem auto; } .navigation--collapsed .navigation-link { justify-content: center; - padding: 0.875rem 0; + padding: 1rem 0; } .navigation--collapsed .navigation-label { @@ -296,12 +275,6 @@ overflow: hidden; position: absolute; } - - .navigation--collapsed .navigation-link--active::before { - display: none; - /* Hide side bar in collapsed mode or adjust */ - /* Alternatively, keep it but ensure it fits */ - } } /* Mobile Visibility Utilities */ @@ -318,27 +291,35 @@ border: none; cursor: pointer; width: 100%; + padding: 0.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + color: var(--text-tertiary); } /* Mobile Menu Popup */ .mobile-menu-popup { position: fixed; - bottom: 80px; - /* Above nav bar */ - right: var(--spacing-md); - width: 180px; - background: rgba(255, 255, 255, 0.9); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid var(--glass-border); - border-radius: var(--radius-xl); - box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1); + bottom: 90px; + right: 16px; + width: 200px; + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid rgba(255, 255, 255, 0.5); + border-radius: 24px; + box-shadow: + 0 20px 40px -10px rgba(0, 0, 0, 0.15), + inset 0 1px 0 0 rgba(255, 255, 255, 0.6); opacity: 0; - transform: translateY(10px) scale(0.95); + transform: translateY(20px) scale(0.9); pointer-events: none; - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - z-index: 99; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + z-index: 101; transform-origin: bottom right; + padding: 0.5rem; } .mobile-menu-popup--open { @@ -353,14 +334,15 @@ left: 0; right: 0; bottom: 0; - background: rgba(0, 0, 0, 0.2); - z-index: -1; + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(4px); + z-index: 100; display: block; } .mobile-menu-list { list-style: none; - padding: var(--spacing-xs); + padding: 0; margin: 0; } @@ -371,20 +353,20 @@ .mobile-menu-link { display: flex; align-items: center; - padding: 10px 12px; + padding: 12px 16px; text-decoration: none; - color: var(--color-text); - border-radius: var(--radius-lg); + color: var(--text-primary); + border-radius: 16px; transition: all 0.2s; - font-size: 0.9rem; + font-size: 0.95rem; font-weight: 500; - gap: 10px; + gap: 12px; } .mobile-menu-link:hover, .mobile-menu-link--active { - background: var(--color-primary-lighter); - color: var(--color-primary); + background: rgba(var(--accent-rgb), 0.1); + color: var(--accent-primary); } .mobile-menu-icon-wrapper { @@ -407,24 +389,4 @@ .mobile-menu-popup { display: none; } -} - -/* Dark Mode for Mobile Menu */ -@media (prefers-color-scheme: dark) { - .mobile-menu-popup { - background: rgba(30, 41, 59, 0.9); - } -} - -/* Reduced Motion */ -@media (prefers-reduced-motion: reduce) { - - .navigation, - .navigation-link, - .navigation-label, - .navigation-toggle, - .navigation-icon, - .mobile-menu-popup { - transition: none; - } } \ No newline at end of file diff --git a/src/components/home/ContributionHeatmap/ContributionHeatmap.css b/src/components/home/ContributionHeatmap/ContributionHeatmap.css index d83f5d2..333d4bf 100644 --- a/src/components/home/ContributionHeatmap/ContributionHeatmap.css +++ b/src/components/home/ContributionHeatmap/ContributionHeatmap.css @@ -1,15 +1,10 @@ +/* Contribution Heatmap - Bento Style */ + .contribution-heatmap-card { - background: rgba(255, 255, 255, 0.65); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid rgba(255, 255, 255, 0.4); - border-radius: var(--radius-xl); - padding: 1.25rem; - box-shadow: - 0 4px 6px -1px rgba(0, 0, 0, 0.02), - inset 0 0 0 1px rgba(255, 255, 255, 0.5); - margin-bottom: 2rem; - animation: fadeIn 0.8s ease-out; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; } .heatmap-header { @@ -23,13 +18,15 @@ display: flex; align-items: center; gap: 0.5rem; - font-size: 0.95rem; + font-size: 0.9rem; font-weight: 700; - color: var(--text-primary); + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; } .title-icon { - color: var(--accent-primary); + color: #f59e0b; } .heatmap-stats { @@ -39,25 +36,29 @@ .mini-stat { display: flex; - align-items: baseline; - gap: 0.35rem; - font-size: 0.8rem; - color: var(--text-secondary); + align-items: center; + gap: 0.5rem; + font-size: 0.75rem; + color: var(--text-tertiary); } .mini-stat-value { font-family: 'Outfit', sans-serif; font-weight: 700; color: var(--text-primary); - font-size: 0.95rem; + font-size: 0.9rem; } .graph-scroll-container { + flex: 1; + display: flex; + align-items: center; overflow-x: auto; - padding-bottom: 0.5rem; + padding-bottom: 0.25rem; scrollbar-width: none; /* Hide scrollbar for cleaner look */ - -ms-overflow-style: none; + mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent); + -webkit-mask-image: linear-gradient(to right, transparent, black 5%, black 95%, transparent); } .graph-scroll-container::-webkit-scrollbar { @@ -66,102 +67,8 @@ .contribution-graph-compact { display: flex; - gap: 4px; - /* Slightly increased gap */ - min-width: fit-content; - padding: 2px; - /* Prevent hover clipping */ -} - -.week-column-compact { - display: flex; - flex-direction: column; - gap: 4px; -} - -.day-cell-compact { - width: 11px; - /* Slightly larger */ - height: 11px; - border-radius: 3px; - /* More rounded */ - background-color: rgba(0, 0, 0, 0.04); - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); -} - -.day-cell-compact:hover { - transform: scale(1.4); - border: 1px solid rgba(0, 0, 0, 0.1); - z-index: 5; -} - -/* Footer & Legend */ -.heatmap-footer { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 0.75rem; - padding-top: 0.75rem; - border-top: 1px dashed rgba(0, 0, 0, 0.05); -} - -.streak-summary { - font-size: 0.8rem; - color: var(--text-secondary); - display: flex; - align-items: center; - gap: 0.5rem; -} - -.streak-summary strong { - color: var(--accent-primary); - font-family: 'Outfit', sans-serif; -} - -.divider { - color: var(--text-tertiary); - font-size: 0.6rem; -} - -.graph-legend-compact { - display: flex; - align-items: center; gap: 3px; - font-size: 0.7rem; - color: var(--text-tertiary); -} - -/* Levels using existing color variables or direct colors */ -.level-0 { - background-color: rgba(0, 0, 0, 0.05); -} - -.level-1 { - background-color: #fca5a5; -} - -.level-2 { - background-color: #f87171; -} - -.level-3 { - background-color: #ef4444; -} - -.level-4 { - background-color: #dc2626; -} - -/* Dark mode adjustment if needed */ -@media (prefers-color-scheme: dark) { - .level-0 { - background-color: rgba(255, 255, 255, 0.05); - } -} - -.heatmap-loading { - padding: 2rem; - text-align: center; - color: var(--text-tertiary); - font-size: 0.85rem; + min-width: 100%; + justify-content: flex-end; + /* Align to right usually looks better for 'latest' days */ } \ No newline at end of file diff --git a/src/components/home/DailyInsightCard/DailyInsightCard.css b/src/components/home/DailyInsightCard/DailyInsightCard.css index e6d9fb0..ced1a40 100644 --- a/src/components/home/DailyInsightCard/DailyInsightCard.css +++ b/src/components/home/DailyInsightCard/DailyInsightCard.css @@ -1,67 +1,28 @@ +/* Daily Insight Card - Bento Style */ .daily-insight-card { - background: rgba(255, 255, 255, 0.75); - backdrop-filter: blur(24px); - -webkit-backdrop-filter: blur(24px); - border: 1px solid rgba(255, 255, 255, 0.6); - border-radius: var(--radius-xl); - padding: 1.5rem; - /* More padding */ - margin-bottom: 2rem; - box-shadow: - 0 15px 35px -5px rgba(0, 0, 0, 0.05), - 0 5px 15px -5px rgba(0, 0, 0, 0.02), - inset 0 0 0 1px rgba(255, 255, 255, 0.8); - animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1); + /* Inherits basis from .bento-card if applied, but defining specific overrides here */ + height: 100%; display: flex; flex-direction: column; - gap: 0; - /* Let internal content handle gaps */ position: relative; - overflow: hidden; - transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease; + /* Specific styling for the Insight within Bento */ + background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.7)); } -.daily-insight-card:hover { - transform: translateY(-4px); +/* AI Variant Styling */ +.daily-insight-card--ai { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(254, 252, 248, 0.9)); + border-color: rgba(245, 158, 11, 0.3); box-shadow: - 0 25px 45px -5px rgba(0, 0, 0, 0.08), - 0 10px 15px -3px rgba(0, 0, 0, 0.03); + 0 10px 40px -10px rgba(245, 158, 11, 0.1), + inset 0 0 0 1px rgba(255, 255, 255, 0.8); } -/* AI Glowing Border Effect - Subtler */ -.daily-insight-card--ai::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: linear-gradient(90deg, #fbbf24, #f59e0b, #fbbf24); - opacity: 0.9; -} - -.daily-insight-card--ai::after { - /* Kept the scan effect but maybe lighter? */ - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, transparent, rgba(251, 191, 36, 0.6), transparent); - animation: scan 4s infinite ease-in-out; - filter: drop-shadow(0 0 4px rgba(251, 191, 36, 0.4)); -} - -@keyframes scan { - 0% { - transform: translateX(-100%); - } - - 50%, - 100% { - transform: translateX(100%); - } +.daily-insight-card--ai:hover { + border-color: rgba(245, 158, 11, 0.5); + box-shadow: + 0 20px 50px -12px rgba(245, 158, 11, 0.15), + inset 0 0 0 1px rgba(255, 255, 255, 0.9); } .daily-insight__header { @@ -70,204 +31,163 @@ gap: 0.75rem; color: #d97706; font-weight: 700; - /* Reduced from 800 */ - font-size: 0.9rem; - /* Slightly bigger */ - text-transform: none; - /* Remove uppercase */ - letter-spacing: 0.02em; - padding-bottom: 1rem; - border-bottom: 1px solid rgba(245, 158, 11, 0.15); + font-size: 0.95rem; + margin-bottom: 1rem; + padding-bottom: 0.75rem; + border-bottom: 1px dashed rgba(245, 158, 11, 0.2); + width: 100%; } -/* ... */ +.daily-insight__loading-badge { + font-size: 0.75rem; + color: var(--text-tertiary); + margin-left: auto; + animation: pulse 2s infinite; +} .daily-insight__content { - display: grid; - grid-template-columns: 1fr 1px 1fr; + display: flex; + /* Changed from grid to flex for better flow in varying widths */ + flex-direction: row; gap: 2rem; - /* Wider gap */ - align-items: stretch; - padding-top: 1.25rem; + flex: 1; } .daily-insight__divider { width: 1px; - height: 100%; - background: linear-gradient(to bottom, rgba(0, 0, 0, 0.02), rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.02)); + background: rgba(0, 0, 0, 0.06); + margin: 0.5rem 0; } .daily-insight__section { + flex: 1; display: flex; flex-direction: column; - gap: 0.75rem; + gap: 0.5rem; + justify-content: center; +} + +.section-header-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.25rem; } .daily-insight__title { font-size: 0.8rem; - color: var(--color-text-tertiary); - font-weight: 600; - letter-spacing: 0.02em; - display: flex; - align-items: center; - gap: 0.5rem; -} - -.daily-insight__title::before { - content: ''; - display: block; - width: 6px; - height: 6px; - border-radius: 50%; - background: #fcd34d; - /* Lighter amber */ - box-shadow: 0 0 0 2px rgba(251, 191, 36, 0.2); + font-weight: 700; + color: var(--text-tertiary); + text-transform: uppercase; + letter-spacing: 0.05em; } .daily-insight__text { - font-family: 'Outfit', sans-serif; - font-size: 1rem; - color: var(--color-text); - line-height: 1.7; - /* More breathing room */ - letter-spacing: 0.01em; + font-size: 1.05rem; + /* Slightly larger text */ + line-height: 1.6; + color: var(--text-primary); + font-weight: 500; + margin: 0; } -@media (max-width: 768px) { - .daily-insight-card { - padding: 1.25rem; - margin-bottom: 1.5rem; - } - - .daily-insight__content { - grid-template-columns: 1fr; - grid-template-rows: auto auto auto; - gap: 1.25rem; - } - - .daily-insight__divider { - width: 100%; - height: 1px; - min-height: 1px; - background: linear-gradient(to right, transparent, rgba(0, 0, 0, 0.06), transparent); - margin: 0.5rem 0; - } - - .daily-insight__text { - font-size: 0.95rem; - } - - .daily-insight__tip { - padding: 0.875rem 1rem; - font-size: 0.85rem; - } +.daily-insight__highlight { + color: var(--text-primary); + font-weight: 800; + background: rgba(0, 0, 0, 0.04); + padding: 2px 6px; + border-radius: 6px; } -.daily-insight__loading-badge { - font-size: 0.7rem; - color: #d97706; - background: rgba(251, 191, 36, 0.1); - padding: 2px 8px; - border-radius: 12px; - margin-left: auto; +.daily-insight__highlight--success { + color: #10b981; + background: rgba(16, 185, 129, 0.1); +} + +.daily-insight__highlight--warning { + color: #f59e0b; + background: rgba(245, 158, 11, 0.1); +} + +.daily-insight__highlight--danger { + color: #ef4444; + background: rgba(239, 68, 68, 0.1); +} + +.week-diff-badge { + font-size: 0.75rem; font-weight: 600; - animation: pulse 2s infinite ease-in-out; + padding: 2px 8px; + border-radius: 99px; +} + +.week-diff-badge.green { + color: #10b981; + background: rgba(16, 185, 129, 0.1); +} + +.week-diff-badge.red { + color: #ef4444; + background: rgba(239, 68, 68, 0.1); +} + +.daily-insight__footer { + margin-top: auto; + padding-top: 1rem; +} + +.daily-insight__tip { + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 0.85rem; + color: var(--text-secondary); + background: rgba(255, 255, 255, 0.5); + padding: 8px 12px; + border-radius: 12px; + border: 1px solid rgba(0, 0, 0, 0.04); } @keyframes pulse { - - 0%, - 100% { + 0% { opacity: 0.6; } 50% { opacity: 1; } + + 100% { + opacity: 0.6; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(5px); + } + + to { + opacity: 1; + transform: translateY(0); + } } .animate-fade-in { - animation: fadeIn 0.8s cubic-bezier(0.16, 1, 0.3, 1); + animation: fadeIn 0.4s ease-out forwards; } -.section-header-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 4px; -} - -.week-diff-badge { - font-size: 0.7rem; - padding: 2px 8px; - border-radius: 12px; - font-weight: 700; - letter-spacing: -0.01em; - white-space: nowrap; -} - -.week-diff-badge.green { - background: rgba(16, 185, 129, 0.15); - color: #059669; - /* Emerald 600 */ -} - -.week-diff-badge.red { - background: rgba(239, 68, 68, 0.1); - color: #DC2626; - /* Red 600 */ -} - -/* Emoji badge */ -.daily-insight__emoji { - font-size: 1.5rem; - margin-left: auto; - filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.15)); - animation: bounce 1s cubic-bezier(0.34, 1.56, 0.64, 1); -} - -@keyframes bounce { - - 0%, - 100% { - transform: scale(1); +/* Mobile Adjustments */ +@media (max-width: 768px) { + .daily-insight__content { + flex-direction: column; + gap: 1.5rem; } - 50% { - transform: scale(1.2) rotate(10deg); + .daily-insight__divider { + width: 100%; + height: 1px; + margin: 0; } -} - -/* Tip section at bottom */ -.daily-insight__footer { - margin-top: 0; - /* Remove margin relative to content, let padding handle it */ - position: relative; - padding-top: 1.5rem; - border-top: none; - /* Remove dashed line */ -} - -.daily-insight__tip { - display: flex; - align-items: flex-start; - /* Align top for multi-line */ - gap: 0.75rem; - font-size: 0.9rem; - color: #92400e; - font-weight: 500; - padding: 1rem 1.25rem; - background: linear-gradient(135deg, rgba(255, 251, 235, 0.8), rgba(254, 243, 199, 0.4)); - border: 1px solid rgba(251, 191, 36, 0.2); - border-radius: var(--radius-lg); - width: 100%; - /* Full width */ - line-height: 1.6; - box-shadow: 0 2px 6px -2px rgba(245, 158, 11, 0.05); -} - -.daily-insight__tip svg { - color: #d97706; - /* Amber 600 */ - flex-shrink: 0; } \ No newline at end of file diff --git a/src/components/home/HealthScoreCard/HealthScoreCard.css b/src/components/home/HealthScoreCard/HealthScoreCard.css new file mode 100644 index 0000000..18b7c48 --- /dev/null +++ b/src/components/home/HealthScoreCard/HealthScoreCard.css @@ -0,0 +1,86 @@ +.health-score-card { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + padding: 1.5rem !important; + /* Override standard bento padding for tighter fit if needed */ +} + +.health-score-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 0.5rem; +} + +.health-label { + font-size: 0.9rem; + font-weight: 600; + color: var(--text-secondary); +} + +.health-trend { + color: var(--text-tertiary); +} + +.health-score-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + flex: 1; + justify-content: center; +} + +.health-ring-container { + position: relative; + width: 80px; + height: 80px; +} + +.health-ring-svg { + transform: rotate(-90deg); +} + +.health-ring-progress { + transition: stroke-dashoffset 1.5s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.health-score-value { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.score-number { + font-size: 1.75rem; + font-weight: 800; + color: var(--text-primary); + font-family: 'Outfit', sans-serif; + letter-spacing: -0.05em; +} + +.health-score-status { + text-align: center; + display: flex; + flex-direction: column; + gap: 0; +} + +.status-text { + font-size: 1.1rem; + font-weight: 700; + line-height: 1.2; +} + +.status-sub { + font-size: 0.75rem; + color: var(--text-tertiary); + font-weight: 500; +} \ No newline at end of file diff --git a/src/components/home/HealthScoreCard/HealthScoreCard.tsx b/src/components/home/HealthScoreCard/HealthScoreCard.tsx new file mode 100644 index 0000000..94a1480 --- /dev/null +++ b/src/components/home/HealthScoreCard/HealthScoreCard.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { Icon } from '@iconify/react'; +import './HealthScoreCard.css'; + +interface HealthScoreCardProps { + score: number; + label: string; + trend?: 'up' | 'down' | 'neutral'; +} + +export const HealthScoreCard: React.FC = ({ score, label, trend }) => { + // Calculate ring stroke + const radius = 32; + const circumference = 2 * Math.PI * radius; + const strokeDashoffset = circumference - (score / 100) * circumference; + + let color = '#10b981'; // Green + if (score < 60) color = '#ef4444'; // Red + else if (score < 80) color = '#f59e0b'; // Orange + + return ( +
+
+ 健康分 +
+ +
+
+ +
+
+ + + + +
+ {score} +
+
+ +
+ {label} + 财务状况 +
+
+
+ ); +}; diff --git a/src/components/home/HealthScoreModal/HealthScoreModal.css b/src/components/home/HealthScoreModal/HealthScoreModal.css index 185c26d..225cf66 100644 --- a/src/components/home/HealthScoreModal/HealthScoreModal.css +++ b/src/components/home/HealthScoreModal/HealthScoreModal.css @@ -4,12 +4,13 @@ :root { --health-score-gradient: linear-gradient(135deg, #4ade80 0%, #22c55e 100%); - --health-bg-gradient: linear-gradient(160deg, #1a1a2e 0%, #16213e 100%); + /* More transparent dark background for glass effect */ + --health-bg-glass: rgba(20, 20, 35, 0.85); --health-text-main: #ffffff; --health-text-sub: rgba(255, 255, 255, 0.7); - --health-glass-border: rgba(255, 255, 255, 0.08); - --health-card-bg: rgba(255, 255, 255, 0.03); - --health-card-hover: rgba(255, 255, 255, 0.08); + --health-glass-border: rgba(255, 255, 255, 0.12); + --health-card-bg: rgba(255, 255, 255, 0.05); + --health-card-hover: rgba(255, 255, 255, 0.1); } .health-modal-overlay { @@ -18,9 +19,9 @@ left: 0; right: 0; bottom: 0; - background-color: rgba(0, 0, 0, 0.85); - backdrop-filter: blur(24px); - -webkit-backdrop-filter: blur(24px); + background-color: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); display: flex; align-items: center; justify-content: center; @@ -37,18 +38,21 @@ } .health-modal-content { - background: var(--health-bg-gradient); + background: var(--health-bg-glass); + backdrop-filter: blur(30px); + -webkit-backdrop-filter: blur(30px); width: 100%; max-width: 420px; border-radius: 36px; padding: 2.5rem 1.75rem 1.75rem; position: relative; box-shadow: - 0 40px 100px -20px rgba(0, 0, 0, 0.7), - 0 0 0 1px var(--health-glass-border) inset; + 0 40px 100px -20px rgba(0, 0, 0, 0.8), + inset 0 0 0 1px var(--health-glass-border), + inset 0 1px 0 0 rgba(255, 255, 255, 0.2); transform: scale(0.92) translateY(30px); opacity: 0; - transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1); + transition: all 0.6s cubic-bezier(0.19, 1, 0.22, 1); display: flex; flex-direction: column; max-height: 85vh; @@ -102,9 +106,9 @@ color: white; transform: scale(1.1) rotate(5deg); border-color: rgba(255, 255, 255, 0.2); + box-shadow: 0 0 15px rgba(255, 255, 255, 0.1); } -/* Score Section */ /* Score Section */ .health-score-section { display: flex; @@ -120,7 +124,7 @@ width: 180px; height: 180px; margin-bottom: 1.25rem; - filter: drop-shadow(0 0 30px rgba(74, 222, 128, 0.15)); + filter: drop-shadow(0 0 40px rgba(74, 222, 128, 0.2)); } .health-score-ring-svg { @@ -128,13 +132,13 @@ } .health-ring-bg { - stroke: rgba(255, 255, 255, 0.03); + stroke: rgba(255, 255, 255, 0.05); } .health-ring-progress { transition: stroke-dasharray 2s cubic-bezier(0.22, 1, 0.36, 1); stroke-linecap: round; - filter: drop-shadow(0 0 10px rgba(74, 222, 128, 0.5)); + filter: drop-shadow(0 0 10px rgba(74, 222, 128, 0.6)); } .health-score-content { @@ -153,7 +157,7 @@ font-weight: 800; font-family: 'Outfit', sans-serif; line-height: 1; - background: linear-gradient(180deg, #ffffff 0%, #c0c0c0 100%); + background: linear-gradient(180deg, #ffffff 0%, #e2e8f0 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; @@ -162,13 +166,13 @@ } .health-score-label { - font-size: 0.9rem; + font-size: 0.85rem; color: var(--health-text-sub); font-weight: 600; margin-top: 0.5rem; - letter-spacing: 0.1em; + letter-spacing: 0.15em; text-transform: uppercase; - opacity: 0.8; + opacity: 0.9; } /* Level Badge */ @@ -183,13 +187,14 @@ font-size: 0.95rem; font-weight: 600; backdrop-filter: blur(12px); - box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25); transition: transform 0.3s; } .health-level-badge:hover { transform: translateY(-2px); - background: rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.2); } /* Metrics Grid */ @@ -202,7 +207,7 @@ .health-metric-card { background: var(--health-card-bg); - border-radius: 20px; + border-radius: 24px; padding: 1.25rem; display: flex; flex-direction: column; @@ -222,15 +227,15 @@ left: 0; right: 0; height: 100%; - background: linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, transparent 100%); + background: linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, transparent 100%); pointer-events: none; } .health-metric-card:hover { transform: translateY(-5px); background: var(--health-card-hover); - border-color: rgba(255, 255, 255, 0.15); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + border-color: rgba(255, 255, 255, 0.2); + box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.5); } .metric-icon-wrapper { @@ -242,10 +247,11 @@ justify-content: center; margin-bottom: 0.75rem; backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.05); } .metric-title { - font-size: 0.875rem; + font-size: 0.85rem; color: var(--health-text-sub); font-weight: 500; } @@ -282,13 +288,14 @@ /* Suggestion Box */ .health-suggestion-box { - background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(15, 23, 42, 0.3)); - border: 1px solid rgba(59, 130, 246, 0.15); - border-radius: 20px; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(15, 23, 42, 0.4)); + border: 1px solid rgba(59, 130, 246, 0.2); + border-radius: 24px; padding: 1.5rem; margin-bottom: 1.25rem; position: relative; overflow: hidden; + backdrop-filter: blur(10px); } .health-suggestion-box::after { @@ -296,11 +303,11 @@ position: absolute; top: -50px; right: -50px; - width: 100px; - height: 100px; - background: radial-gradient(circle, rgba(59, 130, 246, 0.2) 0%, transparent 70%); + width: 120px; + height: 120px; + background: radial-gradient(circle, rgba(59, 130, 246, 0.25) 0%, transparent 70%); border-radius: 50%; - filter: blur(20px); + filter: blur(30px); } .suggestion-header { @@ -312,12 +319,13 @@ font-weight: 700; font-size: 1rem; letter-spacing: 0.02em; + text-transform: uppercase; } .suggestion-content { font-size: 0.95rem; line-height: 1.8; - color: rgba(255, 255, 255, 0.9); + color: rgba(255, 255, 255, 0.95); font-weight: 400; text-align: justify; } @@ -329,19 +337,21 @@ border-radius: 20px; border: none; font-size: 1.05rem; - font-weight: 600; + font-weight: 700; cursor: pointer; background: white; color: #0f172a; transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.5); position: relative; overflow: hidden; + letter-spacing: 0.02em; } .health-confirm-btn:hover { transform: translateY(-2px) scale(1.02); - box-shadow: 0 20px 40px rgba(255, 255, 255, 0.15); + box-shadow: 0 20px 50px -10px rgba(255, 255, 255, 0.2); + background: #f8fafc; } .health-confirm-btn:active { @@ -371,7 +381,7 @@ /* Rules Panel */ .health-rules-panel { - background: rgba(255, 255, 255, 0.02); + background: rgba(255, 255, 255, 0.03); border-radius: 24px; padding: 1.5rem; margin-bottom: 2rem; diff --git a/src/components/home/QuickActionsBox/QuickActionsBox.css b/src/components/home/QuickActionsBox/QuickActionsBox.css new file mode 100644 index 0000000..097c9f9 --- /dev/null +++ b/src/components/home/QuickActionsBox/QuickActionsBox.css @@ -0,0 +1,73 @@ +.quick-actions-box { + display: flex; + flex-direction: column; + height: 100%; +} + +.qa-header { + margin-bottom: 1rem; +} + +.qa-title { + font-size: 0.9rem; + font-weight: 700; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.qa-grid { + display: grid; + grid-template-columns: 1fr; + gap: 0.75rem; + flex: 1; +} + +.qa-btn { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem; + border: 1px solid rgba(0, 0, 0, 0.05); + /* Very subtle border */ + border-radius: 16px; + background: rgba(255, 255, 255, 0.5); + cursor: pointer; + transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); + text-align: left; +} + +.qa-btn:hover { + background: white; + transform: translateX(4px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + border-color: rgba(0, 0, 0, 0.08); +} + +.qa-icon-wrapper { + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + background: var(--qa-color); + /* Fallback or specific color usage */ + background: rgba(255, 255, 255, 0.8); + /* Or white background with colored icon */ + color: var(--qa-color); + font-size: 1.25rem; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.03); +} + +.qa-btn:hover .qa-icon-wrapper { + background: var(--qa-color); + color: white; + transform: scale(1.1) rotate(-5deg); +} + +.qa-label { + font-size: 0.95rem; + font-weight: 600; + color: var(--text-primary); +} \ No newline at end of file diff --git a/src/components/home/QuickActionsBox/QuickActionsBox.tsx b/src/components/home/QuickActionsBox/QuickActionsBox.tsx new file mode 100644 index 0000000..ba0e0cc --- /dev/null +++ b/src/components/home/QuickActionsBox/QuickActionsBox.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Icon } from '@iconify/react'; +import './QuickActionsBox.css'; + +interface QuickAction { + id: string; + icon: string; + label: string; + color: string; + desc?: string; + onClick: () => void; +} + +interface QuickActionsBoxProps { + actions: QuickAction[]; +} + +export const QuickActionsBox: React.FC = ({ actions }) => { + return ( +
+
+ 快捷操作 +
+
+ {actions.map((action) => ( + + ))} +
+
+ ); +}; diff --git a/src/components/ledger/LedgerSelector/LedgerSelector.css b/src/components/ledger/LedgerSelector/LedgerSelector.css index 8af873c..160c2ee 100644 --- a/src/components/ledger/LedgerSelector/LedgerSelector.css +++ b/src/components/ledger/LedgerSelector/LedgerSelector.css @@ -1,6 +1,5 @@ /** - * LedgerSelector Component Styles - * Bottom sheet modal with ledger cards grid and drag-to-reorder + * LedgerSelector Component Styles - Ultra Premium Glass */ /* Main Container */ @@ -14,13 +13,14 @@ display: flex; align-items: flex-end; justify-content: center; - animation: ledger-selector-fade-in 0.3s ease; + animation: ledger-selector-fade-in 0.4s cubic-bezier(0.16, 1, 0.3, 1); } @keyframes ledger-selector-fade-in { from { opacity: 0; } + to { opacity: 1; } @@ -33,8 +33,9 @@ left: 0; right: 0; bottom: 0; - background: rgba(0, 0, 0, 0.5); - backdrop-filter: blur(4px); + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); } /* Bottom Sheet */ @@ -43,12 +44,18 @@ width: 100%; max-width: 600px; max-height: 80vh; - background: #ffffff; - border-radius: 24px 24px 0 0; - box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15); + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border-radius: 32px 32px 0 0; + box-shadow: + 0 -10px 40px rgba(0, 0, 0, 0.1), + inset 0 1px 0 0 rgba(255, 255, 255, 0.6); + border: 1px solid rgba(255, 255, 255, 0.4); + border-bottom: none; display: flex; flex-direction: column; - animation: ledger-selector-slide-up 0.3s ease; + animation: ledger-selector-slide-up 0.5s cubic-bezier(0.19, 1, 0.22, 1); overflow: hidden; } @@ -56,6 +63,7 @@ from { transform: translateY(100%); } + to { transform: translateY(0); } @@ -66,35 +74,38 @@ display: flex; align-items: center; justify-content: space-between; - padding: 20px 24px; - border-bottom: 1px solid #e5e7eb; + padding: 24px 32px; + border-bottom: 1px solid rgba(0, 0, 0, 0.04); flex-shrink: 0; } .ledger-selector__title { margin: 0; - font-size: 20px; + font-family: 'Outfit', sans-serif; + font-size: 1.25rem; font-weight: 700; - color: #111827; + color: var(--text-primary); + letter-spacing: -0.01em; } .ledger-selector__close-btn { display: flex; align-items: center; justify-content: center; - width: 36px; - height: 36px; - background: transparent; + width: 40px; + height: 40px; + background: rgba(0, 0, 0, 0.04); border: none; - border-radius: 8px; - color: #6b7280; + border-radius: 50%; + color: var(--text-secondary); cursor: pointer; transition: all 0.2s ease; } .ledger-selector__close-btn:hover { - background: #f3f4f6; - color: #111827; + background: rgba(0, 0, 0, 0.08); + color: var(--text-primary); + transform: rotate(90deg); } .ledger-selector__close-btn:active { @@ -105,13 +116,13 @@ .ledger-selector__content { flex: 1; overflow-y: auto; - padding: 24px; + padding: 24px 32px; } /* Ledger Grid */ .ledger-selector__grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 16px; } @@ -120,19 +131,21 @@ position: relative; display: flex; flex-direction: column; - gap: 8px; - background: #ffffff; - border: 2px solid #e5e7eb; - border-radius: 12px; + gap: 10px; + background: rgba(255, 255, 255, 0.5); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 20px; padding: 12px; cursor: pointer; - transition: all 0.2s ease; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + overflow: hidden; } .ledger-card:hover { - border-color: #3b82f6; - box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15); - transform: translateY(-2px); + border-color: rgba(59, 130, 246, 0.3); + background: rgba(255, 255, 255, 0.8); + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1); + transform: translateY(-4px); } .ledger-card:active { @@ -141,14 +154,16 @@ .ledger-card--selected { border-color: #3b82f6; - background: #eff6ff; + background: rgba(59, 130, 246, 0.04); + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1); } .ledger-card--dragging { - opacity: 0.5; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); + opacity: 0.6; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2); transform: scale(1.05); z-index: 1000; + backdrop-filter: blur(10px); } /* Drag Handle */ @@ -159,15 +174,16 @@ display: flex; align-items: center; justify-content: center; - width: 24px; - height: 24px; - background: rgba(255, 255, 255, 0.9); - border-radius: 6px; - color: #9ca3af; + width: 28px; + height: 28px; + background: rgba(255, 255, 255, 0.8); + border-radius: 8px; + color: var(--text-secondary); cursor: grab; opacity: 0; transition: all 0.2s ease; z-index: 10; + backdrop-filter: blur(4px); } .ledger-card:hover .ledger-card__drag-handle { @@ -175,9 +191,10 @@ } .ledger-card__drag-handle:hover { - background: #ffffff; + background: white; color: #3b82f6; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); + transform: scale(1.1); } .ledger-card__drag-handle:active { @@ -189,23 +206,28 @@ position: absolute; top: 8px; left: 8px; + width: 24px; + height: 24px; + background: #3b82f6; + border-radius: 50%; display: flex; align-items: center; justify-content: center; - color: #3b82f6; + color: white; z-index: 10; - animation: ledger-card-checkmark-pop 0.3s ease; + animation: ledger-card-checkmark-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + box-shadow: 0 4px 10px rgba(59, 130, 246, 0.3); } @keyframes ledger-card-checkmark-pop { 0% { transform: scale(0); + opacity: 0; } - 50% { - transform: scale(1.2); - } + 100% { transform: scale(1); + opacity: 1; } } @@ -214,11 +236,17 @@ position: relative; width: 100%; aspect-ratio: 3 / 2; - border-radius: 8px; + border-radius: 14px; overflow: hidden; display: flex; align-items: center; justify-content: center; + background: rgba(0, 0, 0, 0.03); + transition: transform 0.3s ease; +} + +.ledger-card:hover .ledger-card__cover { + transform: scale(1.02); } .ledger-card__cover-image { @@ -231,43 +259,47 @@ display: flex; align-items: center; justify-content: center; - color: rgba(0, 0, 0, 0.3); + color: rgba(0, 0, 0, 0.2); + font-size: 1.5rem; } /* Ledger Name */ .ledger-card__name { display: flex; align-items: center; - gap: 6px; - font-size: 14px; - font-weight: 600; - color: #111827; - text-align: center; justify-content: center; + gap: 6px; + font-size: 0.9rem; + font-weight: 600; + color: var(--text-primary); + text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + padding: 0 4px; } .ledger-card__default-badge { display: inline-flex; align-items: center; padding: 2px 6px; - background: #dbeafe; - color: #1e40af; - font-size: 10px; + background: rgba(59, 130, 246, 0.1); + color: #3b82f6; + font-size: 0.65rem; font-weight: 700; - border-radius: 4px; + border-radius: 6px; flex-shrink: 0; + letter-spacing: 0.02em; } /* Footer */ .ledger-selector__footer { display: flex; - gap: 12px; - padding: 20px 24px; - border-top: 1px solid #e5e7eb; + gap: 16px; + padding: 24px 32px; + border-top: 1px solid rgba(0, 0, 0, 0.04); flex-shrink: 0; + background: rgba(255, 255, 255, 0.3); } .ledger-selector__action-btn { @@ -276,59 +308,61 @@ align-items: center; justify-content: center; gap: 8px; - padding: 12px 20px; - font-size: 15px; + padding: 14px 24px; + font-size: 0.95rem; font-weight: 600; border: none; - border-radius: 10px; + border-radius: 16px; cursor: pointer; - transition: all 0.2s ease; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + letter-spacing: 0.01em; } .ledger-selector__action-btn--primary { - background: #3b82f6; + background: var(--accent-primary); color: #ffffff; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .ledger-selector__action-btn--primary:hover { - background: #2563eb; - box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); + filter: brightness(1.1); + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); } .ledger-selector__action-btn--primary:active { - transform: scale(0.98); + transform: translateY(0); } .ledger-selector__action-btn--secondary { - background: #f3f4f6; - color: #374151; + background: rgba(255, 255, 255, 0.6); + color: var(--text-primary); + border: 1px solid rgba(0, 0, 0, 0.05); } .ledger-selector__action-btn--secondary:hover { - background: #e5e7eb; + background: rgba(255, 255, 255, 0.9); + border-color: rgba(0, 0, 0, 0.1); + transform: translateY(-2px); } .ledger-selector__action-btn--secondary:active { - transform: scale(0.98); + transform: translateY(0); } /* Responsive Design */ @media (max-width: 640px) { .ledger-selector__sheet { max-height: 85vh; - border-radius: 20px 20px 0 0; + border-radius: 28px 28px 0 0; } .ledger-selector__header { - padding: 16px 20px; - } - - .ledger-selector__title { - font-size: 18px; + padding: 20px 24px; } .ledger-selector__content { - padding: 20px; + padding: 20px 24px; } .ledger-selector__grid { @@ -336,111 +370,14 @@ gap: 12px; } - .ledger-card { - padding: 10px; - } - - .ledger-card__name { - font-size: 13px; - } - .ledger-selector__footer { - padding: 16px 20px; - gap: 10px; + padding: 20px 24px; } - - .ledger-selector__action-btn { - padding: 10px 16px; - font-size: 14px; - } -} - -/* Dark Mode Support */ -@media (prefers-color-scheme: dark) { - .ledger-selector__sheet { - background: #1f2937; - box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.5); - } - - .ledger-selector__header { - border-bottom-color: #374151; - } - - .ledger-selector__title { - color: #f9fafb; - } - - .ledger-selector__close-btn { - color: #9ca3af; - } - - .ledger-selector__close-btn:hover { - background: #374151; - color: #f9fafb; - } - - .ledger-card { - background: #111827; - border-color: #374151; - } - - .ledger-card:hover { - border-color: #3b82f6; - box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); - } - - .ledger-card--selected { - background: #1e3a5f; - } - - .ledger-card__drag-handle { - background: rgba(31, 41, 55, 0.9); - color: #9ca3af; - } - - .ledger-card__drag-handle:hover { - background: #1f2937; - color: #3b82f6; - } - - .ledger-card__name { - color: #f9fafb; - } - - .ledger-card__default-badge { - background: #1e3a5f; - color: #60a5fa; - } - - .ledger-selector__footer { - border-top-color: #374151; - } - - .ledger-selector__action-btn--secondary { - background: #374151; - color: #d1d5db; - } - - .ledger-selector__action-btn--secondary:hover { - background: #4b5563; - } -} - -/* Accessibility */ -.ledger-card:focus { - outline: 2px solid #3b82f6; - outline-offset: 2px; -} - -.ledger-selector__close-btn:focus, -.ledger-selector__action-btn:focus { - outline: 2px solid #3b82f6; - outline-offset: 2px; } /* Scrollbar Styling */ .ledger-selector__content::-webkit-scrollbar { - width: 8px; + width: 6px; } .ledger-selector__content::-webkit-scrollbar-track { @@ -448,20 +385,10 @@ } .ledger-selector__content::-webkit-scrollbar-thumb { - background: #d1d5db; - border-radius: 4px; + background: rgba(0, 0, 0, 0.1); + border-radius: 3px; } .ledger-selector__content::-webkit-scrollbar-thumb:hover { - background: #9ca3af; -} - -@media (prefers-color-scheme: dark) { - .ledger-selector__content::-webkit-scrollbar-thumb { - background: #4b5563; - } - - .ledger-selector__content::-webkit-scrollbar-thumb:hover { - background: #6b7280; - } -} + background: rgba(0, 0, 0, 0.2); +} \ No newline at end of file diff --git a/src/components/transaction/TransactionForm/TransactionForm.css b/src/components/transaction/TransactionForm/TransactionForm.css index 89c1258..9d20e68 100644 --- a/src/components/transaction/TransactionForm/TransactionForm.css +++ b/src/components/transaction/TransactionForm/TransactionForm.css @@ -1,27 +1,29 @@ /** - * TransactionForm Component - Premium Design (Refined) + * TransactionForm Component - Ultra Premium Glass */ .transaction-form { display: flex; flex-direction: column; - background: var(--bg-elevated); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border: 1px solid var(--border-color); - border-radius: 24px; + background: rgba(255, 255, 255, 0.75); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 28px; width: 100%; - max-width: 440px; + max-width: 460px; margin: 0 auto; overflow: hidden; - box-shadow: 0 20px 60px -10px rgba(0, 0, 0, 0.15); - animation: modalSlideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1); + box-shadow: + 0 25px 60px -15px rgba(0, 0, 0, 0.15), + inset 0 1px 0 0 rgba(255, 255, 255, 0.5); + animation: modalSlideUp 0.4s cubic-bezier(0.19, 1, 0.22, 1); } @keyframes modalSlideUp { from { opacity: 0; - transform: translateY(20px) scale(0.96); + transform: translateY(40px) scale(0.95); } to { @@ -35,44 +37,46 @@ display: flex; justify-content: space-between; align-items: center; - padding: 1.25rem 1.5rem 0.5rem; + padding: 1.5rem 2rem 0.75rem; } .transaction-form__title { margin: 0; - font-size: 1.25rem; - font-weight: 700; + font-size: 1.4rem; + font-weight: 800; color: var(--text-primary); font-family: 'Outfit', sans-serif; + letter-spacing: -0.02em; } .transaction-form__close-btn { - width: 32px; - height: 32px; + width: 36px; + height: 36px; display: flex; align-items: center; justify-content: center; border: none; - background: var(--bg-hover); + background: rgba(0, 0, 0, 0.04); color: var(--text-secondary); font-size: 1.25rem; cursor: pointer; border-radius: 50%; - transition: all 0.2s ease; + transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); } .transaction-form__close-btn:hover { - background: var(--bg-active); + background: rgba(0, 0, 0, 0.08); color: var(--text-primary); + transform: rotate(90deg); } -/* Step Indicator - Cleaner */ +/* Step Indicator */ .transaction-form__steps { display: flex; justify-content: center; align-items: flex-start; - padding: 0 0 1.5rem; - gap: 2rem; + padding: 0 0 2rem; + gap: 2.5rem; position: relative; } @@ -80,31 +84,31 @@ display: flex; flex-direction: column; align-items: center; - gap: 6px; + gap: 8px; position: relative; z-index: 1; - min-width: 40px; + min-width: 44px; } .transaction-form__step-number { - width: 28px; - height: 28px; + width: 32px; + height: 32px; border-radius: 50%; - background: var(--bg-tertiary); + background: rgba(255, 255, 255, 0.4); color: var(--text-tertiary); - font-size: 0.85rem; + font-size: 0.9rem; font-weight: 600; display: flex; align-items: center; justify-content: center; - border: 1px solid var(--border-color); - transition: all 0.3s ease; + border: 1px solid rgba(0, 0, 0, 0.08); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } .transaction-form__step-label { font-size: 0.75rem; color: var(--text-tertiary); - font-weight: 500; + font-weight: 600; transition: all 0.3s ease; white-space: nowrap; } @@ -114,7 +118,7 @@ background: var(--color-primary); color: white; border-color: var(--color-primary); - box-shadow: 0 4px 10px rgba(var(--primary-rgb), 0.3); + box-shadow: 0 6px 16px rgba(var(--primary-rgb), 0.3); transform: scale(1.1); } @@ -128,6 +132,7 @@ background: var(--color-success); color: white; border-color: var(--color-success); + box-shadow: 0 4px 10px rgba(16, 185, 129, 0.2); } .transaction-form__step--completed .transaction-form__step-label { @@ -136,45 +141,36 @@ /* Body */ .transaction-form__body { - padding: 0 1.5rem 1.5rem; - min-height: 340px; + padding: 0 2rem 2rem; + min-height: 360px; } .transaction-form__step-content { display: flex; flex-direction: column; gap: 1.5rem; - animation: fadeIn 0.3s ease; + animation: fadeIn 0.4s cubic-bezier(0.2, 0.8, 0.2, 1); } @keyframes fadeIn { from { opacity: 0; - transform: translateX(10px); + transform: translateY(10px); } to { opacity: 1; - transform: translateX(0); + transform: translateY(0); } } -.transaction-form__step-title { - margin: 0; - font-size: 1rem; - font-weight: 600; - color: var(--text-secondary); - text-align: center; - opacity: 0.8; -} - -/* Type Toggle - Segmented Control */ +/* Type Toggle */ .transaction-form__type-toggle { display: flex; - background: var(--bg-tertiary); - padding: 4px; - border-radius: 16px; - margin-bottom: 1rem; + background: rgba(0, 0, 0, 0.04); + padding: 6px; + border-radius: 20px; + margin-bottom: 1.5rem; } .transaction-form__type-btn { @@ -183,36 +179,32 @@ align-items: center; justify-content: center; gap: 8px; - padding: 10px; + padding: 12px; border: none; - border-radius: 12px; + border-radius: 16px; background: transparent; cursor: pointer; font-weight: 600; color: var(--text-secondary); - transition: all 0.2s ease; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } .transaction-form__type-btn:hover { color: var(--text-primary); -} - -.transaction-form__type-btn--expense { - background: var(--bg-elevated); - /* But we want specific colors for active state */ + background: rgba(255, 255, 255, 0.4); } /* Active States */ .transaction-form__type-btn.transaction-form__type-btn--expense { - background: var(--bg-elevated); + background: white; color: var(--color-error); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } .transaction-form__type-btn.transaction-form__type-btn--income { - background: var(--bg-elevated); + background: white; color: var(--color-success); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } /* Amount Input - Big & Bold */ @@ -220,8 +212,8 @@ display: flex; align-items: baseline; justify-content: center; - gap: 4px; - margin: 1rem 0 2rem; + gap: 6px; + margin: 1.5rem 0 2.5rem; position: relative; } @@ -229,16 +221,16 @@ appearance: none; border: none; background: transparent; - font-size: 1.5rem; - font-weight: 600; + font-size: 1.75rem; + font-weight: 700; color: var(--text-secondary); cursor: pointer; padding: 0 0.5rem; outline: none; + transition: color 0.2s; } -.transaction-form__currency-select option { - background-color: var(--bg-elevated); +.transaction-form__currency-select:hover { color: var(--text-primary); } @@ -246,70 +238,76 @@ width: 100%; border: none; background: transparent; - font-size: 3.5rem; + font-size: 4rem; font-weight: 800; text-align: center; color: var(--text-primary); font-family: 'Outfit', sans-serif; - /* Monospace numbers */ outline: none; padding: 0; caret-color: var(--color-primary); + text-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); } .transaction-form__amount-input::placeholder { - color: var(--border-color); - opacity: 0.5; + color: rgba(0, 0, 0, 0.1); } .transaction-form__error { text-align: center; color: var(--color-error); - font-size: 0.85rem; - font-weight: 500; - margin-top: -1rem; + font-size: 0.9rem; + font-weight: 600; + margin-top: -1.5rem; + background: rgba(239, 68, 68, 0.1); + display: inline-block; + padding: 4px 12px; + border-radius: 8px; + align-self: center; } /* Account Grid */ .transaction-form__account-grid { display: grid; grid-template-columns: repeat(2, 1fr); - gap: 12px; + gap: 16px; } .transaction-form__account-btn { display: flex; align-items: center; gap: 12px; - padding: 12px; - border: 1px solid var(--border-color); - border-radius: 16px; - background: var(--bg-card); + padding: 14px; + border: 1px solid rgba(0, 0, 0, 0.05); + border-radius: 18px; + background: rgba(255, 255, 255, 0.5); cursor: pointer; - transition: all 0.2s ease; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); text-align: left; } .transaction-form__account-btn:hover { - background: var(--bg-hover); - border-color: var(--border-color-strong); + background: white; + transform: translateY(-2px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.04); } .transaction-form__account-btn--selected { border-color: var(--color-primary); - background: rgba(var(--accent-rgb), 0.05); - box-shadow: 0 0 0 2px var(--color-primary-lighter); + background: rgba(var(--primary-rgb), 0.04); + box-shadow: 0 0 0 2px rgba(var(--primary-rgb), 0.1); } .transaction-form__account-icon { - width: 36px; - height: 36px; + width: 40px; + height: 40px; display: flex; align-items: center; justify-content: center; - background: var(--bg-tertiary); - border-radius: 10px; - font-size: 1.2rem; + background: white; + border-radius: 12px; + font-size: 1.4rem; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.03); } .transaction-form__account-info { @@ -318,29 +316,31 @@ } .transaction-form__account-name { - font-size: 0.9rem; - font-weight: 600; + font-size: 0.95rem; + font-weight: 700; color: var(--text-primary); } .transaction-form__account-balance { - font-size: 0.75rem; + font-size: 0.8rem; color: var(--text-secondary); } -/* Summary Card (Step 3) */ +/* Summary Card */ .transaction-form__summary { - background: var(--bg-tertiary); - border-radius: 16px; - padding: 1.25rem; + background: rgba(255, 255, 255, 0.6); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 20px; + padding: 1.5rem; display: flex; justify-content: space-between; align-items: center; + backdrop-filter: blur(10px); } .transaction-form__summary-amount { - font-size: 1.5rem; - font-weight: 700; + font-size: 1.75rem; + font-weight: 800; font-family: 'Outfit', sans-serif; } @@ -354,24 +354,25 @@ /* Inputs in Step 3 */ .transaction-form__field { - margin-bottom: 1rem; + margin-bottom: 1.25rem; } .transaction-form__label { display: block; - font-size: 0.85rem; + font-size: 0.9rem; font-weight: 600; color: var(--text-secondary); - margin-bottom: 0.5rem; + margin-bottom: 0.6rem; + margin-left: 4px; } .transaction-form__input, .transaction-form__textarea { width: 100%; - padding: 12px; - border: 1px solid var(--border-color); - border-radius: 12px; - background: var(--bg-card); + padding: 14px; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 16px; + background: rgba(255, 255, 255, 0.6); color: var(--text-primary); font-size: 1rem; transition: all 0.2s; @@ -380,57 +381,63 @@ .transaction-form__input:focus, .transaction-form__textarea:focus { + background: white; border-color: var(--color-primary); - box-shadow: 0 0 0 3px var(--color-primary-lighter); + box-shadow: 0 0 0 4px rgba(var(--primary-rgb), 0.1); outline: none; } /* Actions Footer */ .transaction-form__actions { - padding: 1rem 1.5rem 1.5rem; + padding: 1.5rem 2rem; display: flex; gap: 1rem; - background: var(--bg-elevated); - /* Match body */ - border-top: 1px solid transparent; + background: rgba(255, 255, 255, 0.4); + border-top: 1px solid rgba(0, 0, 0, 0.03); + backdrop-filter: blur(10px); } .transaction-form__btn { flex: 1; - padding: 14px; - border-radius: 14px; - font-size: 1rem; - font-weight: 600; + padding: 16px; + border-radius: 18px; + font-size: 1.05rem; + font-weight: 700; cursor: pointer; border: none; - transition: all 0.2s; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } .transaction-form__btn--secondary { - background: var(--bg-hover); + background: rgba(255, 255, 255, 0.6); color: var(--text-secondary); + border: 1px solid rgba(0, 0, 0, 0.03); } .transaction-form__btn--secondary:hover { - background: var(--bg-active); + background: white; color: var(--text-primary); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } .transaction-form__btn--primary { - background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark)); + background: var(--color-primary); color: white; - box-shadow: 0 4px 12px var(--color-primary-lighter); + box-shadow: 0 8px 20px -4px rgba(var(--primary-rgb), 0.4); } .transaction-form__btn--primary:hover { - transform: translateY(-1px); - box-shadow: 0 6px 16px var(--color-primary-lighter); + transform: translateY(-2px) scale(1.02); + box-shadow: 0 12px 24px -6px rgba(var(--primary-rgb), 0.5); + filter: brightness(1.1); } /* Loading/Empty States */ .transaction-form__loading, .transaction-form__empty { text-align: center; - padding: 2rem; - color: var(--text-muted); + padding: 3rem; + color: var(--text-tertiary); + font-weight: 500; } \ No newline at end of file diff --git a/src/components/transaction/TransactionList/TransactionList.css b/src/components/transaction/TransactionList/TransactionList.css index 22124f0..ec864bb 100644 --- a/src/components/transaction/TransactionList/TransactionList.css +++ b/src/components/transaction/TransactionList/TransactionList.css @@ -1,11 +1,11 @@ /** - * TransactionList Component - Clean Modern Style + * TransactionList Component - Ultra Premium Glass */ .transaction-list { display: flex; flex-direction: column; - gap: var(--spacing-lg); + gap: 1.5rem; } /* Loading State */ @@ -14,20 +14,20 @@ flex-direction: column; align-items: center; justify-content: center; - gap: var(--spacing-md); - padding: calc(var(--spacing-xl) * 2) var(--spacing-md); - color: var(--color-text-secondary); - background: var(--glass-panel-bg); + gap: 1.5rem; + padding: 4rem 1.5rem; + color: var(--text-tertiary); + background: rgba(255, 255, 255, 0.4); backdrop-filter: blur(12px); - border: 1px solid var(--glass-border); - border-radius: var(--radius-lg); + border: 1px solid rgba(255, 255, 255, 0.5); + border-radius: 24px; } .transaction-list__spinner { - width: 40px; - height: 40px; - border: 3px solid var(--glass-border); - border-top-color: var(--color-primary); + width: 48px; + height: 48px; + border: 4px solid rgba(0, 0, 0, 0.05); + border-top-color: var(--accent-primary); border-radius: 50%; animation: spin 0.8s linear infinite; } @@ -44,128 +44,135 @@ flex-direction: column; align-items: center; justify-content: center; - gap: var(--spacing-md); - padding: calc(var(--spacing-xl) * 2) var(--spacing-md); + gap: 1.5rem; + padding: 4rem 2rem; text-align: center; - background: var(--glass-panel-bg); + background: rgba(255, 255, 255, 0.4); backdrop-filter: blur(12px); - border: 1px dashed var(--glass-border); - border-radius: var(--radius-lg); + border: 1px dashed rgba(0, 0, 0, 0.1); + border-radius: 24px; + transition: all 0.3s ease; +} + +.transaction-list--empty:hover { + background: rgba(255, 255, 255, 0.6); + border-color: rgba(0, 0, 0, 0.2); + transform: translateY(-2px); } .transaction-list__empty-icon { - color: var(--color-text-muted); - opacity: 0.5; + color: var(--text-tertiary); + opacity: 0.6; + font-size: 3rem; + margin-bottom: 0.5rem; } .transaction-list__empty-message { margin: 0; - font-size: 1rem; - color: var(--color-text-secondary); + font-size: 1.1rem; + color: var(--text-secondary); + font-weight: 500; } - /* Date Group */ .transaction-list__group { display: flex; flex-direction: column; - gap: var(--spacing-xs); + gap: 0.75rem; } .transaction-list__group-header { display: flex; justify-content: space-between; align-items: center; - padding: var(--spacing-sm) var(--spacing-md); - background: rgba(var(--color-bg-rgb), 0.5); - /* Semi-transparent header */ - backdrop-filter: blur(8px); - border-bottom: 1px solid var(--glass-border); - border-radius: var(--radius-md); - margin-bottom: 2px; + padding: 0.75rem 1.25rem; + margin-bottom: 0.25rem; } .transaction-list__group-date { - font-size: 0.875rem; + font-size: 0.95rem; font-weight: 700; - color: var(--color-text-secondary); + color: var(--text-secondary); + letter-spacing: 0.02em; } .transaction-list__group-summary { display: flex; - gap: var(--spacing-md); - font-size: 0.8125rem; + gap: 1.25rem; + font-size: 0.85rem; font-weight: 600; } .transaction-list__group-income { - color: var(--color-success); + color: #10b981; display: flex; align-items: center; - gap: var(--spacing-xs); -} - -.transaction-list__group-income::before { - content: none; + gap: 4px; } .transaction-list__group-expense { - color: var(--color-error); + color: var(--text-primary); + /* Keeping distinct but clean */ display: flex; align-items: center; - gap: var(--spacing-xs); -} - -.transaction-list__group-expense::before { - content: none; + gap: 4px; } /* Group Items */ .transaction-list__group-items { display: flex; flex-direction: column; - gap: 0; - /* Remove gap for connected list feel */ - background: var(--glass-panel-bg); - border: 1px solid var(--glass-border); - border-radius: var(--radius-lg); + gap: 4px; + /* Slight gap for separation */ + background: rgba(255, 255, 255, 0.5); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 24px; overflow: hidden; - /* For rounded corners on items */ + padding: 4px; + /* Inner padding for floating items */ + box-shadow: 0 4px 20px -5px rgba(0, 0, 0, 0.03); +} + +/* Ensure individual items have rounded corners when inside the group */ +.transaction-list__group-items>* { + border-radius: 16px; + transition: all 0.2s cubic-bezier(0.2, 0.8, 0.2, 1); +} + +.transaction-list__group-items>*:hover { + background: white; + transform: scale(1.01); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + z-index: 1; } /* Flat List Items */ .transaction-list__items { display: flex; flex-direction: column; - gap: 0; - background: var(--glass-panel-bg); - border: 1px solid var(--glass-border); - border-radius: var(--radius-lg); + gap: 4px; + background: rgba(255, 255, 255, 0.5); + border: 1px solid rgba(255, 255, 255, 0.6); + border-radius: 24px; overflow: hidden; + padding: 4px; + backdrop-filter: blur(20px); } /* Mobile */ @media (max-width: 480px) { .transaction-list { - gap: var(--spacing-md); + gap: 1.25rem; } .transaction-list__group-header { - flex-direction: column; - align-items: flex-start; - gap: var(--spacing-xs); - padding: var(--spacing-sm); + padding: 0.5rem 1rem; } .transaction-list__group-summary { - font-size: 0.75rem; - } -} - -/* Reduced Motion */ -@media (prefers-reduced-motion: reduce) { - .transaction-list__spinner { - animation: none; + font-size: 0.8rem; } } @@ -173,7 +180,7 @@ @keyframes slideUpFade { from { opacity: 0; - transform: translateY(10px); + transform: translateY(15px); } to { @@ -183,13 +190,12 @@ } .transaction-list__group { - animation: slideUpFade 0.4s ease-out forwards; + animation: slideUpFade 0.5s ease-out forwards; } -/* Staggered animation for list items */ +/* Staggered animation */ .transaction-list-item { - animation: slideUpFade 0.3s ease-out forwards; + animation: slideUpFade 0.4s ease-out forwards; opacity: 0; - /* Init hidden */ - animation-delay: calc(var(--item-index, 0) * 0.05s); + animation-delay: calc(var(--item-index, 0) * 0.04s); } \ No newline at end of file diff --git a/src/pages/Home/Home.css b/src/pages/Home/Home.css index a3a6873..49f8dc9 100644 --- a/src/pages/Home/Home.css +++ b/src/pages/Home/Home.css @@ -1,69 +1,76 @@ /** - * Home Page - Premium Glassmorphism Style + * Home Page - Ultra Premium Glassmorphism Style */ .home-page { width: 100%; - max-width: 1200px; + max-width: 1400px; + /* Increased for Bento Grid */ margin: 0 auto; - animation: fadeIn 0.5s ease-out; - padding: 0 1.5rem 2rem 1.5rem; + padding: 0 2rem 3rem 2rem; + /* Staggered animation container */ + --stagger-delay: 100ms; } /* Header New Structure */ .home-header { display: flex; justify-content: space-between; - align-items: center; - /* Center alignment */ - margin-bottom: var(--spacing-md); - padding: var(--spacing-md) 0; - max-width: 1200px; - /* Limit max width */ - margin-left: auto; - margin-right: auto; + align-items: flex-end; + margin-bottom: 1rem; + padding: 2rem 0; width: 100%; + animation: slideDownFade 0.8s cubic-bezier(0.2, 0.8, 0.2, 1); } -/* ... existing greeting classes ... */ - .home-greeting { display: flex; flex-direction: column; gap: 0.5rem; - /* Reduced gap for tighter grouping */ - margin-bottom: 0.5rem; } .home-date { font-size: 0.85rem; color: var(--text-tertiary); - font-weight: 500; - letter-spacing: 0.02em; + font-weight: 600; + letter-spacing: 0.05em; + text-transform: uppercase; + opacity: 0.8; } .greeting-text { font-family: 'Outfit', sans-serif; - font-size: 1.75rem; + font-size: 2.25rem; font-weight: 800; - /* More bold */ margin: 0; color: var(--text-primary); - line-height: 1.2; - letter-spacing: -0.02em; + line-height: 1.1; + letter-spacing: -0.03em; + background: linear-gradient(135deg, var(--text-primary) 30%, var(--text-secondary) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.greeting-highlight { + color: var(--accent-primary); + -webkit-text-fill-color: var(--accent-primary); } .greeting-insight { display: flex; flex-wrap: wrap; - /* Allow wrap if screen is very small, but prefer single line */ align-items: center; - gap: 1rem; - /* Fixed gap instead of margin-auto */ - font-size: 0.9rem; + gap: 0.75rem; + font-size: 0.95rem; color: var(--text-secondary); font-weight: 500; - margin-top: 0.25rem; + margin-top: 0.5rem; + background: rgba(255, 255, 255, 0.4); + padding: 6px 14px; + border-radius: 99px; + width: fit-content; + border: 1px solid rgba(255, 255, 255, 0.4); + backdrop-filter: blur(8px); } .insight-icon { @@ -71,1095 +78,214 @@ flex-shrink: 0; } -.streak-badge { - display: flex; - align-items: center; - gap: 4px; - padding: 2px 8px; - background: rgba(239, 68, 68, 0.1); - /* Light red background for love heart */ - border-radius: 12px; - cursor: pointer; - transition: transform 0.2s ease; -} - -.streak-badge:hover { - transform: scale(1.05); - background: rgba(239, 68, 68, 0.15); -} - -.streak-icon { - color: #ef4444; - /* Red 500 */ -} - -.streak-count { - font-family: 'Outfit', sans-serif; - font-weight: 700; - color: #ef4444; - font-size: 0.85rem; -} - -/* ... */ - -/* Dashboard Grid */ -.dashboard-grid { +/* Bento Grid Layout System */ +.home-bento-grid { display: grid; - grid-template-columns: 2fr 1fr 1fr; - /* 2:1:1 ratio for better Hero emphasis */ - gap: var(--spacing-md); - margin-bottom: var(--spacing-lg); - /* Reduced margin */ -} - -/* Dashboard Card - Premium Base */ -.dashboard-card { - background: rgba(255, 255, 255, 0.65); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid rgba(255, 255, 255, 0.4); - border-radius: var(--radius-xl); - padding: var(--spacing-lg); - display: flex; - flex-direction: column; - position: relative; - overflow: hidden; - transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease; - box-shadow: - 0 4px 6px -1px rgba(0, 0, 0, 0.02), - 0 2px 4px -1px rgba(0, 0, 0, 0.02), - inset 0 0 0 1px rgba(255, 255, 255, 0.5); -} - -.dashboard-card:hover { - transform: translateY(-4px); - box-shadow: - 0 20px 25px -5px rgba(0, 0, 0, 0.05), - 0 10px 10px -5px rgba(0, 0, 0, 0.01); - background: rgba(255, 255, 255, 0.75); -} - -/* Net Worth Card (Hero) */ -.home-net-worth-card { - grid-column: 1; - background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); - /* Indigo to Violet Premium */ - position: relative; - overflow: hidden; - color: white; - border: none; - box-shadow: - 0 20px 25px -5px rgba(79, 70, 229, 0.4), - 0 10px 10px -5px rgba(79, 70, 229, 0.2); -} - -.home-net-worth-card::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - radial-gradient(circle at top right, rgba(255, 255, 255, 0.2), transparent 60%), - radial-gradient(circle at bottom left, rgba(0, 0, 0, 0.2), transparent 60%); - z-index: 0; -} - -.home-net-worth-card:hover { - transform: translateY(-4px) scale(1.01); - box-shadow: - 0 25px 30px -5px rgba(79, 70, 229, 0.5), - 0 15px 15px -5px rgba(79, 70, 229, 0.3); -} - -/* ... content styles ... */ - - -.home-net-worth-card .card-label { - color: rgba(255, 255, 255, 0.9); - font-size: 0.95rem; - font-weight: 500; - letter-spacing: 0.02em; -} - -.home-net-worth-card .card-value-group { - margin: var(--spacing-sm) 0; - display: flex; - align-items: baseline; -} - -.home-net-worth-card .currency-symbol { - font-size: 2rem; - font-weight: 500; - opacity: 0.95; - margin-right: 6px; -} - -.home-net-worth-card .card-value-main { - font-family: 'Outfit', sans-serif; - font-size: 3.5rem; - font-weight: 800; - line-height: 1; - letter-spacing: -3px; - text-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -.home-net-worth-card .trend-neutral { - font-size: 0.85rem; - opacity: 1; - background: rgba(255, 255, 255, 0.15); - padding: 6px 14px; - border-radius: var(--radius-full); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - font-weight: 600; - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); -} - -/* 金额单位后缀样式 (万/亿) */ -.card-value-suffix { - font-size: 1.5rem; - font-weight: 600; - margin-left: 4px; - opacity: 0.9; - align-self: flex-end; - padding-bottom: 4px; -} - -/* Secondary Cards */ -.assets-card, -.liabilities-card { - justify-content: space-between; - cursor: pointer; -} - -.card-icon-wrapper { - width: 52px; - height: 52px; - border-radius: 16px; - /* Softer square */ - display: flex; - align-items: center; - justify-content: center; - margin-bottom: var(--spacing-md); - transition: transform 0.3s ease; -} - -.dashboard-card:hover .card-icon-wrapper { - transform: scale(1.1); -} - -.card-icon-wrapper.income { - background: rgba(16, 185, 129, 0.1); - color: var(--color-success); -} - -.card-icon-wrapper.expense { - background: rgba(239, 68, 68, 0.1); - color: var(--color-error); -} - -.card-label { - font-size: 0.875rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-text-secondary); - display: block; -} - -.card-value-sub { - font-family: 'Outfit', sans-serif; - font-size: 2rem; - font-weight: 700; - color: var(--color-text); - display: block; - margin-top: 0.25rem; - letter-spacing: -1px; -} - -/* Quick Actions Section - Refined */ -.quick-actions-section { - display: flex; - gap: var(--spacing-md); - margin-bottom: var(--spacing-lg); - flex-wrap: wrap; - /* Allow wrap on smaller desktop */ -} - -.action-card { - flex: 1; - min-width: 200px; - max-width: 300px; - /* Prevent overly stretched cards */ - display: flex; - align-items: center; - gap: var(--spacing-md); - padding: 1.25rem; - border: 1px solid rgba(255, 255, 255, 0.4); - border-radius: var(--radius-xl); - background: rgba(255, 255, 255, 0.6); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - cursor: pointer; - transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); - text-align: left; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02); - position: relative; - overflow: hidden; -} - -.action-card:hover { - transform: translateY(-3px); - background: rgba(255, 255, 255, 0.8); - border-color: var(--accent-primary); - box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.08); -} - -.action-icon { - width: 52px; - height: 52px; - border-radius: 16px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - transition: transform 0.3s ease; -} - -.action-card:hover .action-icon { - transform: scale(1.1) rotate(5deg); -} - -.action-card.primary .action-icon { - background: linear-gradient(135deg, #3b82f6, #2563eb); - color: white; - box-shadow: 0 8px 16px rgba(59, 130, 246, 0.25); -} - -.action-card.ai .action-icon { - background: linear-gradient(135deg, #f59e0b, #d97706); - color: white; - box-shadow: 0 8px 16px rgba(245, 158, 11, 0.25); -} - -.action-card.secondary .action-icon { - background: white; - color: var(--color-text); - border: 1px solid var(--border-color); -} - - - -.action-info { - display: flex; - flex-direction: column; -} - -.action-title { - font-size: 1.125rem; - font-weight: 700; - color: var(--color-text); - margin-bottom: 2px; -} - -.action-desc { - font-size: 0.85rem; - color: var(--color-text-secondary); -} - -/* Header Actions & Health Score */ -.header-actions { - display: flex; - align-items: center; - gap: var(--spacing-lg); -} - -.health-score-btn { - display: flex; - flex-direction: column; - align-items: center; - gap: 2px; - background: transparent; - border: none; - cursor: pointer; - padding: 0; - transition: transform 0.2s ease; -} - -.health-score-btn:hover { - transform: scale(1.05); -} - -.health-ring { - width: 42px; - height: 42px; - position: relative; - display: flex; - align-items: center; - justify-content: center; -} - -.health-ring svg { + grid-template-columns: repeat(4, 1fr); + grid-auto-rows: minmax(180px, auto); + /* Base height for rows */ + gap: 24px; width: 100%; - height: 100%; - transform: rotate(-90deg); } -.ring-bg { - fill: none; - stroke: var(--bg-tertiary); - stroke-width: 3; -} - -.ring-fill { - fill: none; - stroke: var(--color); - stroke-width: 3; - stroke-linecap: round; - transition: stroke-dasharray 1s ease-out; -} - -.health-val { - position: absolute; - font-family: 'Outfit', sans-serif; - font-size: 0.8rem; - font-weight: 700; - color: var(--text-primary); -} - -.health-label { - font-size: 0.7rem; - font-weight: 600; - color: var(--text-secondary); -} - -.quick-action-btn-small { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 1rem; - background: var(--glass-panel-bg); - color: var(--accent-primary); - border: 1px solid var(--accent-primary); - border-radius: var(--radius-full); - font-weight: 600; - font-size: 0.85rem; - cursor: pointer; - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: var(--shadow-sm); - backdrop-filter: blur(4px); -} - -.quick-action-btn-small:hover { - background: var(--accent-primary); - color: white; - transform: translateY(-2px); - box-shadow: var(--shadow-glow-sm); -} - -/* Content Columns */ -.content-columns { - display: grid; - grid-template-columns: 2fr 1fr; - gap: var(--spacing-xl); -} - -/* Chart Container */ -.chart-container { - background: var(--glass-panel-bg); - border: 1px solid var(--glass-border); - border-radius: var(--radius-xl); - padding: var(--spacing-lg); - box-shadow: var(--shadow-sm); -} - -/* Recent Transactions */ -/* Recent Transactions */ -.recent-transactions-section { +/* Base Bento Card Style */ +.bento-card { background: rgba(255, 255, 255, 0.65); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid rgba(255, 255, 255, 0.4); - border-radius: var(--radius-xl); - padding: 1.5rem; + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid rgba(255, 255, 255, 0.5); + border-radius: 32px; + padding: 1.75rem; display: flex; flex-direction: column; - max-height: 540px; + position: relative; + overflow: hidden; + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); box-shadow: - 0 10px 25px -5px rgba(0, 0, 0, 0.05), - 0 4px 6px -2px rgba(0, 0, 0, 0.01); + 0 10px 30px -10px rgba(0, 0, 0, 0.05), + inset 0 0 0 1px rgba(255, 255, 255, 0.4); } -.section-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1.25rem; - padding-bottom: 0.75rem; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); +.bento-card:hover { + transform: translateY(-6px) scale(1.01); + background: rgba(255, 255, 255, 0.85); + box-shadow: + 0 25px 50px -12px rgba(0, 0, 0, 0.1), + inset 0 0 0 1px rgba(255, 255, 255, 0.8); + border-color: rgba(255, 255, 255, 0.8); + z-index: 10; } -.section-header h2 { - font-size: 1.1rem; - font-weight: 700; - color: var(--color-text); - letter-spacing: 0.01em; +/* Specific Card Styles Overrides for Bento */ +.bento-card.dark-glass { + background: rgba(30, 30, 35, 0.85); + /* Dark card variant */ + border: 1px solid rgba(255, 255, 255, 0.1); + color: white; + box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.4); } -.view-all-link { - color: var(--accent-primary); - font-size: 0.85rem; - font-weight: 600; - background: rgba(99, 102, 241, 0.1); +.bento-card.dark-glass:hover { + background: rgba(30, 30, 35, 0.95); + border-color: rgba(255, 255, 255, 0.2); + transform: translateY(-6px) scale(1.01); +} + +.bento-card.primary-gradient { + background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%); + color: white; border: none; - cursor: pointer; - padding: 6px 14px; - border-radius: 20px; - transition: all 0.2s ease; + box-shadow: 0 20px 40px -10px rgba(var(--accent-rgb), 0.4); } -.view-all-link:hover { - background: rgba(99, 102, 241, 0.15); - transform: scale(1.05); +.bento-card.primary-gradient:hover { + filter: brightness(1.1); + box-shadow: 0 30px 60px -15px rgba(var(--accent-rgb), 0.6); } -.transaction-list-compact { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-right: 6px; - padding-bottom: 4px; +/* Grid Spans */ +.bento-span-1 { + grid-column: span 1; } -.transaction-row { - display: flex; - align-items: center; - padding: 0.85rem; - border-radius: 16px; - cursor: pointer; - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - background: rgba(255, 255, 255, 0.4); - border: 1px solid transparent; +.bento-span-2 { + grid-column: span 2; } -.transaction-row:last-child { - border-bottom: 1px solid transparent; +.bento-span-3 { + grid-column: span 3; } -.transaction-row:hover { - background: white; - border-color: rgba(0, 0, 0, 0.05); - transform: translateX(4px) scale(1.01); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03); +.bento-span-4 { + grid-column: span 4; } -.transaction-icon { - width: 40px; - height: 40px; - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - margin-right: 1rem; +.bento-row-span-2 { + grid-row: span 2; } -.transaction-icon.income { - background: rgba(16, 185, 129, 0.1); - color: var(--color-success); +/* Staggered Animations */ +.home-bento-grid>*:nth-child(1) { + animation-delay: 100ms; } -.transaction-icon.expense { - background: rgba(239, 68, 68, 0.1); - color: var(--color-error); +.home-bento-grid>*:nth-child(2) { + animation-delay: 150ms; } -.transaction-details { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; +.home-bento-grid>*:nth-child(3) { + animation-delay: 200ms; } -.transaction-category { - font-weight: 600; - font-size: 0.95rem; - color: var(--color-text); +.home-bento-grid>*:nth-child(4) { + animation-delay: 250ms; } -.transaction-note-compact { - font-size: 0.8rem; - color: var(--color-text-muted); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; +.home-bento-grid>*:nth-child(5) { + animation-delay: 300ms; } -.transaction-meta { - text-align: right; - display: flex; - flex-direction: column; - align-items: flex-end; +.home-bento-grid>*:nth-child(6) { + animation-delay: 350ms; } -.transaction-amount-compact { - font-family: 'Outfit', sans-serif; - font-weight: 700; - font-size: 1rem; +.home-bento-grid>*:nth-child(7) { + animation-delay: 400ms; } -.transaction-amount-compact.income { - color: var(--color-success); +.home-bento-grid>*:nth-child(8) { + animation-delay: 450ms; } -.transaction-amount-compact.expense { - color: var(--color-text); +.home-bento-grid>* { + animation: slideUpFade 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) backwards; } -.transaction-time { - font-size: 0.75rem; - color: var(--color-text-muted); - margin-top: 2px; +/* Responsive Grid */ +@media (max-width: 1200px) { + .home-page { + max-width: 100%; + padding: 0 1.5rem 2rem 1.5rem; + } } -.empty-state-compact { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 3rem 1rem; - color: var(--color-text-muted); - opacity: 0.8; -} - -/* Responsive */ @media (max-width: 1024px) { - .dashboard-grid { - grid-template-columns: 1fr 1fr; + .home-bento-grid { + grid-template-columns: repeat(2, 1fr); } - .home-net-worth-card { + .bento-span-1, + .bento-span-3 { + grid-column: span 1; + } + + .bento-span-2, + .bento-span-4 { grid-column: span 2; } - - .content-columns { - grid-template-columns: 1fr; - } } -@media (max-width: 600px) { +@media (max-width: 640px) { + .home-bento-grid { + grid-template-columns: 1fr; + gap: 16px; + } + + .bento-span-1, + .bento-span-2, + .bento-span-3, + .bento-span-4, + .bento-row-span-2 { + grid-column: span 1; + grid-row: span 1 !important; + /* Reset row spans on mobile */ + } + + .bento-card { + padding: 1.25rem; + border-radius: 24px; + min-height: auto; + } + .home-header { flex-direction: column; align-items: flex-start; gap: 1rem; - } - - .quick-action-btn-small { - width: 100%; - justify-content: center; - } - - .dashboard-grid { - grid-template-columns: 1fr; - } - - .home-net-worth-card { - grid-column: span 1; - } - - .quick-actions-section { - grid-template-columns: 1fr; - gap: var(--spacing-md); + padding: 1rem 0; } } -/* Modal Overlay */ -.modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.7); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; - backdrop-filter: blur(4px); - animation: fadeIn 0.3s ease; -} - -.modal-content { - background: var(--bg-card); - border-radius: var(--radius-xl); - max-width: 500px; - width: 90%; - max-height: 90vh; - display: flex; - flex-direction: column; - overflow: hidden; - box-shadow: var(--shadow-2xl); - animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1); -} - -@keyframes fadeIn { +/* Animations */ +@keyframes slideDownFade { from { opacity: 0; + transform: translateY(-20px); } to { opacity: 1; - } -} - -@keyframes slideUp { - from { - transform: translateY(20px); - opacity: 0; - } - - to { transform: translateY(0); - opacity: 1; } } -.error-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 50vh; - gap: 1rem; - color: var(--text-secondary); -} - -.retry-btn { - padding: 0.5rem 1.5rem; - background-color: var(--accent-primary); - color: white; - border: none; - border-radius: var(--radius-md); - cursor: pointer; - font-weight: 500; - transition: all 0.2s ease; -} - -.retry-btn:hover { - background-color: var(--accent-primary-hover); - transform: translateY(-1px); -} - -/* Content Columns */ -.content-columns { - display: grid; - grid-template-columns: 2fr 1fr; - gap: var(--spacing-lg); - /* Reduced from xl */ -} - -/* Recent Transactions */ -.recent-transactions-section { - background: var(--glass-panel-bg); - border: 1px solid var(--glass-border); - border-radius: var(--radius-xl); - padding: var(--spacing-lg); - display: flex; - flex-direction: column; - max-height: 480px; - box-shadow: var(--shadow-sm); -} - -.section-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-md); - padding-bottom: var(--spacing-sm); - border-bottom: 1px solid var(--divider-color); -} - -.section-header h2 { - font-size: 1.125rem; - font-weight: 700; - color: var(--color-text); - margin: 0; -} - -.view-all-link { - color: var(--color-primary); - font-size: 0.85rem; - font-weight: 600; - background: none; - border: none; - cursor: pointer; - padding: 4px 10px; - border-radius: var(--radius-full); -} - -/* Floating Action Button (FAB) */ -.fab-voice-btn { - position: fixed; - bottom: 2rem; - right: 2rem; - width: 56px; - height: 56px; - background: linear-gradient(135deg, #f59e0b, #d97706); - color: white; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 4px 10px rgba(245, 158, 11, 0.3), 0 2px 4px rgba(0, 0, 0, 0.1); - cursor: pointer; - z-index: 100; - border: none; - transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); - animation: scaleIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.5s backwards; -} - -.fab-voice-btn:hover { - transform: scale(1.1) translateY(-2px); - box-shadow: 0 8px 20px rgba(245, 158, 11, 0.5); -} - -.fab-voice-btn:active { - transform: scale(0.95); -} - -/* Pulse animation for microphone */ -.fab-voice-btn::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 50%; - border: 2px solid rgba(255, 255, 255, 0.5); - opacity: 0; - transform: scale(1); -} - -.fab-voice-btn:hover::after { - animation: pulse-ring 1.5s infinite; -} - -@keyframes pulse-ring { - 0% { - transform: scale(1); - opacity: 0.8; - } - - 100% { - transform: scale(1.5); - opacity: 0; - } -} - -@keyframes scaleIn { - from { - transform: scale(0); - opacity: 0; - } - - to { - transform: scale(1); - opacity: 1; - } -} - -/* 适配移动端 */ -@media (max-width: 600px) { - .fab-voice-btn { - bottom: 5.5rem; - /* Avoid bottom nav if present, or just give space */ - right: 1.5rem; - } -} - -transition: background 0.2s; -background: rgba(var(--color-primary-rgb), 0.1); -} - -.view-all-link:hover { - background: rgba(var(--color-primary-rgb), 0.2); -} - -.transaction-list-compact { - display: flex; - flex-direction: column; - gap: 8px; - /* Added gap */ - overflow-y: auto; - padding-right: 4px; -} - -.transaction-row { - display: flex; - align-items: center; - padding: 0.75rem; - border-radius: var(--radius-lg); - cursor: pointer; - transition: all 0.2s ease; - background: transparent; - border: 1px solid transparent; -} - -.transaction-row:last-child { - border-bottom: none; -} - -.transaction-row:hover { - background: var(--bg-hover); - border-color: var(--glass-border); - transform: translateX(2px); -} - -.transaction-icon { - width: 42px; - height: 42px; - border-radius: 14px; - display: flex; - align-items: center; - justify-content: center; - margin-right: 1rem; - font-size: 1.2rem; - flex-shrink: 0; -} - -.transaction-icon.income { - background: linear-gradient(135deg, rgba(16, 185, 129, 0.2), rgba(16, 185, 129, 0.05)); - color: var(--color-success); -} - -.transaction-icon.expense { - background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(239, 68, 68, 0.05)); - color: var(--color-error); -} - -.transaction-details { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - gap: 2px; -} - -.transaction-category { - font-weight: 600; - font-size: 0.95rem; - color: var(--color-text); -} - -.transaction-note-compact { - font-size: 0.8rem; - color: var(--color-text-muted); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.transaction-meta { - text-align: right; - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 2px; -} - -.transaction-amount-compact { - font-family: 'Outfit', sans-serif; - font-weight: 700; - font-size: 1rem; -} - -.transaction-amount-compact.income { - color: var(--color-success); -} - -.transaction-amount-compact.expense { - color: var(--color-text); -} - -.transaction-time { - font-size: 0.75rem; - color: var(--color-text-muted); -} - -.empty-state-compact { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 3rem 1rem; - color: var(--color-text-muted); - opacity: 0.7; -} - -/* Responsive */ -@media (max-width: 1024px) { - .dashboard-grid { - grid-template-columns: 1fr 1fr; - } - - .home-net-worth-card { - grid-column: span 2; - } - - .content-columns { - grid-template-columns: 1fr; - } -} - -@media (max-width: 600px) { - .home-header { - flex-direction: column; - align-items: stretch; - /* Full width items */ - gap: 0.75rem; - margin-bottom: 1rem; - } - - .header-actions { - justify-content: space-between; - width: 100%; - } - - .quick-action-btn-small { - width: 100%; - justify-content: center; - padding: 0.6rem; - } - - /* Bento grid stacks on mobile */ - .dashboard-grid { - grid-template-columns: 1fr; - gap: 0.75rem; - } - - .home-net-worth-card { - grid-column: span 1; - min-height: 140px; - padding: 1.25rem; - } - - .home-net-worth-card .card-value-main { - font-size: 2.75rem; - } - - .quick-actions-section { - grid-template-columns: repeat(3, 1fr); - /* Keep 3 cols but tighter */ - gap: 0.5rem; - } - - .action-card { - padding: 0.75rem; - flex-direction: column; - gap: 0.5rem; - text-align: center; - } - - .action-icon { - margin: 0; - width: 40px; - height: 40px; - font-size: 1.2rem; - } - - .action-info { - align-items: center; - } - - .action-subtitle { - display: none; - /* Hide subtitle on very small screens to save space */ - } - - .action-title { - font-size: 0.8rem; - } -} - -/* Modal Overlay & Animations */ -.modal-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.4); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; - backdrop-filter: blur(8px); - /* Stronger blur */ - animation: fadeIn 0.3s ease; -} - -.modal-content { - background: var(--bg-card); - border-radius: var(--radius-xl); - max-width: 500px; - width: 90%; - max-height: 90vh; - display: flex; - flex-direction: column; - overflow: hidden; - box-shadow: var(--shadow-2xl); - animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1); - border: 1px solid var(--glass-border); -} - -@keyframes fadeIn { +@keyframes slideUpFade { from { opacity: 0; + transform: translateY(30px); } to { opacity: 1; - } -} - -@keyframes slideUp { - from { - transform: translateY(20px); - opacity: 0; - } - - to { transform: translateY(0); - opacity: 1; } } -.error-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 50vh; - gap: 1rem; - color: var(--text-secondary); +/* Utility to hide scroll in cards */ +.no-scrollbar::-webkit-scrollbar { + display: none; } -.retry-btn { - padding: 0.5rem 1.5rem; - background-color: var(--accent-primary); - color: white; - border: none; - border-radius: var(--radius-md); - cursor: pointer; - font-weight: 500; - transition: all 0.2s ease; -} - -.retry-btn:hover { - background-color: var(--accent-primary-hover); - transform: translateY(-1px); +.no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; } \ No newline at end of file diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index f41e922..d63d3e8 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -19,20 +19,16 @@ import { createAccount } from '../../services/accountService'; import { Confetti } from '../../components/common/Confetti'; import { HealthScoreModal } from '../../components/home/HealthScoreModal/HealthScoreModal'; import { ContributionHeatmap } from '../../components/home/ContributionHeatmap/ContributionHeatmap'; -import { DailyInsightCard } from '../../components/home/DailyInsightCard/DailyInsightCard'; // Import -import { getBudgets } from '../../services/budgetService'; // Import +import { DailyInsightCard } from '../../components/home/DailyInsightCard/DailyInsightCard'; +import { HealthScoreCard } from '../../components/home/HealthScoreCard/HealthScoreCard'; +import { QuickActionsBox } from '../../components/home/QuickActionsBox/QuickActionsBox'; +import { getBudgets } from '../../services/budgetService'; import { toLocalDateString, isSameDay } from '../../utils/dateUtils'; import type { Account, Transaction, Category, Ledger, UserSettings } from '../../types'; /** - * Home Page Component - * Displays account balance overview and recent transactions - * Provides quick access to create new transactions - * - * Requirements: - * - 8.1: Quick transaction entry (3 steps or less) - * - 8.2: Fast loading (< 2 seconds) + * Home Page Component - Modern Bento Grid Layout */ function Home() { const navigate = useNavigate(); @@ -46,7 +42,7 @@ function Home() { const [showAccountForm, setShowAccountForm] = useState(false); const [showConfetti, setShowConfetti] = useState(false); const [showHealthModal, setShowHealthModal] = useState(false); - // Removed showContributionModal state + const [todaySpend, setTodaySpend] = useState(0); const [yesterdaySpend, setYesterdaySpend] = useState(0); const [streakInfo, setStreakInfo] = useState(null); @@ -58,8 +54,8 @@ function Home() { const [monthlyBudgetSpentTotal, setMonthlyBudgetSpentTotal] = useState(0); const [todayTransactions, setTodayTransactions] = useState([]); const [lastWeekSpend, setLastWeekSpend] = useState(0); - const [last7DaysSpend, setLast7DaysSpend] = useState([]); // New: 最近7天支出 - const [weekTransactions, setWeekTransactions] = useState([]); // New: 最近7天交易详情 (for AI) + const [last7DaysSpend, setLast7DaysSpend] = useState([]); + const [weekTransactions, setWeekTransactions] = useState([]); useEffect(() => { @@ -71,10 +67,6 @@ function Home() { setLoading(true); setError(null); - - - // Helper for local date string YYYY-MM-DD - REMOVED, using imported utils - const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); @@ -90,7 +82,6 @@ function Home() { const rangeStartStr = toLocalDateString(rangeStart); const rangeEndStr = toLocalDateString(rangeEnd); - // Parallel fetch optimized: Single request for transaction history const [accountsData, recentTxData, categoriesData, ledgersData, settingsData, budgetsData, streakData, rangeTxData] = await Promise.all([ getAccounts(), getTransactions({ page: 1, pageSize: 5 }), // Recent list (server sort) @@ -99,7 +90,7 @@ function Home() { getSettings().catch(() => null), getBudgets().catch(() => []), getStreakInfo().catch(() => null), - getTransactions({ startDate: rangeStartStr, endDate: rangeEndStr, pageSize: 1000 }), // Bulk fetch (All types for AI) + getTransactions({ startDate: rangeStartStr, endDate: rangeEndStr, pageSize: 1000 }), // Bulk fetch ]); setAccounts(accountsData || []); @@ -109,43 +100,34 @@ function Home() { setSettings(settingsData); setStreakInfo(streakData); - // In-Memory Aggregation for Daily Spend (Fixes Timezone Issues) + // In-Memory Aggregation fix const allRangeTx = rangeTxData?.items || []; const expenseRangeTx = allRangeTx.filter(t => t.type === 'expense'); - // Today (Expense Only) const todayTxItems = expenseRangeTx.filter(t => isSameDay(new Date(t.transactionDate), today)); setTodayTransactions(todayTxItems); setTodaySpend(calculateTotalExpense(todayTxItems)); - // Yesterday (Expense Only) const yesterdayTxItems = expenseRangeTx.filter(t => isSameDay(new Date(t.transactionDate), yesterday)); setYesterdaySpend(calculateTotalExpense(yesterdayTxItems)); - // Last Week Same Day (Expense Only) const lastWeekTxItems = expenseRangeTx.filter(t => isSameDay(new Date(t.transactionDate), lastWeekSameDay)); setLastWeekSpend(calculateTotalExpense(lastWeekTxItems)); - // Last 7 Days Array (for chart - Expense Only) const last7DaysSpendArray = []; - const weekTxList: Transaction[] = []; // Filtered by date, contains ALL types + const weekTxList: Transaction[] = []; for (let i = 6; i >= 0; i--) { const d = new Date(today); d.setDate(d.getDate() - i); - - // Expense for chart const dayExpenseTx = expenseRangeTx.filter(t => isSameDay(new Date(t.transactionDate), d)); last7DaysSpendArray.push(calculateTotalExpense(dayExpenseTx)); - - // All types for AI const dayAllTx = allRangeTx.filter(t => isSameDay(new Date(t.transactionDate), d)); weekTxList.push(...dayAllTx); } setLast7DaysSpend(last7DaysSpendArray); setWeekTransactions(weekTxList); - // Monthly Budget Stats let mTotal = 0; let mSpent = 0; (budgetsData || []).forEach((b: any) => { @@ -153,7 +135,6 @@ function Home() { if (b.periodType === 'daily') multiplier = 30; else if (b.periodType === 'weekly') multiplier = 4.3; else if (b.periodType === 'yearly') multiplier = 1 / 12; - mTotal += b.amount * multiplier; mSpent += (b.spent || 0); }); @@ -169,38 +150,23 @@ function Home() { }; const { maxTransaction, topCategory, top3Categories, todayTransactionCount, avgDailySpend } = useMemo(() => { - // Basic stats const todayCount = todayTransactions.length; - - // Max Transaction (Keep as Today's max for immediate context) const maxTx = [...todayTransactions].sort((a, b) => b.amount - a.amount)[0]; - - // Category aggregation - Use Week Transactions for better AI context const sourceTransactions = weekTransactions.length > 0 ? weekTransactions : todayTransactions; - const catMap: Record = {}; sourceTransactions.forEach((t) => { catMap[t.categoryId] = (catMap[t.categoryId] || 0) + t.amount; }); - - // Sort categories by amount const sortedCatIds = Object.keys(catMap).sort((a, b) => catMap[Number(b)] - catMap[Number(a)]); - - // Top category let topCatName = undefined; if (sortedCatIds.length > 0) { - const topCatId = Number(sortedCatIds[0]); - topCatName = categories.find((c) => c.id === topCatId)?.name; + topCatName = categories.find((c) => c.id === Number(sortedCatIds[0]))?.name; } - - // Top 3 categories const top3 = sortedCatIds.slice(0, 3).map(idStr => { const id = Number(idStr); const name = categories.find((c) => c.id === id)?.name || '未知'; return { name, amount: catMap[id] }; }); - - // Avg daily spend (简单计算: 本月已花 / 已过天数) const dayOfMonth = new Date().getDate(); const avgDaily = dayOfMonth > 0 ? monthlyBudgetSpentTotal / dayOfMonth : 0; @@ -223,305 +189,103 @@ function Home() { })); }, [weekTransactions, categories]); - const handleQuickTransaction = () => { - navigate('/transactions?action=new'); - }; - - const handleAIBookkeeping = () => { - navigate('/chat'); - }; - - const handleVoiceBookkeeping = () => { - setVoiceModalOpen(true); - }; - - const handleAIConfirm = () => { - // 确认后刷新数据 - loadData(); - setVoiceModalOpen(false); - }; - - const handleViewAllAccounts = () => { - navigate('/accounts'); - }; - - const handleViewAllTransactions = () => { - navigate('/transactions'); - }; + const handleQuickTransaction = () => navigate('/transactions?action=new'); + const handleAIBookkeeping = () => navigate('/chat'); + const handleVoiceBookkeeping = () => setVoiceModalOpen(true); + const handleAIConfirm = () => { loadData(); setVoiceModalOpen(false); }; + const handleViewAllAccounts = () => navigate('/accounts'); + const handleViewAllTransactions = () => navigate('/transactions'); const handleLedgerSelect = async (ledgerId: number) => { try { - // Update settings with new current ledger if (settings) { await updateSettings({ ...settings, currentLedgerId: ledgerId }); setSettings({ ...settings, currentLedgerId: ledgerId }); } setLedgerSelectorOpen(false); - // Reload data to show transactions from selected ledger await loadData(); - } catch (err) { - console.error('Failed to switch ledger:', err); - setError('切换账本失败'); - } + } catch (err) { setError('切换账本失败'); } }; - const handleLedgerReorder = async (reorderedLedgers: Ledger[]) => { - try { - setLedgers(reorderedLedgers); - const ledgerIds = reorderedLedgers.map(l => l.id); - await reorderLedgers(ledgerIds); - } catch (err) { - console.error('Failed to reorder ledgers:', err); - // Revert on error - await loadData(); - } - }; - - const handleAddLedger = () => { - setLedgerSelectorOpen(false); - navigate('/ledgers/new'); - }; - - const handleManageLedgers = () => { - setLedgerSelectorOpen(false); - navigate('/ledgers'); + setLedgers(reorderedLedgers); + reorderLedgers(reorderedLedgers.map(l => l.id)).catch(() => loadData()); }; + const handleAddLedger = () => { setLedgerSelectorOpen(false); navigate('/ledgers/new'); }; + const handleManageLedgers = () => { setLedgerSelectorOpen(false); navigate('/ledgers'); }; const currentLedger = ledgers.find(l => l.id === settings?.currentLedgerId) || ledgers.find(l => l.isDefault) || ledgers[0]; - const totalAssets = calculateTotalAssets(accounts); const totalLiabilities = calculateTotalLiabilities(accounts); const netWorth = totalAssets - totalLiabilities; + const isHealthy = netWorth > 0 && totalLiabilities < totalAssets * 0.5; - // 标准金额格式化(带完整小数) - const formatCurrency = (amount: number): string => { - return new Intl.NumberFormat('zh-CN', { - style: 'currency', - currency: 'CNY', - minimumFractionDigits: 0, - maximumFractionDigits: amount % 1 === 0 ? 0 : 2, // 整数不显示小数 - }).format(amount); - }; - - // 大数字简洁显示(用于净资产等主要展示) - const formatLargeNumber = (amount: number): { value: string; suffix: string } => { - const absAmount = Math.abs(amount); + const formatCurrencySimple = (amount: number) => new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(amount); + const formatLargeNumber = (amount: number) => { + const abs = Math.abs(amount); const sign = amount < 0 ? '-' : ''; - - if (absAmount >= 100000000) { - // 亿 - const value = absAmount / 100000000; - return { - value: sign + value.toLocaleString('zh-CN', { maximumFractionDigits: 2 }), - suffix: '亿' - }; - } else if (absAmount >= 10000) { - // 万 - const value = absAmount / 10000; - return { - value: sign + value.toLocaleString('zh-CN', { maximumFractionDigits: 2 }), - suffix: '万' - }; - } else { - // 直接显示 - return { - value: sign + absAmount.toLocaleString('zh-CN', { - minimumFractionDigits: 0, - maximumFractionDigits: absAmount % 1 === 0 ? 0 : 2 - }), - suffix: '' - }; - } + if (abs >= 100000000) return { value: sign + (abs / 100000000).toFixed(2), suffix: '亿' }; + else if (abs >= 10000) return { value: sign + (abs / 10000).toFixed(2), suffix: '万' }; + return { value: sign + abs.toLocaleString(), suffix: '' }; }; - // Lock body scroll when modal is open - useEffect(() => { - if (showAccountForm) { - document.body.style.overflow = 'hidden'; - } else { - document.body.style.overflow = ''; - } - return () => { - document.body.style.overflow = ''; - }; - }, [showAccountForm]); - - const formatDate = (dateString: string): string => { - const date = new Date(dateString); + const formatDate = (dateString: string) => { + const d = new Date(dateString); const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(yesterday.getDate() - 1); - - if (date.toDateString() === today.toDateString()) { - return '今天'; - } else if (date.toDateString() === yesterday.toDateString()) { - return '昨天'; - } else { - return date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' }); - } + if (isSameDay(d, today)) return '今天'; + return d.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' }); }; - // Phase 3: Daily Briefing Logic - const getDailyBriefing = () => { - const hour = new Date().getHours(); - let greeting = '你好'; - if (hour < 5) greeting = '夜深了'; - else if (hour < 11) greeting = '早上好'; - else if (hour < 13) greeting = '中午好'; - else if (hour < 18) greeting = '下午好'; - else greeting = '晚上好'; - - let insight = '今天还没有记账哦'; - if (todaySpend > 0) { - if (yesterdaySpend > 0) { - const diff = todaySpend - yesterdaySpend; - const diffPercent = Math.abs((diff / yesterdaySpend) * 100); - - if (Math.abs(diff) < 5) { - insight = `今日支出 ${formatCurrency(todaySpend)},与昨天持平`; - } else if (diff < 0) { - insight = `今日支出 ${formatCurrency(todaySpend)},比昨天节省了 ${diffPercent.toFixed(0)}%`; - } else { - insight = `今日支出 ${formatCurrency(todaySpend)},比昨天多 ${diffPercent.toFixed(0)}%`; - } - } else { - insight = `今天已支出 ${formatCurrency(todaySpend)}`; - } - } else { - if (yesterdaySpend > 0) { - insight = '新的一天,保持理智消费'; - } - } - - return { greeting, insight }; + // Daily Briefing + const getGreeting = () => { + const h = new Date().getHours(); + if (h < 5) return '夜深了'; + if (h < 11) return '早上好'; + if (h < 13) return '中午好'; + if (h < 18) return '下午好'; + return '晚上好'; }; - const { greeting, insight } = getDailyBriefing(); - - // Phase 3: Financial Health Score (Enhanced Logic) const calculateHealthScore = () => { - // 1. Solvency Score (70% weight): Net Worth / Total Assets - // If no assets, assume baseline 60 if no debt, else lower if (totalAssets === 0) return totalLiabilities > 0 ? 40 : 60; - - const solvencyRatio = (totalAssets - totalLiabilities) / totalAssets; // 1.0 = perfect, 0.0 = bankrupt - - // 2. Streak Bonus (10% weight): Encourages habit - const streakBonus = (streakInfo?.currentStreak || 0) > 3 ? 5 : 0; - - // 3. Activity Bonus (20% weight): Scored if todaySpend > 0 or yesterdaySpend > 0 (active user) - const activityBonus = (todaySpend > 0 || yesterdaySpend > 0) ? 5 : 0; - - // Weights: Base (might be high) -> normalized - // Strategy: Map 0-1 solvency to 40-90 range, then add bonuses - // Solvency 1.0 -> 90 - // Solvency 0.5 -> 65 - // Solvency 0.0 -> 40 - let finalScore = 40 + (solvencyRatio * 50); - - finalScore += streakBonus + activityBonus; - - return Math.min(Math.max(Math.round(finalScore), 40), 99); + const ratio = (totalAssets - totalLiabilities) / totalAssets; + let score = 40 + (ratio * 50); + if ((streakInfo?.currentStreak || 0) > 3) score += 5; + if (todaySpend > 0 || yesterdaySpend > 0) score += 5; + return Math.min(Math.max(Math.round(score), 40), 99); }; const healthScore = calculateHealthScore(); + if (loading) return
; + if (error) return
{error}
; - if (loading) { - return ( -
-
- -
-
-
- -
-
-
- - -
-
- -
- - -
-
-
-
-
- ); - } - - if (error) { - return ( -
-
- -

{error}

- -
-
- ); - } + const quickActions = [ + { id: 'add', label: '记一笔', icon: 'solar:add-circle-bold-duotone', color: '#3b82f6', onClick: handleQuickTransaction }, + { id: 'ai', label: 'AI助手', icon: 'solar:magic-stick-3-bold-duotone', color: '#f59e0b', onClick: handleAIBookkeeping }, + { id: 'voice', label: '语音记账', icon: 'solar:microphone-bold-duotone', color: '#10b981', onClick: handleVoiceBookkeeping }, + { id: 'accounts', label: '账户管理', icon: 'solar:wallet-bold-duotone', color: '#8b5cf6', onClick: handleViewAllAccounts }, + ]; return (
-
-
-
setLedgerSelectorOpen(true)}> - {currentLedger && ( - <> - - {currentLedger.name} - - - )} +
+
+
setLedgerSelectorOpen(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: '6px', background: 'rgba(255,255,255,0.4)', padding: '4px 10px', borderRadius: '20px', fontSize: '0.8rem', cursor: 'pointer', border: '1px solid rgba(255,255,255,0.5)' }}> + + {currentLedger?.name || '默认账本'} +
{new Date().toLocaleDateString('zh-CN', { weekday: 'short', month: 'long', day: 'numeric' })}

- {greeting},保持节奏 + {getGreeting()},开启新的一天

-
- - {streakInfo?.message || insight} -
- {streakInfo && streakInfo.currentStreak > 0 && ( -
- - {streakInfo.currentStreak} -
- )} -
-
-
-
- -
-
- {/* Daily Insight Card */} -
+
+ {/* Row 1: Insights & Health */} +
+
setShowHealthModal(true)} style={{ cursor: 'pointer' }}> + 80 ? 'up' : 'neutral'} + /> +
- {/* Contribution Heatmap */} -
+ {/* Row 2: Assets */} +
+
+ 总净资产 +
+ ¥ + {formatLargeNumber(netWorth).value} + {formatLargeNumber(netWorth).suffix} +
+
+
+ 资产状况良好 +
+
+
+
+
+
+
+ 总资产 +
+
+ {formatCurrencySimple(totalAssets)} +
+
+
+
+
+ 总负债 +
+
+ {formatCurrencySimple(totalLiabilities)} +
+
+ + {/* Row 3: Trends & Actions */} +
+ +
+
+ +
+ + {/* Row 4: Transactions & Heatmap */} +
+
+ 最近交易 + +
+
+ {recentTransactions.map(t => ( +
navigate('/transactions')} style={{ marginBottom: '8px' }}> +
+ {t.type === 'income' ? : } +
+
+ {categories.find(c => c.id === t.categoryId)?.name} + {t.note || formatDate(t.transactionDate)} +
+ + {t.type === 'income' ? '+' : '-'}{formatCurrencySimple(Math.abs(t.amount))} + +
+ ))} + {recentTransactions.length === 0 &&
暂无记录
} +
+
+ +
+
- {/* Asset Dashboard - Requirement 8.1 */} -
- {/* Net Worth Card - Main Hero */} -
-
- 净资产 -
- ¥ - - {formatLargeNumber(netWorth).value} - - {formatLargeNumber(netWorth).suffix && ( - {formatLargeNumber(netWorth).suffix} - )} -
-
- 总览所有账户 -
-
-
-
- - {/* Assets Card */} -
-
- -
-
- 总资产 - {formatCurrency(totalAssets)} -
-
- - {/* Liabilities Card */} -
-
- -
-
- 总负债 - {formatCurrency(totalLiabilities)} -
-
-
- - {/* Quick Actions Section */} -
- - - - - -
- -
- {/* Spending Trend Chart */} -
- -
- - {/* Recent Transactions List */} -
-
-

最近交易

- -
- - {recentTransactions.length > 0 ? ( -
- {recentTransactions.map((transaction) => ( -
navigate('/transactions')}> -
- {transaction.type === 'income' ? : } -
-
- - {categories.find(c => c.id === transaction.categoryId)?.name || '无分类'} - - {transaction.note || '无备注'} -
-
- - {transaction.type === 'income' ? '+' : '-'}{formatCurrency(Math.abs(transaction.amount))} - - {formatDate(transaction.transactionDate)} -
-
- ))} -
- ) : ( -
- -

暂无交易记录

-
- )} -
-
- - -
- - {/* Ledger Selector Modal - Requirements 3.2, 3.3 */} + {/* Modals */} {ledgers.length > 0 && ( setLedgerSelectorOpen(false)} /> )} - - {/* AI Voice Input Modal */} - setVoiceModalOpen(false)} - onConfirm={handleAIConfirm} - /> - - {/* First Account Guide Modal */} - setShowAccountForm(true)} - /> - - {/* Account Creation Modal */} + setVoiceModalOpen(false)} onConfirm={handleAIConfirm} /> + setShowAccountForm(true)} /> {showAccountForm && (
{ - try { - setLoading(true); - await createAccount(data); - setShowAccountForm(false); - await loadData(); - } catch (err) { - setError('创建账户失败,请重试'); - console.error(err); - setLoading(false); - } - }} - onCancel={() => { - // Should not allow cancel if it's the first account? - // Let's allow it but the Guide Modal will pop up again immediately because accounts.length is still 0 - setShowAccountForm(false); - }} + onSubmit={async (data) => { await createAccount(data); setShowAccountForm(false); await loadData(); }} + onCancel={() => setShowAccountForm(false)} loading={loading} />
)} - setShowConfetti(false)} /> - setShowHealthModal(false)} @@ -738,8 +420,6 @@ function Home() { todaySpend={todaySpend} yesterdaySpend={yesterdaySpend} /> - - {/* Floating Action Button for Voice Input - Feature: voice-bookkeeping-module */}