12 KiB
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-sensitiveblur 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:180–183, RecurringItemsManager.tsx:57–69, TransactionList.tsx:64–74, 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:84–94, ServiciosMunicipales.tsx:198–211, PasteImportModal.tsx:42–46, Budget.tsx:44–76
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:42–43, 164–188
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:68–72, 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:50–100
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:64–74, 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:57–80, 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:76–116
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:71–95
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:70–72 (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:56–79
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:115–201, 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:19–21 ('en-US'), Budget.tsx:12–15 (Spanish MONTH_NAMES), Salarios.tsx:42–49 ('en-US'), ServiciosMunicipales.tsx:54–57 (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:55–72
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:96–99
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:48–52
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)
- Transaction search & filtering everywhere — full-text on merchant/notes/reference; date-range, amount-range, category filters. High impact, M.
- Bulk transaction actions — multi-select → bulk re-categorize / defer / delete-with-undo. Statement import currently means 50 one-by-one edits. High impact, M.
- 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.
- CSV export — transactions, salarios, pensions; for taxes/accountant. Medium impact, M.
- Budget override history & revert — when/why an override was set. Medium impact, S.
- What-if scenario planner — "save ₡50k more/month" against the 5-year projection. Medium impact, M.
- Exchange-rate history & per-date override for reconciling old statements. Medium impact, S.
- PWA polish — installable, offline-readable balances. Medium impact, L.
- Pension contribution calculator — "reach ₡X by age Y." Low impact, S.
- Per-month recurring-item skip/override — e.g., reduced December salary. Low impact, S.
Effort key: S < 4h, one component. M = 4–16h, may need a backend endpoint. L = 16h+, schema or multi-component work.