feat: add automated Printify mockup generation & POD sample content

Printify Integration:
- Add Printify API client module with full HTTP wrapper
- Add mockup generator with dynamic "cover" scaling algorithm
- Create Mix task (mix generate_mockups) for automated mockup generation
- Support --cleanup flag to delete products after downloading mockups
- Calculate optimal scale factor based on artwork/placeholder aspect ratios
- Handle landscape artwork on portrait print areas without white space

Sample Content (16 products across 5 POD categories):
- Art Prints: Mountain Sunrise, Ocean Waves, Wildflower Meadow, Geometric Abstract, Botanical Illustration
- Apparel: Forest Silhouette T-Shirt, Forest Light Hoodie
- Tote Bags: Wildflower Meadow, Sunset Gradient
- Homewares: Fern Leaf Mug, Ocean Waves Cushion, Night Sky Blanket
- Stationery: Autumn Leaves Notebook, Monstera Leaf Notebook
- Accessories: Monstera Leaf Phone Case, Blue Waves Laptop Sleeve

Preview Data Updates:
- Replace generic e-commerce products with POD-focused items
- Update categories to POD-relevant: Art Prints, Apparel, Homewares, Stationery, Accessories
- Update cart drawer items to match new product range
- Refresh testimonials with POD-appropriate reviews

Theme Content Updates:
- Update hero section for "Wildprint Studio" brand identity
- Rewrite about page narrative with humble, British, personal tone
- Update search hints to nature/POD relevant terms
- Add mockups directory to static paths

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-14 23:35:18 +00:00
parent 1bb8888d7c
commit 0c43d65a04
64 changed files with 1123 additions and 165 deletions

View File

