diff --git a/shop/static/shop/images/products/PC001/blanc/PS_KP912-B_WHITE.png.avif b/shop/static/shop/images/products/PC001/blanc/PS_KP912-B_WHITE.png.avif
new file mode 100644
index 0000000..52ba473
Binary files /dev/null and b/shop/static/shop/images/products/PC001/blanc/PS_KP912-B_WHITE.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/blanc/PS_KP912-S_WHITE.png.avif b/shop/static/shop/images/products/PC001/blanc/PS_KP912-S_WHITE.png.avif
new file mode 100644
index 0000000..b713c00
Binary files /dev/null and b/shop/static/shop/images/products/PC001/blanc/PS_KP912-S_WHITE.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/blanc/PS_KP912_WHITE.png.avif b/shop/static/shop/images/products/PC001/blanc/PS_KP912_WHITE.png.avif
new file mode 100644
index 0000000..c6d5cab
Binary files /dev/null and b/shop/static/shop/images/products/PC001/blanc/PS_KP912_WHITE.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912-B_NAVY.png.avif b/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912-B_NAVY.png.avif
new file mode 100644
index 0000000..773a218
Binary files /dev/null and b/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912-B_NAVY.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912-S_NAVY.png.avif b/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912-S_NAVY.png.avif
new file mode 100644
index 0000000..857968a
Binary files /dev/null and b/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912-S_NAVY.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912_NAVY.png.avif b/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912_NAVY.png.avif
new file mode 100644
index 0000000..cbefe4d
Binary files /dev/null and b/shop/static/shop/images/products/PC001/bleu-sport/PS_KP912_NAVY.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/noir/PS_KP912-B_BLACK.png.avif b/shop/static/shop/images/products/PC001/noir/PS_KP912-B_BLACK.png.avif
new file mode 100644
index 0000000..c467628
Binary files /dev/null and b/shop/static/shop/images/products/PC001/noir/PS_KP912-B_BLACK.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/noir/PS_KP912-S_BLACK.png.avif b/shop/static/shop/images/products/PC001/noir/PS_KP912-S_BLACK.png.avif
new file mode 100644
index 0000000..3956e54
Binary files /dev/null and b/shop/static/shop/images/products/PC001/noir/PS_KP912-S_BLACK.png.avif differ
diff --git a/shop/static/shop/images/products/PC001/noir/noir_hat.png.avif b/shop/static/shop/images/products/PC001/noir/noir_hat.png.avif
new file mode 100644
index 0000000..b451e1d
Binary files /dev/null and b/shop/static/shop/images/products/PC001/noir/noir_hat.png.avif differ
diff --git a/shop/templates/shop/product_item.html b/shop/templates/shop/product_item.html
index 047ec50..75cef84 100644
--- a/shop/templates/shop/product_item.html
+++ b/shop/templates/shop/product_item.html
@@ -3,7 +3,7 @@
{% if product.image %}

