Fix dashboard hydration and archive theming

This commit is contained in:
2026-03-23 23:08:18 -04:00
parent c852ad0d80
commit feb2f2c37e
25 changed files with 290 additions and 13 deletions

View File

@@ -0,0 +1,26 @@
## ADDED Requirements
### Requirement: User can switch between light and dark themes
The system SHALL allow the user to toggle between light and dark themes with a persistent preference.
#### Scenario: User toggles dark mode on
- **WHEN** the user activates the theme toggle while the app is in light mode
- **THEN** the system applies the dark theme and saves the preference for future visits
#### Scenario: User toggles dark mode off
- **WHEN** the user activates the theme toggle while the app is in dark mode
- **THEN** the system applies the light theme and saves the preference for future visits
### Requirement: Theme preference respects system defaults on first visit
The system SHALL use the user's system color scheme preference when no saved preference exists.
#### Scenario: No stored preference uses system theme
- **WHEN** the user opens the app for the first time without a saved theme preference
- **THEN** the system applies dark mode when the operating system prefers dark color schemes and light mode otherwise
### Requirement: Theme selection loads without a flash of the wrong theme
The system SHALL initialize the theme before the first visible paint so the page does not briefly render in the wrong theme.
#### Scenario: Initial paint matches saved theme
- **WHEN** the app loads and a saved theme preference exists
- **THEN** the document theme is applied before the page content is visibly rendered

View File

@@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: Expense tracking UI renders correctly in both themes
The system SHALL render the expense tracking workspace, history list, forms, and item states with readable contrast in both light and dark themes.
#### Scenario: Expense workspace renders in dark mode
- **WHEN** the user opens the expense tracking view while dark mode is active
- **THEN** the form card, history card, inputs, actions, and expense rows use dark-compatible colors and remain readable
#### Scenario: Expense workspace renders in light mode
- **WHEN** the user opens the expense tracking view while light mode is active
- **THEN** the form card, history card, inputs, actions, and expense rows use light-compatible colors and remain readable

View File

@@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: Monthly dashboard UI renders correctly in both themes
The system SHALL render dashboard sections, insight cards, category bars, stat tiles, and empty states with readable contrast in both light and dark themes.
#### Scenario: Dashboard renders in dark mode
- **WHEN** the user opens the dashboard while dark mode is active
- **THEN** the summary cards, comparison cards, progress bars, and empty states use dark-compatible colors and remain readable
#### Scenario: Dashboard renders in light mode
- **WHEN** the user opens the dashboard while light mode is active
- **THEN** the summary cards, comparison cards, progress bars, and empty states use light-compatible colors and remain readable

View File

@@ -0,0 +1,12 @@
## ADDED Requirements
### Requirement: Paycheck tracking UI renders correctly in both themes
The system SHALL render the paycheck tracking workspace, schedule panel, form, and list items with readable contrast in both light and dark themes.
#### Scenario: Paycheck workspace renders in dark mode
- **WHEN** the user opens the paycheck tracking view while dark mode is active
- **THEN** the schedule panel, form card, inputs, and paycheck rows use dark-compatible colors and remain readable
#### Scenario: Paycheck workspace renders in light mode
- **WHEN** the user opens the paycheck tracking view while light mode is active
- **THEN** the schedule panel, form card, inputs, and paycheck rows use light-compatible colors and remain readable

View File

@@ -21,5 +21,5 @@
## 5. Verification
- [ ] 5.1 Visually verify light and dark modes across the dashboard, add-expense, and income pages in the browser.
- [ ] 5.2 Verify that theme preference persists across page refreshes and that there is no flash of the wrong theme on load.
- [x] 5.1 Visually verify light and dark modes across the dashboard, add-expense, and income pages in the browser.
- [x] 5.2 Verify that theme preference persists across page refreshes and that there is no flash of the wrong theme on load.

View File

@@ -0,0 +1,27 @@
## Purpose
TBD
## Requirements
### Requirement: System suggests categories from merchant names
The system SHALL support merchant-name-based category suggestion for expense entry while keeping all suggestion logic fully offline.
#### Scenario: Known merchant resolves from deterministic rules
- **WHEN** the user enters a merchant or shop name that matches a known merchant rule
- **THEN** the system assigns the mapped category without needing model inference
#### Scenario: Unknown merchant falls back to local model
- **WHEN** the user enters a merchant or shop name that does not match a known merchant rule
- **THEN** the system asks the local AI service for a category suggestion and returns the suggested category
### Requirement: Ambiguous suggestions remain user-controlled
The system SHALL keep the final saved category under user control for ambiguous or model-generated suggestions.
#### Scenario: User confirms model suggestion before save
- **WHEN** the category suggestion comes from model inference instead of a deterministic rule
- **THEN** the user can review and confirm or change the category before the expense is saved
#### Scenario: No cloud fallback is used
- **WHEN** the local suggestion service is unavailable
- **THEN** the system continues to allow manual category selection and does not send merchant data to a hosted provider