@@ -0,0 +1,182 @@
defmodule SimpleshopTheme.Printify.Client do
@moduledoc """
HTTP client for the Printify API.
Handles authentication and provides low-level API access.
Requires PRINTIFY_API_TOKEN environment variable to be set.
"""
@base_url "https://api.printify.com/v1"
@doc """
Get the API token from environment.
"""
def api_token do
System.get_env("PRINTIFY_API_TOKEN") ||
raise "PRINTIFY_API_TOKEN environment variable is not set"
end
@doc """
Make a GET request to the Printify API.
"""
def get(path, opts \\ []) do
url = @base_url <> path
case Req.get(url, headers: auth_headers(), receive_timeout: 30_000) do
{:ok, %Req.Response{status: status, body: body}} when status in 200..299 ->
{:ok, body}
{:ok, %Req.Response{status: status, body: body}} ->
{:error, {status, body}}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Make a POST request to the Printify API.
"""
def post(path, body, opts \\ []) do
url = @base_url <> path
case Req.post(url, json: body, headers: auth_headers(), receive_timeout: 60_000) do
{:ok, %Req.Response{status: status, body: body}} when status in 200..299 ->
{:ok, body}
{:ok, %Req.Response{status: status, body: body}} ->
{:error, {status, body}}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Make a DELETE request to the Printify API.
"""
def delete(path, opts \\ []) do
url = @base_url <> path
case Req.delete(url, headers: auth_headers(), receive_timeout: 30_000) do
{:ok, %Req.Response{status: status, body: body}} when status in 200..299 ->
{:ok, body}
{:ok, %Req.Response{status: status}} when status == 204 ->
{:ok, nil}
{:ok, %Req.Response{status: status, body: body}} ->
{:error, {status, body}}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Get all shops for the authenticated account.
"""
def get_shops do
get("/shops.json")
end
@doc """
Get the first shop ID for the account.
"""
def get_shop_id do
case get_shops() do
{:ok, shops} when is_list(shops) and length(shops) > 0 ->
{:ok, hd(shops)["id"]}
{:ok, []} ->
{:error, :no_shops}
error ->
error
end
end
@doc """
Get all blueprints (product types) from the catalog.
"""
def get_blueprints do
get("/catalog/blueprints.json")
end
@doc """
Get print providers for a specific blueprint.
"""
def get_print_providers(blueprint_id) do
get("/catalog/blueprints/#{blueprint_id}/print_providers.json")
end
@doc """
Get variants for a specific blueprint and print provider.
"""
def get_variants(blueprint_id, print_provider_id) do
get("/catalog/blueprints/#{blueprint_id}/print_providers/#{print_provider_id}/variants.json")
end
@doc """
Get shipping information for a blueprint/provider combination.
"""
def get_shipping(blueprint_id, print_provider_id) do
get("/catalog/blueprints/#{blueprint_id}/print_providers/#{print_provider_id}/shipping.json")
end
@doc """
Upload an image to Printify via URL.
"""
def upload_image(file_name, url) do
post("/uploads/images.json", %{
file_name: file_name,
url: url
})
end
@doc """
Create a product in a shop.
"""
def create_product(shop_id, product_data) do
post("/shops/#{shop_id}/products.json", product_data)
end
@doc """
Get a product by ID.
"""
def get_product(shop_id, product_id) do
get("/shops/#{shop_id}/products/#{product_id}.json")
end
@doc """
Delete a product from a shop.
"""
def delete_product(shop_id, product_id) do
delete("/shops/#{shop_id}/products/#{product_id}.json")
end
@doc """
Download a file from a URL to a local path.
"""
def download_file(url, output_path) do
case Req.get(url, into: File.stream!(output_path), receive_timeout: 60_000) do
{:ok, %Req.Response{status: status}} when status in 200..299 ->
{:ok, output_path}
{:ok, %Req.Response{status: status}} ->
File.rm(output_path)
{:error, {:http_error, status}}
{:error, reason} ->
File.rm(output_path)
{:error, reason}
end
end
defp auth_headers do
[
{"Authorization", "Bearer #{api_token()}"},
{"Content-Type", "application/json"}
]
end
end

View File

@@ -0,0 +1,476 @@
defmodule SimpleshopTheme.Printify.MockupGenerator do
@moduledoc """
Generates product mockups using the Printify API.
This module handles the end-to-end process of:
1. Looking up product blueprints and variants
2. Downloading artwork from Unsplash
3. Uploading artwork to Printify
4. Creating products with the artwork
5. Downloading generated mockup images
6. Optionally cleaning up created products
"""
alias SimpleshopTheme.Printify.Client
@output_dir "priv/static/mockups"
@doc """
Product definitions with their artwork URLs and Printify product types.
"""
def product_definitions do
[
%{
name: "Mountain Sunrise Art Print",
slug: "mountain-sunrise-print",
category: "Art Prints",
artwork_url: unsplash_download_url("UweNcthlmDc"),
product_type: :poster,
price: 2400
},
%{
name: "Ocean Waves Art Print",
slug: "ocean-waves-print",
category: "Art Prints",
artwork_url: unsplash_download_url("XRhUTUVuXAE"),
product_type: :poster,
price: 2400
},
%{
name: "Wildflower Meadow Art Print",
slug: "wildflower-meadow-print",
category: "Art Prints",
artwork_url: unsplash_download_url("QvjL4y7SF9k"),
product_type: :poster,
price: 2400
},
%{
name: "Geometric Abstract Art Print",
slug: "geometric-abstract-print",
category: "Art Prints",
artwork_url: unsplash_download_url("-6GvTDpkkPU"),
product_type: :poster,
price: 2800
},
%{
name: "Botanical Illustration Print",
slug: "botanical-illustration-print",
category: "Art Prints",
artwork_url: unsplash_download_url("FNtNIDQWUZY"),
product_type: :poster,
price: 2400
},
%{
name: "Forest Silhouette T-Shirt",
slug: "forest-silhouette-tshirt",
category: "Apparel",
artwork_url: unsplash_download_url("EhvMzMRO4_o"),
product_type: :tshirt,
price: 2999
},
%{
name: "Forest Light Hoodie",
slug: "forest-light-hoodie",
category: "Apparel",
artwork_url: unsplash_download_url("FwVkxITt8Bg"),
product_type: :hoodie,
price: 4499
},
%{
name: "Wildflower Meadow Tote Bag",
slug: "wildflower-meadow-tote",
category: "Apparel",
artwork_url: unsplash_download_url("QvjL4y7SF9k"),
product_type: :tote,
price: 1999
},
%{
name: "Sunset Gradient Tote Bag",
slug: "sunset-gradient-tote",
category: "Apparel",
artwork_url: unsplash_download_url("XRhUTUVuXAE"),
product_type: :tote,
price: 1999
},
%{
name: "Fern Leaf Mug",
slug: "fern-leaf-mug",
category: "Homewares",
artwork_url: unsplash_download_url("bYiJojtkHnc"),
product_type: :mug,
price: 1499
},
%{
name: "Ocean Waves Cushion",
slug: "ocean-waves-cushion",
category: "Homewares",
artwork_url: unsplash_download_url("XRhUTUVuXAE"),
product_type: :cushion,
price: 2999
},
%{
name: "Night Sky Blanket",
slug: "night-sky-blanket",
category: "Homewares",
artwork_url: unsplash_download_url("oQR1B87HsNs"),
product_type: :blanket,
price: 5999
},
%{
name: "Autumn Leaves Notebook",
slug: "autumn-leaves-notebook",
category: "Stationery",
artwork_url: unsplash_download_url("Aa3ALtIxEGY"),
product_type: :notebook,
price: 1999
},
%{
name: "Monstera Leaf Notebook",
slug: "monstera-leaf-notebook",
category: "Stationery",
artwork_url: unsplash_download_url("hETU8_b2IM0"),
product_type: :notebook,
price: 1999
},
%{
name: "Monstera Leaf Phone Case",
slug: "monstera-leaf-phone-case",
category: "Accessories",
artwork_url: unsplash_download_url("hETU8_b2IM0"),
product_type: :phone_case,
price: 2499
},
%{
name: "Blue Waves Laptop Sleeve",
slug: "blue-waves-laptop-sleeve",
category: "Accessories",
artwork_url: unsplash_download_url("dYksH3vHorc"),
product_type: :laptop_sleeve,
price: 3499
}
]
end
@doc """
Blueprint configurations for each product type.
These IDs need to be looked up from the Printify catalog.
"""
def blueprint_config do
%{
# Search terms matched to Printify's actual blueprint titles (partial match)
poster: %{blueprint_id: nil, print_provider_id: nil, search_term: "Matte Posters"},
tshirt: %{blueprint_id: nil, print_provider_id: nil, search_term: "Softstyle T-Shirt"},
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"},
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"},
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"},
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"}
}
end
@doc """
Generate Unsplash download URL from photo ID.
Uses the Unsplash download API which provides high-quality images.
"""
def unsplash_download_url(photo_id) do
"https://unsplash.com/photos/#{photo_id}/download?force=true"
end
@doc """
Search for a blueprint by name/term.
"""
def find_blueprint(search_term) do
case Client.get_blueprints() do
{:ok, blueprints} ->
found =
Enum.find(blueprints, fn bp ->
String.contains?(String.downcase(bp["title"] || ""), String.downcase(search_term))
end)
case found do
nil -> {:error, {:blueprint_not_found, search_term}}
bp -> {:ok, bp}
end
error ->
error
end
end
@doc """
Find a suitable print provider for a blueprint.
Prefers providers with good ratings and reasonable pricing.
"""
def find_print_provider(blueprint_id) do
case Client.get_print_providers(blueprint_id) do
{:ok, providers} when is_list(providers) and length(providers) > 0 ->
# Just pick the first provider for simplicity
{:ok, hd(providers)}
{:ok, []} ->
{:error, :no_providers}
error ->
error
end
end
@doc """
Get variant and placeholder information for a blueprint/provider combination.
"""
def get_variant_info(blueprint_id, print_provider_id) do
case Client.get_variants(blueprint_id, print_provider_id) do
{:ok, %{"variants" => variants}} when is_list(variants) ->
{:ok, variants}
{:ok, variants} when is_list(variants) ->
{:ok, variants}
{:ok, response} when is_map(response) ->
# Handle case where variants might be nested differently
{:ok, response["variants"] || []}
error ->
error
end
end
@doc """
Upload artwork to Printify from a URL.
"""
def upload_artwork(name, url) do
file_name = "#{name}.jpg"
Client.upload_image(file_name, url)
end
@doc """
Calculate scale factor for "cover" behavior.
Image will fill entire placeholder, cropping edges if necessary.
Printify scale is relative to placeholder width (1.0 = artwork width matches placeholder width).
"""
def calculate_cover_scale(artwork_width, artwork_height, placeholder_width, placeholder_height)
when is_number(artwork_width) and is_number(artwork_height) and
is_number(placeholder_width) and is_number(placeholder_height) and
artwork_width > 0 and artwork_height > 0 and
placeholder_width > 0 and placeholder_height > 0 do
# For cover: use the larger scale to ensure full coverage
width_scale = 1.0
height_scale = (placeholder_height * artwork_width) / (artwork_height * placeholder_width)
max(width_scale, height_scale)
end
def calculate_cover_scale(_, _, _, _), do: 1.0
@doc """
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
# Get the first variant for simplicity (typically a standard size/color)
variant = hd(variants)
variant_id = variant["id"]
# Get placeholder info
placeholders = variant["placeholders"] || []
front_placeholder = Enum.find(placeholders, fn p -> p["position"] == "front" end) || hd(placeholders)
# Extract placeholder dimensions and calculate cover scale
placeholder_width = front_placeholder["width"]
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)}")
product_data = %{
title: product_def.name,
description: "#{product_def.name} - Nature-inspired design from Wildprint Studio",
blueprint_id: blueprint_id,
print_provider_id: print_provider_id,
variants: [
%{
id: variant_id,
price: product_def.price,
is_enabled: true
}
],
print_areas: [
%{
variant_ids: [variant_id],
placeholders: [
%{
position: front_placeholder["position"] || "front",
images: [
%{
id: image_id,
x: 0.5,
y: 0.5,
scale: scale,
angle: 0
}
]
}
]
}
]
}
Client.create_product(shop_id, product_data)
end
@doc """
Extract mockup image URLs from a created product.
"""
def extract_mockup_urls(product) do
images = product["images"] || []
Enum.map(images, fn img -> img["src"] end)
end
@doc """
Download mockup images to the output directory.
"""
def download_mockups(product_slug, mockup_urls) do
File.mkdir_p!(@output_dir)
mockup_urls
|> Enum.with_index(1)
|> Enum.map(fn {url, index} ->
output_path = Path.join(@output_dir, "#{product_slug}-#{index}.jpg")
IO.puts(" Downloading mockup #{index} to #{output_path}...")
case Client.download_file(url, output_path) do
{:ok, path} -> {:ok, path}
{:error, reason} -> {:error, {url, reason}}
end
end)
end
@doc """
Generate mockups for all products.
"""
def generate_all(opts \\ []) do
cleanup = Keyword.get(opts, :cleanup, false)
IO.puts("Starting mockup generation...")
IO.puts("")
# Get shop ID
IO.puts("Fetching shop ID...")
{:ok, shop_id} = Client.get_shop_id()
IO.puts("Using shop ID: #{shop_id}")
IO.puts("")
# Track created products for cleanup
created_products = []
results =
product_definitions()
|> Enum.map(fn product_def ->
IO.puts("Processing: #{product_def.name}")
result = generate_single(shop_id, product_def)
case result do
{:ok, product_id, mockup_paths} ->
IO.puts(" ✓ Generated #{length(mockup_paths)} mockups")
{:ok, product_def.slug, product_id, mockup_paths}
{:error, reason} ->
IO.puts(" ✗ Error: #{inspect(reason)}")
{:error, product_def.slug, reason}
end
end)
# Cleanup if requested
if cleanup do
IO.puts("")
IO.puts("Cleaning up created products...")
results
|> Enum.filter(fn
{:ok, _, _, _} -> true
_ -> false
end)
|> Enum.each(fn {:ok, slug, product_id, _} ->
IO.puts(" Deleting #{slug}...")
Client.delete_product(shop_id, product_id)
end)
IO.puts("Cleanup complete.")
end
IO.puts("")
IO.puts("Mockup generation complete!")
IO.puts("Output directory: #{@output_dir}")
results
end
@doc """
Generate mockups for a single product.
"""
def generate_single(shop_id, product_def) do
config = blueprint_config()[product_def.product_type]
with {:ok, blueprint} <- find_blueprint(config.search_term),
blueprint_id = blueprint["id"],
_ = IO.puts(" Found blueprint: #{blueprint["title"]} (#{blueprint_id})"),
{:ok, provider} <- find_print_provider(blueprint_id),
provider_id = provider["id"],
_ = IO.puts(" Using provider: #{provider["title"]} (#{provider_id})"),
{:ok, variants} <- get_variant_info(blueprint_id, provider_id),
_ = IO.puts(" Found #{length(variants)} variants"),
_ = IO.puts(" Uploading artwork..."),
{:ok, upload} <- upload_artwork(product_def.slug, product_def.artwork_url),
image_id = upload["id"],
image_width = upload["width"],
image_height = upload["height"],
_ = IO.puts(" Artwork uploaded (ID: #{image_id}, #{image_width}x#{image_height})"),
_ = IO.puts(" Creating product..."),
{:ok, product} <- create_product(shop_id, product_def, image_id, image_width, image_height, blueprint_id, provider_id, variants),
product_id = product["id"],
mockup_urls = extract_mockup_urls(product),
_ = IO.puts(" Product created (ID: #{product_id})"),
_ = IO.puts(" Downloading #{length(mockup_urls)} mockups..."),
download_results <- download_mockups(product_def.slug, mockup_urls) do
successful_downloads =
download_results
|> Enum.filter(&match?({:ok, _}, &1))
|> Enum.map(fn {:ok, path} -> path end)
{:ok, product_id, successful_downloads}
end
end
@doc """
List all available blueprints (for discovery).
"""
def list_blueprints do
case Client.get_blueprints() do
{:ok, blueprints} ->
blueprints
|> Enum.map(fn bp -> {bp["id"], bp["title"]} end)
|> Enum.sort_by(fn {_, title} -> title end)
error ->
error
end
end
@doc """
Search blueprints by keyword.
"""
def search_blueprints(keyword) do
case Client.get_blueprints() do
{:ok, blueprints} ->
blueprints
|> Enum.filter(fn bp ->
String.contains?(String.downcase(bp["title"] || ""), String.downcase(keyword))
end)
|> Enum.map(fn bp -> {bp["id"], bp["title"]} end)
error ->
error
end
end
end

