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

12 KiB
Raw Permalink Blame History

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 modedata-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.