From 3c73b98d2be5363d645f76a26c01a1b775670b87 Mon Sep 17 00:00:00 2001 From: jamey Date: Tue, 10 Feb 2026 23:15:09 +0000 Subject: [PATCH] fix PDP quantity selector and trust badge consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up +/− buttons with phx-click events and handle_event handlers, clamp to 1–99, reset to 1 after add-to-cart. Trust badges now use a single hero-check-circle icon and sentence case text. Co-Authored-By: Claude Opus 4.6 --- PROGRESS.md | 4 +- .../components/shop_components/content.ex | 69 ++++--------------- .../components/shop_components/product.ex | 24 ++++++- .../live/shop_live/product_show.ex | 13 ++++ .../live/shop_live/product_show_test.exs | 61 ++++++++++++++++ 5 files changed, 109 insertions(+), 62 deletions(-) diff --git a/PROGRESS.md b/PROGRESS.md index 9ae2aa4..0a011ab 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -43,8 +43,8 @@ Issues found during hands-on testing of the deployed prod site on mobile and des ### Product detail page - [x] PDP image gallery: scroll-snap carousel with dots (mobile), thumbnails + arrows + lightbox (desktop) - [x] Product category breadcrumbs look bad — review styling/layout -- [ ] Quantity selector on product page doesn't work -- [ ] Trust badges: two different tick icons, should use sentence case not title case +- [x] Quantity selector on product page doesn't work +- [x] Trust badges: two different tick icons, should use sentence case not title case - [ ] Real product variants need testing and refinement with live data ### Cart diff --git a/lib/simpleshop_theme_web/components/shop_components/content.ex b/lib/simpleshop_theme_web/components/shop_components/content.ex index 520c28a..ab8f62c 100644 --- a/lib/simpleshop_theme_web/components/shop_components/content.ex +++ b/lib/simpleshop_theme_web/components/shop_components/content.ex @@ -701,25 +701,24 @@ defmodule SimpleshopThemeWeb.ShopComponents.Content do end @doc """ - Renders trust badges (e.g., Free Delivery, Easy Returns). + Renders trust badges (e.g., free delivery, easy returns). ## Attributes * `items` - Optional. List of badge items. Each item is a map with: - - `icon` - Icon type: `:check` or `:shield` - - `title` - Badge title + - `title` - Badge title (sentence case) - `description` - Badge description - Defaults to Free Delivery and Easy Returns badges. + Defaults to "Made to order" and "Quality materials" badges. ## Examples <.trust_badges /> - <.trust_badges items={[%{icon: :check, title: "Custom", description: "Badge text"}]} /> + <.trust_badges items={[%{title: "Custom", description: "Badge text"}]} /> """ attr :items, :list, default: [ - %{icon: :check, title: "Made to Order", description: "Printed just for you"}, - %{icon: :shield, title: "Quality Materials", description: "Premium inks and substrates"} + %{title: "Made to order", description: "Printed just for you"}, + %{title: "Quality materials", description: "Premium inks and substrates"} ] def trust_badges(assigns) do @@ -730,7 +729,12 @@ defmodule SimpleshopThemeWeb.ShopComponents.Content do > <%= for item <- @items do %>
- <.trust_badge_icon icon={item.icon} /> + + +

{item.title}

{item.description}

