MishkaBlogWeb.Live.Admin.Dashboard.Home.Index (Mishka Blog v0.0.1)

Copy Markdown View Source

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

activity_label(arg1, arg2)

@spec activity_label(atom(), atom() | nil) :: String.t()

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.

activity_tone(arg)

@spec activity_tone(atom()) :: String.t()

Dot colour for a recent-activity entry, one per kind.

ago(at, now \\ DateTime.utc_now())

@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".

cell_tone(arg1, date)

@spec cell_tone(map(), Date.t()) :: String.t()

Fill for one calendar square: outside the month, selected, today, or an ordinary day.

day_count_label(count)

@spec day_count_label(non_neg_integer()) :: String.t()

The calendar sidebar's count line, e.g. "1 post on this day".

day_label(date)

@spec day_label(Date.t()) :: String.t()

Weekday, month and day for the calendar sidebar, e.g. "Mon, Jul 20".

delta(today, today)

@spec delta(integer(), integer()) :: String.t()

The +2 from yesterday line under a day counter, worded for every sign including zero.

growth(current, previous)

@spec growth(integer(), integer()) :: String.t()

The move between two windows as a percentage, or a word when there is nothing to compare to.

holds?(areas, needs)

@spec holds?(MishkaBlog.Dashboard.Overview.areas(), [:post | :comment]) :: boolean()

Whether areas holds every blog area in needs.

kpi_cards(stats, areas)

@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_label(date)

@spec month_label(Date.t()) :: String.t()

Month and year for the calendar header, e.g. "July 2026".

month_rows(stats, areas)

@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}.

move_tone(current, previous)

@spec move_tone(number(), number()) :: String.t()

Tone for a figure that moved: green when it rose, red when it fell, muted when it held.

on_roadmap?(title)

@spec on_roadmap?(String.t()) :: boolean()

Whether title, as a section card shows it, is a section that is on the roadmap.

percent(points, opts \\ [])

@spec percent(number(), keyword()) :: String.t()

points per hundred as the reader's language writes a percentage, e.g. "12%".

post_time(post)

@spec post_time(map()) :: String.t()

Time of day a post went out, or is due to — the calendar sidebar's second line.

posts_on(arg1, date)

@spec posts_on(map() | nil, Date.t()) :: list()

The posts sitting on date in an already-loaded calendar.

quick_actions(areas)

@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?}.

render(assigns)

Callback implementation for Phoenix.LiveView.render/1.

roadmap_badge(assigns)

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.

section_cards(stats, areas)

@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.

status_label(status)

@spec status_label(atom()) :: String.t()

The word a post status is shown as.

status_tone(status)

@spec status_tone(atom()) :: String.t()

Chip colours for a post status, shared by the calendar grid and its sidebar.

untracked?(label)

@spec untracked?(String.t()) :: boolean()

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.

weekday_labels()

@spec weekday_labels() :: [String.t()]

The calendar's column heads, Sunday first, e.g. ["Sun", "Mon", …].