Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to OpenLedger will be documented here.

## 0.5.0 — 2026-06-19

- Added monthly category budgets with create, edit, delete, progress bars, and overspending warnings.
- Added savings goals with target amounts, progress tracking, contribution support, and optional target dates.
- Added dashboard widgets for budget summary, over-budget categories, goal progress, and upcoming goal dates.
- Updated cloud backup to include budgets and goals in payload and restore preview.
- Added budget and goal finance helpers (budget utilization, remaining budget, overspending detection, goal progress) with 13 new unit tests.
- Added empty states for budgets and goals sections.
- All computations are local derivations from in-memory state. No changes to persistence schema version, auth, or storage keys.

## 0.4.0 — 2026-06-19

- Redesigned dashboard with financial summary cards (income, expenses, net cash flow, net worth).
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ It runs in the browser, stores the active ledger locally, supports CSV import an

## Current Status

**Maturity:** Maintained. v0.4.0 — dashboard with financial insights and improved transaction view; guest mode remains default.
**Maturity:** Maintained. v0.5.0 — budgets, savings goals, and enhanced dashboard widgets; guest mode remains default.

OpenLedger is a **maintained early public MVP**. It is useful today as a browser-local ledger, but it is not a bank-connected finance platform and should not be treated as secure long-term storage for sensitive records.

What exists now:

- Monthly category budgets with create, edit, delete, progress bars, and overspending warnings.
- Savings goals with target amounts, progress tracking, contribution support, and optional target dates.
- Budget and goal dashboard widgets (budget summary, over-budget alerts, goal progress).
- Redesigned dashboard with financial summary cards (income, expenses, net cash flow, net worth).
- SVG charts: spending by category, income vs expenses, account balance distribution, monthly trend.
- Improved transactions view with search, date range filter, account/category/type filters, and sortable columns.
Expand Down
9 changes: 8 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ Computation helpers live in `src/lib/finance/`. All functions are pure derivatio
| `insights.ts` | Largest expense, top category, month-over-month change, recurring detection, low balance alerts |
| `trends.ts` | Monthly trend series (income, expense, net per month) |

All finance functions are tested under `src/lib/finance/__tests__/` (28 tests across 4 test files).
All finance functions are tested under `src/lib/finance/__tests__/` (41 tests across 6 test files). Additional helpers:

| Module | Purpose |
|--------|---------|
| `budgets.ts` | Budget utilization percentage, remaining budget, overspending detection |
| `goals.ts` | Goal progress percentage |

Both `Budget` and `Goal` types join the `LedgerData` and `PersistedLedgerState` types. The backup payload already included `budgets` and `goals` fields — v0.5.0 populates them with real data.

## Supabase Foundation (v0.1.1)

Expand Down
298 changes: 298 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1919,3 +1919,301 @@ select {
.guidance-banner strong {
color: var(--ink);
}

/* ── Budgets panel ── */
.budgets-panel-section {
grid-column: 1 / -1;
}

.budgets-panel {
display: grid;
gap: 20px;
}

.budget-form {
border-bottom: 1px solid var(--line-dark);
padding-bottom: 16px;
}

.budget-form-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
margin-bottom: 12px;
}

.budget-form-grid label {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 13px;
color: var(--muted);
}

.budget-form-grid input,
.budget-form-grid select {
border: 1px solid var(--line-dark);
border-radius: 6px;
background: var(--graphite-2);
color: var(--ink);
padding: 8px 10px;
font-size: 14px;
}

.budget-month-group {
display: grid;
gap: 10px;
}

.budget-month-label {
margin: 0;
font-family: Georgia, "Times New Roman", serif;
font-size: 16px;
font-weight: 500;
color: var(--paper-2);
border-bottom: 1px solid var(--line-dark);
padding-bottom: 6px;
}

.budget-list {
display: grid;
gap: 8px;
}

.budget-row {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: 16px;
align-items: center;
border: 1px solid var(--line-dark);
border-radius: 6px;
background: var(--graphite-2);
padding: 12px 16px;
}

.budget-row.over-budget {
border-color: var(--amber);
background: rgba(193, 136, 64, 0.08);
}

.budget-info {
display: flex;
flex-direction: column;
gap: 2px;
}

.budget-info strong {
font-size: 14px;
font-weight: 560;
}

.budget-amounts {
font-size: 12px;
color: var(--muted);
}

.budget-progress-section {
display: grid;
gap: 6px;
}

.budget-bar-track {
height: 10px;
overflow: hidden;
border-radius: 999px;
background: var(--line-dark);
}

.budget-bar-fill {
height: 100%;
border-radius: inherit;
transition: width 300ms ease;
}

.budget-bar-ok {
background: var(--sage);
}

.budget-bar-warn {
background: var(--amber);
}

.budget-bar-over {
background: var(--danger-soft);
}

.budget-stats,
.goal-stats {
display: flex;
justify-content: space-between;
font-size: 12px;
}

.budget-warn {
color: var(--amber);
}

.budget-pct {
color: var(--muted);
font-variant-numeric: tabular-nums;
}

.budget-dashboard-summary {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 10px;
}

.budget-mini {
border: 1px solid var(--line-dark);
border-radius: 6px;
background: var(--graphite-2);
padding: 12px;
display: grid;
gap: 8px;
}

.budget-mini.over-budget {
border-color: var(--amber);
}

.budget-mini-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 13px;
}

.budget-mini-header strong {
font-weight: 560;
}

/* ── Goals panel ── */
.goals-panel-section {
grid-column: 1 / -1;
}

.goals-panel {
display: grid;
gap: 20px;
}

.goal-form {
border-bottom: 1px solid var(--line-dark);
padding-bottom: 16px;
}

.goal-form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 12px;
}

.goal-form-grid label {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 13px;
color: var(--muted);
}

.goal-form-grid input {
border: 1px solid var(--line-dark);
border-radius: 6px;
background: var(--graphite-2);
color: var(--ink);
padding: 8px 10px;
font-size: 14px;
}

.goal-list {
display: grid;
gap: 12px;
}

.goal-row {
display: grid;
gap: 10px;
border: 1px solid var(--line-dark);
border-radius: 6px;
background: var(--graphite-2);
padding: 14px 16px;
}

.goal-header {
display: flex;
justify-content: space-between;
align-items: center;
}

.goal-header strong {
font-size: 15px;
font-weight: 560;
}

.goal-target {
font-size: 12px;
color: var(--muted);
}

.goal-progress-section {
display: grid;
gap: 6px;
}

.goal-meta {
display: flex;
gap: 20px;
font-size: 12px;
color: var(--muted);
}

.goal-meta .negative {
color: var(--amber);
}

.goal-actions {
display: flex;
gap: 6px;
border-top: 1px solid var(--line-dark);
padding-top: 10px;
}

.goal-actions button {
display: inline-flex;
align-items: center;
gap: 5px;
border: 1px solid var(--line-dark);
border-radius: 4px;
background: transparent;
color: var(--ink);
font-size: 12px;
padding: 5px 10px;
cursor: pointer;
}

.goal-actions button:hover {
background: var(--line-dark);
}

.contribute-form {
display: flex;
gap: 6px;
align-items: center;
}

.contribute-form input {
width: 100px;
border: 1px solid var(--line-dark);
border-radius: 4px;
background: var(--graphite-2);
color: var(--ink);
padding: 5px 8px;
font-size: 13px;
}

.goal-dashboard-list {
display: grid;
gap: 10px;
}
Loading
Loading