PhoenixKitEcommerce.Web.Helpers (PhoenixKitEcommerce v0.1.16)

Copy Markdown View Source

Shared helper functions for Shop public LiveViews.

Centralizes utility functions that were duplicated across shop_catalog, catalog_category, catalog_product, cart_page, checkout_page, and checkout_complete.

Summary

Functions

Find the best enabled language that has a slug for this entity.

Build a localized URL path, adding language prefix for non-default languages. Delegates to Routes.path which handles default vs non-default consistently.

Get the first image URL for a product.

Format a price value with currency. Returns "-" for nil price.

Extract current user from socket assigns scope.

Determine language from URL params.

Get signed URL for a Storage image file.

Whether the storefront drops an all-zero fractional part ("40" rather than "40.00").

Convert a key string to human-readable format.

The billing identity an order was placed with.

Parse an integer from a LiveView event payload, falling back to default.

Parse page param with validation. Returns 1 for invalid/missing values.

Format address for a billing profile struct or an order's snapshot map.

Format display name for a billing profile.

Contact email from a billing profile STRUCT or an order's snapshot MAP.

Point this module's Gettext backend at language, falling back to the base language when the catalogue has no dialect.

Sets the content locale from a socket, falling back to the shop's CONFIGURED default rather than a hardcoded "en".

Functions

best_redirect_language(slug_map)

Find the best enabled language that has a slug for this entity.

Prefers the default language, then checks other enabled languages. Returns nil if no valid language found.

build_lang_url(path, lang)

Build a localized URL path, adding language prefix for non-default languages. Delegates to Routes.path which handles default vs non-default consistently.

first_image(arg1)

Get the first image URL for a product.

Handles Storage-based images (new format with featured_image_uuid or image_uuids) and legacy URL-based images (Shopify imports). Returns nil if no image is available.

format_price(price, currency)

Format a price value with currency. Returns "-" for nil price.

Accepts the amount as a Decimal, number, or numeric string (order line items persist their amounts as strings). The currency may be a Currency struct, a bare code string (Shop.currency_for_code/1 falls back to the code when the record's currency no longer resolves — showing "12.50 XYZ" is honest, borrowing today's default symbol is not), or nil (no default currency configured at all — legacy $).

get_current_user(socket)

Extract current user from socket assigns scope.

get_language_from_params_or_default(arg1)

Determine language from URL params.

Uses locale param if present, otherwise falls back to Translations.default_language/0. Used by catalog and category pages (non-product pages).

get_storage_image_url(file_uuid, variant)

Get signed URL for a Storage image file.

Returns nil if file or variant not found (unlike product detail page which returns a placeholder). Falls back to original variant if requested variant is not available.

hide_zero_decimals?()

Whether the storefront drops an all-zero fractional part ("40" rather than "40.00").

Off by default, because dropping the decimals is wrong for most shops. It exists for shops whose prices are round by nature — services quoted in whole units, where "40.00 EUR" reads as unnecessarily precise and, as one operator put it, faintly alarming.

Storefront only. Invoices, receipts and credit notes keep two decimals: they are accounting documents, and this setting must never reach them.

humanize_key(key)

Convert a key string to human-readable format.

Example: "material_type" -> "Material Type"

order_billing_identity(arg1)

The billing identity an order was placed with.

Prefers the order's immutable billing_snapshot over the live billing profile: the profile is editable, so reading it made a historical order claim an address it was never billed to (and deleting the profile made the true one reappear). The live profile is a fallback only for orders placed before snapshots existed.

parse_int(value, default \\ 0)

Parse an integer from a LiveView event payload, falling back to default.

String.to_integer/1 raises on anything non-numeric, and a raise inside handle_event/3 takes the whole LiveView down — so any hand-crafted or merely stale phx-value-* produced a crashed socket rather than an ignored event. That is reachable unauthenticated on the storefront (quantity fields) and by any admin elsewhere.

Returns default for nil, blank, partially-numeric ("3abc") and non-binary input. Callers that need a floor should still apply one — this only guarantees you get an integer back.

parse_page(page)

Parse page param with validation. Returns 1 for invalid/missing values.

profile_address(profile)

Format address for a billing profile struct or an order's snapshot map.

profile_display_name(profile)

Format display name for a billing profile.

profile_email(profile)

Contact email from a billing profile STRUCT or an order's snapshot MAP.

The snapshot is a plain map, so profile.email raises on it — the crash a rendered confirmation page hit after order pages started preferring the snapshot. Same shape problem as profile_display_name/1.

put_content_locale(language)

Point this module's Gettext backend at language, falling back to the base language when the catalogue has no dialect.

Without this the storefront renders English in every locale, however complete the catalogues are. The content language here is a DIALECT (resolve_dialect/1 returns "ru-RU", "et-EE", "en-US"), and that is also what core puts into the process locale — but this module ships priv/gettext/{en,ru,et}, plain codes with no region. Gettext does not fall back from "ru-RU" to "ru" on its own, so every lookup missed and returned its msgid, which is the English source string.

Core's own catalogue has the same plain-code shape, so this is not specific to the shop; it is why a fully translated module can still render entirely in English. Verified on a dev box: put_locale("ru") translates, put_locale("ru-RU") does not.

Called from mount/3, which runs once per process for both the dead render and the connected mount, so the whole lifecycle of that LiveView is covered.

put_content_locale_from(socket)

Sets the content locale from a socket, falling back to the shop's CONFIGURED default rather than a hardcoded "en".

:current_locale is supplied by core's live_session on_mount; a host that mounts these LiveViews outside it gets nil, and a hardcoded English fallback would force English on a shop whose default language is Russian.