Implement Sankey report for spent and budgeted money#7220
Implement Sankey report for spent and budgeted money#7220emiltb wants to merge 83 commits intoactualbudget:masterfrom
Conversation
Auto-generated by VRT workflow PR: actualbudget#6068
…/actual into sankey-graph-report
Auto-generated by VRT workflow PR: actualbudget#6068
…/actual into sankey-graph-report
Auto-generated by VRT workflow PR: actualbudget#6068
Auto-generated by VRT workflow PR: actualbudget#6068
Now better conforms with components from other reports, e.g. by reusing Header Makes it possible to display a period longer than one month.
… view across months
|
Could there be an "other" endpoint per group instead of one global other? |
Yes, it could be done either way. I chose the single Other group to limit the number of nodes at the last layer. Each node needs some extra vertical space, so having more nodes ends up reducing the height of the nodes, which might be hard to discern on phone screens. I guess it's also a question of what to optimize most for. However, the current solution causes the links to cross to reach the Other node, which might be more confusing and/or less pleasing to look at. |
|
I've pushed quite a few updates this evening.
Per category Other + Per category sort I think the Sankey report covers everything that I personally would like to see from it right now, but I'm of course very open to feedback. @youngcw I'm unsure what the exact process is from here. Is there any chance that this could make it into the 2026.4.0 release as an experimental feature, so feedback could be gathered from a larger group of users? |
|
This looks really good, especially with color coding per category group! Thank you everyone for picking this feature up once more!!! |
|
The "other" option button should read "per group other" not per category since its one other per group. Do you have a preferred option on the other grouping? I feel like the per group option makes the most sense and maybe should be the only option. |
I think both approaches are valid and could suit the needs of individual users. My preferred option would probably be to simplify the choice and condense it to a single option of:
This could make sense since the other combinations (global sort + per group other, group sort + global other) aren't as clean and sensible. |
I think that makes sense, one setting instead of two. It may also be good to add a sort option for budget order. That may be hard with the other endpoint though. And the setting for sorting should read "sort per group". There aren't sub-categories in Actual, only categories and groups of categories. |
I have condensed the sorting to a single option as discussed. It now has options of "Sort per group", "Sort all" and "Sort as budget". For the "Sort as budget" it gets the ordering directly from the |
|
The only thing left I think is that the dashboard card still is weird. It looks good otherwise |
I have implemented a fix, which dynamically adjusts how many groups are shown on the card, dependent on the height of the card. It will now show 1 + Other if the height is very small and up to all groups, if there is room. |
|
That looks better. Ill look over the code, but I think its ready. |




Description
This PR adds a Sankey chart report that can visualise the money flow in two ways: spent and budgeted. It shows either the total spend amount for a time period and how it flows through categories and subcategories, or similarly for the budgeted amount how funds are allocated. This is a new way to visualise money flows, making it easy for users to compare the relative amounts they are spending or allocating in their budgets.
I derived this from the previous work in #6068. Notable changes from that PR are:
Spent view:

Budgeted view:

I'm looking for feedback and ideas on what to improve from here on. One specific thing that should be improved, that I would like to discuss the implementation of is budgets with many small categories, which clutter the view. Ideally we should take the smallest subcategories and lump them together in an 'Other' category, which could show what data they contain in a tooltip. However, I'm unsure what the best approach is to select which categories goes into 'Other'. Options include:
Related issue(s)
This fixes #1716, which has been addressed several times (#1919, #4156 and lately in #6068). I've continued the recent branch from #6068 and provided my own take on some of the outstanding issues.
Testing
I tested the visuals of the graph using the provided test budget in Actual and using my own Budget (with significantly more data). I also updated the test added previously in #6068 to reflect my changes, but I'm unsure how to update and run the visual tests, and was unable to figure that out from the Actual docs.
Checklist
Bundle Stats
View detailed bundle stats
desktop-client
Total
Changeset
node_modules/recharts/es6/chart/Sankey.jssrc/components/reports/reports/Sankey.tsxsrc/components/reports/spreadsheets/sankey-spreadsheet.tssrc/components/reports/graphs/SankeyGraph.tsxsrc/components/reports/reports/SankeyCard.tsxnode_modules/es-toolkit/dist/function/debounce.jsnode_modules/es-toolkit/dist/compat/function/debounce.jsnode_modules/es-toolkit/dist/compat/math/sumBy.jsnode_modules/es-toolkit/dist/array/maxBy.jsnode_modules/es-toolkit/dist/compat/math/maxBy.jsnode_modules/es-toolkit/dist/compat/function/throttle.jsnode_modules/es-toolkit/compat/throttle.jsnode_modules/es-toolkit/compat/maxBy.jsnode_modules/es-toolkit/compat/sumBy.jshome/runner/work/actual/actual/packages/component-library/src/icons/v1/List.tsxnode_modules/recharts/es6/component/ResponsiveContainer.jsnode_modules/recharts/es6/component/responsiveContainerUtils.jsnode_modules/recharts/es6/context/chartDataContext.jssrc/hooks/useFeatureFlag.tssrc/components/reports/ReportRouter.tsxnode_modules/recharts/es6/context/chartLayoutContext.jssrc/components/reports/Overview.tsxsrc/components/settings/Experimental.tsxnode_modules/recharts/es6/chart/PieChart.jsnode_modules/recharts/es6/chart/PolarChart.jsnode_modules/recharts/es6/shape/Trapezoid.jsnode_modules/recharts/es6/shape/Symbols.jssrc/components/reports/ReportLegend.tsxsrc/components/accounts/AccountEmptyMessage.tsxnode_modules/recharts/es6/cartesian/XAxis.jssrc/components/reports/spreadsheets/net-worth-spreadsheet.tssrc/components/reports/reports/BudgetAnalysis.tsxsrc/components/reports/reports/NetWorthCard.tsxsrc/components/reports/graphs/BarGraph.tsxsrc/components/reports/SaveReport.tsxsrc/components/reports/reports/NetWorth.tsxlocale/en.jsonlocale/fr.jsonlocale/ca.jsonlocale/es.jsonlocale/pt-BR.jsonlocale/de.jsonlocale/it.jsonlocale/nb-NO.jsonlocale/nl.jsonView detailed bundle breakdown
Added
No assets were added
Removed
No assets were removed
Bigger
Smaller
Unchanged
loot-core
Total
Changeset
home/runner/work/actual/actual/packages/loot-core/src/server/dashboard/app.tsView detailed bundle breakdown
Added
Removed
Bigger
No assets were bigger
Smaller
No assets were smaller
Unchanged
No assets were unchanged
api
Total
Changeset
home/runner/work/actual/actual/packages/loot-core/src/server/dashboard/app.tsView detailed bundle breakdown
Added
No assets were added
Removed
No assets were removed
Bigger
Smaller
No assets were smaller
Unchanged
cli
Total
View detailed bundle breakdown
Added
No assets were added
Removed
No assets were removed
Bigger
No assets were bigger
Smaller
No assets were smaller
Unchanged