chore: apply mix format to codebase

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jamey 2026-01-31 14:24:58 +00:00
parent d97918d66a
commit 336b2bb81d
43 changed files with 2227 additions and 1204 deletions

View File

@ -194,7 +194,9 @@ defmodule Mix.Tasks.GenerateMockups do
""") """)
if failed > 0 do if failed > 0 do
Mix.shell().error("Some products failed to generate. Check the output above for details.") Mix.shell().error(
"Some products failed to generate. Check the output above for details."
)
end end
end end
end end

View File

@ -31,7 +31,8 @@ defmodule SimpleshopTheme.Images.Optimizer do
{width, _height, _} <- Image.shape(image), {width, _height, _} <- Image.shape(image),
{:ok, resized} <- maybe_resize(image, width), {:ok, resized} <- maybe_resize(image, width),
{final_width, final_height, _} <- Image.shape(resized), {final_width, final_height, _} <- Image.shape(resized),
{:ok, webp_data} <- Image.write(resized, :memory, suffix: ".webp", quality: @storage_quality) do {:ok, webp_data} <-
Image.write(resized, :memory, suffix: ".webp", quality: @storage_quality) do
{:ok, webp_data, final_width, final_height} {:ok, webp_data, final_width, final_height}
end end
rescue rescue
@ -191,9 +192,20 @@ defmodule SimpleshopTheme.Images.Optimizer do
widths = applicable_widths(source_width) widths = applicable_widths(source_width)
tasks = [ tasks = [
Task.async(fn -> generate_variant_to_dir(vips_image, output_basename, output_dir, "thumb", :jpg, @thumb_size) end) Task.async(fn ->
generate_variant_to_dir(
vips_image,
output_basename,
output_dir,
"thumb",
:jpg,
@thumb_size
)
end)
| for w <- widths, fmt <- @pregenerated_formats do | for w <- widths, fmt <- @pregenerated_formats do
Task.async(fn -> generate_variant_to_dir(vips_image, output_basename, output_dir, w, fmt, w) end) Task.async(fn ->
generate_variant_to_dir(vips_image, output_basename, output_dir, w, fmt, w)
end)
end end
] ]

View File

