Skip to content

feat: overview page comparing metrics and events across websites - #4514

Open
CorCvusRamboChen wants to merge 4 commits into
umami-software:devfrom
CorCvusRamboChen:overview-websites
Open

CorCvusRamboChen wants to merge 4 commits into
umami-software:devfrom
CorCvusRamboChen:overview-websites

Conversation

@CorCvusRamboChen

@CorCvusRamboChen CorCvusRamboChen commented Sep 5, 2026 •

Copy link
Copy Markdown

Refs #4513

Proposed as a dedicated page. A Dashboard widget or a Boards component would be the
cheaper place to put it, but both give one widget per website, and the whole point here is
one chart with one shared axis and one metric across several websites. Running on my own
instance. Happy to move it if you disagree; the questions left open below are smaller ones.

What

Adds an Overview page that puts several websites on one chart and one table, so an instance that
hosts a family of related sites can answer "which site grew?" without opening each website page.

  • src/app/(main)/overview/ — page + client component: website multi-select (defaults to every
    website the user can view), metric select (Views / Visitors / any custom event name found in the
    data), the standard DateFilter with an "All time" option, a BarChart with a chart-type toggle
    (stacked bars / lines / stacked area, one colour per website), and a totals table (views,
    visitors, one column per event name) with a column picker in the page header that is remembered
    per browser.
  • src/app/api/websites/overview/route.ts — GET /api/websites/overview?ids=&startAt=&endAt=&unit=&timezone=.
    Composes the existing getPageviewStats, getSessionStats, getEventStats, getWebsiteStats
    and getWebsiteDateRange per website. Authorisation via canViewBatchWebsites, mirroring
    /api/websites/charts, plus canViewWebsiteSection per website so a share token only gets the
    sections it shares (overview/compare for views and visitors, events for event counts).
    Each website's dates go through setWebsiteDate, like the single-website routes. Forbidden or
    unknown ids are dropped, not rejected.
  • SideNav.tsx / MobileNav.tsx — one entry each, reusing the existing label.overview key.

Why

Dashboard and Boards give one widget per website: independent charts with independent axes and no
stacking or totals, and no way to switch all of them to the same custom event. The Websites list's
sparkline is fixed to visitors over seven days. Comparing sites day by day currently means opening
each website in its own tab. See the linked issue for the full use case.

Open questions

  • "Overview" reuses an existing label; renaming it (e.g. "Compare") would mean one more key.
  • Should the column picker persist per user (database) instead of per browser (localStorage)?
  • Cap of 20 websites mirrors /api/websites/charts; fine, or should it page?

Notes for review

  • No migrations and no new dependencies; only @umami/react-zen and Chart.js. Four new
    i18n keys: label.metrics for the metric picker, and names for the three chart types so the
    icon-only toggle is not read out as bar / line / area. They are added to every locale with
    the English text as a placeholder, so check:messages reports nothing missing.
  • Works on both Prisma and ClickHouse because the route only composes existing query functions.
  • ids is capped at 20 like /api/websites/charts.
  • The event-name list and the table's event columns are derived from the response, so nothing
    needs configuring per instance.
  • The table's visitor total is the period-wide distinct count from getWebsiteStats, not a sum of
    the per-bucket sessions series, so it matches a single website's stats and does not change
    with the chart's time unit.
  • getWebsiteStats was declared as returning WebsiteStatsData[], but every branch returns
    result?.[0]; the declared type is corrected to WebsiteStatsData.
  • Rebased on current dev. tsc --noEmit and Biome are clean for the changed files; vitest run
    shows the same 3 failing tests on this branch as on dev (none in files this PR touches).

Screenshots

image

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

@RamboChenUnimelb is attempting to deploy a commit to the Umami Software Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps

greptile-apps Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The PR adds a cross-website analytics view for users who can already access those sites.

Fix the chart's timezone mismatch before merging.

Findings

  1. P1 Chart drops visits ▶

Summary

Adds an Overview page for comparing views, visitors, and custom events across websites.

  • Overview compares the same metric across selected websites.
  • The overview API returns comparable metrics for each allowed website.

The previous-review ancestry and merge-base checks did not pass, so this review used the full PR diff. All supplied previous threads were unnumbered; none require numbered ledger entries. CorCvusRamboChen acknowledged the website cap and pagination as open decisions.

