diff --git a/expenses_manager/expenses/static/expenses/css/base.css b/expenses_manager/expenses/static/expenses/css/base.css index 56d6ee6..c8a4e35 100644 --- a/expenses_manager/expenses/static/expenses/css/base.css +++ b/expenses_manager/expenses/static/expenses/css/base.css @@ -1,91 +1,270 @@ +:root { + --color-bg: #f6f8f6; + --color-surface: #ffffff; + --color-surface-alt: #f2f5f3; + --color-text: #1c1917; + --color-text-muted: #6b6560; + --color-border: #d8d4d0; + + --color-nav-bg: #10241d; + --color-nav-text: #e7f5f0; + --color-nav-text-muted: #8fb8ac; + + --color-primary: #0f766e; + --color-primary-hover: #0b5c56; + --color-secondary: #57534e; + --color-secondary-hover: #44403c; + --color-on-primary: #ffffff; + + --color-chip-bg: #e3ece8; + --color-chip-hover: #d2e0da; + --color-focus-ring: #5eead4; + + --color-success-bg: #e6f4ea; + --color-success-text: #166534; + --color-success-accent: #1e7e34; + + --color-danger-bg: #fee2e2; + --color-danger-text: #991b1b; + --color-danger-accent: #b91c1c; + --color-danger-accent-hover: #991b1b; + + --color-warning-bg: #fef3c7; + --color-warning-text: #92400e; + --color-warning-accent: #d97706; + + --color-table-head: #edf2ef; + --color-table-alt: #f8faf9; + --color-table-hover: #e9f1ed; + --color-progress-track: #e0e0e0; +} + +[data-theme="dark"] { + --color-bg: #0d1512; + --color-surface: #16211c; + --color-surface-alt: #1b2a23; + --color-text: #e7f0eb; + --color-text-muted: #9fb0a8; + --color-border: #2a3a33; + + --color-nav-bg: #0a1712; + --color-nav-text: #e7f5f0; + --color-nav-text-muted: #8fb8ac; + + --color-chip-bg: #22332c; + --color-chip-hover: #2b3f36; + --color-focus-ring: #5eead4; + + --color-success-bg: #113322; + --color-success-text: #86efac; + + --color-danger-bg: #3f1d1d; + --color-danger-text: #fca5a5; + + --color-warning-bg: #3f2d0d; + --color-warning-text: #fcd34d; + + --color-table-head: #1b2a23; + --color-table-alt: #182420; + --color-table-hover: #20302a; + --color-progress-track: #2a3a33; +} + +* { + box-sizing: border-box; +} + body { margin: 0; font-family: system-ui, sans-serif; - background: #f7f7f7; + background: var(--color-bg); + color: var(--color-text); } -a.danger { - color: #b91c1c; +a { + color: var(--color-primary); } +a.danger, +a.danger:visited, +.danger { + color: var(--color-danger-accent); +} + +/* ========================= */ +/* Topbar / navigation */ +/* ========================= */ + .topbar { - background: #1f2937; - padding: 12px 20px; + background: var(--color-nav-bg); } .nav { display: flex; align-items: center; - gap: 16px; + gap: 0.5rem; + padding: 0.75rem 1rem; + flex-wrap: wrap; } -.nav a { - color: #e5e7eb; +.nav-item, +.nav .nav-link { + color: var(--color-nav-text); text-decoration: none; font-weight: 500; + padding: 0.4rem 0.1rem; } -.nav .nav-link { - text-decoration: none; - background-color: none; +.logout-form { + display: inline; + margin: 0; + padding: 0; +} + +.logout-form .nav-link { + background: none; border: none; - padding: 8px 12px; font: inherit; - color: #333; cursor: pointer; + color: var(--color-nav-text); + padding: 0.4rem 0.1rem; } -.nav a:hover, +.nav-item:hover, .nav .nav-link:hover { text-decoration: underline; } +.nav-item.active { + font-weight: 700; + border-bottom: 2px solid var(--color-nav-text); +} + +.spacer { + flex: 1 1 auto; +} + +.user { + color: var(--color-nav-text-muted); + margin-right: 0.5rem; +} + .nav-toggle { display: none; + background: none; + border: none; flex-direction: column; justify-content: center; gap: 4px; - background: none; - border: none; - padding: 8px; + padding: 0.5rem; cursor: pointer; } .nav-toggle-bar { display: block; - width: 24px; - height: 3px; - background: #e5e7eb; + width: 22px; + height: 2px; + background: var(--color-nav-text); border-radius: 2px; } -.nav-item.active { - font-weight: bold; - border-bottom: 2px solid white; -} - -.logout-form { - margin: 0; +.theme-toggle { + background: none; border: none; cursor: pointer; - color: white; + font-size: 1.1rem; + line-height: 1; + color: var(--color-nav-text); + padding: 0.4rem; } -.spacer { - flex: 1; +.dropdown { + position: relative; } -.user { - color: #9ca3af; - margin-right: 8px; +.dropdown-toggle { + background: none; + border: none; + display: inline-block; + font: inherit; + color: var(--color-nav-text); + cursor: pointer; + padding: 0.4rem 0.1rem; } +.dropdown-toggle:focus-visible { + outline: 2px solid var(--color-focus-ring); + outline-offset: 2px; +} + +.dropdown-menu { + display: none; + position: absolute; + top: 100%; + left: 0; + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: 6px; + min-width: 150px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + z-index: 10; +} + +.dropdown-menu a { + display: block; + padding: 10px; + text-decoration: none; + color: var(--color-text); +} + +.dropdown-menu a:hover { + background-color: var(--color-surface-alt); +} + +.dropdown.dropdown--open .dropdown-menu { + display: block; +} + +/* ========================= */ +/* Messages */ +/* ========================= */ + +.messages { + padding: 0 1rem; +} + +.message { + padding: 0.75rem 1rem; + border-radius: 6px; + margin-bottom: 0.75rem; + font-size: 0.9rem; +} + +.message.success { + background: var(--color-success-bg); + color: var(--color-success-text); +} + +.message.error { + background: var(--color-danger-bg); + color: var(--color-danger-text); +} + +.message.warning { + background: var(--color-warning-bg); + color: var(--color-warning-text); +} + +/* ========================= */ +/* Layout */ +/* ========================= */ + .content { - padding: 24px; + padding: 1.5rem; } .dashboard-context { - margin-bottom: 1rem; + margin-bottom: 1rem; } .dashboard-context h2 { @@ -93,18 +272,192 @@ a.danger { } .muted { - color: #6b7280; + color: var(--color-text-muted); font-size: 0.85rem; } +/* ========================= */ +/* Buttons */ +/* ========================= */ + +.btn, +button[type="submit"] { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.35rem; + height: 2.25rem; + box-sizing: border-box; + padding: 0 0.9rem; + border: none; + border-radius: 6px; + font: inherit; + font-size: 0.9rem; + font-weight: 500; + text-decoration: none; + cursor: pointer; + line-height: 1; + background: var(--color-primary); + color: var(--color-on-primary); +} + +button[type="submit"]:hover, +.btn-primary:hover { + background: var(--color-primary-hover); +} + +.btn-primary { + background: var(--color-primary); + color: var(--color-on-primary); +} + +.btn-secondary { + background: var(--color-secondary); + color: var(--color-on-primary); +} + +.btn-secondary:hover { + background: var(--color-secondary-hover); +} + +.btn-danger { + background: var(--color-danger-accent); + color: var(--color-on-primary); +} + +.btn-danger:hover { + background: var(--color-danger-accent-hover); +} + +.form-actions { + display: flex; + align-items: center; + gap: 0.75rem; + margin-top: 1rem; + flex-wrap: wrap; +} + +/* ========================= */ +/* Filters (expense list) */ +/* ========================= */ + +.filters { + margin-bottom: 1.5rem; +} + +.filters-main { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; +} + +.filters-advanced { + margin-top: 0.5rem; +} + +.advanced-content { + display: flex; + flex-wrap: wrap; + gap: 1rem; + align-items: center; + margin-top: 0.5rem; +} + +.tags-filters { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.active-tags { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; +} + +.tag { + display: inline-block; + padding: 4px 10px; + border-radius: 16px; + background: var(--color-chip-bg); + text-decoration: none; + margin-right: 4px; + font-size: 0.85em; + color: var(--color-text); +} + +.tag.active { + background: var(--color-primary); + color: var(--color-on-primary); +} + +.tag:hover { + background: var(--color-chip-hover); +} + +/* ========================= */ +/* Dashboard filters/presets */ +/* ========================= */ + +.dashboard-filters { + padding: 1rem; + margin-bottom: 1.5rem; + background: var(--color-surface-alt); + border-radius: 8px; +} + +.dashboard-presets { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; +} + +.preset { + padding: 4px 12px; + border-radius: 16px; + background: var(--color-chip-bg); + text-decoration: none; + color: var(--color-text); + font-size: 0.9em; +} + +.preset:hover { + background: var(--color-chip-hover); +} + +.preset.active { + background: var(--color-primary); + color: var(--color-on-primary); +} + +.clear-filters-row { + margin-top: 0.5rem; +} + +.clear-filters { + color: var(--color-danger-accent); + text-decoration: none; + font-size: 0.9em; +} + +.clear-filters:hover { + text-decoration: underline; +} + +/* ========================= */ +/* Cards / charts / dashboard grid */ +/* ========================= */ + .dashboard-grid { display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 20px + grid-template-columns: repeat(2, 1fr); + gap: 20px; } .card { - background: #fff; + background: var(--color-surface); border-radius: 8px; padding: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); @@ -146,92 +499,99 @@ a.danger { .canvas-wrapper { flex-grow: 1; min-height: 0; - position: relative; width: 100%; + position: relative; } -.card-chart canvas { - display: block; +.canvas-wrapper canvas { width: 100% !important; height: 100% !important; } +/* ========================= */ +/* KPIs */ +/* ========================= */ + .kpi-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; - margin: 2rem 0; + margin: 1.5rem 0; } .kpi-card { - background: #ffffff; + background: var(--color-surface); border-radius: 8px; padding: 1rem; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .kpi-label { + display: block; font-size: 0.85rem; - color: #666; + color: var(--color-text-muted); } .kpi-value { display: block; - font-size: 1.5rem; + font-size: 1.4rem; font-weight: bold; margin-top: 0.25rem; } .comparison { - background: #f9f9f9; + background: var(--color-surface-alt); padding: 1rem; margin-bottom: 2rem; border-radius: 8px; } -/* .comparison strong { - color:#b91c1c; -} */ - -.comparison strong.positive { - color: #166534; +strong.positive { + color: var(--color-success-text); } -.comparison strong.negative { - color:#b91c1c; +strong.negative { + color: var(--color-danger-accent); } +/* ========================= */ +/* Tables */ +/* ========================= */ + table { width: 100%; border-collapse: collapse; - margin-top: 1rem; + margin-top: 0.5rem; } .table-wrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; + margin-bottom: 1.5rem; } .table-wrap table { white-space: nowrap; } -th, td { - padding: 0.6rem 0.75rem; +th, +td { + padding: 8px 12px; text-align: left; + border-bottom: 1px solid var(--color-border); } -thead { - background: #f4f6f8; +thead th { + background: var(--color-table-head); } -tbody tr:nth-child(even) { - background: #fafafa; +tr:nth-child(even) { + background: var(--color-table-alt); } -tbody tr:hover { - background: #f0f4f8; +tr:hover { + background: var(--color-table-hover); } .table-actions a { @@ -243,7 +603,7 @@ tbody tr:hover { .empty-state { text-align: center; padding: 2rem; - color: #666; + color: var(--color-text-muted); } .empty-state a { @@ -251,303 +611,49 @@ tbody tr:hover { margin-top: 0.5rem; } -.btn { - background: #2563eb; - color: white; - padding: 0.4rem 0.7rem; - border-radius: 6px; - text-decoration: none; - font-size: 0.9rem; +.pagination { + margin-top: 1rem; } -.btn.secondary { - background: #6b7280; -} - -.messages { - margin: 1rem 0; -} - -.message { - padding: 0.6rem 0.8rem; - border-radius: 6px; - margin-bottom: 0.5rem; - font-size: 0.9rem; -} - -.message.success { - background: #e6f4ea; - color: #166534; -} - -.message.error { - background: #fee2e2; - color: #991b1b; -} - -.message.warning { - background: #fef3c7; - color: #92400e; -} - -.dashboard-filters { - margin: 1rem 0 1.5rem; - padding: 1rem; - background: #f9fafb; - border-radius: 8px; -} - -.dashboard-filters form { +.step-links { display: flex; - flex-wrap: wrap; - gap: 0.75rem; - align-items: center; -} - -.dashboard-filters select, -.dashboard-filters button { - padding: 0.4rem 0.6rem; -} - -.dashboard-filters .checkbox { - display: flex; - gap: 0.3rem; - align-items: center; -} - -.dashboard-presets { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; -} - -.clear-filters-row { - margin-top: 0.5rem; -} - -.clear-filters { - color: #b91c1c; - text-decoration: none; - font-size: 0.85rem; -} - -.clear-filters:hover { - text-decoration: underline; -} - -.preset { - padding: 0.35rem 0.6rem; - border-radius: 6px; - background: #e5e7eb; - text-decoration: none; - color: #111827; - font-size: 0.85rem; -} - -.preset:hover { - background: #d1d5db; -} - -.preset.active { - background: #2563eb; - color: white; -} - -.filters { - margin-bottom: 1.5rem; -} - -.filters-main { - display: flex; - flex-wrap: wrap; gap: 0.5rem; align-items: center; } -.filters-advanced { - margin-top: 0.5rem; -} - -.active-tags { - display: flex; - flex-wrap: wrap; - gap: 0.4rem; - margin-top: 0.3rem; -} - -.tag{ - /* display: inline-block; */ - padding: 0.2rem 0.5rem; - border-radius: 4px; - background: #eee; - text-decoration: none; - margin-right: 4px; - font-size: 0.85rem; - color: #333; -} - -.tag.active { - background: #333; - color: white; -} - -.tag:hover { - background: #dbe3ee; -} - -.dropdown { - position: relative; - cursor: pointer; - color: #e5e7eb; -} - -.dropdown.active { - font-weight: bold; - border-bottom: 2px solid white; -} - -.dropdown-toggle { - padding: 10px; +.badge { display: inline-block; - background: none; - border: none; - font: inherit; - color: inherit; - cursor: pointer; + padding: 2px 10px; + border-radius: 12px; + font-size: 0.8rem; + font-weight: 600; } -.dropdown-toggle:focus-visible { - outline: 2px solid #93c5fd; - outline-offset: 2px; +.badge-active { + background-color: var(--color-success-bg); + color: var(--color-success-text); } -.dropdown-menu { - display: none; - position: absolute; - top: 100%; - left: 0; - background: white; - border: 1px solid #ddd; - border-radius: 6px; - min-width: 150px; - box-shadow: 0 4px 8px rgba(0,0,0,0.1); -} - -.dropdown-menu a { - display: block; - padding: 10px; - text-decoration: none; - color: #333; -} - -.dropdown-menu a:hover { - background-color: #f5f5f5; -} - -.dropdown.dropdown--open .dropdown-menu { - display: block; -} - -.progress-container { - display: flex; - align-items: center; - gap: 10px; -} - -.progress-label { - min-width: 50px; - font-size: 12px; - color: #666; -} - -.progress-bar { - width: 100%; - background: #e0e0e0; - border-radius: 6px; - height: 14px; - overflow: hidden; -} - -.progress-fill { - height: 100%; - transition: width 0.6s ease-in-out; -} - -.progress-fill.low { - background: #e74c3c; -} - -.progress-fill.medium { - background: #f1c40f; -} - -.progress-fill.high { - background: #2ecc71; -} - -.goals-widget { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 12px; -} - -.goal-card { - padding: 10px; - border-radius: 8px; - background: #f9f9f9; +.badge-inactive { + background-color: var(--color-danger-bg); + color: var(--color-danger-text); } .row-inactive { opacity: 0.55; } -.badge { - display: inline-block; - padding: 2px 8px; - border-radius: 12px; - font-size: 0.8em; - font-weight: 600; +/* ========================= */ +/* Forms */ +/* ========================= */ + +.form-field { + margin-bottom: 1rem; } -.badge-active { - background-color: #e6f4ea; - color: #1e7e34; -} - -.badge-inactive { - background-color: #fbe9e7; - color: #b71c1c; -} - -.settings-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 16px; - margin-top: 20px; -} - -.settings-card { - display: block; - padding: 16px; - border: 1px solid #ddd; - border-radius: 8px; - text-decoration: none; - color: inherit; - transition: box-shadow 0.15s ease; -} - -.settings-card:hover { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -} - -.settings-card h3 { - margin: 0 0 4px 0; -} - -.settings-card p { - margin: 0; - font-size: 0.9em; - color: #666; +.form-errors { + color: var(--color-danger-accent); + font-size: 0.85rem; } .tag-chip-list { @@ -558,7 +664,7 @@ tbody tr:hover { max-height: 160px; overflow-y: auto; padding: 8px; - border: 1px solid #ddd; + border: 1px solid var(--color-border); border-radius: 8px; } @@ -567,49 +673,148 @@ tbody tr:hover { align-items: center; gap: 4px; padding: 4px 10px; - border: 1px solid #ccc; + border: 1px solid var(--color-border); border-radius: 16px; font-size: 0.85em; cursor: pointer; user-select: none; - background-color: #fafafa; + background-color: var(--color-surface-alt); } .tag-chip input[type="checkbox"] { - accent-color: #1e7e34; + accent-color: var(--color-success-accent); } .tag-chip:has(input:checked) { - background-color: #e6f4ea; - border-color: #1e7e34; - color: #1e7e34; + background-color: var(--color-success-bg); + border-color: var(--color-success-accent); + color: var(--color-success-text); } -.progress-fill.ok { - background-color: #1e88e5; +/* ========================= */ +/* Progress bars */ +/* ========================= */ + +.progress-container { + display: flex; + align-items: center; + gap: 10px; } -.progress-fill.complete { - background-color: #1e7e34; + +.progress-label { + min-width: 90px; + font-size: 0.85rem; + color: var(--color-text-muted); } -.progress-fill.warning { - background-color: #f9a825; + +.progress-bar { + width: 100%; + background: var(--color-progress-track); + border-radius: 6px; + height: 10px; + overflow: hidden; } -.progress-fill.danger { - background-color: #b71c1c; + +.progress-fill { + height: 100%; + transition: width 0.3s ease-in-out; +} + +.progress-fill.low, +.progress-fill.danger { + background-color: var(--color-danger-accent); +} + +.progress-fill.medium, +.progress-fill.warning { + background-color: var(--color-warning-accent); +} + +.progress-fill.high, +.progress-fill.complete { + background-color: var(--color-success-accent); +} + +.progress-fill.ok { + background-color: var(--color-primary); +} + +/* ========================= */ +/* Goals */ +/* ========================= */ + +.goals-widget { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1rem; +} + +.goal-card { + padding: 1rem; + border-radius: 8px; + background: var(--color-surface-alt); +} + +/* ========================= */ +/* Settings */ +/* ========================= */ + +.settings-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + margin-top: 1rem; +} + +.settings-card { + display: block; + padding: 1rem; + border: 1px solid var(--color-border); + border-radius: 8px; + text-decoration: none; + color: var(--color-text); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); +} + +.settings-card:hover { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.settings-card h3 { + margin: 0 0 0.25rem 0; +} + +.settings-card p { + margin: 0; + font-size: 0.9em; + color: var(--color-text-muted); +} + +/* ========================= */ +/* Auth pages */ +/* ========================= */ + +.auth-container { + max-width: 400px; + margin: 4rem auto; + padding: 2rem; + background: var(--color-surface); + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); } .auth-help { - margin-top: 16px; - font-size: 0.9em; + margin-top: 1rem; + font-size: 0.9rem; + color: var(--color-text-muted); } -@media (max-width: 768px) { - .content { - padding: 16px; - } +/* ========================= */ +/* Responsive */ +/* ========================= */ - .topbar { - display: flex; +@media (max-width: 768px) { + .topbar .nav { align-items: center; justify-content: space-between; flex-wrap: wrap; @@ -622,10 +827,9 @@ tbody tr:hover { .nav { display: none; flex-direction: column; - align-items: stretch; - gap: 8px; + align-items: flex-start; width: 100%; - margin-top: 12px; + margin-top: 0.5rem; } .nav.nav--open { @@ -637,17 +841,14 @@ tbody tr:hover { } .dropdown-menu { - display: none; position: static; - border: none; box-shadow: none; - background: transparent; min-width: 0; - padding-left: 12px; + padding-left: 1rem; } .dropdown-menu a { - color: #e5e7eb; + color: var(--color-nav-text); } .dropdown-menu a:hover { @@ -665,14 +866,6 @@ tbody tr:hover { } @media (max-width: 480px) { - .content { - padding: 12px; - } - - .topbar { - padding: 10px 12px; - } - .kpi-grid, .goals-widget, .settings-grid { @@ -686,4 +879,4 @@ tbody tr:hover { .card-chart { height: 260px; } -} \ No newline at end of file +} diff --git a/expenses_manager/expenses/static/expenses/js/theme.js b/expenses_manager/expenses/static/expenses/js/theme.js new file mode 100644 index 0000000..2deaa43 --- /dev/null +++ b/expenses_manager/expenses/static/expenses/js/theme.js @@ -0,0 +1,20 @@ +document.addEventListener('DOMContentLoaded', function () { + var toggle = document.getElementById('themeToggle'); + + if (!toggle) { + return; + } + + function applyIcon(theme) { + toggle.textContent = theme === 'dark' ? '☀️' : '🌙'; + } + + applyIcon(document.documentElement.getAttribute('data-theme')); + + toggle.addEventListener('click', function () { + var next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; + document.documentElement.setAttribute('data-theme', next); + localStorage.setItem('theme', next); + applyIcon(next); + }); +}); diff --git a/expenses_manager/expenses/templates/categories/confirm_delete.html b/expenses_manager/expenses/templates/categories/confirm_delete.html index 87b4140..e871993 100644 --- a/expenses_manager/expenses/templates/categories/confirm_delete.html +++ b/expenses_manager/expenses/templates/categories/confirm_delete.html @@ -32,7 +32,7 @@
{% endblock %} \ No newline at end of file diff --git a/expenses_manager/expenses/templates/categories/form.html b/expenses_manager/expenses/templates/categories/form.html index e45d9c1..e6b67d8 100644 --- a/expenses_manager/expenses/templates/categories/form.html +++ b/expenses_manager/expenses/templates/categories/form.html @@ -10,7 +10,7 @@ {% endblock %} \ No newline at end of file diff --git a/expenses_manager/expenses/templates/categories/list.html b/expenses_manager/expenses/templates/categories/list.html index b057511..e587118 100644 --- a/expenses_manager/expenses/templates/categories/list.html +++ b/expenses_manager/expenses/templates/categories/list.html @@ -12,7 +12,7 @@