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")