* feat(dashboard): zoom into cashflow sankey categories
Click a category node on the dashboard cashflow Sankey to focus on it and
its descendants only; a back button restores the full view. Clicking the
Cash Flow node zooms to the expense (outbound) side.
- Pure utility (app/javascript/utils/sankey_zoom.js) computes the
descendant subgraph from a clicked node, with direction inferred by
reachability from the cash flow node (outbound for expense, inbound
for income).
- Stable node ids emitted from the controller so the JS can identify
nodes across re-renders.
- Stimulus controller adds chart + zoomOutButton targets, fade
transition, and only sets a pointer cursor when a node has children.
- Node:test coverage for expense, income, cash-flow, and malformed-data
cases; \"type\": \"module\" added to package.json so the .js util is
ESM-compatible under Node.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(dashboard): extract cashflow sankey chart partial
Deduplicate sankey chart markup between inline and expanded dialog views,
and reset zoom state when chart data changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(js): rename sankey_zoom util to .mjs to drop project-wide ESM flag
Removes "type": "module" from package.json to avoid implicitly switching
every .js file in the project to ESM (a future footgun for any .js config
file added by Biome, Vite, etc.). Renames the utility to .mjs so node --test
can import the ES module directly, and adds an explicit importmap pin since
pin_all_from only globs .js/.jsm.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(assets): register .mjs MIME type for Propshaft
Propshaft derives Content-Type from Mime::Type.lookup_by_extension, which
returns nil for :mjs by default. Browsers refuse to execute ES modules
served with an empty Content-Type, breaking the sankey_zoom util loaded
via importmap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: move safe-area padding from body/HTML to navbars. Add script to compute app height dynamically.
* fix: Initialize sankey tooltip with top-0 to avoid overflow
* fix: add fallback to HTML height
* fix: properly set bottom spacing and use position fixed for bottom navbar
* fix: move viewport controller initialization
* feat: implement expandable view for cashflow sankey chart
* refactor: migrate cashflow dialog sizing to tailwind utilities
* refactor: declarative draggable restore on cashflow dialog close
* refactor: localized title and use Tailwind utilities
* refactor: update dialog interaction especially on mobile
* refactor: add global expand text to localization
* fix: restore draggable immediately after dialog close
* Whitespace noise
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* feat: Add subcategory breakdown to Cash Flow and Reports
Implements Discussion #546 - adds hierarchical category/subcategory
visualization to both the Sankey chart and Reports breakdown tables.
Sankey chart changes:
- Income: subcategory → parent category → Cash Flow
- Expense: Cash Flow → parent category → subcategory
- Extracted process_category_totals helper to DRY up income/expense logic
Reports breakdown changes:
- Subcategories display nested under parent categories
- Smaller dots and indented rows for visual hierarchy
- Extracted _breakdown_table partial to eliminate duplication
* fix: Dynamic node padding for Sankey chart with many nodes
- Add dynamic nodePadding calculation to prevent padding from dominating
chart height when there are many subcategory nodes
- Extract magic numbers to static constants for configuration
- Decompose monolithic #draw() into focused methods
- Consolidate duplicate tooltip/currency formatting code
- Modernize syntax with spread operators and optional chaining
* fix: Hide overlapping Sankey labels, show on hover
- Add label overlap detection by grouping nodes by column depth
- Hide labels that would overlap with adjacent nodes
- Show hidden labels on hover (node rectangle or connected links)
- Add hover events to node rectangles (not just text)
* fix: Use deterministic fallback colors for categories
- Replace Category::COLORS.sample with Category::UNCATEGORIZED_COLOR
for income categories in Sankey chart (was producing different colors
on each page load)
- Add nil color fallback in reports_controller for parent and root
categories
Addresses CodeRabbit review feedback.
* fix: Expand CSS variable map for d3 color manipulation
Add hex mappings for commonly used CSS variables so d3 can manipulate
opacity for gradients and hover effects:
- var(--color-destructive) -> #EC2222
- var(--color-gray-400) -> #9E9E9E
- var(--color-gray-500) -> #737373
* test: Add tests for subcategory breakdown in dashboard and reports
- Test dashboard renders Sankey chart with parent/subcategory transactions
- Test reports groups transactions by parent and subcategories
- Test reports handles categories with nil colors
- Use EntriesTestHelper#create_transaction for cleaner test setup
* Fix lint: use Number.NEGATIVE_INFINITY
* Remove obsolete nil color test
Category model now validates color presence, so nil color categories
cannot exist. The fallback handling in reports_controller is still in
place but the scenario is unreachable.
* Update reports_controller.rb
* FIX trade category
---------
Co-authored-by: sokie <sokysrm@gmail.com>
* Enhance cash flow dashboard with new cash flow period handling and improved Sankey diagram rendering. Update D3 and related dependencies for better performance and features.
* Fix Rubocop offenses
* Refactor Sankey chart controller to use Number.parseFloat for value formatting and improve code readability by restructuring conditional logic for node shapes.