Sigra.Admin.Components (Sigra v1.4.0)

Copy Markdown View Source

Lib-owned canonical admin component set for Sigra's admin LiveViews.

Provides 16 flat, stateless Phoenix.Component function components that consolidate the duplicated admin chrome across LiveViews. Security-critical design and a11y fixes propagate to host apps via mix deps.update (D-05).

Each component emits only sg-* CSS classes defined by the design contract.

See the Admin Design Contract for the authoritative per-component markup, class, and ARIA specification.

Components

Summary

Functions

Renders an active-filter indicator with a remove affordance.

Renders the audit zero-state, parametrized for per-page copy.

Renders the honest-cursor pagination <nav> for both audit pages.

Renders an audit event card (sg-list-row) with a pill cluster, actor summary, optional detail lines, and a formatted timestamp.

Renders the desktop audit table row (<tr>) for the shared 4-column audit table.

Renders a colour picker control for the auth branding Light/Dark palette panels.

Renders a single text input field for the auth branding Details panel.

Renders a centred zero-rows placeholder with a variable body block.

Renders a compact label-adjacent help trigger with a non-interactive tooltip.

Renders a block-level toned contextual alert.

Renders an inline navigation action for use inside notice/1 copy.

Renders a single-step back-to-list ghost button with a fixed &larr; glyph prefix.

Renders the side-by-side login + email preview rail for a single branding panel (Light, Dark, or Details).

Renders a persistent in-body scope indicator as a decorative inline span.

Renders a loading-shape placeholder.

Renders a read-only numeric KPI with no navigation affordance.

Renders a numeric KPI stat with a navigation destination (always an <a> element).

Renders a non-interactive aggregate posture badge using definition-list semantics.

Renders a verb-first action prompt card with a call-to-action link.

Functions

applied_chip(assigns)

Renders an active-filter indicator with a remove affordance.

The chip label is displayed inline; the remove link carries an aria-label for screen readers and a visually hidden fallback. The &times; glyph is aria-hidden.

Examples

<.applied_chip label="Active" remove_href="/admin/users?status=" />

Attributes

  • label (:string) (required) - the filter label shown inside the chip.
  • remove_href (:string) (required) - the URL to navigate to in order to remove this filter.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

audit_empty_state(assigns)

Renders the audit zero-state, parametrized for per-page copy.

Wraps the generic empty_state/1 with audit-specific attributes. The title attr supplies the heading; the inner block renders the per-page body copy (e.g., the filter-aware copy with "Clear all filters" link on the index page, vs the simple "no scoped events" copy on the per-user page).

Examples

<.audit_empty_state title="No audit events match this view">
  <p class="sg-muted sg-text-sm">No audit events match the active filters.</p>
  <div class="sg-cluster sg-cluster--center">
    <a href={index_path(@admin_scope)} class="sg-btn sg-btn--secondary sg-btn--sm">Clear all filters</a>
  </div>
</.audit_empty_state>

<.audit_empty_state title="No audit events for this user">
  <p class="sg-muted sg-text-sm">No scoped events are currently tied to this user.</p>
</.audit_empty_state>

Attributes

  • title (:string) (required) - the heading for the audit empty state.

Slots

  • inner_block - per-page body copy rendered below the title.

audit_pagination_nav(assigns)

Renders the honest-cursor pagination <nav> for both audit pages.

The nav is rendered only when multi_page?/1 is true (i.e., at least one of meta.previous_page or meta.next_page is non-nil). When results fit a single page the nav is absent entirely — honest cursor pagination (D-10).

The per-page href divergence (index page_path/3 vs per-user page_path/4 with user_id/return_to) is kept in each LiveView; the caller pre-builds the hrefs and passes them in (D-09 legitimate divergence stays per-page).

Examples

<.audit_pagination_nav
  meta={@meta}
  prev_href={page_path(@admin_scope, @current_params, @meta && @meta.previous_page)}
  next_href={page_path(@admin_scope, @current_params, @meta && @meta.next_page)}
/>

