Skip to content

Dashboard Pages v2#6262

Merged
RobertJoonas merged 42 commits intomasterfrom
v2-dashboard-pages
May 6, 2026
Merged

Dashboard Pages v2#6262
RobertJoonas merged 42 commits intomasterfrom
v2-dashboard-pages

Conversation

@RobertJoonas
Copy link
Copy Markdown
Contributor

@RobertJoonas RobertJoonas commented Apr 20, 2026

Instructions to reviewer(s):

I highly recommended to review this PR commit-by-commit -- I've renamed files in separate commits to give the opportunity to see the old and new component diff side by side.

Changes (code-structure oriented description)

  • IndexBreakdown (aka ListReport v2) - for the 9-entry breakdowns on the dashboard index page
    • Caches responses via TanStack useQuery
    • Disables FlipMove animations for any update that's not caused by a realtime "tick"
    • Replaces the broken FadeIn component with a simple set of Tailwind classes. The animation duration is set to 300ms to make it noticeable.
  • DetailsBreakdown (aka BreakdownModal v2) - for details views
    • uses a new usePaginatedQueryAPI hook which uses a StatsQuery object in its queryKey rather than a DashboardState.
    • afterFetchData props -> onDataReady (Same as for IndexBreakdown. Instead of a separate function for next page vs initial page, the single handler now receives all pages of results which will be needed in the conversions report to determine whether revenue metrics should be displayed or not)
    • Decides metric widths on its own, rather than relying on the Metric class.
  • Both Index and DetailsBreakdowns now use an onDataReady callback instead of afterFetchData and afterFetchNextPage -- need to fire the callback when response is read from cache too, not only after a real fetch call)
  • Avoid extra state for apiResponse.query and apiResponse.meta by letting TanStack query cache both as part of the response.
  • Both Index and DetailsBreakdowns now use a shared ColumnConfiguration for defining columns. In order to generate columns, actual data is needed. Unlike the legacy ColumnConfiguration, the new version enforces both renderLabel and renderCell as functions, therefore leaving the rendering components (BreakdownTable and IndexBreakdownRenderer) dumb about any special cases around dimension/metric values/labels.
  • Define metric labelling logic in metrics.ts. Note: it is already extended to components that are not yet migrated (e.g. conversions). Currently dead code but will be used soon.
  • Move pages / entry-pages / exit-pages to the new components (both detailed and index reports)
  • Add external link icon (the one that shows on row hover) to pages/entry-pages/exit-pages details views (currently only available in index-breakdowns, i.e. ListReports).

Recap of user facing changes

  • Add external link icon (the one that shows on row hover) to pages/entry-pages/exit-pages details views (currently only available in index-breakdowns, i.e. ListReports).
  • Improvements to Top Pages, Entry Pages, and Exit Pages on the dashboard index page:
    • Leverage cache to avoid extra fetching and make the UX faster
    • Make the fade-in animation work
    • Disable FlipMove animations when update not caused by a realtime tick -- e.g. currently on production, if you change the dashboard period from 28d -> realtime, FlipMove animation triggers. This PR disables it.

Tests

  • Automated tests have been added

Changelog

  • This PR does not make a user-facing change

Documentation

  • Docs have been updated

Dark mode

  • The UI has been tested both in dark and light mode

Base automatically changed from main-graph-v2-backend to master April 20, 2026 09:17
@RobertJoonas RobertJoonas mentioned this pull request Apr 20, 2026
8 tasks
@RobertJoonas RobertJoonas force-pushed the v2-dashboard-pages branch from 05e3235 to fc2c551 Compare May 4, 2026 09:48
@RobertJoonas RobertJoonas marked this pull request as ready for review May 4, 2026 14:28
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Preview environment👷🏼‍♀️🏗️
PR-6262

@RobertJoonas RobertJoonas requested review from a team and apata May 4, 2026 14:55
- instead of importing Metric from types/query-api.d.ts, define a Metric type in metrics.ts (including all internally available metrics) and use that as the single Metric type everywhere
- add metric labelling logic in metrics.ts
- move MetricValue type from fetch-main-graph.ts -> api.ts, and use it for Top Stats metric value as well
This commit introduces use-order-by-legacy.ts and use-order-by-legacy.test.ts
files as copies of the current modules. The intent is to make the diff easier
to review -- all components currently using the hook will use the legacy variant
instead, and the actual file will change into v2 in the following commit.
* breakdown-modal.tsx -> breakdown-modal-legacy.tsx
* breakdown-table.tsx -> breakdown-table-legacy.tsx
* table.tsx -> table-legacy.tsx