View File

@@ -0,0 +1,30 @@
## Purpose
TBD
## Requirements
### Requirement: User can switch between light and dark themes
The system SHALL allow the user to toggle between light and dark themes with a persistent preference.
#### Scenario: User toggles dark mode on
- **WHEN** the user activates the theme toggle while the app is in light mode
- **THEN** the system applies the dark theme and saves the preference for future visits
#### Scenario: User toggles dark mode off
- **WHEN** the user activates the theme toggle while the app is in dark mode
- **THEN** the system applies the light theme and saves the preference for future visits
### Requirement: Theme preference respects system defaults on first visit
The system SHALL use the user's system color scheme preference when no saved preference exists.
#### Scenario: No stored preference uses system theme
- **WHEN** the user opens the app for the first time without a saved theme preference
- **THEN** the system applies dark mode when the operating system prefers dark color schemes and light mode otherwise
### Requirement: Theme selection loads without a flash of the wrong theme
The system SHALL initialize the theme before the first visible paint so the page does not briefly render in the wrong theme.
#### Scenario: Initial paint matches saved theme
- **WHEN** the app loads and a saved theme preference exists
- **THEN** the document theme is applied before the page content is visibly rendered

View File

@@ -0,0 +1,47 @@
## Purpose
TBD
## Requirements
### Requirement: User can record categorized expenses
The system SHALL allow the user to create an expense with a title, amount, category, and local calendar date using fixed starter categories.
#### Scenario: Valid expense is created
- **WHEN** the user submits a title, positive amount, valid category, and valid local date
- **THEN** the system stores the expense and returns the created record
#### Scenario: Invalid expense is rejected
- **WHEN** the user submits a missing title, invalid amount, invalid category, or invalid date
- **THEN** the system rejects the request with a validation error and does not store the expense
### Requirement: User can review and delete expenses
The system SHALL allow the user to list recorded expenses and delete a specific expense by identifier.
#### Scenario: Expenses are listed
- **WHEN** the user requests expenses for the app
- **THEN** the system returns stored expenses in a stable order with their recorded fields
### Requirement: User can browse expense history by month
The system SHALL allow the user to select a `YYYY-MM` month when reviewing expense history and SHALL return the expenses recorded for that month.
#### Scenario: Prior month entries are visible
- **WHEN** the user selects February 2026 in the add-expense history view
- **THEN** the system shows the expenses recorded in February 2026 and exposes delete actions for deletable entries in that month
#### Scenario: Expense is deleted
- **WHEN** the user deletes an existing expense
- **THEN** the system removes that expense and it no longer appears in future listings or aggregates
## ADDED Requirements
### Requirement: Expense tracking UI renders correctly in both themes
The system SHALL render the expense tracking workspace, history list, forms, and item states with readable contrast in both light and dark themes.
#### Scenario: Expense workspace renders in dark mode
- **WHEN** the user opens the expense tracking view while dark mode is active
- **THEN** the form card, history card, inputs, actions, and expense rows use dark-compatible colors and remain readable
#### Scenario: Expense workspace renders in light mode
- **WHEN** the user opens the expense tracking view while light mode is active
- **THEN** the form card, history card, inputs, actions, and expense rows use light-compatible colors and remain readable

View File

@@ -0,0 +1,40 @@
## Purpose
TBD
## Requirements
### Requirement: Dashboard shows month-specific financial totals
The system SHALL return month-specific dashboard data for a requested `YYYY-MM` month using the local machine timezone for month boundaries.
#### Scenario: Dashboard totals are calculated for a populated month
- **WHEN** the user requests the dashboard for a month with expenses and paychecks
- **THEN** the system returns total expenses, total paychecks, net cash flow, and a category breakdown for that month
#### Scenario: Dashboard supports partial current-month data
- **WHEN** the user requests the dashboard for the current month before the month is complete
- **THEN** the system returns meaningful month-to-date totals and comparisons using the transactions recorded so far
### Requirement: Dashboard includes derived spending comparisons
The system SHALL provide derived comparisons for the selected month, including highest category, largest expense, average daily spend, and paycheck coverage information.
#### Scenario: Derived comparisons are available
- **WHEN** the selected month contains enough data for comparisons
- **THEN** the system returns the highest category, largest single expense, average daily spend, and spend-versus-paycheck coverage values
#### Scenario: Derived comparisons degrade safely for sparse data
- **WHEN** the selected month has no expenses or otherwise insufficient data for a comparison
- **THEN** the system returns null or empty-safe comparison fields instead of failing
## ADDED Requirements
### Requirement: Monthly dashboard UI renders correctly in both themes
The system SHALL render dashboard sections, insight cards, category bars, stat tiles, and empty states with readable contrast in both light and dark themes.
#### Scenario: Dashboard renders in dark mode
- **WHEN** the user opens the dashboard while dark mode is active
- **THEN** the summary cards, comparison cards, progress bars, and empty states use dark-compatible colors and remain readable
#### Scenario: Dashboard renders in light mode
- **WHEN** the user opens the dashboard while light mode is active
- **THEN** the summary cards, comparison cards, progress bars, and empty states use light-compatible colors and remain readable

