From d1efeece046af196a9a2a20b116c7e910e0a392a Mon Sep 17 00:00:00 2001 From: _null Date: Mon, 4 May 2026 20:12:57 -0500 Subject: [PATCH] push --- HISTORY.md | 22 +++ client/App.jsx | 35 ++++- client/api.js | 3 + client/components/layout/Layout.jsx | 6 +- client/components/layout/Sidebar.jsx | 99 +++++++++--- client/pages/AboutPage.jsx | 86 +++++++++++ client/pages/CategoriesPage.jsx | 103 +++++++------ client/pages/LoginPage.jsx | 10 +- client/pages/ReleaseNotesPage.jsx | 8 +- client/pages/SummaryPage.jsx | 131 +++++++++++----- client/pages/TrackerPage.jsx | 215 ++++++++++++++++++++++++++- db/database.js | 25 +++- package-lock.json | 4 +- package.json | 2 +- routes/about.js | 24 +++ routes/categories.js | 21 ++- routes/export.js | 14 +- routes/monthly-starting-amounts.js | 146 ++++++++++++++++++ routes/summary.js | 118 ++++++++++++++- routes/tracker.js | 22 ++- server.js | 4 +- services/userDbImportService.js | 63 +++++++- 22 files changed, 1030 insertions(+), 131 deletions(-) create mode 100644 client/pages/AboutPage.jsx create mode 100644 routes/about.js create mode 100644 routes/monthly-starting-amounts.js diff --git a/HISTORY.md b/HISTORY.md index 11859dd..5e0e45c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,27 @@ # Bill Tracker — Changelog +## v0.18.3 + +### Added +- Added an `Other` monthly starting amount alongside the 1st and 15th amounts. +- New `monthly_starting_amounts` records store user-scoped, month-specific starting cash with `first_amount`, `fifteenth_amount`, and `other_amount`. +- New `GET /api/monthly-starting-amounts` and `PUT /api/monthly-starting-amounts` endpoints manage monthly starting balances. +- Tracker renamed the “Total Expected” card to “Starting” and shows the selected month’s combined starting amount. +- The Tracker Starting card now has an edit control for setting 1st, 15th, and Other monthly amounts. +- Summary now uses monthly starting balances as the planning base and shows a Starting Balance section. +- Remaining balances deduct paid bills: due days 1-14 from the 1st bucket, due days 15-31 from the 15th bucket, and total paid from combined remaining. +- Added monthly starting amounts to user SQLite and Excel exports, and to user SQLite imports. +- Added a public About page with app version, stack, AI-assistance note, and Release Notes access. +- Release Notes are now available without login. + +### Notes +- Starting balances are not bills and are not payments. +- Remaining values can go negative when paid bills exceed starting cash; overages are not blocked. +- Previous month remaining is exposed to Summary as informational context only when available. +- Navigation now groups Overview, Summary, Bills, and Categories under Tracker, and groups Profile, Settings, and Data in the user menu. +- System Status is admin-only and appears in the Admin Panel navigation. +- No payment behavior, bill behavior, Calendar, Analytics, auth, or admin behavior was changed. + ## v0.18.1 ### Changed diff --git a/client/App.jsx b/client/App.jsx index 8a4b61a..cdb7dc1 100644 --- a/client/App.jsx +++ b/client/App.jsx @@ -2,6 +2,7 @@ import { Routes, Route, Navigate, useLocation } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; import Layout from '@/components/layout/Layout'; +import AppNavigation from '@/components/layout/Sidebar'; import { ReleaseNotesDialog } from '@/components/ReleaseNotesDialog'; import LoginPage from '@/pages/LoginPage'; import AdminPage from '@/pages/AdminPage'; @@ -14,6 +15,7 @@ import SettingsPage from '@/pages/SettingsPage'; import StatusPage from '@/pages/StatusPage'; import AnalyticsPage from '@/pages/AnalyticsPage'; import ReleaseNotesPage from '@/pages/ReleaseNotesPage'; +import AboutPage from '@/pages/AboutPage'; import DataPage from '@/pages/DataPage'; import ProfilePage from '@/pages/ProfilePage'; @@ -48,6 +50,17 @@ function RequireAuth({ children, role }) { return children; } +function AdminShell({ children }) { + return ( +
+ +
+ {children} +
+
+ ); +} + export default function App() { const { user } = useAuth(); @@ -58,6 +71,8 @@ export default function App() { } /> + } /> + } /> } /> + + + + + + } + /> + + + + } + /> } /> } /> } /> - } /> - } /> } /> diff --git a/client/api.js b/client/api.js index 44f1996..7d2d658 100644 --- a/client/api.js +++ b/client/api.js @@ -114,6 +114,8 @@ export const api = { // Summary summary: (y, m) => get(`/summary?year=${y}&month=${m}`), saveSummaryIncome: (data) => put('/summary/income', data), + getMonthlyStartingAmounts: (y, m) => get(`/monthly-starting-amounts?year=${y}&month=${m}`), + updateMonthlyStartingAmounts: (data) => put('/monthly-starting-amounts', data), // Bills bills: () => get('/bills'), @@ -162,6 +164,7 @@ export const api = { status: () => get('/status'), // Version (public) + about: () => get('/about'), version: () => get('/version'), releaseHistory: () => get('/version/history'), diff --git a/client/components/layout/Layout.jsx b/client/components/layout/Layout.jsx index fb6ee8d..3837893 100644 --- a/client/components/layout/Layout.jsx +++ b/client/components/layout/Layout.jsx @@ -1,4 +1,4 @@ -import { Outlet } from 'react-router-dom'; +import { Link, Outlet } from 'react-router-dom'; import AppNavigation from './Sidebar'; export default function Layout() { @@ -11,6 +11,10 @@ export default function Layout() { +
+ About + Release Notes +
); } diff --git a/client/components/layout/Sidebar.jsx b/client/components/layout/Sidebar.jsx index 90dfd2e..c4d9112 100644 --- a/client/components/layout/Sidebar.jsx +++ b/client/components/layout/Sidebar.jsx @@ -1,7 +1,7 @@ import { useState } from 'react'; -import { NavLink, useNavigate } from 'react-router-dom'; +import { NavLink, useLocation, useNavigate } from 'react-router-dom'; import { - Activity, BarChart3, CalendarDays, ChevronDown, ClipboardList, LayoutGrid, LogOut, Menu, Receipt, + Activity, BarChart3, CalendarDays, ChevronDown, ClipboardList, Database, Info, LayoutGrid, LogOut, Menu, Receipt, Settings, ShieldCheck, Tag, User, X, } from 'lucide-react'; import { cn } from '@/lib/utils'; @@ -18,18 +18,20 @@ import { } from '@/components/ui/dropdown-menu'; const userNavItems = [ - { to: '/', icon: LayoutGrid, label: 'Tracker', end: true }, { to: '/calendar', icon: CalendarDays, label: 'Calendar' }, - { to: '/summary', icon: ClipboardList, label: 'Summary' }, - { to: '/bills', icon: Receipt, label: 'Bills' }, - { to: '/categories', icon: Tag, label: 'Categories' }, { to: '/analytics', icon: BarChart3, label: 'Analytics' }, - { to: '/settings', icon: Settings, label: 'Settings' }, - { to: '/status', icon: Activity, label: 'Status' }, ]; const adminNavItems = [ - { to: '/admin', icon: ShieldCheck, label: 'Admin', end: true }, + { to: '/admin', icon: ShieldCheck, label: 'Admin Panel', end: true }, + { to: '/admin/status', icon: Activity, label: 'System Status' }, +]; + +const trackerItems = [ + { to: '/', icon: LayoutGrid, label: 'Overview', end: true }, + { to: '/summary', icon: ClipboardList, label: 'Summary' }, + { to: '/bills', icon: Receipt, label: 'Bills' }, + { to: '/categories', icon: Tag, label: 'Categories' }, ]; function BrandBlock({ adminMode = false }) { @@ -74,6 +76,45 @@ function NavPill({ item, onNavigate }) { ); } +function TrackerMenu({ onNavigate }) { + const location = useLocation(); + const navigate = useNavigate(); + const isTrackerActive = trackerItems.some(item => ( + item.end ? location.pathname === item.to : location.pathname.startsWith(item.to) + )); + + return ( + + + + + + {trackerItems.map(item => { + const Icon = item.icon; + return ( + { navigate(item.to); onNavigate?.(); }}> + + {item.label} + + ); + })} + + + ); +} + function UserMenu({ adminMode = false }) { const { user, logout } = useAuth(); const navigate = useNavigate(); @@ -101,16 +142,36 @@ function UserMenu({ adminMode = false }) { {name} + {user?.role === 'admin' && !adminMode && ( + <> + navigate('/admin')}> + + Admin Panel + + navigate('/admin/status')}> + + System Status + + + + )} navigate('/profile')}> Profile - {user?.role === 'admin' && !adminMode && ( - navigate('/admin')}> - - Admin - - )} + navigate('/settings')}> + + Settings + + navigate('/data')}> + + Data + + + navigate('/about')}> + + About + @@ -124,9 +185,7 @@ function UserMenu({ adminMode = false }) { export default function Sidebar({ adminMode = false }) { const [mobileOpen, setMobileOpen] = useState(false); const { user } = useAuth(); - const items = user?.role === 'admin' - ? [...userNavItems, ...adminNavItems] - : userNavItems; + const items = adminMode ? adminNavItems : userNavItems; return (
@@ -134,6 +193,7 @@ export default function Sidebar({ adminMode = false }) {