Skip to content

Show report insights in the dashboard widget - #689

Open
helloDanuk wants to merge 14 commits into
statamic:7.xfrom
helloDanuk:widget-insights
Open

helloDanuk wants to merge 14 commits into
statamic:7.xfrom
helloDanuk:widget-insights

Conversation

@helloDanuk

Copy link
Copy Markdown
Contributor

This follows up on our conversation about the dashboard widget, where the feedback was that the current one is a little basic and that bringing the insights to the forefront is a good idea. Here's the PR for it – I hope you like the proposal. I've put a lot of care into it, but the code itself was written by Claude.

It rebuilds the seo_pro dashboard widget. Until now it showed the score of the latest report and nothing else. It now also tells you when that report was generated, whether it's still up to date, and which rules didn't pass – so you can see from the dashboard what there is to do.

The widget type stays seo_pro, so existing cp.php entries keep working.

Before

screenshot-old

After

The widget has two forms: with the rules table (the default) and without it ('show_rules' => false). The screenshots start without the table, to show the basic parts first. The table follows further down.

No report yet

The empty state shows the same sentence and the same button as before. Only the details changed: the button is one size smaller, and the button in the header is now labelled "Reports".

screenshot-1

Date and score

The date of the latest report, a sentence about its age, and the score as a ring. Narrow widgets stack everything. Once a widget is wider than 42rem, the ring moves to the left. On a typical desktop that means widths of 66 and up get the wide layout, while 50 and below stay stacked. The layout follows the width the widget actually has on screen, not the configured value.

screenshot-2-light screenshot-2-dark

How fresh the report is

The sentence below the date says when the report was generated and how many entries have been edited since. After 30 days (configurable) the widget stops counting and suggests generating a new report instead.

The ring links to the report and gets a subtle fill on hover.

screenshot-3

The number of edited entries comes from a query on updated_at, over the same content the report covers (entries without redirects, and terms). If statamic.system.track_last_update is disabled, that second sentence is left out rather than showing a wrong number.

The ring

The ring uses the same thresholds as the report (red below 70, amber below 90, green from 90). The arc ends with a small gap to the track, and the last bit of track is dropped once it would be too short to read.

screenshot-4

This also fixes a small bug in the current widget: at exactly 70% and exactly 90% the score had no color (> 70 && < 90 and > 90).

The rules

This is the default view. Below the score, the widget lists the rules that didn't pass, in the same order as the report, with the number of affected pages. Passed rules are summed up in the last row. Each row opens the report filtered by that rule.

The rule descriptions in the report are full sentences, which don't fit into a table row. The widget therefore has its own short names ("Title length", "Unique meta description", …). A rule it doesn't know yet falls back to the description. The (i) next to the heading explains that failed rules lower the score and warnings don't.

The site name rule has no list of pages, so its row links to the site defaults instead – if the user is allowed to edit them. On multisite it opens the first site that has no name.

screenshot-5a-light screenshot-5b-light screenshot-5c-light screenshot-5b-dark

Narrow widgets

In narrow widgets, long rule names are truncated and shown in full in a tooltip.

screenshot-6

Translations

All new strings are in lang/en, and I've added German (de, de_CH). Other languages fall back to English for the new strings. The German strings use a non-breaking space between a number and the word that follows it (e.g. "Vor 5 Tagen erstellt."), so a line never breaks between the two. That's what the invisible characters in the diff are.

Options

[
    'type' => 'seo_pro',
    'width' => 50,
    'title' => 'SEO Check', // Defaults to "SEO Pro".
    'show_rules' => false, // Defaults to true. Set to false to only show the date and the score.
    'stale_after_days' => 60, // Defaults to 30. After this many days, the widget suggests generating a new report.
],
  • title sets the widget's title.
  • show_rules hides the rules table, leaving the date, the score and the buttons.
  • stale_after_days is the age at which a report counts as outdated: the sentence below the date changes to "A lot has probably changed since.", and a "New Report" button appears next to "View Report".

The rules table is shown by default, which makes the widget taller on existing dashboards. If you'd rather keep the current footprint, I'm happy to flip show_rules to opt-in.

Things worth knowing

  • Permissions: The widget is now only rendered for users who can view seo reports, like the Recent Errors widget does with its permission. Before, everyone with access to the dashboard saw the score, and without that permission the links led to a 403.
  • Dates and times use the CP's display timezone (statamic.system.display_timezone), so "today" and "yesterday" match what the user sees elsewhere.
  • Status icons: The table uses the same icons as the report, but with its own colors including dark mode variants, instead of reusing StatusIcon.vue, which has none. I'd like to follow up with a small PR that brings these colors to the report, so that both look the same and the widget can reuse StatusIcon.vue.
  • Legacy reports without a score are treated like "no report yet" instead of showing 0%.
  • The header button says "Reports" rather than "View All" as in the Recent Errors widget, because the widget's title "SEO Pro" doesn't say what "all" would refer to.
  • Performance: The widget finds the latest report with Report::latestGenerated(), as before. That call reads the report.yaml of every stored report, which hasn't changed with this PR. What's new is one count query per content type on updated_at, and only while the report isn't stale. latestGenerated() could be made cheaper by walking the report folders from the newest one and stopping at the first generated report, instead of loading them all. I've left that untouched, since it's outside the scope of this PR – just mentioning it in case it's worth picking up.
  • No new dependencies, and the PHP side only uses the public API of Report.
  • Tests: 26 new tests cover permissions, the props with and without a report, the rule rows, the freshness sentence, the change count, and the multisite cases.
  • Docs: The widget section in DOCUMENTATION.md is updated.

helloDanuk and others added 14 commits October 1, 2026 23:28
Adds the report date, a freshness sentence and the open rules with
affected page counts to the widget props. Options: title, show_rules,
stale_after_days.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Covers permissions, the props with and without a report, legacy
reports, rule rows, the freshness sentence and the change count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wide widgets place the ring next to the rules (container query).
The rules table links each open rule to the filtered report and
can be hidden with show_rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rule has no page list to filter, so the row links to where the
site name is set, if the user may edit the site defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On multisite, the site name link opens the first site that has no
name. Adds multisite tests for the link and the change count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Generate Report" didn't tell that a report already exists, and
"View reports" is the permission label, not a button label. Also
drops the arrow, which no other button in SEO Pro has.

The header button says "Reports" instead of "View All" like the
Recent Errors widget: that widget's title names what "all" refers
to, while this one is titled "SEO Pro" (or whatever is configured),
and it already has a "View Report" button below.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new sentence was only available in English and German, and the
icon repeated the one in the widget header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rule names are cut off in narrow widgets. The tooltip only appears
when the name doesn't fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No other text button in SEO Pro has a leading icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It used a hardcoded blue instead of the CP's focus color.

Co-Authored-By: Claude Opus 5.5 <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.

1 participant