@@ -741,55 +745,6 @@ defmodule SimpleshopThemeWeb.ShopComponents.Content do """ end - attr :icon, :atom, required: true - - defp trust_badge_icon(%{icon: :check} = assigns) do - ~H""" - - - - """ - end - - defp trust_badge_icon(%{icon: :shield} = assigns) do - ~H""" - - - - """ - end - - defp trust_badge_icon(assigns) do - ~H""" - - - - """ - end - @doc """ Renders a customer reviews section with collapsible header and review cards. diff --git a/lib/simpleshop_theme_web/components/shop_components/product.ex b/lib/simpleshop_theme_web/components/shop_components/product.ex index 6d939f3..b6a2dfc 100644 --- a/lib/simpleshop_theme_web/components/shop_components/product.ex +++ b/lib/simpleshop_theme_web/components/shop_components/product.ex @@ -1556,14 +1556,32 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do class="flex items-center" style="border: 2px solid var(--t-border-default); border-radius: var(--t-radius-input);" > - + {@quantity} - +
<%= if @in_stock do %> In stock diff --git a/lib/simpleshop_theme_web/live/shop_live/product_show.ex b/lib/simpleshop_theme_web/live/shop_live/product_show.ex index b7a1d42..43f27b1 100644 --- a/lib/simpleshop_theme_web/live/shop_live/product_show.ex +++ b/lib/simpleshop_theme_web/live/shop_live/product_show.ex @@ -130,6 +130,18 @@ defmodule SimpleshopThemeWeb.ShopLive.ProductShow do {:noreply, socket} end + @impl true + def handle_event("increment_quantity", _params, socket) do + quantity = min(socket.assigns.quantity + 1, 99) + {:noreply, assign(socket, :quantity, quantity)} + end + + @impl true + def handle_event("decrement_quantity", _params, socket) do + quantity = max(socket.assigns.quantity - 1, 1) + {:noreply, assign(socket, :quantity, quantity)} + end + @impl true def handle_event("add_to_cart", _params, socket) do variant = socket.assigns.selected_variant @@ -140,6 +152,7 @@ defmodule SimpleshopThemeWeb.ShopLive.ProductShow do socket = socket |> SimpleshopThemeWeb.CartHook.broadcast_and_update(cart) + |> assign(:quantity, 1) |> assign(:cart_drawer_open, true) |> assign(:cart_status, "#{socket.assigns.product.name} added to cart") diff --git a/test/simpleshop_theme_web/live/shop_live/product_show_test.exs b/test/simpleshop_theme_web/live/shop_live/product_show_test.exs index 0df53d2..3b1aa9f 100644 --- a/test/simpleshop_theme_web/live/shop_live/product_show_test.exs +++ b/test/simpleshop_theme_web/live/shop_live/product_show_test.exs @@ -104,6 +104,67 @@ defmodule SimpleshopThemeWeb.ShopLive.ProductShowTest do end end + describe "Quantity selector" do + test "renders quantity selector with initial value of 1", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/products/1") + + assert has_element?(view, "button[phx-click='decrement_quantity']") + assert has_element?(view, "button[phx-click='increment_quantity']") + end + + test "decrement button is disabled at quantity 1", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/products/1") + + assert has_element?(view, "button[phx-click='decrement_quantity'][disabled]") + end + + test "increment increases quantity", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/products/1") + + html = + view + |> element("button[phx-click='increment_quantity']") + |> render_click() + + # Quantity should now be 2, decrement no longer disabled + refute html =~ ~s(phx-click="decrement_quantity" disabled) + end + + test "decrement decreases quantity", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/products/1") + + # Increment twice to get to 3 + view |> element("button[phx-click='increment_quantity']") |> render_click() + view |> element("button[phx-click='increment_quantity']") |> render_click() + + html = + view + |> element("button[phx-click='decrement_quantity']") + |> render_click() + + # Should be back to 2 — decrement still enabled + refute html =~ ~s(phx-click="decrement_quantity" disabled) + end + + test "quantity resets to 1 after adding to cart", %{conn: conn} do + {:ok, view, _html} = live(conn, ~p"/products/1") + + # Increment to 3 + view |> element("button[phx-click='increment_quantity']") |> render_click() + view |> element("button[phx-click='increment_quantity']") |> render_click() + + # Add to cart + html = + view + |> element("button", "Add to basket") + |> render_click() + + # Decrement should be disabled again (quantity reset to 1) + assert html =~ ~s(phx-click="decrement_quantity") + assert html =~ "disabled" + end + end + describe "Add to cart" do test "add to cart opens the cart drawer", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/products/1")