Reviews (2) · Last reviewed commit: "fix(overview): respect share sections an..." · Reviewed by Greptile

Comment thread src/app/api/websites/overview/route.ts
Comment thread src/app/api/websites/overview/route.ts Outdated
Comment thread src/app/(main)/overview/OverviewPage.tsx Outdated
Comment thread src/app/(main)/overview/OverviewPage.tsx
Comment thread src/app/(main)/overview/OverviewPage.tsx
Comment thread public/intl/messages/en-US.json
RamboChenUnimelb and others added 3 commits October 14, 2026 11:00
Adds an Overview page (sidebar entry next to Websites) that shows several
websites on one chart and one totals table: multi-select of the websites the
user can view, a metric select covering Views, Visitors and any custom event
name found in the data, a chart-type toggle (stacked bars, lines, stacked
area), and the standard date filter including "All time" across the selected
sites.

Backend is a single endpoint, GET /api/websites/overview, that composes the
existing getPageviewStats / getSessionStats / getEventStats /
getWebsiteDateRange per website and is gated by canViewBatchWebsites, the same
pattern as /api/websites/charts. No migrations, no new dependencies, no new
i18n keys (reuses label.overview / label.chart); works on Prisma and
ClickHouse alike.
`labels.metrics` was never defined, so the text above the metric picker fell
back to the raw message id. `labels` is typed `Record<string, string>`, which
is why nothing caught it: a missing key is not a type error, it is `undefined`
reaching `t()` at runtime.

The chart-type toggle showed only icons and carried its internal ids -- `bar`,
`line`, `area` -- as aria-labels, so a screen reader read out the ids. They now
have names of their own. The lookup sits inside the component because `labels`
comes from the hook, not from module scope.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… once

Addresses the review on umami-software#4514.

API (/api/websites/overview):
- A share token is now held to the sections it shares, as on the
  single-website routes: views and visitors need `overview`/`compare`, event
  counts need `events`. Previously any token that included the website got
  every custom event name and count.
- Each website's filters go through setWebsiteDate, so the cloud plan's
  history limit and a website's reset date apply here too. The filters are
  built per website rather than shared across Promise.all.
- Returns a period-wide `visitors` total from getWebsiteStats.

Page:
- The table's visitor total uses that figure. Summing the per-bucket
  `sessions` series counted a visitor once per hour/day they appeared in, and
  the total changed with the chart's time unit.
- With All time selected, changing the website selection now follows the new
  websites' combined range instead of keeping the old dates.
- Custom event metric ids carry an `event:` prefix, so events named
  `pageviews` or `visitors` no longer collide with the built-in metrics.

Also:
- The four new labels are added to every locale with the English text as a
  placeholder, so `check:messages` reports nothing missing.
- getWebsiteStats was typed as returning an array, but every branch returns
  `result?.[0]`; the declared type now matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CorCvusRamboChen

Copy link
Copy Markdown
Author

Thanks for the review. All six findings are addressed in 3b9f580 (replies on each thread):

Finding Change
Hidden events become public canViewWebsiteSection per website: views/visitors need overview/compare, event counts need events
Date limits are bypassed filters built per website and passed through setWebsiteDate (no shared object across Promise.all)
Visitor totals count people twice period-wide visitors from getWebsiteStats; the table no longer sums per-bucket sessions
All time misses added history with All time active, the page follows the combined range of the current selection
Event names select wrong metrics custom event metric ids carry an event: prefix everywhere
New labels lack locale fallback the four labels exist in every locale (English placeholders); check:messages is clean

Verified locally on the branch: tsc --noEmit, biome lint on the touched files and check:messages all pass. The 20-website cap and pagination remain open as noted in the description; happy to adjust either if you have a preference.

@greptileai review

Comment thread src/app/(main)/overview/OverviewPage.tsx Outdated
useDateRange() defaulted to the browser's timezone while useDateParameters()
builds the request in the user's saved timezone. When the two fall on
different days, "Today" fetched one day's counts and drew another day's
buckets, so the chart could be empty while the table had totals. Pass the
saved timezone through, as the other report pages do.

Addresses the follow-up review on umami-software#4514.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants