# `MishkaCmsCoreWeb.Templates.ActivityTimeline`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core_web/templates/activity_timeline.ex#L5)

A MishkaGervaz custom table template: an activity log drawn as a vertical timeline — a rail with a
severity-tinted node per event, day headings across it — rather than a table.

A template owns the whole table, so the chrome is here: the filter bar (`render_filters/1`) and the
footer (`render_pagination/1`). The bar is generic — a `:select` with few options becomes a
segmented switch, a long one a dropdown, a `:text` filter the search box and a `:date_range` a pair
of date inputs — so the resource stays the single source of truth and no filter is named here.

Row content comes from each column's `render`, mapped by position: 0 the event (message + badge),
1 the actor, 2 the date, 3+ inline badges.

## What a resource has to give it

Columns and filters are read off the resource, but three things come from the record itself:

  * `:severity` — an atom field (`:info`, `:warn`, `:error`) tinting the node and the actor avatar
    of a row that carries no `:tone`; an unknown or unloaded one renders the `:info` tint.
  * `:tone` — an optional atom field (`:created`, `:updated`, `:destroyed`) a content-change row
    carries. When present it tints the node and the actor avatar by action rather than severity, so
    a create reads green, an update blue and a destroy red; a row without one (a job, a compile, a
    draft) is `nil` and falls back to `:severity`.
  * `__metadata__.first_of_day?` / `__metadata__.day_count` — the day heading. A LiveView stream
    never hands a row its predecessor, so the READ decides which row opens a day and how many
    events that day holds; a row without them simply gets no heading.
  * a timestamp field, named by the `:timestamp` template option.

## Template options

  * `:timestamp` — the record's timestamp field (default `:inserted_at`).
  * `:max_cap` — the read's scan ceiling, when it has one. A count that reaches it is only a floor,
    so it reads `10,000+` rather than claiming an exact total. Default `nil` — counts are exact.

Used by `MishkaCmsCore.Activity.ActivityEntry` (the unified log at
`/admin/dashboard/runtime/activity`) and `MishkaDocument.EventLog` (the document event log at
`/admin/dashboard/document/event-logs`).

See `MishkaCmsCore.Activity.Query` and `MishkaCmsCoreWeb.Live.Admin.Dashboard.Activity.Index`.

# `avatar_tone_class`

```elixir
@spec avatar_tone_class(atom() | String.t() | nil) :: String.t()
```

The `bg-`/`text-` pair an actor avatar carries for a row of this palette value — the same tint the
node beside it uses, so the two always agree. Takes the row's tone-or-severity, not the actor: the
name already says who that was.

For a resource's `:actor` column `render`; a content-change `:tone` (`:created`/`:updated`/
`:destroyed`) colours by action, any other value by severity, and an unknown one reads green.
Accepts the atom or its string form.

# `dot_class`

```elixir
@spec dot_class(atom() | String.t() | nil) :: String.t() | nil
```

The dot colour for one palette value — a `:severity` (`:info`/`:warn`/`:error`), a content-change
`:tone` (`:created`/`:updated`/`:destroyed`), or an action filter's raw value (`create`/`update`/
`destroy`, which resolve to their tone's colour). The node is a tinted disc (`node_class/1`) around
this solid dot; the filter switch reuses it for its per-option dots. An unknown value renders no
dot rather than guess. Accepts the atom or its string form.

# `node_class`

```elixir
@spec node_class(atom() | String.t() | nil) :: String.t()
```

The tinted-disc background the node draws around its `dot_class/1` dot, for a `:severity` or a
content-change `:tone`. An unknown or `nil` value reads as the `:info`/`:created` green. Accepts
the atom or its string form.

# `render_bulk_actions`

# `render_empty`

# `render_error`

# `render_filters`

The filter bar: a short `:select` renders as a segmented switch, a long one as a dropdown, a
`:text` filter as the search box and a `:date_range` as a pair of date inputs. The
`phx-change="filter"` form and the input names are Gervaz's contract — only the look is ours.

# `render_header`

# `render_loading`

# `render_pagination`

The footer: a centred load-more, driven straight from the ui_adapter so it can carry the design's
classes (`Shared.render_pagination/1` hardcodes its own).

# `render_template_switcher`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