View File

@@ -34,16 +34,16 @@ defmodule SimpleshopTheme.Theme.PreviewData do
def cart_drawer_items do
[
%{
name: "Autumn Fern",
variant: "A4 / Unframed",
name: "Mountain Sunrise Art Print",
variant: "12″ x 18″ / Matte",
price: "£24.00",
image: "https://picsum.photos/seed/fern/120/120"
image: "/mockups/mountain-sunrise-print-1.jpg"
},
%{
name: "Wild Roses",
variant: "A3 / Oak frame",
price: "£48.00",
image: "https://picsum.photos/seed/roses/120/120"
name: "Fern Leaf Mug",
variant: "11oz / White",
price: "£14.99",
image: "/mockups/fern-leaf-mug-1.jpg"
}
]
end
@@ -93,147 +93,200 @@ defmodule SimpleshopTheme.Theme.PreviewData do
defp mock_products do
[
# Art Prints
%{
id: "1",
name: "Classic Cotton T-Shirt",
description: "Soft, breathable cotton tee perfect for everyday wear",
price: 2999,
name: "Mountain Sunrise Art Print",
description: "Capture the magic of dawn with this stunning mountain landscape print",
price: 2400,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1622445275576-721325763afe?w=600&h=800&fit=crop&q=80",
category: "Clothing",
image_url: "/mockups/mountain-sunrise-print-1.jpg",
hover_image_url: "/mockups/mountain-sunrise-print-2.jpg",
category: "Art Prints",
in_stock: true,
on_sale: false
},
%{
id: "2",
name: "Leather Crossbody Bag",
description: "Handcrafted genuine leather bag with adjustable strap",
price: 8999,
compare_at_price: 11999,
image_url: "https://images.unsplash.com/photo-1548036328-c9fa89d128fa?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1590874103328-eac38a683ce7?w=600&h=800&fit=crop&q=80",
category: "Accessories",
name: "Ocean Waves Art Print",
description: "A calming sunset over ocean waves to bring peace to any room",
price: 2400,
compare_at_price: nil,
image_url: "/mockups/ocean-waves-print-1.jpg",
hover_image_url: "/mockups/ocean-waves-print-2.jpg",
category: "Art Prints",
in_stock: true,
on_sale: true
on_sale: false
},
%{
id: "3",
name: "Ceramic Coffee Mug",
description: "Handmade ceramic mug with unique glaze finish",
price: 2499,
name: "Wildflower Meadow Art Print",
description: "Beautiful wildflower meadow captured in the summer sunshine",
price: 2400,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1514228742587-6b1558fcca3d?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1481833761820-0509d3217039?w=600&h=800&fit=crop&q=80",
category: "Home",
image_url: "/mockups/wildflower-meadow-print-1.jpg",
hover_image_url: "/mockups/wildflower-meadow-print-2.jpg",
category: "Art Prints",
in_stock: true,
on_sale: false
},
%{
id: "4",
name: "Minimalist Watch",
description: "Sleek design with Japanese quartz movement",
price: 12999,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1524592094714-0f0654e20314?w=600&h=800&fit=crop&q=80",
category: "Accessories",
name: "Geometric Abstract Art Print",
description: "Modern minimalist design with bold geometric shapes",
price: 2800,
compare_at_price: 3200,
image_url: "/mockups/geometric-abstract-print-1.jpg",
hover_image_url: "/mockups/geometric-abstract-print-2.jpg",
category: "Art Prints",
in_stock: true,
on_sale: false
},
%{
id: "5",
name: "Wool Throw Blanket",
description: "Cozy merino wool blanket in herringbone pattern",
price: 7999,
compare_at_price: 9999,
image_url: "https://images.unsplash.com/photo-1580301762395-21ce84d00bc6?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1600369671854-2d9f5db4a5e0?w=600&h=800&fit=crop&q=80",
category: "Home",
in_stock: false,
on_sale: true
},
%{
id: "6",
name: "Artisan Soap Set",
description: "Natural handmade soaps with essential oils",
price: 3499,
id: "5",
name: "Botanical Illustration Print",
description: "Vintage-inspired botanical drawing with intricate detail",
price: 2400,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1607006344380-b6775a0824a7?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1600857544200-b2f666a9a2ec?w=600&h=800&fit=crop&q=80",
category: "Beauty",
image_url: "/mockups/botanical-illustration-print-1.jpg",
hover_image_url: "/mockups/botanical-illustration-print-2.jpg",
category: "Art Prints",
in_stock: true,
on_sale: false
},
# Apparel
%{
id: "6",
name: "Forest Silhouette T-Shirt",
description: "Soft cotton tee featuring a peaceful forest silhouette design",
price: 2999,
compare_at_price: nil,
image_url: "/mockups/forest-silhouette-tshirt-1.jpg",
hover_image_url: "/mockups/forest-silhouette-tshirt-2.jpg",
category: "Apparel",
in_stock: true,
on_sale: false
},
%{
id: "7",
name: "Denim Jacket",
description: "Classic cut denim jacket with vintage wash",
price: 8499,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1576995853123-5a10305d93c0?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1601333144130-8cbb312386b6?w=600&h=800&fit=crop&q=80",
category: "Clothing",
name: "Forest Light Hoodie",
description: "Cosy fleece hoodie with stunning forest light photography",
price: 4499,
compare_at_price: 4999,
image_url: "/mockups/forest-light-hoodie-1.jpg",
hover_image_url: "/mockups/forest-light-hoodie-2.jpg",
category: "Apparel",
in_stock: true,
on_sale: false
on_sale: true
},
%{
id: "8",
name: "Canvas Tote Bag",
description: "Durable organic cotton canvas with reinforced handles",
price: 2999,
name: "Wildflower Meadow Tote Bag",
description: "Sturdy cotton tote bag with vibrant wildflower design",
price: 1999,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1622560480605-d83c853bc5c3?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1597633125097-5a9ae3cb8a8f?w=600&h=800&fit=crop&q=80",
category: "Accessories",
image_url: "/mockups/wildflower-meadow-tote-1.jpg",
hover_image_url: "/mockups/wildflower-meadow-tote-2.jpg",
category: "Apparel",
in_stock: true,
on_sale: false
},
%{
id: "9",
name: "Scented Candle",
description: "Soy wax candle with cedar and vanilla notes",
price: 3299,
compare_at_price: 3999,
image_url: "https://images.unsplash.com/photo-1602028915047-37269d1a73f7?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1603006905003-be475563bc59?w=600&h=800&fit=crop&q=80",
category: "Home",
name: "Sunset Gradient Tote Bag",
description: "Beautiful ocean sunset printed on durable canvas tote",
price: 1999,
compare_at_price: nil,
image_url: "/mockups/sunset-gradient-tote-1.jpg",
hover_image_url: "/mockups/sunset-gradient-tote-2.jpg",
category: "Apparel",
in_stock: true,
on_sale: true
on_sale: false
},
# Homewares
%{
id: "10",
name: "Stainless Steel Water Bottle",
description: "Insulated bottle keeps drinks cold for 24 hours",
price: 3999,
name: "Fern Leaf Mug",
description: "Start your morning right with this nature-inspired ceramic mug",
price: 1499,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1602143407151-7111542de6e8?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1570831739435-6601aa3fa4fb?w=600&h=800&fit=crop&q=80",
category: "Accessories",
image_url: "/mockups/fern-leaf-mug-1.jpg",
hover_image_url: "/mockups/fern-leaf-mug-2.jpg",
category: "Homewares",
in_stock: true,
on_sale: false
},
%{
id: "11",
name: "Organic Cotton Socks",
description: "Comfortable crew socks in solid colors",
price: 1499,
name: "Ocean Waves Cushion",
description: "Soft polyester cushion featuring a stunning ocean sunset",
price: 2999,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1586350977771-b3b0abd50c82?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1582966772680-860e372bb558?w=600&h=800&fit=crop&q=80",
category: "Clothing",
image_url: "/mockups/ocean-waves-cushion-1.jpg",
hover_image_url: "/mockups/ocean-waves-cushion-2.jpg",
category: "Homewares",
in_stock: true,
on_sale: false
},
%{
id: "12",
name: "Bamboo Cutting Board",
description: "Sustainable bamboo with juice groove",
price: 4499,
name: "Night Sky Blanket",
description: "Cosy sherpa fleece blanket with mesmerising milky way print",
price: 5999,
compare_at_price: 6999,
image_url: "/mockups/night-sky-blanket-1.jpg",
hover_image_url: "/mockups/night-sky-blanket-2.jpg",
category: "Homewares",
in_stock: true,
on_sale: true
},
# Stationery
%{
id: "13",
name: "Autumn Leaves Notebook",
description: "Hardcover journal with beautiful autumn foliage design",
price: 1999,
compare_at_price: nil,
image_url: "https://images.unsplash.com/photo-1594226801341-41427b4e5c22?w=600&h=800&fit=crop&q=80",
hover_image_url: "https://images.unsplash.com/photo-1606760227091-3dd870d97f1d?w=600&h=800&fit=crop&q=80",
category: "Kitchen",
image_url: "/mockups/autumn-leaves-notebook-1.jpg",
hover_image_url: "/mockups/autumn-leaves-notebook-2.jpg",
category: "Stationery",
in_stock: true,
on_sale: false
},
%{
id: "14",
name: "Monstera Leaf Notebook",
description: "Tropical-inspired hardcover journal for your thoughts",
price: 1999,
compare_at_price: nil,
image_url: "/mockups/monstera-leaf-notebook-1.jpg",
hover_image_url: "/mockups/monstera-leaf-notebook-2.jpg",
category: "Stationery",
in_stock: true,
on_sale: false
},
# Accessories
%{
id: "15",
name: "Monstera Leaf Phone Case",
description: "Tough phone case with stunning monstera leaf photography",
price: 2499,
compare_at_price: nil,
image_url: "/mockups/monstera-leaf-phone-case-1.jpg",
hover_image_url: "/mockups/monstera-leaf-phone-case-2.jpg",
category: "Accessories",
in_stock: true,
on_sale: false
},
%{
id: "16",
name: "Blue Waves Laptop Sleeve",
description: "Protective laptop sleeve with abstract blue gradient design",
price: 3499,
compare_at_price: nil,
image_url: "/mockups/blue-waves-laptop-sleeve-1.jpg",
hover_image_url: "/mockups/blue-waves-laptop-sleeve-2.jpg",
category: "Accessories",
in_stock: true,
on_sale: false
}
@@ -246,16 +299,16 @@ defmodule SimpleshopTheme.Theme.PreviewData do
[
%{
product: Enum.at(products, 0),
quantity: 2,
variant: "Medium / Navy"
},
%{
product: Enum.at(products, 2),
quantity: 1,
variant: "One Size"
variant: "12″ x 18″ / Matte"
},
%{
product: Enum.at(products, 6),
product: Enum.at(products, 9),
quantity: 2,
variant: "11oz / White"
},
%{
product: Enum.at(products, 5),
quantity: 1,
variant: "Large / Black"
}
@@ -267,44 +320,44 @@ defmodule SimpleshopTheme.Theme.PreviewData do
%{
id: "1",
author: "Sarah M.",
content: "Absolutely love the quality! The attention to detail is incredible.",
content: "The print quality is absolutely stunning - colours are exactly as shown online. My living room looks so much better now!",
rating: 5,
date: "2024-01-15"
date: "2025-01-15"
},
%{
id: "2",
author: "James L.",
content: "Fast shipping and beautiful packaging. Will definitely order again.",
content: "Bought the forest hoodie as a gift. The packaging was lovely and the quality exceeded expectations. Will order again!",
rating: 5,
date: "2024-01-10"
date: "2025-01-10"
},
%{
id: "3",
author: "Emily R.",
content: "These products have become my everyday favorites. Highly recommend!",
content: "My new favourite mug! I love sipping my morning tea while looking at that beautiful fern design.",
rating: 5,
date: "2024-01-05"
date: "2025-01-05"
},
%{
id: "4",
author: "Michael T.",
content: "Great customer service and even better products. Worth every penny.",
content: "The tote bag is so sturdy - I use it for everything now. Great print quality that hasn't faded at all.",
rating: 5,
date: "2023-12-28"
date: "2024-12-28"
},
%{
id: "5",
author: "Lisa K.",
content: "The craftsmanship is outstanding. You can tell these are made with care.",
content: "The night sky blanket is gorgeous and so cosy. Perfect for film nights on the sofa. Highly recommend!",
rating: 5,
date: "2023-12-20"
date: "2024-12-20"
},
%{
id: "6",
author: "David P.",
content: "Perfect gift idea! My friends loved what I got them from here.",
content: "Ordered several prints for my new flat. They arrived well packaged and look amazing on the wall.",
rating: 5,
date: "2023-12-15"
date: "2024-12-15"
}
]
end
@@ -313,38 +366,38 @@ defmodule SimpleshopTheme.Theme.PreviewData do
[
%{
id: "1",
name: "Clothing",
slug: "clothing",
product_count: 3,
image_url: "https://images.unsplash.com/photo-1489987707025-afc232f7ea0f?w=400&h=300&fit=crop&q=80"
name: "Art Prints",
slug: "art-prints",
product_count: 5,
image_url: "/mockups/mountain-sunrise-print-2.jpg"
},
%{
id: "2",
name: "Accessories",
slug: "accessories",
name: "Apparel",
slug: "apparel",
product_count: 4,
image_url: "https://images.unsplash.com/photo-1606760227091-3dd870d97f1d?w=400&h=300&fit=crop&q=80"
image_url: "/mockups/forest-silhouette-tshirt-1.jpg"
},
%{
id: "3",
name: "Home",
slug: "home",
name: "Homewares",
slug: "homewares",
product_count: 3,
image_url: "https://images.unsplash.com/photo-1616046229478-9901c5536a45?w=400&h=300&fit=crop&q=80"
image_url: "/mockups/fern-leaf-mug-1.jpg"
},
%{
id: "4",
name: "Kitchen",
slug: "kitchen",
product_count: 1,
image_url: "https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?w=400&h=300&fit=crop&q=80"
name: "Stationery",
slug: "stationery",
product_count: 2,
image_url: "/mockups/autumn-leaves-notebook-1.jpg"
},
%{
id: "5",
name: "Beauty",
slug: "beauty",
product_count: 1,
image_url: "https://images.unsplash.com/photo-1596462502278-27bfdc403348?w=400&h=300&fit=crop&q=80"
name: "Accessories",
slug: "accessories",
product_count: 2,
image_url: "/mockups/monstera-leaf-phone-case-1.jpg"
}
]
end