@ -59,7 +59,9 @@ defmodule SimpleshopTheme.Images.VariantCache do
if to_process == [] do if to_process == [] do
Logger.info("[VariantCache] All database image variants up to date") Logger.info("[VariantCache] All database image variants up to date")
else else
Logger.info("[VariantCache] Enqueueing #{length(to_process)} database images for processing") Logger.info(
"[VariantCache] Enqueueing #{length(to_process)} database images for processing"
)
Enum.each(to_process, fn image -> Enum.each(to_process, fn image ->
image image

View File

@ -119,7 +119,12 @@ defmodule SimpleshopTheme.Media do
""" """
def get_logo do def get_logo do
Repo.one(from i in ImageSchema, where: i.image_type == "logo", order_by: [desc: i.inserted_at], limit: 1) Repo.one(
from i in ImageSchema,
where: i.image_type == "logo",
order_by: [desc: i.inserted_at],
limit: 1
)
end end
@doc """ @doc """
@ -132,7 +137,12 @@ defmodule SimpleshopTheme.Media do
""" """
def get_header do def get_header do
Repo.one(from i in ImageSchema, where: i.image_type == "header", order_by: [desc: i.inserted_at], limit: 1) Repo.one(
from i in ImageSchema,
where: i.image_type == "header",
order_by: [desc: i.inserted_at],
limit: 1
)
end end
@doc """ @doc """

View File

@ -46,7 +46,8 @@ defmodule SimpleshopTheme.Media.Image do
defp detect_svg(changeset) do defp detect_svg(changeset) do
content_type = get_change(changeset, :content_type) content_type = get_change(changeset, :content_type)
if content_type == "image/svg+xml" or String.ends_with?(get_change(changeset, :filename) || "", ".svg") do if content_type == "image/svg+xml" or
String.ends_with?(get_change(changeset, :filename) || "", ".svg") do
changeset changeset
|> put_change(:is_svg, true) |> put_change(:is_svg, true)
|> maybe_store_svg_content() |> maybe_store_svg_content()

View File

@ -23,7 +23,8 @@ defmodule SimpleshopTheme.Media.SVGRecolorer do
""" """
@spec recolor(String.t(), String.t()) :: String.t() @spec recolor(String.t(), String.t()) :: String.t()
def recolor(svg_content, target_color) when is_binary(svg_content) and is_binary(target_color) do def recolor(svg_content, target_color)
when is_binary(svg_content) and is_binary(target_color) do
svg_content svg_content
|> recolor_fill_attributes(target_color) |> recolor_fill_attributes(target_color)
|> recolor_stroke_attributes(target_color) |> recolor_stroke_attributes(target_color)

View File

@ -163,9 +163,17 @@ defmodule SimpleshopTheme.Mockups.Generator do
hoodie: %{blueprint_id: nil, print_provider_id: nil, search_term: "Pullover Hoodie"}, hoodie: %{blueprint_id: nil, print_provider_id: nil, search_term: "Pullover Hoodie"},
tote: %{blueprint_id: nil, print_provider_id: nil, search_term: "Cotton Tote Bag"}, tote: %{blueprint_id: nil, print_provider_id: nil, search_term: "Cotton Tote Bag"},
mug: %{blueprint_id: nil, print_provider_id: nil, search_term: "Mug 11oz"}, mug: %{blueprint_id: nil, print_provider_id: nil, search_term: "Mug 11oz"},
cushion: %{blueprint_id: nil, print_provider_id: nil, search_term: "Spun Polyester Square Pillow"}, cushion: %{
blueprint_id: nil,
print_provider_id: nil,
search_term: "Spun Polyester Square Pillow"
},
blanket: %{blueprint_id: nil, print_provider_id: nil, search_term: "Sherpa Fleece Blanket"}, blanket: %{blueprint_id: nil, print_provider_id: nil, search_term: "Sherpa Fleece Blanket"},
notebook: %{blueprint_id: nil, print_provider_id: nil, search_term: "Hardcover Journal Matte"}, notebook: %{
blueprint_id: nil,
print_provider_id: nil,
search_term: "Hardcover Journal Matte"
},
phone_case: %{blueprint_id: nil, print_provider_id: nil, search_term: "Tough Phone Cases"}, phone_case: %{blueprint_id: nil, print_provider_id: nil, search_term: "Tough Phone Cases"},
laptop_sleeve: %{blueprint_id: nil, print_provider_id: nil, search_term: "Laptop Sleeve"} laptop_sleeve: %{blueprint_id: nil, print_provider_id: nil, search_term: "Laptop Sleeve"}
} }
@ -253,12 +261,12 @@ defmodule SimpleshopTheme.Mockups.Generator do
""" """
def calculate_cover_scale(artwork_width, artwork_height, placeholder_width, placeholder_height) def calculate_cover_scale(artwork_width, artwork_height, placeholder_width, placeholder_height)
when is_number(artwork_width) and is_number(artwork_height) and when is_number(artwork_width) and is_number(artwork_height) and
is_number(placeholder_width) and is_number(placeholder_height) and is_number(placeholder_width) and is_number(placeholder_height) and
artwork_width > 0 and artwork_height > 0 and artwork_width > 0 and artwork_height > 0 and
placeholder_width > 0 and placeholder_height > 0 do placeholder_width > 0 and placeholder_height > 0 do
# For cover: use the larger scale to ensure full coverage # For cover: use the larger scale to ensure full coverage
width_scale = 1.0 width_scale = 1.0
height_scale = (placeholder_height * artwork_width) / (artwork_height * placeholder_width) height_scale = placeholder_height * artwork_width / (artwork_height * placeholder_width)
max(width_scale, height_scale) max(width_scale, height_scale)
end end
@ -267,21 +275,36 @@ defmodule SimpleshopTheme.Mockups.Generator do
@doc """ @doc """
Create a product with the uploaded artwork. Create a product with the uploaded artwork.
""" """
def create_product(shop_id, product_def, image_id, image_width, image_height, blueprint_id, print_provider_id, variants) do def create_product(
shop_id,
product_def,
image_id,
image_width,
image_height,
blueprint_id,
print_provider_id,
variants
) do
# Get the first variant for simplicity (typically a standard size/color) # Get the first variant for simplicity (typically a standard size/color)
variant = hd(variants) variant = hd(variants)
variant_id = variant["id"] variant_id = variant["id"]
# Get placeholder info # Get placeholder info
placeholders = variant["placeholders"] || [] placeholders = variant["placeholders"] || []
front_placeholder = Enum.find(placeholders, fn p -> p["position"] == "front" end) || hd(placeholders)
front_placeholder =
Enum.find(placeholders, fn p -> p["position"] == "front" end) || hd(placeholders)
# Extract placeholder dimensions and calculate cover scale # Extract placeholder dimensions and calculate cover scale
placeholder_width = front_placeholder["width"] placeholder_width = front_placeholder["width"]
placeholder_height = front_placeholder["height"] placeholder_height = front_placeholder["height"]
scale = calculate_cover_scale(image_width, image_height, placeholder_width, placeholder_height)
IO.puts(" Scale calculation: artwork #{image_width}x#{image_height}, placeholder #{placeholder_width}x#{placeholder_height} -> scale #{Float.round(scale, 3)}") scale =
calculate_cover_scale(image_width, image_height, placeholder_width, placeholder_height)
IO.puts(
" Scale calculation: artwork #{image_width}x#{image_height}, placeholder #{placeholder_width}x#{placeholder_height} -> scale #{Float.round(scale, 3)}"
)
product_data = %{ product_data = %{
title: product_def.name, title: product_def.name,
@ -439,7 +462,17 @@ defmodule SimpleshopTheme.Mockups.Generator do
image_height = upload["height"], image_height = upload["height"],
_ = IO.puts(" Artwork uploaded (ID: #{image_id}, #{image_width}x#{image_height})"), _ = IO.puts(" Artwork uploaded (ID: #{image_id}, #{image_width}x#{image_height})"),
_ = IO.puts(" Creating product..."), _ = IO.puts(" Creating product..."),
{:ok, product} <- create_product(shop_id, product_def, image_id, image_width, image_height, blueprint_id, provider_id, variants), {:ok, product} <-
create_product(
shop_id,
product_def,
image_id,
image_width,
image_height,
blueprint_id,
provider_id,
variants
),
product_id = product["id"], product_id = product["id"],
mockup_urls = extract_mockup_urls(product), mockup_urls = extract_mockup_urls(product),
_ = IO.puts(" Product created (ID: #{product_id})"), _ = IO.puts(" Product created (ID: #{product_id})"),

View File

@ -287,14 +287,17 @@ defmodule SimpleshopTheme.Products do
if MapSet.size(removed_ids) > 0 do if MapSet.size(removed_ids) > 0 do
from(v in ProductVariant, from(v in ProductVariant,
where: v.product_id == ^product_id and v.provider_variant_id in ^MapSet.to_list(removed_ids) where:
v.product_id == ^product_id and v.provider_variant_id in ^MapSet.to_list(removed_ids)
) )
|> Repo.delete_all() |> Repo.delete_all()
end end
# Upsert incoming variants # Upsert incoming variants
Enum.map(variants, fn variant_data -> Enum.map(variants, fn variant_data ->
provider_variant_id = variant_data[:provider_variant_id] || variant_data["provider_variant_id"] provider_variant_id =
variant_data[:provider_variant_id] || variant_data["provider_variant_id"]
attrs = Map.put(variant_data, :product_id, product_id) attrs = Map.put(variant_data, :product_id, product_id)
case get_variant_by_provider(product_id, provider_variant_id) do case get_variant_by_provider(product_id, provider_variant_id) do

View File

@ -88,7 +88,8 @@ defmodule SimpleshopTheme.Products.ProductVariant do
Formats the options as a human-readable title. Formats the options as a human-readable title.
E.g., %{"Size" => "Large", "Color" => "Blue"} -> "Large / Blue" E.g., %{"Size" => "Large", "Color" => "Blue"} -> "Large / Blue"
""" """
def options_title(%__MODULE__{options: options}) when is_map(options) and map_size(options) > 0 do def options_title(%__MODULE__{options: options})
when is_map(options) and map_size(options) > 0 do
options options
|> Map.values() |> Map.values()
|> Enum.join(" / ") |> Enum.join(" / ")

View File

@ -93,7 +93,10 @@ defmodule SimpleshopTheme.Settings.ThemeSettings do
]) ])
|> validate_required([:mood, :typography, :shape, :density]) |> validate_required([:mood, :typography, :shape, :density])
|> validate_inclusion(:mood, ~w(neutral warm cool dark)) |> validate_inclusion(:mood, ~w(neutral warm cool dark))
|> validate_inclusion(:typography, ~w(clean editorial modern classic friendly minimal impulse)) |> validate_inclusion(
:typography,
~w(clean editorial modern classic friendly minimal impulse)
)
|> validate_inclusion(:shape, ~w(sharp soft round pill)) |> validate_inclusion(:shape, ~w(sharp soft round pill))
|> validate_inclusion(:density, ~w(spacious balanced compact)) |> validate_inclusion(:density, ~w(spacious balanced compact))
|> validate_inclusion(:grid_columns, ~w(2 3 4)) |> validate_inclusion(:grid_columns, ~w(2 3 4))
@ -101,8 +104,14 @@ defmodule SimpleshopTheme.Settings.ThemeSettings do
|> validate_inclusion(:logo_mode, ~w(text-only logo-text logo-only)) |> validate_inclusion(:logo_mode, ~w(text-only logo-text logo-only))
|> validate_number(:logo_size, greater_than_or_equal_to: 24, less_than_or_equal_to: 120) |> validate_number(:logo_size, greater_than_or_equal_to: 24, less_than_or_equal_to: 120)
|> validate_number(:header_zoom, greater_than_or_equal_to: 100, less_than_or_equal_to: 200) |> validate_number(:header_zoom, greater_than_or_equal_to: 100, less_than_or_equal_to: 200)
|> validate_number(:header_position_x, greater_than_or_equal_to: 0, less_than_or_equal_to: 100) |> validate_number(:header_position_x,
|> validate_number(:header_position_y, greater_than_or_equal_to: 0, less_than_or_equal_to: 100) greater_than_or_equal_to: 0,
less_than_or_equal_to: 100
)
|> validate_number(:header_position_y,
greater_than_or_equal_to: 0,
less_than_or_equal_to: 100
)
|> validate_inclusion(:layout_width, ~w(contained wide full)) |> validate_inclusion(:layout_width, ~w(contained wide full))
|> validate_inclusion(:card_shadow, ~w(none sm md lg)) |> validate_inclusion(:card_shadow, ~w(none sm md lg))
|> validate_inclusion(:font_size, ~w(small medium large)) |> validate_inclusion(:font_size, ~w(small medium large))

