defmodule BerrypodWeb.Admin.ProductShow do use BerrypodWeb, :live_view alias Berrypod.Products alias Berrypod.Products.{Product, ProductImage, ProductVariant} alias Berrypod.Cart @impl true def mount(%{"id" => id}, _session, socket) do case Products.get_product(id, preload: [:provider_connection, images: :image, variants: []]) do nil -> socket = socket |> put_flash(:error, "Product not found") |> push_navigate(to: ~p"/admin/products") {:ok, socket} product -> form = product |> Product.storefront_changeset(%{}) |> to_form(as: "product") socket = socket |> assign(:page_title, product.title) |> assign(:product, product) |> assign(:form, form) {:ok, socket} end end @impl true def handle_event("validate_storefront", %{"product" => params}, socket) do form = socket.assigns.product |> Product.storefront_changeset(params) |> Map.put(:action, :validate) |> to_form(as: "product") {:noreply, assign(socket, :form, form)} end @impl true def handle_event("save_storefront", %{"product" => params}, socket) do case Products.update_storefront(socket.assigns.product, params) do {:ok, updated} -> product = %{ updated | provider_connection: socket.assigns.product.provider_connection, images: socket.assigns.product.images, variants: socket.assigns.product.variants } form = product |> Product.storefront_changeset(%{}) |> to_form(as: "product") socket = socket |> assign(:product, product) |> assign(:form, form) |> put_flash(:info, "Product updated") {:noreply, socket} {:error, changeset} -> {:noreply, assign(socket, :form, to_form(changeset, as: "product"))} end end @impl true def handle_event("resync", _params, socket) do product = socket.assigns.product if product.provider_connection do Products.enqueue_sync(product.provider_connection) {:noreply, put_flash(socket, :info, "Sync queued for #{product.provider_connection.name}")} else {:noreply, put_flash(socket, :error, "No provider connection")} end end @impl true def render(assigns) do ~H""" <.header> <.link navigate={~p"/admin/products"} class="text-sm font-normal text-base-content/60 hover:underline" > ← Products
{@product.title} <.visibility_badge visible={@product.visible} /> <.status_badge status={@product.status} />
<:actions> <.link :if={provider_edit_url(@product)} href={provider_edit_url(@product)} target="_blank" rel="noopener" class="admin-btn admin-btn-ghost admin-btn-sm" > Edit on {provider_label(@product)} <.icon name="hero-arrow-top-right-on-square-mini" class="size-4" /> <.link navigate={~p"/products/#{@product.slug}"} class="admin-btn admin-btn-ghost admin-btn-sm" > View on shop <.icon name="hero-arrow-top-right-on-square-mini" class="size-4" /> <%!-- images + details --%>
{image.alt

No images

Details

<.list> <:item :if={@product.provider_connection} title="Provider"> {provider_label(@product)} via {@product.provider_connection.name} <:item title="Category">{@product.category || "—"} <:item title="Price">{Cart.format_price(@product.cheapest_price)} <:item title="Variants">{length(@product.variants)} <:item title="Images">{length(@product.images)} <:item title="Created">{format_date(@product.inserted_at)} <:item :if={@product.provider_connection && @product.provider_connection.last_synced_at} title="Last synced" > {format_date(@product.provider_connection.last_synced_at)}
<%!-- storefront controls --%>

Storefront controls

<.form for={@form} phx-submit="save_storefront" phx-change="validate_storefront" class="flex flex-wrap gap-4 items-end" > <.button type="submit" variant="primary" class="admin-btn-sm">Save
<%!-- variants --%>

Variants ({length(@product.variants)})

<.table id="variants" rows={@product.variants}> <:col :let={variant} label="Options">{ProductVariant.options_title(variant)} <:col :let={variant} label="SKU">{variant.sku || "—"} <:col :let={variant} label="Price">{Cart.format_price(variant.price)} <:col :let={variant} label="Cost"> {if variant.cost, do: Cart.format_price(variant.cost), else: "—"} <:col :let={variant} label="Profit"> {if ProductVariant.profit(variant), do: Cart.format_price(ProductVariant.profit(variant)), else: "—"} <:col :let={variant} label="Available"> <.icon :if={variant.is_enabled && variant.is_available} name="hero-check-circle-mini" class="size-5 text-green-600" /> <.icon :if={!variant.is_enabled || !variant.is_available} name="hero-x-circle-mini" class="size-5 text-base-content/30" />
<%!-- provider data --%>

Provider data

<.list> <:item title="Provider"> {provider_label(@product)} via {@product.provider_connection.name} <:item title="Provider product ID">{@product.provider_product_id} <:item title="Status">{@product.status} <:item title="Sync status">{@product.provider_connection.sync_status}
""" end # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- defp sorted_images(product) do (product.images || []) |> Enum.sort_by(& &1.position) end defp visibility_badge(assigns) do {bg, text, ring, label} = if assigns.visible do {"bg-green-50", "text-green-700", "ring-green-600/20", "visible"} else {"bg-base-200/50", "text-base-content/60", "ring-base-content/10", "hidden"} end assigns = assign(assigns, bg: bg, text: text, ring: ring, label: label) ~H""" {@label} """ end defp status_badge(assigns) do {bg, text, ring} = case assigns.status do "active" -> {"bg-green-50", "text-green-700", "ring-green-600/20"} "draft" -> {"bg-amber-50", "text-amber-700", "ring-amber-600/20"} "archived" -> {"bg-base-200/50", "text-base-content/60", "ring-base-content/10"} _ -> {"bg-base-200/50", "text-base-content/60", "ring-base-content/10"} end assigns = assign(assigns, bg: bg, text: text, ring: ring) ~H""" {@status} """ end defp provider_label(%{provider_connection: %{provider_type: "printify"}}), do: "Printify" defp provider_label(%{provider_connection: %{provider_type: "printful"}}), do: "Printful" defp provider_label(%{provider_connection: %{provider_type: type}}), do: String.capitalize(type) defp provider_label(_), do: nil defp provider_edit_url(%{ provider_connection: %{provider_type: "printify", config: config}, provider_product_id: pid }) do shop_id = config["shop_id"] if shop_id && pid, do: "https://printify.com/app/editor/#{shop_id}/#{pid}" end defp provider_edit_url(%{ provider_connection: %{provider_type: "printful"}, provider_product_id: pid }) do if pid, do: "https://www.printful.com/dashboard/sync/update?id=#{pid}" end defp provider_edit_url(_), do: nil defp format_date(nil), do: "—" defp format_date(datetime), do: Calendar.strftime(datetime, "%d %b %Y %H:%M") end