Every number on the blog admin overview page (/admin/dashboard/blog), read from the database.
load/1 gathers the whole page in one pass — the five KPI cards, the eight section cards, the
"This Month" panel and the recent-activity list — and calendar/2 reads one month of the editorial
calendar. Both are pure reads returning plain numbers and records; every label, delta and
abbreviation is the page's business, not this module's.
Every figure is read as user through MishkaBlog.Dashboard.Scope: a master (a user whose
site_id is nil) through each resource's master read, counting every site, and a site user
through the tenant read under its own site, counting only that one. The resources' policies decide
what the viewer reads. The two join resources — MishkaBlog.PostView and MishkaBlog.TeamMember
— have no master read and carry no site_id; both viewers read them through :read, which their
policies keep to the viewer's site, joined to the post or team they belong to
(not is_nil(post.id)), so a row of an archived post or team is not counted by anybody.
What a viewer's figures are read from
Each figure is read only from the blog area the viewer holds (areas/1): posts, views, bloggers,
collections, tags, teams and auto posting from admin:dashboard:blog:post, comments from
admin:dashboard:blog:comment — the permissions those resources' policies ask for. A read of an
area the viewer does not hold is refused and counted as no data, so its counts are zero, its rows
are missing from :activity, and an engagement rate — comments over views — is zero unless both
are held. The calendar is a post holder's.
Counts are deliberately many small Ash.count/2 calls rather than one hand-written query: each
one goes through the resource's own read action, so archival, base filters, multitenancy and
policies stay the resources' business. The caller is expected to run this off the render — see
MishkaBlogWeb.Live.Admin.Dashboard.Home.Index, which loads it in a start_async/3.
See MishkaBlog.Dashboard.Widgets.DraftsScheduled for the same idiom at widget scale.
Summary
Functions
Which blog areas user holds on the dashboard: :post for admin:dashboard:blog:post,
:comment for admin:dashboard:blog:comment. nil, or a user holding neither, holds none.
Reads the editorial calendar for the month containing date, as user.
Reads every statistic the overview page shows, as user (see MishkaBlog.Dashboard.Scope).
The day a post belongs on in the calendar — when it went out, or when it is due to.
Types
Functions
Which blog areas user holds on the dashboard: :post for admin:dashboard:blog:post,
:comment for admin:dashboard:blog:comment. nil, or a user holding neither, holds none.
Reads the editorial calendar for the month containing date, as user.
Returns %{month: Date.t(), cells: [cell]} where the cells are the 42 squares of a six-week grid.
A cell outside the month carries date: nil and no posts; an in-month cell carries the published
posts whose published_at and the scheduled posts whose scheduled_publish_at land on that day.
Only those two states appear: a draft has no place on an editorial calendar even when it carries a
leftover published_at, and an archived post has already left it.
Reads every statistic the overview page shows, as user (see MishkaBlog.Dashboard.Scope).
Returns a flat map of numbers plus :activity, the newest few blog records as
%{kind, title, status, at} — kind being :post, :comment or :blogger, and status the
record's own status where it has one (nil for a blogger) so the page can word the entry without
claiming a draft was published.
The day a post belongs on in the calendar — when it went out, or when it is due to.