PhoenixKitWarehouse.Web.Components.WarehouseBrowser (PhoenixKitWarehouse v0.4.0)

Copy Markdown View Source

Warehouse-specific catalogue tree components.

Stateless function components driven by assigns from the parent LiveView:

  • stock_tree/1 — read-only lazy tree annotated with stock quantity and value.
  • count_sheet/1 — editable table of inventory document lines grouped by catalogue → category, with conditional price/sum columns.

Adding new positions to a document (internal orders, stocktakes, transfers) goes through PhoenixKitCatalogue.Web.Components.ItemSelectorModal directly — not through a component here. The picker that used to live in this module (add_picker/1) only ever added lines; it never touched a stock-specific field, so nothing warehouse-specific was lost by routing that action through the catalogue's own selector instead.

Tree toggle state (expanded_catalogues, expanded_categories, loaded_categories, loaded_items) lives in the parent LV. These components are stateless — they only render.

Summary

Functions

Editable table of inventory document lines grouped by catalogue → category.

Returns the display name of a catalogue / category / item record in the given UI locale, reading translations from the multilang data JSONB via PhoenixKitCatalogue.Catalogue.get_translation/2. Falls back to the embedded primary translation, then to the denormalized name column. Returns nil for nil records.

Stateless modal component for selecting source documents to import into a warehouse document.

Read-only stock sheet grouped by catalogue → category.

Read-only lazy catalogue tree annotated with stock quantity and total value.

Strips a configurable catalogue-name prefix (case-insensitive), along with any single separating space. Returns the original name if the prefix isn't present, or if stripping would yield an empty string.

Short localized label for a catalogue item unit of measure. Ported from Andi.Catalogues.unit_label/1 verbatim.

Functions

count_sheet(assigns)

Editable table of inventory document lines grouped by catalogue → category.

Each catalogue group is rendered as a DaisyUI collapsible section (open by default). Category sub-headers are preserved; each category section ends with a <tfoot> row showing the quantity subtotal. A quantity subtotal badge is also shown in the catalogue-level collapsible header.

Columns:

  • Name / SKU
  • Unit
  • Current stock (read-only, from stock_map)
  • Counted (number input, event set_counted)
  • Unit price (event set_price) — only when track_value
  • Sum (event set_sum) — only when track_value
  • Remove button (event remove_line)

The names map provides localized display names keyed by UUID:

  • item UUID → item name
  • catalogue UUID → catalogue display name
  • category UUID → category name

Line snapshot names (line["name"]) are used as fallback when the UUID is not in the map.

Price/sum columns are hidden entirely when track_value is false.

Attributes

  • lines (:list) (required)
  • track_value (:boolean) (required)
  • names (:map) (required)
  • stock_map (:map) (required)
  • locale (:string) (required)
  • editable (:boolean) - Defaults to true.

localized_name(record, locale)

@spec localized_name(map() | struct() | nil, String.t() | nil) :: String.t() | nil

Returns the display name of a catalogue / category / item record in the given UI locale, reading translations from the multilang data JSONB via PhoenixKitCatalogue.Catalogue.get_translation/2. Falls back to the embedded primary translation, then to the denormalized name column. Returns nil for nil records.

