From 0fe48baaa8d6baa5f222df3f46dbdfba978b880d Mon Sep 17 00:00:00 2001 From: jamey Date: Mon, 16 Feb 2026 00:29:20 +0000 Subject: [PATCH] filter Printify options to enabled variants and order by hero colour Printify's API returns all blueprint option values (e.g. 21 colours) even when only a few are enabled as variants. This caused the PDP to show phantom colour swatches with no images or purchasable variants. Now filter_options_by_variants strips option values to only those with enabled variants, ordered by first appearance so the hero/default colour leads the swatch list. Co-Authored-By: Claude Opus 4.6 --- lib/simpleshop_theme/providers/printify.ex | 56 +++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/simpleshop_theme/providers/printify.ex b/lib/simpleshop_theme/providers/printify.ex index 0696e45..15aa0d6 100644 --- a/lib/simpleshop_theme/providers/printify.ex +++ b/lib/simpleshop_theme/providers/printify.ex @@ -338,6 +338,7 @@ defmodule SimpleshopTheme.Providers.Printify do options = raw["options"] || [] raw_variants = raw["variants"] || [] color_lookup = build_image_color_lookup(raw_variants, options) + filtered_options = filter_options_by_variants(options, raw_variants) %{ provider_product_id: to_string(raw["id"]), @@ -350,7 +351,7 @@ defmodule SimpleshopTheme.Providers.Printify do blueprint_id: raw["blueprint_id"], print_provider_id: raw["print_provider_id"], tags: raw["tags"] || [], - options: options, + options: filtered_options, raw: raw } } @@ -382,6 +383,59 @@ defmodule SimpleshopTheme.Providers.Printify do end) end + # Filter blueprint options to only include values present in enabled variants, + # ordered to match variant ordering (so the default variant's colour comes first). + defp filter_options_by_variants(options, raw_variants) do + enabled_order = enabled_option_value_order(options, raw_variants) + + Enum.map(options, fn opt -> + name = opt["name"] + + case Map.get(enabled_order, name) do + nil -> + opt + + ordered_values -> + values_by_title = Map.new(opt["values"] || [], fn v -> {v["title"], v} end) + + sorted = + ordered_values + |> Enum.map(&Map.get(values_by_title, &1)) + |> Enum.reject(&is_nil/1) + + Map.put(opt, "values", sorted) + end + end) + end + + # Returns %{"Colors" => ["Dark Heather", "Navy", ...], "Sizes" => ["S", "2XL"]} + # preserving first-seen order from enabled variants. + defp enabled_option_value_order(options, raw_variants) do + option_names = Enum.map(options, & &1["name"]) + + raw_variants + |> Enum.filter(& &1["is_enabled"]) + |> Enum.reduce(%{}, fn var, acc -> + parts = String.split(var["title"] || "", " / ") + + option_names + |> Enum.with_index() + |> Enum.reduce(acc, fn {name, idx}, inner_acc -> + case Enum.at(parts, idx) do + nil -> + inner_acc + + val -> + existing = Map.get(inner_acc, name, []) + + if val in existing, + do: inner_acc, + else: Map.put(inner_acc, name, existing ++ [val]) + end + end) + end) + end + defp build_image_color_lookup(raw_variants, options) do option_names = Enum.map(options, & &1["name"]) color_index = Enum.find_index(option_names, &(&1 in ["Colors", "Color"]))