Attributes

  • meta (:map) (required) - cursor pagination meta map from the audit explorer.
  • prev_href (:string) (required) - href for the Previous page link, pre-built by the caller.
  • next_href (:string) (required) - href for the Next page link, pre-built by the caller.

audit_row(assigns)

Renders an audit event card (sg-list-row) with a pill cluster, actor summary, optional detail lines, and a formatted timestamp.

This is the single shared component for all three audit surfaces:

  • AuditIndexLive mobile card list (show_detail: true, show_codes: true)
  • AuditUserLive mobile card list (show_detail: true, show_codes: true)
  • UserShowLive "Recent Audit" compact block (defaults: show_detail: false, show_codes: false)

Tone is derived from row.outcome and row.action_badge via the single internal audit_tone/1 helper, which is the authoritative source replacing the divergent row_tone/1 helpers previously present in AuditIndexLive and AuditUserLive.

The format_date/1 helper handles %DateTime{} and %NaiveDateTime{} values, renders nil as "—", and raises ArgumentError on any other type (D-09 fix).

Examples

<.audit_row row={row} />

<.audit_row row={row} show_detail show_codes />

Attributes

  • row (:map) (required) - the presenter row map for the audit event.
  • show_detail (:boolean) - renders the Actor and Effective user detail lines (on in explorers, off in compact block). Defaults to false.
  • show_codes (:boolean) - renders the event id and action code lines (on in explorers, off in compact block). Defaults to false.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

audit_table_row(assigns)

Renders the desktop audit table row (<tr>) for the shared 4-column audit table.

This is the single shared component for byte-coherent desktop-table markup across both audit pages:

  • AuditIndexLive desktop table body
  • AuditUserLive desktop table body

Column order is FROZEN (td:nth-child positional selectors depend on this):

  1. Occurred — timestamp only
  2. Event — action_label pill + optional action_badge pill + <details> disclosure holding both raw codes (event_id and action code) as code.sg-code text nodes
  3. Actor — actor_summary span (first) + conditional detail lines
  4. Outcome — risk pill or muted span

Both raw code.sg-code nodes live inside the Event <td> disclosure so the firstTexts(desktop, 'code.sg-code', 2) content-equivalence assertion still returns exactly 2 (D-06 contract preserved).

Tone is derived via the shared private audit_tone/1 (single source of truth, D-08). Timestamp uses format_timestamp/1 (with-seconds) for forensic precision, consistent across both pages.

Examples

<.audit_table_row row={row} />

<.audit_table_row :for={row <- @rows} row={row} />

Attributes

  • row (:map) (required) - the presenter row map for the audit event.

color_field(assigns)

Renders a colour picker control for the auth branding Light/Dark palette panels.

The control pairs a native <input type="color"> (hidden circle) with a sibling span that mirrors the current hex value as readable text. Client-side hooks update the span in real-time as the picker changes.

Uses the data-sg-auth-branding-color and data-sg-auth-branding-color-value attribute hooks so AuthBrandingPreview can push live CSS variable updates.

Examples

<.color_field :for={{name, label} <- @light_color_fields}
  name={name}
  label={label}
  value={field_value(@draft_params, name)}
/>

Attributes

  • name (:string) (required) - the form field name; also used as the hook data attribute value.
  • label (:string) (required) - the accessible label for the colour picker.
  • value (:string) (required) - the current hex colour value (e.g., "#c2410c").

detail_input(assigns)

Renders a single text input field for the auth branding Details panel.

The id and help_id are derived deterministically from name using the branding-<name> convention (underscores replaced with hyphens) so the label for and <.field_help> id always stay in sync.

Examples

<.detail_input name="product_name" label="Product name" value={@value} required />

<.detail_input
  name="logo_url"
  label="Logo URL"
  value={@value}
  help="Shown on generated auth screens and email headers when set."
/>

Attributes

  • name (:string) (required) - the form field name (used in both the HTML name and id).
  • label (:string) (required) - the visible label for the field.
  • value (:string) (required) - the current field value.
  • required (:boolean) - marks the input as required. Defaults to false.
  • help (:string) - optional help tooltip copy. Defaults to nil.

empty_state(assigns)