...and use the legacy variants everywhere. The existing files will be
transformed into v2 in the following commit.
- Cache whole api responses (including meta and query), not only results
- AfterFetchData & afterFetchNextPage -> onDataReady. The function now also runs when fetch is a cache hit.
- Avoid usePaginatedQueryAPI for IndexBreakdown. Unnecessary complexity because pagination is not needed. Instead let IndexBreakdown call useQuery directly
- add order by [dim, asc] to Index and DetailsBreakdowns
- Define pagination field in StatsQuery and ReportParams
Comment thread assets/js/dashboard/stats/metrics.ts
Comment thread assets/js/dashboard/stats/metrics.test.ts Outdated
Comment thread assets/js/dashboard/stats/graph/fetch-main-graph.ts Outdated
Comment thread assets/js/dashboard/stats/modals/details-breakdown.tsx
Comment thread assets/js/dashboard/hooks/use-order-by.ts Outdated
Comment thread assets/js/dashboard/hooks/use-order-by.ts Outdated
(orderBy.length
? Object.fromEntries(orderBy)
: Object.fromEntries(defaultOrderBy),
: Object.fromEntries(defaultOrderBy)) as Record<Metric, SortDirection>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, non-blocking: Casting ( as ...) should ideally be avoided. If we declare the const to be of this type (const orderByDictionary: Record<Metric, SortDirection> = ...), we get Typescript compile errors when the const isn't set to this value, which most of the times is useful. Casting is a bit of an escape hatch, to be used for situations when the compiler can't possibly know. Why didn't we need to cast before and now do? Is it because Order = [string, SortDirection] should be [Metric, SortDirection]?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting ( as ...) should ideally be avoided. If we declare the const to be of this type (const orderByDictionary: Record<Metric, SortDirection> = ...), we get Typescript compile errors when the const isn't set to this value, which most of the times is useful. Casting is a bit of an escape hatch, to be used for situations when the compiler can't possibly know.

Makes sense! Thanks for explaining!

Why didn't we need to cast before and now do? Is it because Order = [string, SortDirection] should be [Metric, SortDirection]?

Good question. Yeah I believe it was related to the Metric type but to be honest I don't remember why I added it at some point.

However, I already changed the Order type to be [string, SortDirection] rather than [Metric, SortDirection] (that's because we need to order by dimensions themselves too and there's no explicit Dimension type).

I guess since useOrderBy is only dealing with metric ordering, the correct approach would be to have two separate types:

  • OrderBy = [string, SortDirection][] in stats-query.ts
  • MetricOrderBy = [Metric, SortDirection][] in use-order-by.ts

Happy to tackle that as a follow-up if you think that makes sense. For now I've simply removed the as ... cast. 2f64b3a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I didn't even think of dimension sort. Your idea to split the type is good (and we can also import Dimensions from query-api.d.ts in the FE and do [Metric | Dimensions, SortDirection][]).

validateOrderBy(
parsed,
metrics.filter((m) => m.sortable)
metrics.filter((m) => isSortable(m))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
metrics.filter((m) => isSortable(m))
metrics.filter(isSortable)

This is a bit shorter

Copy link
Copy Markdown
Contributor Author

@RobertJoonas RobertJoonas May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed there's one more instance of the same thing in the same file that can be shortened

Copy link
Copy Markdown
Contributor

@apata apata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! I believe it's very close to being mergable, with the only blocker for my approval being the page urls now wrapping to multiple lines in details views. The other things we discussed in sync, like using the queryKey to build the statsQuery or maintaining pages test suite for longer aren't blockers from my POV.

Using up exactly all the space enables horizontal scroll on Safari which
we don't want on destkop.
@RobertJoonas RobertJoonas added this pull request to the merge queue May 6, 2026
Merged via the queue into master with commit 515ff9e May 6, 2026
28 of 40 checks passed
@RobertJoonas RobertJoonas deleted the v2-dashboard-pages branch May 6, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants