The blog admin overview: five KPI cards, eight section cards, an editorial calendar and three
summary panels, every number read from the database by MishkaBlog.Dashboard.Overview.
Both reads run off the render in a start_async/3 — between them they issue thirty-odd counts, so
the page paints its labels and links first and fills the numbers in when they land. Until then
every figure is an em dash: kpi_cards/1, section_cards/1 and month_rows/1 each have a nil
clause over the same shared metadata, which is why there is no second skeleton markup to keep in
step with the real one. The calendar is only read when its panel is first opened, and again on
each month change, so a visitor who never opens it never pays for it. A calendar result is only
assigned when its month is still the selected one, so paging quickly cannot land an older read on
top of a newer one.
Everything from kpi_cards/1 down is presentation: delta/2, growth/2 and percent/2 turn raw
counts into the labels the design shows, and every number, date and time is written in the reader's
language by MishkaCmsCore.Localized. MishkaCmsCoreWeb.Live.Admin.Components.ActivityFeed
supplies the "Logs" panel and the Calendar toggle rides in its :toggles slot, so both controls
sit on one row as the design has them.
What each viewer is shown
The blog has two areas (MishkaBlog.Dashboard.Overview.areas/1): a holder of admin:dashboard:blog:post is
shown the posts, bloggers, collections, tags, teams, auto posting and views — the calendar and the
Logs panel included — and a holder of admin:dashboard:blog:comment the comments. The Engagement
figures divide comments by views, so they need both. A card, a row or a link whose area the
viewer does not hold is not drawn, and MishkaBlog.Dashboard.Overview does not read its data.
A figure counted from the view log (untracked?/1) carries a "Not tracked yet" label, and the
Auto Posting and Analytics cards (on_roadmap?/1) an "On our roadmap" one, both drawn by
roadmap_badge/1: nothing records a view yet, and those two sections are not built.
Setting the blog up, the Doctor and the kit upgrade write pages, components and a snippet, and
read the site they are for, so they need all three of admin:dashboard:runtime:page (the pages
and the page builder's snippet), admin:dashboard:runtime:component and
admin:dashboard:runtime:site.
See MishkaBlog.Dashboard.Overview for the queries.
Summary
Functions
What a recent-activity entry says happened, worded from the record's own status.
Dot colour for a recent-activity entry, one per kind.
How long before now at was, e.g. "3 minutes ago" or "yesterday"; a moment after now
reads as "now".
Fill for one calendar square: outside the month, selected, today, or an ordinary day.
The calendar sidebar's count line, e.g. "1 post on this day".
Weekday, month and day for the calendar sidebar, e.g. "Mon, Jul 20".
The +2 from yesterday line under a day counter, worded for every sign including zero.
The move between two windows as a percentage, or a word when there is nothing to compare to.
Whether areas holds every blog area in needs.
The KPI cards areas may see, as {label, icon, icon_tone, value, note, note_tone} — of the
five, those whose blog areas areas (MishkaBlog.Dashboard.Overview.areas/1) holds.
Month and year for the calendar header, e.g. "July 2026".
The rows of the This Month panel areas may see, as {label, value, value_tone}.
Tone for a figure that moved: green when it rose, red when it fell, muted when it held.
Whether title, as a section card shows it, is a section that is on the roadmap.
points per hundred as the reader's language writes a percentage, e.g. "12%".
Time of day a post went out, or is due to — the calendar sidebar's second line.
The posts sitting on date in an already-loaded calendar.
The Quick Actions rows areas may follow, as {label, icon, path, primary?}.
Callback implementation for Phoenix.LiveView.render/1.
The label drawn on a figure or a section that is not working yet.
The destination cards areas may open, as {title, description, link, icon, icon_tone, rows},
where each row is {label, value, value_tone} — of the eight, those whose blog areas areas
holds.
The word a post status is shown as.
Chip colours for a post status, shared by the calendar grid and its sidebar.
Whether label, as a KPI tile or a This Month row shows it, is a figure counted from the view log.
The calendar's column heads, Sunday first, e.g. ["Sun", "Mon", …].
Functions
What a recent-activity entry says happened, worded from the record's own status.
A post is announced by the state it is actually in — the feed is ordered by creation, so the newest post is usually a draft, and calling that "published" would tell every admin unfinished copy went live.
Dot colour for a recent-activity entry, one per kind.
@spec ago(DateTime.t(), DateTime.t()) :: String.t()
How long before now at was, e.g. "3 minutes ago" or "yesterday"; a moment after now
reads as "now".
Fill for one calendar square: outside the month, selected, today, or an ordinary day.
@spec day_count_label(non_neg_integer()) :: String.t()
The calendar sidebar's count line, e.g. "1 post on this day".
Weekday, month and day for the calendar sidebar, e.g. "Mon, Jul 20".
The +2 from yesterday line under a day counter, worded for every sign including zero.
The move between two windows as a percentage, or a word when there is nothing to compare to.
@spec holds?(MishkaBlog.Dashboard.Overview.areas(), [:post | :comment]) :: boolean()
Whether areas holds every blog area in needs.
@spec kpi_cards(map() | nil, MishkaBlog.Dashboard.Overview.areas()) :: [ {String.t(), String.t(), String.t(), String.t(), String.t(), String.t()} ]
The KPI cards areas may see, as {label, icon, icon_tone, value, note, note_tone} — of the
five, those whose blog areas areas (MishkaBlog.Dashboard.Overview.areas/1) holds.
The nil clause is what the page renders until MishkaBlog.Dashboard.Overview.load/1 returns.
Month and year for the calendar header, e.g. "July 2026".
@spec month_rows(map() | nil, MishkaBlog.Dashboard.Overview.areas()) :: [ {String.t(), String.t(), String.t()} ]
The rows of the This Month panel areas may see, as {label, value, value_tone}.
Tone for a figure that moved: green when it rose, red when it fell, muted when it held.
Whether title, as a section card shows it, is a section that is on the roadmap.
points per hundred as the reader's language writes a percentage, e.g. "12%".
Time of day a post went out, or is due to — the calendar sidebar's second line.
The posts sitting on date in an already-loaded calendar.
@spec quick_actions(MishkaBlog.Dashboard.Overview.areas()) :: [ {String.t(), String.t(), String.t(), boolean()} ]
The Quick Actions rows areas may follow, as {label, icon, path, primary?}.
Callback implementation for Phoenix.LiveView.render/1.
The label drawn on a figure or a section that is not working yet.
kind is :untracked for a figure the view log feeds, :roadmap for a section on the roadmap.
Attributes
kind(:atom) (required) - Must be one of:untracked, or:roadmap.
@spec section_cards(map() | nil, MishkaBlog.Dashboard.Overview.areas()) :: [ {String.t(), String.t(), String.t(), String.t(), String.t(), [{String.t(), String.t(), String.t()}]} ]
The destination cards areas may open, as {title, description, link, icon, icon_tone, rows},
where each row is {label, value, value_tone} — of the eight, those whose blog areas areas
holds.
The word a post status is shown as.
Chip colours for a post status, shared by the calendar grid and its sidebar.
Whether label, as a KPI tile or a This Month row shows it, is a figure counted from the view log.
Nothing records a view yet, so the figure is empty or sample data and is drawn with a
roadmap_badge/1.
@spec weekday_labels() :: [String.t()]
The calendar's column heads, Sunday first, e.g. ["Sun", "Mon", …].