# `PhoenixKitEcommerce.Catalogue.ShopStatusColumn`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ecommerce/blob/0.5.11/lib/phoenix_kit_ecommerce/catalogue/shop_status_column.ex#L1)

The catalogue admin's "Shop status" extension column — surfaces
`data["ecommerce"]["shop_status"]` next to the catalogue's OWN
`status` on the item/category tables.

The owner's complaint: the catalogue's `status` and the shop's
`shop_status` are two independent signals — an item can be a live
catalogue entry that is deliberately not for sale — and only the
catalogue's own `status` showed on screen, so an item could be
invisible on the storefront (or, worse, reachable when it shouldn't
be — see below) for a reason the admin screen never surfaced. This
column does NOT synchronize the two (that would destroy the
distinction the owner wants to keep); it shows both, side by side,
and never warns — see "No warnings" below for why there is no
combination left that is an actual hazard.

## Why item and category are two different rules (`item_columns/0`
## vs. `category_columns/0` return DIFFERENT column definitions)

Items and categories share neither their `shop_status` value domain
nor their storefront-visibility rule:

  * Item `shop_status` — `draft` / `active` / `archived`
    (`PhoenixKitEcommerce.Catalogue.ItemCommerce`). Listing visibility
    (`ProductSource.Catalogue.Query.active_visibility/1`) requires
    `item.status == "active"` AND `COALESCE(shop_status, 'active') ==
    "active"` — an absent `shop_status` defaults to whatever
    `item.status` already says (`View.product_status/2`'s exact
    fallback), so it never disagrees with the catalogue by itself.
  * Category `shop_status` — `active` / `unlisted` / `hidden`
    (`CategoryCommerce`). Only `hidden` removes a category's items
    from listings (`Query.exclude_hidden_categories/2`); `unlisted`
    only hides the category's own nav entry — its page and items stay
    directly reachable. An absent `shop_status` defaults
    UNCONDITIONALLY to `"active"` (`View.category_view/2`:
    `Map.get(ecommerce, "shop_status") || "active"` — no fallback to
    `c.status` at all).

## No warnings — the reachability leak is closed on both sides

An earlier version of this column warned on the one combination
that was a real hazard: the shop reporting something as visible
while the catalogue had retired or soft-deleted it, so that it was
excluded from every listing, count and facet
(`ProductSource.Catalogue.Query.active_visibility/1` unconditionally
requires `item.status == "active"` / `c.status != "deleted"`) yet
still reachable by direct link, because the single-record pages
resolved the status from `shop_status` alone.

That leak is now closed at the source for both record types, so
there is nothing left to warn about:

  * Items — `View.product_status/2` checks `item.status` FIRST and
    forces `"archived"` on any non-active catalogue status regardless
    of `shop_status` (`phoenix_kit_ecommerce` PR #53), so
    `CatalogProduct.do_mount/3` (which redirects on any resolved
    status `!= "active"`) cannot be fooled by a stale explicit
    `shop_status: "active"`.
  * Categories — `View.category_status/2` forces `"hidden"` for a
    catalogue-deleted category no matter what `shop_status` says
    (commit af9308b; `catalog_category_catalogue_status_test.exs`
    proves the page redirects), so `CatalogCategory.do_mount/3`'s
    block-list gate (redirect only on literal `"hidden"`) is reached
    with the forced value, never the stale one.

What remains is a catalogue/shop DISAGREEMENT (a `discontinued` item
with `shop_status: "active"`; a `deleted` category with `"unlisted"`)
— display-only information the admin may want to see, but not a
hazard, and every other disagreement (catalogue active while the shop
says `draft`/`archived`/`hidden`/`unlisted`) is simply how the owner
deliberately keeps something out of the shop while it stays a live
catalogue entry. All of them render with no warning. An absent
`shop_status` is shown as the value it effectively resolves to (per
the fallbacks above), marked "(default)" rather than as an alarming
"Unknown" — it is not a misconfiguration, just a namespace the Shop
section has never written.

Reached only through `PhoenixKitEcommerce.Catalogue.Extension`'s
`item_columns/0`/`category_columns/0` — see that module's moduledoc
for the discovery contract, and `PhoenixKitCatalogue.Extension`'s
typedoc (in the optional `phoenix_kit_catalogue` dependency) for the
exact `%{id:, label:, render:}` shape this returns. Written
duck-typed on purpose: nothing here references a `PhoenixKitCatalogue`
module, so it compiles and behaves identically whether or not that
dependency, or the extension-column slot it implements, is present at
all.

## Badge colours

Core's `PhoenixKitWeb.Components.Core.Badge.status_badge/1` has no
case for `unlisted`/`hidden`/an unrecognized value — all three fall
through to the same grey `badge-ghost`, which would render three
semantically different category shop-statuses identically. Rather
than edit core, this module picks its own explicit badge class per
status (see `item_shop_badge/1`, `category_shop_badge/1`,
`catalogue_badge/1`) instead of delegating to `status_badge/1`.

# `category_columns`

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

The `category_columns/0` entry — see this module's moduledoc.

# `item_columns`

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

The `item_columns/0` entry — see this module's moduledoc.

---

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