Files
WealthySmart/codebase-review/05-poweruser.md
Carlos Escalante 5c265129e8 Add five-hat codebase review and phased improvement plan
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 19:13:19 -06:00

162 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Poweruser Review — WealthySmart
> Hat: Daily power user of the app + senior product engineer. Focus: real user journeys, feedback loops, data trust, consistency, missing features.
## Executive Summary
WealthySmart has solid fundamentals: clean routing, consistent UI patterns, bilingual support, and thoughtful features like pension projections and municipal receipt tracking. But daily use exposes friction: **no success/error feedback after mutations** (did my delete work?), no error recovery anywhere (failed loads look like empty data), three different month-navigation patterns across modules, a search box that silently does nothing for non-credit-card sources, and — most corrosive to trust — **silent n8n parse failures** that leave invisible gaps in transaction history with no reconciliation view. The data tables are dense and not mobile-adapted. The Asistente is pleasant but read-only and ephemeral. None of these are deep architectural problems; they are a backlog of S/M-effort polish items plus two larger trust-building features (sync status, import review).
## What Works Well
- **Navigation & layout** — well-organized sidebar (General, Finanzas, Servicios); smooth mobile menu.
- **Strong typed data model** on the client — comprehensive interfaces across all modules.
- **Privacy mode** — `data-sensitive` blur with an accessible header toggle. Great for screen-sharing.
- **Thoughtful finance features** — pension projections with adjustable assumptions, recurring items with frequency + override logic, billing-cycle awareness, deferred transactions.
- **Accessibility baseline** — focus-visible rings, aria-labels, disabled-state management.
- **Charts** — Recharts configured properly with tooltips/legends across Analytics, Proyecciones, Pensions, ServiciosMunicipales.
- **Import result summaries** — paste-import and PDF upload report imported/updated/duplicate counts.
## Findings
### UX-01 | No success feedback after mutations — High
**Location:** `Budget.tsx:180183`, `RecurringItemsManager.tsx:5769`, `TransactionList.tsx:6474`, Pensions upload handlers
**Evidence:** Mutations call `onRefresh()`/`refresh()` with zero confirmation — no toast, no inline message. `TransactionList.tsx:68` deletes then refreshes silently.
**User impact:** "Did the delete go through?" Users infer success from list churn — anxiety for irreversible actions.
**Recommendation:** Add a toast layer (Sonner) and emit success/error on every mutation. Effort: **M** (library + sweep).
### UX-02 | No error recovery flow anywhere — High
**Location:** `Analytics.tsx:8494`, `ServiciosMunicipales.tsx:198211`, `PasteImportModal.tsx:4246`, `Budget.tsx:4476`
**Evidence:** `.catch(console.error)` or silent catches; Budget's `fetchTransactions` has no error handling at all.
**User impact:** API failure is indistinguishable from "no data." No retry affordance.
**Recommendation:** Standard `loading → error(retry) → data/empty` states on every page. Effort: **M** (trivial if TanStack Query lands first).
### UX-03 | Three different month/date-navigation patterns — Medium
**Location:** `Budget.tsx` + `Proyecciones.tsx` (chevrons), `Analytics.tsx` (`BillingCycleSelector` dropdown), `Pensions.tsx` (fixed last-12-months, no navigation)
**User impact:** Inconsistent mental model; Pensions history before 12 months is unreachable.
**Recommendation:** One shared period-navigator component; add history browsing to Pensions. Effort: **M**.
### UX-04 | Search silently inert for non-credit-card sources — Medium
**Location:** `Budget.tsx:4243, 164188`
**Evidence:** `txSearch` only feeds CREDIT_CARD queries; on the "Efectivo y Transferencias" tab the visible search box does nothing.
**User impact:** Search appears broken.
**Recommendation:** Make search work for all sources, or hide the field when inapplicable. Effort: **S**.
### UX-05 | Deferred-transaction toggle gives no feedback — Medium
**Location:** `transaction-columns.tsx:6872`, `TransactionList.tsx:37`
**Evidence:** Toggle PATCHes then refetches; no toast, no row styling change beyond a small "Diferida" badge.
**User impact:** User can't tell the deferral took effect without hunting.
**Recommendation:** Toast on toggle + muted/strikethrough styling on deferred rows. Effort: **S**. (Pairs with ARCH-11: add the field to `TransactionModal` too.)
### UX-06 | Dirty-form dismissal loses data silently — Low
**Location:** `RecurringItemDialog.tsx:50100`
**Recommendation:** ConfirmDialog on close when dirty. Effort: **S**.
### UX-07 | "All time" English label in Spanish UI — Low
**Location:** `BillingCycleSelector.tsx:52`
**Recommendation:** "Todo el período". Effort: **S** (part of the UX-19 locale sweep).
### UX-08 | Login page is bare — Low
**Location:** `Login.tsx`
**Evidence:** No remember-me, no error differentiation. (Password reset is N/A for a single-user env-configured credential.)
**Recommendation:** Low priority; longer cookie life (bounded by SEC-05) already covers persistence. Effort: **S**.
### UX-09 | Upload errors returned by the API are never displayed — Medium
**Location:** Pensions upload result rendering
**Evidence:** Backend returns `errors: string[]`; UI shows counts only — a failed parse looks like "Import complete, 0 imported."
**User impact:** User can't tell *why* an upload failed (wrong file? corrupt PDF?).
**Recommendation:** Render `result.errors` in an Alert list. Effort: **S**.
### UX-10 | No undo for destructive operations — Medium
**Location:** `TransactionList.tsx:6474`, RecurringItemsManager delete
**Evidence:** ConfirmDialog exists, but post-confirm deletion is immediate and irreversible.
**Recommendation:** Toast-with-Undo grace period (delay the API call ~5s), or DB soft-delete + restore endpoint. Effort: **M**.
### UX-11 | Transaction table is poor on mobile — Medium
**Location:** `DataTable.tsx:5780`, `transaction-columns.tsx`
**Evidence:** 5+ columns force horizontal scroll at <640px; tiny text.
**Recommendation:** Card-stack rendering on small screens (merchant/amount primary; category/bank secondary) or column-visibility tiers. Effort: **M**.
### UX-12 | Pension projections use hardcoded assumptions — Medium
**Location:** `Pensions.tsx:76116`
**Evidence:** `FUNDS_DEFAULT` hardcodes `startBalance`, `monthlyContribution`, `annualRate`; `applySnapshots()` updates only `saldo_final`.
**User impact:** Retirement projections drift from reality as contributions change.
**Recommendation:** Editable per-fund assumptions persisted server-side (settings or a `PensionAssumption` table). Effort: **M**.
### UX-13 | Cycle filter only applies to one of three Analytics charts — Medium
**Location:** `Analytics.tsx:7195`
**Evidence:** Category breakdown respects the selected cycle; monthly-trend and daily-spending queries ignore it.
**User impact:** User picks a cycle and two charts silently show all-time data.
**Recommendation:** Pass cycle params to all three endpoints (backend support needed) or visually scope the selector to the one chart it affects. Effort: **M**.
### UX-14 | Empty states are dead ends — Low
**Location:** `Budget.tsx:186`, `Salarios.tsx:157`, `TransactionList.tsx:81`
**Recommendation:** `emptyAction` prop rendering an "Add first item" button. Effort: **S**.
### UX-15 | Destructive-action confirmation applied inconsistently — Medium
**Location:** `YearlyOverview.tsx:7072` (clearing a balance override has no confirm; transaction/recurring deletes do)
**Recommendation:** ConfirmDialog on every destructive action. Effort: **S**.
### UX-16 | Asistente is read-only with no export — Low
**Location:** `Asistente.tsx:5679`
**Evidence:** Only `SpendingSummaryCard` renders; no CSV export, no report download, no chat persistence.
**Recommendation:** "Export CSV" on cards; longer-term, persist useful summaries. Effort: **M**.
### UX-17 | n8n parser failures are invisible — High
**Location:** Systemic (n8n → API ingestion path); no sync-status surface anywhere in the app
**Evidence:** If BAC changes its email format, the n8n flow fails in n8n's own logs; the app just shows a gap in transactions.
**User impact:** The core promise — "your finances, automatically tracked" — fails silently. The user discovers missing weeks at month-end close.
**Recommendation:** A Sync Status page: last-received timestamp per source (BAC, salary, municipal, pension), expected-cadence warnings ("no BAC email in 7 days"), n8n error visibility. Backend can derive most of this from existing data (`max(created_at) per source`) — no n8n integration needed for v1. Effort: **M** for derived v1, **L** for full n8n integration.
### UX-18 | No preview when editing recurring items — Medium
**Location:** `Budget.tsx:115201`, `useBudget.ts`
**Evidence:** Changing frequency MONTHLY→QUARTERLY shows impact only after save.
**Recommendation:** Before/after monthly-total preview in the dialog. Effort: **M**.
### UX-19 | Mixed-language dates and labels across the app — Low (but pervasive)
**Location:** `format.ts:1921` ('en-US'), `Budget.tsx:1215` (Spanish `MONTH_NAMES`), `Salarios.tsx:4249` ('en-US'), `ServiciosMunicipales.tsx:5457` (`MONTH_NAMES_ES`)
**Evidence:** Three separate month-name implementations in two languages; `formatDate` returns English.
**Recommendation:** One locale-aware date/format module (`Intl` with `es-CR`), delete the per-page constants. Effort: **M** (sweep).
### UX-20 | Duplicates are counted but never reviewable — High
**Location:** Import flows (paste-import, PDF upload) — UI shows `duplicates: 3` with no detail
**Evidence:** No way to see *which* rows were skipped as duplicates or to override a false-positive dedup.
**User impact:** Data-integrity trust gap; a false-positive dedup silently loses a real transaction.
**Recommendation:** Import-review response listing skipped rows (merchant/date/amount + matched existing row), with a "import anyway" override. Effort: **L** (backend detail + UI).
### UX-21 | Balance override input has no sanity check — Low
**Location:** `YearlyOverview.tsx:5572`
**Recommendation:** Show "Calculated: ₡X" beside the input; warn on large deviation. Effort: **S**.
### UX-22 | Proyecciones → Budget navigation is one-way — Low
**Location:** `Proyecciones.tsx:9699`
**Recommendation:** Back affordance/breadcrumb when arriving from Proyecciones. Effort: **S**.
### UX-23 | Chat cards are ephemeral — Low
**Location:** `Asistente.tsx`, `ChatCards.tsx`
**Evidence:** Refresh loses everything; no save/share affordance on cards.
**Recommendation:** Export/copy button on `SpendingSummaryCard`. Effort: **S**.
### UX-24 | Water-meter chart uses raw meter IDs — Low
**Location:** `ServiciosMunicipales.tsx:4852`
**Evidence:** Legend shows '7335'/'7345'/'9345'; hardcoded colors; unknown meters fall back to one default color.
**Recommendation:** User-editable meter labels ("Casa", "Cochera") in settings. Effort: **S**.
### UX-25 | Sticky header/sidebar scroll jank — Low
**Location:** `Layout.tsx:110, 153`
**Recommendation:** Consistent scroll container (`overflow-y-auto` on main). Effort: **S**.
## Feature Wishlist (prioritized)
1. **Transaction search & filtering everywhere** — full-text on merchant/notes/reference; date-range, amount-range, category filters. *High impact, M.*
2. **Bulk transaction actions** — multi-select → bulk re-categorize / defer / delete-with-undo. Statement import currently means 50 one-by-one edits. *High impact, M.*
3. **Sync status & reconciliation view** — last import per source, cadence warnings, failed-parse visibility, duplicate review (UX-17 + UX-20 together). *High impact, L. This is the single biggest trust feature.*
4. **CSV export** — transactions, salarios, pensions; for taxes/accountant. *Medium impact, M.*
5. **Budget override history & revert** — when/why an override was set. *Medium impact, S.*
6. **What-if scenario planner** — "save ₡50k more/month" against the 5-year projection. *Medium impact, M.*
7. **Exchange-rate history & per-date override** for reconciling old statements. *Medium impact, S.*
8. **PWA polish** — installable, offline-readable balances. *Medium impact, L.*
9. **Pension contribution calculator** — "reach ₡X by age Y." *Low impact, S.*
10. **Per-month recurring-item skip/override** — e.g., reduced December salary. *Low impact, S.*
**Effort key:** S < 4h, one component. M = 416h, may need a backend endpoint. L = 16h+, schema or multi-component work.