MailglassAdmin.Operator.Shell (MailglassAdmin v1.9.0)

Copy Markdown View Source

Shared application shell for the operator surface — the chrome wrapping both MailglassAdmin.OperatorLive (deliveries) and MailglassAdmin.InboundLive (inbound records). The two screens mount in the SAME operator live_session (one Operator.Mount + Auth gate), so a shared shell is a within-surface concern, not a cross-mount one — it never reaches the dev-preview surface.

Provides:

  • a persistent left sidebar that navigates BETWEEN surfaces (Deliveries / Inbound) — navigation, not filtering. The Inbound item is conditionally omitted via inbound_available? (the same OptionalDeps.MailglassInbound gate the router uses to decide whether to emit the /inbound route), so an operator without the inbound package never sees a dead link.
  • a read-only tenant-context chip (forensic trust: always show whose data is on screen).
  • a shell-owned theme toggle (the dark theme is fully built but was previously unreachable from the operator UI). Theme is carried in the URL ?theme= param so it survives both Deliveries↔Inbound navigation and refresh — mirroring the preview surface's pattern.

Nav links reset to each surface's base path (no delivery_id/inbound_id, no stale filters): switching surfaces is a fresh question, and inheriting a selected id across surfaces is the classic sidebar-nav footgun.

This is internal UI — not part of the stable router/auth contract.

Summary

Functions

True when the resolved theme selection is dark.

Whether the inbound surface is present — the SAME gate the router uses to decide whether to emit the /inbound route. Centralized here so the nav and the route never disagree.

Renders the orientation strip for an operator surface — a persistent, symptom-first guidance panel that appears when no record is selected. Each surface has frozen per-surface copy keyed on the most common operator questions for that surface.

Builds the target for setting the theme picker value through the HTTP persistence seam while preserving unrelated query params in return_to.

Renders the operator shell around a surface's body (passed as the inner block).

Derives the {deliveries, inbound} nav paths from a screen's base_path, carrying the ?tenant_id= and ?theme= params so the tenant scope AND the active theme survive cross-surface navigation. active tells us which surface we're on so we can recover the operator root (the inbound screen's base_path has a trailing /inbound to strip).

Builds a same-surface tenant switch path from the current URL.

Resolves the shell's three-choice picker state from the URL theme param, falling back to the persisted theme cookie.

Builds the push_patch target for the theme toggle: flips the theme param on the CURRENT url, preserving every other filter/selection param.

Functions

dark_chrome?(params, cookie \\ nil)

True when the resolved theme selection is dark.

inbound_available?()

Whether the inbound surface is present — the SAME gate the router uses to decide whether to emit the /inbound route. Centralized here so the nav and the route never disagree.

orientation_strip(assigns)

Renders the orientation strip for an operator surface — a persistent, symptom-first guidance panel that appears when no record is selected. Each surface has frozen per-surface copy keyed on the most common operator questions for that surface.

Placed after defp flash_region/1 as the last function component in the module. No motion classes — born token-clean.

Attributes

  • surface (:atom) (required) - Must be one of :deliveries, :inbound, or :preview.

set_theme_path(uri, theme)

Builds the target for setting the theme picker value through the HTTP persistence seam while preserving unrelated query params in return_to.

The system choice removes the explicit theme query key.

shell(assigns)

Renders the operator shell around a surface's body (passed as the inner block).

Attributes

  • active (:atom) (required) - Must be one of :deliveries, or :inbound.
  • deliveries_path (:string) (required)
  • inbound_path (:string) (required)
  • inbound_available? (:boolean) - Defaults to false.
  • dark_chrome (:boolean) - Defaults to false.
  • theme_choice (:atom) - Defaults to :system. Must be one of :system, :light, or :dark.
  • tenant (:string) - Defaults to nil.
  • title (:string) (required)
  • subtitle (:string) - Defaults to nil.
  • flash (:map) - Defaults to %{}.

Slots

  • inner_block (required)

surface_paths(base_path, active, dark_chrome, tenant_id \\ nil)

Derives the {deliveries, inbound} nav paths from a screen's base_path, carrying the ?tenant_id= and ?theme= params so the tenant scope AND the active theme survive cross-surface navigation. active tells us which surface we're on so we can recover the operator root (the inbound screen's base_path has a trailing /inbound to strip).

Only tenant_id is carried across surfaces — it is the shared scoping dimension. Surface-specific filters (delivery vs inbound status sets) are intentionally left behind, since they don't translate between surfaces.

tenant_selector(assigns)

Attributes

  • state (:atom) (required) - Must be one of :select_required, or :none.
  • tenant_options (:list) - Defaults to [].
  • current_uri (:string) (required)

tenant_switch_path(uri, tenant_id)

Builds a same-surface tenant switch path from the current URL.

Tenant switches preserve compatible filters and theme while dropping selected record ids that cannot safely carry across tenants.

theme_choice(params, cookie \\ nil)

Resolves the shell's three-choice picker state from the URL theme param, falling back to the persisted theme cookie.

An explicit ?theme= query value wins (a per-link override, matching the root layout's precedence); otherwise the persisted cookie decides; absent both, :system. The operator/inbound theme picker persists via cookie + a param-stripping redirect (set_theme_path/2 → ThemeController), so the cookie — not the URL — is the source of truth across navigations.

toggle_theme_path(uri, currently_dark?)

Builds the push_patch target for the theme toggle: flips the theme param on the CURRENT url, preserving every other filter/selection param.