PhoenixKitCatalogue.Web.Helpers (PhoenixKitCatalogue v0.1.14)

Copy Markdown View Source

Tiny utilities shared by every catalogue LiveView. Imported into LVs via the standard import PhoenixKitCatalogue.Web.Helpers line.

Currently exports:

  • actor_opts/1 — extract the current user's UUID from socket assigns, return [actor_uuid: uuid] for the opts \\ [] keyword list every mutating context function accepts. Returns [] when no user is signed in (e.g. inside a test that mounts the LV with a bare conn). The atom is suitable to thread through Catalogue.create_* / update_* / trash_* / restore_* / permanently_delete_* etc.
  • actor_uuid/1 — the raw UUID (or nil). Use when you need the value directly rather than a keyword list, e.g. when building activity-log metadata in a LiveView.
  • log_operation_error/3 — engineer-visible Logger.error for a failed mutation plus an Activity row tagged db_pending: true so the user-visible audit feed records the attempted action even when it fails. See PhoenixKitCatalogue.Catalogue.ActivityLog for the success-vs-failure layering.

Summary

Types

Convenience alias for the keyword list shape mutating ctx fns accept.

Functions

Extracts [actor_uuid: uuid] from socket.assigns.phoenix_kit_current_user.

Returns the current user's UUID from socket assigns, or nil.

Maps an LV operation string + entity_type to the canonical activity action atom the catalogue context already uses on the success path.

Logs a failed LV mutation in two places at once

Translates a catalogue/category/item/manufacturer/supplier status field value to a localised label via gettext.

Types

actor_opts()

@type actor_opts() :: [{:actor_uuid, Ecto.UUID.t()}] | []

Convenience alias for the keyword list shape mutating ctx fns accept.

Functions

actor_opts(socket)

@spec actor_opts(Phoenix.LiveView.Socket.t()) :: actor_opts()

Extracts [actor_uuid: uuid] from socket.assigns.phoenix_kit_current_user.

Returns [] when no user is signed in. Pass the result straight into any PhoenixKitCatalogue.Catalogue mutating function as its trailing opts argument.

actor_uuid(socket)

@spec actor_uuid(Phoenix.LiveView.Socket.t()) :: Ecto.UUID.t() | nil

Returns the current user's UUID from socket assigns, or nil.

derive_activity_action(operation, entity_type)

@spec derive_activity_action(String.t(), String.t() | nil) :: String.t() | nil

Maps an LV operation string + entity_type to the canonical activity action atom the catalogue context already uses on the success path.

Falls back to nil when the operation doesn't follow the <verb>_<entity> shape; the caller skips the audit-row write in that case (engineer log still fires).

log_operation_error(socket, operation, context)

@spec log_operation_error(Phoenix.LiveView.Socket.t(), String.t(), map()) :: :ok

Logs a failed LV mutation in two places at once:

  1. Engineer logLogger.error with the operation, the LV-level entity context, and the changeset / atom reason. This is the rich-context line that production-incident triage reads.
  2. User-visible audit row — an Activity entry with the same action atom the success path would have written, plus metadata.db_pending: true. The audit feed therefore records what the user attempted, not just what succeeded — a deliberate change in the post-Apr 2026 pipeline (workspace AGENTS.md C12 agent #2 — "Activity logging coverage").

The action atom is derived from operation via derive_activity_action/2. Validation cycles (form-validate events) never reach this helper — by construction it's only called from {:error, _} handle_event branches, where the failure is a real infrastructure / consistency error worth auditing.

Expected context keys

  • :entity_type"item" / "category" / "catalogue" / "manufacturer" / "supplier" (drives both the activity resource_type and the action-atom prefix).
  • :entity_uuid — primary-key UUID; lands as resource_uuid.
  • :reason — an %Ecto.Changeset{}, an atom, or any other inspectable shape. Logged engineer-side; on the audit row it's summarised into PII-safe metadata.error_keys (changeset field names only — never values, since user-typed strings can carry PII).

Activity-log failures (missing table, ownership errors, sandbox exit) are swallowed by ActivityLog.log/1; they never bubble up to the LV.

status_label(other)

@spec status_label(String.t() | nil) :: String.t()

Translates a catalogue/category/item/manufacturer/supplier status field value to a localised label via gettext.

Handles every status string that any catalogue schema can emit (active / inactive / archived / deleted / discontinued) with explicit literal gettext(...) clauses so mix gettext.extract picks them up. Unknown status values pass through unchanged — never use String.capitalize/1 on translated text because the result would pin English casing on a value the extractor can't see.