Renders a centred zero-rows placeholder with a variable body block.

The title attr provides the primary heading. The inner_block slot accepts the variable body (e.g., filter-specific messaging or a CTA).

Examples

<.empty_state title="No users match this view">
  <p class="sg-muted sg-text-sm">Try adjusting your filters.</p>
</.empty_state>

Attributes

  • title (:string) (required) - the primary heading shown in the empty state.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

Slots

  • inner_block - the variable body rendered below the title (e.g., CTA or filter copy).

field_help(assigns)

Renders a compact label-adjacent help trigger with a non-interactive tooltip.

Use this for admin form fields whose effect is not obvious from the label alone. Keep the tooltip copy brief and plain-language; the panel should not contain links or other interactive content.

Examples

<span class="sg-field-label-row">
  <label class="sg-field-label" for="branding-logo-url">Logo URL</label>
  <.field_help id="branding-logo-url-help" label="Logo URL">
    Shown on generated auth screens and email headers when set.
  </.field_help>
</span>

Attributes

  • id (:string) (required) - stable id for the tooltip panel.
  • label (:string) (required) - field label used in the trigger's accessible name.
  • open (:boolean) - renders the tooltip open for deterministic design-gallery evidence. Defaults to false.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

Slots

  • inner_block (required) - plain explanatory tooltip copy.

notice(assigns)

Renders a block-level toned contextual alert.

Ships the final sg-notice form per the design contract (D-07). The data-tone attribute drives visual treatment via CSS. No live-region role is applied by default (D-08): Load-present content makes any live-region role inert (WAI-ARIA APG), and adding one risks duplicate announcements on LiveView re-render. A live-region role is opt-in per call site via :rest for genuinely post-load dynamic notices.

See the Admin Design Contract for the full rationale (D-07/D-08/D-09).

Examples

<.notice tone={:warn}>
  Password reset email could not be delivered.
</.notice>

<.notice tone={:ok}>
  Email address verified successfully.
</.notice>

Attributes

  • tone (:atom) - the visual tone applied via data-tone; renders as a string in the HTML attribute. Defaults to nil. Must be one of :ok, :warn, :risk, :info, or nil.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes (e.g., a live-region role for opt-in post-load notices).

Slots

  • inner_block (required) - the notice message content.

notice_link(assigns)

Renders an inline navigation action for use inside notice/1 copy.

This remains a native link and keeps notice content sentence-shaped. Use it when the notice names a concrete next step; use task_card/1 or sg-btn variants for standalone primary actions.

Examples

<.notice tone={:risk}>
  3 accounts need review 
  <.notice_link href="/admin/users?needs_review=true">Review accounts</.notice_link>
</.notice>

Attributes

  • href (:string) (required) - the URL the notice action navigates to.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root link.

Slots

  • inner_block (required) - the notice action label.

page_back(assigns)

Renders a single-step back-to-list ghost button with a fixed &larr; glyph prefix.

The label (e.g., "Back to users") is supplied by the caller. The arrow glyph is always rendered aria-hidden so screen readers read only the label text.

Examples

<.page_back return_to={~p"/admin/users"} label="Back to users" />

Attributes

  • return_to (:string) (required) - the URL the button navigates back to.
  • label (:string) (required) - the visible button label (e.g., "Back to users").
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

preview_pair(assigns)

Renders the side-by-side login + email preview rail for a single branding panel (Light, Dark, or Details).

The two preview surfaces are wrapped in sg-branding-preview-rail and accept data-sg-auth-branding-preview attribute hooks that the AuthBrandingPreview client hook reads to push live CSS variable updates as the palette changes.

The active flag controls which data-testid values are emitted so Playwright assertions can target the live panel's previews regardless of tab position.

Examples

<.preview_pair
  profile={@preview_profile}
  theme="light"
  active={@active_panel == :light}
  login_testid="admin-auth-branding-light-login-preview"
  email_testid="admin-auth-branding-light-email-preview"
  email_surface_testid="admin-auth-branding-light-email-preview-surface"
/>