View File

@@ -0,0 +1,34 @@
## Purpose
TBD
## Requirements
### Requirement: User can generate monthly AI insights on demand
The system SHALL allow the user to manually generate monthly AI insights for any month with existing or sparse data by sending structured monthly context to a fully offline local inference runtime.
#### Scenario: Insights are generated for a month with data
- **WHEN** the user requests insight generation for a month with recorded activity
- **THEN** the system sends monthly aggregates plus transaction samples to the local AI service and returns a rendered narrative summary with structured supporting totals
#### Scenario: Prior month insights can be generated
- **WHEN** the user requests insight generation for a previous month that has recorded data
- **THEN** the system generates and stores insight output for that requested month
### Requirement: Insight generation is read-only and safe for sparse months
The system SHALL keep AI insight generation read-only and return a safe fallback summary when a month does not have enough data for meaningful guidance.
#### Scenario: Sparse month returns fallback insight
- **WHEN** the user requests insight generation for a month with empty or near-empty data
- **THEN** the system returns a fallback message instead of low-confidence advice
#### Scenario: AI does not mutate financial records
- **WHEN** the system generates or stores monthly insights
- **THEN** no expense or paycheck records are created, updated, or deleted as part of that request
### Requirement: Insight generation remains private and resilient offline
The system SHALL keep monthly insight generation fully offline and provide a clear fallback response when the local model runtime or selected model is unavailable.
#### Scenario: Local runtime is unavailable
- **WHEN** the user requests monthly insights while the local AI runtime is not running or the configured model is unavailable
- **THEN** the system returns a clear setup or availability message instead of attempting a cloud fallback

View File

@@ -0,0 +1,40 @@
## Purpose
TBD
## Requirements
### Requirement: User can record paychecks by pay date
The system SHALL allow the user to create a paycheck with a positive amount and a local pay date.
#### Scenario: Valid paycheck is created
- **WHEN** the user submits a positive amount and valid local pay date
- **THEN** the system stores the paycheck and returns the created record
#### Scenario: Invalid paycheck is rejected
- **WHEN** the user submits a missing or invalid amount or date
- **THEN** the system rejects the request with a validation error and does not store the paycheck
### Requirement: User can review and delete paychecks
The system SHALL allow the user to list recorded paychecks and delete a specific paycheck by identifier.
#### Scenario: Paychecks are listed
- **WHEN** the user requests paychecks for the app
- **THEN** the system returns stored paychecks in a stable order with their recorded fields
#### Scenario: Paycheck is deleted
- **WHEN** the user deletes an existing paycheck
- **THEN** the system removes that paycheck and it no longer appears in future dashboard totals or insight inputs
## ADDED Requirements
### Requirement: Paycheck tracking UI renders correctly in both themes
The system SHALL render the paycheck tracking workspace, schedule panel, form, and list items with readable contrast in both light and dark themes.
#### Scenario: Paycheck workspace renders in dark mode
- **WHEN** the user opens the paycheck tracking view while dark mode is active
- **THEN** the schedule panel, form card, inputs, and paycheck rows use dark-compatible colors and remain readable
#### Scenario: Paycheck workspace renders in light mode
- **WHEN** the user opens the paycheck tracking view while light mode is active
- **THEN** the schedule panel, form card, inputs, and paycheck rows use light-compatible colors and remain readable

View File

@@ -1,5 +1,6 @@
import { HomeDashboard } from "@/components/home-dashboard";
import { getCurrentMonthKey } from "@/lib/date";
export default function Home() {
return <HomeDashboard />;
return <HomeDashboard initialMonth={getCurrentMonthKey()} />;
}

View File

@@ -40,8 +40,12 @@ type OllamaStatus = {
message: string;
};
export function HomeDashboard() {
const [selectedMonth, setSelectedMonth] = useState("");
type HomeDashboardProps = {
initialMonth: string;
};
export function HomeDashboard({ initialMonth }: HomeDashboardProps) {
const [selectedMonth, setSelectedMonth] = useState(initialMonth);
const [snapshot, setSnapshot] = useState<DashboardSnapshot | null>(null);
const [error, setError] = useState<string | null>(null);
const [insightBusy, setInsightBusy] = useState(false);
@@ -67,14 +71,6 @@ export function HomeDashboard() {
setOllamaStatus(payload);
}
useEffect(() => {
const timeoutId = window.setTimeout(() => {
setSelectedMonth(getCurrentMonthKey());
}, 0);
return () => window.clearTimeout(timeoutId);
}, []);
useEffect(() => {
if (!selectedMonth) {
return;