# `MishkaDocumentWeb.Templates.DocumentCard`
[🔗](https://github.com/mishka-group/mishka_document/blob/v0.0.1/lib/mishka_document_web/templates/document_card.ex#L5)

The workspaces grid: a hero banner leading a card grid.

Declared only by `MishkaDocument.Workspace`, and it reads that record directly rather than by column
position — the design's cards are specific shapes (accent bar, completion track, stat tiles, tags,
language editions) that a generic position mapping cannot describe.

The page's first featured workspace wears the banner (`flagship_id/1`); every other one stays a card
with its badge. A card's chrome is banded by how far along it is and its badges keep their language's
colour (`MishkaDocument.WorkspaceAccent`), and it reaches its translations through the editions row —
the list itself shows one row per document (`MishkaDocument.WorkspaceScope`).

## Features
- `:filter` - the control row: archive switch, search, and a drawer holding the grouped filters
- `:select` - card selection for bulk operations
- `:bulk_actions` - bulk actions on selected cards
- `:paginate` - numbered pagination under the grid

## Options
- `:columns` - the *minimum card width* the grid's tracks are built from (2, 3, or 4)

## Performance
Uses `@static.*` for columns, ui_adapter, etc. (no re-render on user interaction)
Uses `@state.*` for page, filter_values, etc. (re-renders when changed)

Pairs with `MishkaCmsCoreWeb.UIAdapters.Admin`, which draws the archive switch and the buttons.

# `archived?`

Whether a workspace reads as archived — by its status or by having been archived.

# `completion`

How far along the workspace is, 0–100.

`health_metrics` is field-policy gated, so it arrives as `%Ash.NotLoaded{}` for a viewer who cannot
see it — every read here has to survive that rather than take the page down with it.

# `contributors`

How many people have touched the workspace. `maintainers_info` reads the event log and is gated the
same way `health_metrics` is, and it keys its count by string.

# `edition_path`

```elixir
@spec edition_path(map(), :sections | :versions) :: String.t()
```

Where an edition chip goes: that language's own workspace, on the page `edition_view/1` names. The
list only shows masters, so this is the only way to reach a translation without searching for it.

# `edition_view`

```elixir
@spec edition_view(map()) :: :sections | :versions
```

Which page an edition chip opens for the table's viewer: the edition's sections for a section
admin, its versions for a workspace admin — the page its card's own actions open for them.

# `editions`

```elixir
@spec editions(map()) :: [map()]
```

The workspace's sibling editions — the same document in other languages.

The collapsing rule and the self-join are `MishkaCmsCore.LanguageEditions`', shared with every other
translatable admin list. What is workspace-specific is only what a chip shows beyond its language:
its own accent, and how far along that edition is, so the chip can read `FA · 76%`.

# `flagship_id`

Which workspace on this page wears the banner: the first featured one, or nothing if none is.

A stream never tells a row its position, but it does not have to — the page's own records are on
`state.records_result`, which is where the stream's contents came from. Reading them here keeps the
decision with the page rather than pushing a "first?" flag onto every record.

Archived workspaces are skipped: the banner is a hero for live work, and a grey gradient reads as
broken rather than as archived.

# `metric`

One `health_metrics` figure, or 0 when the viewer cannot see the calculation.

# `render_bulk_actions`

# `render_empty`

# `render_error`

# `render_filters`

The panel's control row: the archive switch, the search box, and a drawer holding every filter the
resource grouped. Nothing here names a filter — a `:text` filter becomes the search box and the rest
follow their group, so the resource stays the single source of truth.

The `phx-change="filter"` form, the input names and `clear_filters` are Gervaz's contract; only the
look is ours. The archive switch comes from the ui_adapter (`MishkaCmsCoreWeb.UIAdapters.Admin`).

# `render_header`

# `render_item`

One workspace card, wearing two accents: its chrome is banded by how far along it is, its language
badge and edition chips keep their language's colour, and archiving turns both grey
(`MishkaDocument.WorkspaceAccent`).

The page's *one* flagship gets the design's banner instead: full grid width, a gradient head and a
progress ring rather than a bar. Every other featured workspace stays a card and keeps its badge —
the design leads the grid with a single hero, and a wall of banners is not that grid.

Unlike the generic card templates this one reads the record directly rather than by column position:
it is declared only by `MishkaDocument.Workspace`, and the design's cards are specific shapes (accent
bar, completion track, stat tiles, editions) that positions cannot describe.

# `render_pagination`

The count on the left, the page buttons on the right, under the grid.

`prev_page` / `next_page` / `go_to_page` and every figure here are Gervaz's — only the look is ours.
A resource that asked for `:load_more` or `:infinite` instead keeps the shared pagination, which is
the only thing that knows how to draw those.

# `render_template_switcher`

# `ring_offset`

How much of the flagship's progress ring to leave undrawn.

The ring is a 41px-radius circle, so its circumference is 2π×41 ≈ 257.6 — the dash length the design
hard-codes. Offsetting by the *un*done share leaves the done share drawn.

# `status_label`

```elixir
@spec status_label(map()) :: String.t()
```

The words a workspace's status pill reads, in the viewer's language.

# `tags`

The tag chips the card carries, from the `tag_names` aggregate.

A workspace has no tags of its own — its sections do — so this is a list of names gathered three
relationships away, and it can arrive unloaded like any other aggregate. Sorted so a card's chips
keep their order between renders, and capped: the design gives the row one line, and a workspace
with forty tags would push everything below it off the card.

# `versions_count`

How many versions the workspace has, from the `versions_count` aggregate.

---

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