2026-02-13 16:02:25 +00:00
|
|
|
<.shop_layout {layout_assigns(assigns)} active_page="pdp">
|
2026-02-17 09:03:35 +00:00
|
|
|
<main id="main-content" class="page-container">
|
2026-01-31 14:24:58 +00:00
|
|
|
<.breadcrumb
|
2026-02-01 00:26:19 +00:00
|
|
|
items={
|
2026-02-10 15:59:34 +00:00
|
|
|
if @product.category do
|
|
|
|
|
[
|
|
|
|
|
%{
|
|
|
|
|
label: @product.category,
|
|
|
|
|
page: "collection",
|
|
|
|
|
href:
|
|
|
|
|
"/collections/#{@product.category |> String.downcase() |> String.replace(" ", "-")}"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
else
|
|
|
|
|
[]
|
|
|
|
|
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
|
|
|
[%{label: @product.title, current: true}]
|
2026-02-01 00:26:19 +00:00
|
|
|
}
|
2026-01-31 14:24:58 +00:00
|
|
|
mode={@mode}
|
|
|
|
|
/>
|
2026-01-17 21:57:43 +00:00
|
|
|
|
2026-02-17 09:03:35 +00:00
|
|
|
<div class="pdp-grid">
|
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
|
|
|
<.product_gallery images={@gallery_images} product_name={@product.title} />
|
2026-01-17 21:57:43 +00:00
|
|
|
|
|
|
|
|
<div>
|
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
|
|
|
<.product_info product={@product} display_price={@display_price} />
|
2026-02-03 22:17:48 +00:00
|
|
|
|
2026-02-24 22:56:19 +00:00
|
|
|
<form action="/cart/add" method="post" phx-submit="add_to_cart">
|
|
|
|
|
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
|
|
|
|
<input
|
|
|
|
|
type="hidden"
|
|
|
|
|
name="variant_id"
|
|
|
|
|
value={@selected_variant && @selected_variant.id}
|
2026-02-03 22:17:48 +00:00
|
|
|
/>
|
2026-02-24 22:56:19 +00:00
|
|
|
<input type="hidden" name="quantity" value={@quantity} />
|
2026-02-03 22:17:48 +00:00
|
|
|
|
2026-02-24 22:56:19 +00:00
|
|
|
<%!-- Dynamic variant selectors --%>
|
|
|
|
|
<%= for option_type <- @option_types do %>
|
|
|
|
|
<.variant_selector
|
|
|
|
|
option_type={option_type}
|
|
|
|
|
selected={@selected_options[option_type.name]}
|
|
|
|
|
available={@available_options[option_type.name] || []}
|
|
|
|
|
mode={@mode}
|
|
|
|
|
/>
|
|
|
|
|
<% end %>
|
2026-02-03 22:17:48 +00:00
|
|
|
|
2026-02-24 22:56:19 +00:00
|
|
|
<%!-- Fallback for products with no variant options --%>
|
|
|
|
|
<div
|
|
|
|
|
:if={@option_types == []}
|
|
|
|
|
class="pdp-variant-fallback"
|
|
|
|
|
>
|
|
|
|
|
One size
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<.quantity_selector quantity={@quantity} in_stock={@product.in_stock} />
|
|
|
|
|
<.add_to_cart_button mode={@mode} />
|
|
|
|
|
</form>
|
2026-01-17 21:57:43 +00:00
|
|
|
<.trust_badges :if={@theme_settings.pdp_trust_badges} />
|
|
|
|
|
<.product_details product={@product} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-01-31 14:24:58 +00:00
|
|
|
<.reviews_section
|
|
|
|
|
:if={@theme_settings.pdp_reviews}
|
2026-02-18 21:23:15 +00:00
|
|
|
reviews={Berrypod.Theme.PreviewData.reviews()}
|
2026-01-31 14:24:58 +00:00
|
|
|
average_rating={5}
|
|
|
|
|
total_count={24}
|
|
|
|
|
/>
|
2026-01-17 21:57:43 +00:00
|
|
|
|
|
|
|
|
<.related_products_section
|
|
|
|
|
:if={@theme_settings.pdp_related_products}
|
|
|
|
|
products={@related_products}
|
|
|
|
|
theme_settings={@theme_settings}
|
2026-01-17 22:17:59 +00:00
|
|
|
mode={@mode}
|
2026-01-17 21:57:43 +00:00
|
|
|
/>
|
|
|
|
|
</main>
|
2026-02-08 12:10:08 +00:00
|
|
|
</.shop_layout>
|