Spaces:
Running
Running
| <html lang="pt-BR"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Assistente de Bolso — Dashboard</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| :root { | |
| --green: #1DE199; | |
| --green-dark: #17B87A; | |
| --green-light: #E8FBF4; | |
| --green-glow: rgba(29, 225, 153, 0.12); | |
| --green-subtle: #F0FDF9; | |
| --white: #FFFFFF; | |
| --black: #0B0B0B; | |
| --gray-50: #FAFAFA; | |
| --gray-100: #F4F4F5; | |
| --gray-200: #E4E4E7; | |
| --gray-300: #D4D4D8; | |
| --gray-400: #A1A1AA; | |
| --gray-500: #71717A; | |
| --gray-600: #52525B; | |
| --gray-700: #3F3F46; | |
| --gray-800: #27272A; | |
| --red: #EF4444; | |
| --red-light: #FEF2F2; | |
| --blue: #3B82F6; | |
| --blue-light: #EFF6FF; | |
| --yellow: #F59E0B; | |
| --yellow-light: #FFFBEB; | |
| --purple: #8B5CF6; | |
| --purple-light: #F5F3FF; | |
| --font-serif: 'DM Serif Display', serif; | |
| --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
| --shadow-xs: 0 1px 2px rgba(0,0,0,0.04); | |
| --shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04); | |
| --shadow-md: 0 4px 6px rgba(0,0,0,0.05), 0 2px 4px rgba(0,0,0,0.03); | |
| --shadow-lg: 0 10px 15px rgba(0,0,0,0.08), 0 4px 6px rgba(0,0,0,0.04); | |
| --radius-sm: 6px; | |
| --radius-md: 10px; | |
| --radius-lg: 14px; | |
| --radius-full: 9999px; | |
| --space-1: 4px; | |
| --space-2: 8px; | |
| --space-3: 12px; | |
| --space-4: 16px; | |
| --space-5: 20px; | |
| --space-6: 24px; | |
| --space-8: 32px; | |
| --space-10: 40px; | |
| } | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| html { scroll-behavior: smooth; } | |
| body { | |
| font-family: var(--font-sans); | |
| background: var(--gray-50); | |
| color: var(--black); | |
| line-height: 1.5; | |
| min-height: 100dvh; | |
| -webkit-font-smoothing: antialiased; | |
| } | |
| /* ===== HEADER ===== */ | |
| .header { | |
| background: var(--white); | |
| border-bottom: 1px solid var(--gray-200); | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .header-inner { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| padding: var(--space-3) var(--space-4); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-2); | |
| } | |
| .logo-icon { | |
| width: 32px; | |
| height: 32px; | |
| background: linear-gradient(135deg, var(--green), var(--green-dark)); | |
| border-radius: var(--radius-sm); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .logo-icon svg { width: 18px; height: 18px; color: var(--white); } | |
| .logo-img { width: 28px; height: 28px; object-fit: contain; } | |
| .logo-text { font-family: var(--font-serif); font-size: 1rem; } | |
| .header-actions { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-2); | |
| } | |
| .avatar { | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| background: var(--gray-200); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.7rem; | |
| font-weight: 600; | |
| color: var(--gray-600); | |
| } | |
| /* ===== TABS NAV ===== */ | |
| .tabs-nav { | |
| background: var(--white); | |
| border-bottom: 1px solid var(--gray-200); | |
| position: sticky; | |
| top: 56px; | |
| z-index: 90; | |
| } | |
| .tabs-nav-inner { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| display: flex; | |
| padding: 0 var(--space-4); | |
| } | |
| .tab-btn { | |
| font-family: var(--font-sans); | |
| font-size: 0.8125rem; | |
| font-weight: 500; | |
| padding: var(--space-3) var(--space-4); | |
| background: none; | |
| border: none; | |
| color: var(--gray-500); | |
| cursor: pointer; | |
| position: relative; | |
| transition: color 0.15s; | |
| } | |
| .tab-btn:hover { color: var(--black); } | |
| .tab-btn.active { color: var(--black); } | |
| .tab-btn.active::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: 0; | |
| left: var(--space-4); | |
| right: var(--space-4); | |
| height: 2px; | |
| background: var(--green); | |
| border-radius: 2px 2px 0 0; | |
| } | |
| .tab-btn:focus-visible { outline: 2px solid var(--green); outline-offset: -2px; } | |
| /* ===== FILTERS (STRIPE STYLE) ===== */ | |
| .filters-bar { | |
| background: var(--white); | |
| border-bottom: 1px solid var(--gray-200); | |
| } | |
| .filters-inner { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| padding: var(--space-3) var(--space-4); | |
| display: flex; | |
| flex-wrap: nowrap; | |
| gap: var(--space-2); | |
| align-items: center; | |
| overflow-x: auto; | |
| -webkit-overflow-scrolling: touch; | |
| scrollbar-width: none; | |
| -ms-overflow-style: none; | |
| } | |
| .filters-inner::-webkit-scrollbar { | |
| display: none; | |
| } | |
| .filter-chip { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: var(--space-1); | |
| font-family: var(--font-sans); | |
| font-size: 0.75rem; | |
| font-weight: 500; | |
| padding: 6px 10px; | |
| background: var(--white); | |
| border: 1px solid var(--gray-300); | |
| border-radius: var(--radius-sm); | |
| color: var(--gray-700); | |
| cursor: pointer; | |
| transition: all 0.15s; | |
| white-space: nowrap; | |
| } | |
| .filter-chip:hover { | |
| background: var(--gray-50); | |
| border-color: var(--gray-400); | |
| } | |
| .filter-chip:focus { | |
| outline: none; | |
| border-color: var(--green); | |
| box-shadow: 0 0 0 3px var(--green-glow); | |
| } | |
| .filter-chip svg { | |
| width: 12px; | |
| height: 12px; | |
| color: var(--gray-400); | |
| } | |
| .filter-chip.active { | |
| background: var(--green-light); | |
| border-color: var(--green); | |
| color: var(--green-dark); | |
| } | |
| .filter-chip.active svg { color: var(--green-dark); } | |
| .filter-dropdown { | |
| position: relative; | |
| } | |
| .filter-dropdown-content { | |
| display: none; | |
| position: absolute; | |
| top: calc(100% + 4px); | |
| left: 0; | |
| min-width: 180px; | |
| background: var(--white); | |
| border: 1px solid var(--gray-200); | |
| border-radius: var(--radius-md); | |
| box-shadow: var(--shadow-lg); | |
| z-index: 200; | |
| padding: var(--space-1); | |
| } | |
| .filter-dropdown.open .filter-dropdown-content { display: block; } | |
| .filter-option { | |
| display: block; | |
| width: 100%; | |
| padding: var(--space-2) var(--space-3); | |
| font-family: var(--font-sans); | |
| font-size: 0.8125rem; | |
| text-align: left; | |
| background: none; | |
| border: none; | |
| border-radius: var(--radius-sm); | |
| color: var(--gray-700); | |
| cursor: pointer; | |
| transition: background 0.1s; | |
| } | |
| .filter-option:hover { background: var(--gray-100); } | |
| .filter-option.selected { background: var(--green-light); color: var(--green-dark); font-weight: 500; } | |
| /* ===== MAIN ===== */ | |
| .main { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| padding: var(--space-4); | |
| } | |
| .tab-content { display: none; } | |
| .tab-content.active { display: block; animation: fadeIn 0.2s ease; } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(6px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| /* ===== KPI GRID ===== */ | |
| .kpi-grid { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: var(--space-3); | |
| margin-bottom: var(--space-4); | |
| } | |
| @media (min-width: 640px) { | |
| .kpi-grid { | |
| grid-template-columns: repeat(4, 1fr); | |
| } | |
| } | |
| .kpi-card { | |
| background: var(--white); | |
| border-radius: var(--radius-md); | |
| padding: var(--space-4); | |
| border: 1px solid var(--gray-200); | |
| position: relative; | |
| } | |
| .kpi-header { | |
| display: flex; | |
| align-items: flex-start; | |
| justify-content: space-between; | |
| margin-bottom: var(--space-2); | |
| } | |
| .kpi-label { | |
| font-size: 0.6875rem; | |
| font-weight: 600; | |
| color: var(--gray-500); | |
| text-transform: uppercase; | |
| letter-spacing: 0.03em; | |
| line-height: 1.3; | |
| } | |
| .info-btn { | |
| width: 16px; | |
| height: 16px; | |
| border-radius: 50%; | |
| background: var(--gray-100); | |
| border: none; | |
| color: var(--gray-400); | |
| font-size: 0.6rem; | |
| font-weight: 700; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-shrink: 0; | |
| transition: all 0.15s; | |
| } | |
| .info-btn:hover { background: var(--gray-200); color: var(--gray-600); } | |
| .info-btn:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; } | |
| .kpi-value { | |
| font-family: var(--font-serif); | |
| font-size: 1.375rem; | |
| color: var(--black); | |
| line-height: 1.1; | |
| } | |
| .kpi-value.positive { color: var(--green-dark); } | |
| .kpi-value.negative { color: var(--red); } | |
| .kpi-subtitle { | |
| font-size: 0.6875rem; | |
| color: var(--gray-400); | |
| margin-top: 2px; | |
| } | |
| .connect-link { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 4px; | |
| margin-top: var(--space-2); | |
| font-size: 0.6875rem; | |
| font-weight: 600; | |
| color: var(--green); | |
| text-decoration: none; | |
| } | |
| .connect-link:hover { text-decoration: underline; } | |
| /* ===== PROJECTION CARD (REDESIGNED - LIGHT VERSION) ===== */ | |
| .projection-card { | |
| background: var(--white); | |
| border-radius: var(--radius-lg); | |
| border: 1px solid var(--gray-200); | |
| margin-bottom: var(--space-4); | |
| overflow: hidden; | |
| } | |
| .projection-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: var(--space-4); | |
| border-bottom: 1px solid var(--gray-100); | |
| } | |
| .projection-title { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-3); | |
| } | |
| .projection-icon { | |
| width: 36px; | |
| height: 36px; | |
| border-radius: var(--radius-md); | |
| background: linear-gradient(135deg, var(--green-light), var(--green-subtle)); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--green-dark); | |
| } | |
| .projection-icon svg { | |
| width: 18px; | |
| height: 18px; | |
| } | |
| .projection-title-text h2 { | |
| font-family: var(--font-serif); | |
| font-size: 1rem; | |
| font-weight: 400; | |
| color: var(--black); | |
| margin-bottom: 0; | |
| line-height: 1.2; | |
| } | |
| .projection-title-text span { | |
| font-size: 0.6875rem; | |
| color: var(--gray-500); | |
| line-height: 1.2; | |
| } | |
| .badge { | |
| font-size: 0.5625rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| padding: 4px 10px; | |
| border-radius: var(--radius-full); | |
| background: var(--green-light); | |
| color: var(--green-dark); | |
| border: 1px solid var(--green); | |
| } | |
| .projection-body { | |
| padding: var(--space-4); | |
| } | |
| .projection-metrics { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: var(--space-3); | |
| margin-bottom: var(--space-4); | |
| } | |
| @media (min-width: 500px) { | |
| .projection-metrics { grid-template-columns: repeat(4, 1fr); } | |
| } | |
| .projection-metric { | |
| background: var(--gray-50); | |
| border-radius: var(--radius-md); | |
| padding: var(--space-3); | |
| text-align: center; | |
| } | |
| .projection-metric-label { | |
| font-size: 0.625rem; | |
| font-weight: 600; | |
| color: var(--gray-500); | |
| text-transform: uppercase; | |
| letter-spacing: 0.03em; | |
| margin-bottom: var(--space-1); | |
| } | |
| .projection-metric-value { | |
| font-family: var(--font-serif); | |
| font-size: 1.125rem; | |
| line-height: 1.2; | |
| color: var(--black); | |
| } | |
| .projection-metric-value.income { color: var(--green-dark); } | |
| .projection-metric-value.expense { color: var(--red); } | |
| .projection-metric-value.result { color: var(--green-dark); } | |
| .projection-metric-value.result.negative { color: var(--red); } | |
| .projection-divider { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-3); | |
| margin-bottom: var(--space-3); | |
| } | |
| .projection-divider::before, | |
| .projection-divider::after { | |
| content: ''; | |
| flex: 1; | |
| height: 1px; | |
| background: var(--gray-200); | |
| } | |
| .projection-divider-text { | |
| font-size: 0.625rem; | |
| font-weight: 600; | |
| color: var(--gray-400); | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| } | |
| .projection-list { | |
| list-style: none; | |
| display: grid; | |
| gap: var(--space-2); | |
| } | |
| .projection-item { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: var(--space-3); | |
| background: var(--gray-50); | |
| border-radius: var(--radius-md); | |
| transition: background 0.15s; | |
| } | |
| .projection-item:hover { | |
| background: var(--gray-100); | |
| } | |
| .projection-item-info { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-3); | |
| } | |
| .projection-item-icon { | |
| width: 32px; | |
| height: 32px; | |
| border-radius: var(--radius-sm); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.875rem; | |
| } | |
| .projection-item-icon.expense { | |
| background: var(--red-light); | |
| } | |
| .projection-item-icon.income { | |
| background: var(--green-light); | |
| } | |
| .projection-item-text { display: flex; flex-direction: column; } | |
| .projection-item-title { | |
| font-size: 0.8125rem; | |
| font-weight: 500; | |
| color: var(--black); | |
| } | |
| .projection-item-date { | |
| font-size: 0.6875rem; | |
| color: var(--gray-500); | |
| } | |
| .projection-item-value { | |
| font-size: 0.875rem; | |
| font-weight: 600; | |
| text-align: right; | |
| } | |
| .projection-item-value.income { color: var(--green-dark); } | |
| .projection-item-value.expense { color: var(--red); } | |
| .projection-footer { | |
| padding: var(--space-3) var(--space-4); | |
| background: var(--gray-50); | |
| border-top: 1px solid var(--gray-100); | |
| } | |
| .projection-note { | |
| font-size: 0.6875rem; | |
| color: var(--gray-500); | |
| line-height: 1.5; | |
| display: flex; | |
| align-items: flex-start; | |
| gap: var(--space-2); | |
| } | |
| .projection-note svg { | |
| width: 14px; | |
| height: 14px; | |
| color: var(--gray-400); | |
| flex-shrink: 0; | |
| margin-top: 1px; | |
| } | |
| /* ===== CARD ===== */ | |
| .card { | |
| background: var(--white); | |
| border-radius: var(--radius-md); | |
| border: 1px solid var(--gray-200); | |
| margin-bottom: var(--space-4); | |
| overflow: hidden; | |
| } | |
| .card-header { | |
| padding: var(--space-4); | |
| border-bottom: 1px solid var(--gray-100); | |
| } | |
| .card-header h3 { | |
| font-family: var(--font-serif); | |
| font-size: 0.9375rem; | |
| font-weight: 400; | |
| } | |
| .card-body { padding: var(--space-4); } | |
| /* ===== CHARTS ===== */ | |
| .chart-container { | |
| position: relative; | |
| height: 180px; | |
| } | |
| .chart-row { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: var(--space-4); | |
| } | |
| @media (min-width: 640px) { | |
| .chart-row { grid-template-columns: repeat(2, 1fr); } | |
| } | |
| .mini-chart-container { | |
| position: relative; | |
| height: 140px; | |
| } | |
| /* ===== CATEGORY GRID ===== */ | |
| .category-grid { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: var(--space-4); | |
| } | |
| @media (min-width: 500px) { | |
| .category-grid { grid-template-columns: 140px 1fr; } | |
| } | |
| .category-chart-wrapper { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .category-chart-box { | |
| width: 120px; | |
| height: 120px; | |
| } | |
| .category-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 0.75rem; | |
| } | |
| .category-table th { | |
| text-align: left; | |
| font-size: 0.625rem; | |
| font-weight: 600; | |
| color: var(--gray-500); | |
| text-transform: uppercase; | |
| letter-spacing: 0.03em; | |
| padding: var(--space-2) 0; | |
| border-bottom: 1px solid var(--gray-200); | |
| } | |
| .category-table th:last-child { text-align: right; } | |
| .category-table td { | |
| padding: var(--space-2) 0; | |
| border-bottom: 1px solid var(--gray-100); | |
| } | |
| .category-table td:last-child { text-align: right; font-weight: 600; } | |
| .cat-dot { | |
| display: inline-block; | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| margin-right: var(--space-2); | |
| } | |
| .cat-percent { color: var(--gray-400); font-size: 0.6875rem; } | |
| /* ===== BREAKDOWN MINI CARDS ===== */ | |
| .breakdown-row { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: var(--space-3); | |
| margin-bottom: var(--space-4); | |
| } | |
| @media (min-width: 640px) { | |
| .breakdown-row { grid-template-columns: repeat(3, 1fr); } | |
| } | |
| .breakdown-card { | |
| background: var(--white); | |
| border-radius: var(--radius-md); | |
| border: 1px solid var(--gray-200); | |
| padding: var(--space-3); | |
| } | |
| .breakdown-card h4 { | |
| font-size: 0.6875rem; | |
| font-weight: 600; | |
| color: var(--gray-500); | |
| text-transform: uppercase; | |
| letter-spacing: 0.02em; | |
| margin-bottom: var(--space-2); | |
| } | |
| .breakdown-list { list-style: none; } | |
| .breakdown-item { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: var(--space-1) 0; | |
| font-size: 0.75rem; | |
| } | |
| .breakdown-item-label { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-1); | |
| color: var(--gray-700); | |
| } | |
| .breakdown-item-value { font-weight: 600; color: var(--black); } | |
| /* ===== TRANSACTION LIST ===== */ | |
| .search-bar { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: var(--space-2); | |
| margin-bottom: var(--space-3); | |
| } | |
| .search-input { | |
| flex: 1; | |
| min-width: 160px; | |
| font-family: var(--font-sans); | |
| font-size: 0.75rem; | |
| padding: var(--space-2) var(--space-3); | |
| padding-left: 32px; | |
| border: 1px solid var(--gray-300); | |
| border-radius: var(--radius-sm); | |
| background: var(--white) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23A1A1AA' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E") no-repeat 10px center; | |
| transition: border-color 0.15s, box-shadow 0.15s; | |
| } | |
| .search-input:focus { | |
| outline: none; | |
| border-color: var(--green); | |
| box-shadow: 0 0 0 3px var(--green-glow); | |
| } | |
| .chip-group { display: flex; gap: var(--space-1); } | |
| .chip { | |
| font-family: var(--font-sans); | |
| font-size: 0.6875rem; | |
| font-weight: 500; | |
| padding: var(--space-1) var(--space-2); | |
| border: 1px solid var(--gray-300); | |
| border-radius: var(--radius-full); | |
| background: var(--white); | |
| color: var(--gray-600); | |
| cursor: pointer; | |
| transition: all 0.15s; | |
| } | |
| .chip:hover { border-color: var(--green); color: var(--green-dark); } | |
| .chip.active { background: var(--green-light); border-color: var(--green); color: var(--green-dark); } | |
| .tx-list { list-style: none; } | |
| .tx-item { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: var(--space-3) 0; | |
| border-bottom: 1px solid var(--gray-100); | |
| gap: var(--space-3); | |
| } | |
| .tx-item:last-child { border-bottom: none; } | |
| .tx-info { flex: 1; min-width: 0; } | |
| .tx-main { | |
| display: flex; | |
| align-items: center; | |
| gap: var(--space-2); | |
| margin-bottom: 2px; | |
| } | |
| .tx-title { | |
| font-size: 0.8125rem; | |
| font-weight: 500; | |
| color: var(--black); | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .tx-badge { | |
| font-size: 0.5625rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| padding: 2px 5px; | |
| border-radius: 3px; | |
| flex-shrink: 0; | |
| } | |
| .tx-badge.rec { background: var(--yellow-light); color: var(--yellow); } | |
| .tx-badge.parc { background: var(--blue-light); color: var(--blue); } | |
| .tx-badge.pf { background: var(--gray-100); color: var(--gray-600); } | |
| .tx-badge.pj { background: var(--purple-light); color: var(--purple); } | |
| .tx-meta { | |
| font-size: 0.6875rem; | |
| color: var(--gray-500); | |
| display: flex; | |
| gap: var(--space-2); | |
| flex-wrap: wrap; | |
| } | |
| .tx-value { | |
| font-size: 0.8125rem; | |
| font-weight: 600; | |
| text-align: right; | |
| flex-shrink: 0; | |
| } | |
| .tx-value.expense { color: var(--red); } | |
| .tx-value.income { color: var(--green-dark); } | |
| .view-all-btn { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 100%; | |
| padding: var(--space-3); | |
| font-family: var(--font-sans); | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| color: var(--gray-600); | |
| background: var(--gray-50); | |
| border: none; | |
| border-top: 1px solid var(--gray-200); | |
| cursor: pointer; | |
| transition: background 0.15s, color 0.15s; | |
| } | |
| .view-all-btn:hover { background: var(--gray-100); color: var(--black); } | |
| /* ===== MODAL ===== */ | |
| .modal-overlay { | |
| display: none; | |
| position: fixed; | |
| inset: 0; | |
| background: rgba(0,0,0,0.4); | |
| z-index: 1000; | |
| align-items: center; | |
| justify-content: center; | |
| padding: var(--space-4); | |
| } | |
| .modal-overlay.visible { display: flex; animation: fadeIn 0.15s ease; } | |
| .modal { | |
| background: var(--white); | |
| border-radius: var(--radius-lg); | |
| max-width: 500px; | |
| width: 100%; | |
| max-height: 85vh; | |
| overflow-y: auto; | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .modal-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: var(--space-4); | |
| border-bottom: 1px solid var(--gray-200); | |
| } | |
| .modal-header h4 { | |
| font-family: var(--font-serif); | |
| font-size: 1rem; | |
| font-weight: 400; | |
| } | |
| .modal-close { | |
| width: 28px; | |
| height: 28px; | |
| border-radius: var(--radius-sm); | |
| background: none; | |
| border: none; | |
| color: var(--gray-400); | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: background 0.15s; | |
| } | |
| .modal-close:hover { background: var(--gray-100); color: var(--black); } | |
| .modal-body { padding: var(--space-4); } | |
| .modal-body p { font-size: 0.875rem; color: var(--gray-600); line-height: 1.6; } | |
| .full-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 0.75rem; | |
| } | |
| .full-table th { | |
| text-align: left; | |
| font-size: 0.625rem; | |
| font-weight: 600; | |
| color: var(--gray-500); | |
| text-transform: uppercase; | |
| padding: var(--space-2); | |
| background: var(--gray-50); | |
| border-bottom: 1px solid var(--gray-200); | |
| position: sticky; | |
| top: 0; | |
| } | |
| .full-table td { | |
| padding: var(--space-2); | |
| border-bottom: 1px solid var(--gray-100); | |
| vertical-align: middle; | |
| } | |
| .full-table .val { font-weight: 600; white-space: nowrap; } | |
| .full-table .val.expense { color: var(--red); } | |
| .full-table .val.income { color: var(--green-dark); } | |
| .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- HEADER --> | |
| <header class="header"> | |
| <div class="header-inner"> | |
| <div class="logo"> | |
| <img src="data:image/webp;base64,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" alt="Assistente de Bolso" class="logo-img"> | |
| <span class="logo-text">Assistente de Bolso</span> | |
| </div> | |
| <div class="header-actions"> | |
| <div class="avatar">DS</div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- TABS NAV --> | |
| <nav class="tabs-nav" role="tablist"> | |
| <div class="tabs-nav-inner"> | |
| <button class="tab-btn active" role="tab" data-tab="resumo">Resumo</button> | |
| <button class="tab-btn" role="tab" data-tab="despesas">Despesas</button> | |
| <button class="tab-btn" role="tab" data-tab="receitas">Receitas</button> | |
| </div> | |
| </nav> | |
| <!-- FILTERS BAR (STRIPE STYLE) --> | |
| <div class="filters-bar"> | |
| <div class="filters-inner"> | |
| <div class="filter-dropdown" id="filterPeriod"> | |
| <button class="filter-chip" aria-haspopup="true"> | |
| <span>Dezembro 2024</span> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg> | |
| </button> | |
| <div class="filter-dropdown-content"> | |
| <button class="filter-option selected">Mês atual</button> | |
| <button class="filter-option">Mês anterior</button> | |
| <button class="filter-option">Últimos 3 meses</button> | |
| <button class="filter-option">Últimos 6 meses</button> | |
| <button class="filter-option">Últimos 12 meses</button> | |
| <button class="filter-option">Personalizado</button> | |
| </div> | |
| </div> | |
| <div class="filter-dropdown" id="filterBank"> | |
| <button class="filter-chip" aria-haspopup="true"> | |
| <span>Todos os bancos</span> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg> | |
| </button> | |
| <div class="filter-dropdown-content"> | |
| <button class="filter-option selected">Todos os bancos</button> | |
| <button class="filter-option">Inter</button> | |
| <button class="filter-option">Nubank</button> | |
| <button class="filter-option">Bradesco</button> | |
| </div> | |
| </div> | |
| <div class="filter-dropdown" id="filterMethod"> | |
| <button class="filter-chip" aria-haspopup="true"> | |
| <span>Todos métodos</span> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg> | |
| </button> | |
| <div class="filter-dropdown-content"> | |
| <button class="filter-option selected">Todos métodos</button> | |
| <button class="filter-option">Cartão de crédito</button> | |
| <button class="filter-option">PIX</button> | |
| <button class="filter-option">Boleto</button> | |
| <button class="filter-option">Cartão de débito</button> | |
| <button class="filter-option">Não informado</button> | |
| </div> | |
| </div> | |
| <div class="filter-dropdown" id="filterAccount"> | |
| <button class="filter-chip" aria-haspopup="true"> | |
| <span>PF + PJ</span> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg> | |
| </button> | |
| <div class="filter-dropdown-content"> | |
| <button class="filter-option selected">Ambos</button> | |
| <button class="filter-option">Somente PF</button> | |
| <button class="filter-option">Somente PJ</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- MAIN --> | |
| <main class="main"> | |
| <!-- ===== ABA RESUMO ===== --> | |
| <div class="tab-content active" id="tab-resumo"> | |
| <!-- KPI Grid --> | |
| <div class="kpi-grid"> | |
| <article class="kpi-card"> | |
| <div class="kpi-header"> | |
| <span class="kpi-label">Despesas do mês</span> | |
| <button class="info-btn" data-info="despesas">i</button> | |
| </div> | |
| <div class="kpi-value negative">R$ 7.284,50</div> | |
| <span class="kpi-subtitle">até agora</span> | |
| </article> | |
| <article class="kpi-card"> | |
| <div class="kpi-header"> | |
| <span class="kpi-label">Receitas do mês</span> | |
| <button class="info-btn" data-info="receitas">i</button> | |
| </div> | |
| <div class="kpi-value positive">R$ 11.200,00</div> | |
| <span class="kpi-subtitle">até agora</span> | |
| </article> | |
| <article class="kpi-card"> | |
| <div class="kpi-header"> | |
| <span class="kpi-label">Resultado do mês</span> | |
| <button class="info-btn" data-info="resultado">i</button> | |
| </div> | |
| <div class="kpi-value positive">R$ 3.915,50</div> | |
| <span class="kpi-subtitle">receitas − despesas</span> | |
| </article> | |
| <article class="kpi-card"> | |
| <div class="kpi-header"> | |
| <span class="kpi-label">Saldo atual</span> | |
| <button class="info-btn" data-info="saldo">i</button> | |
| </div> | |
| <div class="kpi-value">R$ 18.742,33</div> | |
| <span class="kpi-subtitle">dinheiro na conta</span> | |
| </article> | |
| </div> | |
| <!-- Projection Card (Redesigned) --> | |
| <section class="projection-card"> | |
| <div class="projection-header"> | |
| <div class="projection-title"> | |
| <div class="projection-icon"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M12 8v4l3 3"/> | |
| <circle cx="12" cy="12" r="10"/> | |
| </svg> | |
| </div> | |
| <div class="projection-title-text"> | |
| <h2>Projeção até o fim do mês</h2> | |
| <span>Baseado em compromissos conhecidos</span> | |
| </div> | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 8px;"> | |
| <span class="badge">Previsão</span> | |
| <button class="info-btn" data-info="projecao">i</button> | |
| </div> | |
| </div> | |
| <div class="projection-body"> | |
| <div class="projection-metrics"> | |
| <div class="projection-metric"> | |
| <div class="projection-metric-label">Entradas</div> | |
| <div class="projection-metric-value income">+R$ 0</div> | |
| </div> | |
| <div class="projection-metric"> | |
| <div class="projection-metric-label">Saídas</div> | |
| <div class="projection-metric-value expense">−R$ 4.120</div> | |
| </div> | |
| <div class="projection-metric"> | |
| <div class="projection-metric-label">Resultado</div> | |
| <div class="projection-metric-value result negative">−R$ 204</div> | |
| </div> | |
| <div class="projection-metric"> | |
| <div class="projection-metric-label">Saldo previsto</div> | |
| <div class="projection-metric-value">R$ 14.622</div> | |
| </div> | |
| </div> | |
| <div class="projection-divider"> | |
| <span class="projection-divider-text">Próximos compromissos</span> | |
| </div> | |
| <ul class="projection-list"> | |
| <li class="projection-item"> | |
| <div class="projection-item-info"> | |
| <div class="projection-item-icon expense">📚</div> | |
| <div class="projection-item-text"> | |
| <span class="projection-item-title">Mensalidade escola</span> | |
| <span class="projection-item-date">Dia 20 • Recorrente</span> | |
| </div> | |
| </div> | |
| <span class="projection-item-value expense">−R$ 1.850</span> | |
| </li> | |
| <li class="projection-item"> | |
| <div class="projection-item-info"> | |
| <div class="projection-item-icon expense">💳</div> | |
| <div class="projection-item-text"> | |
| <span class="projection-item-title">Parcela cartão (Inter)</span> | |
| <span class="projection-item-date">Dia 22 • Fatura</span> | |
| </div> | |
| </div> | |
| <span class="projection-item-value expense">−R$ 1.340</span> | |
| </li> | |
| <li class="projection-item"> | |
| <div class="projection-item-info"> | |
| <div class="projection-item-icon expense">🏥</div> | |
| <div class="projection-item-text"> | |
| <span class="projection-item-title">Plano de saúde</span> | |
| <span class="projection-item-date">Dia 25 • Recorrente</span> | |
| </div> | |
| </div> | |
| <span class="projection-item-value expense">−R$ 680</span> | |
| </li> | |
| <li class="projection-item"> | |
| <div class="projection-item-info"> | |
| <div class="projection-item-icon expense">📶</div> | |
| <div class="projection-item-text"> | |
| <span class="projection-item-title">Internet + Celular</span> | |
| <span class="projection-item-date">Dia 28 • Recorrente</span> | |
| </div> | |
| </div> | |
| <span class="projection-item-value expense">−R$ 250</span> | |
| </li> | |
| </ul> | |
| </div> | |
| <div class="projection-footer"> | |
| <p class="projection-note"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <circle cx="12" cy="12" r="10"/> | |
| <path d="M12 16v-4"/> | |
| <path d="M12 8h.01"/> | |
| </svg> | |
| <span>Projeção baseada em despesas recorrentes, parcelas de cartão e compromissos já conhecidos que ainda não foram pagos.</span> | |
| </p> | |
| </div> | |
| </section> | |
| <!-- Charts Row --> | |
| <div class="chart-row"> | |
| <div class="card"> | |
| <div class="card-header"><h3>Despesas vs Receitas</h3></div> | |
| <div class="card-body"> | |
| <div class="mini-chart-container"><canvas id="summaryBarChart"></canvas></div> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <div class="card-header"><h3>Movimentações no mês</h3></div> | |
| <div class="card-body"> | |
| <div class="mini-chart-container"><canvas id="summaryLineChart"></canvas></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ===== ABA DESPESAS ===== --> | |
| <div class="tab-content" id="tab-despesas"> | |
| <!-- Category Breakdown --> | |
| <div class="card"> | |
| <div class="card-header"><h3>Despesas por categoria</h3></div> | |
| <div class="card-body"> | |
| <div class="category-grid"> | |
| <div class="category-chart-wrapper"> | |
| <div class="category-chart-box"><canvas id="expCatChart"></canvas></div> | |
| </div> | |
| <div class="table-scroll"> | |
| <table class="category-table"> | |
| <thead><tr><th>Categoria</th><th>Total</th><th>%</th></tr></thead> | |
| <tbody id="expCatTable"></tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Mini Breakdowns --> | |
| <div class="breakdown-row"> | |
| <div class="breakdown-card"> | |
| <h4>Por método de pagamento</h4> | |
| <ul class="breakdown-list" id="expMethodList"></ul> | |
| </div> | |
| <div class="breakdown-card"> | |
| <h4>Por banco</h4> | |
| <ul class="breakdown-list" id="expBankList"></ul> | |
| </div> | |
| <div class="breakdown-card"> | |
| <h4>Por tipo de conta</h4> | |
| <ul class="breakdown-list" id="expAccountList"></ul> | |
| </div> | |
| </div> | |
| <!-- Evolution Chart --> | |
| <div class="card"> | |
| <div class="card-header"><h3>Evolução das despesas</h3></div> | |
| <div class="card-body"> | |
| <div class="chart-container"><canvas id="expEvolutionChart"></canvas></div> | |
| </div> | |
| </div> | |
| <!-- Transaction List --> | |
| <div class="card"> | |
| <div class="card-header"><h3>Últimas despesas</h3></div> | |
| <div class="card-body"> | |
| <div class="search-bar"> | |
| <input type="text" class="search-input" placeholder="Buscar..."> | |
| <div class="chip-group"> | |
| <button class="chip">Recorrentes</button> | |
| <button class="chip">Parceladas</button> | |
| </div> | |
| </div> | |
| <ul class="tx-list" id="expList"></ul> | |
| </div> | |
| <button class="view-all-btn" data-modal="expModal">Ver todas as despesas</button> | |
| </div> | |
| </div> | |
| <!-- ===== ABA RECEITAS ===== --> | |
| <div class="tab-content" id="tab-receitas"> | |
| <!-- Category Breakdown --> | |
| <div class="card"> | |
| <div class="card-header"><h3>Receitas por categoria</h3></div> | |
| <div class="card-body"> | |
| <div class="category-grid"> | |
| <div class="category-chart-wrapper"> | |
| <div class="category-chart-box"><canvas id="incCatChart"></canvas></div> | |
| </div> | |
| <div class="table-scroll"> | |
| <table class="category-table"> | |
| <thead><tr><th>Categoria</th><th>Total</th><th>%</th></tr></thead> | |
| <tbody id="incCatTable"></tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Evolution Chart --> | |
| <div class="card"> | |
| <div class="card-header"><h3>Evolução das receitas</h3></div> | |
| <div class="card-body"> | |
| <div class="chart-container"><canvas id="incEvolutionChart"></canvas></div> | |
| </div> | |
| </div> | |
| <!-- Transaction List --> | |
| <div class="card"> | |
| <div class="card-header"><h3>Últimas receitas</h3></div> | |
| <div class="card-body"> | |
| <div class="search-bar"> | |
| <input type="text" class="search-input" placeholder="Buscar..."> | |
| <div class="chip-group"> | |
| <button class="chip">Recorrentes</button> | |
| </div> | |
| </div> | |
| <ul class="tx-list" id="incList"></ul> | |
| </div> | |
| <button class="view-all-btn" data-modal="incModal">Ver todas as receitas</button> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- INFO MODAL --> | |
| <div class="modal-overlay" id="infoModal"> | |
| <div class="modal"> | |
| <div class="modal-header"> | |
| <h4 id="infoTitle">Info</h4> | |
| <button class="modal-close"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button> | |
| </div> | |
| <div class="modal-body"><p id="infoText"></p></div> | |
| </div> | |
| </div> | |
| <!-- FULL LIST MODAL - EXPENSES --> | |
| <div class="modal-overlay" id="expModal"> | |
| <div class="modal" style="max-width:700px;"> | |
| <div class="modal-header"> | |
| <h4>Todas as despesas</h4> | |
| <button class="modal-close"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="table-scroll"> | |
| <table class="full-table"> | |
| <thead><tr><th>Data</th><th>Descrição</th><th>Categoria</th><th>Banco</th><th>Método</th><th>Valor</th></tr></thead> | |
| <tbody id="expFullTable"></tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- FULL LIST MODAL - INCOME --> | |
| <div class="modal-overlay" id="incModal"> | |
| <div class="modal" style="max-width:600px;"> | |
| <div class="modal-header"> | |
| <h4>Todas as receitas</h4> | |
| <button class="modal-close"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="table-scroll"> | |
| <table class="full-table"> | |
| <thead><tr><th>Data</th><th>Descrição</th><th>Categoria</th><th>Valor</th></tr></thead> | |
| <tbody id="incFullTable"></tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // ===== DATA ===== | |
| const infoContent = { | |
| despesas: { title: 'Despesas do mês', text: 'Soma de todos os gastos registrados no mês atual: compras, contas, transferências de saída e qualquer valor que saiu das suas contas.' }, | |
| receitas: { title: 'Receitas do mês', text: 'Soma de todas as entradas registradas no mês atual: salários, vendas, transferências recebidas e qualquer valor que entrou nas suas contas.' }, | |
| resultado: { title: 'Resultado do mês', text: 'Receitas menos despesas. Mostra se você está gastando mais do que ganha (negativo) ou economizando (positivo). Atenção: não é o mesmo que o dinheiro que você tem na conta.' }, | |
| saldo: { title: 'Dinheiro na conta', text: 'Soma dos saldos de todas as suas contas bancárias conectadas. Esse valor pode incluir dinheiro de meses anteriores — é diferente do resultado do mês.' }, | |
| projecao: { title: 'Projeção', text: 'Estimativa de entradas e saídas até o fim do mês, baseada em: despesas recorrentes, parcelas de cartão, e compromissos já conhecidos.' } | |
| }; | |
| const expCategories = { | |
| labels: ['Educação', 'Casa', 'Transporte', 'Saúde', 'Mercado', 'Lazer'], | |
| values: [1850, 1420, 1180, 980, 1120, 734.50], | |
| colors: ['#1DE199', '#17B87A', '#0B0B0B', '#52525B', '#A1A1AA', '#D4D4D8'] | |
| }; | |
| const incCategories = { | |
| labels: ['Salário', 'Freelance', 'Investimentos'], | |
| values: [8500, 2200, 500], | |
| colors: ['#1DE199', '#17B87A', '#0B0B0B'] | |
| }; | |
| const expByMethod = [ | |
| { label: 'Crédito', value: 3840 }, | |
| { label: 'PIX', value: 2120 }, | |
| { label: 'Débito', value: 890 }, | |
| { label: 'Boleto', value: 434.50 } | |
| ]; | |
| const expByBank = [ | |
| { label: 'Inter', value: 3200 }, | |
| { label: 'Nubank', value: 2840 }, | |
| { label: 'Bradesco', value: 1244.50 } | |
| ]; | |
| const expByAccount = [ | |
| { label: 'PF', value: 5420 }, | |
| { label: 'PJ', value: 1864.50 } | |
| ]; | |
| const expenses = [ | |
| { date: '14/12', desc: 'Compras mercado', cat: 'Mercado', bank: 'Nubank', method: 'Crédito', value: 487.32, badge: null, account: 'PF' }, | |
| { date: '12/12', desc: 'Mensalidade escola', cat: 'Educação', bank: 'Inter', method: 'Débito', value: 1850, badge: 'rec', account: 'PF' }, | |
| { date: '10/12', desc: 'Porta banheiro — 2/3', cat: 'Casa', bank: 'Nubank', method: 'Crédito', value: 450, badge: 'parc', account: 'PF' }, | |
| { date: '08/12', desc: 'Combustível', cat: 'Transporte', bank: 'Inter', method: 'PIX', value: 320, badge: null, account: 'PJ' }, | |
| { date: '05/12', desc: 'Netflix + Spotify', cat: 'Lazer', bank: 'Nubank', method: 'Crédito', value: 89.80, badge: 'rec', account: 'PF' }, | |
| { date: '03/12', desc: 'Farmácia', cat: 'Saúde', bank: 'Bradesco', method: 'Débito', value: 156.40, badge: null, account: 'PF' }, | |
| { date: '01/12', desc: 'Contabilidade', cat: 'Casa', bank: 'Inter', method: 'Boleto', value: 434.50, badge: 'rec', account: 'PJ' } | |
| ]; | |
| const incomes = [ | |
| { date: '05/12', desc: 'Salário — Empresa XYZ', cat: 'Salário', value: 8500, badge: 'rec', account: 'PF' }, | |
| { date: '10/12', desc: 'Projeto consultoria', cat: 'Freelance', value: 2200, badge: null, account: 'PJ' }, | |
| { date: '01/12', desc: 'Dividendos FIIs', cat: 'Investimentos', value: 500, badge: 'rec', account: 'PF' } | |
| ]; | |
| const dailyExp = [420, 0, 156.40, 0, 89.80, 0, 0, 320, 0, 450, 0, 1850, 0, 487.32]; | |
| const dailyInc = [500, 0, 0, 0, 8500, 0, 0, 0, 0, 2200, 0, 0, 0, 0]; | |
| const dayLabels = ['01','02','03','04','05','06','07','08','09','10','11','12','13','14']; | |
| // ===== UTILS ===== | |
| const fmt = v => v.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }); | |
| // ===== TABS ===== | |
| document.querySelectorAll('.tab-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); | |
| document.getElementById('tab-' + btn.dataset.tab).classList.add('active'); | |
| }); | |
| }); | |
| // ===== FILTER DROPDOWNS ===== | |
| document.querySelectorAll('.filter-dropdown').forEach(dd => { | |
| const trigger = dd.querySelector('.filter-chip'); | |
| trigger.addEventListener('click', e => { | |
| e.stopPropagation(); | |
| document.querySelectorAll('.filter-dropdown').forEach(d => { if (d !== dd) d.classList.remove('open'); }); | |
| dd.classList.toggle('open'); | |
| }); | |
| dd.querySelectorAll('.filter-option').forEach(opt => { | |
| opt.addEventListener('click', () => { | |
| dd.querySelectorAll('.filter-option').forEach(o => o.classList.remove('selected')); | |
| opt.classList.add('selected'); | |
| trigger.querySelector('span').textContent = opt.textContent; | |
| dd.classList.remove('open'); | |
| }); | |
| }); | |
| }); | |
| document.addEventListener('click', () => { | |
| document.querySelectorAll('.filter-dropdown').forEach(d => d.classList.remove('open')); | |
| }); | |
| // ===== INFO MODAL ===== | |
| const infoModal = document.getElementById('infoModal'); | |
| document.querySelectorAll('.info-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| const key = btn.dataset.info; | |
| const data = infoContent[key] || { title: 'Info', text: '' }; | |
| document.getElementById('infoTitle').textContent = data.title; | |
| document.getElementById('infoText').textContent = data.text; | |
| infoModal.classList.add('visible'); | |
| }); | |
| }); | |
| // ===== FULL LIST MODALS ===== | |
| document.querySelectorAll('.view-all-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| document.getElementById(btn.dataset.modal).classList.add('visible'); | |
| }); | |
| }); | |
| document.querySelectorAll('.modal-close').forEach(btn => { | |
| btn.addEventListener('click', () => btn.closest('.modal-overlay').classList.remove('visible')); | |
| }); | |
| document.querySelectorAll('.modal-overlay').forEach(ov => { | |
| ov.addEventListener('click', e => { if (e.target === ov) ov.classList.remove('visible'); }); | |
| }); | |
| // ===== CHIP TOGGLE ===== | |
| document.querySelectorAll('.chip').forEach(c => c.addEventListener('click', () => c.classList.toggle('active'))); | |
| // ===== RENDER LISTS ===== | |
| function renderTxList(id, items, type) { | |
| const ul = document.getElementById(id); | |
| ul.innerHTML = items.slice(0, 5).map(i => ` | |
| <li class="tx-item"> | |
| <div class="tx-info"> | |
| <div class="tx-main"> | |
| <span class="tx-title">${i.desc}</span> | |
| ${i.badge === 'rec' ? '<span class="tx-badge rec">Rec</span>' : ''} | |
| ${i.badge === 'parc' ? '<span class="tx-badge parc">Parc</span>' : ''} | |
| </div> | |
| <div class="tx-meta"> | |
| <span>${i.date}</span> | |
| <span>${i.cat}</span> | |
| ${i.bank ? `<span>${i.bank}</span>` : ''} | |
| ${i.method ? `<span>${i.method}</span>` : ''} | |
| <span class="tx-badge ${i.account.toLowerCase()}">${i.account}</span> | |
| </div> | |
| </div> | |
| <span class="tx-value ${type}">${type === 'expense' ? '−' : '+'}${fmt(i.value)}</span> | |
| </li> | |
| `).join(''); | |
| } | |
| renderTxList('expList', expenses, 'expense'); | |
| renderTxList('incList', incomes, 'income'); | |
| // ===== RENDER FULL TABLES ===== | |
| function renderFullTable(id, items, type) { | |
| const tb = document.getElementById(id); | |
| tb.innerHTML = items.map(i => ` | |
| <tr> | |
| <td>${i.date}</td> | |
| <td>${i.desc}${i.badge === 'rec' ? ' <span class="tx-badge rec">Rec</span>' : ''}${i.badge === 'parc' ? ' <span class="tx-badge parc">Parc</span>' : ''}</td> | |
| <td>${i.cat}</td> | |
| ${i.bank !== undefined ? `<td>${i.bank}</td>` : ''} | |
| ${i.method !== undefined ? `<td>${i.method}</td>` : ''} | |
| <td class="val ${type}">${type === 'expense' ? '−' : '+'}${fmt(i.value)}</td> | |
| </tr> | |
| `).join(''); | |
| } | |
| renderFullTable('expFullTable', expenses, 'expense'); | |
| renderFullTable('incFullTable', incomes, 'income'); | |
| // ===== RENDER CATEGORY TABLES ===== | |
| function renderCatTable(id, data) { | |
| const tb = document.getElementById(id); | |
| const total = data.values.reduce((a,b) => a + b, 0); | |
| tb.innerHTML = data.labels.map((l, i) => { | |
| const pct = ((data.values[i] / total) * 100).toFixed(1); | |
| return `<tr><td><span class="cat-dot" style="background:${data.colors[i]}"></span>${l}</td><td>${fmt(data.values[i])}</td><td class="cat-percent">${pct}%</td></tr>`; | |
| }).join(''); | |
| } | |
| renderCatTable('expCatTable', expCategories); | |
| renderCatTable('incCatTable', incCategories); | |
| // ===== RENDER BREAKDOWN LISTS ===== | |
| function renderBreakdown(id, items) { | |
| const ul = document.getElementById(id); | |
| ul.innerHTML = items.map(i => ` | |
| <li class="breakdown-item"> | |
| <span class="breakdown-item-label">${i.label}</span> | |
| <span class="breakdown-item-value">${fmt(i.value)}</span> | |
| </li> | |
| `).join(''); | |
| } | |
| renderBreakdown('expMethodList', expByMethod); | |
| renderBreakdown('expBankList', expByBank); | |
| renderBreakdown('expAccountList', expByAccount); | |
| // ===== CHARTS ===== | |
| Chart.defaults.font.family = 'Inter, sans-serif'; | |
| Chart.defaults.color = '#71717A'; | |
| // Summary Bar | |
| new Chart(document.getElementById('summaryBarChart'), { | |
| type: 'bar', | |
| data: { | |
| labels: ['Dezembro'], | |
| datasets: [ | |
| { label: 'Despesas', data: [7284.50], backgroundColor: '#EF4444', borderRadius: 4, barPercentage: 0.5 }, | |
| { label: 'Receitas', data: [11200], backgroundColor: '#1DE199', borderRadius: 4, barPercentage: 0.5 } | |
| ] | |
| }, | |
| options: { | |
| indexAxis: 'y', | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| scales: { x: { grid: { color: '#E4E4E7' }, ticks: { callback: v => 'R$ ' + (v/1000).toFixed(0) + 'k' } }, y: { display: false } }, | |
| plugins: { legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: 'circle', padding: 16 } }, tooltip: { backgroundColor: '#0B0B0B', padding: 10, cornerRadius: 6, callbacks: { label: ctx => ctx.dataset.label + ': ' + fmt(ctx.parsed.x) } } } | |
| } | |
| }); | |
| // Summary Line | |
| new Chart(document.getElementById('summaryLineChart'), { | |
| type: 'line', | |
| data: { | |
| labels: dayLabels, | |
| datasets: [ | |
| { label: 'Despesas', data: dailyExp, borderColor: '#EF4444', backgroundColor: 'rgba(239,68,68,0.08)', fill: true, tension: 0.3, pointRadius: 2 }, | |
| { label: 'Receitas', data: dailyInc, borderColor: '#1DE199', backgroundColor: 'rgba(29,225,153,0.08)', fill: true, tension: 0.3, pointRadius: 2 } | |
| ] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| interaction: { intersect: false, mode: 'index' }, | |
| scales: { x: { grid: { display: false } }, y: { grid: { color: '#E4E4E7' }, ticks: { callback: v => 'R$ ' + (v/1000).toFixed(0) + 'k' } } }, | |
| plugins: { legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: 'circle', padding: 16 } }, tooltip: { backgroundColor: '#0B0B0B', padding: 10, cornerRadius: 6, callbacks: { label: ctx => ctx.dataset.label + ': ' + fmt(ctx.parsed.y) } } } | |
| } | |
| }); | |
| // Expense Category Doughnut | |
| new Chart(document.getElementById('expCatChart'), { | |
| type: 'doughnut', | |
| data: { labels: expCategories.labels, datasets: [{ data: expCategories.values, backgroundColor: expCategories.colors, borderWidth: 0 }] }, | |
| options: { responsive: true, maintainAspectRatio: false, cutout: '55%', plugins: { legend: { display: false }, tooltip: { backgroundColor: '#0B0B0B', padding: 8, cornerRadius: 4, callbacks: { label: ctx => fmt(ctx.parsed) } } } } | |
| }); | |
| // Expense Evolution | |
| new Chart(document.getElementById('expEvolutionChart'), { | |
| type: 'bar', | |
| data: { labels: dayLabels, datasets: [{ label: 'Despesas', data: dailyExp, backgroundColor: '#EF4444', borderRadius: 3 }] }, | |
| options: { responsive: true, maintainAspectRatio: false, scales: { x: { grid: { display: false } }, y: { grid: { color: '#E4E4E7' }, ticks: { callback: v => 'R$ ' + v } } }, plugins: { legend: { display: false }, tooltip: { backgroundColor: '#0B0B0B', padding: 8, cornerRadius: 4, callbacks: { label: ctx => fmt(ctx.parsed.y) } } } } | |
| }); | |
| // Income Category Doughnut | |
| new Chart(document.getElementById('incCatChart'), { | |
| type: 'doughnut', | |
| data: { labels: incCategories.labels, datasets: [{ data: incCategories.values, backgroundColor: incCategories.colors, borderWidth: 0 }] }, | |
| options: { responsive: true, maintainAspectRatio: false, cutout: '55%', plugins: { legend: { display: false }, tooltip: { backgroundColor: '#0B0B0B', padding: 8, cornerRadius: 4, callbacks: { label: ctx => fmt(ctx.parsed) } } } } | |
| }); | |
| // Income Evolution | |
| new Chart(document.getElementById('incEvolutionChart'), { | |
| type: 'bar', | |
| data: { labels: dayLabels, datasets: [{ label: 'Receitas', data: dailyInc, backgroundColor: '#1DE199', borderRadius: 3 }] }, | |
| options: { responsive: true, maintainAspectRatio: false, scales: { x: { grid: { display: false } }, y: { grid: { color: '#E4E4E7' }, ticks: { callback: v => 'R$ ' + (v/1000).toFixed(0) + 'k' } } }, plugins: { legend: { display: false }, tooltip: { backgroundColor: '#0B0B0B', padding: 8, cornerRadius: 4, callbacks: { label: ctx => fmt(ctx.parsed.y) } } } } | |
| }); | |
| </script> | |
| </body> | |
| </html> |