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>
This commit is contained in:
jamey
2026-02-13 01:26:39 +00:00
parent 0b4fe031b7
commit 35e0386abb
20 changed files with 1000 additions and 328 deletions

View File

@@ -17,10 +17,10 @@ defmodule SimpleshopTheme.Theme.PreviewDataTest do
assert is_map(product)
assert Map.has_key?(product, :id)
assert Map.has_key?(product, :name)
assert Map.has_key?(product, :title)
assert Map.has_key?(product, :description)
assert Map.has_key?(product, :price)
assert Map.has_key?(product, :image_url)
assert Map.has_key?(product, :cheapest_price)
assert Map.has_key?(product, :images)
assert Map.has_key?(product, :category)
assert Map.has_key?(product, :in_stock)
assert Map.has_key?(product, :on_sale)
@@ -30,30 +30,26 @@ defmodule SimpleshopTheme.Theme.PreviewDataTest do
products = PreviewData.products()
for product <- products do
assert is_integer(product.price)
assert product.price > 0
assert is_integer(product.cheapest_price)
assert product.cheapest_price > 0
if product.compare_at_price do
assert is_integer(product.compare_at_price)
assert product.compare_at_price > product.price
assert product.compare_at_price > product.cheapest_price
end
end
end
test "products have image URLs" do
test "products have images" do
products = PreviewData.products()
for product <- products do
assert is_binary(product.image_url)
# Images can be either local paths (starting with /) or full URLs
assert String.starts_with?(product.image_url, "/") or
String.starts_with?(product.image_url, "http")
assert is_list(product.images)
assert length(product.images) >= 1
if product.hover_image_url do
assert is_binary(product.hover_image_url)
assert String.starts_with?(product.hover_image_url, "/") or
String.starts_with?(product.hover_image_url, "http")
for image <- product.images do
assert is_integer(image.position)
assert is_binary(image.src) or not is_nil(image.image_id)
end
end
end
@@ -109,8 +105,8 @@ defmodule SimpleshopTheme.Theme.PreviewDataTest do
product = item.product
assert is_map(product)
assert Map.has_key?(product, :id)
assert Map.has_key?(product, :name)
assert Map.has_key?(product, :price)
assert Map.has_key?(product, :title)
assert Map.has_key?(product, :cheapest_price)
end
end
end