diff --git a/backend/api/management/commands/seed_legacy_flavors.py b/backend/api/management/commands/seed_legacy_flavors.py index 0f466bc..42475f9 100644 --- a/backend/api/management/commands/seed_legacy_flavors.py +++ b/backend/api/management/commands/seed_legacy_flavors.py @@ -1,144 +1,95 @@ +import json +import os +import urllib.request from django.core.management.base import BaseCommand +from django.core.files import File +from tempfile import NamedTemporaryFile +from django.conf import settings from api.models import Category, Flavor class Command(BaseCommand): - help = 'Adds legacy flavors that are no longer in the official API or are old versions' + help = 'Adds legacy flavors from JSON files in the legacy/ folder' + + def download_image(self, url, flavor_name): + if not url: + return None + try: + req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) + with urllib.request.urlopen(req) as response: + img_temp = NamedTemporaryFile(delete=True) + img_temp.write(response.read()) + img_temp.flush() + + ext = url.split('.')[-1].split('?')[0] + if len(ext) > 4: ext = 'png' + filename = f"legacy_{flavor_name.replace(' ', '_').lower()}.{ext}" + return File(img_temp, name=filename) + except Exception as e: + self.stdout.write(self.style.WARNING(f"Could not download image for {flavor_name}: {e}")) + return None def handle(self, *args, **options): - # Ensure categories exist - energy = Category.objects.get(slug='energy') - hydration = Category.objects.get(slug='hydration') - iced_tea = Category.objects.get(slug='iced-tea') + # 1. Remove old legacy flavors (ones without external_id or marked as legacy) + # To be safe, we only remove those marked as legacy or with no external_id + removed_count, _ = Flavor.objects.filter(is_legacy=True).delete() + Flavor.objects.filter(external_id__isnull=True).delete() + self.stdout.write(self.style.SUCCESS(f"Removed {removed_count} old legacy flavors.")) - legacy_data = [ - { - "Name": "Hitschies Saure Drachenzunge (Legacy)", - "Geschmack": "Blaue Himbeere", - "Beschreibung": "Eine legendäre Kollaboration mit Hitschies. Schmeckt wie die blauen sauren Drachenzungen – süß, fruchtig und sauer.", - "Status": "Limited Edition / Vaulted", - "Bild_URL": "https://weareholy.com/cdn/shop/files/Holy_Energy_Hitschies_Sour_Dragon_Tongue_Tub_720x.png" - }, - { - "Name": "Hitschies Saure Drachenzunge Rot (Legacy)", - "Geschmack": "Erdbeere & Kirsche", - "Beschreibung": "Die rote Variante der Hitschies-Kollaboration. Fruchtiger Mix aus roten Beeren, angelehnt an die roten Drachenzungen.", - "Status": "Limited Edition", - "Bild_URL": "https://weareholy.com/cdn/shop/files/Holy_Energy_Hitschies_Sour_Dragon_Tongue_Red_Tub_720x.png" - }, - { - "Name": "Lion's Lemonade (Legacy)", - "Geschmack": "Mango & Kiwi", - "Beschreibung": "Einer der beliebtesten Klassiker. Ein tropischer, fruchtiger Mix für maximalen Fokus. Oft von Fans zurückgefordert.", - "Status": "Vaulted", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Lions_Lemonade_Tub_720x.png" - }, - { - "Name": "Lollipop Lovebird (Legacy)", - "Geschmack": "Kirsch-Lolli", - "Beschreibung": "Erinnert an die klassischen roten Kirsch-Lollies aus der Kindheit. Sehr süß und intensiv.", - "Status": "Limited Edition", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Lollipop_Lovebird_Tub_720x.png" - }, - { - "Name": "Frosty Fox (Legacy)", - "Geschmack": "Eisbonbon", - "Beschreibung": "Ein kühler, süßer Geschmack, der an die klassischen Eisbonbons erinnert. Erfrischend mit einer 'kühlen' Note.", - "Status": "Limited Edition", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Frosty_Fox_Tub_720x.png" - }, - { - "Name": "Bubble Gum Butterfly (Legacy)", - "Geschmack": "Kaugummi", - "Beschreibung": "Der typische rosa Kaugummi-Geschmack (Bubblegum). Sehr süß und nostalgisch.", - "Status": "Limited Edition", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Bubblegum_Butterfly_Tub_720x.png" - }, - { - "Name": "Woodruff Wolf (Legacy)", - "Geschmack": "Waldmeister", - "Beschreibung": "Ein traditioneller deutscher Geschmack. Süß-würzig, wie Wackelpudding oder Waldmeister-Sirup.", - "Status": "Limited Edition", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Woodruff_Wolf_Tub_720x.png" - }, - { - "Name": "Baked Apple Boar (Legacy)", - "Geschmack": "Bratapfel", - "Beschreibung": "Eine winterliche Sonderedition mit warmen Noten von Apfel und Zimt.", - "Status": "Seasonal / Vaulted", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Baked_Apple_Boar_Tub_720x.png" - }, - { - "Name": "Kölle Kamelle (Legacy)", - "Geschmack": "Fruchtbonbons (Karnevalsmischung)", - "Beschreibung": "Spezialedition zum Kölner Karneval. Schmeckt nach einem Mix aus verschiedenen Fruchtbonbons ('Kamelle').", - "Status": "Limited Edition", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Koelle_Kamelle_Tub_720x.png" - }, - { - "Name": "Strawberry Shark (Legacy)", - "Geschmack": "Erdbeere & Mandarine", - "Beschreibung": "Ein fruchtiger Mix aus süßer Erdbeere und spritziger Mandarine. Teil des ursprünglichen 'Squads'.", - "Status": "Discontinued", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Strawberry_Shark_Tub_720x.png" - }, - { - "Name": "Blueberry Bear (Legacy)", - "Geschmack": "Blaubeere & Kokosnuss", - "Beschreibung": "Eine weiche Beeren-Note mit einem exotischen Kokos-Abgang.", - "Status": "Discontinued", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Blueberry_Bear_Tub_720x.png" - }, - { - "Name": "Thai Lime Toucan (Legacy)", - "Geschmack": "Kaffir-Limette", - "Beschreibung": "Sehr aromatisch und zesty. Unterscheidet sich von normaler Zitrone durch die herbe Kaffir-Note.", - "Status": "Discontinued", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Thai_Lime_Toucan_Tub_720x.png" - }, - { - "Name": "Cactus Camel (Legacy)", - "Geschmack": "Kaktusfeige", - "Beschreibung": "Erfrischend, leicht melonenartig und nicht zu süß. Ein sehr eigener, beliebter Geschmack.", - "Status": "Discontinued", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Cactus_Camel_Tub_720x.png" - }, - { - "Name": "Apple Alligator (Legacy)", - "Geschmack": "Grüner Apfel", - "Beschreibung": "Knackiger, saurer Apfelgeschmack. Sehr erfrischend.", - "Status": "Discontinued", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Apple_Alligator_Tub_720x.png" - }, - { - "Name": "Cherry Cheetah (Legacy)", - "Geschmack": "Kirsche", - "Beschreibung": "Klassischer, fruchtiger Kirschgeschmack.", - "Status": "Discontinued", - "Bild_URL": "https://weareholy.com/cdn/shop/files/HOLY_Energy_Cherry_Cheetah_Tub_720x.png" - } - ] + # Ensure main categories exist + energy = Category.objects.get_or_create(name='Energy', defaults={'slug': 'energy'})[0] + hydration = Category.objects.get_or_create(name='Hydration', defaults={'slug': 'hydration'})[0] + iced_tea = Category.objects.get_or_create(name='Iced Tea', defaults={'slug': 'iced-tea'})[0] + packs = Category.objects.get_or_create(name='Packs and other', defaults={'slug': 'packs-and-other'})[0] - for data in legacy_data: - full_description = f"**Geschmack:** {data['Geschmack']}\n\n{data['Beschreibung']}\n\n**Status:** {data['Status']}" + legacy_dir = os.path.join(settings.BASE_DIR, '..', 'legacy') + if not os.path.exists(legacy_dir): + self.stdout.write(self.style.ERROR(f"Legacy directory not found at {legacy_dir}")) + return + + json_files = [f for f in os.listdir(legacy_dir) if f.endswith('.json')] + + created_count = 0 + + for json_file in json_files: + file_path = os.path.join(legacy_dir, json_file) + with open(file_path, 'r', encoding='utf-8') as f: + data_list = json.load(f) - cat = energy # Default - if "hydration" in data['Name'].lower() or "hydration" in data['Beschreibung'].lower(): - cat = hydration - elif "iced tea" in data['Name'].lower() or "eistee" in data['Name'].lower(): - cat = iced_tea + for data in data_list: + name = data.get('Name') + desc = data.get('Beschreibung') + taste = data.get('Geschmack') + status_text = data.get('Status') + image_url = data.get('Bild_URL') - flavor, created = Flavor.objects.update_or_create( - name=data['Name'], - defaults={ - 'category': cat, - 'description': full_description, - 'image_url': data['Bild_URL'], - 'is_available': False, - 'external_id': None - } - ) + full_desc = f"**Geschmack:** {taste}\n\n{desc}\n\n**Status:** {status_text}" + + # Determine category + cat = energy + if "hydration" in name.lower() or "hydration" in desc.lower(): + cat = hydration + elif "iced tea" in name.lower() or "eistee" in name.lower(): + cat = iced_tea + elif any(k in name.lower() for k in ['bundle', 'set', 'box', 'probe', 'sample', 'taster', 'shaker', 'starter']): + cat = packs - if created: - self.stdout.write(self.style.SUCCESS(f"Added legacy flavor: {data['Name']}")) - else: - self.stdout.write(self.style.SUCCESS(f"Updated legacy flavor: {data['Name']}")) + flavor = Flavor( + name=name, + category=cat, + description=full_desc, + image_url=image_url, + is_available=False, + is_legacy=True, + external_id=None + ) + + if image_url: + img_file = self.download_image(image_url, name) + if img_file: + flavor.image.save(img_file.name, img_file, save=False) + + flavor.save() + created_count += 1 + self.stdout.write(f"Added legacy flavor: {name}") + + self.stdout.write(self.style.SUCCESS(f"Finished! Created {created_count} legacy flavors from JSON.")) diff --git a/backend/api/management/commands/sync_flavors.py b/backend/api/management/commands/sync_flavors.py index c6ed03b..a1943fd 100644 --- a/backend/api/management/commands/sync_flavors.py +++ b/backend/api/management/commands/sync_flavors.py @@ -1,8 +1,10 @@ import json import urllib.request import re +import os from django.core.management.base import BaseCommand -from django.utils.text import slugify +from django.core.files import File +from tempfile import NamedTemporaryFile from api.models import Category, Flavor class Command(BaseCommand): @@ -15,12 +17,34 @@ class Command(BaseCommand): cleantext = re.sub(cleanr, '', raw_html) return cleantext.strip() + def download_image(self, url, flavor_name): + if not url: + return None + try: + # Simple caching: check if we already have a local image for this URL + # (In a real app, you might compare filenames or hashes) + img_temp = NamedTemporaryFile(delete=True) + req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) + with urllib.request.urlopen(req) as response: + img_temp.write(response.read()) + img_temp.flush() + + # Use extension from URL or default to .png + ext = url.split('.')[-1].split('?')[0] + if len(ext) > 4: ext = 'png' + filename = f"{flavor_name.replace(' ', '_').lower()}.{ext}" + return File(img_temp, name=filename) + except Exception as e: + self.stdout.write(self.style.WARNING(f"Could not download image for {flavor_name}: {e}")) + return None + def handle(self, *args, **options): url = "https://weareholy.com/products.json?limit=250" self.stdout.write(f"Fetching from {url}...") try: - with urllib.request.urlopen(url) as response: + req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) + with urllib.request.urlopen(req) as response: data = json.loads(response.read().decode()) except Exception as e: self.stdout.write(self.style.ERROR(f"Failed to fetch data: {e}")) @@ -54,7 +78,6 @@ class Command(BaseCommand): tags_lower = [t.lower() for t in tags] title_lower = title.lower() - # Check for Packs/Bundles/Samples is_pack = False pack_keywords = ['bundle', 'set', 'box', 'probe', 'sample', 'taster', 'starter', 'collection', 'probier'] if any(k in title_lower for k in pack_keywords): @@ -62,7 +85,6 @@ class Command(BaseCommand): if 'shaker' in title_lower or 'merch' in tags_lower: is_pack = True - # Determine Category if is_pack: category = cat_map['packs'] elif 'holy energy' in tags_lower or 'energy' in tags_lower: @@ -80,52 +102,59 @@ class Command(BaseCommand): else: continue - # Check availability variants = p.get('variants', []) is_available = any(v.get('available') for v in variants) - - # Image images = p.get('images', []) image_url = images[0].get('src') if images else None - - # Description description = self.clean_html(p.get('body_html', '')) - - # Shop URL handle = p.get('handle') shop_url = f"https://weareholy.com/products/{handle}" if handle else None # Create or update flavor = Flavor.objects.filter(external_id=p['id']).first() if not flavor: - flavor = Flavor.objects.filter(name=title).first() + flavor = Flavor.objects.filter(name=title, is_legacy=False).first() if flavor: flavor.external_id = p['id'] flavor.name = title flavor.category = category flavor.description = description - flavor.image_url = image_url flavor.shop_url = shop_url flavor.is_available = is_available + flavor.is_legacy = False # API flavors are not legacy + + # Cache image if URL changed or local image missing + if image_url and (flavor.image_url != image_url or not flavor.image): + img_file = self.download_image(image_url, title) + if img_file: + flavor.image.save(img_file.name, img_file, save=False) + + flavor.image_url = image_url flavor.save() updated_count += 1 else: - Flavor.objects.create( + new_flavor = Flavor( external_id=p['id'], name=title, category=category, description=description, image_url=image_url, shop_url=shop_url, - is_available=is_available + is_available=is_available, + is_legacy=False ) + if image_url: + img_file = self.download_image(image_url, title) + if img_file: + new_flavor.image.save(img_file.name, img_file, save=False) + new_flavor.save() created_count += 1 synced_external_ids.append(p['id']) # Mark flavors NOT in the sync as unavailable (only for those that have an external_id) - # This keeps manually added flavors (with external_id=None) as they are. + # These are discontinued API flavors. discontinued_count = Flavor.objects.filter(external_id__isnull=False).exclude(external_id__in=synced_external_ids).update(is_available=False) self.stdout.write(self.style.SUCCESS(f"Finished! Created: {created_count}, Updated: {updated_count}, Marked Unavailable: {discontinued_count}")) diff --git a/backend/api/migrations/0007_flavor_image_flavor_is_legacy_alter_flavor_image_url.py b/backend/api/migrations/0007_flavor_image_flavor_is_legacy_alter_flavor_image_url.py new file mode 100644 index 0000000..c28d7c0 --- /dev/null +++ b/backend/api/migrations/0007_flavor_image_flavor_is_legacy_alter_flavor_image_url.py @@ -0,0 +1,28 @@ +# Generated by Django 6.0.2 on 2026-02-16 12:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0006_reply'), + ] + + operations = [ + migrations.AddField( + model_name='flavor', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='flavors/'), + ), + migrations.AddField( + model_name='flavor', + name='is_legacy', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='flavor', + name='image_url', + field=models.URLField(blank=True, max_length=1000, null=True), + ), + ] diff --git a/backend/api/models.py b/backend/api/models.py index ebe67dd..34b9b82 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -31,9 +31,11 @@ class Flavor(models.Model): category = models.ForeignKey(Category, related_name='flavors', on_delete=models.CASCADE) name = models.CharField(max_length=100, unique=True) description = models.TextField(blank=True) - image_url = models.URLField(max_length=500, blank=True, null=True) + image_url = models.URLField(max_length=1000, blank=True, null=True) + image = models.ImageField(upload_to='flavors/', blank=True, null=True) shop_url = models.URLField(max_length=500, blank=True, null=True) is_available = models.BooleanField(default=True) + is_legacy = models.BooleanField(default=False) external_id = models.BigIntegerField(unique=True, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) @@ -47,6 +49,12 @@ class Flavor(models.Model): return sum(r.score for r in ratings) / ratings.count() return 0.0 + @property + def cached_image_url(self): + if self.image: + return self.image.url + return self.image_url + class Rating(models.Model): user = models.ForeignKey(User, related_name='ratings', on_delete=models.CASCADE) flavor = models.ForeignKey(Flavor, related_name='ratings', on_delete=models.CASCADE) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 99ae14a..ea8688d 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -22,23 +22,40 @@ class ReplySerializer(serializers.ModelSerializer): class RatingSerializer(serializers.ModelSerializer): user = serializers.StringRelatedField(read_only=True) flavor_name = serializers.CharField(source='flavor.name', read_only=True) - flavor_image = serializers.CharField(source='flavor.image_url', read_only=True) + flavor_image = serializers.SerializerMethodField() replies = ReplySerializer(many=True, read_only=True) class Meta: model = Rating fields = ['id', 'user', 'flavor', 'flavor_name', 'flavor_image', 'score', 'comment', 'created_at', 'replies'] + def get_flavor_image(self, obj): + if obj.flavor.image: + request = self.context.get('request') + if request: + return request.build_absolute_uri(obj.flavor.image.url) + return obj.flavor.image.url + return obj.flavor.image_url + class FlavorSerializer(serializers.ModelSerializer): category_name = serializers.CharField(source='category.name', read_only=True) category_slug = serializers.CharField(source='category.slug', read_only=True) average_rating = serializers.FloatField(read_only=True) user_rating = serializers.SerializerMethodField() ratings = RatingSerializer(many=True, read_only=True) + image_url = serializers.SerializerMethodField() class Meta: model = Flavor - fields = ['id', 'name', 'category', 'category_name', 'category_slug', 'description', 'average_rating', 'user_rating', 'ratings', 'image_url', 'is_available', 'shop_url'] + fields = ['id', 'name', 'category', 'category_name', 'category_slug', 'description', 'average_rating', 'user_rating', 'ratings', 'image_url', 'is_available', 'is_legacy', 'shop_url'] + + def get_image_url(self, obj): + if obj.image: + request = self.context.get('request') + if request: + return request.build_absolute_uri(obj.image.url) + return obj.image.url + return obj.image_url def get_user_rating(self, obj): request = self.context.get('request') diff --git a/backend/holy_backend/settings.py b/backend/holy_backend/settings.py index 76e2342..84c3e38 100644 --- a/backend/holy_backend/settings.py +++ b/backend/holy_backend/settings.py @@ -130,6 +130,9 @@ STATIC_URL = 'static/' STATIC_ROOT = BASE_DIR / 'staticfiles' STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' +MEDIA_URL = 'media/' +MEDIA_ROOT = BASE_DIR / 'media' + # --- Reverse Proxy Configuration --- # These settings are crucial for running behind a proxy like Traefik USE_X_FORWARDED_HOST = True diff --git a/backend/holy_backend/urls.py b/backend/holy_backend/urls.py index c7d1dfb..9b828e3 100644 --- a/backend/holy_backend/urls.py +++ b/backend/holy_backend/urls.py @@ -2,6 +2,8 @@ from django.contrib import admin from django.urls import path, include from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView from rest_framework.authtoken.views import obtain_auth_token +from django.conf import settings +from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), @@ -11,4 +13,4 @@ urlpatterns = [ path('api/schema/', SpectacularAPIView.as_view(), name='schema'), path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), -] +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/media/flavors/3er_box_energy_cherry_cheetah.png b/backend/media/flavors/3er_box_energy_cherry_cheetah.png new file mode 100644 index 0000000..46d71a0 Binary files /dev/null and b/backend/media/flavors/3er_box_energy_cherry_cheetah.png differ diff --git a/backend/media/flavors/acai.jpg b/backend/media/flavors/acai.jpg new file mode 100644 index 0000000..5d95956 Binary files /dev/null and b/backend/media/flavors/acai.jpg differ diff --git a/backend/media/flavors/acai_anaconda.jpg b/backend/media/flavors/acai_anaconda.jpg new file mode 100644 index 0000000..d45273d Binary files /dev/null and b/backend/media/flavors/acai_anaconda.jpg differ diff --git a/backend/media/flavors/ananas.png b/backend/media/flavors/ananas.png new file mode 100644 index 0000000..0723a7a Binary files /dev/null and b/backend/media/flavors/ananas.png differ diff --git a/backend/media/flavors/apfel.jpg b/backend/media/flavors/apfel.jpg new file mode 100644 index 0000000..40ce5bf Binary files /dev/null and b/backend/media/flavors/apfel.jpg differ diff --git a/backend/media/flavors/apple_alligator.jpg b/backend/media/flavors/apple_alligator.jpg new file mode 100644 index 0000000..7378e70 Binary files /dev/null and b/backend/media/flavors/apple_alligator.jpg differ diff --git a/backend/media/flavors/badeschlappen.png b/backend/media/flavors/badeschlappen.png new file mode 100644 index 0000000..a962eb2 Binary files /dev/null and b/backend/media/flavors/badeschlappen.png differ diff --git a/backend/media/flavors/banane.jpg b/backend/media/flavors/banane.jpg new file mode 100644 index 0000000..4e81c47 Binary files /dev/null and b/backend/media/flavors/banane.jpg differ diff --git a/backend/media/flavors/birne.jpg b/backend/media/flavors/birne.jpg new file mode 100644 index 0000000..05feb8d Binary files /dev/null and b/backend/media/flavors/birne.jpg differ diff --git a/backend/media/flavors/blood_orange_bat.jpg b/backend/media/flavors/blood_orange_bat.jpg new file mode 100644 index 0000000..6fc5c44 Binary files /dev/null and b/backend/media/flavors/blood_orange_bat.jpg differ diff --git a/backend/media/flavors/blueberry_bear.jpg b/backend/media/flavors/blueberry_bear.jpg new file mode 100644 index 0000000..6babf34 Binary files /dev/null and b/backend/media/flavors/blueberry_bear.jpg differ diff --git a/backend/media/flavors/bratapfel.jpg b/backend/media/flavors/bratapfel.jpg new file mode 100644 index 0000000..1c25551 Binary files /dev/null and b/backend/media/flavors/bratapfel.jpg differ diff --git a/backend/media/flavors/brombeere.png b/backend/media/flavors/brombeere.png new file mode 100644 index 0000000..81cd3c3 Binary files /dev/null and b/backend/media/flavors/brombeere.png differ diff --git a/backend/media/flavors/bubblegum_butterfly.jpg b/backend/media/flavors/bubblegum_butterfly.jpg new file mode 100644 index 0000000..33adcdd Binary files /dev/null and b/backend/media/flavors/bubblegum_butterfly.jpg differ diff --git a/backend/media/flavors/burrsday_shaker.png b/backend/media/flavors/burrsday_shaker.png new file mode 100644 index 0000000..95a3141 Binary files /dev/null and b/backend/media/flavors/burrsday_shaker.png differ diff --git a/backend/media/flavors/cactus_camel.jpg b/backend/media/flavors/cactus_camel.jpg new file mode 100644 index 0000000..81ecb3e Binary files /dev/null and b/backend/media/flavors/cactus_camel.jpg differ diff --git a/backend/media/flavors/cherry_cheetah.jpg b/backend/media/flavors/cherry_cheetah.jpg new file mode 100644 index 0000000..e307128 Binary files /dev/null and b/backend/media/flavors/cherry_cheetah.jpg differ diff --git a/backend/media/flavors/citrus_cobra.jpg b/backend/media/flavors/citrus_cobra.jpg new file mode 100644 index 0000000..a806999 Binary files /dev/null and b/backend/media/flavors/citrus_cobra.jpg differ diff --git a/backend/media/flavors/community_thermo_shaker.jpg b/backend/media/flavors/community_thermo_shaker.jpg new file mode 100644 index 0000000..42a1781 Binary files /dev/null and b/backend/media/flavors/community_thermo_shaker.jpg differ diff --git a/backend/media/flavors/cranberry.jpg b/backend/media/flavors/cranberry.jpg new file mode 100644 index 0000000..caea8f9 Binary files /dev/null and b/backend/media/flavors/cranberry.jpg differ diff --git a/backend/media/flavors/darjeeling_x_honig.jpg b/backend/media/flavors/darjeeling_x_honig.jpg new file mode 100644 index 0000000..c5ab399 Binary files /dev/null and b/backend/media/flavors/darjeeling_x_honig.jpg differ diff --git a/backend/media/flavors/dosierlöffel__metall.png b/backend/media/flavors/dosierlöffel__metall.png new file mode 100644 index 0000000..93f0e7c Binary files /dev/null and b/backend/media/flavors/dosierlöffel__metall.png differ diff --git a/backend/media/flavors/drachenfrucht.jpg b/backend/media/flavors/drachenfrucht.jpg new file mode 100644 index 0000000..52353c0 Binary files /dev/null and b/backend/media/flavors/drachenfrucht.jpg differ diff --git a/backend/media/flavors/dragon_fruit_dragon.jpg b/backend/media/flavors/dragon_fruit_dragon.jpg new file mode 100644 index 0000000..bdf73f3 Binary files /dev/null and b/backend/media/flavors/dragon_fruit_dragon.jpg differ diff --git a/backend/media/flavors/einzelsorten-box_energy.jpg b/backend/media/flavors/einzelsorten-box_energy.jpg new file mode 100644 index 0000000..671a5bb Binary files /dev/null and b/backend/media/flavors/einzelsorten-box_energy.jpg differ diff --git a/backend/media/flavors/einzelsorten-box_hydration.jpg b/backend/media/flavors/einzelsorten-box_hydration.jpg new file mode 100644 index 0000000..d55703e Binary files /dev/null and b/backend/media/flavors/einzelsorten-box_hydration.jpg differ diff --git a/backend/media/flavors/einzelsorten-box_iced_tea.webp b/backend/media/flavors/einzelsorten-box_iced_tea.webp new file mode 100644 index 0000000..17bbee2 Binary files /dev/null and b/backend/media/flavors/einzelsorten-box_iced_tea.webp differ diff --git a/backend/media/flavors/einzelsorten-box_milkshake.jpg b/backend/media/flavors/einzelsorten-box_milkshake.jpg new file mode 100644 index 0000000..bd8208d Binary files /dev/null and b/backend/media/flavors/einzelsorten-box_milkshake.jpg differ diff --git a/backend/media/flavors/energy-probe.png b/backend/media/flavors/energy-probe.png new file mode 100644 index 0000000..49778d5 Binary files /dev/null and b/backend/media/flavors/energy-probe.png differ diff --git a/backend/media/flavors/energy_starter_set.jpg b/backend/media/flavors/energy_starter_set.jpg new file mode 100644 index 0000000..25a885a Binary files /dev/null and b/backend/media/flavors/energy_starter_set.jpg differ diff --git a/backend/media/flavors/erdbeere.jpg b/backend/media/flavors/erdbeere.jpg new file mode 100644 index 0000000..b3e36fd Binary files /dev/null and b/backend/media/flavors/erdbeere.jpg differ diff --git a/backend/media/flavors/erdbeere.webp b/backend/media/flavors/erdbeere.webp new file mode 100644 index 0000000..085d885 Binary files /dev/null and b/backend/media/flavors/erdbeere.webp differ diff --git a/backend/media/flavors/erdbeere_x_kiwi.jpg b/backend/media/flavors/erdbeere_x_kiwi.jpg new file mode 100644 index 0000000..a35a8a2 Binary files /dev/null and b/backend/media/flavors/erdbeere_x_kiwi.jpg differ diff --git a/backend/media/flavors/ersatzdichtung__glasflasche.png b/backend/media/flavors/ersatzdichtung__glasflasche.png new file mode 100644 index 0000000..0fea288 Binary files /dev/null and b/backend/media/flavors/ersatzdichtung__glasflasche.png differ diff --git a/backend/media/flavors/ersatzteil__shaker.png b/backend/media/flavors/ersatzteil__shaker.png new file mode 100644 index 0000000..a4b78c3 Binary files /dev/null and b/backend/media/flavors/ersatzteil__shaker.png differ diff --git a/backend/media/flavors/ersatzteile__thermo_shaker.webp b/backend/media/flavors/ersatzteile__thermo_shaker.webp new file mode 100644 index 0000000..77c7aec Binary files /dev/null and b/backend/media/flavors/ersatzteile__thermo_shaker.webp differ diff --git a/backend/media/flavors/ersatzteile__thermo_shaker_1l.webp b/backend/media/flavors/ersatzteile__thermo_shaker_1l.webp new file mode 100644 index 0000000..77c7aec Binary files /dev/null and b/backend/media/flavors/ersatzteile__thermo_shaker_1l.webp differ diff --git a/backend/media/flavors/ersatzteile_thermo_shaker.png b/backend/media/flavors/ersatzteile_thermo_shaker.png new file mode 100644 index 0000000..5477849 Binary files /dev/null and b/backend/media/flavors/ersatzteile_thermo_shaker.png differ diff --git a/backend/media/flavors/fanschal__1._fc_köln.webp b/backend/media/flavors/fanschal__1._fc_köln.webp new file mode 100644 index 0000000..8f96328 Binary files /dev/null and b/backend/media/flavors/fanschal__1._fc_köln.webp differ diff --git a/backend/media/flavors/frosty_fox.jpg b/backend/media/flavors/frosty_fox.jpg new file mode 100644 index 0000000..afb5d50 Binary files /dev/null and b/backend/media/flavors/frosty_fox.jpg differ diff --git a/backend/media/flavors/fruity_frog.png b/backend/media/flavors/fruity_frog.png new file mode 100644 index 0000000..8c75be8 Binary files /dev/null and b/backend/media/flavors/fruity_frog.png differ diff --git a/backend/media/flavors/geschenkgutschein.png b/backend/media/flavors/geschenkgutschein.png new file mode 100644 index 0000000..d3d120a Binary files /dev/null and b/backend/media/flavors/geschenkgutschein.png differ diff --git a/backend/media/flavors/giga_gallon_weiß.png b/backend/media/flavors/giga_gallon_weiß.png new file mode 100644 index 0000000..8f0e217 Binary files /dev/null and b/backend/media/flavors/giga_gallon_weiß.png differ diff --git a/backend/media/flavors/gorillas_grape.jpg b/backend/media/flavors/gorillas_grape.jpg new file mode 100644 index 0000000..fcf1049 Binary files /dev/null and b/backend/media/flavors/gorillas_grape.jpg differ diff --git a/backend/media/flavors/grapefruit.jpg b/backend/media/flavors/grapefruit.jpg new file mode 100644 index 0000000..e197829 Binary files /dev/null and b/backend/media/flavors/grapefruit.jpg differ diff --git a/backend/media/flavors/grapefruit_giraffe.jpg b/backend/media/flavors/grapefruit_giraffe.jpg new file mode 100644 index 0000000..c90e924 Binary files /dev/null and b/backend/media/flavors/grapefruit_giraffe.jpg differ diff --git a/backend/media/flavors/grüner_tee.png b/backend/media/flavors/grüner_tee.png new file mode 100644 index 0000000..c732f7a Binary files /dev/null and b/backend/media/flavors/grüner_tee.png differ diff --git a/backend/media/flavors/guave.jpg b/backend/media/flavors/guave.jpg new file mode 100644 index 0000000..213d118 Binary files /dev/null and b/backend/media/flavors/guave.jpg differ diff --git a/backend/media/flavors/haselnuss.jpg b/backend/media/flavors/haselnuss.jpg new file mode 100644 index 0000000..88d1980 Binary files /dev/null and b/backend/media/flavors/haselnuss.jpg differ diff --git a/backend/media/flavors/hibiskus.png b/backend/media/flavors/hibiskus.png new file mode 100644 index 0000000..c2c1419 Binary files /dev/null and b/backend/media/flavors/hibiskus.png differ diff --git a/backend/media/flavors/himbeere_x_vanille.jpg b/backend/media/flavors/himbeere_x_vanille.jpg new file mode 100644 index 0000000..099d106 Binary files /dev/null and b/backend/media/flavors/himbeere_x_vanille.jpg differ diff --git a/backend/media/flavors/holy_brushed_logo_thermo_shaker_adventcalendar.png b/backend/media/flavors/holy_brushed_logo_thermo_shaker_adventcalendar.png new file mode 100644 index 0000000..bb3856e Binary files /dev/null and b/backend/media/flavors/holy_brushed_logo_thermo_shaker_adventcalendar.png differ diff --git a/backend/media/flavors/holy_geschenkbox.png b/backend/media/flavors/holy_geschenkbox.png new file mode 100644 index 0000000..2261efe Binary files /dev/null and b/backend/media/flavors/holy_geschenkbox.png differ diff --git a/backend/media/flavors/holy_holy_holy_shaker.png b/backend/media/flavors/holy_holy_holy_shaker.png new file mode 100644 index 0000000..5da30fe Binary files /dev/null and b/backend/media/flavors/holy_holy_holy_shaker.png differ diff --git a/backend/media/flavors/holy_löwen_frankfurt_shaker.png b/backend/media/flavors/holy_löwen_frankfurt_shaker.png new file mode 100644 index 0000000..47fea1f Binary files /dev/null and b/backend/media/flavors/holy_löwen_frankfurt_shaker.png differ diff --git a/backend/media/flavors/holy_pflege_shaker.png b/backend/media/flavors/holy_pflege_shaker.png new file mode 100644 index 0000000..6668fa8 Binary files /dev/null and b/backend/media/flavors/holy_pflege_shaker.png differ diff --git a/backend/media/flavors/holy_squad_shaker.jpg b/backend/media/flavors/holy_squad_shaker.jpg new file mode 100644 index 0000000..cd78e6b Binary files /dev/null and b/backend/media/flavors/holy_squad_shaker.jpg differ diff --git a/backend/media/flavors/holy_x_7_vs._wild_thermo_shaker_2025.png b/backend/media/flavors/holy_x_7_vs._wild_thermo_shaker_2025.png new file mode 100644 index 0000000..b9457bf Binary files /dev/null and b/backend/media/flavors/holy_x_7_vs._wild_thermo_shaker_2025.png differ diff --git a/backend/media/flavors/holy_x_mission_unknown_shaker.png b/backend/media/flavors/holy_x_mission_unknown_shaker.png new file mode 100644 index 0000000..37d205f Binary files /dev/null and b/backend/media/flavors/holy_x_mission_unknown_shaker.png differ diff --git a/backend/media/flavors/honigmelone.jpg b/backend/media/flavors/honigmelone.jpg new file mode 100644 index 0000000..da8fb61 Binary files /dev/null and b/backend/media/flavors/honigmelone.jpg differ diff --git a/backend/media/flavors/hotfoot_run_turnbeutel.png b/backend/media/flavors/hotfoot_run_turnbeutel.png new file mode 100644 index 0000000..77edda0 Binary files /dev/null and b/backend/media/flavors/hotfoot_run_turnbeutel.png differ diff --git a/backend/media/flavors/hydration-probe.png b/backend/media/flavors/hydration-probe.png new file mode 100644 index 0000000..7c59cf0 Binary files /dev/null and b/backend/media/flavors/hydration-probe.png differ diff --git a/backend/media/flavors/hydration_starter_set.jpg b/backend/media/flavors/hydration_starter_set.jpg new file mode 100644 index 0000000..898153e Binary files /dev/null and b/backend/media/flavors/hydration_starter_set.jpg differ diff --git a/backend/media/flavors/iced_tea-probe.webp b/backend/media/flavors/iced_tea-probe.webp new file mode 100644 index 0000000..e100b39 Binary files /dev/null and b/backend/media/flavors/iced_tea-probe.webp differ diff --git a/backend/media/flavors/iced_tea_starter_set.jpg b/backend/media/flavors/iced_tea_starter_set.jpg new file mode 100644 index 0000000..820f7c4 Binary files /dev/null and b/backend/media/flavors/iced_tea_starter_set.jpg differ diff --git a/backend/media/flavors/kappe_-_hotfoot_run.png b/backend/media/flavors/kappe_-_hotfoot_run.png new file mode 100644 index 0000000..06a1db3 Binary files /dev/null and b/backend/media/flavors/kappe_-_hotfoot_run.png differ diff --git a/backend/media/flavors/karamell.jpg b/backend/media/flavors/karamell.jpg new file mode 100644 index 0000000..1a4af65 Binary files /dev/null and b/backend/media/flavors/karamell.jpg differ diff --git a/backend/media/flavors/kiwi_komodo.jpg b/backend/media/flavors/kiwi_komodo.jpg new file mode 100644 index 0000000..6f2a19f Binary files /dev/null and b/backend/media/flavors/kiwi_komodo.jpg differ diff --git a/backend/media/flavors/kokosnuss.png b/backend/media/flavors/kokosnuss.png new file mode 100644 index 0000000..6facb00 Binary files /dev/null and b/backend/media/flavors/kokosnuss.png differ diff --git a/backend/media/flavors/kölle_kamelle.webp b/backend/media/flavors/kölle_kamelle.webp new file mode 100644 index 0000000..fb83f7e Binary files /dev/null and b/backend/media/flavors/kölle_kamelle.webp differ diff --git a/backend/media/flavors/kühltasche.png b/backend/media/flavors/kühltasche.png new file mode 100644 index 0000000..1cd428d Binary files /dev/null and b/backend/media/flavors/kühltasche.png differ diff --git a/backend/media/flavors/legacy_dolphin_daiquiri.png b/backend/media/flavors/legacy_dolphin_daiquiri.png new file mode 100644 index 0000000..9adcf21 Binary files /dev/null and b/backend/media/flavors/legacy_dolphin_daiquiri.png differ diff --git a/backend/media/flavors/legacy_hitschies_saure_drachenzunge_blau.png b/backend/media/flavors/legacy_hitschies_saure_drachenzunge_blau.png new file mode 100644 index 0000000..83176ae Binary files /dev/null and b/backend/media/flavors/legacy_hitschies_saure_drachenzunge_blau.png differ diff --git a/backend/media/flavors/legacy_hitschies_saure_krakenarme.png b/backend/media/flavors/legacy_hitschies_saure_krakenarme.png new file mode 100644 index 0000000..d71a1d8 Binary files /dev/null and b/backend/media/flavors/legacy_hitschies_saure_krakenarme.png differ diff --git a/backend/media/flavors/legacy_hitschies_saure_spinnenbeine.png b/backend/media/flavors/legacy_hitschies_saure_spinnenbeine.png new file mode 100644 index 0000000..336929c Binary files /dev/null and b/backend/media/flavors/legacy_hitschies_saure_spinnenbeine.png differ diff --git a/backend/media/flavors/lemon_lizard.jpg b/backend/media/flavors/lemon_lizard.jpg new file mode 100644 index 0000000..f6993d8 Binary files /dev/null and b/backend/media/flavors/lemon_lizard.jpg differ diff --git a/backend/media/flavors/limette_x_matcha_x_minze.jpg b/backend/media/flavors/limette_x_matcha_x_minze.jpg new file mode 100644 index 0000000..8f24ab6 Binary files /dev/null and b/backend/media/flavors/limette_x_matcha_x_minze.jpg differ diff --git a/backend/media/flavors/limited_strawberry_shark.png b/backend/media/flavors/limited_strawberry_shark.png new file mode 100644 index 0000000..cfea85c Binary files /dev/null and b/backend/media/flavors/limited_strawberry_shark.png differ diff --git a/backend/media/flavors/lions_lemonade.jpg b/backend/media/flavors/lions_lemonade.jpg new file mode 100644 index 0000000..2c5f232 Binary files /dev/null and b/backend/media/flavors/lions_lemonade.jpg differ diff --git a/backend/media/flavors/logo_shaker.png b/backend/media/flavors/logo_shaker.png new file mode 100644 index 0000000..6f8964f Binary files /dev/null and b/backend/media/flavors/logo_shaker.png differ diff --git a/backend/media/flavors/lollipop_lovebird.webp b/backend/media/flavors/lollipop_lovebird.webp new file mode 100644 index 0000000..e8ed170 Binary files /dev/null and b/backend/media/flavors/lollipop_lovebird.webp differ diff --git a/backend/media/flavors/mango_x_passionsfrucht.png b/backend/media/flavors/mango_x_passionsfrucht.png new file mode 100644 index 0000000..9c2e418 Binary files /dev/null and b/backend/media/flavors/mango_x_passionsfrucht.png differ diff --git a/backend/media/flavors/milkshake-probe.png b/backend/media/flavors/milkshake-probe.png new file mode 100644 index 0000000..aa4ac5e Binary files /dev/null and b/backend/media/flavors/milkshake-probe.png differ diff --git a/backend/media/flavors/milkshake_starter_set.png b/backend/media/flavors/milkshake_starter_set.png new file mode 100644 index 0000000..0681110 Binary files /dev/null and b/backend/media/flavors/milkshake_starter_set.png differ diff --git a/backend/media/flavors/mix_probier-box_energy.png b/backend/media/flavors/mix_probier-box_energy.png new file mode 100644 index 0000000..0f0fa7f Binary files /dev/null and b/backend/media/flavors/mix_probier-box_energy.png differ diff --git a/backend/media/flavors/mix_probier-box_hydration.png b/backend/media/flavors/mix_probier-box_hydration.png new file mode 100644 index 0000000..755cfb8 Binary files /dev/null and b/backend/media/flavors/mix_probier-box_hydration.png differ diff --git a/backend/media/flavors/mix_probier-box_iced_tea.png b/backend/media/flavors/mix_probier-box_iced_tea.png new file mode 100644 index 0000000..31e31c2 Binary files /dev/null and b/backend/media/flavors/mix_probier-box_iced_tea.png differ diff --git a/backend/media/flavors/mix_probier-box_iced_tea_classics.webp b/backend/media/flavors/mix_probier-box_iced_tea_classics.webp new file mode 100644 index 0000000..608a48b Binary files /dev/null and b/backend/media/flavors/mix_probier-box_iced_tea_classics.webp differ diff --git a/backend/media/flavors/mix_probier-box_milkshake.png b/backend/media/flavors/mix_probier-box_milkshake.png new file mode 100644 index 0000000..f4dc887 Binary files /dev/null and b/backend/media/flavors/mix_probier-box_milkshake.png differ diff --git a/backend/media/flavors/multivitamin.jpg b/backend/media/flavors/multivitamin.jpg new file mode 100644 index 0000000..941cc2d Binary files /dev/null and b/backend/media/flavors/multivitamin.jpg differ diff --git a/backend/media/flavors/orange.jpg b/backend/media/flavors/orange.jpg new file mode 100644 index 0000000..2abf847 Binary files /dev/null and b/backend/media/flavors/orange.jpg differ diff --git a/backend/media/flavors/passionsfrucht.jpg b/backend/media/flavors/passionsfrucht.jpg new file mode 100644 index 0000000..f24c980 Binary files /dev/null and b/backend/media/flavors/passionsfrucht.jpg differ diff --git a/backend/media/flavors/peach_panther.jpg b/backend/media/flavors/peach_panther.jpg new file mode 100644 index 0000000..ce445bd Binary files /dev/null and b/backend/media/flavors/peach_panther.jpg differ diff --git a/backend/media/flavors/peacocks_punch.jpg b/backend/media/flavors/peacocks_punch.jpg new file mode 100644 index 0000000..6822c43 Binary files /dev/null and b/backend/media/flavors/peacocks_punch.jpg differ diff --git a/backend/media/flavors/pfirsich.jpg b/backend/media/flavors/pfirsich.jpg new file mode 100644 index 0000000..a0c21d1 Binary files /dev/null and b/backend/media/flavors/pfirsich.jpg differ diff --git a/backend/media/flavors/pomegranate_piranha.jpg b/backend/media/flavors/pomegranate_piranha.jpg new file mode 100644 index 0000000..2f08cf7 Binary files /dev/null and b/backend/media/flavors/pomegranate_piranha.jpg differ diff --git a/backend/media/flavors/premium_hoodie_-_blackweek.png b/backend/media/flavors/premium_hoodie_-_blackweek.png new file mode 100644 index 0000000..b839239 Binary files /dev/null and b/backend/media/flavors/premium_hoodie_-_blackweek.png differ diff --git a/backend/media/flavors/raspberry_raptor.jpg b/backend/media/flavors/raspberry_raptor.jpg new file mode 100644 index 0000000..4a31f5f Binary files /dev/null and b/backend/media/flavors/raspberry_raptor.jpg differ diff --git a/backend/media/flavors/rote_traube.jpg b/backend/media/flavors/rote_traube.jpg new file mode 100644 index 0000000..934a1c4 Binary files /dev/null and b/backend/media/flavors/rote_traube.jpg differ diff --git a/backend/media/flavors/sauerkirsche.png b/backend/media/flavors/sauerkirsche.png new file mode 100644 index 0000000..9b028e6 Binary files /dev/null and b/backend/media/flavors/sauerkirsche.png differ diff --git a/backend/media/flavors/schwarzer_tee.png b/backend/media/flavors/schwarzer_tee.png new file mode 100644 index 0000000..744960b Binary files /dev/null and b/backend/media/flavors/schwarzer_tee.png differ diff --git a/backend/media/flavors/shaker__1._fc_köln.webp b/backend/media/flavors/shaker__1._fc_köln.webp new file mode 100644 index 0000000..bea18d7 Binary files /dev/null and b/backend/media/flavors/shaker__1._fc_köln.webp differ diff --git a/backend/media/flavors/shaker__hydration.jpg b/backend/media/flavors/shaker__hydration.jpg new file mode 100644 index 0000000..e197829 Binary files /dev/null and b/backend/media/flavors/shaker__hydration.jpg differ diff --git a/backend/media/flavors/shaker__iced_tea.jpg b/backend/media/flavors/shaker__iced_tea.jpg new file mode 100644 index 0000000..e8dcec4 Binary files /dev/null and b/backend/media/flavors/shaker__iced_tea.jpg differ diff --git a/backend/media/flavors/shaker__milkshake.jpg b/backend/media/flavors/shaker__milkshake.jpg new file mode 100644 index 0000000..ed6630c Binary files /dev/null and b/backend/media/flavors/shaker__milkshake.jpg differ diff --git a/backend/media/flavors/shaker_energy.webp b/backend/media/flavors/shaker_energy.webp new file mode 100644 index 0000000..a7e2254 Binary files /dev/null and b/backend/media/flavors/shaker_energy.webp differ diff --git a/backend/media/flavors/spekulatius.jpg b/backend/media/flavors/spekulatius.jpg new file mode 100644 index 0000000..81f1e71 Binary files /dev/null and b/backend/media/flavors/spekulatius.jpg differ diff --git a/backend/media/flavors/spongebob__friends_shaker.png b/backend/media/flavors/spongebob__friends_shaker.png new file mode 100644 index 0000000..8228064 Binary files /dev/null and b/backend/media/flavors/spongebob__friends_shaker.png differ diff --git a/backend/media/flavors/squad_shaker_xl.png b/backend/media/flavors/squad_shaker_xl.png new file mode 100644 index 0000000..7330189 Binary files /dev/null and b/backend/media/flavors/squad_shaker_xl.png differ diff --git a/backend/media/flavors/starter_set_deluxe.jpg b/backend/media/flavors/starter_set_deluxe.jpg new file mode 100644 index 0000000..60d7977 Binary files /dev/null and b/backend/media/flavors/starter_set_deluxe.jpg differ diff --git a/backend/media/flavors/starter_set_deluxe.png b/backend/media/flavors/starter_set_deluxe.png new file mode 100644 index 0000000..11ae4db Binary files /dev/null and b/backend/media/flavors/starter_set_deluxe.png differ diff --git a/backend/media/flavors/starter_set_deluxe_4DZjmGt.jpg b/backend/media/flavors/starter_set_deluxe_4DZjmGt.jpg new file mode 100644 index 0000000..d3e1fe5 Binary files /dev/null and b/backend/media/flavors/starter_set_deluxe_4DZjmGt.jpg differ diff --git a/backend/media/flavors/starter_set_deluxe__thermo_shaker.png b/backend/media/flavors/starter_set_deluxe__thermo_shaker.png new file mode 100644 index 0000000..4b265bc Binary files /dev/null and b/backend/media/flavors/starter_set_deluxe__thermo_shaker.png differ diff --git a/backend/media/flavors/starter_set_deluxe__thermo_shaker_Kbno0U5.png b/backend/media/flavors/starter_set_deluxe__thermo_shaker_Kbno0U5.png new file mode 100644 index 0000000..01408d8 Binary files /dev/null and b/backend/media/flavors/starter_set_deluxe__thermo_shaker_Kbno0U5.png differ diff --git a/backend/media/flavors/starter_set_deluxe_löwen_frankfurt.jpg b/backend/media/flavors/starter_set_deluxe_löwen_frankfurt.jpg new file mode 100644 index 0000000..e4646aa Binary files /dev/null and b/backend/media/flavors/starter_set_deluxe_löwen_frankfurt.jpg differ diff --git a/backend/media/flavors/stell_dir_dein_starter_set_zusammen.jpg b/backend/media/flavors/stell_dir_dein_starter_set_zusammen.jpg new file mode 100644 index 0000000..2084b0e Binary files /dev/null and b/backend/media/flavors/stell_dir_dein_starter_set_zusammen.jpg differ diff --git a/backend/media/flavors/strawberry_shark.jpg b/backend/media/flavors/strawberry_shark.jpg new file mode 100644 index 0000000..fb066bf Binary files /dev/null and b/backend/media/flavors/strawberry_shark.jpg differ diff --git a/backend/media/flavors/tangerine_tarantula.png b/backend/media/flavors/tangerine_tarantula.png new file mode 100644 index 0000000..449444a Binary files /dev/null and b/backend/media/flavors/tangerine_tarantula.png differ diff --git a/backend/media/flavors/thai_lime_toucan.jpg b/backend/media/flavors/thai_lime_toucan.jpg new file mode 100644 index 0000000..4a84948 Binary files /dev/null and b/backend/media/flavors/thai_lime_toucan.jpg differ diff --git a/backend/media/flavors/thermo_shaker.jpg b/backend/media/flavors/thermo_shaker.jpg new file mode 100644 index 0000000..f21ef3f Binary files /dev/null and b/backend/media/flavors/thermo_shaker.jpg differ diff --git a/backend/media/flavors/thermo_shaker.png b/backend/media/flavors/thermo_shaker.png new file mode 100644 index 0000000..5f63da4 Binary files /dev/null and b/backend/media/flavors/thermo_shaker.png differ diff --git a/backend/media/flavors/thermo_shaker_-_1l.webp b/backend/media/flavors/thermo_shaker_-_1l.webp new file mode 100644 index 0000000..8c665e0 Binary files /dev/null and b/backend/media/flavors/thermo_shaker_-_1l.webp differ diff --git a/backend/media/flavors/thermo_shaker_1l_blau.png b/backend/media/flavors/thermo_shaker_1l_blau.png new file mode 100644 index 0000000..dcca6fe Binary files /dev/null and b/backend/media/flavors/thermo_shaker_1l_blau.png differ diff --git a/backend/media/flavors/thermo_shaker__1._fc_köln.webp b/backend/media/flavors/thermo_shaker__1._fc_köln.webp new file mode 100644 index 0000000..d94394b Binary files /dev/null and b/backend/media/flavors/thermo_shaker__1._fc_köln.webp differ diff --git a/backend/media/flavors/thermo_shaker__1l_new_year.png b/backend/media/flavors/thermo_shaker__1l_new_year.png new file mode 100644 index 0000000..d47cdf2 Binary files /dev/null and b/backend/media/flavors/thermo_shaker__1l_new_year.png differ diff --git a/backend/media/flavors/thermo_shaker__700ml_new_year.png b/backend/media/flavors/thermo_shaker__700ml_new_year.png new file mode 100644 index 0000000..0d6ae41 Binary files /dev/null and b/backend/media/flavors/thermo_shaker__700ml_new_year.png differ diff --git a/backend/media/flavors/thermo_shaker__700ml_new_year_mit_sport-deckel.png b/backend/media/flavors/thermo_shaker__700ml_new_year_mit_sport-deckel.png new file mode 100644 index 0000000..f666f90 Binary files /dev/null and b/backend/media/flavors/thermo_shaker__700ml_new_year_mit_sport-deckel.png differ diff --git a/backend/media/flavors/thermo_shaker__spongebob.jpg b/backend/media/flavors/thermo_shaker__spongebob.jpg new file mode 100644 index 0000000..62aab15 Binary files /dev/null and b/backend/media/flavors/thermo_shaker__spongebob.jpg differ diff --git a/backend/media/flavors/thermo_shaker__sports_lid.jpg b/backend/media/flavors/thermo_shaker__sports_lid.jpg new file mode 100644 index 0000000..ac03d71 Binary files /dev/null and b/backend/media/flavors/thermo_shaker__sports_lid.jpg differ diff --git a/backend/media/flavors/thermo_shaker_multichrom.png b/backend/media/flavors/thermo_shaker_multichrom.png new file mode 100644 index 0000000..758bc2a Binary files /dev/null and b/backend/media/flavors/thermo_shaker_multichrom.png differ diff --git a/backend/media/flavors/thermo_shaker_sportsfreund.png b/backend/media/flavors/thermo_shaker_sportsfreund.png new file mode 100644 index 0000000..4698365 Binary files /dev/null and b/backend/media/flavors/thermo_shaker_sportsfreund.png differ diff --git a/backend/media/flavors/tropical_tiger.jpg b/backend/media/flavors/tropical_tiger.jpg new file mode 100644 index 0000000..d5b510d Binary files /dev/null and b/backend/media/flavors/tropical_tiger.jpg differ diff --git a/backend/media/flavors/vanille.jpg b/backend/media/flavors/vanille.jpg new file mode 100644 index 0000000..518d73a Binary files /dev/null and b/backend/media/flavors/vanille.jpg differ diff --git a/backend/media/flavors/wassermelone.jpg b/backend/media/flavors/wassermelone.jpg new file mode 100644 index 0000000..bc337c8 Binary files /dev/null and b/backend/media/flavors/wassermelone.jpg differ diff --git a/backend/media/flavors/wassermelone_y1gSuob.jpg b/backend/media/flavors/wassermelone_y1gSuob.jpg new file mode 100644 index 0000000..f4b7493 Binary files /dev/null and b/backend/media/flavors/wassermelone_y1gSuob.jpg differ diff --git a/backend/media/flavors/watermelon_whale.jpg b/backend/media/flavors/watermelon_whale.jpg new file mode 100644 index 0000000..8738004 Binary files /dev/null and b/backend/media/flavors/watermelon_whale.jpg differ diff --git a/backend/media/flavors/weißer_pfirsich.jpg b/backend/media/flavors/weißer_pfirsich.jpg new file mode 100644 index 0000000..2b260c9 Binary files /dev/null and b/backend/media/flavors/weißer_pfirsich.jpg differ diff --git a/backend/media/flavors/wildberry_wolf.jpg b/backend/media/flavors/wildberry_wolf.jpg new file mode 100644 index 0000000..8684ec7 Binary files /dev/null and b/backend/media/flavors/wildberry_wolf.jpg differ diff --git a/backend/media/flavors/winter_punch.jpg b/backend/media/flavors/winter_punch.jpg new file mode 100644 index 0000000..95a6335 Binary files /dev/null and b/backend/media/flavors/winter_punch.jpg differ diff --git a/backend/media/flavors/woodruff_wolf.jpg b/backend/media/flavors/woodruff_wolf.jpg new file mode 100644 index 0000000..8e9e94e Binary files /dev/null and b/backend/media/flavors/woodruff_wolf.jpg differ diff --git a/backend/media/flavors/zitrone.jpg b/backend/media/flavors/zitrone.jpg new file mode 100644 index 0000000..0eb1f4f Binary files /dev/null and b/backend/media/flavors/zitrone.jpg differ diff --git a/backend/media/flavors/zitrone_x_honig.jpg b/backend/media/flavors/zitrone_x_honig.jpg new file mode 100644 index 0000000..9ba952c Binary files /dev/null and b/backend/media/flavors/zitrone_x_honig.jpg differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a828726..04da845 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useMemo } from 'react'; -import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; +import { BrowserRouter as Router, Routes, Route, Link, useNavigate, useLocation } from 'react-router-dom'; import { ThemeProvider, CssBaseline, @@ -18,10 +18,13 @@ import { ListItem, ListItemButton, ListItemText, - useMediaQuery + useMediaQuery, + TextField, + InputAdornment } from '@mui/material'; import PaletteIcon from '@mui/icons-material/Palette'; import MenuIcon from '@mui/icons-material/Menu'; +import SearchIcon from '@mui/icons-material/Search'; import { getTheme, type CatppuccinTheme } from './theme'; import CategoryList from './pages/CategoryList'; import CategoryFlavors from './pages/categories/CategoryFlavors'; @@ -32,13 +35,61 @@ import Login from './pages/Login'; import Settings from './pages/Settings'; import api from './api'; +const GlobalSearch = () => { + const [query, setQuery] = useState(''); + const navigate = useNavigate(); + const location = useLocation(); + + useEffect(() => { + const params = new URLSearchParams(location.search); + setQuery(params.get('q') || ''); + }, [location.search]); + + const handleSearch = (e: React.FormEvent) => { + e.preventDefault(); + // Redirect to categories page with search query + if (query.trim()) { + navigate(`/?q=${encodeURIComponent(query.trim())}`); + } else { + navigate('/'); + } + }; + + return ( +
+ ); +}; + const App: React.FC = () => { const [themeName, setThemeName] = useState