{% else %}
@@ -37,7 +37,7 @@
title="{{ color.name }}"
data-color-id="{{ color.id }}"
data-color-name="{{ color.name }}"
- data-color-image="{{ product.image|color_image_url:color.name }}"
+ data-color-image="{% color_image_url product.image color.name product.sku %}"
onclick="selectColor('{{ product.id }}', '{{ color.id }}', '{{ color.name }}', this)">
{% endfor %}
diff --git a/shop/templatetags/shop_extras.py b/shop/templatetags/shop_extras.py
index c6f6406..dcb40b9 100644
--- a/shop/templatetags/shop_extras.py
+++ b/shop/templatetags/shop_extras.py
@@ -2,69 +2,64 @@ from django import template
import os
register = template.Library()
+from django.conf import settings
-@register.filter
-def color_image_url(product_image, color_name):
+@register.simple_tag
+def color_image_url(default_image, color_name, sku):
"""
- Returns color-specific image URL with any supported extension.
- Falls back to the original image if no color variant exists.
+ Returns color-specific image URL from the color-specific folder structure.
+ Structure expected:
+ static/shop/images/products/
+ ├── {sku}/
+ │ ├── default.jpg (or any supported extension)
+ │ └── {color_name}/
+ │ └── image.jpg (or any supported extension)
"""
- if not product_image or not color_name:
- return product_image
- # Generate color suffix
- suffix = generate_color_suffix(color_name)
-
- # Split path
- directory, filename = os.path.split(product_image)
- base_name, original_ext = os.path.splitext(filename)
-
- # List of supported image extensions to check
+ # List of supported image extensions
supported_extensions = ['.png.avif', '.jpg', '.jpeg', '.png', '.gif', '.webp', '.avif']
- # Check for the color image with original extension first
- color_filename = f"{suffix}_{base_name}{original_ext}"
- color_image = os.path.join(directory, color_filename)
-
- # Extract the path after /static/
- static_prefix = '/static/'
- if color_image.startswith(static_prefix):
- rel_path = color_image[len(static_prefix):]
- else:
- rel_path = color_image.lstrip('/')
-
- # Check if file with original extension exists
- from django.conf import settings
- app_static_path = os.path.join(settings.BASE_DIR, 'shop', 'static', rel_path)
-
- if os.path.exists(app_static_path):
- return color_image
-
- # If not found with original extension, try other extensions
- for ext in supported_extensions:
- if ext == original_ext:
- continue # Skip the original extension as we already checked it
-
- color_filename = f"{suffix}_{base_name}{ext}"
- color_image = os.path.join(directory, color_filename)
-
- if color_image.startswith(static_prefix):
- rel_path = color_image[len(static_prefix):]
- else:
- rel_path = color_image.lstrip('/')
-
- app_static_path = os.path.join(settings.BASE_DIR, 'shop', 'static', rel_path)
-
- if os.path.exists(app_static_path):
- return color_image
-
- # If no color variant is found with any extension, return the original image
- return product_image
-
-def generate_color_suffix(color_name):
+ # Base path for products
+ base_path = f'/static/shop/images/products/{sku}/'
+ physical_base_path = os.path.join(settings.BASE_DIR, 'shop', 'static', 'shop', 'images', 'products', sku)
+
+ if color_name:
+ # Generate color folder name (sanitize the color name)
+ color_folder = generate_color_folder_name(color_name)
+
+ # Check color-specific folder
+ color_path = os.path.join(physical_base_path, color_folder)
+ if os.path.exists(color_path):
+ # Get first image from color folder
+ files = [f for f in os.listdir(color_path)
+ if any(f.lower().endswith(ext) for ext in supported_extensions)]
+
+ if files:
+ # Sort files to ensure consistent selection
+ files.sort(key=lambda x: (
+ 1 if '-B_' in x else
+ 2 if '-S_' in x else
+ 0
+ ))
+
+ # Get the first image
+ first_file = files[0]
+ return f'{base_path}{color_folder}/{first_file}'
+
+ # If no color-specific image found, look for default image in product folder
+ if os.path.exists(physical_base_path):
+ for file in os.listdir(physical_base_path):
+ if any(file.lower().endswith(ext) for ext in supported_extensions):
+ if os.path.isfile(os.path.join(physical_base_path, file)): # Make sure it's a file, not a directory
+ return f'{base_path}{file}'
+
+ # If nothing found, return the default image from the database
+ return default_image
+
+def generate_color_folder_name(color_name):
"""
- Generates a URL-friendly suffix from a color name
- Example: "Noir / Gris Foncé Chiné" becomes "noir_gris_fonce_chine"
+ Generates a folder-friendly name from a color name
+ Example: "Noir / Gris Foncé Chiné" becomes "noir-gris-fonce-chine"
"""
import unicodedata
import re
@@ -73,10 +68,10 @@ def generate_color_suffix(color_name):
value = color_name.lower()
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
- # Replace slashes and spaces with underscores
- value = re.sub(r'[/\s]+', '_', value)
+ # Replace slashes and spaces with hyphens
+ value = re.sub(r'[/\s]+', '-', value)
- # Remove any remaining non-alphanumeric characters
- value = re.sub(r'[^\w_]', '', value)
+ # Remove any remaining non-alphanumeric characters except hyphens
+ value = re.sub(r'[^\w-]', '', value)
return value