PhoenixKitCatalogue. Web. Components. ItemSelectorModal
(PhoenixKitCatalogue v0.19.1)
Copy Markdown
View Source
Catalogue item selector modal: the catalogue's analogue of core's
MediaSelectorModal. A logged-in user browses the catalogue inside a
modal — search, category chips, photo-forward card grid — picks items,
sets a quantity per item, reviews the selection in a tray, and confirms.
Usage
# In the parent LiveView's template. Mount it with :if — unmounting
# on close is what gives clean reopen semantics (fresh search, fresh
# scroll, preselects re-read).
<.live_component
:if={@show_item_selector}
module={PhoenixKitCatalogue.Web.Components.ItemSelectorModal}
id="order-item-selector"
scope={%{catalogue_uuids: [@catalogue.uuid]}}
selected={@order_lines_by_uuid}
/>Required host wiring (do not skip — silent failure otherwise)
This is a LiveComponent; it reports through process messages to the
host LiveView, exactly like MediaSelectorModal. The host MUST handle
both, or a confirmed selection is silently dropped:
handle_info({:items_selected, %{id: id, mode: mode, picks: picks}}, socket)— fired on Confirm. Each pick is a map with:uuid,:qty(always aDecimal, integers included — hosts write one clause),:unit, and a display snapshot::name,:sku,:price,:line_total(price × qty, nil when the item has no price) and:photo_url(a signed URL — it expires, render it, never persist it). The snapshot is for rendering the host's own summary without a re-query; it is NOT an order record — re-read and re-price items server-side when the selection becomes something real.handle_info({:item_selector_closed, %{id: id}}, socket)— fired on cancel/ESC/backdrop, AND after a confirm. Reset the:ifassign here.
Scope
scope fixes what the user may browse: any of :catalogue_uuids,
:category_uuids, :only, :statuses, :include_descendants (the
Catalogue.search_items/2 vocabulary). It is enforced in BrowseState
— every fetch re-derives from it, and client events can only narrow
within it, so a crafted event cannot browse or select outside what the
host allowed. Selection events are additionally accepted only for uuids
the component itself has rendered (or that arrived preselected).
Preselection
selected is %{uuid => qty} (string uuids, Decimal or integer
quantities). Preselected items are hydrated at mount ignoring scope —
the tray must be able to render what the host handed it. A hydrated item
that falls OUTSIDE the scope renders in the tray marked unavailable and
is excluded from the confirm picks, never silently dropped.
Quantities
qty_precision: 0 (default) is whole numbers; a positive precision
turns the same stepper decimal-capable ("2.5" of unit "L"). The input
commits on blur/Enter; decimal commas are accepted ("2,5" — ru/et
keyboards); all limits re-clamped server-side.