Repository navigation
fix(web): make the dashboard tell the truth about imported data (Phase 0) - #11
Merged
Merged
Conversation
The UI tested for 'outflow'/'inflow' but the API sends 'debit'/'credit', causing every row to render as an inflow (green, positive) and the Activity direction filter to do nothing. Four sites in app.js fixed (candidate preview + activity ledger). HTML direction filter option values updated to match.
…e 0) The browser dashboard rendered figures that did not match what the API sent, which made every manual test of the import path uninformative: you could not tell a real extraction defect from a display defect. - Budgets read `allocated_minor`/`spent_minor`; the API sends `budget_minor`, `actual_minor`, `remaining_minor` and `over_budget`. Every budget rendered as empty with a full bar. - Descriptions, merchants, categories, account names, filter values and issue messages were interpolated into `innerHTML` unescaped. Statement text is attacker-influenced in the only way that matters here: it comes from a file. All 23 render paths now escape. - A hardcoded June-August 2026 dataset stood in whenever an API call failed, so a dead backend looked like a healthy one and three-month charts could mix real and invented figures. Deleted. The API failing now says so, the month menu is derived from the current date, and an empty ledger renders as empty. - `amount_sign` existed in the API but not the UI, so an all-positive credit-card export could only be committed under `as_written` - booking every purchase as income. The preview now asks for the convention when every amount is an unsigned positive, and blocks commit until it is answered. - Commit was gated on excluded count alone and let rows with blocking errors through to a server-side rejection. It is now disabled with the reason shown. Verified against a running server, not by reading the diff: salary renders as an inflow and rent as an outflow, the direction filter splits 4 credits from 30 debits, a seeded 800.00 budget shows 260.00 spent and 540.00 remaining at 32.5%, a `<script>` payload in a description renders as text with zero injected script elements, an all-positive CSV blocks commit until the convention is set and then flips all five rows to outflows, and a dead API shows the error banner with no figures. Gate: ruff, ruff format, mypy, 101 tests, node --check. README now states that no real PDF statement imports end to end yet, rather than implying broad bank support. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYgbBDgmuiRTGt5QNo6313
Section 1.1 listed the six dashboard defects to ignore while testing. All six are fixed in this branch, so the table now describes a version of the app that no longer exists - the worst kind of doc, because it tells a reader to disregard behaviour that is now correct. The record of what was wrong belongs in this PR's description, not in a living plan. The run order kept the curl workaround for setting a batch's sign convention, which the preview UI now asks for directly. Replaced with what the UI actually does. Kept the one fact from the deleted table that is still true: OCR needs Tesseract on PATH and it is not installed here, so image-only PDFs stay untestable until it is. Retitled: this is the v0.2 plan, not a guide to a single afternoon's testing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYgbBDgmuiRTGt5QNo6313
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The dashboard rendered figures that did not match what the API sent. That made every manual test of the import path uninformative — you could not tell a real extraction defect from a display defect. Since the next milestone is importing my own real bank statements end to end, fixing the instrument comes before running the experiment.
What changed
debit/credit; UI compared againstoutflow/inflow, so every row rendered as a green inflow and the direction filter did nothingapp.js(4 sites) andindex.html(filter options)allocated_minor/spent_minor; API sendsbudget_minor/actual_minor/remaining_minor/over_budgetinnerHTMLunescapedescapeHtml()on all 23 render pathsFALLBACK_DATAdeleted →EMPTY_MONTH+ explicit error banner; month menu derived from the current dateamount_signexisted in the API but not the UI, so an all-positive credit-card export could only commit underas_written— booking every purchase as incomeVerification
Run against a live server with seeded demo data, not by reading the diff:
is-inflow+£3,500.00; rent/groceries renderis-outflow−£1,200.00eating_outbudget: APIbudget_minor=80000, actual_minor=26000, remaining_minor=54000→ UI shows Budget £800.00, Spent £260.00, Remaining £540.00, bar 32.5%<script>alert(1)</script>payload in a CSV description renders as literal text; 0 injected script elementsdebit_positiveflips all 5 rows to outflows and unblocks with "Ready to commit 5 transactions"Gate:
ruff check✅ ·ruff format --check✅ ·mypy src✅ · 101 tests passed ✅ ·node --check app.js✅ ·git diff --check✅Also checked: all 86 DOM ids used by
app.jsexist inindex.html; every new CSS class has a rule; no hardcoded money values or periods remain in either web asset.Also in this PR
docs/plans/2026-08-29-v0.2-currency-investments-plan.md— the v0.2 plan this is Phase 0 of: real-statement import as the core objective, GBP+INR in the core, then investments as holdings snapshots.Known follow-up (not a blocker here)
amount_signis applied to candidates but never persisted or echoed back —ImportBatchModelhas no column andImportBatchResponsehas no field. So the UI'sif (batch.amount_sign)pre-select can never fire, and there is no server-side record of which convention a committed batch used. The flow works in one sitting because the commit gate reads the live DOM value, and_apply_amount_signre-derives direction from raw text rather than flipping it, so it stays idempotent. Fix belongs with the Phase 2 migration that addsstatement_start/statement_end: one column, one response field.🤖 Generated with Claude Code
https://claude.ai/code/session_01RYgbBDgmuiRTGt5QNo6313