PhoenixKitCatalogue. Web. Components. ProductCard
(PhoenixKitCatalogue v0.19.1)
Copy Markdown
View Source
A read-only product card for a catalogue %Item{} — opened from the
ItemPicker thumbnail or a list's featured-image thumb, and potentially
shown to a CLIENT, not just admins, so it has to stand on its own.
product_card/1 renders a <.modal> whose media area is ONE continuous
swipeable carousel: the item's photos first, then its attached files (a
PDF renders inline, any other file as a tile with an Open action) — swipe
through the photos and just keep going into the files. Below it: the
item's filled scalar fields (SKU, price, unit, description, metadata) and
a compact file list for saving. Slide switching is entirely client-side
(scroll-snap); the only server event left is the close.
Image/file resolution and field extraction (the DB-backed work) live in
the public helpers resolve_images/1, resolve_files/1, resolve_name/2,
and build_fields/2 so the component itself stays render-only and
testable without a database. product_card_body/1 is the same content
without the modal shell (the "notpopup" form).
Usage (from a LiveComponent or LiveView that owns the state)
<ProductCard.product_card
id={@id}
show={@card_open}
item_name={@card_name}
images={@card_images}
fields={@card_fields}
files={@card_files}
target={@myself}
on_close="card_close"
/>
Summary
Functions
Builds the ordered {label, value} list of the item's filled,
user-facing scalar fields — SKU, price, unit, description, then
metadata. Empty values are dropped, so the card only shows what is set.
Renders the product card modal. Pure: every DB-backed value
(images, fields, item_name) is resolved by the caller and passed
in.
The card's content without the modal shell — the "notpopup" form, for
embedding the same product view inline (a detail pane, a future product
page). Same attrs as product_card/1 minus the modal ones.
Resolves the item's attached NON-image files (documents, PDFs, …) from
its files_folder_uuid, as %{uuid, name, size, pdf?} maps. PDFs are
flagged so the card can offer the inline viewer. Nil/blank-safe and
rescued the same way resolve_images/1 is.
Resolves the ordered gallery images for an item: the main
(featured_image_uuid) first, then the remaining image files in the
item's files_folder_uuid, de-duplicated. Returns %{uuid, name}
maps. Nil/blank-safe and rescued — a missing folder or a Storage
hiccup degrades to just the main image (or [] if there is none).
Resolves the item's display name for the given locale (translation, then bare name).
Functions
@spec build_fields(PhoenixKitCatalogue.Schemas.Item.t() | term(), String.t()) :: [ {String.t(), String.t()} ]
Builds the ordered {label, value} list of the item's filled,
user-facing scalar fields — SKU, price, unit, description, then
metadata. Empty values are dropped, so the card only shows what is set.
Renders the product card modal. Pure: every DB-backed value
(images, fields, item_name) is resolved by the caller and passed
in.
Attrs:
:id(required) — used to derive the modal's DOM id.:show(required) — whether the modal is open.:target(required) — the@myselfof the LiveComponent that handlescard_select_image/ the close event (theItemPicker).:item_name— card title.:images— ordered list of%{uuid, name}(main image first).:current_image— UUID of the image shown large.:fields— list of{label, value}for the already-filtered, non-empty fields.:on_close— event pushed to@targeton close (default"card_close").
Attributes
id(:string) (required)show(:boolean) (required)target(:any) (required)item_name(:string) - Defaults tonil.images(:list) - Defaults to[].current_image(:string) - Accepted for API compatibility; the carousel starts at the first slide (the featured image is already first) and slides are switched client-side, so this no longer drives the render. Defaults tonil.fields(:list) - Defaults to[].files(:list) - Defaults to[].on_close(:string) - Defaults to"card_close".
The card's content without the modal shell — the "notpopup" form, for
embedding the same product view inline (a detail pane, a future product
page). Same attrs as product_card/1 minus the modal ones.
The media area is ONE continuous swipeable carousel: photos first, then
the attached files (a PDF renders inline, any other file as a tile) — the
client swipes through the photos and just keeps going into the files.
Scroll-snap (daisyUI carousel) drives it entirely client-side: native
swipe on touch, arrow buttons on desktop, no server round-trip per slide.
Attributes
target(:any) (required)item_name(:string) - Defaults tonil.images(:list) - Defaults to[].current_image(:string) - accepted for API compatibility; unused. Defaults tonil.fields(:list) - Defaults to[].files(:list) - Defaults to[].
@spec resolve_files(PhoenixKitCatalogue.Schemas.Item.t() | term()) :: [ %{ uuid: String.t(), name: String.t() | nil, size: integer() | nil, pdf?: boolean() } ]
Resolves the item's attached NON-image files (documents, PDFs, …) from
its files_folder_uuid, as %{uuid, name, size, pdf?} maps. PDFs are
flagged so the card can offer the inline viewer. Nil/blank-safe and
rescued the same way resolve_images/1 is.
@spec resolve_images(PhoenixKitCatalogue.Schemas.Item.t() | term()) :: [ %{uuid: String.t(), name: String.t() | nil} ]
Resolves the ordered gallery images for an item: the main
(featured_image_uuid) first, then the remaining image files in the
item's files_folder_uuid, de-duplicated. Returns %{uuid, name}
maps. Nil/blank-safe and rescued — a missing folder or a Storage
hiccup degrades to just the main image (or [] if there is none).
@spec resolve_name(PhoenixKitCatalogue.Schemas.Item.t() | term(), String.t()) :: String.t() | nil
Resolves the item's display name for the given locale (translation, then bare name).