2026-02-18 21:23:15 +00:00
|
|
|
|
defmodule BerrypodWeb.ShopComponents.Cart do
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
@moduledoc false
|
|
|
|
|
|
|
|
|
|
|
|
use Phoenix.Component
|
|
|
|
|
|
|
2026-02-18 21:23:15 +00:00
|
|
|
|
import BerrypodWeb.ShopComponents.Base
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
2026-02-18 21:23:15 +00:00
|
|
|
|
alias Berrypod.Products.{Product, ProductImage}
|
add denormalized product fields and use Product structs throughout
Adds cheapest_price, compare_at_price, in_stock, on_sale columns to
products table (recomputed from variants after each sync). Shop
components now work with Product structs directly instead of plain
maps from PreviewData. Renames .name to .title, adds Product display
helpers (primary_image, hover_image, option_types) and ProductImage
helpers (display_url, direct_url, source_width). Adds Products context
query functions for storefront use (list_visible_products,
get_visible_product, list_categories with DB-level sort/filter).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:26:39 +00:00
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
defp close_cart_drawer_js do
|
|
|
|
|
|
Phoenix.LiveView.JS.push("close_cart_drawer")
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the cart drawer (floating sidebar).
|
|
|
|
|
|
|
|
|
|
|
|
The drawer slides in from the right when opened. It displays cart items
|
|
|
|
|
|
and checkout options. Follows WAI-ARIA dialog pattern for accessibility.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `cart_items` - List of cart items to display. Each item should have
|
|
|
|
|
|
`image`, `name`, `variant`, `price`, and `variant_id` keys. Default: []
|
|
|
|
|
|
* `subtotal` - The subtotal to display. Default: nil (shows "£0.00")
|
|
|
|
|
|
* `cart_count` - Number of items for screen reader description. Default: 0
|
|
|
|
|
|
* `mode` - Either `:live` (default) for real stores or `:preview` for theme editor.
|
|
|
|
|
|
In preview mode, "View basket" navigates via LiveView JS commands.
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.cart_drawer cart_items={@cart.items} subtotal={@cart.subtotal} />
|
|
|
|
|
|
<.cart_drawer cart_items={demo_items} subtotal="£72.00" mode={:preview} />
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
attr :cart_items, :list, default: []
|
|
|
|
|
|
attr :subtotal, :string, default: nil
|
2026-02-14 10:48:00 +00:00
|
|
|
|
attr :total, :string, default: nil
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :cart_count, :integer, default: 0
|
|
|
|
|
|
attr :cart_status, :string, default: nil
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
attr :open, :boolean, default: false
|
2026-02-14 10:48:00 +00:00
|
|
|
|
attr :shipping_estimate, :integer, default: nil
|
|
|
|
|
|
attr :country_code, :string, default: "GB"
|
|
|
|
|
|
attr :available_countries, :list, default: []
|
2026-03-04 14:02:49 +00:00
|
|
|
|
attr :stripe_connected, :boolean, default: true
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
def cart_drawer(assigns) do
|
|
|
|
|
|
assigns =
|
2026-02-14 10:48:00 +00:00
|
|
|
|
assign_new(assigns, :display_total, fn ->
|
|
|
|
|
|
assigns.total || assigns.subtotal || "£0.00"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<%!-- Screen reader announcements for cart changes --%>
|
|
|
|
|
|
<div aria-live="polite" aria-atomic="true" class="sr-only">
|
|
|
|
|
|
{@cart_status}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Cart Drawer Overlay -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
id="cart-drawer-overlay"
|
|
|
|
|
|
class={["cart-drawer-overlay", @open && "open"]}
|
|
|
|
|
|
aria-hidden={to_string(!@open)}
|
|
|
|
|
|
phx-click={close_cart_drawer_js()}
|
|
|
|
|
|
>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Cart Drawer -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
id="cart-drawer"
|
|
|
|
|
|
role="dialog"
|
|
|
|
|
|
aria-modal="true"
|
|
|
|
|
|
aria-labelledby="cart-drawer-title"
|
|
|
|
|
|
aria-describedby="cart-drawer-description"
|
|
|
|
|
|
aria-hidden={to_string(!@open)}
|
|
|
|
|
|
phx-hook="CartDrawer"
|
|
|
|
|
|
class={["cart-drawer", @open && "open"]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<p id="cart-drawer-description" class="sr-only">
|
|
|
|
|
|
Shopping basket with {@cart_count} {if @cart_count == 1, do: "item", else: "items"}. Press Escape to close.
|
|
|
|
|
|
</p>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="cart-drawer-header">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<h2
|
|
|
|
|
|
id="cart-drawer-title"
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class="cart-drawer-title"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
Your basket
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="cart-drawer-close"
|
|
|
|
|
|
phx-click={close_cart_drawer_js()}
|
|
|
|
|
|
aria-label="Close cart"
|
|
|
|
|
|
>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<svg
|
2026-02-17 10:32:48 +00:00
|
|
|
|
class="cart-drawer-close-icon"
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="20"
|
|
|
|
|
|
height="20"
|
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
|
>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
|
|
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="cart-drawer-items">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= if @cart_items == [] do %>
|
|
|
|
|
|
<.cart_empty_state mode={@mode} />
|
|
|
|
|
|
<% else %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<ul role="list" aria-label="Cart items">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= for item <- @cart_items do %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<li>
|
2026-02-11 00:15:04 +00:00
|
|
|
|
<.cart_item_row item={item} size={:compact} show_quantity_controls mode={@mode} />
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</li>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="cart-drawer-footer">
|
2026-02-14 10:48:00 +00:00
|
|
|
|
<.delivery_line
|
|
|
|
|
|
shipping_estimate={@shipping_estimate}
|
|
|
|
|
|
country_code={@country_code}
|
|
|
|
|
|
available_countries={@available_countries}
|
|
|
|
|
|
mode={@mode}
|
|
|
|
|
|
/>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="cart-drawer-total">
|
2026-02-14 10:48:00 +00:00
|
|
|
|
<span>{if @shipping_estimate, do: "Estimated total", else: "Subtotal"}</span>
|
|
|
|
|
|
<span>{@display_total}</span>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
2026-03-04 14:02:49 +00:00
|
|
|
|
<%= cond do %>
|
|
|
|
|
|
<% @mode == :preview -> %>
|
|
|
|
|
|
<button type="button" class="cart-drawer-checkout">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Checkout
|
|
|
|
|
|
</button>
|
2026-03-04 14:02:49 +00:00
|
|
|
|
<% !@stripe_connected -> %>
|
|
|
|
|
|
<button type="button" disabled class="cart-drawer-checkout">
|
|
|
|
|
|
Checkout
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<p class="cart-drawer-notice">Checkout isn't available yet.</p>
|
|
|
|
|
|
<% true -> %>
|
|
|
|
|
|
<form action="/checkout" method="post">
|
|
|
|
|
|
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
|
|
|
|
|
<button type="submit" class="cart-drawer-checkout">
|
|
|
|
|
|
Checkout
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</form>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Shared cart item row component used by both drawer and cart page.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `item` - Required. Cart item with `name`, `variant`, `price`, `quantity`, `image`, `variant_id`, `product_id`.
|
|
|
|
|
|
* `size` - Either `:compact` (drawer) or `:default` (cart page). Default: :default
|
|
|
|
|
|
* `show_quantity_controls` - Show +/- buttons. Default: false
|
|
|
|
|
|
* `mode` - Either `:live` or `:preview`. Default: :live
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :item, :map, required: true
|
|
|
|
|
|
attr :size, :atom, default: :default
|
|
|
|
|
|
attr :show_quantity_controls, :boolean, default: false
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
|
|
|
|
|
|
def cart_item_row(assigns) do
|
|
|
|
|
|
~H"""
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="cart-item-row"
|
2026-02-17 01:10:49 +00:00
|
|
|
|
data-size={if @size == :compact, do: "compact"}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
<%= if @mode != :preview do %>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<.link
|
2026-03-09 14:47:50 +00:00
|
|
|
|
patch={"/products/#{@item.product_id}"}
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class={["cart-item-image", !@item.image && "cart-item-image--empty"]}
|
|
|
|
|
|
data-size={if @size == :compact, do: "compact"}
|
|
|
|
|
|
style={if @item.image, do: "background-image: url('#{@item.image}');"}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-label={"View #{@item.name}"}
|
|
|
|
|
|
>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% else %>
|
|
|
|
|
|
<div
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class={["cart-item-image", !@item.image && "cart-item-image--empty"]}
|
|
|
|
|
|
data-size={if @size == :compact, do: "compact"}
|
|
|
|
|
|
style={if @item.image, do: "background-image: url('#{@item.image}');"}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<% end %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="cart-item-details">
|
|
|
|
|
|
<h3 class="cart-item-name" data-size={if @size == :compact, do: "compact"}>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= if @mode != :preview do %>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<.link
|
2026-03-09 14:47:50 +00:00
|
|
|
|
patch={"/products/#{@item.product_id}"}
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class="cart-item-name-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
{@item.name}
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% else %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<span>{@item.name}</span>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
<%= if @item.variant do %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<p class="cart-item-variant-text">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
{@item.variant}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
|
2026-03-13 13:34:22 +00:00
|
|
|
|
<p :if={Map.get(@item, :is_available) == false} class="cart-item-unavailable">
|
|
|
|
|
|
This item is currently unavailable
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<div class="cart-item-actions">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= if @show_quantity_controls do %>
|
2026-02-24 22:56:19 +00:00
|
|
|
|
<form
|
|
|
|
|
|
action="/cart/update"
|
|
|
|
|
|
method="post"
|
|
|
|
|
|
phx-submit="update_quantity_form"
|
|
|
|
|
|
class="cart-qty-group"
|
|
|
|
|
|
>
|
|
|
|
|
|
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
|
|
|
|
|
<input type="hidden" name="variant_id" value={@item.variant_id} />
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<button
|
2026-02-24 22:56:19 +00:00
|
|
|
|
type="submit"
|
|
|
|
|
|
name="quantity"
|
|
|
|
|
|
value={@item.quantity - 1}
|
2026-02-17 10:32:48 +00:00
|
|
|
|
class="cart-qty-btn"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-label={"Decrease quantity of #{@item.name}"}
|
|
|
|
|
|
>
|
|
|
|
|
|
−
|
|
|
|
|
|
</button>
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<span class="cart-qty-display">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
{@item.quantity}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<button
|
2026-02-24 22:56:19 +00:00
|
|
|
|
type="submit"
|
|
|
|
|
|
name="quantity"
|
|
|
|
|
|
value={@item.quantity + 1}
|
2026-02-17 10:32:48 +00:00
|
|
|
|
class="cart-qty-btn"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
aria-label={"Increase quantity of #{@item.name}"}
|
|
|
|
|
|
>
|
|
|
|
|
|
+
|
|
|
|
|
|
</button>
|
2026-02-24 22:56:19 +00:00
|
|
|
|
</form>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% else %>
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<span class="cart-qty-text">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Qty: {@item.quantity}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
|
|
|
|
|
|
<.cart_remove_button variant_id={@item.variant_id} item_name={@item.name} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="cart-item-price-col">
|
2026-02-17 01:10:49 +00:00
|
|
|
|
<p class="cart-item-price" data-size={if @size == :compact, do: "compact"}>
|
2026-02-18 21:23:15 +00:00
|
|
|
|
{Berrypod.Cart.format_price(@item.price * @item.quantity)}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Cart empty state component.
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
|
|
|
|
|
|
def cart_empty_state(assigns) do
|
|
|
|
|
|
~H"""
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="cart-empty">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<svg
|
2026-02-17 10:32:48 +00:00
|
|
|
|
class="cart-empty-icon"
|
2026-02-11 14:46:12 +00:00
|
|
|
|
width="64"
|
|
|
|
|
|
height="64"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
|
fill="none"
|
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
|
stroke-width="1.5"
|
|
|
|
|
|
>
|
|
|
|
|
|
<path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"></path>
|
|
|
|
|
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
|
|
|
|
<path d="M16 10a4 4 0 01-8 0"></path>
|
|
|
|
|
|
</svg>
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<p>Your basket is empty</p>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<%= if @mode == :preview do %>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page="collection"
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class="cart-continue-link"
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
>
|
|
|
|
|
|
Continue shopping
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<% else %>
|
2026-02-11 14:46:12 +00:00
|
|
|
|
<.link
|
2026-03-09 14:47:50 +00:00
|
|
|
|
patch="/collections/all"
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class="cart-continue-link"
|
2026-02-11 14:46:12 +00:00
|
|
|
|
>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Continue shopping
|
2026-02-11 14:46:12 +00:00
|
|
|
|
</.link>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Remove button for cart items.
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :variant_id, :string, required: true
|
|
|
|
|
|
attr :item_name, :string, default: "item"
|
|
|
|
|
|
|
|
|
|
|
|
def cart_remove_button(assigns) do
|
|
|
|
|
|
~H"""
|
2026-02-24 22:56:19 +00:00
|
|
|
|
<form action="/cart/remove" method="post" phx-submit="remove_item_form" class="cart-remove-form">
|
|
|
|
|
|
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
|
|
|
|
|
<input type="hidden" name="variant_id" value={@variant_id} />
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="submit"
|
|
|
|
|
|
class="cart-remove-btn"
|
|
|
|
|
|
aria-label={"Remove #{@item_name} from cart"}
|
|
|
|
|
|
>
|
|
|
|
|
|
Remove
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</form>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders a cart item row.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `item` - Required. Map with `product` (containing `image_url`, `name`, `price`), `variant`, and `quantity`.
|
|
|
|
|
|
* `currency` - Optional. Currency symbol. Defaults to "£".
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.cart_item item={item} />
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :item, :map, required: true
|
|
|
|
|
|
|
|
|
|
|
|
def cart_item(assigns) do
|
|
|
|
|
|
~H"""
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<.shop_card class="cart-page-item">
|
|
|
|
|
|
<div class="cart-page-image">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<img
|
add denormalized product fields and use Product structs throughout
Adds cheapest_price, compare_at_price, in_stock, on_sale columns to
products table (recomputed from variants after each sync). Shop
components now work with Product structs directly instead of plain
maps from PreviewData. Renames .name to .title, adds Product display
helpers (primary_image, hover_image, option_types) and ProductImage
helpers (display_url, direct_url, source_width). Adds Products context
query functions for storefront use (list_visible_products,
get_visible_product, list_categories with DB-level sort/filter).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:26:39 +00:00
|
|
|
|
src={cart_item_image(@item.product)}
|
|
|
|
|
|
alt={@item.product.title}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
width="96"
|
|
|
|
|
|
height="96"
|
|
|
|
|
|
loading="lazy"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="cart-page-item-info">
|
|
|
|
|
|
<h3 class="cart-page-item-name">
|
add denormalized product fields and use Product structs throughout
Adds cheapest_price, compare_at_price, in_stock, on_sale columns to
products table (recomputed from variants after each sync). Shop
components now work with Product structs directly instead of plain
maps from PreviewData. Renames .name to .title, adds Product display
helpers (primary_image, hover_image, option_types) and ProductImage
helpers (display_url, direct_url, source_width). Adds Products context
query functions for storefront use (list_visible_products,
get_visible_product, list_categories with DB-level sort/filter).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:26:39 +00:00
|
|
|
|
{@item.product.title}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</h3>
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<p class="cart-page-item-variant">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
{@item.variant}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="cart-page-item-actions">
|
|
|
|
|
|
<div class="cart-qty-group">
|
|
|
|
|
|
<button class="cart-qty-btn">−</button>
|
|
|
|
|
|
<span class="cart-qty-display">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
{@item.quantity}
|
|
|
|
|
|
</span>
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<button class="cart-qty-btn">+</button>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<button class="cart-page-item-remove">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Remove
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="cart-page-item-price-col">
|
|
|
|
|
|
<p class="cart-page-item-price">
|
2026-02-18 21:23:15 +00:00
|
|
|
|
{Berrypod.Cart.format_price(@item.product.cheapest_price * @item.quantity)}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</.shop_card>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
add denormalized product fields and use Product structs throughout
Adds cheapest_price, compare_at_price, in_stock, on_sale columns to
products table (recomputed from variants after each sync). Shop
components now work with Product structs directly instead of plain
maps from PreviewData. Renames .name to .title, adds Product display
helpers (primary_image, hover_image, option_types) and ProductImage
helpers (display_url, direct_url, source_width). Adds Products context
query functions for storefront use (list_visible_products,
get_visible_product, list_categories with DB-level sort/filter).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:26:39 +00:00
|
|
|
|
defp cart_item_image(product) do
|
2026-02-16 17:47:41 +00:00
|
|
|
|
ProductImage.url(Product.primary_image(product), 400)
|
add denormalized product fields and use Product structs throughout
Adds cheapest_price, compare_at_price, in_stock, on_sale columns to
products table (recomputed from variants after each sync). Shop
components now work with Product structs directly instead of plain
maps from PreviewData. Renames .name to .title, adds Product display
helpers (primary_image, hover_image, option_types) and ProductImage
helpers (display_url, direct_url, source_width). Adds Products context
query functions for storefront use (list_visible_products,
get_visible_product, list_categories with DB-level sort/filter).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 01:26:39 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
2026-02-14 10:48:00 +00:00
|
|
|
|
# Shared delivery line used by both cart_drawer and order_summary.
|
|
|
|
|
|
# Shows a country <select> when rates are available, falls back to plain text.
|
|
|
|
|
|
attr :shipping_estimate, :integer, default: nil
|
|
|
|
|
|
attr :country_code, :string, default: "GB"
|
|
|
|
|
|
attr :available_countries, :list, default: []
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
|
|
|
|
|
|
defp delivery_line(assigns) do
|
|
|
|
|
|
~H"""
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="delivery-line">
|
|
|
|
|
|
<span class="delivery-line-label">
|
2026-02-14 10:48:00 +00:00
|
|
|
|
Delivery to
|
|
|
|
|
|
<%= if @available_countries != [] and @mode != :preview do %>
|
2026-02-24 23:14:48 +00:00
|
|
|
|
<form action="/cart/country" method="post" phx-change="change_country">
|
|
|
|
|
|
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
2026-02-14 10:48:00 +00:00
|
|
|
|
<select
|
|
|
|
|
|
name="country"
|
2026-02-17 01:10:49 +00:00
|
|
|
|
class="delivery-select"
|
2026-02-14 10:48:00 +00:00
|
|
|
|
aria-label="Delivery country"
|
|
|
|
|
|
>
|
|
|
|
|
|
<%= for {code, name} <- @available_countries do %>
|
|
|
|
|
|
<option value={code} selected={code == @country_code}>{name}</option>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</select>
|
2026-02-24 23:14:48 +00:00
|
|
|
|
<noscript>
|
|
|
|
|
|
<button type="submit" class="themed-button delivery-country-submit">Update</button>
|
|
|
|
|
|
</noscript>
|
2026-02-14 10:48:00 +00:00
|
|
|
|
</form>
|
|
|
|
|
|
<% else %>
|
2026-02-18 21:23:15 +00:00
|
|
|
|
<span>{Berrypod.Shipping.country_name(@country_code)}</span>
|
2026-02-14 10:48:00 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<%= if @shipping_estimate do %>
|
2026-02-18 21:23:15 +00:00
|
|
|
|
<span>{Berrypod.Cart.format_price(@shipping_estimate)}</span>
|
2026-02-14 10:48:00 +00:00
|
|
|
|
<% else %>
|
|
|
|
|
|
<span>Calculated at checkout</span>
|
|
|
|
|
|
<% end %>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
@doc """
|
|
|
|
|
|
Renders the order summary card.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `subtotal` - Required. Subtotal amount (in pence/cents).
|
2026-02-14 10:48:00 +00:00
|
|
|
|
* `shipping_estimate` - Optional. Shipping estimate in pence.
|
|
|
|
|
|
* `country_code` - Optional. Current country code. Default "GB".
|
|
|
|
|
|
* `available_countries` - Optional. List of `{code, name}` tuples.
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
* `mode` - Either `:live` (default) or `:preview`.
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.order_summary subtotal={3600} />
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :subtotal, :integer, required: true
|
2026-02-14 10:48:00 +00:00
|
|
|
|
attr :shipping_estimate, :integer, default: nil
|
|
|
|
|
|
attr :country_code, :string, default: "GB"
|
|
|
|
|
|
attr :available_countries, :list, default: []
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
attr :mode, :atom, default: :live
|
2026-03-04 14:02:49 +00:00
|
|
|
|
attr :stripe_connected, :boolean, default: true
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
|
|
|
|
|
|
def order_summary(assigns) do
|
2026-02-14 10:48:00 +00:00
|
|
|
|
assigns =
|
|
|
|
|
|
assign(assigns, :estimated_total, assigns.subtotal + (assigns.shipping_estimate || 0))
|
|
|
|
|
|
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
~H"""
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<.shop_card class="order-summary-card">
|
|
|
|
|
|
<h2 class="order-summary-heading">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Order summary
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="order-summary-lines">
|
|
|
|
|
|
<div class="order-summary-line">
|
|
|
|
|
|
<span>Subtotal</span>
|
|
|
|
|
|
<span>
|
2026-02-18 21:23:15 +00:00
|
|
|
|
{Berrypod.Cart.format_price(@subtotal)}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2026-02-14 10:48:00 +00:00
|
|
|
|
<.delivery_line
|
|
|
|
|
|
shipping_estimate={@shipping_estimate}
|
|
|
|
|
|
country_code={@country_code}
|
|
|
|
|
|
available_countries={@available_countries}
|
|
|
|
|
|
mode={@mode}
|
|
|
|
|
|
/>
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="order-summary-divider">
|
|
|
|
|
|
<div class="order-summary-total">
|
|
|
|
|
|
<span>
|
2026-02-14 10:48:00 +00:00
|
|
|
|
{if @shipping_estimate, do: "Estimated total", else: "Subtotal"}
|
|
|
|
|
|
</span>
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<span>
|
2026-02-18 21:23:15 +00:00
|
|
|
|
{Berrypod.Cart.format_price(@estimated_total)}
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-04 14:02:49 +00:00
|
|
|
|
<%= cond do %>
|
|
|
|
|
|
<% @mode == :preview -> %>
|
|
|
|
|
|
<.shop_button class="order-summary-checkout">
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
Checkout
|
|
|
|
|
|
</.shop_button>
|
2026-03-04 14:02:49 +00:00
|
|
|
|
<.shop_button_outline
|
|
|
|
|
|
phx-click="change_preview_page"
|
|
|
|
|
|
phx-value-page="collection"
|
|
|
|
|
|
class="order-summary-continue"
|
|
|
|
|
|
>
|
|
|
|
|
|
Continue shopping
|
|
|
|
|
|
</.shop_button_outline>
|
|
|
|
|
|
<% !@stripe_connected -> %>
|
|
|
|
|
|
<.shop_button disabled class="order-summary-checkout">
|
|
|
|
|
|
Checkout
|
|
|
|
|
|
</.shop_button>
|
|
|
|
|
|
<p class="order-summary-notice">Checkout isn't available yet.</p>
|
|
|
|
|
|
<.shop_link_outline
|
|
|
|
|
|
href="/collections/all"
|
|
|
|
|
|
class="order-summary-continue"
|
|
|
|
|
|
>
|
|
|
|
|
|
Continue shopping
|
|
|
|
|
|
</.shop_link_outline>
|
|
|
|
|
|
<% true -> %>
|
|
|
|
|
|
<form action="/checkout" method="post" class="order-summary-checkout-form">
|
|
|
|
|
|
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
|
|
|
|
|
<.shop_button type="submit" class="order-summary-checkout">
|
|
|
|
|
|
Checkout
|
|
|
|
|
|
</.shop_button>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
<.shop_link_outline
|
|
|
|
|
|
href="/collections/all"
|
|
|
|
|
|
class="order-summary-continue"
|
|
|
|
|
|
>
|
|
|
|
|
|
Continue shopping
|
|
|
|
|
|
</.shop_link_outline>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
<% end %>
|
|
|
|
|
|
</.shop_card>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@doc """
|
|
|
|
|
|
Renders a cart items list with order summary layout.
|
|
|
|
|
|
|
|
|
|
|
|
## Attributes
|
|
|
|
|
|
|
|
|
|
|
|
* `items` - Required. List of cart items.
|
|
|
|
|
|
* `subtotal` - Required. Subtotal in pence/cents.
|
|
|
|
|
|
* `currency` - Optional. Currency symbol. Defaults to "£".
|
|
|
|
|
|
* `mode` - Either `:live` (default) or `:preview`.
|
|
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
|
|
<.cart_layout items={@cart_items} subtotal={3600} mode={:preview} />
|
|
|
|
|
|
"""
|
|
|
|
|
|
attr :items, :list, required: true
|
|
|
|
|
|
attr :subtotal, :integer, required: true
|
|
|
|
|
|
attr :mode, :atom, default: :live
|
|
|
|
|
|
|
|
|
|
|
|
def cart_layout(assigns) do
|
|
|
|
|
|
~H"""
|
2026-02-17 10:32:48 +00:00
|
|
|
|
<div class="cart-layout">
|
|
|
|
|
|
<div class="cart-items-stack">
|
|
|
|
|
|
<%= for item <- @items do %>
|
|
|
|
|
|
<.cart_item item={item} />
|
|
|
|
|
|
<% end %>
|
refactor: split shop_components.ex into 5 focused sub-modules
4,487-line monolith → 23-line facade + 5 modules:
- Base (inputs, buttons, cards)
- Layout (header, footer, mobile nav, shop_layout)
- Cart (drawer, items, order summary)
- Product (cards, gallery, variant selector, hero)
- Content (rich text, images, contact, reviews)
`use SimpleshopThemeWeb.ShopComponents` imports all sub-modules.
No single file over ~1,600 lines now.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 14:30:25 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<.order_summary subtotal={@subtotal} mode={@mode} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
"""
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|