View File

@ -234,5 +234,4 @@ defmodule SimpleshopTheme.Theme.Fonts do
"" ""
end end
end end
end end

View File

@ -67,7 +67,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
%{ %{
rating: 5, rating: 5,
title: "Absolutely beautiful", title: "Absolutely beautiful",
body: "The quality exceeded my expectations. The colours are vibrant and the paper feels premium. It's now pride of place in my living room.", body:
"The quality exceeded my expectations. The colours are vibrant and the paper feels premium. It's now pride of place in my living room.",
author: "Sarah M.", author: "Sarah M.",
date: "2 weeks ago", date: "2 weeks ago",
verified: true verified: true
@ -75,7 +76,8 @@ defmodule SimpleshopTheme.Theme.PreviewData do
%{ %{
rating: 4, rating: 4,
title: "Great gift", title: "Great gift",
body: "Bought this as a gift and it arrived beautifully packaged. Fast shipping too. Would definitely order again.", body:
"Bought this as a gift and it arrived beautifully packaged. Fast shipping too. Would definitely order again.",
author: "James T.", author: "James T.",
date: "1 month ago", date: "1 month ago",
verified: true verified: true
@ -90,13 +92,33 @@ defmodule SimpleshopTheme.Theme.PreviewData do
""" """
def about_content do def about_content do
[ [
%{type: :lead, text: "I'm Emma, a nature photographer based in the UK. What started as weekend walks with my camera has grown into something I never expected a little shop where I can share my favourite captures with others."}, %{
%{type: :paragraph, text: "Every design in this shop comes from my own photography. Whether it's early morning mist over the hills, autumn leaves in the local woods, or the quiet beauty of wildflower meadows, I'm drawn to the peaceful moments that nature offers."}, type: :lead,
%{type: :paragraph, text: "I work with quality print partners to bring these images to life on products you can actually use and enjoy from art prints for your walls to mugs for your morning tea."}, text:
"I'm Emma, a nature photographer based in the UK. What started as weekend walks with my camera has grown into something I never expected a little shop where I can share my favourite captures with others."
},
%{
type: :paragraph,
text:
"Every design in this shop comes from my own photography. Whether it's early morning mist over the hills, autumn leaves in the local woods, or the quiet beauty of wildflower meadows, I'm drawn to the peaceful moments that nature offers."
},
%{
type: :paragraph,
text:
"I work with quality print partners to bring these images to life on products you can actually use and enjoy from art prints for your walls to mugs for your morning tea."
},
%{type: :heading, text: "Quality you can trust"}, %{type: :heading, text: "Quality you can trust"},
%{type: :paragraph, text: "I've carefully chosen print partners who share my commitment to quality. Every product is made to order using premium materials and printing techniques that ensure vibrant colours and lasting quality."}, %{
type: :paragraph,
text:
"I've carefully chosen print partners who share my commitment to quality. Every product is made to order using premium materials and printing techniques that ensure vibrant colours and lasting quality."
},
%{type: :heading, text: "Printed sustainably"}, %{type: :heading, text: "Printed sustainably"},
%{type: :paragraph, text: "Because each item is printed on demand, there's no waste from unsold stock. My print partners use eco-friendly inks where possible, and products are shipped directly to you to minimise unnecessary handling."}, %{
type: :paragraph,
text:
"Because each item is printed on demand, there's no waste from unsold stock. My print partners use eco-friendly inks where possible, and products are shipped directly to you to minimise unnecessary handling."
},
%{type: :closing, text: "Thank you for visiting. It means a lot that you're here."} %{type: :closing, text: "Thank you for visiting. It means a lot that you're here."}
] ]
end end
@ -427,42 +449,48 @@ defmodule SimpleshopTheme.Theme.PreviewData do
%{ %{
id: "1", id: "1",
author: "Sarah M.", author: "Sarah M.",
content: "The print quality is absolutely stunning - colours are exactly as shown online. My living room looks so much better now!", content:
"The print quality is absolutely stunning - colours are exactly as shown online. My living room looks so much better now!",
rating: 5, rating: 5,
date: "2025-01-15" date: "2025-01-15"
}, },
%{ %{
id: "2", id: "2",
author: "James L.", author: "James L.",
content: "Bought the forest hoodie as a gift. The packaging was lovely and the quality exceeded expectations. Will order again!", content:
"Bought the forest hoodie as a gift. The packaging was lovely and the quality exceeded expectations. Will order again!",
rating: 5, rating: 5,
date: "2025-01-10" date: "2025-01-10"
}, },
%{ %{
id: "3", id: "3",
author: "Emily R.", author: "Emily R.",
content: "My new favourite mug! I love sipping my morning tea while looking at that beautiful fern design.", content:
"My new favourite mug! I love sipping my morning tea while looking at that beautiful fern design.",
rating: 5, rating: 5,
date: "2025-01-05" date: "2025-01-05"
}, },
%{ %{
id: "4", id: "4",
author: "Michael T.", author: "Michael T.",
content: "The tote bag is so sturdy - I use it for everything now. Great print quality that hasn't faded at all.", content:
"The tote bag is so sturdy - I use it for everything now. Great print quality that hasn't faded at all.",
rating: 5, rating: 5,
date: "2024-12-28" date: "2024-12-28"
}, },
%{ %{
id: "5", id: "5",
author: "Lisa K.", author: "Lisa K.",
content: "The night sky blanket is gorgeous and so cosy. Perfect for film nights on the sofa. Highly recommend!", content:
"The night sky blanket is gorgeous and so cosy. Perfect for film nights on the sofa. Highly recommend!",
rating: 5, rating: 5,
date: "2024-12-20" date: "2024-12-20"
}, },
%{ %{
id: "6", id: "6",
author: "David P.", author: "David P.",
content: "Ordered several prints for my new flat. They arrived well packaged and look amazing on the wall.", content:
"Ordered several prints for my new flat. They arrived well packaged and look amazing on the wall.",
rating: 5, rating: 5,
date: "2024-12-15" date: "2024-12-15"
} }

View File

@ -17,7 +17,8 @@ defmodule SimpleshopThemeWeb do
those modules here. those modules here.
""" """
def static_paths, do: ~w(assets css fonts images image_cache mockups favicon.ico robots.txt demo.html) def static_paths,
do: ~w(assets css fonts images image_cache mockups favicon.ico robots.txt demo.html)
def router do def router do
quote do quote do

View File

@ -1 +1 @@
<%= @inner_content %> {@inner_content}

View File

@ -4,8 +4,14 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} /> <meta name="csrf-token" content={get_csrf_token()} />
<meta name="description" content={assigns[:page_description] || @theme_settings.site_description || "Welcome to #{@theme_settings.site_name}"} /> <meta
<.live_title><%= assigns[:page_title] || @theme_settings.site_name %></.live_title> name="description"
content={
assigns[:page_description] || @theme_settings.site_description ||
"Welcome to #{@theme_settings.site_name}"
}
/>
<.live_title>{assigns[:page_title] || @theme_settings.site_name}</.live_title>
<!-- Preload critical fonts for the current typography preset --> <!-- Preload critical fonts for the current typography preset -->
<%= for preload <- SimpleshopTheme.Theme.Fonts.preload_links( <%= for preload <- SimpleshopTheme.Theme.Fonts.preload_links(
@theme_settings.typography, @theme_settings.typography,
@ -17,7 +23,9 @@
<script defer phx-track-static src={~p"/assets/js/app.js"}> <script defer phx-track-static src={~p"/assets/js/app.js"}>
</script> </script>
<!-- Generated theme CSS with @font-face declarations --> <!-- Generated theme CSS with @font-face declarations -->
<style id="theme-css"><%= Phoenix.HTML.raw(@generated_css) %></style> <style id="theme-css">
<%= Phoenix.HTML.raw(@generated_css) %>
</style>
</head> </head>
<body class="h-full"> <body class="h-full">
<div <div

View File

@ -1,11 +1,21 @@
<div class="shop-container min-h-screen pb-20 md:pb-0" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen pb-20 md:pb-0"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<.skip_link /> <.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
<.announcement_bar theme_settings={@theme_settings} /> <.announcement_bar theme_settings={@theme_settings} />
<% end %> <% end %>
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="about" mode={@mode} cart_count={@cart_count} /> <.shop_header
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
active_page="about"
mode={@mode}
cart_count={@cart_count}
/>
<main id="main-content" class="content-page" style="background-color: var(--t-surface-base);"> <main id="main-content" class="content-page" style="background-color: var(--t-surface-base);">
<.hero_section <.hero_section

View File

@ -1,11 +1,21 @@
<div class="shop-container min-h-screen pb-20 md:pb-0" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen pb-20 md:pb-0"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<.skip_link /> <.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
<.announcement_bar theme_settings={@theme_settings} /> <.announcement_bar theme_settings={@theme_settings} />
<% end %> <% end %>
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="cart" mode={@mode} cart_count={@cart_count} /> <.shop_header
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
active_page="cart"
mode={@mode}
cart_count={@cart_count}
/>
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<.page_title text="Your basket" /> <.page_title text="Your basket" />

View File

@ -1,11 +1,21 @@
<div class="shop-container min-h-screen pb-20 md:pb-0" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen pb-20 md:pb-0"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<.skip_link /> <.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
<.announcement_bar theme_settings={@theme_settings} /> <.announcement_bar theme_settings={@theme_settings} />
<% end %> <% end %>
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="collection" mode={@mode} cart_count={@cart_count} /> <.shop_header
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
active_page="collection"
mode={@mode}
cart_count={@cart_count}
/>
<main id="main-content"> <main id="main-content">
<.collection_header title="All Products" product_count={length(@preview_data.products)} /> <.collection_header title="All Products" product_count={length(@preview_data.products)} />

View File

@ -1,11 +1,21 @@
<div class="shop-container min-h-screen pb-20 md:pb-0" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen pb-20 md:pb-0"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<.skip_link /> <.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
<.announcement_bar theme_settings={@theme_settings} /> <.announcement_bar theme_settings={@theme_settings} />
<% end %> <% end %>
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="contact" mode={@mode} cart_count={@cart_count} /> <.shop_header
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
active_page="contact"
mode={@mode}
cart_count={@cart_count}
/>
<main id="main-content" class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16"> <main id="main-content" class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
<.hero_section <.hero_section
@ -20,11 +30,14 @@
<div class="flex flex-col gap-6"> <div class="flex flex-col gap-6">
<.order_tracking_card /> <.order_tracking_card />
<.info_card title="Handy to know" items={[ <.info_card
%{label: "Printing", value: "2-5 business days"}, title="Handy to know"
%{label: "Delivery", value: "3-7 business days after printing"}, items={[
%{label: "Returns", value: "Happy to help with faulty or damaged items"} %{label: "Printing", value: "2-5 business days"},
]} /> %{label: "Delivery", value: "3-7 business days after printing"},
%{label: "Returns", value: "Happy to help with faulty or damaged items"}
]}
/>
<.newsletter_card /> <.newsletter_card />

View File

@ -1,13 +1,27 @@
<div class="shop-container min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<.skip_link /> <.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
<.announcement_bar theme_settings={@theme_settings} /> <.announcement_bar theme_settings={@theme_settings} />
<% end %> <% end %>
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="error" mode={@mode} cart_count={@cart_count} /> <.shop_header
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
active_page="error"
mode={@mode}
cart_count={@cart_count}
/>
<main id="main-content" class="flex items-center justify-center" style="min-height: calc(100vh - 4rem);"> <main
id="main-content"
class="flex items-center justify-center"
style="min-height: calc(100vh - 4rem);"
>
<div class="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-16"> <div class="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
<.hero_section <.hero_section
variant={:error} variant={:error}

View File

@ -1,11 +1,21 @@
<div class="shop-container min-h-screen pb-20 md:pb-0" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen pb-20 md:pb-0"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<.skip_link /> <.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
<.announcement_bar theme_settings={@theme_settings} /> <.announcement_bar theme_settings={@theme_settings} />
<% end %> <% end %>
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="home" mode={@mode} cart_count={@cart_count} /> <.shop_header
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
active_page="home"
mode={@mode}
cart_count={@cart_count}
/>
<main id="main-content"> <main id="main-content">
<.hero_section <.hero_section

View File

@ -1,18 +1,36 @@
<div class="shop-container min-h-screen pb-20 md:pb-0" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen pb-20 md:pb-0"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<.skip_link /> <.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
<.announcement_bar theme_settings={@theme_settings} /> <.announcement_bar theme_settings={@theme_settings} />
<% end %> <% end %>
<.shop_header theme_settings={@theme_settings} logo_image={@logo_image} header_image={@header_image} active_page="pdp" mode={@mode} cart_count={@cart_count} /> <.shop_header
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
active_page="pdp"
mode={@mode}
cart_count={@cart_count}
/>
<main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <main id="main-content" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<.breadcrumb items={[ <.breadcrumb
%{label: "Home", page: "home", href: "/"}, items={[
%{label: @product.category, page: "collection", href: "/collections/#{@product.category |> String.downcase() |> String.replace(" ", "-")}"}, %{label: "Home", page: "home", href: "/"},
%{label: @product.name, current: true} %{
]} mode={@mode} /> label: @product.category,
page: "collection",
href:
"/collections/#{@product.category |> String.downcase() |> String.replace(" ", "-")}"
},
%{label: @product.name, current: true}
]}
mode={@mode}
/>
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 mb-16"> <div class="grid grid-cols-1 md:grid-cols-2 gap-12 mb-16">
<.product_gallery images={@gallery_images} product_name={@product.name} /> <.product_gallery images={@gallery_images} product_name={@product.name} />
@ -27,7 +45,12 @@
</div> </div>
</div> </div>
<.reviews_section :if={@theme_settings.pdp_reviews} reviews={SimpleshopTheme.Theme.PreviewData.reviews()} average_rating={5} total_count={24} /> <.reviews_section
:if={@theme_settings.pdp_reviews}
reviews={SimpleshopTheme.Theme.PreviewData.reviews()}
average_rating={5}
total_count={24}
/>
<.related_products_section <.related_products_section
:if={@theme_settings.pdp_related_products} :if={@theme_settings.pdp_related_products}

File diff suppressed because it is too large Load Diff

View File

@ -12,13 +12,21 @@ defmodule SimpleshopThemeWeb.ErrorHTML do
alias SimpleshopTheme.Theme.{CSSCache, CSSGenerator, PreviewData} alias SimpleshopTheme.Theme.{CSSCache, CSSGenerator, PreviewData}
def render("404.html", assigns) do def render("404.html", assigns) do
render_error_page(assigns, "404", "Page Not Found", render_error_page(
"Sorry, we couldn't find the page you're looking for. Perhaps you've mistyped the URL or the page has been moved.") assigns,
"404",
"Page Not Found",
"Sorry, we couldn't find the page you're looking for. Perhaps you've mistyped the URL or the page has been moved."
)
end end
def render("500.html", assigns) do def render("500.html", assigns) do
render_error_page(assigns, "500", "Server Error", render_error_page(
"Something went wrong on our end. Please try again later or contact support if the problem persists.") assigns,
"500",
"Server Error",
"Something went wrong on our end. Please try again later or contact support if the problem persists."
)
end end
def render(template, _assigns) do def render(template, _assigns) do
@ -57,23 +65,25 @@ defmodule SimpleshopThemeWeb.ErrorHTML do
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= @error_code %> - <%= @error_title %></title> <title>{@error_code} - {@error_title}</title>
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} /> <link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
<style id="theme-css"> <style id="theme-css">
<%= Phoenix.HTML.raw(@generated_css) %> <%= Phoenix.HTML.raw(@generated_css) %>
</style> </style>
</head> </head>
<body class="h-full"> <body class="h-full">
<div class="shop-root themed h-full" <div
data-mood={@theme_settings.mood} class="shop-root themed h-full"
data-typography={@theme_settings.typography} data-mood={@theme_settings.mood}
data-shape={@theme_settings.shape} data-typography={@theme_settings.typography}
data-density={@theme_settings.density} data-shape={@theme_settings.shape}
data-grid={@theme_settings.grid_columns} data-density={@theme_settings.density}
data-header={@theme_settings.header_layout} data-grid={@theme_settings.grid_columns}
data-sticky={to_string(@theme_settings.sticky_header)} data-header={@theme_settings.header_layout}
data-layout={@theme_settings.layout_width} data-sticky={to_string(@theme_settings.sticky_header)}
data-shadow={@theme_settings.card_shadow}> data-layout={@theme_settings.layout_width}
data-shadow={@theme_settings.card_shadow}
>
<SimpleshopThemeWeb.PageTemplates.error <SimpleshopThemeWeb.PageTemplates.error
theme_settings={@theme_settings} theme_settings={@theme_settings}
logo_image={@logo_image} logo_image={@logo_image}
@ -96,14 +106,18 @@ defmodule SimpleshopThemeWeb.ErrorHTML do
defp load_theme_data do defp load_theme_data do
try do try do
theme_settings = Settings.get_theme_settings() theme_settings = Settings.get_theme_settings()
generated_css = generated_css =
case CSSCache.get() do case CSSCache.get() do
{:ok, css} -> css {:ok, css} ->
css
:miss -> :miss ->
css = CSSGenerator.generate(theme_settings) css = CSSGenerator.generate(theme_settings)
CSSCache.put(css) CSSCache.put(css)
css css
end end
{theme_settings, generated_css} {theme_settings, generated_css}
rescue rescue
_ -> {%ThemeSettings{}, ""} _ -> {%ThemeSettings{}, ""}

View File

@ -11,7 +11,9 @@ defmodule SimpleshopThemeWeb.ShopLive.About do
generated_css = generated_css =
case CSSCache.get() do case CSSCache.get() do
{:ok, css} -> css {:ok, css} ->
css
:miss -> :miss ->
css = CSSGenerator.generate(theme_settings) css = CSSGenerator.generate(theme_settings)
CSSCache.put(css) CSSCache.put(css)

View File

@ -11,7 +11,9 @@ defmodule SimpleshopThemeWeb.ShopLive.Cart do
generated_css = generated_css =
case CSSCache.get() do case CSSCache.get() do
{:ok, css} -> css {:ok, css} ->
css
:miss -> :miss ->
css = CSSGenerator.generate(theme_settings) css = CSSGenerator.generate(theme_settings)
CSSCache.put(css) CSSCache.put(css)
@ -24,9 +26,11 @@ defmodule SimpleshopThemeWeb.ShopLive.Cart do
# For now, use preview data for cart items # For now, use preview data for cart items
# In a real implementation, this would come from session/database # In a real implementation, this would come from session/database
cart_page_items = PreviewData.cart_items() cart_page_items = PreviewData.cart_items()
cart_page_subtotal = Enum.reduce(cart_page_items, 0, fn item, acc ->
acc + item.product.price * item.quantity cart_page_subtotal =
end) Enum.reduce(cart_page_items, 0, fn item, acc ->
acc + item.product.price * item.quantity
end)
socket = socket =
socket socket

View File

@ -20,7 +20,9 @@ defmodule SimpleshopThemeWeb.ShopLive.Collection do
generated_css = generated_css =
case CSSCache.get() do case CSSCache.get() do
{:ok, css} -> css {:ok, css} ->
css
:miss -> :miss ->
css = CSSGenerator.generate(theme_settings) css = CSSGenerator.generate(theme_settings)
CSSCache.put(css) CSSCache.put(css)
@ -82,7 +84,9 @@ defmodule SimpleshopThemeWeb.ShopLive.Collection do
@impl true @impl true
def handle_event("sort_changed", %{"sort" => sort}, socket) do def handle_event("sort_changed", %{"sort" => sort}, socket) do
slug = if socket.assigns.current_category, do: socket.assigns.current_category.slug, else: "all" slug =
if socket.assigns.current_category, do: socket.assigns.current_category.slug, else: "all"
{:noreply, push_patch(socket, to: ~p"/collections/#{slug}?sort=#{sort}")} {:noreply, push_patch(socket, to: ~p"/collections/#{slug}?sort=#{sort}")}
end end
@ -100,7 +104,10 @@ defmodule SimpleshopThemeWeb.ShopLive.Collection do
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div class="shop-container min-h-screen pb-20 md:pb-0" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"> <div
class="shop-container min-h-screen pb-20 md:pb-0"
style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);"
>
<SimpleshopThemeWeb.ShopComponents.skip_link /> <SimpleshopThemeWeb.ShopComponents.skip_link />
<%= if @theme_settings.announcement_bar do %> <%= if @theme_settings.announcement_bar do %>
@ -145,7 +152,11 @@ defmodule SimpleshopThemeWeb.ShopLive.Collection do
<%= if @products == [] do %> <%= if @products == [] do %>
<div class="text-center py-16" style="color: var(--t-text-secondary);"> <div class="text-center py-16" style="color: var(--t-text-secondary);">
<p class="text-lg">No products found in this collection.</p> <p class="text-lg">No products found in this collection.</p>
<.link navigate={~p"/collections/all"} class="mt-4 inline-block underline" style="color: var(--t-text-accent);"> <.link
navigate={~p"/collections/all"}
class="mt-4 inline-block underline"
style="color: var(--t-text-accent);"
>
View all products View all products
</.link> </.link>
</div> </div>
@ -155,9 +166,15 @@ defmodule SimpleshopThemeWeb.ShopLive.Collection do
<SimpleshopThemeWeb.ShopComponents.shop_footer theme_settings={@theme_settings} mode={@mode} /> <SimpleshopThemeWeb.ShopComponents.shop_footer theme_settings={@theme_settings} mode={@mode} />
<SimpleshopThemeWeb.ShopComponents.cart_drawer cart_items={@cart_items} subtotal={@cart_subtotal} mode={@mode} /> <SimpleshopThemeWeb.ShopComponents.cart_drawer
cart_items={@cart_items}
subtotal={@cart_subtotal}
mode={@mode}
/>
<SimpleshopThemeWeb.ShopComponents.search_modal hint_text={~s(Try searching for "mountain", "forest", or "ocean")} /> <SimpleshopThemeWeb.ShopComponents.search_modal hint_text={
~s(Try searching for "mountain", "forest", or "ocean")
} />
<SimpleshopThemeWeb.ShopComponents.mobile_bottom_nav active_page="collection" mode={@mode} /> <SimpleshopThemeWeb.ShopComponents.mobile_bottom_nav active_page="collection" mode={@mode} />
</div> </div>
@ -176,10 +193,12 @@ defmodule SimpleshopThemeWeb.ShopLive.Collection do
"px-4 py-2 rounded-full text-sm transition-colors", "px-4 py-2 rounded-full text-sm transition-colors",
if(@current_slug == nil, do: "font-medium", else: "hover:opacity-80") if(@current_slug == nil, do: "font-medium", else: "hover:opacity-80")
]} ]}
style={if(@current_slug == nil, style={
do: "background-color: var(--t-accent); color: var(--t-text-on-accent);", if(@current_slug == nil,
else: "background-color: var(--t-surface-raised); color: var(--t-text-primary);" do: "background-color: var(--t-accent); color: var(--t-text-on-accent);",
)} else: "background-color: var(--t-surface-raised); color: var(--t-text-primary);"
)
}
> >
All All
</.link> </.link>
@ -192,10 +211,12 @@ defmodule SimpleshopThemeWeb.ShopLive.Collection do
"px-4 py-2 rounded-full text-sm transition-colors", "px-4 py-2 rounded-full text-sm transition-colors",
if(@current_slug == category.slug, do: "font-medium", else: "hover:opacity-80") if(@current_slug == category.slug, do: "font-medium", else: "hover:opacity-80")
]} ]}
style={if(@current_slug == category.slug, style={
do: "background-color: var(--t-accent); color: var(--t-text-on-accent);", if(@current_slug == category.slug,
else: "background-color: var(--t-surface-raised); color: var(--t-text-primary);" do: "background-color: var(--t-accent); color: var(--t-text-on-accent);",
)} else: "background-color: var(--t-surface-raised); color: var(--t-text-primary);"
)
}
> >
{category.name} {category.name}
</.link> </.link>

