PhoenixKitEcommerce.Web.ShopifySync (PhoenixKitEcommerce v0.4.2)

Copy Markdown View Source

Admin LiveView for the Shopify → shop one-way sync.

Fetches products from the Shopify Admin API connection registered in PhoenixKitEcommerce.Shopify.Provider, diffs them against the local catalog (PhoenixKitEcommerce.Shopify.Sync.check/2), and lets an operator apply changes — nothing is written without an explicit click. When the Admin API token is rejected, Sync.check/2 falls back to a price-only storefront read instead of failing outright; this LiveView surfaces that with a banner so an operator never mistakes a price-only report for a complete one, and only ever renders the Prices section in that mode (see visible_sections/2).

Changes are grouped into field sections (Prices, Titles, Descriptions, HTML texts, Tags, Statuses, Vendors — in that order, price first). One product's change can appear in more than one section if more than one of its fields differs. Sections are collapsed by default and show a count; expanding one reveals its rows, 25 at a time (see the module attribute doc on @per_page for why pagination here is a correctness requirement, not polish). An operator can apply a single field on a single product, a whole section, or every pending change at once — always through PhoenixKitEcommerce.Shopify.Sync's existing apply_change/2 / apply_changes/2, never by writing to a product directly.

Only updates products that already exist locally (matched by Shopify's handle against the product's slug). A Shopify product with no local match is skipped — creating new products is the CSV import's job, not this sync's.

Applying changes: request → confirm, never a direct write

Every apply affordance (one field on one product, a whole section, the checked rows in a section, or everything) is two-phase: a request_* event validates the click and stashes what it would do in @pending, then renders <.confirm_modal> describing it; only "confirm_apply" (the modal's own confirm button) calls into Sync.apply_change/2 / apply_changes/2. "cancel_apply" — and re-deriving what to write from live @changes at confirm time rather than trusting whatever @pending captured at request time — both exist so a stale or cancelled confirmation can never turn into a write; see clear_pending/1 and the do_confirm_* functions. This replaces the page's previous data-confirm (a bare browser confirm(), which cannot show the extreme-price exclusion notice below) with PhoenixKit's own modal.

Selection is a bulk scope, scoped to one section, one page

A checkbox column (<.bulk_select_scope> / bulk_select_cell) lets an operator pick specific rows within one expanded section and apply just that field to just those products via "Apply selection" — distinct from "Apply section" (every row) and a single row's own "Apply" button. Selection is client-side (see BulkSelectScope's JS-hook moduledoc) and scoped to the section's CURRENT PAGE of @per_page; paging re-renders the row set, which prunes any selection that isn't on the new page — so selection deliberately does not persist across pages. Like "Apply section", it excludes extreme price changes (price_extreme?) from the bulk write; see split_bulk_eligible/2.