Attributes

  • profile (:any) (required) - the %Sigra.Branding.Profile{} used to derive CSS variables and preview content.
  • theme (:string) (required) - the data-theme attribute value ("light" or "dark").
  • active (:boolean) (required) - true when this panel is the currently visible tab.
  • login_testid (:string) (required) - data-testid for the login preview card (used when not active).
  • email_testid (:string) (required) - data-testid for the email preview card (used when not active).
  • email_surface_testid (:string) (required) - data-testid for the email preview surface (used when not active).

scope_ribbon(assigns)

Renders a persistent in-body scope indicator as a decorative inline span.

The copy is caller-supplied (e.g., the result of scope_copy/1). The component emits the stable hook class sg-scope-ribbon (consumed by Playwright, per D-07) alongside the visual utility classes sg-muted sg-text-sm.

Examples

<.scope_ribbon copy={scope_copy(@admin_scope)} />

Attributes

  • copy (:string) (required) - the scope indicator copy (e.g., "Platform admin").
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

skeleton(assigns)

Renders a loading-shape placeholder.

Shimmer motion is owned entirely by the CSS layer (app.css) and is stripped by the universal prefers-reduced-motion rule. No inline motion or transition style is added by this component.

Examples

<.skeleton class="h-8 w-32" />

Attributes

  • class (:any) - additional CSS classes merged onto the root element (e.g., height/width). Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

stat(assigns)

Renders a read-only numeric KPI with no navigation affordance.

Unlike stat_link/1, this component renders no <a> element and introduces no dedicated component class. It reuses the existing sg-metric* utility classes.

Examples

<.stat label="Active sessions" value={42} />

Attributes

  • label (:string) (required) - the human-readable KPI label.
  • value (:integer) (required) - the numeric KPI value.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

stat_link(assigns)

Renders a numeric KPI stat with a navigation destination (always an <a> element).

Examples

<.stat_link label="Total users" value={1_234} href="/admin/users" />

Attributes

  • href (:string) (required) - the URL the stat link navigates to.
  • label (:string) (required) - the human-readable KPI label.
  • value (:integer) (required) - the numeric KPI value.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

summary_chip(assigns)

Renders a non-interactive aggregate posture badge using definition-list semantics.

The basic form preserves the original compact <dt>/<dd> output. Optional icon, value_suffix, subvalue, tone, and help assigns opt into the richer posture-summary treatment used by list page headers.

Examples

<.summary_chip label="MFA enabled" value={7} />
<.summary_chip
  id="users-metric-mfa"
  icon="mfa"
  label="MFA enrolled"
  value={42}
  value_unit="%"
  value_suffix="MFA coverage"
  subvalue="7 users with MFA"
  help="These users have multifactor authentication enabled. Higher coverage lowers account takeover risk."
/>

Attributes

  • id (:string) - optional root id; also seeds the help id. Defaults to nil.
  • label (:string) (required) - the term / KPI label.
  • value (:integer) (required) - the numeric KPI value.
  • icon (:string) - optional built-in metric icon name. Defaults to nil.
  • value_unit (:string) - optional unit rendered tight against the number. Defaults to nil.
  • value_suffix (:string) - optional second-line label for the number. Defaults to nil.
  • subvalue (:string) - optional secondary metric context. Defaults to nil.
  • help (:string) - optional short explanatory help copy. Defaults to nil.
  • open (:boolean) - renders help open for deterministic design-gallery evidence. Defaults to false.
  • tone (:string) - optional metric tone (risk, warn, ok, info). Defaults to nil.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.

task_card(assigns)

Renders a verb-first action prompt card with a call-to-action link.

Examples

<.task_card
  title="Invite your team"
  body="Add teammates so they can access the admin panel."
  href="/admin/users/invite"
  action="Send invitations"
/>

Attributes

  • title (:string) (required) - the card heading.
  • body (:string) (required) - the card description copy.
  • href (:string) (required) - the URL the CTA button navigates to.
  • action (:string) (required) - the CTA button label.
  • class (:any) - additional CSS classes merged onto the root element. Defaults to nil.
  • Global attributes are accepted. arbitrary HTML attributes added to the root element.