View File

@ -11,7 +11,9 @@ defmodule SimpleshopThemeWeb.ShopLive.Contact do
generated_css = generated_css =
case CSSCache.get() do case CSSCache.get() do
{:ok, css} -> css {:ok, css} ->
css
:miss -> :miss ->
css = CSSGenerator.generate(theme_settings) css = CSSGenerator.generate(theme_settings)
CSSCache.put(css) CSSCache.put(css)

View File

@ -11,7 +11,9 @@ defmodule SimpleshopThemeWeb.ShopLive.Home do
generated_css = generated_css =
case CSSCache.get() do case CSSCache.get() do
{:ok, css} -> css {:ok, css} ->
css
:miss -> :miss ->
css = CSSGenerator.generate(theme_settings) css = CSSGenerator.generate(theme_settings)
CSSCache.put(css) CSSCache.put(css)

View File

@ -11,7 +11,9 @@ defmodule SimpleshopThemeWeb.ShopLive.ProductShow do
generated_css = generated_css =
case CSSCache.get() do case CSSCache.get() do
{:ok, css} -> css {:ok, css} ->
css
:miss -> :miss ->
css = CSSGenerator.generate(theme_settings) css = CSSGenerator.generate(theme_settings)
CSSCache.put(css) CSSCache.put(css)

