docs: complete project research
This commit is contained in:
@@ -1,182 +1,202 @@
|
||||
# Pitfalls Research
|
||||
|
||||
**Domain:** Personal finance dashboard UI overhaul (React SPA)
|
||||
**Researched:** 2026-03-16
|
||||
**Confidence:** HIGH (most findings verified against official docs, Recharts issues tracker, and Tailwind v4 official docs)
|
||||
**Domain:** Adding wizard setup, auto-budget creation, design system rework, and page consolidation to an existing personal budget app (React + Supabase + TanStack Query)
|
||||
**Researched:** 2026-04-02
|
||||
**Confidence:** HIGH (based on direct codebase analysis + known patterns for wizard state, auto-creation concurrency, and design system rework in existing apps)
|
||||
|
||||
---
|
||||
|
||||
## Critical Pitfalls
|
||||
|
||||
### Pitfall 1: Recharts Re-renders Every Parent State Change
|
||||
### Pitfall 1: Duplicate Budget Creation on Concurrent Auto-Create Calls
|
||||
|
||||
**What goes wrong:**
|
||||
The existing `DashboardPage.tsx` computes `pieData` and `progressGroups` directly in the render body — no `useMemo`. Every unrelated state change (e.g., a tooltip hover, a loading flag flip, QuickAddPicker opening) re-runs these array transforms and causes Recharts to diff its entire virtual DOM tree. Adding two more charts (bar chart, horizontal bar) multiplies this cost by 3x. With a large budget, each O(n) filter+reduce runs six times per render for each chart type.
|
||||
The v2.0 goal is "auto-create this month's budget on first visit." The current `generateFromTemplate` mutation in `useBudgets.ts` has no deduplication guard — it creates a budget row unconditionally. If the dashboard mounts, checks for a current-month budget, finds none, and fires the auto-create, there is a race: if the component re-renders (e.g., auth state settles, React Strict Mode double-invokes effects) or the user has two tabs open, two budget rows get created for the same month. Both have the same `start_date`/`end_date` and `user_id`, but different UUIDs. There is no unique constraint on `(user_id, start_date)` in the current `004_budgets.sql` migration.
|
||||
|
||||
**Why it happens:**
|
||||
The existing single-chart dashboard is fast enough that the missing memoization is invisible. During the overhaul, three chart instances are added to the same render tree, and the dashboard becomes visually complex enough that parent re-renders happen more frequently (collapsible state toggles, hover events on multiple chart tooltips).
|
||||
Auto-create logic placed in a React component or effect reacts to data absence, but TanStack Query's `enabled` flag and mutation guards don't prevent double-invocation in Strict Mode or multi-tab scenarios. The "check then create" pattern is an inherently non-atomic operation in the current client-side architecture.
|
||||
|
||||
**How to avoid:**
|
||||
- Wrap all chart data derivations in `useMemo` with explicit deps arrays:
|
||||
```tsx
|
||||
const pieData = useMemo(() =>
|
||||
EXPENSE_TYPES.map(type => { ... }).filter(d => d.value > 0),
|
||||
[items, t]
|
||||
)
|
||||
```
|
||||
- Wrap formatter callbacks passed to `<Tooltip formatter={...}>` in `useCallback`. New function references on every render force Recharts to re-render tooltip internals.
|
||||
- Extract each chart into its own memoized sub-component (`React.memo`) so only the chart whose data changed re-renders.
|
||||
- Create a category index Map once (in a hook or useMemo) and look up by key rather than `.filter()` per item across three charts.
|
||||
- Add a database unique constraint: `UNIQUE (user_id, start_date)` on the `budgets` table via a new migration. This makes duplicate creation fail at the DB level rather than silently succeeding.
|
||||
- In the auto-create logic, use an upsert (INSERT ... ON CONFLICT DO NOTHING) rather than a plain INSERT, so concurrent calls are idempotent.
|
||||
- Use TanStack Query's mutation state to gate the auto-create: only fire if `mutation.isIdle` and no existing budget is found, never fire inside a `useEffect` without a ref guard.
|
||||
- After auto-create, immediately invalidate the budgets list query so the new budget is fetched in the same round-trip.
|
||||
|
||||
**Warning signs:**
|
||||
- React DevTools Profiler shows `DashboardContent` re-rendering on tooltip mouseover
|
||||
- Multiple chart tooltips fighting each other (Recharts issues #281 / #1770)
|
||||
- "ResizeObserver loop limit exceeded" console errors when multiple `ResponsiveContainer` instances mount simultaneously
|
||||
- Two budget entries appear for the same month on the BudgetListPage after first visit
|
||||
- `console.error` shows a Supabase `23505 unique_violation` if the constraint is added and the race is triggered
|
||||
- Dashboard shows "Budget already exists" error toasts intermittently on page load
|
||||
|
||||
**Phase to address:** Dashboard redesign phase (charts implementation)
|
||||
**Phase to address:** Auto-budget creation phase — add the DB constraint first, implement upsert-based creation second. Do not build the auto-create UI trigger before the constraint exists.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 2: CSS Variable Scope — Recharts Cannot Read Tailwind `@theme inline` Variables
|
||||
### Pitfall 2: Wizard State Lost on Browser Back/Refresh — No Persistence Strategy
|
||||
|
||||
**What goes wrong:**
|
||||
The existing `palette.ts` already uses `var(--color-income)` etc. as chart fill values. This works correctly because Tailwind v4's `@theme inline` inlines these as true CSS custom properties on `:root`. However, the risk is subtle: if any chart color is set via a Tailwind utility class string (e.g., `fill-[var(...)]`) rather than through the `style` prop or a direct CSS variable reference, Recharts SVG elements — which render outside the standard DOM paint context — may not resolve them correctly in all browsers.
|
||||
|
||||
Additionally, dark mode: the current CSS has no dark-mode overrides for `--color-income` through `--color-investment`. If the overhaul adds a dark mode toggle, all chart colors will remain light-mode pastel on dark backgrounds, creating very poor contrast.
|
||||
A multi-step wizard (step 1: pick categories, step 2: set amounts, step 3: confirm) built with local React state (`useState`) loses all progress if the user navigates away, refreshes, or presses the browser back button. For a setup wizard that may have 3–5 steps and requires selecting 10–20 template items with amounts, this is a critical UX failure on first run. The user loses all work and must restart from step 0.
|
||||
|
||||
**Why it happens:**
|
||||
Tailwind v4's `@theme inline` block defines variables at `:root` scope, but the dark mode variant uses `.dark` class scoping. Theme variables defined only in `@theme inline` are not automatically duplicated under `.dark {}`. Dark-mode overrides for semantic colors (background, foreground) exist in shadcn's generated block, but category colors were custom-added without dark variants.
|
||||
The natural implementation is `const [step, setStep] = useState(0)` and `const [selections, setSelections] = useState([])` inside the wizard component. React state is ephemeral — it does not survive unmount. This works fine for modals but fails for a first-run setup wizard that the user may pause mid-way.
|
||||
|
||||
**How to avoid:**
|
||||
- Pass all Recharts color values via JavaScript (the `fill={categoryColors[type]}` pattern is correct — maintain it).
|
||||
- If dark mode is added in the overhaul: add a `.dark {}` block in `index.css` that overrides `--color-income`, `--color-bill`, etc. with darker/brighter variants appropriate for dark backgrounds.
|
||||
- Never attempt to pass Tailwind utility class strings as Recharts `fill` props. Recharts `Cell`, `Bar`, `Line` props require resolved color values (hex, oklch string, or `var()` reference).
|
||||
- Test chart colors in both light and dark modes before marking a phase complete.
|
||||
- Persist wizard state to `localStorage` keyed by user ID (e.g., `wizard_state_${userId}`) — read on mount, write on every step change, clear on completion/skip.
|
||||
- Use a simple serializable state shape: `{ step: number, selections: { categoryId, amount, tier }[] }`.
|
||||
- On wizard mount: check localStorage first; if partial state exists, resume from the saved step with a "Continue where you left off" prompt.
|
||||
- On wizard completion or explicit skip: clear the localStorage key.
|
||||
- Do NOT use URL params to store full wizard state (too much data); use URL only to track the current step number (e.g., `?setup-step=2`) for browser back/forward support.
|
||||
|
||||
**Warning signs:**
|
||||
- Chart fills show as `oklch(...)` literal text in DOM attributes instead of resolved colors
|
||||
- Colors are invisible or white-on-white on one theme variant
|
||||
- Browser DevTools shows SVG `fill` as unresolved `var(--color-income)` with no computed value
|
||||
- Wizard always starts at step 1 even after the user had reached step 3
|
||||
- User reports losing selected categories after an accidental page reload
|
||||
- No `localStorage` keys being set/read during wizard interaction
|
||||
|
||||
**Phase to address:** Design token / theming phase (early); dashboard charts phase (verification)
|
||||
**Phase to address:** Wizard setup phase — define the persistence strategy before building any step components. The storage format must be finalized before the first step is wired.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 3: Collapsible Sections Causing Layout Shift and CLS Jank
|
||||
### Pitfall 3: Design Token Rework Breaks Existing Pages Silently
|
||||
|
||||
**What goes wrong:**
|
||||
The planned hybrid dashboard includes collapsible inline sections for each category group (income, bills, variable expenses, debt, savings). If these are implemented by toggling `display: none` / `display: block` or by animating the `height` CSS property from `0` to `auto`, the result is either: (a) instant snap with no animation, or (b) jank where the browser triggers full layout recalculations on every animation frame. With five collapsible sections and charts above them, collapsing a section causes the charts to resize (their `ResponsiveContainer` detects parent width change), triggering a cascade of resize events.
|
||||
The v2.0 design system rework changes CSS variables in `index.css` (border-radius tokens from rounded to sharp, color palette from current OKLCH pastels to sharper/clearer ones, spacing adjustments). Because every existing page consumes these global tokens, any change to `--radius`, `--background`, `--foreground`, `--border`, or the six category color variables cascades to all 9 pages simultaneously. Changing `--radius` from `0.5rem` to `0px` sharps every Button, Card, Input, and Badge across the app in one line — but it also breaks charts (Recharts SVG `rx` attributes don't follow CSS variables) and any component that had visual design crafted around rounded corners.
|
||||
|
||||
**Why it happens:**
|
||||
Animating `height` from `0` to `auto` is a known browser limitation — you cannot CSS-transition to `height: auto`. Common naive workarounds (JavaScript measuring `scrollHeight` on every frame) cause layout thrashing. Radix UI's `Collapsible` component handles this correctly via CSS custom properties for height, but requires the `data-state` attribute pattern and the correct CSS transition on the inner `CollapsibleContent` element.
|
||||
The current system uses shadcn/ui's CSS variable convention which is explicitly global-scope. Every component that uses `rounded-lg` (which resolves to `var(--radius)`) will change simultaneously. Pages that were "done" in v1.0 become visually broken the moment a global token changes.
|
||||
|
||||
**How to avoid:**
|
||||
- Use Radix UI `Collapsible` (already available via shadcn) — it sets `--radix-collapsible-content-height` as a CSS variable during open/close, enabling smooth CSS-only transition:
|
||||
```css
|
||||
[data-state='open'] > .collapsible-content {
|
||||
animation: slideDown 200ms ease-out;
|
||||
}
|
||||
[data-state='closed'] > .collapsible-content {
|
||||
animation: slideUp 200ms ease-out;
|
||||
}
|
||||
@keyframes slideDown {
|
||||
from { height: 0 }
|
||||
to { height: var(--radix-collapsible-content-height) }
|
||||
}
|
||||
```
|
||||
- Add `debounce={50}` to all `ResponsiveContainer` instances to prevent rapid resize recalculations when parent containers animate.
|
||||
- Use `padding` instead of `margin` inside collapsible children — margin collapsing causes jump artifacts on some browsers.
|
||||
- Never animate `height`, `padding`, or `margin` directly with JavaScript setInterval; use CSS animations or Radix primitives.
|
||||
- Before touching any tokens, audit which existing pages use `--radius`, `--border`, and `--background` implicitly (via Tailwind utilities like `rounded-md`, `border`, `bg-background`). This is every page.
|
||||
- Change tokens in isolation with a visual regression pass on ALL 9 pages before touching any page's component code. The token change should be one commit; the per-page fixup should be separate commits.
|
||||
- For the category color variables (`--color-income` through `--color-investment`), verify Recharts SVG fills still resolve after the change — these are passed as `fill={var(--color-income)}` in JS and require testing in both the browser DOM and printed output.
|
||||
- Do not land the token rework mid-phase alongside page component changes — treat it as its own atomic commit.
|
||||
|
||||
**Warning signs:**
|
||||
- Charts visually "snap" to a narrower width and back when a section above them is toggled
|
||||
- Frame rate drops to under 30fps when expanding/collapsing sections (visible in DevTools Performance panel)
|
||||
- `ResizeObserver loop limit exceeded` errors spike after section toggle
|
||||
- Auth pages (login/register) have wrong border-radius after a `--radius` token change
|
||||
- Chart SVG fills appear correct in DevTools computed styles but wrong visually on screen
|
||||
- Any page that was marked "done" in v1.0 shows new visual regressions after token PR merges
|
||||
|
||||
**Phase to address:** Dashboard collapsible sections phase
|
||||
**Phase to address:** Design system foundation phase — the first phase of v2.0. Token rework must land before any page is rebuilt. Requires an explicit visual regression check of all existing pages immediately after the token commit.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 4: Color Accessibility Failures in Financial Semantic Colors
|
||||
### Pitfall 4: Quick-Add Page Removal Breaks Existing User Data and Nav References
|
||||
|
||||
**What goes wrong:**
|
||||
The most dangerous pattern in the existing dashboard is using pure green / red to signal positive / negative balance:
|
||||
```tsx
|
||||
const balanceColor = availableBalance >= 0
|
||||
? "text-green-600 dark:text-green-400"
|
||||
: "text-red-600 dark:text-red-400"
|
||||
```
|
||||
And in the progress bar:
|
||||
```tsx
|
||||
const barColor = group.overBudget
|
||||
? "bg-red-500 dark:bg-red-400"
|
||||
: "bg-green-500 dark:bg-green-400"
|
||||
```
|
||||
The `quick_add_items` table and `QuickAddPage` exist in production. Existing users have data in this table (names, icons, sort_order). If the page is simply removed from routing and the nav (deleting `<Route path="quick-add" />` and the nav item in `AppLayout.tsx`) without a data migration or a clear deprecation path, those users:
|
||||
1. Lose access to their quick-add items (no UI)
|
||||
2. Have orphaned `quick_add_items` rows in the database that no longer serve any purpose
|
||||
3. May hit broken bookmarks or history entries for `/quick-add`
|
||||
|
||||
Pure green (#00FF00) has a contrast ratio of only 1.4:1 against white — catastrophically below WCAG AA's 4.5:1 minimum for text. Even `green-600` (#16a34a) must be verified. Additionally, pie chart adjacent slice colors must maintain 3:1 contrast against each other (WCAG 2.1 SC 1.4.11 Non-text Contrast) — the existing six category colors are all similar lightness (OKLCH L ≈ 0.65–0.72), meaning they may be hard to distinguish for colorblind users or when printed.
|
||||
The new v2.0 model replaces quick-add with "add one-offs from category library directly in budget detail." This is a different data shape — category library items are categories, not quick_add_items. If a user had quick-add items that conceptually map to categories, that mapping is lost.
|
||||
|
||||
**Why it happens:**
|
||||
Designers focus on making the palette "look rich" during an overhaul without running contrast checks. The redesign goal is "rich, colorful visual style" — this is a direct risk factor for accessibility regressions. Color-alone encoding (green = good, red = bad) violates WCAG 1.4.1 Use of Color, which requires that color not be the sole means of conveying information.
|
||||
Page removal feels like a simple delete-the-route operation. The data persistence and existing user considerations are invisible during development because the developer's test account may have no quick-add data.
|
||||
|
||||
**How to avoid:**
|
||||
- Run every text color against its background through a WCAG contrast checker (target 4.5:1 for normal text, 3:1 for large text and UI components). Use the Tailwind oklch values — compute with tools like https://webaim.org/resources/contrastchecker/
|
||||
- For semantic colors (balance positive/negative, over-budget): supplement color with an icon or text label, not color alone. Example: a checkmark icon + green for positive balance; an exclamation icon + red for over-budget.
|
||||
- For pie/donut chart slices: ensure adjacent colors have at least 3:1 contrast, or add a visible stroke separator (1px white/black stroke between slices provides a natural contrast boundary).
|
||||
- For the 6 category colors: vary hue and lightness, not just hue. Consider making the OKLCH lightness values more spread (e.g., 0.55 to 0.80 range) so colors are distinguishable at reduced color sensitivity.
|
||||
- Do not rely on `dark:text-green-400` having passed contrast automatically — verify each dark mode color pair independently.
|
||||
- Audit: Does any existing user data in `quick_add_items` map to something meaningful in the new model? If quick-add items are just named shortcuts and the new model uses categories instead, the migration path is: surface existing quick-add items in the new "category library" flow, or clearly communicate to users that this data is being retired.
|
||||
- For the route: add a redirect `<Route path="quick-add" element={<Navigate to="/budgets" replace />} />` rather than leaving `/quick-add` as a 404. This handles bookmarks and history.
|
||||
- Remove the nav link in `AppLayout.tsx` at the same time as the redirect is added — never before.
|
||||
- Add a DB migration only if you decide to drop or repurpose the `quick_add_items` table. If keeping the table dormant, document why. If dropping it, confirm no FK references exist (none in the current schema — `budget_items` does not reference `quick_add_items`).
|
||||
|
||||
**Warning signs:**
|
||||
- All six category pie slices are clearly distinguishable to the developer but indistinguishable when the browser's "Emulate vision deficiency" filter is applied in DevTools
|
||||
- Running `window.getComputedStyle` on a colored element and checking its OKLCH L value — if multiple semantic colors cluster at the same lightness, colorblind users see identical grays
|
||||
- Developer tests removal on a fresh account with no quick-add data, misses the issue
|
||||
- Browser history entries for `/quick-add` result in a blank or redirect-looping page
|
||||
- `useQuickAdd` hook is still imported somewhere but the page is gone, causing a dead import
|
||||
|
||||
**Phase to address:** Design token / visual language phase (establish accessible palette before building components); then verify in each component phase
|
||||
**Phase to address:** Page consolidation phase (when BudgetDetailPage gets inline add-from-library). Remove the page only after the replacement flow is functional and tested with existing user data.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 5: i18n Key Regressions When Renaming or Adding UI Sections
|
||||
### Pitfall 5: Auto-Budget Creation Ignores User Currency Setting
|
||||
|
||||
**What goes wrong:**
|
||||
The current translation files (`en.json`, `de.json`) have flat keys per page section. The overhaul adds new dashboard sections (bar chart, horizontal bar chart, collapsible income/bill/expense groups, richer donut legend). Each new label, section header, tooltip, and ARIA label needs a corresponding key in both files. During rapid UI iteration, developers commonly add `t("dashboard.incomeSection")` to the JSX, forget to add it to `de.json`, and only notice when the German locale shows the raw key string — or worse, the i18next fallback silently shows the English value and the German regression goes undetected.
|
||||
The current `generateFromTemplate` mutation in `useBudgets.ts` accepts `currency?: string` with a default of `"EUR"`. If the auto-create logic fires without reading the user's profile `currency` field first, every auto-created budget will be denominated in EUR regardless of the user's settings. A user who has set their currency to USD or GBP in Settings will get EUR-denominated budgets. The `formatCurrency` calls will then display amounts in the wrong currency until the user manually edits the budget.
|
||||
|
||||
**Why it happens:**
|
||||
`i18next` with `fallbackLng: 'en'` (the existing config) silently falls back to English when a German key is missing. There is no visible failure. The project has no i18n linting step and no build-time key extraction check. The `debug: false` production config makes this invisible.
|
||||
The auto-create is triggered by checking if a budget exists for the current month and calling `generateFromTemplate({ month, year })` — the `currency` parameter is optional and defaults silently. During development on a EUR-default account, this works fine and the bug is invisible.
|
||||
|
||||
**How to avoid:**
|
||||
- When adding a new UI section, add its keys to **both** `en.json` and `de.json` in the same commit — never split across commits.
|
||||
- Use `i18next-cli` or `i18next-scanner` (npm package) to extract all `t("...")` call keys from source and diff against the JSON files. Run this as a pre-commit check or part of the build.
|
||||
- In development, consider setting `debug: true` in `i18n.ts` — i18next logs `missingKey` warnings to console for every untranslated key in the non-fallback language.
|
||||
- Use a TypeScript-typed i18n setup (e.g., `i18next-typescript`) so that `t("dashboard.nonExistentKey")` produces a type error at compile time.
|
||||
- Before marking any phase complete, manually switch locale to German and click through every changed screen.
|
||||
- The auto-create trigger must first load the user's profile (from the `profiles` table, `currency` column) and pass that value explicitly: `generateFromTemplate({ month, year, currency: profile.currency })`.
|
||||
- The `useProfile` or `useAuth` hook should be resolved before the auto-create mutation fires. Use TanStack Query's `enabled` flag to chain the dependency: only auto-create when both the budgets list AND the profile are loaded.
|
||||
- Add an assertion in the mutation: if `currency` is undefined, throw rather than silently default.
|
||||
|
||||
**Warning signs:**
|
||||
- German UI text contains raw dot-notation strings (e.g., `dashboard.barChart.title`)
|
||||
- `console.warn` messages containing `i18next::translator: missingKey de`
|
||||
- `de.json` has fewer top-level keys than `en.json` after a phase's changes
|
||||
- Auto-created budgets always show EUR symbol even when user has set USD in Settings
|
||||
- The profile `currency` is not read anywhere in the auto-create code path
|
||||
- Test passes for EUR users only; USD/GBP users never tested
|
||||
|
||||
**Phase to address:** Every phase that adds new UI text; establish the key-parity check process in the first phase of the overhaul
|
||||
**Phase to address:** Auto-budget creation phase — the profile dependency must be resolved as part of the auto-create implementation, not as a follow-up fix.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 6: Design Inconsistency Across Page Refreshes (The "Island Redesign" Problem)
|
||||
### Pitfall 6: Wizard Creates Categories and Template Items Without Idempotency — Re-run Corrupts Data
|
||||
|
||||
**What goes wrong:**
|
||||
The overhaul covers all pages, but if phases are structured page-by-page, early pages get the richest design attention and later pages get inconsistency or fatigue-driven shortcuts. The most common failure mode: the dashboard uses a specific card style (colored header accent, icon in corner), but the Categories page — redesigned two weeks later — uses a subtly different card variant. Buttons on the Budget Detail page use different spacing than on the Template page. The result is a design that looks cohesive in screenshots of individual pages but feels broken when navigating between them.
|
||||
The setup wizard pre-fills common items (rent, groceries, car insurance, etc.) and on completion creates: (a) category rows in `categories`, (b) template_items in `template_items`. If the user runs the wizard, quits mid-way, then re-runs it, or if the wizard's "complete" step fires twice due to a network retry, duplicate categories and template items are created. The `categories` table has no unique constraint on `(user_id, name)`. The user ends up with "Rent" appearing twice as a category and twice as a template item.
|
||||
|
||||
**Why it happens:**
|
||||
There is no design system enforced at the component level. shadcn/ui components are used directly in pages without project-specific wrappers. When the overhaul introduces new visual patterns (e.g., a colored icon badge, a section divider style), they are implemented inline in the first page that needs them and then drift in subsequent pages as developers make minor "feels close enough" adaptations.
|
||||
Wizard completion is typically a single "save all" mutation. If the user navigates back to step 1 and completes again, or if a network error causes a retry, the entire creation sequence runs again. Category creation has no deduplication.
|
||||
|
||||
**How to avoid:**
|
||||
- Before redesigning any pages, establish a small shared component library of new visual primitives (e.g., `<SectionHeader>`, `<StatCard>`, `<CategoryBadge>`) with fixed prop interfaces. All page redesigns consume these components — they never re-implement them inline.
|
||||
- Define the full color palette and spacing scale in the first phase, not incrementally. The `index.css` `@theme` block is the single source of truth — no hardcoded hex values anywhere else.
|
||||
- Create a visual spec (even a quick screenshot grid) of what each page will look like before coding begins, so inconsistencies are caught at design time not code review time.
|
||||
- In code review: any new Tailwind classes that don't use design tokens (e.g., `text-[#6b21a8]`, `p-[13px]`) should be flagged as violations.
|
||||
- Add a database unique constraint on `categories (user_id, name)` (or at minimum check for existence before insert). Use upsert for category creation in the wizard flow.
|
||||
- Gate the wizard's final "create" step behind a `isFirstRun` flag stored in the user's `profiles` table (e.g., a `setup_completed` boolean). Once `true`, the wizard's create mutation is a no-op.
|
||||
- Use a two-phase completion approach: "Preview what will be created" → "Confirm" — this prevents accidental double-submission.
|
||||
- If using localStorage for wizard state, clear it atomically when the server-side creation succeeds, not before.
|
||||
|
||||
**Warning signs:**
|
||||
- Two pages use different visual patterns for "section with a list of items" (one uses a table, one uses cards)
|
||||
- Color values appear as raw hex or oklch literals in component files instead of semantic tokens
|
||||
- shadcn Card component is used in 3 different ways across 3 pages (no wrapper abstraction)
|
||||
- Running the wizard twice results in duplicated category rows in the Categories page
|
||||
- Template page shows duplicate items with identical names
|
||||
- A Supabase unique constraint error on `categories` fires if the constraint is added after the wizard is built
|
||||
|
||||
**Phase to address:** Design foundation phase (first phase of overhaul) before any page work begins
|
||||
**Phase to address:** Wizard setup phase — idempotency requirements must be specified as acceptance criteria before any wizard mutation code is written.
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 7: CSS Variable Scope Rework Breaks Recharts SVG Fill Resolution
|
||||
|
||||
**What goes wrong:**
|
||||
v1.0 established that category colors are passed to Recharts as CSS variable references resolved in JavaScript (`var(--color-income)` etc. via `categoryColors` in `palette.ts`). If the v2.0 design token rework renames these variables (e.g., from `--color-income` to `--category-income`, or changes the OKLCH values such that the Recharts SVG `fill` attributes show visually different colors than intended), charts break silently — the SVG renders but fills are wrong.
|
||||
|
||||
Additionally, Recharts SVG elements operate outside the standard CSS cascade for some browsers. If OKLCH values are changed to be very light (high L) in pursuit of "clearer pastels," SVG fills may become nearly invisible on white chart backgrounds because SVG doesn't inherit `background` opacity in the same way HTML elements do.
|
||||
|
||||
**Why it happens:**
|
||||
Design token rework focuses on HTML components and Tailwind utility class output. Recharts SVG fills are wired through `palette.ts` as literal CSS variable strings — not Tailwind classes — so they are easy to miss in a token audit.
|
||||
|
||||
**How to avoid:**
|
||||
- After any `index.css` token change, explicitly open the dashboard and inspect all three charts (donut, bar, horizontal bar) to verify fill colors match intention.
|
||||
- Keep `palette.ts` as the single source of truth for chart colors — do not duplicate color definitions in component files.
|
||||
- If renaming CSS variable names (not just changing values), grep the entire codebase for the old variable name before deleting it.
|
||||
- Test chart fill visibility against the new `--background` value — lighter backgrounds require slightly more saturated or darker chart fills to maintain visibility.
|
||||
|
||||
**Warning signs:**
|
||||
- Chart fills appear white or very faint after a design token change
|
||||
- `palette.ts` still references `--color-income` after a rename to `--category-income`
|
||||
- Charts look correct in Storybook/isolation but wrong on the actual dashboard
|
||||
|
||||
**Phase to address:** Design system foundation phase (when tokens change) AND dashboard phase (verification).
|
||||
|
||||
---
|
||||
|
||||
### Pitfall 8: "Template Empty" Edge Case Breaks Auto-Budget and New-User Wizard
|
||||
|
||||
**What goes wrong:**
|
||||
The auto-budget creation flow depends on the template having items. The current `generateFromTemplate` code handles an empty template gracefully — it creates an empty budget. But the v2.0 UX intent is that visiting the dashboard for the first time should show a useful budget, not an empty one. If the wizard is bypassed (user skips setup), or if wizard completion fails silently, the auto-created budget has zero items, and the dashboard shows nothing meaningful.
|
||||
|
||||
A second edge case: a user who completed v1.0 setup manually (has categories + template items) visits v2.0 for the first time. The wizard should not re-run — but if the "has template items" check is the gate, and they had template items before, the wizard is skipped, and the auto-create fires correctly. If that check is wrong (e.g., checking `template.name === "My Monthly Template"` instead of `template_items.length > 0`), existing users get redirected into the wizard.
|
||||
|
||||
**Why it happens:**
|
||||
The first-run detection logic is complex: it must distinguish between (a) brand new user with no data, (b) existing user who set up manually in v1.0, (c) user who started wizard but didn't finish, (d) user who completed wizard. Each case needs different behavior and the logic branches are easy to conflate.
|
||||
|
||||
**How to avoid:**
|
||||
- Define the first-run gate explicitly using the `profiles.setup_completed` boolean (recommended in Pitfall 6) — this is the single authoritative signal, not inferred from data shape.
|
||||
- For existing v1.0 users: write a migration or a one-time check that sets `setup_completed = true` for any user who already has template items. This prevents the wizard from showing for users who have already configured their template.
|
||||
- Test all four user cases explicitly before shipping the wizard.
|
||||
|
||||
**Warning signs:**
|
||||
- Existing users (who set up in v1.0) see the setup wizard on their next login
|
||||
- New users who skip the wizard get a blank dashboard with no explanation
|
||||
- The `setup_completed` flag is never set to `true` for existing users via migration
|
||||
|
||||
**Phase to address:** Wizard setup phase — the first-run gate and existing-user migration must be defined as part of the wizard's acceptance criteria, not as an afterthought.
|
||||
|
||||
---
|
||||
|
||||
@@ -184,12 +204,13 @@ There is no design system enforced at the component level. shadcn/ui components
|
||||
|
||||
| Shortcut | Immediate Benefit | Long-term Cost | When Acceptable |
|
||||
|----------|-------------------|----------------|-----------------|
|
||||
| Inline chart data transforms in render | Faster to write | Re-renders on every state change; adding more charts multiplies the cost | Never — always `useMemo` for chart data |
|
||||
| Hardcoded color classes `text-green-600` for semantic states | Familiar Tailwind pattern | Dark mode variants need to be manually maintained in two places; fails contrast checks | Only for truly non-semantic colors (e.g., a green checkmark icon that is always green) |
|
||||
| Copying shadcn component patterns across pages without abstraction | Faster per-page development | Design drift guarantees inconsistency; changes to shared patterns require hunting all usages | MVP only, with explicit note to abstract before more pages are built |
|
||||
| Adding English i18n keys without German equivalent | Unblocks development | Silent regression in German locale; accumulates debt that is hard to audit later | Never in this project — add both languages in the same commit |
|
||||
| Using `ResponsiveContainer` without `debounce` | Default behavior, no extra code | Multiple containers trigger simultaneous resize cascades when sections open/close | Acceptable for single-chart pages; set `debounce={50}` on all multi-chart layouts |
|
||||
| Implementing collapsible with `display: none` toggle | Simplest implementation | No animation; abrupt layout shift; screen readers cannot detect intermediate states | Never for primary dashboard sections; acceptable for debug/dev-only UI |
|
||||
| No `UNIQUE (user_id, start_date)` constraint on `budgets` — skip the migration | Saves one migration | Duplicate budgets on concurrent auto-create; data corruption that is painful to clean | Never — add the constraint before any auto-create code ships |
|
||||
| Auto-create logic in a `useEffect` | Familiar React pattern | Double-invocation in Strict Mode, fires on re-render, race conditions | Never for write operations; use TanStack Query `useQuery` with `initialData` or a mutation gated on query result |
|
||||
| Wizard state in component `useState` only | Simplest to write | Lost on refresh; user frustration on first run | Only for wizards that complete in under 30 seconds and have no network steps |
|
||||
| Skipping the `profiles.setup_completed` flag and inferring first-run from data shape | No migration needed | Complex conditional logic; wrong inference for edge-case users; breaks if data shape changes | Never — explicit flag is always more reliable than inferred state |
|
||||
| Removing `/quick-add` route without a redirect | One less route to maintain | Broken bookmarks, broken history, 404 for any hardcoded link | Never — always add a redirect when removing a route |
|
||||
| CSS token rework and page component rework in the same PR | Ships faster | Impossible to attribute regressions; review is overwhelming; rollback is all-or-nothing | Never for design-system-wide token changes — tokens must land as their own commit |
|
||||
| Using the `quick_add_items` table schema for the new category library | Reuses existing table and hooks | Conflates two different concepts; data model becomes unclear; future features referencing categories become harder | Only if category library is purely a UI concern with no persistence — but it needs persistence, so use categories table |
|
||||
|
||||
---
|
||||
|
||||
@@ -197,11 +218,13 @@ There is no design system enforced at the component level. shadcn/ui components
|
||||
|
||||
| Integration | Common Mistake | Correct Approach |
|
||||
|-------------|----------------|------------------|
|
||||
| Recharts + Tailwind CSS variables | Passing Tailwind utility strings (e.g., `"text-blue-500"`) as chart `fill` or `stroke` props | Pass `var(--color-income)` or a resolved hex/oklch string via JS. Recharts SVG does not process Tailwind class strings. |
|
||||
| Recharts + `ResponsiveContainer` | Placing `ResponsiveContainer` inside a flex/grid parent without an explicit height | The container measures `0px` height. Always wrap in a `<div style={{ height: 240 }}>` or give the parent an explicit height class. |
|
||||
| shadcn `Collapsible` + Recharts | Placing charts inside collapsible sections without `debounce` | When the collapsible opens, the chart container resizes and triggers `ResizeObserver` rapidly. Add `debounce={50}` to `ResponsiveContainer`. |
|
||||
| Tailwind v4 `@theme inline` + dark mode | Defining category colors only in `@theme inline` without a `.dark {}` override block | Category colors remain light-mode pastels on dark backgrounds. Define dark variants in a `.dark {}` block in `index.css`. |
|
||||
| i18next + new UI sections | Adding `t("new.key")` JSX without adding the key to `de.json` | Silently falls back to English in German locale. Always update both files simultaneously. |
|
||||
| Supabase + auto-create budget | Plain INSERT in a client-side effect, no deduplication | Use INSERT ... ON CONFLICT DO NOTHING with a unique constraint on `(user_id, start_date)` |
|
||||
| TanStack Query + auto-create mutation | Firing mutation inside `useEffect` with data dependency | Use `useQuery` to check existence, then fire mutation via user action or a stable effect with ref guard |
|
||||
| localStorage + wizard state | Storing state without user-scoping — one user's wizard state leaks to another on shared device | Key all localStorage entries with `${userId}` prefix |
|
||||
| Supabase RLS + category library pre-population | Inserting pre-filled categories from client-side wizard without user_id | `user_id` must be set to `auth.uid()` on all inserts; Supabase RLS policy will reject rows with wrong user_id |
|
||||
| Recharts + new design tokens | Assuming token rename in `index.css` auto-updates SVG fill values | `palette.ts` references CSS variable names explicitly — any rename requires updating `palette.ts` too |
|
||||
| React Router + wizard steps | Using browser back button to go "back" in wizard while step is tracked only in state | Use URL search params (`?step=2`) so browser back/forward navigate wizard steps correctly |
|
||||
| TanStack Query + profile currency dependency | Auto-create fires before profile query resolves; uses default currency | Use `enabled: profileLoaded && budgetMissing` to chain query dependencies |
|
||||
|
||||
---
|
||||
|
||||
@@ -209,11 +232,20 @@ There is no design system enforced at the component level. shadcn/ui components
|
||||
|
||||
| Trap | Symptoms | Prevention | When It Breaks |
|
||||
|------|----------|------------|----------------|
|
||||
| Unmemoized chart data on a multi-chart dashboard | Tooltip hover causes all three charts to re-render simultaneously; visible lag | `useMemo` for all data transforms, `useCallback` for formatter props, `React.memo` on chart sub-components | Noticeable with 50+ budget items; subtle with 10–20 |
|
||||
| O(n) category lookup per budget item across 3 charts | CPU spike when dashboard loads; slow initial paint | Build a `Map<id, Category>` once in the hook layer, O(1) lookup in components | Becomes visible at ~100 budget items |
|
||||
| Multiple `ResponsiveContainer` without `debounce` | Rapid resize loop on section toggle; "ResizeObserver loop" errors | Add `debounce={50}` to all `ResponsiveContainer` instances on the dashboard | Any time two or more containers are mounted simultaneously |
|
||||
| Animating collapsible height with JavaScript | Dropped frames during expand/collapse; chart reflow cascade | Use Radix `Collapsible` with CSS `@keyframes` on `--radix-collapsible-content-height` | Every interaction on the dashboard |
|
||||
| TanStack Query unbounded cache growth | Long browser session consumes excessive memory | Set `gcTime` and `staleTime` on QueryClient config | After ~30 minutes of active use without page reload |
|
||||
| Wizard pre-populates 20+ categories in one bulk insert without optimistic UI | Wizard "complete" step hangs for 2–3 seconds; no feedback to user | Show a loading state immediately; use `mutate` with `onMutate` for optimistic feedback | Every user on wizard completion |
|
||||
| Auto-create budget fires on every dashboard mount during loading state | Multiple budget rows created; duplicate toasts; TanStack Query cache stale | Gate auto-create on `budgets.isSuccess` (not `isLoading`), and use a ref to prevent repeat fires | Any fast page navigation that remounts the dashboard |
|
||||
| Category library rendered as a flat list with 50+ items and no virtualization | Picker feels slow and sluggish when inline in BudgetDetailPage | Virtualize the list with TanStack Virtual or paginate by category type | Not an issue at 10 items; noticeable at 30+; problematic at 50+ |
|
||||
| Re-fetching template items on every wizard step navigation | Network calls on every back/forward in wizard; slow perceived performance | Cache template items in TanStack Query with a long `staleTime`; prefetch on wizard mount | Noticeable on slow connections even with 5 template items |
|
||||
|
||||
---
|
||||
|
||||
## Security Mistakes
|
||||
|
||||
| Mistake | Risk | Prevention |
|
||||
|---------|------|------------|
|
||||
| Wizard inserts categories without user_id from auth.uid() | Categories created with wrong or null user_id bypass RLS or are inaccessible | Always derive user_id from `supabase.auth.getUser()` inside the mutation; never pass it from client state |
|
||||
| Setup wizard marks `setup_completed = true` client-side before DB write succeeds | User appears "set up" but has no data if the write fails; auto-create never triggers again | Mark `setup_completed = true` only in the mutation's `onSuccess` callback |
|
||||
| Exposing wizard pre-fill library (category names + amounts) as a static client bundle | Not a critical risk for personal finance app, but hardcoded amounts (e.g., "Groceries: $500") are visible in source | Acceptable for this use case; note it is not PII |
|
||||
|
||||
---
|
||||
|
||||
@@ -221,25 +253,27 @@ There is no design system enforced at the component level. shadcn/ui components
|
||||
|
||||
| Pitfall | User Impact | Better Approach |
|
||||
|---------|-------------|-----------------|
|
||||
| Color as the only signal for budget overrun (red progress bar) | Colorblind users cannot distinguish over-budget from on-track | Add an icon (exclamation mark) or text indicator alongside the color change |
|
||||
| Hiding line items behind collapsible sections with no affordance | Users don't discover that sections are expandable; they think the dashboard is incomplete | Use an explicit chevron icon with visible state change; consider first-time-open hint |
|
||||
| Chart tooltips showing raw numbers without currency formatting | Users see "1234.5" instead of "$1,234.50" — especially jarring in a financial app | Always pass `formatCurrency(value, currency)` in the Recharts `formatter` prop |
|
||||
| Summary cards showing totals without context (no comparison to last month or budget) | Numbers without context are harder to interpret; users don't know if $1,200 expenses is good or bad | Show budget vs actual delta or a "vs budget" sub-label on cards |
|
||||
| Five-section collapsible dashboard that defaults to all-collapsed | Users land on a nearly empty dashboard and are confused | Default the primary sections (income, bills) to open on first load |
|
||||
| Progress bar clamped to 100% without showing actual overage amount | Users cannot see how much they are over budget from the bar alone | Show the actual percentage (e.g., "132%") in the label even when the bar is clamped |
|
||||
| Wizard with "skip" that results in empty dashboard | User skips setup, sees blank dashboard, doesn't know why nothing is there | If user skips, show a non-empty empty state with a clear "Set up your template" CTA |
|
||||
| Auto-created budget with no items (empty template) silently creates an empty budget | Dashboard appears to "work" but shows zeros; user confused why their categories aren't there | Check if template has items before auto-creating; if not, show a "complete your template setup" prompt |
|
||||
| Removing quick-add nav item without explanation | Users who relied on quick-add can't find the feature; assume it's broken | Add a deprecation note or in-app transition message ("Quick-add is now available inline in your budget") |
|
||||
| Wizard "back" button erases selections made in the later step | User goes back to change one thing and loses all subsequent work | Wizard back/forward must preserve all step state, not reset forward steps |
|
||||
| Auto-created budget uses wrong month if user's timezone differs from server | User visiting on Dec 31 in UTC+11 gets a January budget auto-created | Derive month from the client's local timezone, not the server's |
|
||||
| Category library in BudgetDetailPage shows all categories including types that contradict the budget context | Income categories shown when adding a one-off expense item; confusing | Filter the inline picker by relevant category types for the context (expense-only for one-off adds) |
|
||||
|
||||
---
|
||||
|
||||
## "Looks Done But Isn't" Checklist
|
||||
|
||||
- [ ] **Charts:** Verify all three chart types (donut, bar, horizontal bar) render correctly when `items` is empty — Recharts renders a blank SVG that can overlap other content if dimensions are not handled
|
||||
- [ ] **Dark mode:** Switch to dark theme (if implemented) and verify all category colors, chart fills, and semantic state colors (green/red) have sufficient contrast — light-mode testing only is the most common gap
|
||||
- [ ] **German locale:** Navigate every redesigned page in German and verify no raw key strings appear — `de.json` parity check
|
||||
- [ ] **Collapsible sections:** Toggle each collapsible section open and closed rapidly three times — verify no layout shift in charts above/below, no "ResizeObserver loop" errors in console
|
||||
- [ ] **Empty states:** Load the dashboard with a budget that has zero actual amounts — verify charts handle `pieData.length === 0` and empty bar data without rendering broken empty SVGs
|
||||
- [ ] **Long category names:** Enter a category named "Wiederkehrende monatliche Haushaltsausgaben" (long German string) — verify it doesn't overflow card boundaries or truncate without tooltip
|
||||
- [ ] **Currency formatting:** Verify EUR formatting (€1.234,56) and USD formatting ($1,234.56) both display correctly in chart tooltips and summary cards when locale is switched in Settings
|
||||
- [ ] **Responsive at 1024px:** View the dashboard at exactly 1024px viewport width — the breakpoint where desktop layout switches — verify no horizontal overflow or chart sizing issues
|
||||
- [ ] **Auto-create:** Verify that visiting the dashboard twice in quick succession (e.g., double-clicking the nav link) creates exactly one budget, not two — check the budgets list after each scenario
|
||||
- [ ] **Auto-create currency:** Create a test account with currency set to USD in Settings, then visit the dashboard — verify the auto-created budget shows USD, not EUR
|
||||
- [ ] **Wizard re-run:** Complete the wizard on a fresh account, then manually navigate back to the wizard URL — verify it does not create duplicate categories
|
||||
- [ ] **Existing user path:** Log in with an account that has v1.0 template items configured — verify the wizard does NOT appear, the existing template is preserved, and a budget is auto-created correctly
|
||||
- [ ] **Wizard refresh:** Start the wizard, reach step 3, refresh the browser — verify state is restored from localStorage and the wizard resumes at step 3
|
||||
- [ ] **Quick-add redirect:** Navigate directly to `/quick-add` after the page is removed — verify a redirect to `/budgets` (or equivalent) occurs, not a blank page or 404
|
||||
- [ ] **Empty template budget:** Remove all items from the template, then navigate to the dashboard — verify no crash, a meaningful empty state is shown, and no empty budget is silently created
|
||||
- [ ] **Token rework regression:** After changing `--radius` or category color tokens, navigate through all 9 pages (dashboard, categories, template, budget list, budget detail, settings, login, register, and any wizard page) — verify no visual regressions
|
||||
- [ ] **German locale wizard:** Complete the entire wizard flow in German locale — verify no raw translation keys appear and all pre-filled item names are localized
|
||||
- [ ] **Design consistency:** Navigate between the newly redesigned pages and the pages not yet touched in the current phase — verify no jarring visual inconsistencies at page transitions
|
||||
|
||||
---
|
||||
|
||||
@@ -247,12 +281,12 @@ There is no design system enforced at the component level. shadcn/ui components
|
||||
|
||||
| Pitfall | Recovery Cost | Recovery Steps |
|
||||
|---------|---------------|----------------|
|
||||
| Chart re-render performance discovered in production | LOW | Add `useMemo` wrappers to chart data transforms; wrap chart components in `React.memo`; requires no visual changes |
|
||||
| Color accessibility failures discovered post-launch | MEDIUM | Audit all semantic color uses with WCAG checker; update `index.css` CSS variable values; may require minor component changes for icon-supplement approach |
|
||||
| i18n key regressions across multiple pages | MEDIUM | Run `i18next-scanner` to enumerate all missing German keys; systematically add translations; no code changes needed |
|
||||
| Design inconsistency across pages after all pages are shipped | HIGH | Requires extracting shared component abstractions retroactively and refactoring all pages — avoid by establishing components in first phase |
|
||||
| `height: 0 → auto` collapsible causing jank discovered mid-phase | LOW | Replace toggle logic with Radix `Collapsible` and add CSS keyframe animation — isolated to the collapsible component, no broader refactor needed |
|
||||
| `ResponsiveContainer` ResizeObserver loop in multi-chart layout | LOW | Add `debounce={50}` prop to each `ResponsiveContainer` — one-line fix per chart instance |
|
||||
| Duplicate budgets from concurrent auto-create | MEDIUM | Write a cleanup query to find duplicate `(user_id, start_date)` pairs and delete the empty duplicates; add the unique constraint; patch the auto-create to use upsert |
|
||||
| Wizard created duplicate categories | MEDIUM | Deduplicate categories by `(user_id, name)` — merge any budget_items or template_items pointing to the duplicate, then delete the duplicate category rows |
|
||||
| `setup_completed` flag never set for v1.0 users — wizard shows on login | LOW | Write a Supabase migration that sets `setup_completed = true` for any user with `template_items.count > 0`; deploy before releasing v2.0 |
|
||||
| Token rework broke 3 pages discovered post-merge | MEDIUM | Revert the token commit if pages are too broken to patch quickly, or fix page-by-page with a visual regression pass; establish page-level snapshot tests |
|
||||
| Quick-add route removed without redirect — users hitting 404 | LOW | Add `<Route path="quick-add" element={<Navigate to="/budgets" replace />} />` — one-line fix; deploy immediately |
|
||||
| Auto-budget uses wrong currency for existing users | LOW | Add a one-time correction query: update budgets created since v2.0 launch that have EUR currency to the user's profile currency where they differ; patch the auto-create to read profile currency |
|
||||
|
||||
---
|
||||
|
||||
@@ -260,36 +294,28 @@ There is no design system enforced at the component level. shadcn/ui components
|
||||
|
||||
| Pitfall | Prevention Phase | Verification |
|
||||
|---------|------------------|--------------|
|
||||
| Recharts re-render on every parent state change | Dashboard charts phase — add memoization before wiring data | React DevTools Profiler: chart sub-components should not appear in flame graph on tooltip hover |
|
||||
| CSS variable scope / dark mode chart color gaps | Design tokens phase (first) | Inspect SVG `fill` in DevTools; toggle dark mode and visually verify all chart colors resolve |
|
||||
| Collapsible layout shift and ResizeObserver cascade | Dashboard collapsible sections phase | Toggle all sections rapidly; check console for ResizeObserver errors; check DevTools Performance for layout thrash |
|
||||
| Color accessibility failures | Design tokens phase — define accessible palette upfront | Run each color pair through WCAG contrast checker; use DevTools "Emulate vision deficiency" filter |
|
||||
| i18n key regressions | Every phase — enforce dual-language commit rule from the start | Run `i18next-scanner` before marking any phase complete; switch to German locale and navigate all changed pages |
|
||||
| Design inconsistency across page refreshes | Design foundation phase — create shared components before any page work | Code review: flag any Tailwind color/spacing classes that are not semantic tokens; check that all pages use shared `<SectionHeader>` / `<StatCard>` etc. |
|
||||
| Duplicate budget creation on concurrent auto-create | Auto-budget creation phase — add DB unique constraint before any auto-create code | Verify `(user_id, start_date)` unique constraint exists in migration; test double-navigation scenario |
|
||||
| Wizard state lost on refresh | Wizard setup phase — define localStorage persistence strategy in design | Refresh browser at step 3; verify wizard resumes at step 3 |
|
||||
| Design token rework breaks existing pages | Design system foundation phase — token commit is isolated and followed by full-app visual pass | All 9 pages visually checked after every token change |
|
||||
| Quick-add removal breaks nav/bookmarks | Page consolidation phase — redirect added in same commit as route removal | Navigate to `/quick-add` directly; verify redirect |
|
||||
| Auto-budget ignores user currency | Auto-budget creation phase — profile dependency in auto-create spec | Test with USD-configured account; verify budget currency |
|
||||
| Wizard creates duplicate categories on re-run | Wizard setup phase — idempotency specified as acceptance criteria | Run wizard twice; verify no duplicates in Categories page |
|
||||
| CSS variable rename breaks Recharts fills | Design system foundation phase — grep for all variable references before rename | Open dashboard after any token rename; inspect all chart fills |
|
||||
| Empty template edge case shows empty dashboard | Auto-budget + wizard phases — explicit first-run gate with `setup_completed` | Test "skip wizard" path; verify empty state shown, not silent empty budget |
|
||||
| First-run gate triggers for existing v1.0 users | Wizard setup phase — migration to set `setup_completed` for users with existing template items | Log in with a v1.0 account; verify wizard is skipped |
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
- [Recharts performance guide (official)](https://recharts.github.io/en-US/guide/performance/)
|
||||
- [Recharts deep-compare issue #281](https://github.com/recharts/recharts/issues/281)
|
||||
- [Recharts ResizeObserver loop issue #1770](https://github.com/recharts/recharts/issues/1770)
|
||||
- [Recharts ResponsiveContainer API](https://recharts.github.io/en-US/api/ResponsiveContainer/)
|
||||
- [Improving Recharts performance (Belchior)](https://belchior.hashnode.dev/improving-recharts-performance-clp5w295y000b0ajq8hu6cnmm)
|
||||
- [Tailwind v4 dark mode docs](https://tailwindcss.com/docs/dark-mode)
|
||||
- [shadcn/ui Tailwind v4 guide](https://ui.shadcn.com/docs/tailwind-v4)
|
||||
- [Tailwind v4 migration breaking changes discussion](https://github.com/tailwindlabs/tailwindcss/discussions/16517)
|
||||
- [shadcn theming with Tailwind v4 and CSS variables (Goins)](https://medium.com/@joseph.goins/theming-shadcn-with-tailwind-v4-and-css-variables-d602f6b3c258)
|
||||
- [WCAG 2.1 SC 1.4.11 Non-text Contrast](https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast.html)
|
||||
- [WCAG SC 1.4.3 Contrast Minimum](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html)
|
||||
- [WebAIM contrast checker](https://webaim.org/resources/contrastchecker/)
|
||||
- [Radix UI Collapsible primitive](https://www.radix-ui.com/primitives/docs/components/collapsible)
|
||||
- [i18next missing key detection discussion](https://github.com/i18next/i18next/discussions/2088)
|
||||
- [Fintech dashboard design (Merge Rocks)](https://merge.rocks/blog/fintech-dashboard-design-or-how-to-make-data-look-pretty)
|
||||
- [Dashboard UX design principles (Smashing Magazine)](https://www.smashingmagazine.com/2025/09/ux-strategies-real-time-dashboards/)
|
||||
- Existing codebase analysis: `src/pages/DashboardPage.tsx`, `src/lib/palette.ts`, `src/index.css`, `.planning/codebase/CONCERNS.md`
|
||||
- Direct codebase analysis: `src/hooks/useBudgets.ts` (generateFromTemplate), `src/hooks/useTemplate.ts` (getOrCreateTemplate), `supabase/migrations/004_budgets.sql`, `supabase/migrations/005_quick_add.sql`, `src/lib/types.ts`, `src/App.tsx`, `src/components/AppLayout.tsx`
|
||||
- TanStack Query mutation patterns: known double-invocation behavior in React 18 Strict Mode (`useEffect` firing twice)
|
||||
- Supabase upsert documentation: INSERT ... ON CONFLICT DO NOTHING / DO UPDATE pattern
|
||||
- React Router v6 redirect patterns for deprecated routes
|
||||
- localStorage-based wizard state persistence: common pattern in onboarding flow implementations
|
||||
- PostgreSQL unique constraint behavior on concurrent inserts
|
||||
|
||||
---
|
||||
|
||||
*Pitfalls research for: SimpleFinanceDash UI overhaul (React + Recharts + Tailwind v4 + shadcn/ui)*
|
||||
*Researched: 2026-03-16*
|
||||
*Pitfalls research for: SimpleFinanceDash v2.0 — wizard setup, auto-budget creation, design system rework, page consolidation*
|
||||
*Researched: 2026-04-02*
|
||||
|
||||
Reference in New Issue
Block a user