diff --git a/shop/management/commands/create_initial_shop_data.py b/shop/management/commands/create_initial_shop_data.py index 92fc353..623a5d1 100644 --- a/shop/management/commands/create_initial_shop_data.py +++ b/shop/management/commands/create_initial_shop_data.py @@ -34,10 +34,11 @@ class Command(BaseCommand): self.stdout.write(f'Created color: {color_data["name"]}') else: # Update existing colors with secondary color if needed - if color.secondary_hex_color != color_data['secondary_hex']: + if color.colorHex != color_data['hex'] or color.secondary_hex_color != color_data['secondary_hex']: + color.colorHex = color_data['hex'] color.secondary_hex_color = color_data['secondary_hex'] color.save() - self.stdout.write(f'Updated color: {color_data["name"]} with secondary color') + self.stdout.write(f'Updated color: {color_data["name"]}') else: self.stdout.write(f'Color already exists: {color_data["name"]}') @@ -58,38 +59,46 @@ class Command(BaseCommand): self.stdout.write('Creating products...') products = [ { + 'sku': 'PC001', 'title': 'Tennis Racket Pro', + 'description': 'Professional grade tennis racket with advanced stability control.', 'price': 99.99, 'ordering_value': 1, 'cut': 2, # Men - 'colors': ['Black', 'White', 'Red', 'Black/White'], + 'colors': ['Black/White'], 'sizes': ['M', 'L', 'XL'], - 'image_filename': 'hat.jpg' # Just the filename + 'image_filename': 'hat.jpg' }, { + 'sku': 'PC002', 'title': 'Sports T-Shirt', + 'description': 'Breathable sports t-shirt made with moisture-wicking fabric.', 'price': 29.99, 'ordering_value': 2, 'cut': 1, # Women 'colors': ['Black', 'White', 'Blue', 'Red', 'Red/Blue'], 'sizes': ['XS', 'S', 'M', 'L', 'XL'], - 'image_filename': 'tshirt.jpg' # Just the filename + 'image_filename': 'tshirt.jpg' }, { + 'sku': 'PC003', 'title': 'Kids Tennis Shorts', + 'description': 'Comfortable tennis shorts for kids with elastic waistband.', 'price': 19.99, 'ordering_value': 3, 'cut': 3, # Kids 'colors': ['Blue', 'White', 'Green/Yellow'], 'sizes': ['XS', 'S', 'M'], - 'image_filename': 'kids_shorts.jpg' # Just the filename + 'image_filename': 'kids_shorts.jpg' } ] for product_data in products: - product, created = Product.objects.get_or_create( - title=product_data['title'], + product, created = Product.objects.update_or_create( + sku=product_data['sku'], defaults={ + 'title': product_data['title'], + 'description': product_data.get('description', ''), 'price': product_data['price'], 'ordering_value': product_data['ordering_value'], 'cut': product_data['cut'] @@ -97,35 +106,30 @@ class Command(BaseCommand): ) if created: - self.stdout.write(f'Created product: {product_data["title"]}') - - # Add colors - for color_name in product_data['colors']: - product.colors.add(color_objects[color_name]) - - # Add sizes - for size_name in product_data['sizes']: - product.sizes.add(size_objects[size_name]) + self.stdout.write(f'Created product: {product_data["sku"]} - {product_data["title"]}') + else: + self.stdout.write(f'Updated product: {product_data["sku"]} - {product_data["title"]}') - # Construct the full path for storage - if 'image_filename' in product_data and product_data['image_filename']: - # Construct the URL path to the image - # This uses STATIC_URL from your settings - image_path = f"{settings.STATIC_URL}shop/images/products/{product_data['image_filename']}" - print(image_path) - # Store this path in the database + # Handle the image path + if 'image_filename' in product_data and product_data['image_filename']: + image_path = f"{settings.STATIC_URL}shop/images/products/{product_data['image_filename']}" + if product.image != image_path: product.image = image_path product.save() + self.stdout.write(f'Updated image path to "{image_path}" for: {product_data["sku"]}') - self.stdout.write(f'Added image path "{image_path}" for: {product_data["title"]}') - else: - self.stdout.write(f'Product already exists: {product_data["title"]}') + # Update colors - first clear existing then add new ones + product.colors.clear() + for color_name in product_data['colors']: + if color_name in color_objects: + product.colors.add(color_objects[color_name]) + self.stdout.write(f'Updated colors for: {product_data["sku"]}') - # Update existing products with new colors if needed - existing_colors = set(product.colors.all().values_list('name', flat=True)) - for color_name in product_data['colors']: - if color_name not in existing_colors: - product.colors.add(color_objects[color_name]) - self.stdout.write(f'Added color {color_name} to existing product: {product_data["title"]}') + # Update sizes - first clear existing then add new ones + product.sizes.clear() + for size_name in product_data['sizes']: + if size_name in size_objects: + product.sizes.add(size_objects[size_name]) + self.stdout.write(f'Updated sizes for: {product_data["sku"]}') - self.stdout.write(self.style.SUCCESS('Successfully created initial shop data')) + self.stdout.write(self.style.SUCCESS('Successfully created/updated shop data')) diff --git a/shop/migrations/0019_product_description.py b/shop/migrations/0019_product_description.py new file mode 100644 index 0000000..5bc157e --- /dev/null +++ b/shop/migrations/0019_product_description.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.11 on 2025-03-21 12:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0018_color_secondary_hex_color'), + ] + + operations = [ + migrations.AddField( + model_name='product', + name='description', + field=models.TextField(blank=True, help_text='Product description text', null=True), + ), + ] diff --git a/shop/migrations/0020_product_sku.py b/shop/migrations/0020_product_sku.py new file mode 100644 index 0000000..a216205 --- /dev/null +++ b/shop/migrations/0020_product_sku.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.11 on 2025-03-21 12:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0019_product_description'), + ] + + operations = [ + migrations.AddField( + model_name='product', + name='sku', + field=models.CharField(default='PC000', help_text='Product SKU (unique identifier)', max_length=50, unique=True), + ), + ] diff --git a/shop/models.py b/shop/models.py index 73008b2..187d77b 100644 --- a/shop/models.py +++ b/shop/models.py @@ -29,7 +29,9 @@ class Size(models.Model): return self.name class Product(models.Model): + sku = models.CharField(max_length=50, unique=True, help_text="Product SKU (unique identifier)") title = models.CharField(max_length=200) + description = models.TextField(blank=True, null=True, help_text="Product description text") image = models.CharField(max_length=200, null=True, blank=True) price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) @@ -43,7 +45,7 @@ class Product(models.Model): ordering = ['ordering_value', 'cut'] # Add this line to sort by title def __str__(self): - return self.title + return f"{self.sku} - {self.title}" class CartItem(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True) diff --git a/shop/static/shop/css/shop.css b/shop/static/shop/css/shop.css index 2cc272d..253d54c 100644 --- a/shop/static/shop/css/shop.css +++ b/shop/static/shop/css/shop.css @@ -40,29 +40,40 @@ background-color: blue; } +.option-element.product-description { + grid-column: 1 / span 2; + grid-row: 2; + font-size: 14px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; /* limit to 2 lines */ + -webkit-box-orient: vertical; +} + .option-element.product-price { grid-column: 2; - grid-row: 2; + grid-row: 3; text-align: center; background-color: green; } .option-element.color-label { grid-column: 1; - grid-row: 2; - font-size: 12px; + grid-row: 3; + font-size: 14px; background-color: yellow; } .option-element.color-selector { grid-column: 1; - grid-row: 3; + grid-row: 4; background-color: red; } .option-element.size-selector { grid-column: 2; - grid-row: 3; + grid-row: 4; font-size: 12px; text-align: center; background-color: purple; @@ -70,12 +81,12 @@ .option-element.quantity-selector { grid-column: 1; - grid-row: 4; + grid-row: 5; } .option-element.total-price { grid-column: 2; - grid-row: 4; + grid-row: 5; text-align: center; } @@ -156,7 +167,6 @@ .cart-table th, .cart-table td { - padding: 10px; text-align: center; } @@ -381,15 +391,6 @@ v .cart-table { .cart-table td { display: flex; - /* padding: 8px 5px; - text-align: right; - justify-content: space-between; - align-items: center; */ -} - -.cart-table .price-column, -.cart-table .text-left { - /* text-align: right !important; */ } .cart-table td.product-name { @@ -399,24 +400,24 @@ v .cart-table { font-size: 1.1em; } -.cart-table td.product-color { - grid-column: 1 / span 2; +.cart-table td.product-description { + grid-column: 1 / span 3; grid-row: 2; } -/* .cart-table td.product-size { - grid-column: 2; - grid-row: 2; -} */ +.cart-table td.product-color { + grid-column: 1 / span 2; + grid-row: 3; +} .cart-table td.product-quantity { grid-column: 1; - grid-row: 3; + grid-row: 4; } .cart-table td.product-price { grid-column: 3; - grid-row: 3; + grid-row: 4; justify-content: right; } diff --git a/shop/templates/shop/partials/order_items_display.html b/shop/templates/shop/partials/order_items_display.html index d86045f..fabb5f9 100644 --- a/shop/templates/shop/partials/order_items_display.html +++ b/shop/templates/shop/partials/order_items_display.html @@ -3,6 +3,9 @@ {% for item in items %}