View File

@ -10,6 +10,7 @@ defmodule SimpleshopThemeWeb.ThemeLive.Index do
theme_settings = Settings.get_theme_settings() theme_settings = Settings.get_theme_settings()
generated_css = CSSGenerator.generate(theme_settings) generated_css = CSSGenerator.generate(theme_settings)
active_preset = Presets.detect_preset(theme_settings) active_preset = Presets.detect_preset(theme_settings)
preview_data = %{ preview_data = %{
products: PreviewData.products(), products: PreviewData.products(),
cart_items: PreviewData.cart_items(), cart_items: PreviewData.cart_items(),
@ -361,7 +362,9 @@ defmodule SimpleshopThemeWeb.ThemeLive.Index do
defp preview_page(%{page: :cart} = assigns) do defp preview_page(%{page: :cart} = assigns) do
cart_items = assigns.preview_data.cart_items cart_items = assigns.preview_data.cart_items
subtotal = Enum.reduce(cart_items, 0, fn item, acc -> acc + item.product.price * item.quantity end)
subtotal =
Enum.reduce(cart_items, 0, fn item, acc -> acc + item.product.price * item.quantity end)
assigns = assigns =
assigns assigns

File diff suppressed because it is too large Load Diff

View File

@ -126,6 +126,7 @@ defmodule SimpleshopThemeWeb.UserLive.Login do
end end
defp local_mail_adapter? do defp local_mail_adapter? do
Application.get_env(:simpleshop_theme, SimpleshopTheme.Mailer)[:adapter] == Swoosh.Adapters.Local Application.get_env(:simpleshop_theme, SimpleshopTheme.Mailer)[:adapter] ==
Swoosh.Adapters.Local
end end
end end

View File

@ -4,7 +4,10 @@ defmodule SimpleshopTheme.Repo.Migrations.CreateProducts do
def change do def change do
create table(:products, primary_key: false) do create table(:products, primary_key: false) do
add :id, :binary_id, primary_key: true add :id, :binary_id, primary_key: true
add :provider_connection_id, references(:provider_connections, type: :binary_id, on_delete: :delete_all), null: false
add :provider_connection_id,
references(:provider_connections, type: :binary_id, on_delete: :delete_all), null: false
add :provider_product_id, :string, null: false add :provider_product_id, :string, null: false
add :title, :string, null: false add :title, :string, null: false
add :description, :text add :description, :text

View File

@ -4,7 +4,10 @@ defmodule SimpleshopTheme.Repo.Migrations.CreateProductImages do
def change do def change do
create table(:product_images, primary_key: false) do create table(:product_images, primary_key: false) do
add :id, :binary_id, primary_key: true add :id, :binary_id, primary_key: true
add :product_id, references(:products, type: :binary_id, on_delete: :delete_all), null: false
add :product_id, references(:products, type: :binary_id, on_delete: :delete_all),
null: false
add :src, :string, null: false add :src, :string, null: false
add :position, :integer, default: 0, null: false add :position, :integer, default: 0, null: false
add :alt, :string add :alt, :string

View File

@ -4,7 +4,10 @@ defmodule SimpleshopTheme.Repo.Migrations.CreateProductVariants do
def change do def change do
create table(:product_variants, primary_key: false) do create table(:product_variants, primary_key: false) do
add :id, :binary_id, primary_key: true add :id, :binary_id, primary_key: true
add :product_id, references(:products, type: :binary_id, on_delete: :delete_all), null: false
add :product_id, references(:products, type: :binary_id, on_delete: :delete_all),
null: false
add :provider_variant_id, :string, null: false add :provider_variant_id, :string, null: false
add :title, :string, null: false add :title, :string, null: false
add :sku, :string add :sku, :string

View File

@ -70,6 +70,7 @@ defmodule SimpleshopTheme.Media.SVGRecolorerTest do
svg = """ svg = """
<svg><style>.st0{fill:#FFFFFF;}.st1{fill:#EF1D1D;stroke:#000000;}</style><path class="st0"/></svg> <svg><style>.st0{fill:#FFFFFF;}.st1{fill:#EF1D1D;stroke:#000000;}</style><path class="st0"/></svg>
""" """
result = SVGRecolorer.recolor(svg, "#ff6600") result = SVGRecolorer.recolor(svg, "#ff6600")
assert result =~ "fill:#ff6600" assert result =~ "fill:#ff6600"
refute result =~ "#FFFFFF" refute result =~ "#FFFFFF"
@ -80,6 +81,7 @@ defmodule SimpleshopTheme.Media.SVGRecolorerTest do
svg = """ svg = """
<svg><style>.st1{stroke:#000000;stroke-miterlimit:10;}</style><path class="st1"/></svg> <svg><style>.st1{stroke:#000000;stroke-miterlimit:10;}</style><path class="st1"/></svg>
""" """
result = SVGRecolorer.recolor(svg, "#ff6600") result = SVGRecolorer.recolor(svg, "#ff6600")
assert result =~ "stroke:#ff6600" assert result =~ "stroke:#ff6600"
refute result =~ "#000000" refute result =~ "#000000"
@ -89,6 +91,7 @@ defmodule SimpleshopTheme.Media.SVGRecolorerTest do
svg = """ svg = """
<svg><style>.st0{fill:none;stroke:#000;}</style><path class="st0"/></svg> <svg><style>.st0{fill:none;stroke:#000;}</style><path class="st0"/></svg>
""" """
result = SVGRecolorer.recolor(svg, "#ff6600") result = SVGRecolorer.recolor(svg, "#ff6600")
assert result =~ "fill:none" assert result =~ "fill:none"
assert result =~ "stroke:#ff6600" assert result =~ "stroke:#ff6600"
@ -98,6 +101,7 @@ defmodule SimpleshopTheme.Media.SVGRecolorerTest do
svg = """ svg = """
<svg><style>.icon{fill:black;stroke:white;}</style><path class="icon"/></svg> <svg><style>.icon{fill:black;stroke:white;}</style><path class="icon"/></svg>
""" """
result = SVGRecolorer.recolor(svg, "#ff6600") result = SVGRecolorer.recolor(svg, "#ff6600")
assert result =~ "fill:#ff6600" assert result =~ "fill:#ff6600"
assert result =~ "stroke:#ff6600" assert result =~ "stroke:#ff6600"

View File

@ -96,7 +96,10 @@ defmodule SimpleshopTheme.Products.ProductTest do
test "stores provider_data as map", %{conn: conn} do test "stores provider_data as map", %{conn: conn} do
provider_data = %{"blueprint_id" => 145, "print_provider_id" => 29, "extra" => "value"} provider_data = %{"blueprint_id" => 145, "print_provider_id" => 29, "extra" => "value"}
attrs = valid_product_attrs(%{provider_connection_id: conn.id, provider_data: provider_data})
attrs =
valid_product_attrs(%{provider_connection_id: conn.id, provider_data: provider_data})
changeset = Product.changeset(%Product{}, attrs) changeset = Product.changeset(%Product{}, attrs)
assert changeset.valid? assert changeset.valid?

View File

@ -437,7 +437,9 @@ defmodule SimpleshopTheme.ProductsTest do
test "updates existing variants" do test "updates existing variants" do
product = product_fixture() product = product_fixture()
existing = product_variant_fixture(%{product: product, provider_variant_id: "v1", price: 2000})
existing =
product_variant_fixture(%{product: product, provider_variant_id: "v1", price: 2000})
variants = [ variants = [
%{provider_variant_id: "v1", title: "Small Updated", price: 2200} %{provider_variant_id: "v1", title: "Small Updated", price: 2200}

View File

@ -76,13 +76,15 @@ defmodule SimpleshopTheme.SettingsTest do
# Cache should now contain new CSS with the red accent color # Cache should now contain new CSS with the red accent color
{:ok, updated_css} = CSSCache.get() {:ok, updated_css} = CSSCache.get()
assert updated_css =~ ".themed {" assert updated_css =~ ".themed {"
assert updated_css =~ "--t-accent-h: 0" # Red = hue 0 # Red = hue 0
assert updated_css =~ "--t-accent-h: 0"
# Change to blue # Change to blue
{:ok, _settings} = Settings.update_theme_settings(%{accent_color: "#0000ff"}) {:ok, _settings} = Settings.update_theme_settings(%{accent_color: "#0000ff"})
{:ok, blue_css} = CSSCache.get() {:ok, blue_css} = CSSCache.get()
assert blue_css =~ "--t-accent-h: 240" # Blue = hue 240 # Blue = hue 240
assert blue_css =~ "--t-accent-h: 240"
# Restore default # Restore default
CSSCache.warm() CSSCache.warm()

View File

@ -61,7 +61,9 @@ defmodule SimpleshopTheme.Sync.ProductSyncWorkerTest do
test "new/2 with scheduled_at creates scheduled job" do test "new/2 with scheduled_at creates scheduled job" do
future = DateTime.add(DateTime.utc_now(), 60, :second) future = DateTime.add(DateTime.utc_now(), 60, :second)
changeset = ProductSyncWorker.new(%{provider_connection_id: "test-id"}, scheduled_at: future)
changeset =
ProductSyncWorker.new(%{provider_connection_id: "test-id"}, scheduled_at: future)
assert changeset.changes.scheduled_at == future assert changeset.changes.scheduled_at == future
end end

View File

@ -51,6 +51,7 @@ defmodule SimpleshopTheme.Theme.PreviewDataTest do
if product.hover_image_url do if product.hover_image_url do
assert is_binary(product.hover_image_url) assert is_binary(product.hover_image_url)
assert String.starts_with?(product.hover_image_url, "/") or assert String.starts_with?(product.hover_image_url, "/") or
String.starts_with?(product.hover_image_url, "http") String.starts_with?(product.hover_image_url, "http")
end end

View File

@ -2,7 +2,9 @@ defmodule SimpleshopThemeWeb.ErrorJSONTest do
use SimpleshopThemeWeb.ConnCase, async: true use SimpleshopThemeWeb.ConnCase, async: true
test "renders 404" do test "renders 404" do
assert SimpleshopThemeWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}} assert SimpleshopThemeWeb.ErrorJSON.render("404.json", %{}) == %{
errors: %{detail: "Not Found"}
}
end end
test "renders 500" do test "renders 500" do