Ported from Andi.Catalogues.localized_name/2 — that function does not exist on PhoenixKitCatalogue.Catalogue itself (confirmed, matching Plan 3's identical finding for Inventories), so this is real ported logic, not a call-through. Unlike the original, locale is used exactly as given — the original's Andi.Locales.entity_locale/1/current_locale/0 normalization is Andi-specific and dropped; callers pass an already-resolved locale string (see this plan's Global Constraints on Andi.Locales.sync_from_phoenix_kit/0).

source_picker(assigns)

Stateless modal component for selecting source documents to import into a warehouse document.

Attrs

  • id — explicit modal id (string, required). Pass a stable per-form id to avoid collision when multiple modals share the same page.
  • show — controls visibility (boolean, required).
  • title — modal header title string (required).
  • on_close — phx event name sent on cancel / backdrop click (string, required).
  • candidates — list of %{uuid, label, label_prefix} maps to display (label_prefix is optional). The parent LiveView builds this list from DB queries in handle_event/3 for "source_picker_search".
  • selected_uuidsMapSet (or list) of already-selected UUIDs; drives the checkbox state per row (required).
  • search_query — current value of the search input, echoed into the field on re-render (default "").

Parent events to implement

The component fires these fixed phx event names; the parent LiveView MUST implement handle_event/3 for each:

  • "source_picker_search" — fired on phx-change of the search form. Params: %{"query" => string}. The parent should re-query candidates and assign them.

  • "source_picker_toggle" — fired on phx-click of a candidate row. Params: %{"uuid" => binary}. The parent should toggle the UUID in/out of selected_uuids.

  • "source_picker_select_all" — fired on phx-click of the "Select all" toggle. No extra phx-values. The parent should select every UUID currently in candidates when not all are selected yet, or clear the selection when they already all are.

  • "source_picker_confirm" — fired on the "Import (N)" primary button. No extra phx-values. The parent reads its own selected_uuids assign, runs the import logic, and then clears the modal.

The on_close event is also a parent-side event (cancel / backdrop close).

Attributes

  • id (:string) (required)
  • show (:boolean) (required)
  • title (:string) (required)
  • on_close (:string) (required)
  • candidates (:list) - Defaults to [].
  • selected_uuids (:any) - Defaults to [].
  • search_query (:string) - Defaults to "".

stock_sheet(assigns)

Read-only stock sheet grouped by catalogue → category.

Accepts a flat list of %{item, quantity, unit_value} maps where item has :catalogue and :category preloaded. Groups are rendered as DaisyUI collapsible sections (open by default) with a quantity subtotal badge in the catalogue header and a per-category <tfoot> subtotal row.

Columns: Item (name + SKU) / Unit / In stock (qty) / Total value.

Entries may optionally carry a :below_min? boolean (§5 — deficit tracking, set by Web.StockLive.build_stock_items/1) — when true, a small warning icon renders next to the item name. Missing/false is the default (Map.get(entry, :below_min?, false)), so callers that don't track deficits can keep passing the plain %{item, quantity, unit_value} shape.

Attributes

  • stock_items (:list) (required)
  • locale (:string) (required)

stock_tree(assigns)

Read-only lazy catalogue tree annotated with stock quantity and total value.

Each item row shows:

  • Quantity in stock (with unit label)
  • Total value (quantity * unit_value) — only when unit_value is known; otherwise shows a placeholder.

Toggle events (toggle_catalogue / toggle_category) are sent to the parent LiveView which updates expanded_* and loaded_* assigns.

Attributes

  • catalogue_summaries (:list) (required)
  • expanded_catalogues (:any) (required)
  • expanded_categories (:any) (required)
  • loaded_categories (:map) (required)
  • loaded_items (:map) (required)
  • locale (:string) (required)
  • stock_map (:map) (required)

strip_prefix(name)

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

Strips a configurable catalogue-name prefix (case-insensitive), along with any single separating space. Returns the original name if the prefix isn't present, or if stripping would yield an empty string.

Ported from Andi.Catalogues.strip_prefix/1. The original reads Application.get_env(:andi, :catalogue_prefix, "ANDI") — Andi-specific config the package cannot reference. This reads Application.get_env(:phoenix_kit_warehouse, :catalogue_prefix, "") instead, defaulting to an empty string (no-op — every catalogue name passes through unchanged) rather than a hardcoded brand string, so the package behaves sensibly out of the box for a fresh host with no catalogue-naming convention at all. A host that does use a shared naming prefix opts in via config.

unit_label(other)

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

Short localized label for a catalogue item unit of measure. Ported from Andi.Catalogues.unit_label/1 verbatim.