Compare commits

..

No commits in common. "1c92c56e822cef2c98c23b8c0921d2e969ac7d95" and "5b9b867963eca600ed64b617dc2dc86c30dbd9cb" have entirely different histories.

103 changed files with 15889 additions and 34148 deletions

223
.gitignore vendored
View File

@ -1,157 +1,3 @@
<<<<<<< HEAD
# Django
*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
db.sqlite3-journal
media/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Virtual environment
venv/
env/
ENV/
env.bak/
venv.bak/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output
# Dependency directories
node_modules/
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
public
# Storybook build outputs
.out
.storybook-out
# Temporary folders
tmp/
temp/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Database
*.db
*.sqlite
*.sqlite3
# Static files (if collected)
staticfiles/
# Media files
media/
# Backup files
*.bak
*.backup
*.old
# Certificate files
*.pem
*.key
*.crt
# Docker
.dockerignore
Dockerfile.prod
# Testing
.coverage
htmlcov/
.tox/
.pytest_cache/
nosetests.xml
coverage.xml
*.cover
.hypothesis/
=======
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -204,63 +50,11 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Translations
*.mo
*.pot
<<<<<<< HEAD
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
=======
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Django stuff:
*.log
local_settings.py
@ -283,13 +77,6 @@ target/
# Jupyter Notebook
.ipynb_checkpoints
<<<<<<< HEAD
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
=======
# IPython
profile_default/
ipython_config.py
@ -310,7 +97,6 @@ __pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# SageMath parsed files
*.sage.py
@ -342,12 +128,6 @@ dmypy.json
# Pyre type checker
.pyre/
<<<<<<< HEAD
# Project specific
uploads/
temp/
cache/
=======
# Django static files
staticfiles/
@ -373,5 +153,4 @@ Thumbs.db
# Environment variables
.env.local
.env.production
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
.env.production

View File

@ -1,44 +1,21 @@
<<<<<<< HEAD
import os
from pathlib import Path
from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Lade .env Datei
load_dotenv(BASE_DIR / '.env')
# E-Mail-Einstellungen (temporär Console-Backend)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = 'Fursuit Shop <noreply@fursuitshop.com>'
# Admin-E-Mail-Empfänger
ADMINS = [
('Shop Admin', 'admin@fursuitshop.com'),
]
# Lagerbestand-Einstellungen
=======
import os
from pathlib import Path
from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Lade .env Datei
load_dotenv(BASE_DIR / '.env')
# E-Mail-Einstellungen (temporär Console-Backend)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = 'Fursuit Shop <noreply@fursuitshop.com>'
# Admin-E-Mail-Empfänger
ADMINS = [
('Shop Admin', 'admin@fursuitshop.com'),
]
# Lagerbestand-Einstellungen
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
import os
from pathlib import Path
from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Lade .env Datei
load_dotenv(BASE_DIR / '.env')
# E-Mail-Einstellungen (temporär Console-Backend)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = 'Fursuit Shop <noreply@fursuitshop.com>'
# Admin-E-Mail-Empfänger
ADMINS = [
('Shop Admin', 'admin@fursuitshop.com'),
]
# Lagerbestand-Einstellungen
LOW_STOCK_THRESHOLD = 5 # Schwellenwert für niedrigen Lagerbestand

View File

@ -1,312 +1,155 @@
<<<<<<< HEAD
# E-Mail-System Dokumentation
## Übersicht
Das E-Mail-System des Fursuit Shops versendet automatisch Benachrichtigungen an Kunden und Administratoren bei verschiedenen Shop-Ereignissen. Das System ist mehrsprachig (DE/EN) und verwendet responsive HTML-Templates mit Text-Alternativen.
## Konfiguration
### E-Mail-Einstellungen
Die E-Mail-Konfiguration erfolgt über Umgebungsvariablen in der `.env`-Datei:
```env
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-specific-password
DEFAULT_FROM_EMAIL=Fursuit Shop <noreply@fursuitshop.com>
```
### Admin-Benachrichtigungen
Administratoren werden in `settings.py` konfiguriert:
```python
ADMINS = [
('Shop Admin', 'admin@fursuitshop.com'),
]
```
### Lagerbestand-Schwellenwert
```python
LOW_STOCK_THRESHOLD = 5 # Benachrichtigung bei ≤ 5 Artikeln
```
## E-Mail-Typen
### 1. Kundenbenachrichtigungen
#### Bestellbestätigung
- Gesendet nach erfolgreicher Zahlung
- Enthält Bestelldetails, Produkte und Preise
- Template: `order_confirmation.html/txt`
#### Status-Updates
- Gesendet bei Statusänderungen der Bestellung
- Kann Fortschrittsbilder und Beschreibungen enthalten
- Template: `order_status_update.html/txt`
#### Versandbestätigung
- Gesendet wenn Bestellung versendet wurde
- Enthält Tracking-Nummer und Versanddetails
- Template: `shipping_confirmation.html/txt`
### 2. Admin-Benachrichtigungen
#### Neue Bestellung
- Bei jeder neuen Bestellung
- Spezielle Markierung für Fursuit-Bestellungen
- Template: `admin_notification.html/txt`
#### Zahlungsfehler
- Bei fehlgeschlagenen Zahlungen
- Enthält detaillierte Fehlerinformationen
- Template: `admin_notification.html/txt`
#### Custom Design
- Bei Bestellungen mit Custom Designs
- Enthält Design-Dateien und Notizen
- Template: `admin_notification.html/txt`
#### Niedriger Lagerbestand
- Bei Unterschreitung des Schwellenwerts
- Enthält Produktdetails und aktuellen Bestand
- Template: `low_stock_notification.html/txt`
## Signal-Handler
Das System verwendet Django-Signals für automatische Benachrichtigungen:
### Order-Signals
```python
@receiver(post_save, sender=Order)
def handle_order_notifications(sender, instance, created, **kwargs):
# Sendet Benachrichtigungen bei neuen Bestellungen und Statusänderungen
```
### Payment-Signals
```python
@receiver(post_save, sender=PaymentError)
def handle_payment_error(sender, instance, created, **kwargs):
# Benachrichtigt Admins über Zahlungsfehler
```
### Product-Signals
```python
@receiver(pre_save, sender=Product)
def check_stock_level(sender, instance, **kwargs):
# Überprüft Lagerbestand und sendet Warnungen
```
## E-Mail-Templates
Alle E-Mail-Templates:
- Sind vollständig responsive
- Unterstützen HTML und Text-Alternativen
- Sind mehrsprachig (DE/EN)
- Verwenden einheitliches Branding
### Template-Struktur
```
shop/templates/shop/emails/
├── order_confirmation.html
├── order_confirmation.txt
├── order_status_update.html
├── order_status_update.txt
├── shipping_confirmation.html
├── shipping_confirmation.txt
├── admin_notification.html
├── admin_notification.txt
├── low_stock_notification.html
└── low_stock_notification.txt
```
## Sicherheit
- TLS-Verschlüsselung für E-Mail-Versand
- Keine sensiblen Daten in E-Mails
- Sichere Links zu Admin-Bereich
- App-spezifische Passwörter für SMTP
## Fehlerbehandlung
- Robuste Exception-Handling
- Logging von Versandfehlern
- Vermeidung von Doppel-Benachrichtigungen
- Fallback auf Text-Version bei HTML-Problemen
## Wartung
### Neue E-Mail-Typen hinzufügen
1. Templates erstellen (HTML und Text)
2. E-Mail-Funktion in `emails.py` hinzufügen
3. Signal-Handler in `signals.py` registrieren
4. Übersetzungen in `.po`-Dateien hinzufügen
### Template-Anpassung
- CSS-Styles in Template-Header
- Einheitliche Farbcodes und Abstände
- Bootstrap-kompatible Klassen
=======
# E-Mail-System Dokumentation
## Übersicht
Das E-Mail-System des Fursuit Shops versendet automatisch Benachrichtigungen an Kunden und Administratoren bei verschiedenen Shop-Ereignissen. Das System ist mehrsprachig (DE/EN) und verwendet responsive HTML-Templates mit Text-Alternativen.
## Konfiguration
### E-Mail-Einstellungen
Die E-Mail-Konfiguration erfolgt über Umgebungsvariablen in der `.env`-Datei:
```env
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-specific-password
DEFAULT_FROM_EMAIL=Fursuit Shop <noreply@fursuitshop.com>
```
### Admin-Benachrichtigungen
Administratoren werden in `settings.py` konfiguriert:
```python
ADMINS = [
('Shop Admin', 'admin@fursuitshop.com'),
]
```
### Lagerbestand-Schwellenwert
```python
LOW_STOCK_THRESHOLD = 5 # Benachrichtigung bei ≤ 5 Artikeln
```
## E-Mail-Typen
### 1. Kundenbenachrichtigungen
#### Bestellbestätigung
- Gesendet nach erfolgreicher Zahlung
- Enthält Bestelldetails, Produkte und Preise
- Template: `order_confirmation.html/txt`
#### Status-Updates
- Gesendet bei Statusänderungen der Bestellung
- Kann Fortschrittsbilder und Beschreibungen enthalten
- Template: `order_status_update.html/txt`
#### Versandbestätigung
- Gesendet wenn Bestellung versendet wurde
- Enthält Tracking-Nummer und Versanddetails
- Template: `shipping_confirmation.html/txt`
### 2. Admin-Benachrichtigungen
#### Neue Bestellung
- Bei jeder neuen Bestellung
- Spezielle Markierung für Fursuit-Bestellungen
- Template: `admin_notification.html/txt`
#### Zahlungsfehler
- Bei fehlgeschlagenen Zahlungen
- Enthält detaillierte Fehlerinformationen
- Template: `admin_notification.html/txt`
#### Custom Design
- Bei Bestellungen mit Custom Designs
- Enthält Design-Dateien und Notizen
- Template: `admin_notification.html/txt`
#### Niedriger Lagerbestand
- Bei Unterschreitung des Schwellenwerts
- Enthält Produktdetails und aktuellen Bestand
- Template: `low_stock_notification.html/txt`
## Signal-Handler
Das System verwendet Django-Signals für automatische Benachrichtigungen:
### Order-Signals
```python
@receiver(post_save, sender=Order)
def handle_order_notifications(sender, instance, created, **kwargs):
# Sendet Benachrichtigungen bei neuen Bestellungen und Statusänderungen
```
### Payment-Signals
```python
@receiver(post_save, sender=PaymentError)
def handle_payment_error(sender, instance, created, **kwargs):
# Benachrichtigt Admins über Zahlungsfehler
```
### Product-Signals
```python
@receiver(pre_save, sender=Product)
def check_stock_level(sender, instance, **kwargs):
# Überprüft Lagerbestand und sendet Warnungen
```
## E-Mail-Templates
Alle E-Mail-Templates:
- Sind vollständig responsive
- Unterstützen HTML und Text-Alternativen
- Sind mehrsprachig (DE/EN)
- Verwenden einheitliches Branding
### Template-Struktur
```
shop/templates/shop/emails/
├── order_confirmation.html
├── order_confirmation.txt
├── order_status_update.html
├── order_status_update.txt
├── shipping_confirmation.html
├── shipping_confirmation.txt
├── admin_notification.html
├── admin_notification.txt
├── low_stock_notification.html
└── low_stock_notification.txt
```
## Sicherheit
- TLS-Verschlüsselung für E-Mail-Versand
- Keine sensiblen Daten in E-Mails
- Sichere Links zu Admin-Bereich
- App-spezifische Passwörter für SMTP
## Fehlerbehandlung
- Robuste Exception-Handling
- Logging von Versandfehlern
- Vermeidung von Doppel-Benachrichtigungen
- Fallback auf Text-Version bei HTML-Problemen
## Wartung
### Neue E-Mail-Typen hinzufügen
1. Templates erstellen (HTML und Text)
2. E-Mail-Funktion in `emails.py` hinzufügen
3. Signal-Handler in `signals.py` registrieren
4. Übersetzungen in `.po`-Dateien hinzufügen
### Template-Anpassung
- CSS-Styles in Template-Header
- Einheitliche Farbcodes und Abstände
- Bootstrap-kompatible Klassen
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# E-Mail-System Dokumentation
## Übersicht
Das E-Mail-System des Fursuit Shops versendet automatisch Benachrichtigungen an Kunden und Administratoren bei verschiedenen Shop-Ereignissen. Das System ist mehrsprachig (DE/EN) und verwendet responsive HTML-Templates mit Text-Alternativen.
## Konfiguration
### E-Mail-Einstellungen
Die E-Mail-Konfiguration erfolgt über Umgebungsvariablen in der `.env`-Datei:
```env
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-specific-password
DEFAULT_FROM_EMAIL=Fursuit Shop <noreply@fursuitshop.com>
```
### Admin-Benachrichtigungen
Administratoren werden in `settings.py` konfiguriert:
```python
ADMINS = [
('Shop Admin', 'admin@fursuitshop.com'),
]
```
### Lagerbestand-Schwellenwert
```python
LOW_STOCK_THRESHOLD = 5 # Benachrichtigung bei ≤ 5 Artikeln
```
## E-Mail-Typen
### 1. Kundenbenachrichtigungen
#### Bestellbestätigung
- Gesendet nach erfolgreicher Zahlung
- Enthält Bestelldetails, Produkte und Preise
- Template: `order_confirmation.html/txt`
#### Status-Updates
- Gesendet bei Statusänderungen der Bestellung
- Kann Fortschrittsbilder und Beschreibungen enthalten
- Template: `order_status_update.html/txt`
#### Versandbestätigung
- Gesendet wenn Bestellung versendet wurde
- Enthält Tracking-Nummer und Versanddetails
- Template: `shipping_confirmation.html/txt`
### 2. Admin-Benachrichtigungen
#### Neue Bestellung
- Bei jeder neuen Bestellung
- Spezielle Markierung für Fursuit-Bestellungen
- Template: `admin_notification.html/txt`
#### Zahlungsfehler
- Bei fehlgeschlagenen Zahlungen
- Enthält detaillierte Fehlerinformationen
- Template: `admin_notification.html/txt`
#### Custom Design
- Bei Bestellungen mit Custom Designs
- Enthält Design-Dateien und Notizen
- Template: `admin_notification.html/txt`
#### Niedriger Lagerbestand
- Bei Unterschreitung des Schwellenwerts
- Enthält Produktdetails und aktuellen Bestand
- Template: `low_stock_notification.html/txt`
## Signal-Handler
Das System verwendet Django-Signals für automatische Benachrichtigungen:
### Order-Signals
```python
@receiver(post_save, sender=Order)
def handle_order_notifications(sender, instance, created, **kwargs):
# Sendet Benachrichtigungen bei neuen Bestellungen und Statusänderungen
```
### Payment-Signals
```python
@receiver(post_save, sender=PaymentError)
def handle_payment_error(sender, instance, created, **kwargs):
# Benachrichtigt Admins über Zahlungsfehler
```
### Product-Signals
```python
@receiver(pre_save, sender=Product)
def check_stock_level(sender, instance, **kwargs):
# Überprüft Lagerbestand und sendet Warnungen
```
## E-Mail-Templates
Alle E-Mail-Templates:
- Sind vollständig responsive
- Unterstützen HTML und Text-Alternativen
- Sind mehrsprachig (DE/EN)
- Verwenden einheitliches Branding
### Template-Struktur
```
shop/templates/shop/emails/
├── order_confirmation.html
├── order_confirmation.txt
├── order_status_update.html
├── order_status_update.txt
├── shipping_confirmation.html
├── shipping_confirmation.txt
├── admin_notification.html
├── admin_notification.txt
├── low_stock_notification.html
└── low_stock_notification.txt
```
## Sicherheit
- TLS-Verschlüsselung für E-Mail-Versand
- Keine sensiblen Daten in E-Mails
- Sichere Links zu Admin-Bereich
- App-spezifische Passwörter für SMTP
## Fehlerbehandlung
- Robuste Exception-Handling
- Logging von Versandfehlern
- Vermeidung von Doppel-Benachrichtigungen
- Fallback auf Text-Version bei HTML-Problemen
## Wartung
### Neue E-Mail-Typen hinzufügen
1. Templates erstellen (HTML und Text)
2. E-Mail-Funktion in `emails.py` hinzufügen
3. Signal-Handler in `signals.py` registrieren
4. Übersetzungen in `.po`-Dateien hinzufügen
### Template-Anpassung
- CSS-Styles in Template-Header
- Einheitliche Farbcodes und Abstände
- Bootstrap-kompatible Klassen
- Responsive Breakpoints

View File

@ -1,64 +1,31 @@
<<<<<<< HEAD
import sqlite3
def add_test_data():
conn = sqlite3.connect('shop.db')
cursor = conn.cursor()
# Lösche vorhandene Daten
cursor.execute("DELETE FROM products")
# Testprodukte
products = [
("Gaming Maus", "Hochwertige Gaming-Maus mit RGB-Beleuchtung", 49.99, 10),
("Mechanische Tastatur", "Mechanische Gaming-Tastatur mit blauen Switches", 89.99, 5),
("Gaming Headset", "7.1 Surround Sound Gaming Headset", 79.99, 8),
("Mousepad XL", "Extra großes Gaming-Mousepad", 19.99, 15),
("Webcam HD", "1080p Webcam für Streaming", 59.99, 3)
]
# Füge Produkte ein
cursor.executemany(
"INSERT INTO products (name, description, price, stock) VALUES (?, ?, ?, ?)",
products
)
# Speichere Änderungen
conn.commit()
conn.close()
print("Testdaten wurden erfolgreich hinzugefügt!")
if __name__ == "__main__":
=======
import sqlite3
def add_test_data():
conn = sqlite3.connect('shop.db')
cursor = conn.cursor()
# Lösche vorhandene Daten
cursor.execute("DELETE FROM products")
# Testprodukte
products = [
("Gaming Maus", "Hochwertige Gaming-Maus mit RGB-Beleuchtung", 49.99, 10),
("Mechanische Tastatur", "Mechanische Gaming-Tastatur mit blauen Switches", 89.99, 5),
("Gaming Headset", "7.1 Surround Sound Gaming Headset", 79.99, 8),
("Mousepad XL", "Extra großes Gaming-Mousepad", 19.99, 15),
("Webcam HD", "1080p Webcam für Streaming", 59.99, 3)
]
# Füge Produkte ein
cursor.executemany(
"INSERT INTO products (name, description, price, stock) VALUES (?, ?, ?, ?)",
products
)
# Speichere Änderungen
conn.commit()
conn.close()
print("Testdaten wurden erfolgreich hinzugefügt!")
if __name__ == "__main__":
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
import sqlite3
def add_test_data():
conn = sqlite3.connect('shop.db')
cursor = conn.cursor()
# Lösche vorhandene Daten
cursor.execute("DELETE FROM products")
# Testprodukte
products = [
("Gaming Maus", "Hochwertige Gaming-Maus mit RGB-Beleuchtung", 49.99, 10),
("Mechanische Tastatur", "Mechanische Gaming-Tastatur mit blauen Switches", 89.99, 5),
("Gaming Headset", "7.1 Surround Sound Gaming Headset", 79.99, 8),
("Mousepad XL", "Extra großes Gaming-Mousepad", 19.99, 15),
("Webcam HD", "1080p Webcam für Streaming", 59.99, 3)
]
# Füge Produkte ein
cursor.executemany(
"INSERT INTO products (name, description, price, stock) VALUES (?, ?, ?, ?)",
products
)
# Speichere Änderungen
conn.commit()
conn.close()
print("Testdaten wurden erfolgreich hinzugefügt!")
if __name__ == "__main__":
add_test_data()

View File

@ -1,88 +1,43 @@
<<<<<<< HEAD
import sqlite3
from contextlib import contextmanager
@contextmanager
def get_db():
conn = sqlite3.connect('shop.db')
try:
yield conn
finally:
conn.close()
def init_db():
with get_db() as conn:
# Tabelle erstellen
conn.execute("""
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
stock INTEGER NOT NULL
)
""")
# Beispieldaten einfügen
products = [
("Laptop", "Leistungsstarker Laptop für Arbeit und Gaming", 999.99, 5),
("Smartphone", "Neuestes Modell mit Top-Kamera", 699.99, 10),
("Kopfhörer", "Kabellose Kopfhörer mit Noise-Cancelling", 199.99, 15),
("Tablet", "Perfekt für Unterhaltung und Produktivität", 449.99, 8),
("Smartwatch", "Fitness-Tracking und Benachrichtigungen", 299.99, 12)
]
conn.execute("DELETE FROM products") # Alte Daten löschen
conn.executemany(
"INSERT INTO products (name, description, price, stock) VALUES (?, ?, ?, ?)",
products
)
conn.commit()
if __name__ == "__main__":
init_db()
=======
import sqlite3
from contextlib import contextmanager
@contextmanager
def get_db():
conn = sqlite3.connect('shop.db')
try:
yield conn
finally:
conn.close()
def init_db():
with get_db() as conn:
# Tabelle erstellen
conn.execute("""
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
stock INTEGER NOT NULL
)
""")
# Beispieldaten einfügen
products = [
("Laptop", "Leistungsstarker Laptop für Arbeit und Gaming", 999.99, 5),
("Smartphone", "Neuestes Modell mit Top-Kamera", 699.99, 10),
("Kopfhörer", "Kabellose Kopfhörer mit Noise-Cancelling", 199.99, 15),
("Tablet", "Perfekt für Unterhaltung und Produktivität", 449.99, 8),
("Smartwatch", "Fitness-Tracking und Benachrichtigungen", 299.99, 12)
]
conn.execute("DELETE FROM products") # Alte Daten löschen
conn.executemany(
"INSERT INTO products (name, description, price, stock) VALUES (?, ?, ?, ?)",
products
)
conn.commit()
if __name__ == "__main__":
init_db()
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
import sqlite3
from contextlib import contextmanager
@contextmanager
def get_db():
conn = sqlite3.connect('shop.db')
try:
yield conn
finally:
conn.close()
def init_db():
with get_db() as conn:
# Tabelle erstellen
conn.execute("""
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
stock INTEGER NOT NULL
)
""")
# Beispieldaten einfügen
products = [
("Laptop", "Leistungsstarker Laptop für Arbeit und Gaming", 999.99, 5),
("Smartphone", "Neuestes Modell mit Top-Kamera", 699.99, 10),
("Kopfhörer", "Kabellose Kopfhörer mit Noise-Cancelling", 199.99, 15),
("Tablet", "Perfekt für Unterhaltung und Produktivität", 449.99, 8),
("Smartwatch", "Fitness-Tracking und Benachrichtigungen", 299.99, 12)
]
conn.execute("DELETE FROM products") # Alte Daten löschen
conn.executemany(
"INSERT INTO products (name, description, price, stock) VALUES (?, ?, ?, ?)",
products
)
conn.commit()
if __name__ == "__main__":
init_db()
print("Datenbank wurde erfolgreich initialisiert!")

View File

@ -1,47 +1,22 @@
<<<<<<< HEAD
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webshop.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
=======
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webshop.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webshop.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@ -1,7 +1,2 @@
<<<<<<< HEAD
# Diese Datei ist jetzt leer, da alle Admin-Konfigurationen in webshop/admin.py zentral verwaltet werden
# Die Admin-Konfigurationen wurden in die neue zentrale Admin-Site verschoben
=======
# Diese Datei ist jetzt leer, da alle Admin-Konfigurationen in webshop/admin.py zentral verwaltet werden
# Die Admin-Konfigurationen wurden in die neue zentrale Admin-Site verschoben
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Diese Datei ist jetzt leer, da alle Admin-Konfigurationen in webshop/admin.py zentral verwaltet werden
# Die Admin-Konfigurationen wurden in die neue zentrale Admin-Site verschoben

View File

@ -1,15 +1,6 @@
<<<<<<< HEAD
from django.apps import AppConfig
class ProductsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'products'
=======
from django.apps import AppConfig
class ProductsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'products'
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.apps import AppConfig
class ProductsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'products'

View File

@ -1,440 +1,219 @@
<<<<<<< HEAD
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm
from django.contrib.auth.models import User
from .models import Order, Product, Review, UserProfile, ContactMessage, CustomOrder, OrderProgress
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['full_name', 'email', 'address', 'phone']
widgets = {
'full_name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'address': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'phone': forms.TextInput(attrs={'class': 'form-control'}),
}
labels = {
'full_name': 'Vollständiger Name',
'email': 'E-Mail-Adresse',
'address': 'Lieferadresse',
'phone': 'Telefonnummer',
}
class CustomUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={'class': 'form-control'}))
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
self.fields['username'].label = 'Benutzername'
self.fields['password1'].label = 'Passwort'
self.fields['password2'].label = 'Passwort bestätigen'
class CustomAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
self.fields['username'].label = 'Benutzername'
self.fields['password'].label = 'Passwort'
class ReviewForm(forms.ModelForm):
class Meta:
model = Review
fields = ['rating', 'comment']
widgets = {
'rating': forms.Select(attrs={'class': 'form-control'}),
'comment': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
labels = {
'rating': 'Bewertung',
'comment': 'Kommentar',
}
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(max_length=30, required=False, label='Vorname')
last_name = forms.CharField(max_length=30, required=False, label='Nachname')
email = forms.EmailField(required=True, label='E-Mail-Adresse')
class Meta:
model = UserProfile
fields = ['phone', 'address', 'default_shipping_address', 'newsletter']
labels = {
'phone': 'Telefonnummer',
'address': 'Adresse',
'default_shipping_address': 'Standard-Lieferadresse',
'newsletter': 'Newsletter abonnieren',
}
widgets = {
'address': forms.Textarea(attrs={'rows': 3}),
'default_shipping_address': forms.Textarea(attrs={'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.user:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
self.fields['email'].initial = self.instance.user.email
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
def save(self, commit=True):
profile = super().save(commit=False)
if commit:
user = profile.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
user.save()
profile.save()
return profile
class ContactForm(forms.ModelForm):
class Meta:
model = ContactMessage
fields = ['name', 'email', 'category', 'order_number', 'subject', 'message']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'category': forms.Select(attrs={'class': 'form-control'}),
'order_number': forms.TextInput(attrs={'class': 'form-control'}),
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'message': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
}
labels = {
'name': 'Name',
'email': 'E-Mail-Adresse',
'category': 'Kategorie',
'order_number': 'Bestellnummer (optional)',
'subject': 'Betreff',
'message': 'Ihre Nachricht',
}
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user and user.is_authenticated:
self.fields['name'].initial = user.get_full_name() or user.username
self.fields['email'].initial = user.email
class CustomOrderForm(forms.ModelForm):
class Meta:
model = CustomOrder
fields = [
'fursuit_type', 'style', 'character_name',
'character_description', 'reference_images',
'special_requests', 'measurements',
'color_preferences', 'budget_range',
'deadline_request'
]
widgets = {
'fursuit_type': forms.Select(attrs={'class': 'form-select'}),
'style': forms.Select(attrs={'class': 'form-select'}),
'character_name': forms.TextInput(attrs={'class': 'form-control'}),
'character_description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Beschreiben Sie Ihren Character so detailliert wie möglich...'
}),
'special_requests': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Besondere Wünsche oder Anforderungen...'
}),
'measurements': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Bitte geben Sie alle relevanten Maße an (Kopfumfang, Körpergröße, etc.)'
}),
'color_preferences': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Beschreiben Sie die gewünschten Farben und Farbkombinationen...'
}),
'budget_range': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'z.B. 2000-3000€'
}),
'deadline_request': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'reference_images': forms.FileInput(attrs={
'class': 'form-control',
'accept': 'image/*'
})
}
labels = {
'fursuit_type': 'Art des Fursuits',
'style': 'Gewünschter Stil',
'character_name': 'Name des Characters',
'character_description': 'Beschreibung des Characters',
'reference_images': 'Referenzbilder',
'special_requests': 'Besondere Wünsche',
'measurements': 'Maße',
'color_preferences': 'Farbwünsche',
'budget_range': 'Budget-Rahmen',
'deadline_request': 'Gewünschter Fertigstellungstermin'
}
help_texts = {
'character_description': 'Je detaillierter die Beschreibung, desto besser können wir Ihre Vision umsetzen.',
'reference_images': 'Laden Sie bis zu 5 Referenzbilder hoch (max. 5MB pro Bild)',
'measurements': 'Genaue Maße sind wichtig für eine perfekte Passform.',
'budget_range': 'Geben Sie einen Bereich an, in dem Sie sich preislich bewegen möchten.',
'deadline_request': 'Optional: Wenn Sie einen bestimmten Termin im Auge haben.'
}
class OrderProgressForm(forms.ModelForm):
class Meta:
model = OrderProgress
fields = ['stage', 'description', 'image', 'completed']
widgets = {
'stage': forms.Select(attrs={'class': 'form-select'}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Beschreiben Sie den aktuellen Fortschritt...'
}),
'image': forms.FileInput(attrs={
'class': 'form-control',
'accept': 'image/*'
}),
'completed': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
labels = {
'stage': 'Arbeitsschritt',
'description': 'Beschreibung des Fortschritts',
'image': 'Foto des Fortschritts',
'completed': 'Abgeschlossen'
}
help_texts = {
'description': 'Beschreiben Sie detailliert, was in diesem Schritt gemacht wurde.',
'image': 'Fügen Sie ein Foto hinzu, um den Fortschritt zu dokumentieren.',
'completed': 'Markieren Sie diesen Schritt als abgeschlossen, wenn er fertig ist.'
=======
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm
from django.contrib.auth.models import User
from .models import Order, Product, Review, UserProfile, ContactMessage, CustomOrder, OrderProgress
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['full_name', 'email', 'address', 'phone']
widgets = {
'full_name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'address': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'phone': forms.TextInput(attrs={'class': 'form-control'}),
}
labels = {
'full_name': 'Vollständiger Name',
'email': 'E-Mail-Adresse',
'address': 'Lieferadresse',
'phone': 'Telefonnummer',
}
class CustomUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={'class': 'form-control'}))
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
self.fields['username'].label = 'Benutzername'
self.fields['password1'].label = 'Passwort'
self.fields['password2'].label = 'Passwort bestätigen'
class CustomAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
self.fields['username'].label = 'Benutzername'
self.fields['password'].label = 'Passwort'
class ReviewForm(forms.ModelForm):
class Meta:
model = Review
fields = ['rating', 'comment']
widgets = {
'rating': forms.Select(attrs={'class': 'form-control'}),
'comment': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
labels = {
'rating': 'Bewertung',
'comment': 'Kommentar',
}
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(max_length=30, required=False, label='Vorname')
last_name = forms.CharField(max_length=30, required=False, label='Nachname')
email = forms.EmailField(required=True, label='E-Mail-Adresse')
class Meta:
model = UserProfile
fields = ['phone', 'address', 'default_shipping_address', 'newsletter']
labels = {
'phone': 'Telefonnummer',
'address': 'Adresse',
'default_shipping_address': 'Standard-Lieferadresse',
'newsletter': 'Newsletter abonnieren',
}
widgets = {
'address': forms.Textarea(attrs={'rows': 3}),
'default_shipping_address': forms.Textarea(attrs={'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.user:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
self.fields['email'].initial = self.instance.user.email
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
def save(self, commit=True):
profile = super().save(commit=False)
if commit:
user = profile.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
user.save()
profile.save()
return profile
class ContactForm(forms.ModelForm):
class Meta:
model = ContactMessage
fields = ['name', 'email', 'category', 'order_number', 'subject', 'message']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'category': forms.Select(attrs={'class': 'form-control'}),
'order_number': forms.TextInput(attrs={'class': 'form-control'}),
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'message': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
}
labels = {
'name': 'Name',
'email': 'E-Mail-Adresse',
'category': 'Kategorie',
'order_number': 'Bestellnummer (optional)',
'subject': 'Betreff',
'message': 'Ihre Nachricht',
}
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user and user.is_authenticated:
self.fields['name'].initial = user.get_full_name() or user.username
self.fields['email'].initial = user.email
class CustomOrderForm(forms.ModelForm):
class Meta:
model = CustomOrder
fields = [
'fursuit_type', 'style', 'character_name',
'character_description', 'reference_images',
'special_requests', 'measurements',
'color_preferences', 'budget_range',
'deadline_request'
]
widgets = {
'fursuit_type': forms.Select(attrs={'class': 'form-select'}),
'style': forms.Select(attrs={'class': 'form-select'}),
'character_name': forms.TextInput(attrs={'class': 'form-control'}),
'character_description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Beschreiben Sie Ihren Character so detailliert wie möglich...'
}),
'special_requests': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Besondere Wünsche oder Anforderungen...'
}),
'measurements': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Bitte geben Sie alle relevanten Maße an (Kopfumfang, Körpergröße, etc.)'
}),
'color_preferences': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Beschreiben Sie die gewünschten Farben und Farbkombinationen...'
}),
'budget_range': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'z.B. 2000-3000€'
}),
'deadline_request': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'reference_images': forms.FileInput(attrs={
'class': 'form-control',
'accept': 'image/*'
})
}
labels = {
'fursuit_type': 'Art des Fursuits',
'style': 'Gewünschter Stil',
'character_name': 'Name des Characters',
'character_description': 'Beschreibung des Characters',
'reference_images': 'Referenzbilder',
'special_requests': 'Besondere Wünsche',
'measurements': 'Maße',
'color_preferences': 'Farbwünsche',
'budget_range': 'Budget-Rahmen',
'deadline_request': 'Gewünschter Fertigstellungstermin'
}
help_texts = {
'character_description': 'Je detaillierter die Beschreibung, desto besser können wir Ihre Vision umsetzen.',
'reference_images': 'Laden Sie bis zu 5 Referenzbilder hoch (max. 5MB pro Bild)',
'measurements': 'Genaue Maße sind wichtig für eine perfekte Passform.',
'budget_range': 'Geben Sie einen Bereich an, in dem Sie sich preislich bewegen möchten.',
'deadline_request': 'Optional: Wenn Sie einen bestimmten Termin im Auge haben.'
}
class OrderProgressForm(forms.ModelForm):
class Meta:
model = OrderProgress
fields = ['stage', 'description', 'image', 'completed']
widgets = {
'stage': forms.Select(attrs={'class': 'form-select'}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Beschreiben Sie den aktuellen Fortschritt...'
}),
'image': forms.FileInput(attrs={
'class': 'form-control',
'accept': 'image/*'
}),
'completed': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
labels = {
'stage': 'Arbeitsschritt',
'description': 'Beschreibung des Fortschritts',
'image': 'Foto des Fortschritts',
'completed': 'Abgeschlossen'
}
help_texts = {
'description': 'Beschreiben Sie detailliert, was in diesem Schritt gemacht wurde.',
'image': 'Fügen Sie ein Foto hinzu, um den Fortschritt zu dokumentieren.',
'completed': 'Markieren Sie diesen Schritt als abgeschlossen, wenn er fertig ist.'
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm
from django.contrib.auth.models import User
from .models import Order, Product, Review, UserProfile, ContactMessage, CustomOrder, OrderProgress
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['full_name', 'email', 'address', 'phone']
widgets = {
'full_name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'address': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'phone': forms.TextInput(attrs={'class': 'form-control'}),
}
labels = {
'full_name': 'Vollständiger Name',
'email': 'E-Mail-Adresse',
'address': 'Lieferadresse',
'phone': 'Telefonnummer',
}
class CustomUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={'class': 'form-control'}))
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
self.fields['username'].label = 'Benutzername'
self.fields['password1'].label = 'Passwort'
self.fields['password2'].label = 'Passwort bestätigen'
class CustomAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
self.fields['username'].label = 'Benutzername'
self.fields['password'].label = 'Passwort'
class ReviewForm(forms.ModelForm):
class Meta:
model = Review
fields = ['rating', 'comment']
widgets = {
'rating': forms.Select(attrs={'class': 'form-control'}),
'comment': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
labels = {
'rating': 'Bewertung',
'comment': 'Kommentar',
}
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(max_length=30, required=False, label='Vorname')
last_name = forms.CharField(max_length=30, required=False, label='Nachname')
email = forms.EmailField(required=True, label='E-Mail-Adresse')
class Meta:
model = UserProfile
fields = ['phone', 'address', 'default_shipping_address', 'newsletter']
labels = {
'phone': 'Telefonnummer',
'address': 'Adresse',
'default_shipping_address': 'Standard-Lieferadresse',
'newsletter': 'Newsletter abonnieren',
}
widgets = {
'address': forms.Textarea(attrs={'rows': 3}),
'default_shipping_address': forms.Textarea(attrs={'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.user:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
self.fields['email'].initial = self.instance.user.email
for field in self.fields:
self.fields[field].widget.attrs['class'] = 'form-control'
def save(self, commit=True):
profile = super().save(commit=False)
if commit:
user = profile.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
user.save()
profile.save()
return profile
class ContactForm(forms.ModelForm):
class Meta:
model = ContactMessage
fields = ['name', 'email', 'category', 'order_number', 'subject', 'message']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'category': forms.Select(attrs={'class': 'form-control'}),
'order_number': forms.TextInput(attrs={'class': 'form-control'}),
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'message': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
}
labels = {
'name': 'Name',
'email': 'E-Mail-Adresse',
'category': 'Kategorie',
'order_number': 'Bestellnummer (optional)',
'subject': 'Betreff',
'message': 'Ihre Nachricht',
}
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user and user.is_authenticated:
self.fields['name'].initial = user.get_full_name() or user.username
self.fields['email'].initial = user.email
class CustomOrderForm(forms.ModelForm):
class Meta:
model = CustomOrder
fields = [
'fursuit_type', 'style', 'character_name',
'character_description', 'reference_images',
'special_requests', 'measurements',
'color_preferences', 'budget_range',
'deadline_request'
]
widgets = {
'fursuit_type': forms.Select(attrs={'class': 'form-select'}),
'style': forms.Select(attrs={'class': 'form-select'}),
'character_name': forms.TextInput(attrs={'class': 'form-control'}),
'character_description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Beschreiben Sie Ihren Character so detailliert wie möglich...'
}),
'special_requests': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Besondere Wünsche oder Anforderungen...'
}),
'measurements': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Bitte geben Sie alle relevanten Maße an (Kopfumfang, Körpergröße, etc.)'
}),
'color_preferences': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Beschreiben Sie die gewünschten Farben und Farbkombinationen...'
}),
'budget_range': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'z.B. 2000-3000€'
}),
'deadline_request': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'reference_images': forms.FileInput(attrs={
'class': 'form-control',
'accept': 'image/*'
})
}
labels = {
'fursuit_type': 'Art des Fursuits',
'style': 'Gewünschter Stil',
'character_name': 'Name des Characters',
'character_description': 'Beschreibung des Characters',
'reference_images': 'Referenzbilder',
'special_requests': 'Besondere Wünsche',
'measurements': 'Maße',
'color_preferences': 'Farbwünsche',
'budget_range': 'Budget-Rahmen',
'deadline_request': 'Gewünschter Fertigstellungstermin'
}
help_texts = {
'character_description': 'Je detaillierter die Beschreibung, desto besser können wir Ihre Vision umsetzen.',
'reference_images': 'Laden Sie bis zu 5 Referenzbilder hoch (max. 5MB pro Bild)',
'measurements': 'Genaue Maße sind wichtig für eine perfekte Passform.',
'budget_range': 'Geben Sie einen Bereich an, in dem Sie sich preislich bewegen möchten.',
'deadline_request': 'Optional: Wenn Sie einen bestimmten Termin im Auge haben.'
}
class OrderProgressForm(forms.ModelForm):
class Meta:
model = OrderProgress
fields = ['stage', 'description', 'image', 'completed']
widgets = {
'stage': forms.Select(attrs={'class': 'form-select'}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Beschreiben Sie den aktuellen Fortschritt...'
}),
'image': forms.FileInput(attrs={
'class': 'form-control',
'accept': 'image/*'
}),
'completed': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
labels = {
'stage': 'Arbeitsschritt',
'description': 'Beschreibung des Fortschritts',
'image': 'Foto des Fortschritts',
'completed': 'Abgeschlossen'
}
help_texts = {
'description': 'Beschreiben Sie detailliert, was in diesem Schritt gemacht wurde.',
'image': 'Fügen Sie ein Foto hinzu, um den Fortschritt zu dokumentieren.',
'completed': 'Markieren Sie diesen Schritt als abgeschlossen, wenn er fertig ist.'
}

View File

@ -1,61 +1,29 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 14:00
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField()),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('stock', models.IntegerField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'ordering': ['-created_at'],
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 14:00
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField()),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('stock', models.IntegerField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'ordering': ['-created_at'],
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 14:00
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('description', models.TextField()),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('stock', models.IntegerField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'ordering': ['-created_at'],
},
),
]

View File

@ -1,81 +1,39 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 14:12
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Cart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('session_id', models.CharField(blank=True, max_length=100, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='CartItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('created_at', models.DateTimeField(auto_now_add=True)),
('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='products.cart')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.product')),
],
options={
'unique_together': {('cart', 'product')},
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 14:12
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Cart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('session_id', models.CharField(blank=True, max_length=100, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='CartItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('created_at', models.DateTimeField(auto_now_add=True)),
('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='products.cart')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.product')),
],
options={
'unique_together': {('cart', 'product')},
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 14:12
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Cart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('session_id', models.CharField(blank=True, max_length=100, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='CartItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('created_at', models.DateTimeField(auto_now_add=True)),
('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='products.cart')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.product')),
],
options={
'unique_together': {('cart', 'product')},
},
),
]

View File

@ -1,123 +1,60 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 14:19
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0002_cart_cartitem'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='product',
name='category',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='product',
name='featured',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='products/'),
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('full_name', models.CharField(max_length=200)),
('email', models.EmailField(max_length=254)),
('address', models.TextField()),
('phone', models.CharField(max_length=20)),
('total_amount', models.DecimalField(decimal_places=2, max_digits=10)),
('status', models.CharField(choices=[('pending', 'Ausstehend'), ('processing', 'In Bearbeitung'), ('shipped', 'Versendet'), ('delivered', 'Geliefert'), ('cancelled', 'Storniert')], default='pending', max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='OrderItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_name', models.CharField(max_length=200)),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('quantity', models.PositiveIntegerField()),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='products.order')),
('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='products.product')),
],
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 14:19
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0002_cart_cartitem'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='product',
name='category',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='product',
name='featured',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='products/'),
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('full_name', models.CharField(max_length=200)),
('email', models.EmailField(max_length=254)),
('address', models.TextField()),
('phone', models.CharField(max_length=20)),
('total_amount', models.DecimalField(decimal_places=2, max_digits=10)),
('status', models.CharField(choices=[('pending', 'Ausstehend'), ('processing', 'In Bearbeitung'), ('shipped', 'Versendet'), ('delivered', 'Geliefert'), ('cancelled', 'Storniert')], default='pending', max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='OrderItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_name', models.CharField(max_length=200)),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('quantity', models.PositiveIntegerField()),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='products.order')),
('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='products.product')),
],
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 14:19
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0002_cart_cartitem'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='product',
name='category',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='product',
name='featured',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='products/'),
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('full_name', models.CharField(max_length=200)),
('email', models.EmailField(max_length=254)),
('address', models.TextField()),
('phone', models.CharField(max_length=20)),
('total_amount', models.DecimalField(decimal_places=2, max_digits=10)),
('status', models.CharField(choices=[('pending', 'Ausstehend'), ('processing', 'In Bearbeitung'), ('shipped', 'Versendet'), ('delivered', 'Geliefert'), ('cancelled', 'Storniert')], default='pending', max_length=20)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='OrderItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_name', models.CharField(max_length=200)),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('quantity', models.PositiveIntegerField()),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='products.order')),
('product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='products.product')),
],
),
]

View File

@ -1,69 +1,33 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 14:25
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0003_product_category_product_featured_product_image_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Review',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rating', models.IntegerField(choices=[(1, '1 - Sehr schlecht'), (2, '2 - Schlecht'), (3, '3 - Okay'), (4, '4 - Gut'), (5, '5 - Sehr gut')], validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)])),
('comment', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='products.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at'],
'unique_together': {('product', 'user')},
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 14:25
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0003_product_category_product_featured_product_image_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Review',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rating', models.IntegerField(choices=[(1, '1 - Sehr schlecht'), (2, '2 - Schlecht'), (3, '3 - Okay'), (4, '4 - Gut'), (5, '5 - Sehr gut')], validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)])),
('comment', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='products.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at'],
'unique_together': {('product', 'user')},
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 14:25
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0003_product_category_product_featured_product_image_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Review',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rating', models.IntegerField(choices=[(1, '1 - Sehr schlecht'), (2, '2 - Schlecht'), (3, '3 - Okay'), (4, '4 - Gut'), (5, '5 - Sehr gut')], validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)])),
('comment', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='products.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at'],
'unique_together': {('product', 'user')},
},
),
]

View File

@ -1,79 +1,38 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 14:50
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0004_review'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('phone', models.CharField(blank=True, max_length=20)),
('address', models.TextField(blank=True)),
('default_shipping_address', models.TextField(blank=True)),
('newsletter', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Wishlist',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('products', models.ManyToManyField(to='products.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 14:50
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0004_review'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('phone', models.CharField(blank=True, max_length=20)),
('address', models.TextField(blank=True)),
('default_shipping_address', models.TextField(blank=True)),
('newsletter', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Wishlist',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('products', models.ManyToManyField(to='products.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 14:50
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0004_review'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('phone', models.CharField(blank=True, max_length=20)),
('address', models.TextField(blank=True)),
('default_shipping_address', models.TextField(blank=True)),
('newsletter', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Wishlist',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('products', models.ManyToManyField(to='products.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -1,111 +1,54 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 14:57
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0005_userprofile_wishlist'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='FAQ',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question', models.CharField(max_length=255, verbose_name='Frage')),
('answer', models.TextField(verbose_name='Antwort')),
('category', models.CharField(max_length=100, verbose_name='Kategorie')),
('order', models.IntegerField(default=0, verbose_name='Reihenfolge')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'FAQ',
'verbose_name_plural': 'FAQs',
'ordering': ['category', 'order'],
},
),
migrations.CreateModel(
name='ContactMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('email', models.EmailField(max_length=254)),
('order_number', models.CharField(blank=True, max_length=50, null=True)),
('category', models.CharField(choices=[('general', 'Allgemeine Anfrage'), ('order', 'Bestellung'), ('return', 'Rückgabe/Umtausch'), ('complaint', 'Beschwerde'), ('technical', 'Technische Frage')], max_length=20)),
('subject', models.CharField(max_length=200)),
('message', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('status', models.CharField(choices=[('new', 'Neu'), ('in_progress', 'In Bearbeitung'), ('resolved', 'Erledigt'), ('closed', 'Geschlossen')], default='new', max_length=20)),
('staff_notes', models.TextField(blank=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Kontaktanfrage',
'verbose_name_plural': 'Kontaktanfragen',
'ordering': ['-created_at'],
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 14:57
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0005_userprofile_wishlist'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='FAQ',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question', models.CharField(max_length=255, verbose_name='Frage')),
('answer', models.TextField(verbose_name='Antwort')),
('category', models.CharField(max_length=100, verbose_name='Kategorie')),
('order', models.IntegerField(default=0, verbose_name='Reihenfolge')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'FAQ',
'verbose_name_plural': 'FAQs',
'ordering': ['category', 'order'],
},
),
migrations.CreateModel(
name='ContactMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('email', models.EmailField(max_length=254)),
('order_number', models.CharField(blank=True, max_length=50, null=True)),
('category', models.CharField(choices=[('general', 'Allgemeine Anfrage'), ('order', 'Bestellung'), ('return', 'Rückgabe/Umtausch'), ('complaint', 'Beschwerde'), ('technical', 'Technische Frage')], max_length=20)),
('subject', models.CharField(max_length=200)),
('message', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('status', models.CharField(choices=[('new', 'Neu'), ('in_progress', 'In Bearbeitung'), ('resolved', 'Erledigt'), ('closed', 'Geschlossen')], default='new', max_length=20)),
('staff_notes', models.TextField(blank=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Kontaktanfrage',
'verbose_name_plural': 'Kontaktanfragen',
'ordering': ['-created_at'],
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 14:57
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0005_userprofile_wishlist'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='FAQ',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question', models.CharField(max_length=255, verbose_name='Frage')),
('answer', models.TextField(verbose_name='Antwort')),
('category', models.CharField(max_length=100, verbose_name='Kategorie')),
('order', models.IntegerField(default=0, verbose_name='Reihenfolge')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'FAQ',
'verbose_name_plural': 'FAQs',
'ordering': ['category', 'order'],
},
),
migrations.CreateModel(
name='ContactMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('email', models.EmailField(max_length=254)),
('order_number', models.CharField(blank=True, max_length=50, null=True)),
('category', models.CharField(choices=[('general', 'Allgemeine Anfrage'), ('order', 'Bestellung'), ('return', 'Rückgabe/Umtausch'), ('complaint', 'Beschwerde'), ('technical', 'Technische Frage')], max_length=20)),
('subject', models.CharField(max_length=200)),
('message', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('status', models.CharField(choices=[('new', 'Neu'), ('in_progress', 'In Bearbeitung'), ('resolved', 'Erledigt'), ('closed', 'Geschlossen')], default='new', max_length=20)),
('staff_notes', models.TextField(blank=True)),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Kontaktanfrage',
'verbose_name_plural': 'Kontaktanfragen',
'ordering': ['-created_at'],
},
),
]

View File

@ -1,177 +1,87 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 15:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0006_faq_contactmessage'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='product',
name='extras_description',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='product',
name='fursuit_type',
field=models.CharField(choices=[('fullsuit', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Kopf'), ('paws', 'Pfoten'), ('tail', 'Schwanz'), ('other', 'Sonstiges')], default='head', max_length=20),
),
migrations.AddField(
model_name='product',
name='includes_extras',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='is_custom_order',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='product',
name='production_time_weeks',
field=models.IntegerField(default=8),
),
migrations.AddField(
model_name='product',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistisch'), ('realistic', 'Realistisch')], default='toony', max_length=20),
),
migrations.AlterField(
model_name='product',
name='stock',
field=models.IntegerField(default=1),
),
migrations.CreateModel(
name='CustomOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fursuit_type', models.CharField(choices=[('fullsuit', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Kopf'), ('paws', 'Pfoten'), ('tail', 'Schwanz'), ('other', 'Sonstiges')], max_length=20)),
('style', models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistisch'), ('realistic', 'Realistisch')], max_length=20)),
('character_name', models.CharField(max_length=100)),
('character_description', models.TextField()),
('reference_images', models.FileField(blank=True, null=True, upload_to='references/')),
('special_requests', models.TextField(blank=True)),
('measurements', models.TextField()),
('color_preferences', models.TextField()),
('budget_range', models.CharField(max_length=100)),
('deadline_request', models.DateField(blank=True, null=True)),
('status', models.CharField(choices=[('pending', 'Anfrage eingegangen'), ('quoted', 'Angebot erstellt'), ('approved', 'Angebot akzeptiert'), ('in_progress', 'In Arbeit'), ('ready', 'Fertig zur Abholung'), ('shipped', 'Versendet'), ('completed', 'Abgeschlossen'), ('cancelled', 'Storniert')], default='pending', max_length=20)),
('quoted_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='OrderProgress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stage', models.CharField(choices=[('design', 'Design & Planung'), ('base', 'Grundform'), ('fur', 'Fell'), ('details', 'Details'), ('electronics', 'Elektronik (optional)'), ('finishing', 'Finishing')], max_length=20)),
('description', models.TextField()),
('image', models.ImageField(blank=True, null=True, upload_to='progress/')),
('completed', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('custom_order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_updates', to='products.customorder')),
],
options={
'ordering': ['created_at'],
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 15:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0006_faq_contactmessage'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='product',
name='extras_description',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='product',
name='fursuit_type',
field=models.CharField(choices=[('fullsuit', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Kopf'), ('paws', 'Pfoten'), ('tail', 'Schwanz'), ('other', 'Sonstiges')], default='head', max_length=20),
),
migrations.AddField(
model_name='product',
name='includes_extras',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='is_custom_order',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='product',
name='production_time_weeks',
field=models.IntegerField(default=8),
),
migrations.AddField(
model_name='product',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistisch'), ('realistic', 'Realistisch')], default='toony', max_length=20),
),
migrations.AlterField(
model_name='product',
name='stock',
field=models.IntegerField(default=1),
),
migrations.CreateModel(
name='CustomOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fursuit_type', models.CharField(choices=[('fullsuit', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Kopf'), ('paws', 'Pfoten'), ('tail', 'Schwanz'), ('other', 'Sonstiges')], max_length=20)),
('style', models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistisch'), ('realistic', 'Realistisch')], max_length=20)),
('character_name', models.CharField(max_length=100)),
('character_description', models.TextField()),
('reference_images', models.FileField(blank=True, null=True, upload_to='references/')),
('special_requests', models.TextField(blank=True)),
('measurements', models.TextField()),
('color_preferences', models.TextField()),
('budget_range', models.CharField(max_length=100)),
('deadline_request', models.DateField(blank=True, null=True)),
('status', models.CharField(choices=[('pending', 'Anfrage eingegangen'), ('quoted', 'Angebot erstellt'), ('approved', 'Angebot akzeptiert'), ('in_progress', 'In Arbeit'), ('ready', 'Fertig zur Abholung'), ('shipped', 'Versendet'), ('completed', 'Abgeschlossen'), ('cancelled', 'Storniert')], default='pending', max_length=20)),
('quoted_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='OrderProgress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stage', models.CharField(choices=[('design', 'Design & Planung'), ('base', 'Grundform'), ('fur', 'Fell'), ('details', 'Details'), ('electronics', 'Elektronik (optional)'), ('finishing', 'Finishing')], max_length=20)),
('description', models.TextField()),
('image', models.ImageField(blank=True, null=True, upload_to='progress/')),
('completed', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('custom_order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_updates', to='products.customorder')),
],
options={
'ordering': ['created_at'],
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 15:01
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0006_faq_contactmessage'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='product',
name='extras_description',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='product',
name='fursuit_type',
field=models.CharField(choices=[('fullsuit', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Kopf'), ('paws', 'Pfoten'), ('tail', 'Schwanz'), ('other', 'Sonstiges')], default='head', max_length=20),
),
migrations.AddField(
model_name='product',
name='includes_extras',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='is_custom_order',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='product',
name='production_time_weeks',
field=models.IntegerField(default=8),
),
migrations.AddField(
model_name='product',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistisch'), ('realistic', 'Realistisch')], default='toony', max_length=20),
),
migrations.AlterField(
model_name='product',
name='stock',
field=models.IntegerField(default=1),
),
migrations.CreateModel(
name='CustomOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('fursuit_type', models.CharField(choices=[('fullsuit', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Kopf'), ('paws', 'Pfoten'), ('tail', 'Schwanz'), ('other', 'Sonstiges')], max_length=20)),
('style', models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistisch'), ('realistic', 'Realistisch')], max_length=20)),
('character_name', models.CharField(max_length=100)),
('character_description', models.TextField()),
('reference_images', models.FileField(blank=True, null=True, upload_to='references/')),
('special_requests', models.TextField(blank=True)),
('measurements', models.TextField()),
('color_preferences', models.TextField()),
('budget_range', models.CharField(max_length=100)),
('deadline_request', models.DateField(blank=True, null=True)),
('status', models.CharField(choices=[('pending', 'Anfrage eingegangen'), ('quoted', 'Angebot erstellt'), ('approved', 'Angebot akzeptiert'), ('in_progress', 'In Arbeit'), ('ready', 'Fertig zur Abholung'), ('shipped', 'Versendet'), ('completed', 'Abgeschlossen'), ('cancelled', 'Storniert')], default='pending', max_length=20)),
('quoted_price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='OrderProgress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stage', models.CharField(choices=[('design', 'Design & Planung'), ('base', 'Grundform'), ('fur', 'Fell'), ('details', 'Details'), ('electronics', 'Elektronik (optional)'), ('finishing', 'Finishing')], max_length=20)),
('description', models.TextField()),
('image', models.ImageField(blank=True, null=True, upload_to='progress/')),
('completed', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('custom_order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_updates', to='products.customorder')),
],
options={
'ordering': ['created_at'],
},
),
]

View File

@ -1,293 +1,145 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 18:29
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0007_product_extras_description_product_fursuit_type_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterModelOptions(
name='contactmessage',
options={'ordering': ['-created'], 'verbose_name': 'Kontaktanfrage', 'verbose_name_plural': 'Kontaktanfragen'},
),
migrations.AlterModelOptions(
name='customorder',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='order',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='orderprogress',
options={'ordering': ['created']},
),
migrations.AlterModelOptions(
name='product',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='review',
options={'ordering': ['-created']},
),
migrations.RenameField(
model_name='cart',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='cart',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='cartitem',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='contactmessage',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='customorder',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='customorder',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='faq',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='faq',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='order',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='order',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='orderprogress',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='product',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='product',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='review',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='review',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='userprofile',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='userprofile',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='wishlist',
old_name='created_at',
new_name='created',
),
migrations.AddField(
model_name='orderprogress',
name='updated',
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name='cart',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_carts', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='order',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_orders', to=settings.AUTH_USER_MODEL),
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 18:29
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0007_product_extras_description_product_fursuit_type_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterModelOptions(
name='contactmessage',
options={'ordering': ['-created'], 'verbose_name': 'Kontaktanfrage', 'verbose_name_plural': 'Kontaktanfragen'},
),
migrations.AlterModelOptions(
name='customorder',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='order',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='orderprogress',
options={'ordering': ['created']},
),
migrations.AlterModelOptions(
name='product',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='review',
options={'ordering': ['-created']},
),
migrations.RenameField(
model_name='cart',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='cart',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='cartitem',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='contactmessage',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='customorder',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='customorder',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='faq',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='faq',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='order',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='order',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='orderprogress',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='product',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='product',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='review',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='review',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='userprofile',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='userprofile',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='wishlist',
old_name='created_at',
new_name='created',
),
migrations.AddField(
model_name='orderprogress',
name='updated',
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name='cart',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_carts', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='order',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_orders', to=settings.AUTH_USER_MODEL),
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 18:29
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0007_product_extras_description_product_fursuit_type_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterModelOptions(
name='contactmessage',
options={'ordering': ['-created'], 'verbose_name': 'Kontaktanfrage', 'verbose_name_plural': 'Kontaktanfragen'},
),
migrations.AlterModelOptions(
name='customorder',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='order',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='orderprogress',
options={'ordering': ['created']},
),
migrations.AlterModelOptions(
name='product',
options={'ordering': ['-created']},
),
migrations.AlterModelOptions(
name='review',
options={'ordering': ['-created']},
),
migrations.RenameField(
model_name='cart',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='cart',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='cartitem',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='contactmessage',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='customorder',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='customorder',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='faq',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='faq',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='order',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='order',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='orderprogress',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='product',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='product',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='review',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='review',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='userprofile',
old_name='created_at',
new_name='created',
),
migrations.RenameField(
model_name='userprofile',
old_name='updated_at',
new_name='updated',
),
migrations.RenameField(
model_name='wishlist',
old_name='created_at',
new_name='created',
),
migrations.AddField(
model_name='orderprogress',
name='updated',
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name='cart',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_carts', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='order',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_orders', to=settings.AUTH_USER_MODEL),
),
]

View File

@ -1,297 +1,147 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-30 07:31
import django.core.validators
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0008_alter_contactmessage_options_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='product',
name='category',
),
migrations.RemoveField(
model_name='product',
name='extras_description',
),
migrations.RemoveField(
model_name='product',
name='featured',
),
migrations.RemoveField(
model_name='product',
name='includes_extras',
),
migrations.RemoveField(
model_name='product',
name='production_time_weeks',
),
migrations.RemoveField(
model_name='review',
name='updated',
),
migrations.AddField(
model_name='order',
name='payment_date',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='order',
name='payment_method',
field=models.CharField(blank=True, choices=[('card', 'Kreditkarte'), ('sepa', 'SEPA-Lastschrift'), ('giropay', 'Giropay'), ('sofort', 'Sofort'), ('bancontact', 'Bancontact')], max_length=20, null=True),
),
migrations.AddField(
model_name='order',
name='payment_status',
field=models.CharField(choices=[('pending', 'Ausstehend'), ('processing', 'Wird bearbeitet'), ('paid', 'Bezahlt'), ('failed', 'Fehlgeschlagen'), ('refunded', 'Zurückerstattet')], default='pending', max_length=20),
),
migrations.AddField(
model_name='order',
name='stripe_payment_intent_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='order',
name='stripe_payment_method_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='product',
name='is_featured',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='wishlist_users',
field=models.ManyToManyField(blank=True, related_name='wishlist_products', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='customorder',
name='fursuit_type',
field=models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], max_length=20),
),
migrations.AlterField(
model_name='customorder',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], max_length=20),
),
migrations.AlterField(
model_name='product',
name='fursuit_type',
field=models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], default='other', max_length=20),
),
migrations.AlterField(
model_name='product',
name='is_custom_order',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='product',
name='price',
field=models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AlterField(
model_name='product',
name='stock',
field=models.IntegerField(default=0),
),
migrations.AlterField(
model_name='product',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], default='toony', max_length=20),
),
migrations.AlterField(
model_name='review',
name='rating',
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['status'], name='products_or_status_bd22a2_idx'),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['payment_status'], name='products_or_payment_0d94df_idx'),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['created'], name='products_or_created_a2e72d_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['name'], name='products_pr_name_9ff0a3_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['price'], name='products_pr_price_9b1a5f_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['created'], name='products_pr_created_9a1943_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['fursuit_type'], name='products_pr_fursuit_fde435_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['style'], name='products_pr_style_de3c68_idx'),
),
]
=======
# Generated by Django 5.2.1 on 2025-05-30 07:31
import django.core.validators
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0008_alter_contactmessage_options_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='product',
name='category',
),
migrations.RemoveField(
model_name='product',
name='extras_description',
),
migrations.RemoveField(
model_name='product',
name='featured',
),
migrations.RemoveField(
model_name='product',
name='includes_extras',
),
migrations.RemoveField(
model_name='product',
name='production_time_weeks',
),
migrations.RemoveField(
model_name='review',
name='updated',
),
migrations.AddField(
model_name='order',
name='payment_date',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='order',
name='payment_method',
field=models.CharField(blank=True, choices=[('card', 'Kreditkarte'), ('sepa', 'SEPA-Lastschrift'), ('giropay', 'Giropay'), ('sofort', 'Sofort'), ('bancontact', 'Bancontact')], max_length=20, null=True),
),
migrations.AddField(
model_name='order',
name='payment_status',
field=models.CharField(choices=[('pending', 'Ausstehend'), ('processing', 'Wird bearbeitet'), ('paid', 'Bezahlt'), ('failed', 'Fehlgeschlagen'), ('refunded', 'Zurückerstattet')], default='pending', max_length=20),
),
migrations.AddField(
model_name='order',
name='stripe_payment_intent_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='order',
name='stripe_payment_method_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='product',
name='is_featured',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='wishlist_users',
field=models.ManyToManyField(blank=True, related_name='wishlist_products', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='customorder',
name='fursuit_type',
field=models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], max_length=20),
),
migrations.AlterField(
model_name='customorder',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], max_length=20),
),
migrations.AlterField(
model_name='product',
name='fursuit_type',
field=models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], default='other', max_length=20),
),
migrations.AlterField(
model_name='product',
name='is_custom_order',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='product',
name='price',
field=models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AlterField(
model_name='product',
name='stock',
field=models.IntegerField(default=0),
),
migrations.AlterField(
model_name='product',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], default='toony', max_length=20),
),
migrations.AlterField(
model_name='review',
name='rating',
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['status'], name='products_or_status_bd22a2_idx'),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['payment_status'], name='products_or_payment_0d94df_idx'),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['created'], name='products_or_created_a2e72d_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['name'], name='products_pr_name_9ff0a3_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['price'], name='products_pr_price_9b1a5f_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['created'], name='products_pr_created_9a1943_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['fursuit_type'], name='products_pr_fursuit_fde435_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['style'], name='products_pr_style_de3c68_idx'),
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-30 07:31
import django.core.validators
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0008_alter_contactmessage_options_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='product',
name='category',
),
migrations.RemoveField(
model_name='product',
name='extras_description',
),
migrations.RemoveField(
model_name='product',
name='featured',
),
migrations.RemoveField(
model_name='product',
name='includes_extras',
),
migrations.RemoveField(
model_name='product',
name='production_time_weeks',
),
migrations.RemoveField(
model_name='review',
name='updated',
),
migrations.AddField(
model_name='order',
name='payment_date',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='order',
name='payment_method',
field=models.CharField(blank=True, choices=[('card', 'Kreditkarte'), ('sepa', 'SEPA-Lastschrift'), ('giropay', 'Giropay'), ('sofort', 'Sofort'), ('bancontact', 'Bancontact')], max_length=20, null=True),
),
migrations.AddField(
model_name='order',
name='payment_status',
field=models.CharField(choices=[('pending', 'Ausstehend'), ('processing', 'Wird bearbeitet'), ('paid', 'Bezahlt'), ('failed', 'Fehlgeschlagen'), ('refunded', 'Zurückerstattet')], default='pending', max_length=20),
),
migrations.AddField(
model_name='order',
name='stripe_payment_intent_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='order',
name='stripe_payment_method_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='product',
name='is_featured',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='product',
name='wishlist_users',
field=models.ManyToManyField(blank=True, related_name='wishlist_products', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='customorder',
name='fursuit_type',
field=models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], max_length=20),
),
migrations.AlterField(
model_name='customorder',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], max_length=20),
),
migrations.AlterField(
model_name='product',
name='fursuit_type',
field=models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], default='other', max_length=20),
),
migrations.AlterField(
model_name='product',
name='is_custom_order',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='product',
name='price',
field=models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AlterField(
model_name='product',
name='stock',
field=models.IntegerField(default=0),
),
migrations.AlterField(
model_name='product',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], default='toony', max_length=20),
),
migrations.AlterField(
model_name='review',
name='rating',
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['status'], name='products_or_status_bd22a2_idx'),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['payment_status'], name='products_or_payment_0d94df_idx'),
),
migrations.AddIndex(
model_name='order',
index=models.Index(fields=['created'], name='products_or_created_a2e72d_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['name'], name='products_pr_name_9ff0a3_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['price'], name='products_pr_price_9b1a5f_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['created'], name='products_pr_created_9a1943_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['fursuit_type'], name='products_pr_fursuit_fde435_idx'),
),
migrations.AddIndex(
model_name='product',
index=models.Index(fields=['style'], name='products_pr_style_de3c68_idx'),
),
]

View File

@ -1,67 +1,32 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-30 07:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0009_remove_product_category_and_more'),
]
operations = [
migrations.CreateModel(
name='GalleryImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Titel')),
('description', models.TextField(blank=True, verbose_name='Beschreibung')),
('image', models.ImageField(upload_to='gallery/', verbose_name='Bild')),
('fursuit_type', models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], max_length=20, verbose_name='Fursuit-Typ')),
('style', models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], max_length=20, verbose_name='Stil')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am')),
('is_featured', models.BooleanField(default=False, verbose_name='Hervorgehoben')),
('order', models.IntegerField(default=0, verbose_name='Reihenfolge')),
],
options={
'verbose_name': 'Galeriebild',
'verbose_name_plural': 'Galeriebilder',
'ordering': ['order', '-created'],
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-30 07:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0009_remove_product_category_and_more'),
]
operations = [
migrations.CreateModel(
name='GalleryImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Titel')),
('description', models.TextField(blank=True, verbose_name='Beschreibung')),
('image', models.ImageField(upload_to='gallery/', verbose_name='Bild')),
('fursuit_type', models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], max_length=20, verbose_name='Fursuit-Typ')),
('style', models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], max_length=20, verbose_name='Stil')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am')),
('is_featured', models.BooleanField(default=False, verbose_name='Hervorgehoben')),
('order', models.IntegerField(default=0, verbose_name='Reihenfolge')),
],
options={
'verbose_name': 'Galeriebild',
'verbose_name_plural': 'Galeriebilder',
'ordering': ['order', '-created'],
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-30 07:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0009_remove_product_category_and_more'),
]
operations = [
migrations.CreateModel(
name='GalleryImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Titel')),
('description', models.TextField(blank=True, verbose_name='Beschreibung')),
('image', models.ImageField(upload_to='gallery/', verbose_name='Bild')),
('fursuit_type', models.CharField(choices=[('partial', 'Partial'), ('fullsuit', 'Fullsuit'), ('head', 'Head Only'), ('paws', 'Paws'), ('tail', 'Tail'), ('other', 'Other')], max_length=20, verbose_name='Fursuit-Typ')),
('style', models.CharField(choices=[('toony', 'Toony'), ('semi_realistic', 'Semi-Realistic'), ('realistic', 'Realistic'), ('anime', 'Anime'), ('chibi', 'Chibi')], max_length=20, verbose_name='Stil')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am')),
('is_featured', models.BooleanField(default=False, verbose_name='Hervorgehoben')),
('order', models.IntegerField(default=0, verbose_name='Reihenfolge')),
],
options={
'verbose_name': 'Galeriebild',
'verbose_name_plural': 'Galeriebilder',
'ordering': ['order', '-created'],
},
),
]

View File

@ -1,49 +1,23 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-30 08:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0010_galleryimage'),
]
operations = [
migrations.AlterField(
model_name='galleryimage',
name='fursuit_type',
field=models.CharField(choices=[('full', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Head Only'), ('other', 'Other')], default='full', max_length=20, verbose_name='Fursuit-Typ'),
),
migrations.AlterField(
model_name='galleryimage',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi', 'Semi-Realistic'), ('real', 'Realistic'), ('anime', 'Anime')], default='toony', max_length=20, verbose_name='Stil'),
),
]
=======
# Generated by Django 5.2.1 on 2025-05-30 08:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0010_galleryimage'),
]
operations = [
migrations.AlterField(
model_name='galleryimage',
name='fursuit_type',
field=models.CharField(choices=[('full', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Head Only'), ('other', 'Other')], default='full', max_length=20, verbose_name='Fursuit-Typ'),
),
migrations.AlterField(
model_name='galleryimage',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi', 'Semi-Realistic'), ('real', 'Realistic'), ('anime', 'Anime')], default='toony', max_length=20, verbose_name='Stil'),
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-30 08:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0010_galleryimage'),
]
operations = [
migrations.AlterField(
model_name='galleryimage',
name='fursuit_type',
field=models.CharField(choices=[('full', 'Fullsuit'), ('partial', 'Partial'), ('head', 'Head Only'), ('other', 'Other')], default='full', max_length=20, verbose_name='Fursuit-Typ'),
),
migrations.AlterField(
model_name='galleryimage',
name='style',
field=models.CharField(choices=[('toony', 'Toony'), ('semi', 'Semi-Realistic'), ('real', 'Realistic'), ('anime', 'Anime')], default='toony', max_length=20, verbose_name='Stil'),
),
]

View File

@ -1,73 +1,35 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-30 11:51
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0011_alter_galleryimage_fursuit_type_and_more'),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('slug', models.SlugField(unique=True, verbose_name='URL-Slug')),
('description', models.TextField(blank=True, verbose_name='Beschreibung')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Kategorie',
'verbose_name_plural': 'Kategorien',
'ordering': ['name'],
},
),
migrations.AddField(
model_name='product',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='products', to='products.category', verbose_name='Kategorie'),
),
]
=======
# Generated by Django 5.2.1 on 2025-05-30 11:51
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0011_alter_galleryimage_fursuit_type_and_more'),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('slug', models.SlugField(unique=True, verbose_name='URL-Slug')),
('description', models.TextField(blank=True, verbose_name='Beschreibung')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Kategorie',
'verbose_name_plural': 'Kategorien',
'ordering': ['name'],
},
),
migrations.AddField(
model_name='product',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='products', to='products.category', verbose_name='Kategorie'),
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-30 11:51
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0011_alter_galleryimage_fursuit_type_and_more'),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('slug', models.SlugField(unique=True, verbose_name='URL-Slug')),
('description', models.TextField(blank=True, verbose_name='Beschreibung')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Kategorie',
'verbose_name_plural': 'Kategorien',
'ordering': ['name'],
},
),
migrations.AddField(
model_name='product',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='products', to='products.category', verbose_name='Kategorie'),
),
]

View File

@ -1,49 +1,23 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-30 11:54
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0012_category_product_category'),
('shop', '0003_contactmessage'),
]
operations = [
migrations.AlterField(
model_name='product',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_items', to='shop.category', verbose_name='Kategorie'),
),
migrations.DeleteModel(
name='Category',
),
]
=======
# Generated by Django 5.2.1 on 2025-05-30 11:54
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0012_category_product_category'),
('shop', '0003_contactmessage'),
]
operations = [
migrations.AlterField(
model_name='product',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_items', to='shop.category', verbose_name='Kategorie'),
),
migrations.DeleteModel(
name='Category',
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-30 11:54
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0012_category_product_category'),
('shop', '0003_contactmessage'),
]
operations = [
migrations.AlterField(
model_name='product',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='product_items', to='shop.category', verbose_name='Kategorie'),
),
migrations.DeleteModel(
name='Category',
),
]

File diff suppressed because it is too large Load Diff

View File

@ -1,430 +1,214 @@
<<<<<<< HEAD
from rest_framework import serializers
from .models import Product, Category, Review, Wishlist, GalleryImage, CustomOrder, Payment
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
"""Serializer für User-Model"""
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name']
read_only_fields = ['id']
class CategorySerializer(serializers.ModelSerializer):
"""Serializer für Category-Model"""
product_count = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ['id', 'name', 'slug', 'description', 'image', 'product_count']
def get_product_count(self, obj):
return obj.products.count()
class ReviewSerializer(serializers.ModelSerializer):
"""Serializer für Review-Model"""
user = UserSerializer(read_only=True)
user_name = serializers.CharField(source='user.username', read_only=True)
class Meta:
model = Review
fields = ['id', 'product', 'user', 'user_name', 'rating', 'comment', 'created_at']
read_only_fields = ['id', 'created_at']
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
class GalleryImageSerializer(serializers.ModelSerializer):
"""Serializer für GalleryImage-Model"""
class Meta:
model = GalleryImage
fields = ['id', 'product', 'image', 'alt_text', 'is_featured', 'created_at']
read_only_fields = ['id', 'created_at']
class ProductSerializer(serializers.ModelSerializer):
"""Basis-Serializer für Product-Model"""
category = CategorySerializer(read_only=True)
category_id = serializers.IntegerField(write_only=True, required=False)
reviews = ReviewSerializer(many=True, read_only=True)
gallery_images = GalleryImageSerializer(many=True, read_only=True)
average_rating = serializers.FloatField(read_only=True)
review_count = serializers.IntegerField(read_only=True)
is_in_wishlist = serializers.SerializerMethodField()
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'base_price', 'sale_price',
'on_sale', 'stock', 'product_type', 'fursuit_type', 'style',
'is_custom_order', 'is_featured', 'category', 'category_id',
'image', 'gallery_images', 'reviews', 'average_rating',
'review_count', 'is_in_wishlist', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'slug', 'created_at', 'updated_at']
def get_is_in_wishlist(self, obj):
user = self.context['request'].user
if user.is_authenticated:
return obj.wishlists.filter(user=user).exists()
return False
def create(self, validated_data):
category_id = validated_data.pop('category_id', None)
if category_id:
validated_data['category'] = Category.objects.get(id=category_id)
return super().create(validated_data)
class ProductListSerializer(serializers.ModelSerializer):
"""Vereinfachter Serializer für Produktlisten"""
category = CategorySerializer(read_only=True)
average_rating = serializers.FloatField(read_only=True)
review_count = serializers.IntegerField(read_only=True)
is_in_wishlist = serializers.SerializerMethodField()
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'base_price', 'sale_price',
'on_sale', 'stock', 'product_type', 'fursuit_type', 'style',
'is_custom_order', 'is_featured', 'category', 'image',
'average_rating', 'review_count', 'is_in_wishlist'
]
def get_is_in_wishlist(self, obj):
user = self.context['request'].user
if user.is_authenticated:
return obj.wishlists.filter(user=user).exists()
return False
class ProductDetailSerializer(ProductSerializer):
"""Detaillierter Serializer für Produktdetails"""
related_products = ProductListSerializer(many=True, read_only=True)
class Meta(ProductSerializer.Meta):
fields = ProductSerializer.Meta.fields + ['related_products']
class WishlistSerializer(serializers.ModelSerializer):
"""Serializer für Wishlist-Model"""
products = ProductListSerializer(many=True, read_only=True)
product_count = serializers.SerializerMethodField()
class Meta:
model = Wishlist
fields = ['id', 'user', 'products', 'product_count', 'created_at']
read_only_fields = ['id', 'created_at']
def get_product_count(self, obj):
return obj.products.count()
class CustomOrderSerializer(serializers.ModelSerializer):
"""Serializer für CustomOrder-Model"""
user = UserSerializer(read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = CustomOrder
fields = [
'id', 'user', 'title', 'description', 'fursuit_type', 'style',
'size', 'color_preferences', 'special_requirements', 'budget',
'status', 'status_display', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
class PaymentSerializer(serializers.ModelSerializer):
"""Serializer für Payment-Model"""
user = UserSerializer(read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Payment
fields = [
'id', 'user', 'order', 'amount', 'payment_method', 'status',
'status_display', 'transaction_id', 'created_at'
]
read_only_fields = ['id', 'created_at']
class CartItemSerializer(serializers.Serializer):
"""Serializer für Warenkorb-Items"""
product_id = serializers.IntegerField()
quantity = serializers.IntegerField(min_value=1)
product = ProductListSerializer(read_only=True)
total_price = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True)
class CartSerializer(serializers.Serializer):
"""Serializer für Warenkorb"""
items = CartItemSerializer(many=True)
subtotal = serializers.DecimalField(max_digits=10, decimal_places=2)
shipping_cost = serializers.DecimalField(max_digits=10, decimal_places=2)
total = serializers.DecimalField(max_digits=10, decimal_places=2)
item_count = serializers.IntegerField()
class SearchFilterSerializer(serializers.Serializer):
"""Serializer für Such- und Filter-Parameter"""
query = serializers.CharField(required=False, allow_blank=True)
category = serializers.CharField(required=False, allow_blank=True)
fursuit_type = serializers.CharField(required=False, allow_blank=True)
style = serializers.CharField(required=False, allow_blank=True)
min_price = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
max_price = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
on_sale = serializers.BooleanField(required=False)
is_featured = serializers.BooleanField(required=False)
sort_by = serializers.CharField(required=False, default='newest')
page = serializers.IntegerField(required=False, default=1)
page_size = serializers.IntegerField(required=False, default=12)
class ProductStatsSerializer(serializers.Serializer):
"""Serializer für Produkt-Statistiken"""
total_products = serializers.IntegerField()
featured_products = serializers.IntegerField()
on_sale_products = serializers.IntegerField()
low_stock_products = serializers.IntegerField()
categories_count = serializers.IntegerField()
average_rating = serializers.FloatField()
total_reviews = serializers.IntegerField()
class GalleryImageDetailSerializer(serializers.ModelSerializer):
"""Detaillierter Serializer für Galerie-Bilder"""
product = ProductListSerializer(read_only=True)
class Meta:
model = GalleryImage
fields = [
'id', 'product', 'image', 'alt_text', 'title', 'description',
'is_featured', 'fursuit_type', 'style', 'created_at'
]
=======
from rest_framework import serializers
from .models import Product, Category, Review, Wishlist, GalleryImage, CustomOrder, Payment
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
"""Serializer für User-Model"""
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name']
read_only_fields = ['id']
class CategorySerializer(serializers.ModelSerializer):
"""Serializer für Category-Model"""
product_count = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ['id', 'name', 'slug', 'description', 'image', 'product_count']
def get_product_count(self, obj):
return obj.products.count()
class ReviewSerializer(serializers.ModelSerializer):
"""Serializer für Review-Model"""
user = UserSerializer(read_only=True)
user_name = serializers.CharField(source='user.username', read_only=True)
class Meta:
model = Review
fields = ['id', 'product', 'user', 'user_name', 'rating', 'comment', 'created_at']
read_only_fields = ['id', 'created_at']
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
class GalleryImageSerializer(serializers.ModelSerializer):
"""Serializer für GalleryImage-Model"""
class Meta:
model = GalleryImage
fields = ['id', 'product', 'image', 'alt_text', 'is_featured', 'created_at']
read_only_fields = ['id', 'created_at']
class ProductSerializer(serializers.ModelSerializer):
"""Basis-Serializer für Product-Model"""
category = CategorySerializer(read_only=True)
category_id = serializers.IntegerField(write_only=True, required=False)
reviews = ReviewSerializer(many=True, read_only=True)
gallery_images = GalleryImageSerializer(many=True, read_only=True)
average_rating = serializers.FloatField(read_only=True)
review_count = serializers.IntegerField(read_only=True)
is_in_wishlist = serializers.SerializerMethodField()
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'base_price', 'sale_price',
'on_sale', 'stock', 'product_type', 'fursuit_type', 'style',
'is_custom_order', 'is_featured', 'category', 'category_id',
'image', 'gallery_images', 'reviews', 'average_rating',
'review_count', 'is_in_wishlist', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'slug', 'created_at', 'updated_at']
def get_is_in_wishlist(self, obj):
user = self.context['request'].user
if user.is_authenticated:
return obj.wishlists.filter(user=user).exists()
return False
def create(self, validated_data):
category_id = validated_data.pop('category_id', None)
if category_id:
validated_data['category'] = Category.objects.get(id=category_id)
return super().create(validated_data)
class ProductListSerializer(serializers.ModelSerializer):
"""Vereinfachter Serializer für Produktlisten"""
category = CategorySerializer(read_only=True)
average_rating = serializers.FloatField(read_only=True)
review_count = serializers.IntegerField(read_only=True)
is_in_wishlist = serializers.SerializerMethodField()
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'base_price', 'sale_price',
'on_sale', 'stock', 'product_type', 'fursuit_type', 'style',
'is_custom_order', 'is_featured', 'category', 'image',
'average_rating', 'review_count', 'is_in_wishlist'
]
def get_is_in_wishlist(self, obj):
user = self.context['request'].user
if user.is_authenticated:
return obj.wishlists.filter(user=user).exists()
return False
class ProductDetailSerializer(ProductSerializer):
"""Detaillierter Serializer für Produktdetails"""
related_products = ProductListSerializer(many=True, read_only=True)
class Meta(ProductSerializer.Meta):
fields = ProductSerializer.Meta.fields + ['related_products']
class WishlistSerializer(serializers.ModelSerializer):
"""Serializer für Wishlist-Model"""
products = ProductListSerializer(many=True, read_only=True)
product_count = serializers.SerializerMethodField()
class Meta:
model = Wishlist
fields = ['id', 'user', 'products', 'product_count', 'created_at']
read_only_fields = ['id', 'created_at']
def get_product_count(self, obj):
return obj.products.count()
class CustomOrderSerializer(serializers.ModelSerializer):
"""Serializer für CustomOrder-Model"""
user = UserSerializer(read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = CustomOrder
fields = [
'id', 'user', 'title', 'description', 'fursuit_type', 'style',
'size', 'color_preferences', 'special_requirements', 'budget',
'status', 'status_display', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
class PaymentSerializer(serializers.ModelSerializer):
"""Serializer für Payment-Model"""
user = UserSerializer(read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Payment
fields = [
'id', 'user', 'order', 'amount', 'payment_method', 'status',
'status_display', 'transaction_id', 'created_at'
]
read_only_fields = ['id', 'created_at']
class CartItemSerializer(serializers.Serializer):
"""Serializer für Warenkorb-Items"""
product_id = serializers.IntegerField()
quantity = serializers.IntegerField(min_value=1)
product = ProductListSerializer(read_only=True)
total_price = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True)
class CartSerializer(serializers.Serializer):
"""Serializer für Warenkorb"""
items = CartItemSerializer(many=True)
subtotal = serializers.DecimalField(max_digits=10, decimal_places=2)
shipping_cost = serializers.DecimalField(max_digits=10, decimal_places=2)
total = serializers.DecimalField(max_digits=10, decimal_places=2)
item_count = serializers.IntegerField()
class SearchFilterSerializer(serializers.Serializer):
"""Serializer für Such- und Filter-Parameter"""
query = serializers.CharField(required=False, allow_blank=True)
category = serializers.CharField(required=False, allow_blank=True)
fursuit_type = serializers.CharField(required=False, allow_blank=True)
style = serializers.CharField(required=False, allow_blank=True)
min_price = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
max_price = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
on_sale = serializers.BooleanField(required=False)
is_featured = serializers.BooleanField(required=False)
sort_by = serializers.CharField(required=False, default='newest')
page = serializers.IntegerField(required=False, default=1)
page_size = serializers.IntegerField(required=False, default=12)
class ProductStatsSerializer(serializers.Serializer):
"""Serializer für Produkt-Statistiken"""
total_products = serializers.IntegerField()
featured_products = serializers.IntegerField()
on_sale_products = serializers.IntegerField()
low_stock_products = serializers.IntegerField()
categories_count = serializers.IntegerField()
average_rating = serializers.FloatField()
total_reviews = serializers.IntegerField()
class GalleryImageDetailSerializer(serializers.ModelSerializer):
"""Detaillierter Serializer für Galerie-Bilder"""
product = ProductListSerializer(read_only=True)
class Meta:
model = GalleryImage
fields = [
'id', 'product', 'image', 'alt_text', 'title', 'description',
'is_featured', 'fursuit_type', 'style', 'created_at'
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from rest_framework import serializers
from .models import Product, Category, Review, Wishlist, GalleryImage, CustomOrder, Payment
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
"""Serializer für User-Model"""
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name']
read_only_fields = ['id']
class CategorySerializer(serializers.ModelSerializer):
"""Serializer für Category-Model"""
product_count = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ['id', 'name', 'slug', 'description', 'image', 'product_count']
def get_product_count(self, obj):
return obj.products.count()
class ReviewSerializer(serializers.ModelSerializer):
"""Serializer für Review-Model"""
user = UserSerializer(read_only=True)
user_name = serializers.CharField(source='user.username', read_only=True)
class Meta:
model = Review
fields = ['id', 'product', 'user', 'user_name', 'rating', 'comment', 'created_at']
read_only_fields = ['id', 'created_at']
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
class GalleryImageSerializer(serializers.ModelSerializer):
"""Serializer für GalleryImage-Model"""
class Meta:
model = GalleryImage
fields = ['id', 'product', 'image', 'alt_text', 'is_featured', 'created_at']
read_only_fields = ['id', 'created_at']
class ProductSerializer(serializers.ModelSerializer):
"""Basis-Serializer für Product-Model"""
category = CategorySerializer(read_only=True)
category_id = serializers.IntegerField(write_only=True, required=False)
reviews = ReviewSerializer(many=True, read_only=True)
gallery_images = GalleryImageSerializer(many=True, read_only=True)
average_rating = serializers.FloatField(read_only=True)
review_count = serializers.IntegerField(read_only=True)
is_in_wishlist = serializers.SerializerMethodField()
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'base_price', 'sale_price',
'on_sale', 'stock', 'product_type', 'fursuit_type', 'style',
'is_custom_order', 'is_featured', 'category', 'category_id',
'image', 'gallery_images', 'reviews', 'average_rating',
'review_count', 'is_in_wishlist', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'slug', 'created_at', 'updated_at']
def get_is_in_wishlist(self, obj):
user = self.context['request'].user
if user.is_authenticated:
return obj.wishlists.filter(user=user).exists()
return False
def create(self, validated_data):
category_id = validated_data.pop('category_id', None)
if category_id:
validated_data['category'] = Category.objects.get(id=category_id)
return super().create(validated_data)
class ProductListSerializer(serializers.ModelSerializer):
"""Vereinfachter Serializer für Produktlisten"""
category = CategorySerializer(read_only=True)
average_rating = serializers.FloatField(read_only=True)
review_count = serializers.IntegerField(read_only=True)
is_in_wishlist = serializers.SerializerMethodField()
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'base_price', 'sale_price',
'on_sale', 'stock', 'product_type', 'fursuit_type', 'style',
'is_custom_order', 'is_featured', 'category', 'image',
'average_rating', 'review_count', 'is_in_wishlist'
]
def get_is_in_wishlist(self, obj):
user = self.context['request'].user
if user.is_authenticated:
return obj.wishlists.filter(user=user).exists()
return False
class ProductDetailSerializer(ProductSerializer):
"""Detaillierter Serializer für Produktdetails"""
related_products = ProductListSerializer(many=True, read_only=True)
class Meta(ProductSerializer.Meta):
fields = ProductSerializer.Meta.fields + ['related_products']
class WishlistSerializer(serializers.ModelSerializer):
"""Serializer für Wishlist-Model"""
products = ProductListSerializer(many=True, read_only=True)
product_count = serializers.SerializerMethodField()
class Meta:
model = Wishlist
fields = ['id', 'user', 'products', 'product_count', 'created_at']
read_only_fields = ['id', 'created_at']
def get_product_count(self, obj):
return obj.products.count()
class CustomOrderSerializer(serializers.ModelSerializer):
"""Serializer für CustomOrder-Model"""
user = UserSerializer(read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = CustomOrder
fields = [
'id', 'user', 'title', 'description', 'fursuit_type', 'style',
'size', 'color_preferences', 'special_requirements', 'budget',
'status', 'status_display', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at']
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
class PaymentSerializer(serializers.ModelSerializer):
"""Serializer für Payment-Model"""
user = UserSerializer(read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Payment
fields = [
'id', 'user', 'order', 'amount', 'payment_method', 'status',
'status_display', 'transaction_id', 'created_at'
]
read_only_fields = ['id', 'created_at']
class CartItemSerializer(serializers.Serializer):
"""Serializer für Warenkorb-Items"""
product_id = serializers.IntegerField()
quantity = serializers.IntegerField(min_value=1)
product = ProductListSerializer(read_only=True)
total_price = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True)
class CartSerializer(serializers.Serializer):
"""Serializer für Warenkorb"""
items = CartItemSerializer(many=True)
subtotal = serializers.DecimalField(max_digits=10, decimal_places=2)
shipping_cost = serializers.DecimalField(max_digits=10, decimal_places=2)
total = serializers.DecimalField(max_digits=10, decimal_places=2)
item_count = serializers.IntegerField()
class SearchFilterSerializer(serializers.Serializer):
"""Serializer für Such- und Filter-Parameter"""
query = serializers.CharField(required=False, allow_blank=True)
category = serializers.CharField(required=False, allow_blank=True)
fursuit_type = serializers.CharField(required=False, allow_blank=True)
style = serializers.CharField(required=False, allow_blank=True)
min_price = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
max_price = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
on_sale = serializers.BooleanField(required=False)
is_featured = serializers.BooleanField(required=False)
sort_by = serializers.CharField(required=False, default='newest')
page = serializers.IntegerField(required=False, default=1)
page_size = serializers.IntegerField(required=False, default=12)
class ProductStatsSerializer(serializers.Serializer):
"""Serializer für Produkt-Statistiken"""
total_products = serializers.IntegerField()
featured_products = serializers.IntegerField()
on_sale_products = serializers.IntegerField()
low_stock_products = serializers.IntegerField()
categories_count = serializers.IntegerField()
average_rating = serializers.FloatField()
total_reviews = serializers.IntegerField()
class GalleryImageDetailSerializer(serializers.ModelSerializer):
"""Detaillierter Serializer für Galerie-Bilder"""
product = ProductListSerializer(read_only=True)
class Meta:
model = GalleryImage
fields = [
'id', 'product', 'image', 'alt_text', 'title', 'description',
'is_featured', 'fursuit_type', 'style', 'created_at'
]
read_only_fields = ['id', 'created_at']

View File

@ -1,32 +1,15 @@
<<<<<<< HEAD
{% extends "base.html" %}
{% block extra_head %}
<link rel="stylesheet" href="/static/css/furry.css">
<link rel="stylesheet" href="/static/css/furry-theme.css">
<link rel="stylesheet" href="/static/css/products.css">
<link rel="stylesheet" href="/static/css/dashboard.css">
<link rel="icon" type="image/svg+xml" href="/static/images/kasico-logo-simple.svg">
{{ block.super }}
{% endblock %}
{# Der eigentliche Seiteninhalt kommt in den content-Block #}
{% block content %}
{{ block.super }}
=======
{% extends "base.html" %}
{% block extra_head %}
<link rel="stylesheet" href="/static/css/furry.css">
<link rel="stylesheet" href="/static/css/furry-theme.css">
<link rel="stylesheet" href="/static/css/products.css">
<link rel="stylesheet" href="/static/css/dashboard.css">
<link rel="icon" type="image/svg+xml" href="/static/images/kasico-logo-simple.svg">
{{ block.super }}
{% endblock %}
{# Der eigentliche Seiteninhalt kommt in den content-Block #}
{% block content %}
{{ block.super }}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "base.html" %}
{% block extra_head %}
<link rel="stylesheet" href="/static/css/furry.css">
<link rel="stylesheet" href="/static/css/furry-theme.css">
<link rel="stylesheet" href="/static/css/products.css">
<link rel="stylesheet" href="/static/css/dashboard.css">
<link rel="icon" type="image/svg+xml" href="/static/images/kasico-logo-simple.svg">
{{ block.super }}
{% endblock %}
{# Der eigentliche Seiteninhalt kommt in den content-Block #}
{% block content %}
{{ block.super }}
{% endblock %}

View File

@ -1,200 +1,99 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% block title %}Fortschritt hinzufügen - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
Fortschritts-Update für {{ order.character_name }}
</h5>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.stage.id_for_label }}" class="form-label">
{{ form.stage.label }}
</label>
{{ form.stage }}
{% if form.stage.errors %}
<div class="invalid-feedback d-block">
{% for error in form.stage.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.description.id_for_label }}" class="form-label">
{{ form.description.label }}
</label>
{{ form.description }}
{% if form.description.errors %}
<div class="invalid-feedback d-block">
{% for error in form.description.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.image.id_for_label }}" class="form-label">
{{ form.image.label }}
</label>
{{ form.image }}
{% if form.image.errors %}
<div class="invalid-feedback d-block">
{% for error in form.image.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-4">
<div class="form-check">
{{ form.completed }}
<label class="form-check-label" for="{{ form.completed.id_for_label }}">
{{ form.completed.label }}
</label>
</div>
{% if form.completed.errors %}
<div class="invalid-feedback d-block">
{% for error in form.completed.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-flex justify-content-between">
<a href="{% url 'custom_order_detail' order.id %}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Zurück
</a>
<button type="submit" class="btn btn-primary">
<i class="bi bi-save"></i> Update speichern
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
=======
{% extends 'base.html' %}
{% block title %}Fortschritt hinzufügen - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
Fortschritts-Update für {{ order.character_name }}
</h5>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.stage.id_for_label }}" class="form-label">
{{ form.stage.label }}
</label>
{{ form.stage }}
{% if form.stage.errors %}
<div class="invalid-feedback d-block">
{% for error in form.stage.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.description.id_for_label }}" class="form-label">
{{ form.description.label }}
</label>
{{ form.description }}
{% if form.description.errors %}
<div class="invalid-feedback d-block">
{% for error in form.description.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.image.id_for_label }}" class="form-label">
{{ form.image.label }}
</label>
{{ form.image }}
{% if form.image.errors %}
<div class="invalid-feedback d-block">
{% for error in form.image.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-4">
<div class="form-check">
{{ form.completed }}
<label class="form-check-label" for="{{ form.completed.id_for_label }}">
{{ form.completed.label }}
</label>
</div>
{% if form.completed.errors %}
<div class="invalid-feedback d-block">
{% for error in form.completed.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-flex justify-content-between">
<a href="{% url 'custom_order_detail' order.id %}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Zurück
</a>
<button type="submit" class="btn btn-primary">
<i class="bi bi-save"></i> Update speichern
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% block title %}Fortschritt hinzufügen - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
Fortschritts-Update für {{ order.character_name }}
</h5>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.stage.id_for_label }}" class="form-label">
{{ form.stage.label }}
</label>
{{ form.stage }}
{% if form.stage.errors %}
<div class="invalid-feedback d-block">
{% for error in form.stage.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.description.id_for_label }}" class="form-label">
{{ form.description.label }}
</label>
{{ form.description }}
{% if form.description.errors %}
<div class="invalid-feedback d-block">
{% for error in form.description.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.image.id_for_label }}" class="form-label">
{{ form.image.label }}
</label>
{{ form.image }}
{% if form.image.errors %}
<div class="invalid-feedback d-block">
{% for error in form.image.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-4">
<div class="form-check">
{{ form.completed }}
<label class="form-check-label" for="{{ form.completed.id_for_label }}">
{{ form.completed.label }}
</label>
</div>
{% if form.completed.errors %}
<div class="invalid-feedback d-block">
{% for error in form.completed.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-flex justify-content-between">
<a href="{% url 'custom_order_detail' order.id %}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Zurück
</a>
<button type="submit" class="btn btn-primary">
<i class="bi bi-save"></i> Update speichern
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,354 +1,176 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% load static %}
{% block title %}Warenkorb{% endblock %}
{% block content %}
<h2>Warenkorb</h2>
{% if cart.items %}
<div class="cart-list">
{% for item in cart.items %}
<div class="cart-item-card">
<img src="{{ item.product.image.url }}" alt="{{ item.product.name }}" class="cart-item-image">
<div class="cart-item-info">
<h3>{{ item.product.name }}</h3>
<p class="cart-item-price">{{ item.product.price }} €</p>
<div class="cart-item-qty">
<form method="post" action="{% url 'products:cart_update' item.product.pk %}">
{% csrf_token %}
<input type="number" name="quantity" value="{{ item.quantity }}" min="1" class="cart-qty-input">
<button type="submit" class="btn furry-btn-outline">Menge ändern</button>
</form>
<form method="post" action="{% url 'products:cart_remove' item.product.pk %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn furry-btn-secondary">Entfernen</button>
</form>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="cart-summary">
<span>Gesamtsumme:</span>
<span class="cart-total">{{ cart.total_price }} €</span>
<a href="{% url 'products:checkout' %}" class="btn furry-btn">Weiter zur Kasse</a>
</div>
{% else %}
<p>Dein Warenkorb ist leer.</p>
{% endif %}
<!-- Related Products -->
{% if related_products %}
<div class="furry-card" style="margin-top: 2rem;">
<div class="furry-card-header">
<h2 class="furry-card-title">💡 Das könnte dir auch gefallen</h2>
<p class="furry-card-subtitle">Weitere Fursuits in deinem Stil</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.5rem;">
{% for product in related_products %}
<div class="furry-card furry-product-card">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="furry-card-image">
{% else %}
<div class="furry-card-image" style="background: linear-gradient(45deg, var(--furry-primary), var(--furry-secondary)); display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">
🐾
</div>
{% endif %}
<div class="furry-card-header">
<div>
<h3 class="furry-card-title">{{ product.name }}</h3>
<p class="furry-card-subtitle">{{ product.get_fursuit_type_display }}</p>
</div>
</div>
<div class="furry-card-content">
<p>{{ product.description|truncatewords:10 }}</p>
<div class="furry-product-price">{{ product.price }}€</div>
</div>
<div class="furry-card-footer">
<a href="{% url 'products:product_detail' product.id %}" class="furry-btn furry-btn-primary furry-btn-sm">
👁️ Details
</a>
<button class="furry-btn furry-btn-outline furry-btn-sm" onclick="addToCart({{ product.id }})">
🛒 Hinzufügen
</button>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<script>
function updateQuantity(itemId, change) {
fetch(`/update-cart-item/${itemId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
body: JSON.stringify({
change: change
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
function removeItem(itemId) {
if (confirm('Möchtest du dieses Produkt wirklich aus dem Warenkorb entfernen?')) {
fetch(`/remove-cart-item/${itemId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function clearCart() {
if (confirm('Möchtest du wirklich den gesamten Warenkorb leeren?')) {
fetch('/clear-cart/', {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function addToCart(productId) {
fetch(`/add-to-cart/${productId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
const alert = document.createElement('div');
alert.className = 'furry-alert furry-alert-success';
alert.innerHTML = `
<div class="furry-alert-icon"></div>
<div class="furry-alert-content">
<div class="furry-alert-title">Erfolg</div>
<div class="furry-alert-message">Produkt wurde zum Warenkorb hinzugefügt!</div>
</div>
`;
document.querySelector('main').insertBefore(alert, document.querySelector('main').firstChild);
setTimeout(() => alert.remove(), 3000);
}
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
=======
{% extends 'base.html' %}
{% load static %}
{% block title %}Warenkorb{% endblock %}
{% block content %}
<h2>Warenkorb</h2>
{% if cart.items %}
<div class="cart-list">
{% for item in cart.items %}
<div class="cart-item-card">
<img src="{{ item.product.image.url }}" alt="{{ item.product.name }}" class="cart-item-image">
<div class="cart-item-info">
<h3>{{ item.product.name }}</h3>
<p class="cart-item-price">{{ item.product.price }} €</p>
<div class="cart-item-qty">
<form method="post" action="{% url 'products:cart_update' item.product.pk %}">
{% csrf_token %}
<input type="number" name="quantity" value="{{ item.quantity }}" min="1" class="cart-qty-input">
<button type="submit" class="btn furry-btn-outline">Menge ändern</button>
</form>
<form method="post" action="{% url 'products:cart_remove' item.product.pk %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn furry-btn-secondary">Entfernen</button>
</form>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="cart-summary">
<span>Gesamtsumme:</span>
<span class="cart-total">{{ cart.total_price }} €</span>
<a href="{% url 'products:checkout' %}" class="btn furry-btn">Weiter zur Kasse</a>
</div>
{% else %}
<p>Dein Warenkorb ist leer.</p>
{% endif %}
<!-- Related Products -->
{% if related_products %}
<div class="furry-card" style="margin-top: 2rem;">
<div class="furry-card-header">
<h2 class="furry-card-title">💡 Das könnte dir auch gefallen</h2>
<p class="furry-card-subtitle">Weitere Fursuits in deinem Stil</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.5rem;">
{% for product in related_products %}
<div class="furry-card furry-product-card">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="furry-card-image">
{% else %}
<div class="furry-card-image" style="background: linear-gradient(45deg, var(--furry-primary), var(--furry-secondary)); display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">
🐾
</div>
{% endif %}
<div class="furry-card-header">
<div>
<h3 class="furry-card-title">{{ product.name }}</h3>
<p class="furry-card-subtitle">{{ product.get_fursuit_type_display }}</p>
</div>
</div>
<div class="furry-card-content">
<p>{{ product.description|truncatewords:10 }}</p>
<div class="furry-product-price">{{ product.price }}€</div>
</div>
<div class="furry-card-footer">
<a href="{% url 'products:product_detail' product.id %}" class="furry-btn furry-btn-primary furry-btn-sm">
👁️ Details
</a>
<button class="furry-btn furry-btn-outline furry-btn-sm" onclick="addToCart({{ product.id }})">
🛒 Hinzufügen
</button>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<script>
function updateQuantity(itemId, change) {
fetch(`/update-cart-item/${itemId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
body: JSON.stringify({
change: change
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
function removeItem(itemId) {
if (confirm('Möchtest du dieses Produkt wirklich aus dem Warenkorb entfernen?')) {
fetch(`/remove-cart-item/${itemId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function clearCart() {
if (confirm('Möchtest du wirklich den gesamten Warenkorb leeren?')) {
fetch('/clear-cart/', {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function addToCart(productId) {
fetch(`/add-to-cart/${productId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
const alert = document.createElement('div');
alert.className = 'furry-alert furry-alert-success';
alert.innerHTML = `
<div class="furry-alert-icon"></div>
<div class="furry-alert-content">
<div class="furry-alert-title">Erfolg</div>
<div class="furry-alert-message">Produkt wurde zum Warenkorb hinzugefügt!</div>
</div>
`;
document.querySelector('main').insertBefore(alert, document.querySelector('main').firstChild);
setTimeout(() => alert.remove(), 3000);
}
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% load static %}
{% block title %}Warenkorb{% endblock %}
{% block content %}
<h2>Warenkorb</h2>
{% if cart.items %}
<div class="cart-list">
{% for item in cart.items %}
<div class="cart-item-card">
<img src="{{ item.product.image.url }}" alt="{{ item.product.name }}" class="cart-item-image">
<div class="cart-item-info">
<h3>{{ item.product.name }}</h3>
<p class="cart-item-price">{{ item.product.price }} €</p>
<div class="cart-item-qty">
<form method="post" action="{% url 'products:cart_update' item.product.pk %}">
{% csrf_token %}
<input type="number" name="quantity" value="{{ item.quantity }}" min="1" class="cart-qty-input">
<button type="submit" class="btn furry-btn-outline">Menge ändern</button>
</form>
<form method="post" action="{% url 'products:cart_remove' item.product.pk %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn furry-btn-secondary">Entfernen</button>
</form>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="cart-summary">
<span>Gesamtsumme:</span>
<span class="cart-total">{{ cart.total_price }} €</span>
<a href="{% url 'products:checkout' %}" class="btn furry-btn">Weiter zur Kasse</a>
</div>
{% else %}
<p>Dein Warenkorb ist leer.</p>
{% endif %}
<!-- Related Products -->
{% if related_products %}
<div class="furry-card" style="margin-top: 2rem;">
<div class="furry-card-header">
<h2 class="furry-card-title">💡 Das könnte dir auch gefallen</h2>
<p class="furry-card-subtitle">Weitere Fursuits in deinem Stil</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.5rem;">
{% for product in related_products %}
<div class="furry-card furry-product-card">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="furry-card-image">
{% else %}
<div class="furry-card-image" style="background: linear-gradient(45deg, var(--furry-primary), var(--furry-secondary)); display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">
🐾
</div>
{% endif %}
<div class="furry-card-header">
<div>
<h3 class="furry-card-title">{{ product.name }}</h3>
<p class="furry-card-subtitle">{{ product.get_fursuit_type_display }}</p>
</div>
</div>
<div class="furry-card-content">
<p>{{ product.description|truncatewords:10 }}</p>
<div class="furry-product-price">{{ product.price }}€</div>
</div>
<div class="furry-card-footer">
<a href="{% url 'products:product_detail' product.id %}" class="furry-btn furry-btn-primary furry-btn-sm">
👁️ Details
</a>
<button class="furry-btn furry-btn-outline furry-btn-sm" onclick="addToCart({{ product.id }})">
🛒 Hinzufügen
</button>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<script>
function updateQuantity(itemId, change) {
fetch(`/update-cart-item/${itemId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
body: JSON.stringify({
change: change
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
function removeItem(itemId) {
if (confirm('Möchtest du dieses Produkt wirklich aus dem Warenkorb entfernen?')) {
fetch(`/remove-cart-item/${itemId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function clearCart() {
if (confirm('Möchtest du wirklich den gesamten Warenkorb leeren?')) {
fetch('/clear-cart/', {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function addToCart(productId) {
fetch(`/add-to-cart/${productId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
const alert = document.createElement('div');
alert.className = 'furry-alert furry-alert-success';
alert.innerHTML = `
<div class="furry-alert-icon"></div>
<div class="furry-alert-content">
<div class="furry-alert-title">Erfolg</div>
<div class="furry-alert-message">Produkt wurde zum Warenkorb hinzugefügt!</div>
</div>
`;
document.querySelector('main').insertBefore(alert, document.querySelector('main').firstChild);
setTimeout(() => alert.remove(), 3000);
}
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
{% endblock %}

View File

@ -1,88 +1,43 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% block title %}Checkout{% endblock %}
{% block content %}
<h2>Checkout</h2>
<div class="checkout-container">
<form class="furry-form checkout-form">
<div class="form-group">
<label for="address">Adresse</label>
<input type="text" id="address" name="address" required placeholder="Straße, Hausnummer">
<div class="form-hint">Bitte gib deine vollständige Adresse ein 🐾</div>
</div>
<div class="form-group">
<label for="city">Stadt</label>
<input type="text" id="city" name="city" required placeholder="Stadt">
<div class="form-hint">Stadt nicht vergessen!</div>
</div>
<div class="form-group">
<label for="zip">PLZ</label>
<input type="text" id="zip" name="zip" required placeholder="PLZ">
<div class="form-hint">Postleitzahl bitte 🐾</div>
</div>
<div class="form-group">
<label for="payment">Zahlungsart</label>
<select id="payment" name="payment" required>
<option value="">Bitte wählen…</option>
<option value="paypal">PayPal</option>
<option value="stripe">Kreditkarte (Stripe)</option>
</select>
<div class="form-hint">Wähle deine bevorzugte Zahlungsart</div>
</div>
<button type="submit" class="btn furry-btn">Jetzt kaufen</button>
</form>
<div class="checkout-summary">
<h3>Deine Bestellung</h3>
<ul>
{% for item in cart.items %}
<li>{{ item.product.name }} × {{ item.quantity }} <span>{{ item.product.price|floatformat:2 }} €</span></li>
{% endfor %}
</ul>
<div class="checkout-total">Gesamtsumme: <span>{{ cart.total_price|floatformat:2 }} €</span></div>
</div>
</div>
=======
{% extends 'base.html' %}
{% block title %}Checkout{% endblock %}
{% block content %}
<h2>Checkout</h2>
<div class="checkout-container">
<form class="furry-form checkout-form">
<div class="form-group">
<label for="address">Adresse</label>
<input type="text" id="address" name="address" required placeholder="Straße, Hausnummer">
<div class="form-hint">Bitte gib deine vollständige Adresse ein 🐾</div>
</div>
<div class="form-group">
<label for="city">Stadt</label>
<input type="text" id="city" name="city" required placeholder="Stadt">
<div class="form-hint">Stadt nicht vergessen!</div>
</div>
<div class="form-group">
<label for="zip">PLZ</label>
<input type="text" id="zip" name="zip" required placeholder="PLZ">
<div class="form-hint">Postleitzahl bitte 🐾</div>
</div>
<div class="form-group">
<label for="payment">Zahlungsart</label>
<select id="payment" name="payment" required>
<option value="">Bitte wählen…</option>
<option value="paypal">PayPal</option>
<option value="stripe">Kreditkarte (Stripe)</option>
</select>
<div class="form-hint">Wähle deine bevorzugte Zahlungsart</div>
</div>
<button type="submit" class="btn furry-btn">Jetzt kaufen</button>
</form>
<div class="checkout-summary">
<h3>Deine Bestellung</h3>
<ul>
{% for item in cart.items %}
<li>{{ item.product.name }} × {{ item.quantity }} <span>{{ item.product.price|floatformat:2 }} €</span></li>
{% endfor %}
</ul>
<div class="checkout-total">Gesamtsumme: <span>{{ cart.total_price|floatformat:2 }} €</span></div>
</div>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% block title %}Checkout{% endblock %}
{% block content %}
<h2>Checkout</h2>
<div class="checkout-container">
<form class="furry-form checkout-form">
<div class="form-group">
<label for="address">Adresse</label>
<input type="text" id="address" name="address" required placeholder="Straße, Hausnummer">
<div class="form-hint">Bitte gib deine vollständige Adresse ein 🐾</div>
</div>
<div class="form-group">
<label for="city">Stadt</label>
<input type="text" id="city" name="city" required placeholder="Stadt">
<div class="form-hint">Stadt nicht vergessen!</div>
</div>
<div class="form-group">
<label for="zip">PLZ</label>
<input type="text" id="zip" name="zip" required placeholder="PLZ">
<div class="form-hint">Postleitzahl bitte 🐾</div>
</div>
<div class="form-group">
<label for="payment">Zahlungsart</label>
<select id="payment" name="payment" required>
<option value="">Bitte wählen…</option>
<option value="paypal">PayPal</option>
<option value="stripe">Kreditkarte (Stripe)</option>
</select>
<div class="form-hint">Wähle deine bevorzugte Zahlungsart</div>
</div>
<button type="submit" class="btn furry-btn">Jetzt kaufen</button>
</form>
<div class="checkout-summary">
<h3>Deine Bestellung</h3>
<ul>
{% for item in cart.items %}
<li>{{ item.product.name }} × {{ item.quantity }} <span>{{ item.product.price|floatformat:2 }} €</span></li>
{% endfor %}
</ul>
<div class="checkout-total">Gesamtsumme: <span>{{ cart.total_price|floatformat:2 }} €</span></div>
</div>
</div>
{% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,68 +1,33 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% block title %}Nachricht gesendet - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card">
<div class="card-body">
<i class="bi bi-check-circle text-success" style="font-size: 4rem;"></i>
<h1 class="h3 mt-3">Vielen Dank für Ihre Nachricht!</h1>
<p class="lead">
Wir haben Ihre Anfrage erhalten und werden uns schnellstmöglich bei Ihnen melden.
</p>
<p class="text-muted">
Unsere durchschnittliche Antwortzeit beträgt 24-48 Stunden an Werktagen.
</p>
<hr>
<div class="mt-4">
<a href="{% url 'products:product_list' %}" class="btn btn-primary me-2">
<i class="bi bi-shop"></i> Zurück zum Shop
</a>
<a href="{% url 'products:faq' %}" class="btn btn-outline-primary">
<i class="bi bi-question-circle"></i> FAQ ansehen
</a>
</div>
</div>
</div>
</div>
</div>
</div>
=======
{% extends 'base.html' %}
{% block title %}Nachricht gesendet - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card">
<div class="card-body">
<i class="bi bi-check-circle text-success" style="font-size: 4rem;"></i>
<h1 class="h3 mt-3">Vielen Dank für Ihre Nachricht!</h1>
<p class="lead">
Wir haben Ihre Anfrage erhalten und werden uns schnellstmöglich bei Ihnen melden.
</p>
<p class="text-muted">
Unsere durchschnittliche Antwortzeit beträgt 24-48 Stunden an Werktagen.
</p>
<hr>
<div class="mt-4">
<a href="{% url 'products:product_list' %}" class="btn btn-primary me-2">
<i class="bi bi-shop"></i> Zurück zum Shop
</a>
<a href="{% url 'products:faq' %}" class="btn btn-outline-primary">
<i class="bi bi-question-circle"></i> FAQ ansehen
</a>
</div>
</div>
</div>
</div>
</div>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% block title %}Nachricht gesendet - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card">
<div class="card-body">
<i class="bi bi-check-circle text-success" style="font-size: 4rem;"></i>
<h1 class="h3 mt-3">Vielen Dank für Ihre Nachricht!</h1>
<p class="lead">
Wir haben Ihre Anfrage erhalten und werden uns schnellstmöglich bei Ihnen melden.
</p>
<p class="text-muted">
Unsere durchschnittliche Antwortzeit beträgt 24-48 Stunden an Werktagen.
</p>
<hr>
<div class="mt-4">
<a href="{% url 'products:product_list' %}" class="btn btn-primary me-2">
<i class="bi bi-shop"></i> Zurück zum Shop
</a>
<a href="{% url 'products:faq' %}" class="btn btn-outline-primary">
<i class="bi bi-question-circle"></i> FAQ ansehen
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,360 +1,179 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% block title %}Custom Order #{{ order.id }} - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row">
<!-- Order Details -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">Anfrage-Details</h5>
</div>
<div class="card-body">
<ul class="list-unstyled">
<li class="mb-2">
<strong>Status:</strong>
<span class="badge bg-{{ order.status }}">
{{ order.get_status_display }}
</span>
</li>
<li class="mb-2">
<strong>Character:</strong>
{{ order.character_name }}
</li>
<li class="mb-2">
<strong>Typ:</strong>
{{ order.get_fursuit_type_display }}
</li>
<li class="mb-2">
<strong>Stil:</strong>
{{ order.get_style_display }}
</li>
<li class="mb-2">
<strong>Budget:</strong>
{{ order.budget_range }}
</li>
{% if order.deadline_request %}
<li class="mb-2">
<strong>Gewünschter Termin:</strong>
{{ order.deadline_request }}
</li>
{% endif %}
<li class="mb-2">
<strong>Bestelldatum:</strong>
{{ order.created|date:"d.m.Y" }}
</li>
</ul>
{% if order.quoted_price %}
<div class="alert alert-info">
<h6 class="alert-heading">Angebot</h6>
<p class="mb-0">{{ order.quoted_price }} €</p>
</div>
{% endif %}
</div>
</div>
<!-- Character Details -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title mb-0">Character-Details</h5>
</div>
<div class="card-body">
<h6>Beschreibung</h6>
<p>{{ order.character_description }}</p>
{% if order.reference_images %}
<h6>Referenzbilder</h6>
<img data-src="{{ order.reference_images.url }}" class="img-fluid rounded furry-lazy-image" alt="Referenzbild">
{% endif %}
<h6 class="mt-3">Farbwünsche</h6>
<p>{{ order.color_preferences }}</p>
{% if order.special_requests %}
<h6>Besondere Wünsche</h6>
<p>{{ order.special_requests }}</p>
{% endif %}
</div>
</div>
</div>
<!-- Progress Timeline -->
<div class="col-md-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Fortschritt</h5>
{% if user.is_staff %}
<a href="{% url 'add_progress_update' order.id %}" class="btn btn-primary btn-sm">
<i class="bi bi-plus"></i> Update hinzufügen
</a>
{% endif %}
</div>
<div class="card-body">
{% if progress_updates %}
<div class="timeline">
{% for update in progress_updates %}
<div class="timeline-item">
<div class="timeline-marker {% if update.completed %}bg-success{% endif %}"></div>
<div class="timeline-content">
<h6 class="mb-2">
{{ update.get_stage_display }}
{% if update.completed %}
<i class="bi bi-check-circle-fill text-success"></i>
{% endif %}
</h6>
<p>{{ update.description }}</p>
{% if update.image %}
<img data-src="{{ update.image.url }}" class="img-fluid rounded mb-2 furry-lazy-image" alt="Fortschrittsbild">
{% endif %}
<small class="text-muted">
{{ update.created|date:"d.m.Y H:i" }}
</small>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-4">
<i class="bi bi-clock text-muted" style="font-size: 2rem;"></i>
<p class="mt-2">Noch keine Fortschritts-Updates vorhanden.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<style>
.timeline {
position: relative;
padding: 20px 0;
}
.timeline::before {
content: '';
position: absolute;
top: 0;
left: 15px;
height: 100%;
width: 2px;
background: #e9ecef;
}
.timeline-item {
position: relative;
margin-bottom: 30px;
padding-left: 40px;
}
.timeline-marker {
position: absolute;
left: 7px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #dee2e6;
border: 2px solid #fff;
box-shadow: 0 0 0 2px #dee2e6;
}
.timeline-content {
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
}
.badge.bg-pending { background-color: #ffc107; }
.badge.bg-quoted { background-color: #17a2b8; }
.badge.bg-approved { background-color: #28a745; }
.badge.bg-in_progress { background-color: #007bff; }
.badge.bg-ready { background-color: #20c997; }
.badge.bg-shipped { background-color: #6f42c1; }
.badge.bg-completed { background-color: #28a745; }
.badge.bg-cancelled { background-color: #dc3545; }
</style>
=======
{% extends 'base.html' %}
{% block title %}Custom Order #{{ order.id }} - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row">
<!-- Order Details -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">Anfrage-Details</h5>
</div>
<div class="card-body">
<ul class="list-unstyled">
<li class="mb-2">
<strong>Status:</strong>
<span class="badge bg-{{ order.status }}">
{{ order.get_status_display }}
</span>
</li>
<li class="mb-2">
<strong>Character:</strong>
{{ order.character_name }}
</li>
<li class="mb-2">
<strong>Typ:</strong>
{{ order.get_fursuit_type_display }}
</li>
<li class="mb-2">
<strong>Stil:</strong>
{{ order.get_style_display }}
</li>
<li class="mb-2">
<strong>Budget:</strong>
{{ order.budget_range }}
</li>
{% if order.deadline_request %}
<li class="mb-2">
<strong>Gewünschter Termin:</strong>
{{ order.deadline_request }}
</li>
{% endif %}
<li class="mb-2">
<strong>Bestelldatum:</strong>
{{ order.created|date:"d.m.Y" }}
</li>
</ul>
{% if order.quoted_price %}
<div class="alert alert-info">
<h6 class="alert-heading">Angebot</h6>
<p class="mb-0">{{ order.quoted_price }} €</p>
</div>
{% endif %}
</div>
</div>
<!-- Character Details -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title mb-0">Character-Details</h5>
</div>
<div class="card-body">
<h6>Beschreibung</h6>
<p>{{ order.character_description }}</p>
{% if order.reference_images %}
<h6>Referenzbilder</h6>
<img data-src="{{ order.reference_images.url }}" class="img-fluid rounded furry-lazy-image" alt="Referenzbild">
{% endif %}
<h6 class="mt-3">Farbwünsche</h6>
<p>{{ order.color_preferences }}</p>
{% if order.special_requests %}
<h6>Besondere Wünsche</h6>
<p>{{ order.special_requests }}</p>
{% endif %}
</div>
</div>
</div>
<!-- Progress Timeline -->
<div class="col-md-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Fortschritt</h5>
{% if user.is_staff %}
<a href="{% url 'add_progress_update' order.id %}" class="btn btn-primary btn-sm">
<i class="bi bi-plus"></i> Update hinzufügen
</a>
{% endif %}
</div>
<div class="card-body">
{% if progress_updates %}
<div class="timeline">
{% for update in progress_updates %}
<div class="timeline-item">
<div class="timeline-marker {% if update.completed %}bg-success{% endif %}"></div>
<div class="timeline-content">
<h6 class="mb-2">
{{ update.get_stage_display }}
{% if update.completed %}
<i class="bi bi-check-circle-fill text-success"></i>
{% endif %}
</h6>
<p>{{ update.description }}</p>
{% if update.image %}
<img data-src="{{ update.image.url }}" class="img-fluid rounded mb-2 furry-lazy-image" alt="Fortschrittsbild">
{% endif %}
<small class="text-muted">
{{ update.created|date:"d.m.Y H:i" }}
</small>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-4">
<i class="bi bi-clock text-muted" style="font-size: 2rem;"></i>
<p class="mt-2">Noch keine Fortschritts-Updates vorhanden.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<style>
.timeline {
position: relative;
padding: 20px 0;
}
.timeline::before {
content: '';
position: absolute;
top: 0;
left: 15px;
height: 100%;
width: 2px;
background: #e9ecef;
}
.timeline-item {
position: relative;
margin-bottom: 30px;
padding-left: 40px;
}
.timeline-marker {
position: absolute;
left: 7px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #dee2e6;
border: 2px solid #fff;
box-shadow: 0 0 0 2px #dee2e6;
}
.timeline-content {
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
}
.badge.bg-pending { background-color: #ffc107; }
.badge.bg-quoted { background-color: #17a2b8; }
.badge.bg-approved { background-color: #28a745; }
.badge.bg-in_progress { background-color: #007bff; }
.badge.bg-ready { background-color: #20c997; }
.badge.bg-shipped { background-color: #6f42c1; }
.badge.bg-completed { background-color: #28a745; }
.badge.bg-cancelled { background-color: #dc3545; }
</style>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% block title %}Custom Order #{{ order.id }} - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row">
<!-- Order Details -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">Anfrage-Details</h5>
</div>
<div class="card-body">
<ul class="list-unstyled">
<li class="mb-2">
<strong>Status:</strong>
<span class="badge bg-{{ order.status }}">
{{ order.get_status_display }}
</span>
</li>
<li class="mb-2">
<strong>Character:</strong>
{{ order.character_name }}
</li>
<li class="mb-2">
<strong>Typ:</strong>
{{ order.get_fursuit_type_display }}
</li>
<li class="mb-2">
<strong>Stil:</strong>
{{ order.get_style_display }}
</li>
<li class="mb-2">
<strong>Budget:</strong>
{{ order.budget_range }}
</li>
{% if order.deadline_request %}
<li class="mb-2">
<strong>Gewünschter Termin:</strong>
{{ order.deadline_request }}
</li>
{% endif %}
<li class="mb-2">
<strong>Bestelldatum:</strong>
{{ order.created|date:"d.m.Y" }}
</li>
</ul>
{% if order.quoted_price %}
<div class="alert alert-info">
<h6 class="alert-heading">Angebot</h6>
<p class="mb-0">{{ order.quoted_price }} €</p>
</div>
{% endif %}
</div>
</div>
<!-- Character Details -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title mb-0">Character-Details</h5>
</div>
<div class="card-body">
<h6>Beschreibung</h6>
<p>{{ order.character_description }}</p>
{% if order.reference_images %}
<h6>Referenzbilder</h6>
<img data-src="{{ order.reference_images.url }}" class="img-fluid rounded furry-lazy-image" alt="Referenzbild">
{% endif %}
<h6 class="mt-3">Farbwünsche</h6>
<p>{{ order.color_preferences }}</p>
{% if order.special_requests %}
<h6>Besondere Wünsche</h6>
<p>{{ order.special_requests }}</p>
{% endif %}
</div>
</div>
</div>
<!-- Progress Timeline -->
<div class="col-md-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Fortschritt</h5>
{% if user.is_staff %}
<a href="{% url 'add_progress_update' order.id %}" class="btn btn-primary btn-sm">
<i class="bi bi-plus"></i> Update hinzufügen
</a>
{% endif %}
</div>
<div class="card-body">
{% if progress_updates %}
<div class="timeline">
{% for update in progress_updates %}
<div class="timeline-item">
<div class="timeline-marker {% if update.completed %}bg-success{% endif %}"></div>
<div class="timeline-content">
<h6 class="mb-2">
{{ update.get_stage_display }}
{% if update.completed %}
<i class="bi bi-check-circle-fill text-success"></i>
{% endif %}
</h6>
<p>{{ update.description }}</p>
{% if update.image %}
<img data-src="{{ update.image.url }}" class="img-fluid rounded mb-2 furry-lazy-image" alt="Fortschrittsbild">
{% endif %}
<small class="text-muted">
{{ update.created|date:"d.m.Y H:i" }}
</small>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-4">
<i class="bi bi-clock text-muted" style="font-size: 2rem;"></i>
<p class="mt-2">Noch keine Fortschritts-Updates vorhanden.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<style>
.timeline {
position: relative;
padding: 20px 0;
}
.timeline::before {
content: '';
position: absolute;
top: 0;
left: 15px;
height: 100%;
width: 2px;
background: #e9ecef;
}
.timeline-item {
position: relative;
margin-bottom: 30px;
padding-left: 40px;
}
.timeline-marker {
position: absolute;
left: 7px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #dee2e6;
border: 2px solid #fff;
box-shadow: 0 0 0 2px #dee2e6;
}
.timeline-content {
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
}
.badge.bg-pending { background-color: #ffc107; }
.badge.bg-quoted { background-color: #17a2b8; }
.badge.bg-approved { background-color: #28a745; }
.badge.bg-in_progress { background-color: #007bff; }
.badge.bg-ready { background-color: #20c997; }
.badge.bg-shipped { background-color: #6f42c1; }
.badge.bg-completed { background-color: #28a745; }
.badge.bg-cancelled { background-color: #dc3545; }
</style>
{% endblock %}

View File

@ -1,98 +1,48 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% block title %}Anfrage erfolgreich - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card">
<div class="card-body">
<i class="bi bi-check-circle-fill text-success" style="font-size: 4rem;"></i>
<h1 class="h3 mt-3">Ihre Fursuit-Anfrage wurde erfolgreich übermittelt!</h1>
<p class="lead mb-4">
Vielen Dank für Ihr Interesse an einem Custom Fursuit.
</p>
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Ihre Anfrage-Details</h5>
<ul class="list-unstyled text-start">
<li><strong>Anfrage-ID:</strong> #{{ order.id }}</li>
<li><strong>Character:</strong> {{ order.character_name }}</li>
<li><strong>Typ:</strong> {{ order.get_fursuit_type_display }}</li>
<li><strong>Stil:</strong> {{ order.get_style_display }}</li>
<li><strong>Status:</strong> {{ order.get_status_display }}</li>
</ul>
</div>
</div>
<div class="alert alert-info" role="alert">
<i class="bi bi-info-circle"></i>
Wir werden Ihre Anfrage prüfen und uns innerhalb von 2-3 Werktagen mit einem detaillierten Angebot bei Ihnen melden.
</div>
<div class="mt-4">
<a href="{% url 'custom_order_detail' order.id %}" class="btn btn-primary me-2">
<i class="bi bi-eye"></i> Anfrage ansehen
</a>
<a href="{% url 'gallery' %}" class="btn btn-outline-primary">
<i class="bi bi-images"></i> Galerie ansehen
</a>
</div>
</div>
</div>
</div>
</div>
</div>
=======
{% extends 'base.html' %}
{% block title %}Anfrage erfolgreich - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card">
<div class="card-body">
<i class="bi bi-check-circle-fill text-success" style="font-size: 4rem;"></i>
<h1 class="h3 mt-3">Ihre Fursuit-Anfrage wurde erfolgreich übermittelt!</h1>
<p class="lead mb-4">
Vielen Dank für Ihr Interesse an einem Custom Fursuit.
</p>
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Ihre Anfrage-Details</h5>
<ul class="list-unstyled text-start">
<li><strong>Anfrage-ID:</strong> #{{ order.id }}</li>
<li><strong>Character:</strong> {{ order.character_name }}</li>
<li><strong>Typ:</strong> {{ order.get_fursuit_type_display }}</li>
<li><strong>Stil:</strong> {{ order.get_style_display }}</li>
<li><strong>Status:</strong> {{ order.get_status_display }}</li>
</ul>
</div>
</div>
<div class="alert alert-info" role="alert">
<i class="bi bi-info-circle"></i>
Wir werden Ihre Anfrage prüfen und uns innerhalb von 2-3 Werktagen mit einem detaillierten Angebot bei Ihnen melden.
</div>
<div class="mt-4">
<a href="{% url 'custom_order_detail' order.id %}" class="btn btn-primary me-2">
<i class="bi bi-eye"></i> Anfrage ansehen
</a>
<a href="{% url 'gallery' %}" class="btn btn-outline-primary">
<i class="bi bi-images"></i> Galerie ansehen
</a>
</div>
</div>
</div>
</div>
</div>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% block title %}Anfrage erfolgreich - {{ block.super }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card">
<div class="card-body">
<i class="bi bi-check-circle-fill text-success" style="font-size: 4rem;"></i>
<h1 class="h3 mt-3">Ihre Fursuit-Anfrage wurde erfolgreich übermittelt!</h1>
<p class="lead mb-4">
Vielen Dank für Ihr Interesse an einem Custom Fursuit.
</p>
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Ihre Anfrage-Details</h5>
<ul class="list-unstyled text-start">
<li><strong>Anfrage-ID:</strong> #{{ order.id }}</li>
<li><strong>Character:</strong> {{ order.character_name }}</li>
<li><strong>Typ:</strong> {{ order.get_fursuit_type_display }}</li>
<li><strong>Stil:</strong> {{ order.get_style_display }}</li>
<li><strong>Status:</strong> {{ order.get_status_display }}</li>
</ul>
</div>
</div>
<div class="alert alert-info" role="alert">
<i class="bi bi-info-circle"></i>
Wir werden Ihre Anfrage prüfen und uns innerhalb von 2-3 Werktagen mit einem detaillierten Angebot bei Ihnen melden.
</div>
<div class="mt-4">
<a href="{% url 'custom_order_detail' order.id %}" class="btn btn-primary me-2">
<i class="bi bi-eye"></i> Anfrage ansehen
</a>
<a href="{% url 'gallery' %}" class="btn btn-outline-primary">
<i class="bi bi-images"></i> Galerie ansehen
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +1,21 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% block title %}Bestellung erfolgreich{% endblock %}
{% block content %}
<div class="order-confirmation furry-404">
<div class="furry-404-emoji">🎉</div>
<h1>Danke für deine Bestellung!</h1>
<p>Wir haben deine Bestellung erhalten und unsere Furry-Freunde machen sich sofort an die Arbeit 🐾</p>
<div class="order-summary">
<h3>Deine Bestellung:</h3>
<ul>
{% for item in order.items.all %}
<li>{{ item.product_name }} × {{ item.quantity }} <span>{{ item.price|floatformat:2 }} €</span></li>
{% endfor %}
</ul>
<div class="checkout-total">Gesamtsumme: <span>{{ order.total_amount|floatformat:2 }} €</span></div>
</div>
<a href="/" class="btn furry-btn">Zurück zur Startseite</a>
</div>
=======
{% extends 'base.html' %}
{% block title %}Bestellung erfolgreich{% endblock %}
{% block content %}
<div class="order-confirmation furry-404">
<div class="furry-404-emoji">🎉</div>
<h1>Danke für deine Bestellung!</h1>
<p>Wir haben deine Bestellung erhalten und unsere Furry-Freunde machen sich sofort an die Arbeit 🐾</p>
<div class="order-summary">
<h3>Deine Bestellung:</h3>
<ul>
{% for item in order.items.all %}
<li>{{ item.product_name }} × {{ item.quantity }} <span>{{ item.price|floatformat:2 }} €</span></li>
{% endfor %}
</ul>
<div class="checkout-total">Gesamtsumme: <span>{{ order.total_amount|floatformat:2 }} €</span></div>
</div>
<a href="/" class="btn furry-btn">Zurück zur Startseite</a>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% block title %}Bestellung erfolgreich{% endblock %}
{% block content %}
<div class="order-confirmation furry-404">
<div class="furry-404-emoji">🎉</div>
<h1>Danke für deine Bestellung!</h1>
<p>Wir haben deine Bestellung erhalten und unsere Furry-Freunde machen sich sofort an die Arbeit 🐾</p>
<div class="order-summary">
<h3>Deine Bestellung:</h3>
<ul>
{% for item in order.items.all %}
<li>{{ item.product_name }} × {{ item.quantity }} <span>{{ item.price|floatformat:2 }} €</span></li>
{% endfor %}
</ul>
<div class="checkout-total">Gesamtsumme: <span>{{ order.total_amount|floatformat:2 }} €</span></div>
</div>
<a href="/" class="btn furry-btn">Zurück zur Startseite</a>
</div>
{% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,394 +1,196 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% load static %}
{% block title %}Produkte - Kasico Fursuit Shop{% endblock %}
{% block content %}
<div class="furry-card">
<div class="furry-card-header">
<div>
<h1 class="furry-card-title">🐾 Unsere Fursuits</h1>
<p class="furry-card-subtitle">Entdecke unsere handgefertigten Fursuits und Accessoires</p>
</div>
<div class="furry-product-badge">Neu</div>
</div>
<!-- Filter Section -->
<div class="furry-card" style="margin-bottom: 2rem;">
<h3>🔍 Filter & Suche</h3>
<form method="get" class="furry-form-group">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
<div>
<label class="furry-label">Fursuit Typ</label>
<select name="fursuit_type" class="furry-input furry-select">
<option value="">Alle Typen</option>
<option value="partial">Partial</option>
<option value="fullsuit">Fullsuit</option>
<option value="head_only">Head Only</option>
<option value="paws">Paws</option>
<option value="tail">Tail</option>
</select>
</div>
<div>
<label class="furry-label">Stil</label>
<select name="style" class="furry-input furry-select">
<option value="">Alle Stile</option>
<option value="toony">Toony</option>
<option value="semi_realistic">Semi-Realistic</option>
<option value="realistic">Realistic</option>
<option value="anime">Anime</option>
<option value="chibi">Chibi</option>
</select>
</div>
<div>
<label class="furry-label">Preisbereich</label>
<select name="price_range" class="furry-input furry-select">
<option value="">Alle Preise</option>
<option value="0-500">0 - 500€</option>
<option value="500-1000">500 - 1000€</option>
<option value="1000-2000">1000 - 2000€</option>
<option value="2000+">2000€+</option>
</select>
</div>
<div>
<label class="furry-label">Sortierung</label>
<select name="sort" class="furry-input furry-select">
<option value="newest">Neueste zuerst</option>
<option value="price_low">Preis: Niedrig zu Hoch</option>
<option value="price_high">Preis: Hoch zu Niedrig</option>
<option value="name">Name A-Z</option>
</select>
</div>
</div>
<div style="margin-top: 1rem; display: flex; gap: 1rem; flex-wrap: wrap;">
<button type="submit" class="furry-btn furry-btn-primary">
🔍 Filter anwenden
</button>
<a href="{% url 'products:product_list' %}" class="furry-btn furry-btn-ghost">
🗑️ Filter zurücksetzen
</a>
</div>
</form>
</div>
<!-- Products Grid -->
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem;">
{% for product in products %}
<div class="furry-card furry-product-card">
{% if product.image %}
<img data-src="{{ product.image.url }}" alt="{{ product.name }}" class="furry-card-image furry-lazy-image">
{% else %}
<div class="furry-card-image" style="background: linear-gradient(45deg, var(--furry-primary), var(--furry-secondary)); display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">
🐾
</div>
{% endif %}
<div class="furry-card-header">
<div>
<h3 class="furry-card-title">{{ product.name }}</h3>
<p class="furry-card-subtitle">{{ product.get_fursuit_type_display }} • {{ product.get_style_display }}</p>
</div>
{% if product.featured %}
<div class="furry-product-badge">Featured</div>
{% endif %}
</div>
<div class="furry-card-content">
<p>{{ product.description|truncatewords:20 }}</p>
<div class="furry-product-price">{{ product.price }}€</div>
</div>
<div class="furry-card-footer">
<a href="{% url 'products:product_detail' product.id %}" class="furry-btn furry-btn-primary furry-btn-sm">
👁️ Details
</a>
<button class="furry-btn furry-btn-outline furry-btn-sm" onclick="addToCart({{ product.id }})">
🛒 In Warenkorb
</button>
<button class="furry-btn furry-btn-ghost furry-btn-sm" onclick="furryAjax.addToWishlist({{ product.id }})">
❤️ Zur Wunschliste
</button>
</div>
</div>
{% empty %}
<div class="furry-card" style="grid-column: 1 / -1; text-align: center; padding: 3rem;">
<div style="font-size: 4rem; margin-bottom: 1rem;">🐾</div>
<h3>Keine Produkte gefunden</h3>
<p>Versuche andere Filter-Einstellungen oder schaue später wieder vorbei!</p>
<a href="{% url 'products:product_list' %}" class="furry-btn furry-btn-primary">
Alle Produkte anzeigen
</a>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if products.has_other_pages %}
<div class="furry-card" style="margin-top: 2rem; text-align: center;">
<div style="display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap;">
{% if products.has_previous %}
<a href="?page={{ products.previous_page_number }}" class="furry-btn furry-btn-ghost furry-btn-sm">
← Zurück
</a>
{% endif %}
{% for num in products.paginator.page_range %}
{% if products.number == num %}
<span class="furry-btn furry-btn-primary furry-btn-sm">{{ num }}</span>
{% elif num > products.number|add:'-3' and num < products.number|add:'3' %}
<a href="?page={{ num }}" class="furry-btn furry-btn-ghost furry-btn-sm">{{ num }}</a>
{% endif %}
{% endfor %}
{% if products.has_next %}
<a href="?page={{ products.next_page_number }}" class="furry-btn furry-btn-ghost furry-btn-sm">
Weiter →
</a>
{% endif %}
</div>
</div>
{% endif %}
</div>
<!-- Success/Error Messages -->
{% if messages %}
{% for message in messages %}
<div class="furry-alert furry-alert-{{ message.tags }}">
<div class="furry-alert-icon">
{% if message.tags == 'success' %}✅
{% elif message.tags == 'error' %}❌
{% elif message.tags == 'warning' %}⚠️
{% else %}{% endif %}
</div>
<div class="furry-alert-content">
<div class="furry-alert-title">{{ message.tags|title }}</div>
<div class="furry-alert-message">{{ message }}</div>
</div>
</div>
{% endfor %}
{% endif %}
<div id="cart-feedback" class="cart-added-feedback">✔️ Zum Warenkorb hinzugefügt!</div>
<div id="furry-spinner" class="furry-spinner" style="display:none;">
<div></div><div></div><div></div>
</div>
<script>
function showCartFeedback() {
const feedback = document.getElementById('cart-feedback');
feedback.classList.add('show');
setTimeout(() => feedback.classList.remove('show'), 1500);
}
function showSpinner(show) {
document.getElementById('furry-spinner').style.display = show ? 'inline-block' : 'none';
}
window.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.add-to-cart-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
showSpinner(true);
setTimeout(() => {
showSpinner(false);
showCartFeedback();
}, 1000);
});
});
});
</script>
=======
{% extends 'base.html' %}
{% load static %}
{% block title %}Produkte - Kasico Fursuit Shop{% endblock %}
{% block content %}
<div class="furry-card">
<div class="furry-card-header">
<div>
<h1 class="furry-card-title">🐾 Unsere Fursuits</h1>
<p class="furry-card-subtitle">Entdecke unsere handgefertigten Fursuits und Accessoires</p>
</div>
<div class="furry-product-badge">Neu</div>
</div>
<!-- Filter Section -->
<div class="furry-card" style="margin-bottom: 2rem;">
<h3>🔍 Filter & Suche</h3>
<form method="get" class="furry-form-group">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
<div>
<label class="furry-label">Fursuit Typ</label>
<select name="fursuit_type" class="furry-input furry-select">
<option value="">Alle Typen</option>
<option value="partial">Partial</option>
<option value="fullsuit">Fullsuit</option>
<option value="head_only">Head Only</option>
<option value="paws">Paws</option>
<option value="tail">Tail</option>
</select>
</div>
<div>
<label class="furry-label">Stil</label>
<select name="style" class="furry-input furry-select">
<option value="">Alle Stile</option>
<option value="toony">Toony</option>
<option value="semi_realistic">Semi-Realistic</option>
<option value="realistic">Realistic</option>
<option value="anime">Anime</option>
<option value="chibi">Chibi</option>
</select>
</div>
<div>
<label class="furry-label">Preisbereich</label>
<select name="price_range" class="furry-input furry-select">
<option value="">Alle Preise</option>
<option value="0-500">0 - 500€</option>
<option value="500-1000">500 - 1000€</option>
<option value="1000-2000">1000 - 2000€</option>
<option value="2000+">2000€+</option>
</select>
</div>
<div>
<label class="furry-label">Sortierung</label>
<select name="sort" class="furry-input furry-select">
<option value="newest">Neueste zuerst</option>
<option value="price_low">Preis: Niedrig zu Hoch</option>
<option value="price_high">Preis: Hoch zu Niedrig</option>
<option value="name">Name A-Z</option>
</select>
</div>
</div>
<div style="margin-top: 1rem; display: flex; gap: 1rem; flex-wrap: wrap;">
<button type="submit" class="furry-btn furry-btn-primary">
🔍 Filter anwenden
</button>
<a href="{% url 'products:product_list' %}" class="furry-btn furry-btn-ghost">
🗑️ Filter zurücksetzen
</a>
</div>
</form>
</div>
<!-- Products Grid -->
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem;">
{% for product in products %}
<div class="furry-card furry-product-card">
{% if product.image %}
<img data-src="{{ product.image.url }}" alt="{{ product.name }}" class="furry-card-image furry-lazy-image">
{% else %}
<div class="furry-card-image" style="background: linear-gradient(45deg, var(--furry-primary), var(--furry-secondary)); display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">
🐾
</div>
{% endif %}
<div class="furry-card-header">
<div>
<h3 class="furry-card-title">{{ product.name }}</h3>
<p class="furry-card-subtitle">{{ product.get_fursuit_type_display }} • {{ product.get_style_display }}</p>
</div>
{% if product.featured %}
<div class="furry-product-badge">Featured</div>
{% endif %}
</div>
<div class="furry-card-content">
<p>{{ product.description|truncatewords:20 }}</p>
<div class="furry-product-price">{{ product.price }}€</div>
</div>
<div class="furry-card-footer">
<a href="{% url 'products:product_detail' product.id %}" class="furry-btn furry-btn-primary furry-btn-sm">
👁️ Details
</a>
<button class="furry-btn furry-btn-outline furry-btn-sm" onclick="addToCart({{ product.id }})">
🛒 In Warenkorb
</button>
<button class="furry-btn furry-btn-ghost furry-btn-sm" onclick="furryAjax.addToWishlist({{ product.id }})">
❤️ Zur Wunschliste
</button>
</div>
</div>
{% empty %}
<div class="furry-card" style="grid-column: 1 / -1; text-align: center; padding: 3rem;">
<div style="font-size: 4rem; margin-bottom: 1rem;">🐾</div>
<h3>Keine Produkte gefunden</h3>
<p>Versuche andere Filter-Einstellungen oder schaue später wieder vorbei!</p>
<a href="{% url 'products:product_list' %}" class="furry-btn furry-btn-primary">
Alle Produkte anzeigen
</a>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if products.has_other_pages %}
<div class="furry-card" style="margin-top: 2rem; text-align: center;">
<div style="display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap;">
{% if products.has_previous %}
<a href="?page={{ products.previous_page_number }}" class="furry-btn furry-btn-ghost furry-btn-sm">
← Zurück
</a>
{% endif %}
{% for num in products.paginator.page_range %}
{% if products.number == num %}
<span class="furry-btn furry-btn-primary furry-btn-sm">{{ num }}</span>
{% elif num > products.number|add:'-3' and num < products.number|add:'3' %}
<a href="?page={{ num }}" class="furry-btn furry-btn-ghost furry-btn-sm">{{ num }}</a>
{% endif %}
{% endfor %}
{% if products.has_next %}
<a href="?page={{ products.next_page_number }}" class="furry-btn furry-btn-ghost furry-btn-sm">
Weiter →
</a>
{% endif %}
</div>
</div>
{% endif %}
</div>
<!-- Success/Error Messages -->
{% if messages %}
{% for message in messages %}
<div class="furry-alert furry-alert-{{ message.tags }}">
<div class="furry-alert-icon">
{% if message.tags == 'success' %}✅
{% elif message.tags == 'error' %}❌
{% elif message.tags == 'warning' %}⚠️
{% else %}{% endif %}
</div>
<div class="furry-alert-content">
<div class="furry-alert-title">{{ message.tags|title }}</div>
<div class="furry-alert-message">{{ message }}</div>
</div>
</div>
{% endfor %}
{% endif %}
<div id="cart-feedback" class="cart-added-feedback">✔️ Zum Warenkorb hinzugefügt!</div>
<div id="furry-spinner" class="furry-spinner" style="display:none;">
<div></div><div></div><div></div>
</div>
<script>
function showCartFeedback() {
const feedback = document.getElementById('cart-feedback');
feedback.classList.add('show');
setTimeout(() => feedback.classList.remove('show'), 1500);
}
function showSpinner(show) {
document.getElementById('furry-spinner').style.display = show ? 'inline-block' : 'none';
}
window.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.add-to-cart-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
showSpinner(true);
setTimeout(() => {
showSpinner(false);
showCartFeedback();
}, 1000);
});
});
});
</script>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% load static %}
{% block title %}Produkte - Kasico Fursuit Shop{% endblock %}
{% block content %}
<div class="furry-card">
<div class="furry-card-header">
<div>
<h1 class="furry-card-title">🐾 Unsere Fursuits</h1>
<p class="furry-card-subtitle">Entdecke unsere handgefertigten Fursuits und Accessoires</p>
</div>
<div class="furry-product-badge">Neu</div>
</div>
<!-- Filter Section -->
<div class="furry-card" style="margin-bottom: 2rem;">
<h3>🔍 Filter & Suche</h3>
<form method="get" class="furry-form-group">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
<div>
<label class="furry-label">Fursuit Typ</label>
<select name="fursuit_type" class="furry-input furry-select">
<option value="">Alle Typen</option>
<option value="partial">Partial</option>
<option value="fullsuit">Fullsuit</option>
<option value="head_only">Head Only</option>
<option value="paws">Paws</option>
<option value="tail">Tail</option>
</select>
</div>
<div>
<label class="furry-label">Stil</label>
<select name="style" class="furry-input furry-select">
<option value="">Alle Stile</option>
<option value="toony">Toony</option>
<option value="semi_realistic">Semi-Realistic</option>
<option value="realistic">Realistic</option>
<option value="anime">Anime</option>
<option value="chibi">Chibi</option>
</select>
</div>
<div>
<label class="furry-label">Preisbereich</label>
<select name="price_range" class="furry-input furry-select">
<option value="">Alle Preise</option>
<option value="0-500">0 - 500€</option>
<option value="500-1000">500 - 1000€</option>
<option value="1000-2000">1000 - 2000€</option>
<option value="2000+">2000€+</option>
</select>
</div>
<div>
<label class="furry-label">Sortierung</label>
<select name="sort" class="furry-input furry-select">
<option value="newest">Neueste zuerst</option>
<option value="price_low">Preis: Niedrig zu Hoch</option>
<option value="price_high">Preis: Hoch zu Niedrig</option>
<option value="name">Name A-Z</option>
</select>
</div>
</div>
<div style="margin-top: 1rem; display: flex; gap: 1rem; flex-wrap: wrap;">
<button type="submit" class="furry-btn furry-btn-primary">
🔍 Filter anwenden
</button>
<a href="{% url 'products:product_list' %}" class="furry-btn furry-btn-ghost">
🗑️ Filter zurücksetzen
</a>
</div>
</form>
</div>
<!-- Products Grid -->
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem;">
{% for product in products %}
<div class="furry-card furry-product-card">
{% if product.image %}
<img data-src="{{ product.image.url }}" alt="{{ product.name }}" class="furry-card-image furry-lazy-image">
{% else %}
<div class="furry-card-image" style="background: linear-gradient(45deg, var(--furry-primary), var(--furry-secondary)); display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">
🐾
</div>
{% endif %}
<div class="furry-card-header">
<div>
<h3 class="furry-card-title">{{ product.name }}</h3>
<p class="furry-card-subtitle">{{ product.get_fursuit_type_display }} • {{ product.get_style_display }}</p>
</div>
{% if product.featured %}
<div class="furry-product-badge">Featured</div>
{% endif %}
</div>
<div class="furry-card-content">
<p>{{ product.description|truncatewords:20 }}</p>
<div class="furry-product-price">{{ product.price }}€</div>
</div>
<div class="furry-card-footer">
<a href="{% url 'products:product_detail' product.id %}" class="furry-btn furry-btn-primary furry-btn-sm">
👁️ Details
</a>
<button class="furry-btn furry-btn-outline furry-btn-sm" onclick="addToCart({{ product.id }})">
🛒 In Warenkorb
</button>
<button class="furry-btn furry-btn-ghost furry-btn-sm" onclick="furryAjax.addToWishlist({{ product.id }})">
❤️ Zur Wunschliste
</button>
</div>
</div>
{% empty %}
<div class="furry-card" style="grid-column: 1 / -1; text-align: center; padding: 3rem;">
<div style="font-size: 4rem; margin-bottom: 1rem;">🐾</div>
<h3>Keine Produkte gefunden</h3>
<p>Versuche andere Filter-Einstellungen oder schaue später wieder vorbei!</p>
<a href="{% url 'products:product_list' %}" class="furry-btn furry-btn-primary">
Alle Produkte anzeigen
</a>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if products.has_other_pages %}
<div class="furry-card" style="margin-top: 2rem; text-align: center;">
<div style="display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap;">
{% if products.has_previous %}
<a href="?page={{ products.previous_page_number }}" class="furry-btn furry-btn-ghost furry-btn-sm">
← Zurück
</a>
{% endif %}
{% for num in products.paginator.page_range %}
{% if products.number == num %}
<span class="furry-btn furry-btn-primary furry-btn-sm">{{ num }}</span>
{% elif num > products.number|add:'-3' and num < products.number|add:'3' %}
<a href="?page={{ num }}" class="furry-btn furry-btn-ghost furry-btn-sm">{{ num }}</a>
{% endif %}
{% endfor %}
{% if products.has_next %}
<a href="?page={{ products.next_page_number }}" class="furry-btn furry-btn-ghost furry-btn-sm">
Weiter →
</a>
{% endif %}
</div>
</div>
{% endif %}
</div>
<!-- Success/Error Messages -->
{% if messages %}
{% for message in messages %}
<div class="furry-alert furry-alert-{{ message.tags }}">
<div class="furry-alert-icon">
{% if message.tags == 'success' %}✅
{% elif message.tags == 'error' %}❌
{% elif message.tags == 'warning' %}⚠️
{% else %}{% endif %}
</div>
<div class="furry-alert-content">
<div class="furry-alert-title">{{ message.tags|title }}</div>
<div class="furry-alert-message">{{ message }}</div>
</div>
</div>
{% endfor %}
{% endif %}
<div id="cart-feedback" class="cart-added-feedback">✔️ Zum Warenkorb hinzugefügt!</div>
<div id="furry-spinner" class="furry-spinner" style="display:none;">
<div></div><div></div><div></div>
</div>
<script>
function showCartFeedback() {
const feedback = document.getElementById('cart-feedback');
feedback.classList.add('show');
setTimeout(() => feedback.classList.remove('show'), 1500);
}
function showSpinner(show) {
document.getElementById('furry-spinner').style.display = show ? 'inline-block' : 'none';
}
window.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.add-to-cart-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
showSpinner(true);
setTimeout(() => {
showSpinner(false);
showCartFeedback();
}, 1000);
});
});
});
</script>
{% endblock %}

View File

@ -1,58 +1,28 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% load i18n %}
{% block title %}Profil{% endblock %}
{% block content %}
<div class="profile-card">
<h2>Willkommen, {{ user.username }}!</h2>
<div class="profile-info">
<p><strong>E-Mail:</strong> {{ user.email }}</p>
<p><strong>Mitglied seit:</strong> {{ user.date_joined|date:"d.m.Y" }}</p>
</div>
<div class="profile-orders">
<h3>Bestellungen</h3>
{% if orders %}
<ul>
{% for order in orders %}
<li>Bestellung #{{ order.id }} vom {{ order.created|date:"d.m.Y" }} {{ order.total_amount|floatformat:2 }} €</li>
{% endfor %}
</ul>
{% else %}
<p>Du hast noch keine Bestellungen.</p>
{% endif %}
</div>
<a href="/products/wishlist/" class="btn furry-btn-outline">Wunschliste ansehen</a>
<a href="/logout/" class="btn furry-btn-secondary">Logout</a>
</div>
=======
{% extends 'base.html' %}
{% load i18n %}
{% block title %}Profil{% endblock %}
{% block content %}
<div class="profile-card">
<h2>Willkommen, {{ user.username }}!</h2>
<div class="profile-info">
<p><strong>E-Mail:</strong> {{ user.email }}</p>
<p><strong>Mitglied seit:</strong> {{ user.date_joined|date:"d.m.Y" }}</p>
</div>
<div class="profile-orders">
<h3>Bestellungen</h3>
{% if orders %}
<ul>
{% for order in orders %}
<li>Bestellung #{{ order.id }} vom {{ order.created|date:"d.m.Y" }} {{ order.total_amount|floatformat:2 }} €</li>
{% endfor %}
</ul>
{% else %}
<p>Du hast noch keine Bestellungen.</p>
{% endif %}
</div>
<a href="/products/wishlist/" class="btn furry-btn-outline">Wunschliste ansehen</a>
<a href="/logout/" class="btn furry-btn-secondary">Logout</a>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% load i18n %}
{% block title %}Profil{% endblock %}
{% block content %}
<div class="profile-card">
<h2>Willkommen, {{ user.username }}!</h2>
<div class="profile-info">
<p><strong>E-Mail:</strong> {{ user.email }}</p>
<p><strong>Mitglied seit:</strong> {{ user.date_joined|date:"d.m.Y" }}</p>
</div>
<div class="profile-orders">
<h3>Bestellungen</h3>
{% if orders %}
<ul>
{% for order in orders %}
<li>Bestellung #{{ order.id }} vom {{ order.created|date:"d.m.Y" }} {{ order.total_amount|floatformat:2 }} €</li>
{% endfor %}
</ul>
{% else %}
<p>Du hast noch keine Bestellungen.</p>
{% endif %}
</div>
<a href="/products/wishlist/" class="btn furry-btn-outline">Wunschliste ansehen</a>
<a href="/logout/" class="btn furry-btn-secondary">Logout</a>
</div>
{% endblock %}

View File

@ -1,450 +1,224 @@
<<<<<<< HEAD
{% extends 'base.html' %}
{% load static %}
{% block title %}Registrieren - {{ block.super }}{% endblock %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">🐾 Registrieren</h1>
<p class="text-muted">Werde Teil der Furry-Community!</p>
</div>
<form method="post" class="needs-validation furry-form" novalidate>
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form %}
<div class="form-group mb-3">
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{{ field }}
{% if field.help_text %}
<div class="form-text">{{ field.help_text }}</div>
{% endif %}
{% if field.errors %}
<div class="invalid-feedback d-block">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary furry-btn">
🐾 Registrieren
</button>
</div>
</form>
<div class="text-center mt-4">
<p>Bereits ein Konto? <a href="{% url 'login' %}">Jetzt anmelden</a></p>
<p><a href="{% url 'password_reset' %}">Passwort vergessen?</a></p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
margin: 2rem 0;
}
.furry-form input, .furry-form textarea {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
font-size: 1rem;
}
.furry-form input:focus, .furry-form textarea:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.furry-form input.invalid, .furry-form textarea.invalid {
border-color: #EF4444;
box-shadow: 0 0 0 0.25rem rgba(239, 68, 68, 0.25);
}
.furry-form input.valid, .furry-form textarea.valid {
border-color: #10B981;
box-shadow: 0 0 0 0.25rem rgba(16, 185, 129, 0.25);
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
font-weight: 700;
font-size: 1.1rem;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
font-weight: 600;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
.alert-danger {
background: linear-gradient(135deg, #FEE2E2, #FECACA);
border: 2px solid #EF4444;
border-radius: 10px;
color: #991B1B;
padding: 1rem;
margin-bottom: 1rem;
}
.invalid-feedback {
color: #EF4444;
font-size: 0.875rem;
margin-top: 0.25rem;
}
/* Animation für erfolgreiche Registrierung */
@keyframes success-bounce {
0%, 20%, 53%, 80%, 100% {
transform: translate3d(0,0,0);
}
40%, 43% {
transform: translate3d(0, -30px, 0);
}
70% {
transform: translate3d(0, -15px, 0);
}
90% {
transform: translate3d(0, -4px, 0);
}
}
.success-animation {
animation: success-bounce 1s ease-in-out;
}
</style>
{% block extra_js %}
<script>
// Formular-Validierung
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('.furry-form');
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
if (this.classList.contains('invalid')) {
validateField(this);
}
});
});
function validateField(field) {
if (field.checkValidity()) {
field.classList.remove('invalid');
field.classList.add('valid');
} else {
field.classList.remove('valid');
field.classList.add('invalid');
}
}
// Erfolgreiche Registrierung Animation
// {% if form.is_valid and not form.errors %}
// document.querySelector('.furry-card').classList.add('success-animation');
// {% endif %}
});
// Formular neu laden, wenn der Benutzer zurück navigiert
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
window.location.reload();
}
});
</script>
{% endblock %}
=======
{% extends 'base.html' %}
{% load static %}
{% block title %}Registrieren - {{ block.super }}{% endblock %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">🐾 Registrieren</h1>
<p class="text-muted">Werde Teil der Furry-Community!</p>
</div>
<form method="post" class="needs-validation furry-form" novalidate>
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form %}
<div class="form-group mb-3">
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{{ field }}
{% if field.help_text %}
<div class="form-text">{{ field.help_text }}</div>
{% endif %}
{% if field.errors %}
<div class="invalid-feedback d-block">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary furry-btn">
🐾 Registrieren
</button>
</div>
</form>
<div class="text-center mt-4">
<p>Bereits ein Konto? <a href="{% url 'login' %}">Jetzt anmelden</a></p>
<p><a href="{% url 'password_reset' %}">Passwort vergessen?</a></p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
margin: 2rem 0;
}
.furry-form input, .furry-form textarea {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
font-size: 1rem;
}
.furry-form input:focus, .furry-form textarea:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.furry-form input.invalid, .furry-form textarea.invalid {
border-color: #EF4444;
box-shadow: 0 0 0 0.25rem rgba(239, 68, 68, 0.25);
}
.furry-form input.valid, .furry-form textarea.valid {
border-color: #10B981;
box-shadow: 0 0 0 0.25rem rgba(16, 185, 129, 0.25);
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
font-weight: 700;
font-size: 1.1rem;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
font-weight: 600;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
.alert-danger {
background: linear-gradient(135deg, #FEE2E2, #FECACA);
border: 2px solid #EF4444;
border-radius: 10px;
color: #991B1B;
padding: 1rem;
margin-bottom: 1rem;
}
.invalid-feedback {
color: #EF4444;
font-size: 0.875rem;
margin-top: 0.25rem;
}
/* Animation für erfolgreiche Registrierung */
@keyframes success-bounce {
0%, 20%, 53%, 80%, 100% {
transform: translate3d(0,0,0);
}
40%, 43% {
transform: translate3d(0, -30px, 0);
}
70% {
transform: translate3d(0, -15px, 0);
}
90% {
transform: translate3d(0, -4px, 0);
}
}
.success-animation {
animation: success-bounce 1s ease-in-out;
}
</style>
{% block extra_js %}
<script>
// Formular-Validierung
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('.furry-form');
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
if (this.classList.contains('invalid')) {
validateField(this);
}
});
});
function validateField(field) {
if (field.checkValidity()) {
field.classList.remove('invalid');
field.classList.add('valid');
} else {
field.classList.remove('valid');
field.classList.add('invalid');
}
}
// Erfolgreiche Registrierung Animation
// {% if form.is_valid and not form.errors %}
// document.querySelector('.furry-card').classList.add('success-animation');
// {% endif %}
});
// Formular neu laden, wenn der Benutzer zurück navigiert
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
window.location.reload();
}
});
</script>
{% endblock %}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'base.html' %}
{% load static %}
{% block title %}Registrieren - {{ block.super }}{% endblock %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">🐾 Registrieren</h1>
<p class="text-muted">Werde Teil der Furry-Community!</p>
</div>
<form method="post" class="needs-validation furry-form" novalidate>
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form %}
<div class="form-group mb-3">
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{{ field }}
{% if field.help_text %}
<div class="form-text">{{ field.help_text }}</div>
{% endif %}
{% if field.errors %}
<div class="invalid-feedback d-block">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary furry-btn">
🐾 Registrieren
</button>
</div>
</form>
<div class="text-center mt-4">
<p>Bereits ein Konto? <a href="{% url 'login' %}">Jetzt anmelden</a></p>
<p><a href="{% url 'password_reset' %}">Passwort vergessen?</a></p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
margin: 2rem 0;
}
.furry-form input, .furry-form textarea {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
font-size: 1rem;
}
.furry-form input:focus, .furry-form textarea:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.furry-form input.invalid, .furry-form textarea.invalid {
border-color: #EF4444;
box-shadow: 0 0 0 0.25rem rgba(239, 68, 68, 0.25);
}
.furry-form input.valid, .furry-form textarea.valid {
border-color: #10B981;
box-shadow: 0 0 0 0.25rem rgba(16, 185, 129, 0.25);
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
font-weight: 700;
font-size: 1.1rem;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
font-weight: 600;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
.alert-danger {
background: linear-gradient(135deg, #FEE2E2, #FECACA);
border: 2px solid #EF4444;
border-radius: 10px;
color: #991B1B;
padding: 1rem;
margin-bottom: 1rem;
}
.invalid-feedback {
color: #EF4444;
font-size: 0.875rem;
margin-top: 0.25rem;
}
/* Animation für erfolgreiche Registrierung */
@keyframes success-bounce {
0%, 20%, 53%, 80%, 100% {
transform: translate3d(0,0,0);
}
40%, 43% {
transform: translate3d(0, -30px, 0);
}
70% {
transform: translate3d(0, -15px, 0);
}
90% {
transform: translate3d(0, -4px, 0);
}
}
.success-animation {
animation: success-bounce 1s ease-in-out;
}
</style>
{% block extra_js %}
<script>
// Formular-Validierung
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('.furry-form');
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
if (this.classList.contains('invalid')) {
validateField(this);
}
});
});
function validateField(field) {
if (field.checkValidity()) {
field.classList.remove('invalid');
field.classList.add('valid');
} else {
field.classList.remove('valid');
field.classList.add('invalid');
}
}
// Erfolgreiche Registrierung Animation
// {% if form.is_valid and not form.errors %}
// document.querySelector('.furry-card').classList.add('success-animation');
// {% endif %}
});
// Formular neu laden, wenn der Benutzer zurück navigiert
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
window.location.reload();
}
});
</script>
{% endblock %}
{% endblock %}

View File

@ -1,9 +1,3 @@
<<<<<<< HEAD
from django.test import TestCase
# Create your tests here.
=======
from django.test import TestCase
# Create your tests here.
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.test import TestCase
# Create your tests here.

View File

@ -18,26 +18,12 @@ from django.utils import timezone
from django.core.mail import send_mail
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
import json
<<<<<<< HEAD
# import paypalrestsdk # Temporär auskommentiert
# from payments import get_payment_model, RedirectNeeded # Temporär auskommentiert
# from paypal.standard.ipn.models import PayPalIPN # Temporär auskommentiert
from django.urls import reverse
from django.http import HttpResponse
# from paypal.standard.forms import PayPalPaymentsForm # Temporär auskommentiert
import logging
from django.core.exceptions import ValidationError
from django.http import Http404
logger = logging.getLogger(__name__)
=======
import paypalrestsdk
from payments import get_payment_model, RedirectNeeded
from paypal.standard.ipn.models import PayPalIPN
from django.urls import reverse
from django.http import HttpResponse
from paypal.standard.forms import PayPalPaymentsForm
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Create your views here.
@ -48,99 +34,6 @@ class ProductListView(ListView):
paginate_by = 12
def get_queryset(self):
<<<<<<< HEAD
try:
queryset = Product.objects.all().annotate(
average_rating=Avg('reviews__rating'),
review_count=Count('reviews')
).select_related('category').prefetch_related(
'reviews__user', # Optimiert N+1 Queries für Reviews
'wishlist_users' # Optimiert Wishlist-Queries
)
# Erweiterte Suchfilter
search_query = self.request.GET.get('search')
if search_query:
# Verbesserte Suche mit Q-Objekten
search_terms = search_query.split()
q_objects = Q()
for term in search_terms:
q_objects |= (
Q(name__icontains=term) |
Q(description__icontains=term) |
Q(fursuit_type__icontains=term) |
Q(style__icontains=term) |
Q(category__name__icontains=term)
)
queryset = queryset.filter(q_objects)
# Preisbereich Filter mit Validierung
min_price = self.request.GET.get('min_price')
max_price = self.request.GET.get('max_price')
if min_price and min_price.isdigit():
queryset = queryset.filter(price__gte=float(min_price))
if max_price and max_price.isdigit():
queryset = queryset.filter(price__lte=float(max_price))
# Fursuit-Typ Filter
fursuit_type = self.request.GET.get('fursuit_type')
if fursuit_type:
queryset = queryset.filter(fursuit_type=fursuit_type)
# Style Filter
style = self.request.GET.get('style')
if style:
queryset = queryset.filter(style=style)
# Kategorie Filter
category = self.request.GET.get('category')
if category:
queryset = queryset.filter(category__slug=category)
# Verfügbarkeit Filter
availability = self.request.GET.get('availability')
if availability == 'in_stock':
queryset = queryset.filter(stock__gt=0)
elif availability == 'low_stock':
queryset = queryset.filter(stock__lte=5, stock__gt=0)
elif availability == 'out_of_stock':
queryset = queryset.filter(stock=0)
# Sortierung mit verbesserter Performance
sort = self.request.GET.get('sort', '-created')
if sort == 'price':
queryset = queryset.order_by('price')
elif sort == '-price':
queryset = queryset.order_by('-price')
elif sort == 'name':
queryset = queryset.order_by('name')
elif sort == '-name':
queryset = queryset.order_by('-name')
elif sort == '-rating':
queryset = queryset.order_by('-average_rating')
elif sort == 'popularity':
queryset = queryset.order_by('-review_count')
else: # Default: Neueste zuerst
queryset = queryset.order_by('-created')
return queryset.distinct()
except Exception as e:
logger.error(f"Error in ProductListView.get_queryset: {e}")
return Product.objects.none()
def get_context_data(self, **kwargs):
try:
context = super().get_context_data(**kwargs)
# Zusätzliche Context-Daten
context['categories'] = Category.objects.all()
context['fursuit_types'] = Product.FURSUIT_TYPE_CHOICES
context['styles'] = Product.STYLE_CHOICES
return context
except Exception as e:
logger.error(f"Error in ProductListView.get_context_data: {e}")
return super().get_context_data(**kwargs)
=======
queryset = Product.objects.all().annotate(
average_rating=Avg('reviews__rating'),
review_count=Count('reviews')
@ -241,7 +134,6 @@ class ProductListView(ListView):
context['low_stock_count'] = Product.objects.filter(stock__lte=5, stock__gt=0).count()
return context
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
class ProductDetailView(DetailView):
model = Product

View File

@ -1,31 +1,14 @@
<<<<<<< HEAD
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ShopConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'shop'
verbose_name = _('Shop')
def ready(self):
"""
Importiert und registriert die Signal-Handler
"""
import shop.signals
=======
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ShopConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'shop'
verbose_name = _('Shop')
def ready(self):
"""
Importiert und registriert die Signal-Handler
"""
import shop.signals
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class ShopConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'shop'
verbose_name = _('Shop')
def ready(self):
"""
Importiert und registriert die Signal-Handler
"""
import shop.signals

View File

@ -1,35 +1,16 @@
<<<<<<< HEAD
"""
ASGI config for shop project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shop.settings')
application = get_asgi_application()
=======
"""
ASGI config for shop project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shop.settings')
application = get_asgi_application()
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
"""
ASGI config for shop project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shop.settings')
application = get_asgi_application()

View File

@ -1,414 +1,206 @@
<<<<<<< HEAD
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.translation import gettext as _
from django.conf import settings
from django.urls import reverse
from django.contrib.sites.shortcuts import get_current_site
def send_order_confirmation(request, order):
"""
Sendet eine Bestellbestätigungs-E-Mail an den Kunden
"""
context = {
'order': order,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/order_confirmation.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/order_confirmation.txt',
context
)
subject = _('Order Confirmation - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_order_status_update(request, order, update=None):
"""
Sendet eine E-Mail über Statusänderungen der Bestellung
"""
context = {
'order': order,
'update': update,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/order_status_update.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/order_status_update.txt',
context
)
subject = _('Order Status Update - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_shipping_confirmation(request, order):
"""
Sendet eine Versandbestätigungs-E-Mail mit Tracking-Nummer
"""
context = {
'order': order,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/shipping_confirmation.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/shipping_confirmation.txt',
context
)
subject = _('Your Order Has Been Shipped - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_admin_notification(request, order, notification_type, extra_context=None):
"""
Sendet eine Benachrichtigung an den Shop-Administrator
"""
context = {
'order': order,
'notification_type': notification_type,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('admin:shop_order_change', args=[order.id])
)
}
if extra_context:
context.update(extra_context)
# HTML-Version
html_content = render_to_string(
'shop/emails/admin_notification.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/admin_notification.txt',
context
)
# Betreff basierend auf Benachrichtigungstyp
subjects = {
'new_order': _('New Order Received - Order #{}'),
'payment_failed': _('Payment Failed - Order #{}'),
'custom_design': _('New Custom Design Order #{}'),
'fursuit_order': _('New Fursuit Order #{}'),
}
subject = subjects.get(notification_type, _('Order Notification - Order #{}')).format(order.id)
# E-Mail an alle konfigurierten Admin-E-Mail-Adressen senden
admin_emails = [email for name, email in settings.ADMINS]
if admin_emails:
msg = EmailMultiAlternatives(
subject,
text_content,
settings.DEFAULT_FROM_EMAIL,
admin_emails
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_low_stock_notification(request, product):
"""
Benachrichtigt den Admin über niedrigen Lagerbestand
"""
context = {
'product': product,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'product_url': request.build_absolute_uri(
reverse('admin:shop_product_change', args=[product.id])
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/low_stock_notification.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/low_stock_notification.txt',
context
)
subject = _('Low Stock Alert - {}').format(product.name)
admin_emails = [email for name, email in settings.ADMINS]
if admin_emails:
msg = EmailMultiAlternatives(
subject,
text_content,
settings.DEFAULT_FROM_EMAIL,
admin_emails
)
msg.attach_alternative(html_content, "text/html")
=======
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.translation import gettext as _
from django.conf import settings
from django.urls import reverse
from django.contrib.sites.shortcuts import get_current_site
def send_order_confirmation(request, order):
"""
Sendet eine Bestellbestätigungs-E-Mail an den Kunden
"""
context = {
'order': order,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/order_confirmation.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/order_confirmation.txt',
context
)
subject = _('Order Confirmation - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_order_status_update(request, order, update=None):
"""
Sendet eine E-Mail über Statusänderungen der Bestellung
"""
context = {
'order': order,
'update': update,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/order_status_update.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/order_status_update.txt',
context
)
subject = _('Order Status Update - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_shipping_confirmation(request, order):
"""
Sendet eine Versandbestätigungs-E-Mail mit Tracking-Nummer
"""
context = {
'order': order,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/shipping_confirmation.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/shipping_confirmation.txt',
context
)
subject = _('Your Order Has Been Shipped - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_admin_notification(request, order, notification_type, extra_context=None):
"""
Sendet eine Benachrichtigung an den Shop-Administrator
"""
context = {
'order': order,
'notification_type': notification_type,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('admin:shop_order_change', args=[order.id])
)
}
if extra_context:
context.update(extra_context)
# HTML-Version
html_content = render_to_string(
'shop/emails/admin_notification.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/admin_notification.txt',
context
)
# Betreff basierend auf Benachrichtigungstyp
subjects = {
'new_order': _('New Order Received - Order #{}'),
'payment_failed': _('Payment Failed - Order #{}'),
'custom_design': _('New Custom Design Order #{}'),
'fursuit_order': _('New Fursuit Order #{}'),
}
subject = subjects.get(notification_type, _('Order Notification - Order #{}')).format(order.id)
# E-Mail an alle konfigurierten Admin-E-Mail-Adressen senden
admin_emails = [email for name, email in settings.ADMINS]
if admin_emails:
msg = EmailMultiAlternatives(
subject,
text_content,
settings.DEFAULT_FROM_EMAIL,
admin_emails
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_low_stock_notification(request, product):
"""
Benachrichtigt den Admin über niedrigen Lagerbestand
"""
context = {
'product': product,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'product_url': request.build_absolute_uri(
reverse('admin:shop_product_change', args=[product.id])
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/low_stock_notification.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/low_stock_notification.txt',
context
)
subject = _('Low Stock Alert - {}').format(product.name)
admin_emails = [email for name, email in settings.ADMINS]
if admin_emails:
msg = EmailMultiAlternatives(
subject,
text_content,
settings.DEFAULT_FROM_EMAIL,
admin_emails
)
msg.attach_alternative(html_content, "text/html")
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.translation import gettext as _
from django.conf import settings
from django.urls import reverse
from django.contrib.sites.shortcuts import get_current_site
def send_order_confirmation(request, order):
"""
Sendet eine Bestellbestätigungs-E-Mail an den Kunden
"""
context = {
'order': order,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/order_confirmation.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/order_confirmation.txt',
context
)
subject = _('Order Confirmation - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_order_status_update(request, order, update=None):
"""
Sendet eine E-Mail über Statusänderungen der Bestellung
"""
context = {
'order': order,
'update': update,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/order_status_update.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/order_status_update.txt',
context
)
subject = _('Order Status Update - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_shipping_confirmation(request, order):
"""
Sendet eine Versandbestätigungs-E-Mail mit Tracking-Nummer
"""
context = {
'order': order,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('shop:my_orders')
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/shipping_confirmation.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/shipping_confirmation.txt',
context
)
subject = _('Your Order Has Been Shipped - Order #{}').format(order.id)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = order.shipping_address.email
msg = EmailMultiAlternatives(
subject,
text_content,
from_email,
[to_email]
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_admin_notification(request, order, notification_type, extra_context=None):
"""
Sendet eine Benachrichtigung an den Shop-Administrator
"""
context = {
'order': order,
'notification_type': notification_type,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'order_url': request.build_absolute_uri(
reverse('admin:shop_order_change', args=[order.id])
)
}
if extra_context:
context.update(extra_context)
# HTML-Version
html_content = render_to_string(
'shop/emails/admin_notification.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/admin_notification.txt',
context
)
# Betreff basierend auf Benachrichtigungstyp
subjects = {
'new_order': _('New Order Received - Order #{}'),
'payment_failed': _('Payment Failed - Order #{}'),
'custom_design': _('New Custom Design Order #{}'),
'fursuit_order': _('New Fursuit Order #{}'),
}
subject = subjects.get(notification_type, _('Order Notification - Order #{}')).format(order.id)
# E-Mail an alle konfigurierten Admin-E-Mail-Adressen senden
admin_emails = [email for name, email in settings.ADMINS]
if admin_emails:
msg = EmailMultiAlternatives(
subject,
text_content,
settings.DEFAULT_FROM_EMAIL,
admin_emails
)
msg.attach_alternative(html_content, "text/html")
msg.send()
def send_low_stock_notification(request, product):
"""
Benachrichtigt den Admin über niedrigen Lagerbestand
"""
context = {
'product': product,
'logo_url': f"{settings.STATIC_URL}images/logo.png",
'product_url': request.build_absolute_uri(
reverse('admin:shop_product_change', args=[product.id])
)
}
# HTML-Version
html_content = render_to_string(
'shop/emails/low_stock_notification.html',
context
)
# Text-Version
text_content = render_to_string(
'shop/emails/low_stock_notification.txt',
context
)
subject = _('Low Stock Alert - {}').format(product.name)
admin_emails = [email for name, email in settings.ADMINS]
if admin_emails:
msg = EmailMultiAlternatives(
subject,
text_content,
settings.DEFAULT_FROM_EMAIL,
admin_emails
)
msg.attach_alternative(html_content, "text/html")
msg.send()

View File

@ -1,48 +1,23 @@
<<<<<<< HEAD
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import ShippingAddress, Order
class ShippingAddressForm(forms.ModelForm):
class Meta:
model = ShippingAddress
fields = ['first_name', 'last_name', 'email', 'address', 'city', 'zip', 'country']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
class PaymentMethodForm(forms.Form):
payment_method = forms.ChoiceField(
choices=Order.PAYMENT_METHODS,
widget=forms.HiddenInput(),
required=True,
error_messages={
'required': _('Please select a payment method.')
}
=======
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import ShippingAddress, Order
class ShippingAddressForm(forms.ModelForm):
class Meta:
model = ShippingAddress
fields = ['first_name', 'last_name', 'email', 'address', 'city', 'zip', 'country']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
class PaymentMethodForm(forms.Form):
payment_method = forms.ChoiceField(
choices=Order.PAYMENT_METHODS,
widget=forms.HiddenInput(),
required=True,
error_messages={
'required': _('Please select a payment method.')
}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import ShippingAddress, Order
class ShippingAddressForm(forms.ModelForm):
class Meta:
model = ShippingAddress
fields = ['first_name', 'last_name', 'email', 'address', 'city', 'zip', 'country']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
class PaymentMethodForm(forms.Form):
payment_method = forms.ChoiceField(
choices=Order.PAYMENT_METHODS,
widget=forms.HiddenInput(),
required=True,
error_messages={
'required': _('Please select a payment method.')
}
)

View File

@ -1,222 +1,110 @@
<<<<<<< HEAD
from django.core.management.base import BaseCommand
from django.core.mail import send_mail, get_connection
from django.conf import settings
from django.template.loader import render_to_string
from shop.models import Order, Product
from shop.emails import (
send_order_confirmation,
send_order_status_update,
send_shipping_confirmation,
send_admin_notification,
send_low_stock_notification
)
class Command(BaseCommand):
help = 'Testet das E-Mail-System mit verschiedenen E-Mail-Typen'
def add_arguments(self, parser):
parser.add_argument(
'--email',
type=str,
help='Test-E-Mail-Adresse',
)
parser.add_argument(
'--type',
type=str,
choices=['all', 'order', 'status', 'shipping', 'admin', 'stock'],
default='all',
help='Art der Test-E-Mail',
)
def handle(self, *args, **options):
test_email = options['email']
if not test_email:
self.stdout.write(self.style.ERROR('Bitte geben Sie eine Test-E-Mail-Adresse an mit --email=ihre@email.de'))
return
email_type = options['type']
self.stdout.write('Starte E-Mail-Test...')
try:
# Debug-Informationen ausgeben
self.stdout.write(f'Backend: {settings.EMAIL_BACKEND}')
self.stdout.write(f'Host: {settings.EMAIL_HOST}')
self.stdout.write(f'Port: {settings.EMAIL_PORT}')
self.stdout.write(f'TLS: {settings.EMAIL_USE_TLS}')
self.stdout.write(f'Benutzer: {settings.EMAIL_HOST_USER}')
self.stdout.write('Passwort: ***versteckt***')
# Basis-Test
send_mail(
'Test E-Mail',
'Dies ist eine Test-E-Mail vom Fursuit Shop.',
settings.DEFAULT_FROM_EMAIL,
[test_email],
fail_silently=False,
)
self.stdout.write(self.style.SUCCESS('✓ Basis-E-Mail erfolgreich gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Basis-E-Mail fehlgeschlagen: {str(e)}'))
return
if email_type in ['all', 'order']:
try:
# Test-Bestellung erstellen
order = Order.objects.first()
if order:
send_order_confirmation(None, order)
self.stdout.write(self.style.SUCCESS('✓ Bestellbestätigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Bestellbestätigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'status']:
try:
order = Order.objects.first()
if order:
send_order_status_update(None, order)
self.stdout.write(self.style.SUCCESS('✓ Status-Update gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Status-Update fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'shipping']:
try:
order = Order.objects.filter(tracking_number__isnull=False).first()
if order:
send_shipping_confirmation(None, order)
self.stdout.write(self.style.SUCCESS('✓ Versandbestätigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Versandbestätigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'admin']:
try:
order = Order.objects.first()
if order:
send_admin_notification(None, order, 'new_order')
self.stdout.write(self.style.SUCCESS('✓ Admin-Benachrichtigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Admin-Benachrichtigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'stock']:
try:
product = Product.objects.first()
if product:
send_low_stock_notification(None, product)
self.stdout.write(self.style.SUCCESS('✓ Lagerbestand-Warnung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Lagerbestand-Warnung fehlgeschlagen: {str(e)}'))
=======
from django.core.management.base import BaseCommand
from django.core.mail import send_mail, get_connection
from django.conf import settings
from django.template.loader import render_to_string
from shop.models import Order, Product
from shop.emails import (
send_order_confirmation,
send_order_status_update,
send_shipping_confirmation,
send_admin_notification,
send_low_stock_notification
)
class Command(BaseCommand):
help = 'Testet das E-Mail-System mit verschiedenen E-Mail-Typen'
def add_arguments(self, parser):
parser.add_argument(
'--email',
type=str,
help='Test-E-Mail-Adresse',
)
parser.add_argument(
'--type',
type=str,
choices=['all', 'order', 'status', 'shipping', 'admin', 'stock'],
default='all',
help='Art der Test-E-Mail',
)
def handle(self, *args, **options):
test_email = options['email']
if not test_email:
self.stdout.write(self.style.ERROR('Bitte geben Sie eine Test-E-Mail-Adresse an mit --email=ihre@email.de'))
return
email_type = options['type']
self.stdout.write('Starte E-Mail-Test...')
try:
# Debug-Informationen ausgeben
self.stdout.write(f'Backend: {settings.EMAIL_BACKEND}')
self.stdout.write(f'Host: {settings.EMAIL_HOST}')
self.stdout.write(f'Port: {settings.EMAIL_PORT}')
self.stdout.write(f'TLS: {settings.EMAIL_USE_TLS}')
self.stdout.write(f'Benutzer: {settings.EMAIL_HOST_USER}')
self.stdout.write('Passwort: ***versteckt***')
# Basis-Test
send_mail(
'Test E-Mail',
'Dies ist eine Test-E-Mail vom Fursuit Shop.',
settings.DEFAULT_FROM_EMAIL,
[test_email],
fail_silently=False,
)
self.stdout.write(self.style.SUCCESS('✓ Basis-E-Mail erfolgreich gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Basis-E-Mail fehlgeschlagen: {str(e)}'))
return
if email_type in ['all', 'order']:
try:
# Test-Bestellung erstellen
order = Order.objects.first()
if order:
send_order_confirmation(None, order)
self.stdout.write(self.style.SUCCESS('✓ Bestellbestätigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Bestellbestätigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'status']:
try:
order = Order.objects.first()
if order:
send_order_status_update(None, order)
self.stdout.write(self.style.SUCCESS('✓ Status-Update gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Status-Update fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'shipping']:
try:
order = Order.objects.filter(tracking_number__isnull=False).first()
if order:
send_shipping_confirmation(None, order)
self.stdout.write(self.style.SUCCESS('✓ Versandbestätigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Versandbestätigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'admin']:
try:
order = Order.objects.first()
if order:
send_admin_notification(None, order, 'new_order')
self.stdout.write(self.style.SUCCESS('✓ Admin-Benachrichtigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Admin-Benachrichtigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'stock']:
try:
product = Product.objects.first()
if product:
send_low_stock_notification(None, product)
self.stdout.write(self.style.SUCCESS('✓ Lagerbestand-Warnung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Lagerbestand-Warnung fehlgeschlagen: {str(e)}'))
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.core.management.base import BaseCommand
from django.core.mail import send_mail, get_connection
from django.conf import settings
from django.template.loader import render_to_string
from shop.models import Order, Product
from shop.emails import (
send_order_confirmation,
send_order_status_update,
send_shipping_confirmation,
send_admin_notification,
send_low_stock_notification
)
class Command(BaseCommand):
help = 'Testet das E-Mail-System mit verschiedenen E-Mail-Typen'
def add_arguments(self, parser):
parser.add_argument(
'--email',
type=str,
help='Test-E-Mail-Adresse',
)
parser.add_argument(
'--type',
type=str,
choices=['all', 'order', 'status', 'shipping', 'admin', 'stock'],
default='all',
help='Art der Test-E-Mail',
)
def handle(self, *args, **options):
test_email = options['email']
if not test_email:
self.stdout.write(self.style.ERROR('Bitte geben Sie eine Test-E-Mail-Adresse an mit --email=ihre@email.de'))
return
email_type = options['type']
self.stdout.write('Starte E-Mail-Test...')
try:
# Debug-Informationen ausgeben
self.stdout.write(f'Backend: {settings.EMAIL_BACKEND}')
self.stdout.write(f'Host: {settings.EMAIL_HOST}')
self.stdout.write(f'Port: {settings.EMAIL_PORT}')
self.stdout.write(f'TLS: {settings.EMAIL_USE_TLS}')
self.stdout.write(f'Benutzer: {settings.EMAIL_HOST_USER}')
self.stdout.write('Passwort: ***versteckt***')
# Basis-Test
send_mail(
'Test E-Mail',
'Dies ist eine Test-E-Mail vom Fursuit Shop.',
settings.DEFAULT_FROM_EMAIL,
[test_email],
fail_silently=False,
)
self.stdout.write(self.style.SUCCESS('✓ Basis-E-Mail erfolgreich gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Basis-E-Mail fehlgeschlagen: {str(e)}'))
return
if email_type in ['all', 'order']:
try:
# Test-Bestellung erstellen
order = Order.objects.first()
if order:
send_order_confirmation(None, order)
self.stdout.write(self.style.SUCCESS('✓ Bestellbestätigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Bestellbestätigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'status']:
try:
order = Order.objects.first()
if order:
send_order_status_update(None, order)
self.stdout.write(self.style.SUCCESS('✓ Status-Update gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Status-Update fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'shipping']:
try:
order = Order.objects.filter(tracking_number__isnull=False).first()
if order:
send_shipping_confirmation(None, order)
self.stdout.write(self.style.SUCCESS('✓ Versandbestätigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Versandbestätigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'admin']:
try:
order = Order.objects.first()
if order:
send_admin_notification(None, order, 'new_order')
self.stdout.write(self.style.SUCCESS('✓ Admin-Benachrichtigung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Admin-Benachrichtigung fehlgeschlagen: {str(e)}'))
if email_type in ['all', 'stock']:
try:
product = Product.objects.first()
if product:
send_low_stock_notification(None, product)
self.stdout.write(self.style.SUCCESS('✓ Lagerbestand-Warnung gesendet'))
except Exception as e:
self.stdout.write(self.style.ERROR(f'✗ Lagerbestand-Warnung fehlgeschlagen: {str(e)}'))
self.stdout.write(self.style.SUCCESS('\nE-Mail-Test abgeschlossen!'))

View File

@ -1,279 +1,138 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 10:19
import django.db.models.deletion
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('slug', models.SlugField(unique=True)),
],
options={
'verbose_name': 'Category',
'verbose_name_plural': 'Categories',
},
),
migrations.CreateModel(
name='DesignTemplate',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('image', models.ImageField(upload_to='designs/')),
('model_3d', models.FileField(blank=True, null=True, upload_to='3d_models/', verbose_name='3D Model')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='FursuitGallery',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('slug', models.SlugField(unique=True)),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'Fursuit Gallery',
'verbose_name_plural': 'Fursuit Galleries',
},
),
migrations.CreateModel(
name='CustomerDesign',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Design Name')),
('design_file', models.FileField(upload_to='customer_designs/')),
('notes', models.TextField(blank=True, verbose_name='Notes')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='GalleryImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='gallery/')),
('title', models.CharField(blank=True, max_length=200, verbose_name='Title')),
('description', models.TextField(blank=True, verbose_name='Description')),
('order', models.PositiveIntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('gallery', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='shop.fursuitgallery')),
],
options={
'ordering': ['order', 'created_at'],
},
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('pending', 'Pending'), ('confirmed', 'Confirmed'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=20)),
('payment_method', models.CharField(choices=[('paypal', 'PayPal'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer')], max_length=20)),
('special_instructions', models.TextField(blank=True)),
('measurements', models.JSONField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('total_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10)),
('customer_design', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.customerdesign')),
('design_template', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.designtemplate')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='OrderProgress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Title')),
('description', models.TextField(verbose_name='Description')),
('image', models.ImageField(blank=True, null=True, upload_to='progress/')),
('created_at', models.DateTimeField(auto_now_add=True)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_updates', to='shop.order')),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_type', models.CharField(choices=[('fursuit', 'Fursuit'), ('printed', 'Printed Item')], default='printed', max_length=10, verbose_name='Product Type')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('base_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, verbose_name='Base Price')),
('image', models.ImageField(upload_to='products/')),
('model_3d', models.FileField(blank=True, null=True, upload_to='3d_models/', verbose_name='3D Model')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.category')),
('gallery', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.fursuitgallery')),
],
),
migrations.AddField(
model_name='order',
name='product',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product'),
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 10:19
import django.db.models.deletion
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('slug', models.SlugField(unique=True)),
],
options={
'verbose_name': 'Category',
'verbose_name_plural': 'Categories',
},
),
migrations.CreateModel(
name='DesignTemplate',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('image', models.ImageField(upload_to='designs/')),
('model_3d', models.FileField(blank=True, null=True, upload_to='3d_models/', verbose_name='3D Model')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='FursuitGallery',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('slug', models.SlugField(unique=True)),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'Fursuit Gallery',
'verbose_name_plural': 'Fursuit Galleries',
},
),
migrations.CreateModel(
name='CustomerDesign',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Design Name')),
('design_file', models.FileField(upload_to='customer_designs/')),
('notes', models.TextField(blank=True, verbose_name='Notes')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='GalleryImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='gallery/')),
('title', models.CharField(blank=True, max_length=200, verbose_name='Title')),
('description', models.TextField(blank=True, verbose_name='Description')),
('order', models.PositiveIntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('gallery', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='shop.fursuitgallery')),
],
options={
'ordering': ['order', 'created_at'],
},
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('pending', 'Pending'), ('confirmed', 'Confirmed'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=20)),
('payment_method', models.CharField(choices=[('paypal', 'PayPal'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer')], max_length=20)),
('special_instructions', models.TextField(blank=True)),
('measurements', models.JSONField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('total_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10)),
('customer_design', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.customerdesign')),
('design_template', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.designtemplate')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='OrderProgress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Title')),
('description', models.TextField(verbose_name='Description')),
('image', models.ImageField(blank=True, null=True, upload_to='progress/')),
('created_at', models.DateTimeField(auto_now_add=True)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_updates', to='shop.order')),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_type', models.CharField(choices=[('fursuit', 'Fursuit'), ('printed', 'Printed Item')], default='printed', max_length=10, verbose_name='Product Type')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('base_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, verbose_name='Base Price')),
('image', models.ImageField(upload_to='products/')),
('model_3d', models.FileField(blank=True, null=True, upload_to='3d_models/', verbose_name='3D Model')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.category')),
('gallery', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.fursuitgallery')),
],
),
migrations.AddField(
model_name='order',
name='product',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product'),
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 10:19
import django.db.models.deletion
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('slug', models.SlugField(unique=True)),
],
options={
'verbose_name': 'Category',
'verbose_name_plural': 'Categories',
},
),
migrations.CreateModel(
name='DesignTemplate',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('image', models.ImageField(upload_to='designs/')),
('model_3d', models.FileField(blank=True, null=True, upload_to='3d_models/', verbose_name='3D Model')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='FursuitGallery',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('slug', models.SlugField(unique=True)),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'Fursuit Gallery',
'verbose_name_plural': 'Fursuit Galleries',
},
),
migrations.CreateModel(
name='CustomerDesign',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Design Name')),
('design_file', models.FileField(upload_to='customer_designs/')),
('notes', models.TextField(blank=True, verbose_name='Notes')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='GalleryImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='gallery/')),
('title', models.CharField(blank=True, max_length=200, verbose_name='Title')),
('description', models.TextField(blank=True, verbose_name='Description')),
('order', models.PositiveIntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('gallery', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='shop.fursuitgallery')),
],
options={
'ordering': ['order', 'created_at'],
},
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('pending', 'Pending'), ('confirmed', 'Confirmed'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=20)),
('payment_method', models.CharField(choices=[('paypal', 'PayPal'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer')], max_length=20)),
('special_instructions', models.TextField(blank=True)),
('measurements', models.JSONField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('total_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10)),
('customer_design', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.customerdesign')),
('design_template', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.designtemplate')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='OrderProgress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Title')),
('description', models.TextField(verbose_name='Description')),
('image', models.ImageField(blank=True, null=True, upload_to='progress/')),
('created_at', models.DateTimeField(auto_now_add=True)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_updates', to='shop.order')),
],
options={
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_type', models.CharField(choices=[('fursuit', 'Fursuit'), ('printed', 'Printed Item')], default='printed', max_length=10, verbose_name='Product Type')),
('name', models.CharField(max_length=200, verbose_name='Name')),
('name_en', models.CharField(max_length=200, verbose_name='Name (English)')),
('description', models.TextField(verbose_name='Description')),
('description_en', models.TextField(verbose_name='Description (English)')),
('base_price', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, verbose_name='Base Price')),
('image', models.ImageField(upload_to='products/')),
('model_3d', models.FileField(blank=True, null=True, upload_to='3d_models/', verbose_name='3D Model')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.category')),
('gallery', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.fursuitgallery')),
],
),
migrations.AddField(
model_name='order',
name='product',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product'),
),
]

View File

@ -1,557 +1,277 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-29 12:47
import django.core.validators
import django.db.models.deletion
import django.utils.timezone
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ProductType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('has_sizes', models.BooleanField(default=False, verbose_name='Hat Größen')),
('has_colors', models.BooleanField(default=False, verbose_name='Hat Farben')),
('has_custom_design', models.BooleanField(default=False, verbose_name='Erlaubt Custom Design')),
('requires_measurements', models.BooleanField(default=False, verbose_name='Benötigt Maße')),
],
options={
'verbose_name': 'Produkttyp',
'verbose_name_plural': 'Produkttypen',
},
),
migrations.AlterModelOptions(
name='category',
options={'ordering': ['name'], 'verbose_name': 'Kategorie', 'verbose_name_plural': 'Kategorien'},
),
migrations.AlterModelOptions(
name='product',
options={'ordering': ['-created'], 'verbose_name': 'Produkt', 'verbose_name_plural': 'Produkte'},
),
migrations.RemoveField(
model_name='category',
name='name_en',
),
migrations.RemoveField(
model_name='product',
name='base_price',
),
migrations.RemoveField(
model_name='product',
name='created_at',
),
migrations.RemoveField(
model_name='product',
name='description_en',
),
migrations.RemoveField(
model_name='product',
name='gallery',
),
migrations.RemoveField(
model_name='product',
name='model_3d',
),
migrations.RemoveField(
model_name='product',
name='name_en',
),
migrations.RemoveField(
model_name='product',
name='updated_at',
),
migrations.AddField(
model_name='category',
name='description',
field=models.TextField(blank=True, verbose_name='Beschreibung'),
),
migrations.AddField(
model_name='category',
name='image',
field=models.ImageField(blank=True, upload_to='categories/', verbose_name='Bild'),
),
migrations.AddField(
model_name='category',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='shop.category', verbose_name='Übergeordnete Kategorie'),
),
migrations.AddField(
model_name='product',
name='available',
field=models.BooleanField(default=True, verbose_name='Verfügbar'),
),
migrations.AddField(
model_name='product',
name='created',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt'),
),
migrations.AddField(
model_name='product',
name='price',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='Preis'),
),
migrations.AddField(
model_name='product',
name='slug',
field=models.SlugField(default='', max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AddField(
model_name='product',
name='stock',
field=models.PositiveIntegerField(default=0, verbose_name='Lagerbestand'),
),
migrations.AddField(
model_name='product',
name='updated',
field=models.DateTimeField(auto_now=True, verbose_name='Aktualisiert'),
),
migrations.AlterField(
model_name='category',
name='name',
field=models.CharField(max_length=100, verbose_name='Name'),
),
migrations.AlterField(
model_name='category',
name='slug',
field=models.SlugField(max_length=100, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='product',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='shop.category', verbose_name='Kategorie'),
),
migrations.AlterField(
model_name='product',
name='description',
field=models.TextField(verbose_name='Beschreibung'),
),
migrations.AlterField(
model_name='product',
name='image',
field=models.ImageField(upload_to='products/', verbose_name='Hauptbild'),
),
migrations.CreateModel(
name='Cart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='CartItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('size', models.CharField(blank=True, max_length=20, null=True)),
('notes', models.TextField(blank=True, null=True)),
('custom_design', models.ImageField(blank=True, null=True, upload_to='custom_designs/')),
('added_at', models.DateTimeField(auto_now_add=True)),
('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='shop.cart')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product')),
],
options={
'ordering': ['-added_at'],
},
),
migrations.CreateModel(
name='CustomDesign',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('design_file', models.FileField(upload_to='designs/', verbose_name='Design-Datei')),
('notes', models.TextField(verbose_name='Anmerkungen')),
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt')),
('status', models.CharField(choices=[('pending', 'Ausstehend'), ('approved', 'Genehmigt'), ('rejected', 'Abgelehnt'), ('in_progress', 'In Bearbeitung'), ('completed', 'Abgeschlossen')], default='pending', max_length=20, verbose_name='Status')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Kunde')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_designs', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Custom Design',
'verbose_name_plural': 'Custom Designs',
'ordering': ['-created'],
},
),
migrations.CreateModel(
name='PaymentError',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('error_code', models.CharField(max_length=100, verbose_name='Error Code')),
('error_message', models.TextField(verbose_name='Error Message')),
('created_at', models.DateTimeField(auto_now_add=True)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payment_errors', to='shop.order')),
],
),
migrations.CreateModel(
name='PayPalPayment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_id', models.CharField(max_length=100, verbose_name='PayPal Payment ID')),
('payer_id', models.CharField(max_length=100, verbose_name='PayPal Payer ID')),
('status', models.CharField(choices=[('pending', 'Pending'), ('completed', 'Completed'), ('failed', 'Failed'), ('refunded', 'Refunded')], max_length=20, verbose_name='Payment Status')),
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')),
('currency', models.CharField(default='EUR', max_length=3, verbose_name='Currency')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('order', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='paypal_payment', to='shop.order')),
],
),
migrations.CreateModel(
name='ProductImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='products/', verbose_name='Bild')),
('alt_text', models.CharField(blank=True, max_length=200, verbose_name='Alternativer Text')),
('is_feature', models.BooleanField(default=False, verbose_name='Ist Hauptbild')),
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Produktbild',
'verbose_name_plural': 'Produktbilder',
'ordering': ['-is_feature', '-created'],
},
),
migrations.AlterField(
model_name='product',
name='product_type',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.producttype', verbose_name='Produkttyp'),
),
migrations.CreateModel(
name='ShippingAddress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='First Name')),
('last_name', models.CharField(max_length=100, verbose_name='Last Name')),
('email', models.EmailField(max_length=254, verbose_name='Email')),
('address', models.CharField(max_length=200, verbose_name='Address')),
('city', models.CharField(max_length=100, verbose_name='City')),
('zip', models.CharField(max_length=10, verbose_name='ZIP Code')),
('country', models.CharField(choices=[('DE', 'Deutschland'), ('AT', 'Österreich'), ('CH', 'Schweiz')], max_length=2, verbose_name='Country')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Checkout',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_method', models.CharField(choices=[('paypal', 'PayPal'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer')], max_length=20, null=True, verbose_name='Payment Method')),
('status', models.CharField(choices=[('address', 'Shipping Address'), ('payment', 'Payment Method'), ('confirm', 'Confirmation'), ('completed', 'Completed')], default='address', max_length=20, verbose_name='Status')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('cart', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='shop.cart')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('shipping_address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.shippingaddress')),
],
),
migrations.CreateModel(
name='ProductVariant',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('size', models.CharField(blank=True, choices=[('XS', 'Extra Small'), ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ('XL', 'Extra Large'), ('XXL', '2X Large'), ('XXXL', '3X Large')], max_length=10, verbose_name='Größe')),
('color', models.CharField(blank=True, max_length=50, verbose_name='Farbe')),
('sku', models.CharField(max_length=50, unique=True, verbose_name='Artikelnummer')),
('price_adjustment', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Preisanpassung')),
('stock', models.PositiveIntegerField(default=0, verbose_name='Lagerbestand')),
('image', models.ImageField(blank=True, upload_to='variants/', verbose_name='Variantenbild')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variants', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Produktvariante',
'verbose_name_plural': 'Produktvarianten',
'unique_together': {('product', 'size', 'color')},
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-29 12:47
import django.core.validators
import django.db.models.deletion
import django.utils.timezone
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ProductType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('has_sizes', models.BooleanField(default=False, verbose_name='Hat Größen')),
('has_colors', models.BooleanField(default=False, verbose_name='Hat Farben')),
('has_custom_design', models.BooleanField(default=False, verbose_name='Erlaubt Custom Design')),
('requires_measurements', models.BooleanField(default=False, verbose_name='Benötigt Maße')),
],
options={
'verbose_name': 'Produkttyp',
'verbose_name_plural': 'Produkttypen',
},
),
migrations.AlterModelOptions(
name='category',
options={'ordering': ['name'], 'verbose_name': 'Kategorie', 'verbose_name_plural': 'Kategorien'},
),
migrations.AlterModelOptions(
name='product',
options={'ordering': ['-created'], 'verbose_name': 'Produkt', 'verbose_name_plural': 'Produkte'},
),
migrations.RemoveField(
model_name='category',
name='name_en',
),
migrations.RemoveField(
model_name='product',
name='base_price',
),
migrations.RemoveField(
model_name='product',
name='created_at',
),
migrations.RemoveField(
model_name='product',
name='description_en',
),
migrations.RemoveField(
model_name='product',
name='gallery',
),
migrations.RemoveField(
model_name='product',
name='model_3d',
),
migrations.RemoveField(
model_name='product',
name='name_en',
),
migrations.RemoveField(
model_name='product',
name='updated_at',
),
migrations.AddField(
model_name='category',
name='description',
field=models.TextField(blank=True, verbose_name='Beschreibung'),
),
migrations.AddField(
model_name='category',
name='image',
field=models.ImageField(blank=True, upload_to='categories/', verbose_name='Bild'),
),
migrations.AddField(
model_name='category',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='shop.category', verbose_name='Übergeordnete Kategorie'),
),
migrations.AddField(
model_name='product',
name='available',
field=models.BooleanField(default=True, verbose_name='Verfügbar'),
),
migrations.AddField(
model_name='product',
name='created',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt'),
),
migrations.AddField(
model_name='product',
name='price',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='Preis'),
),
migrations.AddField(
model_name='product',
name='slug',
field=models.SlugField(default='', max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AddField(
model_name='product',
name='stock',
field=models.PositiveIntegerField(default=0, verbose_name='Lagerbestand'),
),
migrations.AddField(
model_name='product',
name='updated',
field=models.DateTimeField(auto_now=True, verbose_name='Aktualisiert'),
),
migrations.AlterField(
model_name='category',
name='name',
field=models.CharField(max_length=100, verbose_name='Name'),
),
migrations.AlterField(
model_name='category',
name='slug',
field=models.SlugField(max_length=100, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='product',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='shop.category', verbose_name='Kategorie'),
),
migrations.AlterField(
model_name='product',
name='description',
field=models.TextField(verbose_name='Beschreibung'),
),
migrations.AlterField(
model_name='product',
name='image',
field=models.ImageField(upload_to='products/', verbose_name='Hauptbild'),
),
migrations.CreateModel(
name='Cart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='CartItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('size', models.CharField(blank=True, max_length=20, null=True)),
('notes', models.TextField(blank=True, null=True)),
('custom_design', models.ImageField(blank=True, null=True, upload_to='custom_designs/')),
('added_at', models.DateTimeField(auto_now_add=True)),
('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='shop.cart')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product')),
],
options={
'ordering': ['-added_at'],
},
),
migrations.CreateModel(
name='CustomDesign',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('design_file', models.FileField(upload_to='designs/', verbose_name='Design-Datei')),
('notes', models.TextField(verbose_name='Anmerkungen')),
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt')),
('status', models.CharField(choices=[('pending', 'Ausstehend'), ('approved', 'Genehmigt'), ('rejected', 'Abgelehnt'), ('in_progress', 'In Bearbeitung'), ('completed', 'Abgeschlossen')], default='pending', max_length=20, verbose_name='Status')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Kunde')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_designs', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Custom Design',
'verbose_name_plural': 'Custom Designs',
'ordering': ['-created'],
},
),
migrations.CreateModel(
name='PaymentError',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('error_code', models.CharField(max_length=100, verbose_name='Error Code')),
('error_message', models.TextField(verbose_name='Error Message')),
('created_at', models.DateTimeField(auto_now_add=True)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payment_errors', to='shop.order')),
],
),
migrations.CreateModel(
name='PayPalPayment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_id', models.CharField(max_length=100, verbose_name='PayPal Payment ID')),
('payer_id', models.CharField(max_length=100, verbose_name='PayPal Payer ID')),
('status', models.CharField(choices=[('pending', 'Pending'), ('completed', 'Completed'), ('failed', 'Failed'), ('refunded', 'Refunded')], max_length=20, verbose_name='Payment Status')),
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')),
('currency', models.CharField(default='EUR', max_length=3, verbose_name='Currency')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('order', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='paypal_payment', to='shop.order')),
],
),
migrations.CreateModel(
name='ProductImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='products/', verbose_name='Bild')),
('alt_text', models.CharField(blank=True, max_length=200, verbose_name='Alternativer Text')),
('is_feature', models.BooleanField(default=False, verbose_name='Ist Hauptbild')),
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Produktbild',
'verbose_name_plural': 'Produktbilder',
'ordering': ['-is_feature', '-created'],
},
),
migrations.AlterField(
model_name='product',
name='product_type',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.producttype', verbose_name='Produkttyp'),
),
migrations.CreateModel(
name='ShippingAddress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='First Name')),
('last_name', models.CharField(max_length=100, verbose_name='Last Name')),
('email', models.EmailField(max_length=254, verbose_name='Email')),
('address', models.CharField(max_length=200, verbose_name='Address')),
('city', models.CharField(max_length=100, verbose_name='City')),
('zip', models.CharField(max_length=10, verbose_name='ZIP Code')),
('country', models.CharField(choices=[('DE', 'Deutschland'), ('AT', 'Österreich'), ('CH', 'Schweiz')], max_length=2, verbose_name='Country')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Checkout',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_method', models.CharField(choices=[('paypal', 'PayPal'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer')], max_length=20, null=True, verbose_name='Payment Method')),
('status', models.CharField(choices=[('address', 'Shipping Address'), ('payment', 'Payment Method'), ('confirm', 'Confirmation'), ('completed', 'Completed')], default='address', max_length=20, verbose_name='Status')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('cart', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='shop.cart')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('shipping_address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.shippingaddress')),
],
),
migrations.CreateModel(
name='ProductVariant',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('size', models.CharField(blank=True, choices=[('XS', 'Extra Small'), ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ('XL', 'Extra Large'), ('XXL', '2X Large'), ('XXXL', '3X Large')], max_length=10, verbose_name='Größe')),
('color', models.CharField(blank=True, max_length=50, verbose_name='Farbe')),
('sku', models.CharField(max_length=50, unique=True, verbose_name='Artikelnummer')),
('price_adjustment', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Preisanpassung')),
('stock', models.PositiveIntegerField(default=0, verbose_name='Lagerbestand')),
('image', models.ImageField(blank=True, upload_to='variants/', verbose_name='Variantenbild')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variants', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Produktvariante',
'verbose_name_plural': 'Produktvarianten',
'unique_together': {('product', 'size', 'color')},
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-29 12:47
import django.core.validators
import django.db.models.deletion
import django.utils.timezone
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ProductType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('has_sizes', models.BooleanField(default=False, verbose_name='Hat Größen')),
('has_colors', models.BooleanField(default=False, verbose_name='Hat Farben')),
('has_custom_design', models.BooleanField(default=False, verbose_name='Erlaubt Custom Design')),
('requires_measurements', models.BooleanField(default=False, verbose_name='Benötigt Maße')),
],
options={
'verbose_name': 'Produkttyp',
'verbose_name_plural': 'Produkttypen',
},
),
migrations.AlterModelOptions(
name='category',
options={'ordering': ['name'], 'verbose_name': 'Kategorie', 'verbose_name_plural': 'Kategorien'},
),
migrations.AlterModelOptions(
name='product',
options={'ordering': ['-created'], 'verbose_name': 'Produkt', 'verbose_name_plural': 'Produkte'},
),
migrations.RemoveField(
model_name='category',
name='name_en',
),
migrations.RemoveField(
model_name='product',
name='base_price',
),
migrations.RemoveField(
model_name='product',
name='created_at',
),
migrations.RemoveField(
model_name='product',
name='description_en',
),
migrations.RemoveField(
model_name='product',
name='gallery',
),
migrations.RemoveField(
model_name='product',
name='model_3d',
),
migrations.RemoveField(
model_name='product',
name='name_en',
),
migrations.RemoveField(
model_name='product',
name='updated_at',
),
migrations.AddField(
model_name='category',
name='description',
field=models.TextField(blank=True, verbose_name='Beschreibung'),
),
migrations.AddField(
model_name='category',
name='image',
field=models.ImageField(blank=True, upload_to='categories/', verbose_name='Bild'),
),
migrations.AddField(
model_name='category',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='shop.category', verbose_name='Übergeordnete Kategorie'),
),
migrations.AddField(
model_name='product',
name='available',
field=models.BooleanField(default=True, verbose_name='Verfügbar'),
),
migrations.AddField(
model_name='product',
name='created',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt'),
),
migrations.AddField(
model_name='product',
name='price',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='Preis'),
),
migrations.AddField(
model_name='product',
name='slug',
field=models.SlugField(default='', max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AddField(
model_name='product',
name='stock',
field=models.PositiveIntegerField(default=0, verbose_name='Lagerbestand'),
),
migrations.AddField(
model_name='product',
name='updated',
field=models.DateTimeField(auto_now=True, verbose_name='Aktualisiert'),
),
migrations.AlterField(
model_name='category',
name='name',
field=models.CharField(max_length=100, verbose_name='Name'),
),
migrations.AlterField(
model_name='category',
name='slug',
field=models.SlugField(max_length=100, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='product',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='shop.category', verbose_name='Kategorie'),
),
migrations.AlterField(
model_name='product',
name='description',
field=models.TextField(verbose_name='Beschreibung'),
),
migrations.AlterField(
model_name='product',
name='image',
field=models.ImageField(upload_to='products/', verbose_name='Hauptbild'),
),
migrations.CreateModel(
name='Cart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='CartItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('size', models.CharField(blank=True, max_length=20, null=True)),
('notes', models.TextField(blank=True, null=True)),
('custom_design', models.ImageField(blank=True, null=True, upload_to='custom_designs/')),
('added_at', models.DateTimeField(auto_now_add=True)),
('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='shop.cart')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.product')),
],
options={
'ordering': ['-added_at'],
},
),
migrations.CreateModel(
name='CustomDesign',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('design_file', models.FileField(upload_to='designs/', verbose_name='Design-Datei')),
('notes', models.TextField(verbose_name='Anmerkungen')),
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt')),
('status', models.CharField(choices=[('pending', 'Ausstehend'), ('approved', 'Genehmigt'), ('rejected', 'Abgelehnt'), ('in_progress', 'In Bearbeitung'), ('completed', 'Abgeschlossen')], default='pending', max_length=20, verbose_name='Status')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Kunde')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_designs', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Custom Design',
'verbose_name_plural': 'Custom Designs',
'ordering': ['-created'],
},
),
migrations.CreateModel(
name='PaymentError',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('error_code', models.CharField(max_length=100, verbose_name='Error Code')),
('error_message', models.TextField(verbose_name='Error Message')),
('created_at', models.DateTimeField(auto_now_add=True)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payment_errors', to='shop.order')),
],
),
migrations.CreateModel(
name='PayPalPayment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_id', models.CharField(max_length=100, verbose_name='PayPal Payment ID')),
('payer_id', models.CharField(max_length=100, verbose_name='PayPal Payer ID')),
('status', models.CharField(choices=[('pending', 'Pending'), ('completed', 'Completed'), ('failed', 'Failed'), ('refunded', 'Refunded')], max_length=20, verbose_name='Payment Status')),
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')),
('currency', models.CharField(default='EUR', max_length=3, verbose_name='Currency')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('order', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='paypal_payment', to='shop.order')),
],
),
migrations.CreateModel(
name='ProductImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='products/', verbose_name='Bild')),
('alt_text', models.CharField(blank=True, max_length=200, verbose_name='Alternativer Text')),
('is_feature', models.BooleanField(default=False, verbose_name='Ist Hauptbild')),
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Erstellt')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Produktbild',
'verbose_name_plural': 'Produktbilder',
'ordering': ['-is_feature', '-created'],
},
),
migrations.AlterField(
model_name='product',
name='product_type',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.producttype', verbose_name='Produkttyp'),
),
migrations.CreateModel(
name='ShippingAddress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='First Name')),
('last_name', models.CharField(max_length=100, verbose_name='Last Name')),
('email', models.EmailField(max_length=254, verbose_name='Email')),
('address', models.CharField(max_length=200, verbose_name='Address')),
('city', models.CharField(max_length=100, verbose_name='City')),
('zip', models.CharField(max_length=10, verbose_name='ZIP Code')),
('country', models.CharField(choices=[('DE', 'Deutschland'), ('AT', 'Österreich'), ('CH', 'Schweiz')], max_length=2, verbose_name='Country')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Checkout',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_method', models.CharField(choices=[('paypal', 'PayPal'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer')], max_length=20, null=True, verbose_name='Payment Method')),
('status', models.CharField(choices=[('address', 'Shipping Address'), ('payment', 'Payment Method'), ('confirm', 'Confirmation'), ('completed', 'Completed')], default='address', max_length=20, verbose_name='Status')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('cart', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='shop.cart')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('shipping_address', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.shippingaddress')),
],
),
migrations.CreateModel(
name='ProductVariant',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('size', models.CharField(blank=True, choices=[('XS', 'Extra Small'), ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ('XL', 'Extra Large'), ('XXL', '2X Large'), ('XXXL', '3X Large')], max_length=10, verbose_name='Größe')),
('color', models.CharField(blank=True, max_length=50, verbose_name='Farbe')),
('sku', models.CharField(max_length=50, unique=True, verbose_name='Artikelnummer')),
('price_adjustment', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Preisanpassung')),
('stock', models.PositiveIntegerField(default=0, verbose_name='Lagerbestand')),
('image', models.ImageField(blank=True, upload_to='variants/', verbose_name='Variantenbild')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variants', to='shop.product', verbose_name='Produkt')),
],
options={
'verbose_name': 'Produktvariante',
'verbose_name_plural': 'Produktvarianten',
'unique_together': {('product', 'size', 'color')},
},
),
]

View File

@ -1,61 +1,29 @@
<<<<<<< HEAD
# Generated by Django 5.2.1 on 2025-05-30 10:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0002_producttype_alter_category_options_and_more'),
]
operations = [
migrations.CreateModel(
name='ContactMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('email', models.EmailField(max_length=254, verbose_name='E-Mail')),
('subject', models.CharField(max_length=200, verbose_name='Betreff')),
('message', models.TextField(verbose_name='Nachricht')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am')),
],
options={
'verbose_name': 'Kontaktnachricht',
'verbose_name_plural': 'Kontaktnachrichten',
'ordering': ['-created_at'],
},
),
]
=======
# Generated by Django 5.2.1 on 2025-05-30 10:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0002_producttype_alter_category_options_and_more'),
]
operations = [
migrations.CreateModel(
name='ContactMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('email', models.EmailField(max_length=254, verbose_name='E-Mail')),
('subject', models.CharField(max_length=200, verbose_name='Betreff')),
('message', models.TextField(verbose_name='Nachricht')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am')),
],
options={
'verbose_name': 'Kontaktnachricht',
'verbose_name_plural': 'Kontaktnachrichten',
'ordering': ['-created_at'],
},
),
]
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Generated by Django 5.2.1 on 2025-05-30 10:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0002_producttype_alter_category_options_and_more'),
]
operations = [
migrations.CreateModel(
name='ContactMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Name')),
('email', models.EmailField(max_length=254, verbose_name='E-Mail')),
('subject', models.CharField(max_length=200, verbose_name='Betreff')),
('message', models.TextField(verbose_name='Nachricht')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am')),
],
options={
'verbose_name': 'Kontaktnachricht',
'verbose_name_plural': 'Kontaktnachrichten',
'ordering': ['-created_at'],
},
),
]

File diff suppressed because it is too large Load Diff

View File

@ -1,289 +1,143 @@
<<<<<<< HEAD
"""
Django settings for shop project.
Generated by 'django-admin startproject' using Django 5.2.1.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-8m^(a*6xx46y=v*z8j*s*f=hup!+cyzghx8e9f^eugbg1)o!1s'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 'rest_framework', # Temporär auskommentiert
'products',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'shop.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'shop.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'de'
TIME_ZONE = 'Europe/Berlin'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Authentication
LOGIN_REDIRECT_URL = 'product_list'
LOGOUT_REDIRECT_URL = 'product_list'
# REST Framework - Temporär auskommentiert
# REST_FRAMEWORK = {
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly',
# ],
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 'PAGE_SIZE': 10
# }
=======
"""
Django settings for shop project.
Generated by 'django-admin startproject' using Django 5.2.1.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-8m^(a*6xx46y=v*z8j*s*f=hup!+cyzghx8e9f^eugbg1)o!1s'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 'rest_framework', # Temporär auskommentiert
'products',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'shop.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'shop.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'de'
TIME_ZONE = 'Europe/Berlin'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Authentication
LOGIN_REDIRECT_URL = 'product_list'
LOGOUT_REDIRECT_URL = 'product_list'
# REST Framework - Temporär auskommentiert
# REST_FRAMEWORK = {
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly',
# ],
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 'PAGE_SIZE': 10
# }
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
"""
Django settings for shop project.
Generated by 'django-admin startproject' using Django 5.2.1.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-8m^(a*6xx46y=v*z8j*s*f=hup!+cyzghx8e9f^eugbg1)o!1s'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 'rest_framework', # Temporär auskommentiert
'products',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'shop.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'shop.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'de'
TIME_ZONE = 'Europe/Berlin'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Authentication
LOGIN_REDIRECT_URL = 'product_list'
LOGOUT_REDIRECT_URL = 'product_list'
# REST Framework - Temporär auskommentiert
# REST_FRAMEWORK = {
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly',
# ],
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 'PAGE_SIZE': 10
# }

View File

@ -1,140 +1,69 @@
<<<<<<< HEAD
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ObjectDoesNotExist
from .models import Order, Product, PaymentError
from .emails import (
send_order_confirmation,
send_order_status_update,
send_shipping_confirmation,
send_admin_notification,
send_low_stock_notification
)
@receiver(post_save, sender=Order)
def handle_order_notifications(sender, instance, created, **kwargs):
"""
Sendet E-Mail-Benachrichtigungen basierend auf Bestellstatus
"""
if created:
# Neue Bestellung - Admin benachrichtigen
notification_type = 'fursuit_order' if any(p.product_type == 'fursuit' for p in instance.products.all()) else 'new_order'
if hasattr(instance, 'customer_design') and instance.customer_design:
notification_type = 'custom_design'
send_admin_notification(None, instance, notification_type)
else:
# Status-Änderung
try:
old_instance = Order.objects.get(id=instance.id)
if old_instance.status != instance.status:
# Status hat sich geändert - Kunde benachrichtigen
send_order_status_update(None, instance)
# Bei Versand zusätzlich Versandbestätigung senden
if instance.status == 'completed' and instance.tracking_number:
send_shipping_confirmation(None, instance)
except ObjectDoesNotExist:
pass
@receiver(post_save, sender=PaymentError)
def handle_payment_error(sender, instance, created, **kwargs):
"""
Benachrichtigt den Admin über Zahlungsfehler
"""
if created:
send_admin_notification(None, instance.order, 'payment_failed', {
'payment_error': instance
})
@receiver(pre_save, sender=Product)
def check_stock_level(sender, instance, **kwargs):
"""
Überprüft den Lagerbestand und sendet Benachrichtigungen bei niedrigem Stand
"""
if not instance.pk: # Neues Produkt
return
try:
old_instance = Product.objects.get(pk=instance.pk)
# Wenn der Lagerbestand unter den Schwellenwert fällt
if (old_instance.stock > settings.LOW_STOCK_THRESHOLD and
instance.stock <= settings.LOW_STOCK_THRESHOLD):
send_low_stock_notification(None, instance)
except ObjectDoesNotExist:
=======
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ObjectDoesNotExist
from .models import Order, Product, PaymentError
from .emails import (
send_order_confirmation,
send_order_status_update,
send_shipping_confirmation,
send_admin_notification,
send_low_stock_notification
)
@receiver(post_save, sender=Order)
def handle_order_notifications(sender, instance, created, **kwargs):
"""
Sendet E-Mail-Benachrichtigungen basierend auf Bestellstatus
"""
if created:
# Neue Bestellung - Admin benachrichtigen
notification_type = 'fursuit_order' if any(p.product_type == 'fursuit' for p in instance.products.all()) else 'new_order'
if hasattr(instance, 'customer_design') and instance.customer_design:
notification_type = 'custom_design'
send_admin_notification(None, instance, notification_type)
else:
# Status-Änderung
try:
old_instance = Order.objects.get(id=instance.id)
if old_instance.status != instance.status:
# Status hat sich geändert - Kunde benachrichtigen
send_order_status_update(None, instance)
# Bei Versand zusätzlich Versandbestätigung senden
if instance.status == 'completed' and instance.tracking_number:
send_shipping_confirmation(None, instance)
except ObjectDoesNotExist:
pass
@receiver(post_save, sender=PaymentError)
def handle_payment_error(sender, instance, created, **kwargs):
"""
Benachrichtigt den Admin über Zahlungsfehler
"""
if created:
send_admin_notification(None, instance.order, 'payment_failed', {
'payment_error': instance
})
@receiver(pre_save, sender=Product)
def check_stock_level(sender, instance, **kwargs):
"""
Überprüft den Lagerbestand und sendet Benachrichtigungen bei niedrigem Stand
"""
if not instance.pk: # Neues Produkt
return
try:
old_instance = Product.objects.get(pk=instance.pk)
# Wenn der Lagerbestand unter den Schwellenwert fällt
if (old_instance.stock > settings.LOW_STOCK_THRESHOLD and
instance.stock <= settings.LOW_STOCK_THRESHOLD):
send_low_stock_notification(None, instance)
except ObjectDoesNotExist:
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ObjectDoesNotExist
from .models import Order, Product, PaymentError
from .emails import (
send_order_confirmation,
send_order_status_update,
send_shipping_confirmation,
send_admin_notification,
send_low_stock_notification
)
@receiver(post_save, sender=Order)
def handle_order_notifications(sender, instance, created, **kwargs):
"""
Sendet E-Mail-Benachrichtigungen basierend auf Bestellstatus
"""
if created:
# Neue Bestellung - Admin benachrichtigen
notification_type = 'fursuit_order' if any(p.product_type == 'fursuit' for p in instance.products.all()) else 'new_order'
if hasattr(instance, 'customer_design') and instance.customer_design:
notification_type = 'custom_design'
send_admin_notification(None, instance, notification_type)
else:
# Status-Änderung
try:
old_instance = Order.objects.get(id=instance.id)
if old_instance.status != instance.status:
# Status hat sich geändert - Kunde benachrichtigen
send_order_status_update(None, instance)
# Bei Versand zusätzlich Versandbestätigung senden
if instance.status == 'completed' and instance.tracking_number:
send_shipping_confirmation(None, instance)
except ObjectDoesNotExist:
pass
@receiver(post_save, sender=PaymentError)
def handle_payment_error(sender, instance, created, **kwargs):
"""
Benachrichtigt den Admin über Zahlungsfehler
"""
if created:
send_admin_notification(None, instance.order, 'payment_failed', {
'payment_error': instance
})
@receiver(pre_save, sender=Product)
def check_stock_level(sender, instance, **kwargs):
"""
Überprüft den Lagerbestand und sendet Benachrichtigungen bei niedrigem Stand
"""
if not instance.pk: # Neues Produkt
return
try:
old_instance = Product.objects.get(pk=instance.pk)
# Wenn der Lagerbestand unter den Schwellenwert fällt
if (old_instance.stock > settings.LOW_STOCK_THRESHOLD and
instance.stock <= settings.LOW_STOCK_THRESHOLD):
send_low_stock_notification(None, instance)
except ObjectDoesNotExist:
pass

View File

@ -1,32 +1,15 @@
<<<<<<< HEAD
{% extends "base.html" %}
{% load static %}
{% block extra_head %}
<link rel="stylesheet" href="{% static 'css/furry.css' %}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&family=Quicksand:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/kofi-button.css' %}">
{{ block.super }}
{% endblock %}
{% block content %}
{{ block.super }}
=======
{% extends "base.html" %}
{% load static %}
{% block extra_head %}
<link rel="stylesheet" href="{% static 'css/furry.css' %}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&family=Quicksand:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/kofi-button.css' %}">
{{ block.super }}
{% endblock %}
{% block content %}
{{ block.super }}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "base.html" %}
{% load static %}
{% block extra_head %}
<link rel="stylesheet" href="{% static 'css/furry.css' %}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&family=Quicksand:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/kofi-button.css' %}">
{{ block.super }}
{% endblock %}
{% block content %}
{{ block.super }}
{% endblock %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,426 +1,212 @@
<<<<<<< HEAD
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.notification {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.notification-type {
display: inline-block;
padding: 5px 12px;
border-radius: 4px;
font-weight: bold;
color: white;
margin-bottom: 15px;
}
.type-new_order {
background-color: #0d6efd;
}
.type-payment_failed {
background-color: #dc3545;
}
.type-custom_design {
background-color: #6f42c1;
}
.type-fursuit_order {
background-color: #fd7e14;
}
.customer-info {
background: #e9ecef;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.total {
text-align: right;
margin-top: 20px;
padding-top: 20px;
border-top: 2px solid #dee2e6;
font-size: 1.2em;
font-weight: bold;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<h1>
{% if notification_type == 'new_order' %}
{% trans "New Order Received" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "New Custom Design Order" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "New Fursuit Order" %}
{% else %}
{% trans "Order Notification" %}
{% endif %}
</h1>
</div>
<div class="notification">
<span class="notification-type type-{{ notification_type }}">
{% if notification_type == 'new_order' %}
{% trans "New Order" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "Custom Design" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "Fursuit Order" %}
{% endif %}
</span>
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<div class="customer-info">
<h3>{% trans "Customer Information" %}</h3>
<p>
<strong>{% trans "Name" %}:</strong>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
</p>
<p>
<strong>{% trans "Email" %}:</strong>
{{ order.shipping_address.email }}
</p>
<p>
<strong>{% trans "Address" %}:</strong><br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
</div>
<h3>{% trans "Ordered Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div style="flex-grow: 1;">
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
<div style="text-align: right;">
<strong>{{ product.base_price }} €</strong>
</div>
</div>
{% endfor %}
<div class="total">
{% trans "Total" %}: {{ order.total_price }} €
</div>
{% if notification_type == 'payment_failed' and payment_error %}
<div style="margin-top: 20px; padding: 15px; background-color: #f8d7da; border-radius: 4px; color: #721c24;">
<h3 style="margin-top: 0;">{% trans "Payment Error Details" %}</h3>
<p><strong>{% trans "Error Code" %}:</strong> {{ payment_error.error_code }}</p>
<p><strong>{% trans "Error Message" %}:</strong> {{ payment_error.error_message }}</p>
</div>
{% endif %}
{% if notification_type == 'custom_design' and order.customer_design %}
<div style="margin-top: 20px;">
<h3>{% trans "Custom Design Details" %}</h3>
<p><strong>{% trans "Design Name" %}:</strong> {{ order.customer_design.name }}</p>
{% if order.customer_design.notes %}
<p><strong>{% trans "Notes" %}:</strong> {{ order.customer_design.notes }}</p>
{% endif %}
{% if order.customer_design.design_file %}
<p><strong>{% trans "Design File" %}:</strong>
<a href="{{ order.customer_design.design_file.url }}">
{% trans "Download Design File" %}
</a>
</p>
{% endif %}
</div>
{% endif %}
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
</body>
=======
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.notification {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.notification-type {
display: inline-block;
padding: 5px 12px;
border-radius: 4px;
font-weight: bold;
color: white;
margin-bottom: 15px;
}
.type-new_order {
background-color: #0d6efd;
}
.type-payment_failed {
background-color: #dc3545;
}
.type-custom_design {
background-color: #6f42c1;
}
.type-fursuit_order {
background-color: #fd7e14;
}
.customer-info {
background: #e9ecef;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.total {
text-align: right;
margin-top: 20px;
padding-top: 20px;
border-top: 2px solid #dee2e6;
font-size: 1.2em;
font-weight: bold;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<h1>
{% if notification_type == 'new_order' %}
{% trans "New Order Received" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "New Custom Design Order" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "New Fursuit Order" %}
{% else %}
{% trans "Order Notification" %}
{% endif %}
</h1>
</div>
<div class="notification">
<span class="notification-type type-{{ notification_type }}">
{% if notification_type == 'new_order' %}
{% trans "New Order" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "Custom Design" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "Fursuit Order" %}
{% endif %}
</span>
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<div class="customer-info">
<h3>{% trans "Customer Information" %}</h3>
<p>
<strong>{% trans "Name" %}:</strong>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
</p>
<p>
<strong>{% trans "Email" %}:</strong>
{{ order.shipping_address.email }}
</p>
<p>
<strong>{% trans "Address" %}:</strong><br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
</div>
<h3>{% trans "Ordered Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div style="flex-grow: 1;">
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
<div style="text-align: right;">
<strong>{{ product.base_price }} €</strong>
</div>
</div>
{% endfor %}
<div class="total">
{% trans "Total" %}: {{ order.total_price }} €
</div>
{% if notification_type == 'payment_failed' and payment_error %}
<div style="margin-top: 20px; padding: 15px; background-color: #f8d7da; border-radius: 4px; color: #721c24;">
<h3 style="margin-top: 0;">{% trans "Payment Error Details" %}</h3>
<p><strong>{% trans "Error Code" %}:</strong> {{ payment_error.error_code }}</p>
<p><strong>{% trans "Error Message" %}:</strong> {{ payment_error.error_message }}</p>
</div>
{% endif %}
{% if notification_type == 'custom_design' and order.customer_design %}
<div style="margin-top: 20px;">
<h3>{% trans "Custom Design Details" %}</h3>
<p><strong>{% trans "Design Name" %}:</strong> {{ order.customer_design.name }}</p>
{% if order.customer_design.notes %}
<p><strong>{% trans "Notes" %}:</strong> {{ order.customer_design.notes }}</p>
{% endif %}
{% if order.customer_design.design_file %}
<p><strong>{% trans "Design File" %}:</strong>
<a href="{{ order.customer_design.design_file.url }}">
{% trans "Download Design File" %}
</a>
</p>
{% endif %}
</div>
{% endif %}
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
</body>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.notification {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.notification-type {
display: inline-block;
padding: 5px 12px;
border-radius: 4px;
font-weight: bold;
color: white;
margin-bottom: 15px;
}
.type-new_order {
background-color: #0d6efd;
}
.type-payment_failed {
background-color: #dc3545;
}
.type-custom_design {
background-color: #6f42c1;
}
.type-fursuit_order {
background-color: #fd7e14;
}
.customer-info {
background: #e9ecef;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.total {
text-align: right;
margin-top: 20px;
padding-top: 20px;
border-top: 2px solid #dee2e6;
font-size: 1.2em;
font-weight: bold;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<h1>
{% if notification_type == 'new_order' %}
{% trans "New Order Received" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "New Custom Design Order" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "New Fursuit Order" %}
{% else %}
{% trans "Order Notification" %}
{% endif %}
</h1>
</div>
<div class="notification">
<span class="notification-type type-{{ notification_type }}">
{% if notification_type == 'new_order' %}
{% trans "New Order" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "Custom Design" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "Fursuit Order" %}
{% endif %}
</span>
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<div class="customer-info">
<h3>{% trans "Customer Information" %}</h3>
<p>
<strong>{% trans "Name" %}:</strong>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
</p>
<p>
<strong>{% trans "Email" %}:</strong>
{{ order.shipping_address.email }}
</p>
<p>
<strong>{% trans "Address" %}:</strong><br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
</div>
<h3>{% trans "Ordered Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div style="flex-grow: 1;">
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
<div style="text-align: right;">
<strong>{{ product.base_price }} €</strong>
</div>
</div>
{% endfor %}
<div class="total">
{% trans "Total" %}: {{ order.total_price }} €
</div>
{% if notification_type == 'payment_failed' and payment_error %}
<div style="margin-top: 20px; padding: 15px; background-color: #f8d7da; border-radius: 4px; color: #721c24;">
<h3 style="margin-top: 0;">{% trans "Payment Error Details" %}</h3>
<p><strong>{% trans "Error Code" %}:</strong> {{ payment_error.error_code }}</p>
<p><strong>{% trans "Error Message" %}:</strong> {{ payment_error.error_message }}</p>
</div>
{% endif %}
{% if notification_type == 'custom_design' and order.customer_design %}
<div style="margin-top: 20px;">
<h3>{% trans "Custom Design Details" %}</h3>
<p><strong>{% trans "Design Name" %}:</strong> {{ order.customer_design.name }}</p>
{% if order.customer_design.notes %}
<p><strong>{% trans "Notes" %}:</strong> {{ order.customer_design.notes }}</p>
{% endif %}
{% if order.customer_design.design_file %}
<p><strong>{% trans "Design File" %}:</strong>
<a href="{{ order.customer_design.design_file.url }}">
{% trans "Download Design File" %}
</a>
</p>
{% endif %}
</div>
{% endif %}
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
</body>
</html>

View File

@ -1,104 +1,51 @@
<<<<<<< HEAD
{% load i18n %}
{% if notification_type == 'new_order' %}
{% trans "New Order Received" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "New Custom Design Order" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "New Fursuit Order" %}
{% else %}
{% trans "Order Notification" %}
{% endif %}
{% trans "Order" %} #{{ order.id }}
{% trans "Customer Information" %}:
{% trans "Name" %}: {{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{% trans "Email" %}: {{ order.shipping_address.email }}
{% trans "Address" %}:
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{{ product.base_price }} €
{% endfor %}
{% trans "Total" %}: {{ order.total_price }} €
{% if notification_type == 'payment_failed' and payment_error %}
{% trans "Payment Error Details" %}:
{% trans "Error Code" %}: {{ payment_error.error_code }}
{% trans "Error Message" %}: {{ payment_error.error_message }}
{% endif %}
{% if notification_type == 'custom_design' and order.customer_design %}
{% trans "Custom Design Details" %}:
{% trans "Design Name" %}: {{ order.customer_design.name }}
{% if order.customer_design.notes %}
{% trans "Notes" %}: {{ order.customer_design.notes }}
{% endif %}
{% if order.customer_design.design_file %}
{% trans "Design File" %}: {{ order.customer_design.design_file.url }}
{% endif %}
{% endif %}
=======
{% load i18n %}
{% if notification_type == 'new_order' %}
{% trans "New Order Received" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "New Custom Design Order" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "New Fursuit Order" %}
{% else %}
{% trans "Order Notification" %}
{% endif %}
{% trans "Order" %} #{{ order.id }}
{% trans "Customer Information" %}:
{% trans "Name" %}: {{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{% trans "Email" %}: {{ order.shipping_address.email }}
{% trans "Address" %}:
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{{ product.base_price }} €
{% endfor %}
{% trans "Total" %}: {{ order.total_price }} €
{% if notification_type == 'payment_failed' and payment_error %}
{% trans "Payment Error Details" %}:
{% trans "Error Code" %}: {{ payment_error.error_code }}
{% trans "Error Message" %}: {{ payment_error.error_message }}
{% endif %}
{% if notification_type == 'custom_design' and order.customer_design %}
{% trans "Custom Design Details" %}:
{% trans "Design Name" %}: {{ order.customer_design.name }}
{% if order.customer_design.notes %}
{% trans "Notes" %}: {{ order.customer_design.notes }}
{% endif %}
{% if order.customer_design.design_file %}
{% trans "Design File" %}: {{ order.customer_design.design_file.url }}
{% endif %}
{% endif %}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% load i18n %}
{% if notification_type == 'new_order' %}
{% trans "New Order Received" %}
{% elif notification_type == 'payment_failed' %}
{% trans "Payment Failed" %}
{% elif notification_type == 'custom_design' %}
{% trans "New Custom Design Order" %}
{% elif notification_type == 'fursuit_order' %}
{% trans "New Fursuit Order" %}
{% else %}
{% trans "Order Notification" %}
{% endif %}
{% trans "Order" %} #{{ order.id }}
{% trans "Customer Information" %}:
{% trans "Name" %}: {{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{% trans "Email" %}: {{ order.shipping_address.email }}
{% trans "Address" %}:
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{{ product.base_price }} €
{% endfor %}
{% trans "Total" %}: {{ order.total_price }} €
{% if notification_type == 'payment_failed' and payment_error %}
{% trans "Payment Error Details" %}:
{% trans "Error Code" %}: {{ payment_error.error_code }}
{% trans "Error Message" %}: {{ payment_error.error_message }}
{% endif %}
{% if notification_type == 'custom_design' and order.customer_design %}
{% trans "Custom Design Details" %}:
{% trans "Design Name" %}: {{ order.customer_design.name }}
{% if order.customer_design.notes %}
{% trans "Notes" %}: {{ order.customer_design.notes }}
{% endif %}
{% if order.customer_design.design_file %}
{% trans "Design File" %}: {{ order.customer_design.design_file.url }}
{% endif %}
{% endif %}
{% trans "View Order Details" %}: {{ order_url }}

View File

@ -1,600 +1,299 @@
<<<<<<< HEAD
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Low Stock Alert" %}</title>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--success-color: #10B981;
--danger-color: #EF4444;
--warning-color: #F59E0B;
--info-color: #3B82F6;
--light-color: #F8FAFC;
--dark-color: #1E293B;
--text-color: #334155;
--text-muted: #64748B;
--border-color: #E2E8F0;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--gradient-end: #F59E0B;
}
body {
font-family: 'Fredoka', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, var(--light-color), #FDF2F8);
margin: 0;
padding: 0;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(139, 92, 246, 0.1);
}
.header {
background: linear-gradient(135deg, var(--danger-color), #F87171);
color: white;
text-align: center;
padding: 2rem;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/><circle cx="10" cy="60" r="0.5" fill="white" opacity="0.1"/><circle cx="90" cy="40" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.logo {
max-width: 150px;
height: auto;
margin-bottom: 1rem;
position: relative;
z-index: 1;
}
.header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
position: relative;
z-index: 1;
}
.header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
position: relative;
z-index: 1;
}
.alert {
background: linear-gradient(135deg, #FEF2F2, #FEE2E2);
border: 1px solid #FECACA;
border-radius: 15px;
padding: 1.5rem;
margin: 2rem;
color: #991B1B;
}
.alert strong {
color: var(--danger-color);
}
.product {
background: white;
border-radius: 15px;
padding: 2rem;
margin: 2rem;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.1);
display: flex;
align-items: center;
gap: 1.5rem;
}
.product-image {
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.product-details {
flex: 1;
}
.product-details h2 {
margin: 0 0 0.5rem 0;
color: var(--primary-color);
font-size: 1.5rem;
font-weight: 600;
}
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 15px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0.25rem 0.25rem 0.25rem 0;
}
.badge-fursuit {
background: linear-gradient(135deg, var(--primary-color), #A78BFA);
color: white;
}
.badge-printed {
background: linear-gradient(135deg, var(--success-color), #34D399);
color: white;
}
.stock-level {
background: linear-gradient(135deg, var(--warning-color), #FBBF24);
color: white;
padding: 0.5rem 1rem;
border-radius: 15px;
font-weight: 600;
margin: 0.5rem 0;
display: inline-block;
}
.product-info {
margin: 1rem 0;
}
.product-info p {
margin: 0.25rem 0;
color: var(--text-muted);
}
.product-info strong {
color: var(--text-color);
}
.button {
display: inline-block;
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-decoration: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(139, 92, 246, 0.4);
}
.footer {
background: var(--light-color);
padding: 2rem;
text-align: center;
border-top: 1px solid var(--border-color);
}
.footer p {
margin: 0;
color: var(--text-muted);
font-size: 0.875rem;
}
.social-links {
margin-top: 1rem;
}
.social-links a {
display: inline-block;
margin: 0 0.5rem;
color: var(--primary-color);
text-decoration: none;
font-size: 1.25rem;
}
@media (max-width: 600px) {
.email-container {
margin: 0;
border-radius: 0;
}
.header {
padding: 1.5rem;
}
.product {
flex-direction: column;
text-align: center;
margin: 1rem;
}
.product-image {
width: 100px;
height: 100px;
}
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<img src="{{ logo_url }}" alt="Kasico Art & Design" class="logo">
<h1>{% trans "Low Stock Alert" %}</h1>
<p>{% trans "Action required for inventory management" %}</p>
</div>
<div class="alert">
<strong>{% trans "Warning" %}:</strong>
{% trans "The following product is running low on stock and needs attention." %}
</div>
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div class="product-details">
<h2>{{ product.name }}</h2>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
<div class="stock-level">
{% trans "Current Stock" %}: {{ product.stock }}
</div>
<div class="product-info">
<p><strong>{% trans "SKU" %}:</strong> {{ product.sku }}</p>
<p><strong>{% trans "Base Price" %}:</strong> {{ product.base_price }} €</p>
{% if product.category %}
<p><strong>{% trans "Category" %}:</strong> {{ product.category.name }}</p>
{% endif %}
</div>
</div>
</div>
<div style="text-align: center; padding: 2rem;">
<a href="{{ product_url }}" class="button">
{% trans "Manage Product" %}
</a>
</div>
<div class="footer">
<p>{% trans "This is an automated notification from Kasico Art & Design." %}</p>
<p>{% trans "Please take action to restock this product or update its availability." %}</p>
<div class="social-links">
<a href="https://instagram.com/kasicoart" title="Instagram">📷</a>
<a href="https://twitter.com/kasicoart" title="Twitter">🐦</a>
<a href="https://discord.gg/kasico" title="Discord">🎮</a>
</div>
</div>
</div>
</body>
=======
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Low Stock Alert" %}</title>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--success-color: #10B981;
--danger-color: #EF4444;
--warning-color: #F59E0B;
--info-color: #3B82F6;
--light-color: #F8FAFC;
--dark-color: #1E293B;
--text-color: #334155;
--text-muted: #64748B;
--border-color: #E2E8F0;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--gradient-end: #F59E0B;
}
body {
font-family: 'Fredoka', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, var(--light-color), #FDF2F8);
margin: 0;
padding: 0;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(139, 92, 246, 0.1);
}
.header {
background: linear-gradient(135deg, var(--danger-color), #F87171);
color: white;
text-align: center;
padding: 2rem;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/><circle cx="10" cy="60" r="0.5" fill="white" opacity="0.1"/><circle cx="90" cy="40" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.logo {
max-width: 150px;
height: auto;
margin-bottom: 1rem;
position: relative;
z-index: 1;
}
.header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
position: relative;
z-index: 1;
}
.header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
position: relative;
z-index: 1;
}
.alert {
background: linear-gradient(135deg, #FEF2F2, #FEE2E2);
border: 1px solid #FECACA;
border-radius: 15px;
padding: 1.5rem;
margin: 2rem;
color: #991B1B;
}
.alert strong {
color: var(--danger-color);
}
.product {
background: white;
border-radius: 15px;
padding: 2rem;
margin: 2rem;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.1);
display: flex;
align-items: center;
gap: 1.5rem;
}
.product-image {
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.product-details {
flex: 1;
}
.product-details h2 {
margin: 0 0 0.5rem 0;
color: var(--primary-color);
font-size: 1.5rem;
font-weight: 600;
}
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 15px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0.25rem 0.25rem 0.25rem 0;
}
.badge-fursuit {
background: linear-gradient(135deg, var(--primary-color), #A78BFA);
color: white;
}
.badge-printed {
background: linear-gradient(135deg, var(--success-color), #34D399);
color: white;
}
.stock-level {
background: linear-gradient(135deg, var(--warning-color), #FBBF24);
color: white;
padding: 0.5rem 1rem;
border-radius: 15px;
font-weight: 600;
margin: 0.5rem 0;
display: inline-block;
}
.product-info {
margin: 1rem 0;
}
.product-info p {
margin: 0.25rem 0;
color: var(--text-muted);
}
.product-info strong {
color: var(--text-color);
}
.button {
display: inline-block;
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-decoration: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(139, 92, 246, 0.4);
}
.footer {
background: var(--light-color);
padding: 2rem;
text-align: center;
border-top: 1px solid var(--border-color);
}
.footer p {
margin: 0;
color: var(--text-muted);
font-size: 0.875rem;
}
.social-links {
margin-top: 1rem;
}
.social-links a {
display: inline-block;
margin: 0 0.5rem;
color: var(--primary-color);
text-decoration: none;
font-size: 1.25rem;
}
@media (max-width: 600px) {
.email-container {
margin: 0;
border-radius: 0;
}
.header {
padding: 1.5rem;
}
.product {
flex-direction: column;
text-align: center;
margin: 1rem;
}
.product-image {
width: 100px;
height: 100px;
}
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<img src="{{ logo_url }}" alt="Kasico Art & Design" class="logo">
<h1>{% trans "Low Stock Alert" %}</h1>
<p>{% trans "Action required for inventory management" %}</p>
</div>
<div class="alert">
<strong>{% trans "Warning" %}:</strong>
{% trans "The following product is running low on stock and needs attention." %}
</div>
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div class="product-details">
<h2>{{ product.name }}</h2>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
<div class="stock-level">
{% trans "Current Stock" %}: {{ product.stock }}
</div>
<div class="product-info">
<p><strong>{% trans "SKU" %}:</strong> {{ product.sku }}</p>
<p><strong>{% trans "Base Price" %}:</strong> {{ product.base_price }} €</p>
{% if product.category %}
<p><strong>{% trans "Category" %}:</strong> {{ product.category.name }}</p>
{% endif %}
</div>
</div>
</div>
<div style="text-align: center; padding: 2rem;">
<a href="{{ product_url }}" class="button">
{% trans "Manage Product" %}
</a>
</div>
<div class="footer">
<p>{% trans "This is an automated notification from Kasico Art & Design." %}</p>
<p>{% trans "Please take action to restock this product or update its availability." %}</p>
<div class="social-links">
<a href="https://instagram.com/kasicoart" title="Instagram">📷</a>
<a href="https://twitter.com/kasicoart" title="Twitter">🐦</a>
<a href="https://discord.gg/kasico" title="Discord">🎮</a>
</div>
</div>
</div>
</body>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Low Stock Alert" %}</title>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--success-color: #10B981;
--danger-color: #EF4444;
--warning-color: #F59E0B;
--info-color: #3B82F6;
--light-color: #F8FAFC;
--dark-color: #1E293B;
--text-color: #334155;
--text-muted: #64748B;
--border-color: #E2E8F0;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--gradient-end: #F59E0B;
}
body {
font-family: 'Fredoka', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, var(--light-color), #FDF2F8);
margin: 0;
padding: 0;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(139, 92, 246, 0.1);
}
.header {
background: linear-gradient(135deg, var(--danger-color), #F87171);
color: white;
text-align: center;
padding: 2rem;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/><circle cx="10" cy="60" r="0.5" fill="white" opacity="0.1"/><circle cx="90" cy="40" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.logo {
max-width: 150px;
height: auto;
margin-bottom: 1rem;
position: relative;
z-index: 1;
}
.header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
position: relative;
z-index: 1;
}
.header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
position: relative;
z-index: 1;
}
.alert {
background: linear-gradient(135deg, #FEF2F2, #FEE2E2);
border: 1px solid #FECACA;
border-radius: 15px;
padding: 1.5rem;
margin: 2rem;
color: #991B1B;
}
.alert strong {
color: var(--danger-color);
}
.product {
background: white;
border-radius: 15px;
padding: 2rem;
margin: 2rem;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.1);
display: flex;
align-items: center;
gap: 1.5rem;
}
.product-image {
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.product-details {
flex: 1;
}
.product-details h2 {
margin: 0 0 0.5rem 0;
color: var(--primary-color);
font-size: 1.5rem;
font-weight: 600;
}
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 15px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0.25rem 0.25rem 0.25rem 0;
}
.badge-fursuit {
background: linear-gradient(135deg, var(--primary-color), #A78BFA);
color: white;
}
.badge-printed {
background: linear-gradient(135deg, var(--success-color), #34D399);
color: white;
}
.stock-level {
background: linear-gradient(135deg, var(--warning-color), #FBBF24);
color: white;
padding: 0.5rem 1rem;
border-radius: 15px;
font-weight: 600;
margin: 0.5rem 0;
display: inline-block;
}
.product-info {
margin: 1rem 0;
}
.product-info p {
margin: 0.25rem 0;
color: var(--text-muted);
}
.product-info strong {
color: var(--text-color);
}
.button {
display: inline-block;
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-decoration: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(139, 92, 246, 0.4);
}
.footer {
background: var(--light-color);
padding: 2rem;
text-align: center;
border-top: 1px solid var(--border-color);
}
.footer p {
margin: 0;
color: var(--text-muted);
font-size: 0.875rem;
}
.social-links {
margin-top: 1rem;
}
.social-links a {
display: inline-block;
margin: 0 0.5rem;
color: var(--primary-color);
text-decoration: none;
font-size: 1.25rem;
}
@media (max-width: 600px) {
.email-container {
margin: 0;
border-radius: 0;
}
.header {
padding: 1.5rem;
}
.product {
flex-direction: column;
text-align: center;
margin: 1rem;
}
.product-image {
width: 100px;
height: 100px;
}
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<img src="{{ logo_url }}" alt="Kasico Art & Design" class="logo">
<h1>{% trans "Low Stock Alert" %}</h1>
<p>{% trans "Action required for inventory management" %}</p>
</div>
<div class="alert">
<strong>{% trans "Warning" %}:</strong>
{% trans "The following product is running low on stock and needs attention." %}
</div>
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div class="product-details">
<h2>{{ product.name }}</h2>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
<div class="stock-level">
{% trans "Current Stock" %}: {{ product.stock }}
</div>
<div class="product-info">
<p><strong>{% trans "SKU" %}:</strong> {{ product.sku }}</p>
<p><strong>{% trans "Base Price" %}:</strong> {{ product.base_price }} €</p>
{% if product.category %}
<p><strong>{% trans "Category" %}:</strong> {{ product.category.name }}</p>
{% endif %}
</div>
</div>
</div>
<div style="text-align: center; padding: 2rem;">
<a href="{{ product_url }}" class="button">
{% trans "Manage Product" %}
</a>
</div>
<div class="footer">
<p>{% trans "This is an automated notification from Kasico Art & Design." %}</p>
<p>{% trans "Please take action to restock this product or update its availability." %}</p>
<div class="social-links">
<a href="https://instagram.com/kasicoart" title="Instagram">📷</a>
<a href="https://twitter.com/kasicoart" title="Twitter">🐦</a>
<a href="https://discord.gg/kasico" title="Discord">🎮</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,32 +1,15 @@
<<<<<<< HEAD
{% load i18n %}
{% trans "Low Stock Alert" %}
{% trans "Warning" %}: {% trans "The following product is running low on stock and needs attention." %}
{% trans "Product Details" %}:
{% trans "Name" %}: {{ product.name }}
{% trans "Type" %}: {% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %}
{% trans "SKU" %}: {{ product.sku }}
{% trans "Base Price" %}: {{ product.base_price }} €
{% trans "Current Stock" %}: {{ product.stock }}
=======
{% load i18n %}
{% trans "Low Stock Alert" %}
{% trans "Warning" %}: {% trans "The following product is running low on stock and needs attention." %}
{% trans "Product Details" %}:
{% trans "Name" %}: {{ product.name }}
{% trans "Type" %}: {% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %}
{% trans "SKU" %}: {{ product.sku }}
{% trans "Base Price" %}: {{ product.base_price }} €
{% trans "Current Stock" %}: {{ product.stock }}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% load i18n %}
{% trans "Low Stock Alert" %}
{% trans "Warning" %}: {% trans "The following product is running low on stock and needs attention." %}
{% trans "Product Details" %}:
{% trans "Name" %}: {{ product.name }}
{% trans "Type" %}: {% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %}
{% trans "SKU" %}: {{ product.sku }}
{% trans "Base Price" %}: {{ product.base_price }} €
{% trans "Current Stock" %}: {{ product.stock }}
{% trans "Manage this product at" %}: {{ product_url }}

View File

@ -1,366 +1,182 @@
<<<<<<< HEAD
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.order-details {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.product-details {
flex-grow: 1;
}
.product-price {
font-weight: bold;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.total {
text-align: right;
margin-top: 20px;
padding-top: 20px;
border-top: 2px solid #dee2e6;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.footer {
margin-top: 40px;
text-align: center;
font-size: 14px;
color: #6c757d;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<h1>{% trans "Thank You for Your Order!" %}</h1>
<p>{% trans "Your order has been confirmed and is being processed." %}</p>
</div>
<div class="order-details">
<h2>{% trans "Order Details" %}</h2>
<p><strong>{% trans "Order Number" %}:</strong> #{{ order.id }}</p>
<p><strong>{% trans "Order Date" %}:</strong> {{ order.created_at|date:"d.m.Y" }}</p>
<p>
<strong>{% trans "Payment Method" %}:</strong>
{% if order.payment_method == 'paypal' %}
PayPal
{% elif order.payment_method == 'credit_card' %}
{% trans "Credit Card" %}
{% else %}
{% trans "Bank Transfer" %}
{% endif %}
</p>
<h3>{% trans "Shipping Address" %}</h3>
<p>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
<h3>{% trans "Ordered Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div class="product-details">
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
<div class="product-price">
{{ product.base_price }} €
</div>
</div>
{% endfor %}
<div class="total">
<p>
<strong>{% trans "Subtotal" %}:</strong>
{{ order.total_price }} €
</p>
{% if order.total_price < 200 %}
<p>
<strong>{% trans "Shipping" %}:</strong>
5.99 €
</p>
<p style="font-size: 1.2em; font-weight: bold;">
<strong>{% trans "Total" %}:</strong>
{{ order.total_price|add:"5.99" }} €
</p>
{% else %}
<p>
<strong>{% trans "Shipping" %}:</strong>
<span style="color: #198754;">{% trans "FREE" %}</span>
</p>
<p style="font-size: 1.2em; font-weight: bold;">
<strong>{% trans "Total" %}:</strong>
{{ order.total_price }} €
</p>
{% endif %}
</div>
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
<div class="footer">
<p>
{% trans "If you have any questions about your order, please contact our support team." %}<br>
<a href="mailto:support@fursuitshop.com">support@fursuitshop.com</a>
</p>
<p>
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}
</p>
</div>
</body>
=======
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.order-details {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.product-details {
flex-grow: 1;
}
.product-price {
font-weight: bold;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.total {
text-align: right;
margin-top: 20px;
padding-top: 20px;
border-top: 2px solid #dee2e6;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.footer {
margin-top: 40px;
text-align: center;
font-size: 14px;
color: #6c757d;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<h1>{% trans "Thank You for Your Order!" %}</h1>
<p>{% trans "Your order has been confirmed and is being processed." %}</p>
</div>
<div class="order-details">
<h2>{% trans "Order Details" %}</h2>
<p><strong>{% trans "Order Number" %}:</strong> #{{ order.id }}</p>
<p><strong>{% trans "Order Date" %}:</strong> {{ order.created_at|date:"d.m.Y" }}</p>
<p>
<strong>{% trans "Payment Method" %}:</strong>
{% if order.payment_method == 'paypal' %}
PayPal
{% elif order.payment_method == 'credit_card' %}
{% trans "Credit Card" %}
{% else %}
{% trans "Bank Transfer" %}
{% endif %}
</p>
<h3>{% trans "Shipping Address" %}</h3>
<p>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
<h3>{% trans "Ordered Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div class="product-details">
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
<div class="product-price">
{{ product.base_price }} €
</div>
</div>
{% endfor %}
<div class="total">
<p>
<strong>{% trans "Subtotal" %}:</strong>
{{ order.total_price }} €
</p>
{% if order.total_price < 200 %}
<p>
<strong>{% trans "Shipping" %}:</strong>
5.99 €
</p>
<p style="font-size: 1.2em; font-weight: bold;">
<strong>{% trans "Total" %}:</strong>
{{ order.total_price|add:"5.99" }} €
</p>
{% else %}
<p>
<strong>{% trans "Shipping" %}:</strong>
<span style="color: #198754;">{% trans "FREE" %}</span>
</p>
<p style="font-size: 1.2em; font-weight: bold;">
<strong>{% trans "Total" %}:</strong>
{{ order.total_price }} €
</p>
{% endif %}
</div>
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
<div class="footer">
<p>
{% trans "If you have any questions about your order, please contact our support team." %}<br>
<a href="mailto:support@fursuitshop.com">support@fursuitshop.com</a>
</p>
<p>
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}
</p>
</div>
</body>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.order-details {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.product-details {
flex-grow: 1;
}
.product-price {
font-weight: bold;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.total {
text-align: right;
margin-top: 20px;
padding-top: 20px;
border-top: 2px solid #dee2e6;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.footer {
margin-top: 40px;
text-align: center;
font-size: 14px;
color: #6c757d;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<h1>{% trans "Thank You for Your Order!" %}</h1>
<p>{% trans "Your order has been confirmed and is being processed." %}</p>
</div>
<div class="order-details">
<h2>{% trans "Order Details" %}</h2>
<p><strong>{% trans "Order Number" %}:</strong> #{{ order.id }}</p>
<p><strong>{% trans "Order Date" %}:</strong> {{ order.created_at|date:"d.m.Y" }}</p>
<p>
<strong>{% trans "Payment Method" %}:</strong>
{% if order.payment_method == 'paypal' %}
PayPal
{% elif order.payment_method == 'credit_card' %}
{% trans "Credit Card" %}
{% else %}
{% trans "Bank Transfer" %}
{% endif %}
</p>
<h3>{% trans "Shipping Address" %}</h3>
<p>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
<h3>{% trans "Ordered Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div class="product-details">
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
<div class="product-price">
{{ product.base_price }} €
</div>
</div>
{% endfor %}
<div class="total">
<p>
<strong>{% trans "Subtotal" %}:</strong>
{{ order.total_price }} €
</p>
{% if order.total_price < 200 %}
<p>
<strong>{% trans "Shipping" %}:</strong>
5.99 €
</p>
<p style="font-size: 1.2em; font-weight: bold;">
<strong>{% trans "Total" %}:</strong>
{{ order.total_price|add:"5.99" }} €
</p>
{% else %}
<p>
<strong>{% trans "Shipping" %}:</strong>
<span style="color: #198754;">{% trans "FREE" %}</span>
</p>
<p style="font-size: 1.2em; font-weight: bold;">
<strong>{% trans "Total" %}:</strong>
{{ order.total_price }} €
</p>
{% endif %}
</div>
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
<div class="footer">
<p>
{% trans "If you have any questions about your order, please contact our support team." %}<br>
<a href="mailto:support@fursuitshop.com">support@fursuitshop.com</a>
</p>
<p>
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}
</p>
</div>
</body>
</html>

View File

@ -1,78 +1,38 @@
<<<<<<< HEAD
{% load i18n %}
{% trans "Thank You for Your Order!" %}
{% trans "Your order has been confirmed and is being processed." %}
{% trans "Order Details" %}:
{% trans "Order Number" %}: #{{ order.id }}
{% trans "Order Date" %}: {{ order.created_at|date:"d.m.Y" }}
{% trans "Payment Method" %}: {% if order.payment_method == 'paypal' %}PayPal{% elif order.payment_method == 'credit_card' %}{% trans "Credit Card" %}{% else %}{% trans "Bank Transfer" %}{% endif %}
{% trans "Shipping Address" %}:
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{{ product.base_price }} €
{% endfor %}
{% trans "Subtotal" %}: {{ order.total_price }} €
{% if order.total_price < 200 %}
{% trans "Shipping" %}: 5.99 €
{% trans "Total" %}: {{ order.total_price|add:"5.99" }} €
{% else %}
{% trans "Shipping" %}: {% trans "FREE" %}
{% trans "Total" %}: {{ order.total_price }} €
{% endif %}
{% trans "You can view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your order, please contact our support team." %}
support@fursuitshop.com
=======
{% load i18n %}
{% trans "Thank You for Your Order!" %}
{% trans "Your order has been confirmed and is being processed." %}
{% trans "Order Details" %}:
{% trans "Order Number" %}: #{{ order.id }}
{% trans "Order Date" %}: {{ order.created_at|date:"d.m.Y" }}
{% trans "Payment Method" %}: {% if order.payment_method == 'paypal' %}PayPal{% elif order.payment_method == 'credit_card' %}{% trans "Credit Card" %}{% else %}{% trans "Bank Transfer" %}{% endif %}
{% trans "Shipping Address" %}:
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{{ product.base_price }} €
{% endfor %}
{% trans "Subtotal" %}: {{ order.total_price }} €
{% if order.total_price < 200 %}
{% trans "Shipping" %}: 5.99 €
{% trans "Total" %}: {{ order.total_price|add:"5.99" }} €
{% else %}
{% trans "Shipping" %}: {% trans "FREE" %}
{% trans "Total" %}: {{ order.total_price }} €
{% endif %}
{% trans "You can view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your order, please contact our support team." %}
support@fursuitshop.com
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% load i18n %}
{% trans "Thank You for Your Order!" %}
{% trans "Your order has been confirmed and is being processed." %}
{% trans "Order Details" %}:
{% trans "Order Number" %}: #{{ order.id }}
{% trans "Order Date" %}: {{ order.created_at|date:"d.m.Y" }}
{% trans "Payment Method" %}: {% if order.payment_method == 'paypal' %}PayPal{% elif order.payment_method == 'credit_card' %}{% trans "Credit Card" %}{% else %}{% trans "Bank Transfer" %}{% endif %}
{% trans "Shipping Address" %}:
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{{ product.base_price }} €
{% endfor %}
{% trans "Subtotal" %}: {{ order.total_price }} €
{% if order.total_price < 200 %}
{% trans "Shipping" %}: 5.99 €
{% trans "Total" %}: {{ order.total_price|add:"5.99" }} €
{% else %}
{% trans "Shipping" %}: {% trans "FREE" %}
{% trans "Total" %}: {{ order.total_price }} €
{% endif %}
{% trans "You can view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your order, please contact our support team." %}
support@fursuitshop.com
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}

View File

@ -1,516 +1,257 @@
<<<<<<< HEAD
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Order Status Update" %}</title>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--success-color: #10B981;
--danger-color: #EF4444;
--warning-color: #F59E0B;
--info-color: #3B82F6;
--light-color: #F8FAFC;
--dark-color: #1E293B;
--text-color: #334155;
--text-muted: #64748B;
--border-color: #E2E8F0;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--gradient-end: #F59E0B;
}
body {
font-family: 'Fredoka', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, var(--light-color), #FDF2F8);
margin: 0;
padding: 0;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(139, 92, 246, 0.1);
}
.header {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-align: center;
padding: 2rem;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/><circle cx="10" cy="60" r="0.5" fill="white" opacity="0.1"/><circle cx="90" cy="40" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.logo {
max-width: 150px;
height: auto;
margin-bottom: 1rem;
position: relative;
z-index: 1;
}
.header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
position: relative;
z-index: 1;
}
.header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
position: relative;
z-index: 1;
}
.status-update {
padding: 2rem;
background: white;
}
.status-update h2 {
color: var(--primary-color);
margin-bottom: 1rem;
font-size: 1.5rem;
font-weight: 600;
}
.status-badge {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 25px;
font-weight: 600;
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0.5rem 0;
}
.status-confirmed {
background: linear-gradient(135deg, var(--success-color), #34D399);
color: white;
}
.status-in_progress {
background: linear-gradient(135deg, var(--warning-color), #FBBF24);
color: white;
}
.status-completed {
background: linear-gradient(135deg, var(--success-color), #10B981);
color: white;
}
.status-shipped {
background: linear-gradient(135deg, var(--info-color), #60A5FA);
color: white;
}
.progress-image {
max-width: 100%;
height: auto;
border-radius: 15px;
margin: 1rem 0;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.15);
}
.button {
display: inline-block;
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-decoration: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(139, 92, 246, 0.4);
}
.footer {
background: var(--light-color);
padding: 2rem;
text-align: center;
border-top: 1px solid var(--border-color);
}
.footer p {
margin: 0;
color: var(--text-muted);
font-size: 0.875rem;
}
.social-links {
margin-top: 1rem;
}
.social-links a {
display: inline-block;
margin: 0 0.5rem;
color: var(--primary-color);
text-decoration: none;
font-size: 1.25rem;
}
@media (max-width: 600px) {
.email-container {
margin: 0;
border-radius: 0;
}
.header {
padding: 1.5rem;
}
.status-update {
padding: 1.5rem;
}
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<img src="{{ logo_url }}" alt="Kasico Art & Design" class="logo">
<h1>{% trans "Order Status Update" %}</h1>
<p>{% trans "Your order has been updated." %}</p>
</div>
<div class="status-update">
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<p>
<strong>{% trans "New Status" %}:</strong><br>
<span class="status-badge status-{{ order.status }}">
{{ order.get_status_display }}
</span>
</p>
{% if update %}
<h3>{{ update.title }}</h3>
<p>{{ update.description }}</p>
{% if update.image %}
<img src="{{ update.image.url }}" alt="{% trans 'Progress Image' %}" class="progress-image">
{% endif %}
{% endif %}
{% if order.status == 'confirmed' %}
<p>{% trans "Your order has been confirmed and will be processed soon." %}</p>
{% elif order.status == 'in_progress' %}
<p>{% trans "We are currently working on your order." %}</p>
{% elif order.status == 'completed' %}
<p>{% trans "Your order has been completed and will be shipped soon." %}</p>
{% if order.tracking_number %}
<p>
<strong>{% trans "Tracking Number" %}:</strong>
{{ order.tracking_number }}
</p>
{% endif %}
{% endif %}
</div>
<div style="text-align: center; padding: 2rem;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
<div class="footer">
<p>{% trans "Thank you for choosing Kasico Art & Design!" %}</p>
<p>{% trans "If you have any questions, please don't hesitate to contact us." %}</p>
<div class="social-links">
<a href="https://instagram.com/kasicoart" title="Instagram">📷</a>
<a href="https://twitter.com/kasicoart" title="Twitter">🐦</a>
<a href="https://discord.gg/kasico" title="Discord">🎮</a>
</div>
</div>
</div>
</body>
=======
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Order Status Update" %}</title>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--success-color: #10B981;
--danger-color: #EF4444;
--warning-color: #F59E0B;
--info-color: #3B82F6;
--light-color: #F8FAFC;
--dark-color: #1E293B;
--text-color: #334155;
--text-muted: #64748B;
--border-color: #E2E8F0;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--gradient-end: #F59E0B;
}
body {
font-family: 'Fredoka', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, var(--light-color), #FDF2F8);
margin: 0;
padding: 0;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(139, 92, 246, 0.1);
}
.header {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-align: center;
padding: 2rem;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/><circle cx="10" cy="60" r="0.5" fill="white" opacity="0.1"/><circle cx="90" cy="40" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.logo {
max-width: 150px;
height: auto;
margin-bottom: 1rem;
position: relative;
z-index: 1;
}
.header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
position: relative;
z-index: 1;
}
.header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
position: relative;
z-index: 1;
}
.status-update {
padding: 2rem;
background: white;
}
.status-update h2 {
color: var(--primary-color);
margin-bottom: 1rem;
font-size: 1.5rem;
font-weight: 600;
}
.status-badge {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 25px;
font-weight: 600;
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0.5rem 0;
}
.status-confirmed {
background: linear-gradient(135deg, var(--success-color), #34D399);
color: white;
}
.status-in_progress {
background: linear-gradient(135deg, var(--warning-color), #FBBF24);
color: white;
}
.status-completed {
background: linear-gradient(135deg, var(--success-color), #10B981);
color: white;
}
.status-shipped {
background: linear-gradient(135deg, var(--info-color), #60A5FA);
color: white;
}
.progress-image {
max-width: 100%;
height: auto;
border-radius: 15px;
margin: 1rem 0;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.15);
}
.button {
display: inline-block;
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-decoration: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(139, 92, 246, 0.4);
}
.footer {
background: var(--light-color);
padding: 2rem;
text-align: center;
border-top: 1px solid var(--border-color);
}
.footer p {
margin: 0;
color: var(--text-muted);
font-size: 0.875rem;
}
.social-links {
margin-top: 1rem;
}
.social-links a {
display: inline-block;
margin: 0 0.5rem;
color: var(--primary-color);
text-decoration: none;
font-size: 1.25rem;
}
@media (max-width: 600px) {
.email-container {
margin: 0;
border-radius: 0;
}
.header {
padding: 1.5rem;
}
.status-update {
padding: 1.5rem;
}
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<img src="{{ logo_url }}" alt="Kasico Art & Design" class="logo">
<h1>{% trans "Order Status Update" %}</h1>
<p>{% trans "Your order has been updated." %}</p>
</div>
<div class="status-update">
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<p>
<strong>{% trans "New Status" %}:</strong><br>
<span class="status-badge status-{{ order.status }}">
{{ order.get_status_display }}
</span>
</p>
{% if update %}
<h3>{{ update.title }}</h3>
<p>{{ update.description }}</p>
{% if update.image %}
<img src="{{ update.image.url }}" alt="{% trans 'Progress Image' %}" class="progress-image">
{% endif %}
{% endif %}
{% if order.status == 'confirmed' %}
<p>{% trans "Your order has been confirmed and will be processed soon." %}</p>
{% elif order.status == 'in_progress' %}
<p>{% trans "We are currently working on your order." %}</p>
{% elif order.status == 'completed' %}
<p>{% trans "Your order has been completed and will be shipped soon." %}</p>
{% if order.tracking_number %}
<p>
<strong>{% trans "Tracking Number" %}:</strong>
{{ order.tracking_number }}
</p>
{% endif %}
{% endif %}
</div>
<div style="text-align: center; padding: 2rem;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
<div class="footer">
<p>{% trans "Thank you for choosing Kasico Art & Design!" %}</p>
<p>{% trans "If you have any questions, please don't hesitate to contact us." %}</p>
<div class="social-links">
<a href="https://instagram.com/kasicoart" title="Instagram">📷</a>
<a href="https://twitter.com/kasicoart" title="Twitter">🐦</a>
<a href="https://discord.gg/kasico" title="Discord">🎮</a>
</div>
</div>
</div>
</body>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% trans "Order Status Update" %}</title>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--success-color: #10B981;
--danger-color: #EF4444;
--warning-color: #F59E0B;
--info-color: #3B82F6;
--light-color: #F8FAFC;
--dark-color: #1E293B;
--text-color: #334155;
--text-muted: #64748B;
--border-color: #E2E8F0;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--gradient-end: #F59E0B;
}
body {
font-family: 'Fredoka', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, var(--light-color), #FDF2F8);
margin: 0;
padding: 0;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(139, 92, 246, 0.1);
}
.header {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-align: center;
padding: 2rem;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.1"/><circle cx="10" cy="60" r="0.5" fill="white" opacity="0.1"/><circle cx="90" cy="40" r="0.5" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.logo {
max-width: 150px;
height: auto;
margin-bottom: 1rem;
position: relative;
z-index: 1;
}
.header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
position: relative;
z-index: 1;
}
.header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
position: relative;
z-index: 1;
}
.status-update {
padding: 2rem;
background: white;
}
.status-update h2 {
color: var(--primary-color);
margin-bottom: 1rem;
font-size: 1.5rem;
font-weight: 600;
}
.status-badge {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 25px;
font-weight: 600;
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 0.5rem 0;
}
.status-confirmed {
background: linear-gradient(135deg, var(--success-color), #34D399);
color: white;
}
.status-in_progress {
background: linear-gradient(135deg, var(--warning-color), #FBBF24);
color: white;
}
.status-completed {
background: linear-gradient(135deg, var(--success-color), #10B981);
color: white;
}
.status-shipped {
background: linear-gradient(135deg, var(--info-color), #60A5FA);
color: white;
}
.progress-image {
max-width: 100%;
height: auto;
border-radius: 15px;
margin: 1rem 0;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.15);
}
.button {
display: inline-block;
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
text-decoration: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
transition: all 0.3s ease;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(139, 92, 246, 0.4);
}
.footer {
background: var(--light-color);
padding: 2rem;
text-align: center;
border-top: 1px solid var(--border-color);
}
.footer p {
margin: 0;
color: var(--text-muted);
font-size: 0.875rem;
}
.social-links {
margin-top: 1rem;
}
.social-links a {
display: inline-block;
margin: 0 0.5rem;
color: var(--primary-color);
text-decoration: none;
font-size: 1.25rem;
}
@media (max-width: 600px) {
.email-container {
margin: 0;
border-radius: 0;
}
.header {
padding: 1.5rem;
}
.status-update {
padding: 1.5rem;
}
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<img src="{{ logo_url }}" alt="Kasico Art & Design" class="logo">
<h1>{% trans "Order Status Update" %}</h1>
<p>{% trans "Your order has been updated." %}</p>
</div>
<div class="status-update">
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<p>
<strong>{% trans "New Status" %}:</strong><br>
<span class="status-badge status-{{ order.status }}">
{{ order.get_status_display }}
</span>
</p>
{% if update %}
<h3>{{ update.title }}</h3>
<p>{{ update.description }}</p>
{% if update.image %}
<img src="{{ update.image.url }}" alt="{% trans 'Progress Image' %}" class="progress-image">
{% endif %}
{% endif %}
{% if order.status == 'confirmed' %}
<p>{% trans "Your order has been confirmed and will be processed soon." %}</p>
{% elif order.status == 'in_progress' %}
<p>{% trans "We are currently working on your order." %}</p>
{% elif order.status == 'completed' %}
<p>{% trans "Your order has been completed and will be shipped soon." %}</p>
{% if order.tracking_number %}
<p>
<strong>{% trans "Tracking Number" %}:</strong>
{{ order.tracking_number }}
</p>
{% endif %}
{% endif %}
</div>
<div style="text-align: center; padding: 2rem;">
<a href="{{ order_url }}" class="button">
{% trans "View Order Details" %}
</a>
</div>
<div class="footer">
<p>{% trans "Thank you for choosing Kasico Art & Design!" %}</p>
<p>{% trans "If you have any questions, please don't hesitate to contact us." %}</p>
<div class="social-links">
<a href="https://instagram.com/kasicoart" title="Instagram">📷</a>
<a href="https://twitter.com/kasicoart" title="Twitter">🐦</a>
<a href="https://discord.gg/kasico" title="Discord">🎮</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,66 +1,32 @@
<<<<<<< HEAD
{% load i18n %}
{% trans "Order Status Update" %}
{% trans "Your order has been updated." %}
{% trans "Order" %} #{{ order.id }}
{% trans "New Status" %}: {{ order.get_status_display }}
{% if update %}
{{ update.title }}
{{ update.description }}
{% endif %}
{% if order.status == 'confirmed' %}
{% trans "Your order has been confirmed and will be processed soon." %}
{% elif order.status == 'in_progress' %}
{% trans "We are currently working on your order." %}
{% elif order.status == 'completed' %}
{% trans "Your order has been completed and will be shipped soon." %}
{% if order.tracking_number %}
{% trans "Tracking Number" %}: {{ order.tracking_number }}
{% endif %}
{% endif %}
{% trans "You can view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your order, please contact our support team." %}
support@fursuitshop.com
=======
{% load i18n %}
{% trans "Order Status Update" %}
{% trans "Your order has been updated." %}
{% trans "Order" %} #{{ order.id }}
{% trans "New Status" %}: {{ order.get_status_display }}
{% if update %}
{{ update.title }}
{{ update.description }}
{% endif %}
{% if order.status == 'confirmed' %}
{% trans "Your order has been confirmed and will be processed soon." %}
{% elif order.status == 'in_progress' %}
{% trans "We are currently working on your order." %}
{% elif order.status == 'completed' %}
{% trans "Your order has been completed and will be shipped soon." %}
{% if order.tracking_number %}
{% trans "Tracking Number" %}: {{ order.tracking_number }}
{% endif %}
{% endif %}
{% trans "You can view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your order, please contact our support team." %}
support@fursuitshop.com
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% load i18n %}
{% trans "Order Status Update" %}
{% trans "Your order has been updated." %}
{% trans "Order" %} #{{ order.id }}
{% trans "New Status" %}: {{ order.get_status_display }}
{% if update %}
{{ update.title }}
{{ update.description }}
{% endif %}
{% if order.status == 'confirmed' %}
{% trans "Your order has been confirmed and will be processed soon." %}
{% elif order.status == 'in_progress' %}
{% trans "We are currently working on your order." %}
{% elif order.status == 'completed' %}
{% trans "Your order has been completed and will be shipped soon." %}
{% if order.tracking_number %}
{% trans "Tracking Number" %}: {{ order.tracking_number }}
{% endif %}
{% endif %}
{% trans "You can view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your order, please contact our support team." %}
support@fursuitshop.com
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}

View File

@ -1,336 +1,167 @@
<<<<<<< HEAD
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.shipping-info {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.tracking-box {
background: #e9ecef;
padding: 15px;
border-radius: 4px;
text-align: center;
margin: 20px 0;
}
.tracking-number {
font-size: 1.2em;
font-weight: bold;
color: #0d6efd;
padding: 10px;
background: white;
border-radius: 3px;
display: inline-block;
margin: 10px 0;
}
.product-list {
margin-top: 20px;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.footer {
margin-top: 40px;
text-align: center;
font-size: 14px;
color: #6c757d;
}
.shipping-icon {
font-size: 48px;
color: #198754;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<div class="shipping-icon">📦</div>
<h1>{% trans "Your Order Has Been Shipped!" %}</h1>
<p>{% trans "Great news! Your order has been shipped and is on its way to you." %}</p>
</div>
<div class="shipping-info">
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<div class="tracking-box">
<h3>{% trans "Tracking Information" %}</h3>
<div class="tracking-number">
{{ order.tracking_number }}
</div>
<p class="text-muted">
{% trans "Use this number to track your shipment" %}
</p>
</div>
<h3>{% trans "Shipping Address" %}</h3>
<p>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
<div class="product-list">
<h3>{% trans "Shipped Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div>
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "Track Your Shipment" %}
</a>
</div>
<div class="footer">
<p>
{% trans "If you have any questions about your shipment, please contact our support team." %}<br>
<a href="mailto:support@fursuitshop.com">support@fursuitshop.com</a>
</p>
<p>
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}
</p>
</div>
</body>
=======
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.shipping-info {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.tracking-box {
background: #e9ecef;
padding: 15px;
border-radius: 4px;
text-align: center;
margin: 20px 0;
}
.tracking-number {
font-size: 1.2em;
font-weight: bold;
color: #0d6efd;
padding: 10px;
background: white;
border-radius: 3px;
display: inline-block;
margin: 10px 0;
}
.product-list {
margin-top: 20px;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.footer {
margin-top: 40px;
text-align: center;
font-size: 14px;
color: #6c757d;
}
.shipping-icon {
font-size: 48px;
color: #198754;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<div class="shipping-icon">📦</div>
<h1>{% trans "Your Order Has Been Shipped!" %}</h1>
<p>{% trans "Great news! Your order has been shipped and is on its way to you." %}</p>
</div>
<div class="shipping-info">
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<div class="tracking-box">
<h3>{% trans "Tracking Information" %}</h3>
<div class="tracking-number">
{{ order.tracking_number }}
</div>
<p class="text-muted">
{% trans "Use this number to track your shipment" %}
</p>
</div>
<h3>{% trans "Shipping Address" %}</h3>
<p>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
<div class="product-list">
<h3>{% trans "Shipped Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div>
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "Track Your Shipment" %}
</a>
</div>
<div class="footer">
<p>
{% trans "If you have any questions about your shipment, please contact our support team." %}<br>
<a href="mailto:support@fursuitshop.com">support@fursuitshop.com</a>
</p>
<p>
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}
</p>
</div>
</body>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.logo {
max-width: 200px;
margin-bottom: 20px;
}
.shipping-info {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
margin-bottom: 30px;
}
.tracking-box {
background: #e9ecef;
padding: 15px;
border-radius: 4px;
text-align: center;
margin: 20px 0;
}
.tracking-number {
font-size: 1.2em;
font-weight: bold;
color: #0d6efd;
padding: 10px;
background: white;
border-radius: 3px;
display: inline-block;
margin: 10px 0;
}
.product-list {
margin-top: 20px;
}
.product {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #dee2e6;
}
.product:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
margin-right: 15px;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
color: white;
}
.badge-fursuit {
background-color: #0d6efd;
}
.badge-printed {
background-color: #198754;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0d6efd;
color: white;
text-decoration: none;
border-radius: 5px;
margin-top: 20px;
}
.footer {
margin-top: 40px;
text-align: center;
font-size: 14px;
color: #6c757d;
}
.shipping-icon {
font-size: 48px;
color: #198754;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="header">
<img src="{{ logo_url }}" alt="Fursuit Shop" class="logo">
<div class="shipping-icon">📦</div>
<h1>{% trans "Your Order Has Been Shipped!" %}</h1>
<p>{% trans "Great news! Your order has been shipped and is on its way to you." %}</p>
</div>
<div class="shipping-info">
<h2>{% trans "Order" %} #{{ order.id }}</h2>
<div class="tracking-box">
<h3>{% trans "Tracking Information" %}</h3>
<div class="tracking-number">
{{ order.tracking_number }}
</div>
<p class="text-muted">
{% trans "Use this number to track your shipment" %}
</p>
</div>
<h3>{% trans "Shipping Address" %}</h3>
<p>
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
<div class="product-list">
<h3>{% trans "Shipped Items" %}</h3>
{% for product in order.products.all %}
<div class="product">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.name }}" class="product-image">
{% endif %}
<div>
<h4 style="margin: 0;">{{ product.name }}</h4>
{% if product.product_type == 'fursuit' %}
<span class="badge badge-fursuit">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge badge-printed">{% trans "Printed Item" %}</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
<div style="text-align: center;">
<a href="{{ order_url }}" class="button">
{% trans "Track Your Shipment" %}
</a>
</div>
<div class="footer">
<p>
{% trans "If you have any questions about your shipment, please contact our support team." %}<br>
<a href="mailto:support@fursuitshop.com">support@fursuitshop.com</a>
</p>
<p>
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}
</p>
</div>
</body>
</html>

View File

@ -1,58 +1,28 @@
<<<<<<< HEAD
{% load i18n %}
{% trans "Your Order Has Been Shipped!" %}
{% trans "Great news! Your order has been shipped and is on its way to you." %}
{% trans "Order" %} #{{ order.id }}
{% trans "Tracking Information" %}:
{% trans "Tracking Number" %}: {{ order.tracking_number }}
{% trans "Shipping Address" %}:
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{% endfor %}
{% trans "You can track your shipment and view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your shipment, please contact our support team." %}
support@fursuitshop.com
=======
{% load i18n %}
{% trans "Your Order Has Been Shipped!" %}
{% trans "Great news! Your order has been shipped and is on its way to you." %}
{% trans "Order" %} #{{ order.id }}
{% trans "Tracking Information" %}:
{% trans "Tracking Number" %}: {{ order.tracking_number }}
{% trans "Shipping Address" %}:
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{% endfor %}
{% trans "You can track your shipment and view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your shipment, please contact our support team." %}
support@fursuitshop.com
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% load i18n %}
{% trans "Your Order Has Been Shipped!" %}
{% trans "Great news! Your order has been shipped and is on its way to you." %}
{% trans "Order" %} #{{ order.id }}
{% trans "Tracking Information" %}:
{% trans "Tracking Number" %}: {{ order.tracking_number }}
{% trans "Shipping Address" %}:
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}
{{ order.shipping_address.address }}
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}
{{ order.shipping_address.get_country_display }}
{% trans "Ordered Items" %}:
{% for product in order.products.all %}
- {{ product.name }} ({% if product.product_type == 'fursuit' %}{% trans "Fursuit" %}{% else %}{% trans "Printed Item" %}{% endif %})
{% endfor %}
{% trans "You can track your shipment and view your order details at" %}: {{ order_url }}
{% trans "If you have any questions about your shipment, please contact our support team." %}
support@fursuitshop.com
© {% now "Y" %} Fursuit Shop. {% trans "All rights reserved." %}

File diff suppressed because it is too large Load Diff

View File

@ -1,600 +1,299 @@
<<<<<<< HEAD
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{{ gallery.name }} - {% trans "Gallery" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox@3.2.0/dist/css/glightbox.min.css">
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.gallery-item {
position: relative;
aspect-ratio: 1;
overflow: hidden;
border-radius: 0.5rem;
cursor: pointer;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-item:hover img {
transform: scale(1.05);
}
.gallery-item::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
opacity: 0;
transition: opacity 0.3s ease;
}
.gallery-item:hover::after {
opacity: 1;
}
.like-button {
transition: transform 0.2s ease;
}
.like-button:hover {
transform: scale(1.1);
}
.like-button.liked {
color: #dc3545;
}
.specs-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.specs-list .card {
height: 100%;
}
</style>
{% endblock %}
{% block content %}
<div class="content-container">
<div class="row">
<div class="col-lg-8">
<!-- Hauptbild -->
{% if gallery.images.first %}
<div class="position-relative mb-4">
<a href="{{ gallery.images.first.image.url }}" class="glightbox"
data-gallery="gallery-{{ gallery.id }}">
<img data-src="{{ gallery.images.first.image.url }}"
alt="{{ gallery.name }}"
class="img-fluid rounded-3 furry-lazy-image shadow-sm"
style="width: 100%; max-height: 600px; object-fit: contain;">
</a>
</div>
{% endif %}
<!-- Galerie-Grid -->
{% if gallery.images.all|length > 1 %}
<div class="gallery-grid">
{% for image in gallery.images.all %}
{% if not forloop.first %}
<a href="{{ image.image.url }}"
class="gallery-item glightbox"
data-gallery="gallery-{{ gallery.id }}">
<img data-src="{{ image.image.url }}"
alt="{{ gallery.name }} - {% trans 'Image' %} {{ forloop.counter }}"
class="furry-lazy-image">
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<div class="col-lg-4">
<div class="furry-card mb-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<h1 class="card-title h2">{{ gallery.name }}</h1>
<button class="btn btn-outline-danger like-button {% if user_has_liked %}liked{% endif %}"
data-gallery-id="{{ gallery.id }}"
onclick="toggleLike(this)">
<i class="bi bi-heart-fill"></i>
<span class="likes-count">{{ gallery.likes }}</span>
</button>
</div>
<div class="mb-3">
{% if gallery.fursuit_type == 'fullsuit' %}
<span class="badge bg-primary">{% trans "Fullsuit" %}</span>
{% elif gallery.fursuit_type == 'partial' %}
<span class="badge bg-info">{% trans "Partial Suit" %}</span>
{% else %}
<span class="badge bg-secondary">{% trans "Head Only" %}</span>
{% endif %}
{% if gallery.style == 'toony' %}
<span class="badge bg-success">{% trans "Toony" %}</span>
{% elif gallery.style == 'realistic' %}
<span class="badge bg-warning">{% trans "Realistic" %}</span>
{% else %}
<span class="badge bg-info">{% trans "Semi-Realistic" %}</span>
{% endif %}
</div>
<p class="card-text">{{ gallery.description }}</p>
<hr>
<div class="specs-list">
{% if gallery.features %}
<div class="card bg-light">
<div class="card-body">
<h6 class="card-subtitle mb-2">{% trans "Features" %}</h6>
<ul class="list-unstyled mb-0">
{% for feature in gallery.features %}
<li><i class="bi bi-check-circle text-success"></i> {{ feature }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if gallery.materials %}
<div class="card bg-light">
<div class="card-body">
<h6 class="card-subtitle mb-2">{% trans "Materials" %}</h6>
<ul class="list-unstyled mb-0">
{% for material in gallery.materials %}
<li><i class="bi bi-circle-fill text-primary"></i> {{ material }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
<hr>
<div class="d-grid gap-2">
<a href="{% url 'shop:fursuit_order' %}?inspiration={{ gallery.id }}"
class="btn btn-primary">
<i class="bi bi-cart"></i>
{% trans "Order Similar Fursuit" %}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
margin-top: 2rem;
}
.gallery-item {
position: relative;
overflow: hidden;
border-radius: 15px;
transition: transform 0.3s ease;
}
.gallery-item:hover {
transform: scale(1.05);
}
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-meta {
margin-top: 2rem;
}
.meta-item {
padding: 1rem;
border-radius: 15px;
background: var(--light-bg);
transition: transform 0.3s ease;
}
.meta-item:hover {
transform: translateY(-2px);
}
.meta-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
}
.meta-label {
font-size: 0.875rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.like-button {
border-radius: 25px;
padding: 0.5rem 1rem;
transition: all 0.3s ease;
}
.like-button:hover {
transform: scale(1.1);
}
.like-button.liked {
background-color: var(--danger-color);
color: white;
}
@media (max-width: 768px) {
.gallery-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 0.5rem;
}
.gallery-item img {
height: 150px;
}
}
</style>
<script>
function toggleLike(button) {
const galleryId = button.dataset.galleryId;
const likesCount = button.querySelector('.likes-count');
fetch(`/gallery/${galleryId}/like/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
button.classList.toggle('liked');
likesCount.textContent = data.likes;
// Animation
button.style.transform = 'scale(1.2)';
setTimeout(() => {
button.style.transform = 'scale(1)';
}, 200);
}
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
=======
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{{ gallery.name }} - {% trans "Gallery" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox@3.2.0/dist/css/glightbox.min.css">
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.gallery-item {
position: relative;
aspect-ratio: 1;
overflow: hidden;
border-radius: 0.5rem;
cursor: pointer;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-item:hover img {
transform: scale(1.05);
}
.gallery-item::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
opacity: 0;
transition: opacity 0.3s ease;
}
.gallery-item:hover::after {
opacity: 1;
}
.like-button {
transition: transform 0.2s ease;
}
.like-button:hover {
transform: scale(1.1);
}
.like-button.liked {
color: #dc3545;
}
.specs-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.specs-list .card {
height: 100%;
}
</style>
{% endblock %}
{% block content %}
<div class="content-container">
<div class="row">
<div class="col-lg-8">
<!-- Hauptbild -->
{% if gallery.images.first %}
<div class="position-relative mb-4">
<a href="{{ gallery.images.first.image.url }}" class="glightbox"
data-gallery="gallery-{{ gallery.id }}">
<img data-src="{{ gallery.images.first.image.url }}"
alt="{{ gallery.name }}"
class="img-fluid rounded-3 furry-lazy-image shadow-sm"
style="width: 100%; max-height: 600px; object-fit: contain;">
</a>
</div>
{% endif %}
<!-- Galerie-Grid -->
{% if gallery.images.all|length > 1 %}
<div class="gallery-grid">
{% for image in gallery.images.all %}
{% if not forloop.first %}
<a href="{{ image.image.url }}"
class="gallery-item glightbox"
data-gallery="gallery-{{ gallery.id }}">
<img data-src="{{ image.image.url }}"
alt="{{ gallery.name }} - {% trans 'Image' %} {{ forloop.counter }}"
class="furry-lazy-image">
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<div class="col-lg-4">
<div class="furry-card mb-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<h1 class="card-title h2">{{ gallery.name }}</h1>
<button class="btn btn-outline-danger like-button {% if user_has_liked %}liked{% endif %}"
data-gallery-id="{{ gallery.id }}"
onclick="toggleLike(this)">
<i class="bi bi-heart-fill"></i>
<span class="likes-count">{{ gallery.likes }}</span>
</button>
</div>
<div class="mb-3">
{% if gallery.fursuit_type == 'fullsuit' %}
<span class="badge bg-primary">{% trans "Fullsuit" %}</span>
{% elif gallery.fursuit_type == 'partial' %}
<span class="badge bg-info">{% trans "Partial Suit" %}</span>
{% else %}
<span class="badge bg-secondary">{% trans "Head Only" %}</span>
{% endif %}
{% if gallery.style == 'toony' %}
<span class="badge bg-success">{% trans "Toony" %}</span>
{% elif gallery.style == 'realistic' %}
<span class="badge bg-warning">{% trans "Realistic" %}</span>
{% else %}
<span class="badge bg-info">{% trans "Semi-Realistic" %}</span>
{% endif %}
</div>
<p class="card-text">{{ gallery.description }}</p>
<hr>
<div class="specs-list">
{% if gallery.features %}
<div class="card bg-light">
<div class="card-body">
<h6 class="card-subtitle mb-2">{% trans "Features" %}</h6>
<ul class="list-unstyled mb-0">
{% for feature in gallery.features %}
<li><i class="bi bi-check-circle text-success"></i> {{ feature }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if gallery.materials %}
<div class="card bg-light">
<div class="card-body">
<h6 class="card-subtitle mb-2">{% trans "Materials" %}</h6>
<ul class="list-unstyled mb-0">
{% for material in gallery.materials %}
<li><i class="bi bi-circle-fill text-primary"></i> {{ material }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
<hr>
<div class="d-grid gap-2">
<a href="{% url 'shop:fursuit_order' %}?inspiration={{ gallery.id }}"
class="btn btn-primary">
<i class="bi bi-cart"></i>
{% trans "Order Similar Fursuit" %}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
margin-top: 2rem;
}
.gallery-item {
position: relative;
overflow: hidden;
border-radius: 15px;
transition: transform 0.3s ease;
}
.gallery-item:hover {
transform: scale(1.05);
}
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-meta {
margin-top: 2rem;
}
.meta-item {
padding: 1rem;
border-radius: 15px;
background: var(--light-bg);
transition: transform 0.3s ease;
}
.meta-item:hover {
transform: translateY(-2px);
}
.meta-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
}
.meta-label {
font-size: 0.875rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.like-button {
border-radius: 25px;
padding: 0.5rem 1rem;
transition: all 0.3s ease;
}
.like-button:hover {
transform: scale(1.1);
}
.like-button.liked {
background-color: var(--danger-color);
color: white;
}
@media (max-width: 768px) {
.gallery-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 0.5rem;
}
.gallery-item img {
height: 150px;
}
}
</style>
<script>
function toggleLike(button) {
const galleryId = button.dataset.galleryId;
const likesCount = button.querySelector('.likes-count');
fetch(`/gallery/${galleryId}/like/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
button.classList.toggle('liked');
likesCount.textContent = data.likes;
// Animation
button.style.transform = 'scale(1.2)';
setTimeout(() => {
button.style.transform = 'scale(1)';
}, 200);
}
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{{ gallery.name }} - {% trans "Gallery" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox@3.2.0/dist/css/glightbox.min.css">
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.gallery-item {
position: relative;
aspect-ratio: 1;
overflow: hidden;
border-radius: 0.5rem;
cursor: pointer;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-item:hover img {
transform: scale(1.05);
}
.gallery-item::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
opacity: 0;
transition: opacity 0.3s ease;
}
.gallery-item:hover::after {
opacity: 1;
}
.like-button {
transition: transform 0.2s ease;
}
.like-button:hover {
transform: scale(1.1);
}
.like-button.liked {
color: #dc3545;
}
.specs-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.specs-list .card {
height: 100%;
}
</style>
{% endblock %}
{% block content %}
<div class="content-container">
<div class="row">
<div class="col-lg-8">
<!-- Hauptbild -->
{% if gallery.images.first %}
<div class="position-relative mb-4">
<a href="{{ gallery.images.first.image.url }}" class="glightbox"
data-gallery="gallery-{{ gallery.id }}">
<img data-src="{{ gallery.images.first.image.url }}"
alt="{{ gallery.name }}"
class="img-fluid rounded-3 furry-lazy-image shadow-sm"
style="width: 100%; max-height: 600px; object-fit: contain;">
</a>
</div>
{% endif %}
<!-- Galerie-Grid -->
{% if gallery.images.all|length > 1 %}
<div class="gallery-grid">
{% for image in gallery.images.all %}
{% if not forloop.first %}
<a href="{{ image.image.url }}"
class="gallery-item glightbox"
data-gallery="gallery-{{ gallery.id }}">
<img data-src="{{ image.image.url }}"
alt="{{ gallery.name }} - {% trans 'Image' %} {{ forloop.counter }}"
class="furry-lazy-image">
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<div class="col-lg-4">
<div class="furry-card mb-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-3">
<h1 class="card-title h2">{{ gallery.name }}</h1>
<button class="btn btn-outline-danger like-button {% if user_has_liked %}liked{% endif %}"
data-gallery-id="{{ gallery.id }}"
onclick="toggleLike(this)">
<i class="bi bi-heart-fill"></i>
<span class="likes-count">{{ gallery.likes }}</span>
</button>
</div>
<div class="mb-3">
{% if gallery.fursuit_type == 'fullsuit' %}
<span class="badge bg-primary">{% trans "Fullsuit" %}</span>
{% elif gallery.fursuit_type == 'partial' %}
<span class="badge bg-info">{% trans "Partial Suit" %}</span>
{% else %}
<span class="badge bg-secondary">{% trans "Head Only" %}</span>
{% endif %}
{% if gallery.style == 'toony' %}
<span class="badge bg-success">{% trans "Toony" %}</span>
{% elif gallery.style == 'realistic' %}
<span class="badge bg-warning">{% trans "Realistic" %}</span>
{% else %}
<span class="badge bg-info">{% trans "Semi-Realistic" %}</span>
{% endif %}
</div>
<p class="card-text">{{ gallery.description }}</p>
<hr>
<div class="specs-list">
{% if gallery.features %}
<div class="card bg-light">
<div class="card-body">
<h6 class="card-subtitle mb-2">{% trans "Features" %}</h6>
<ul class="list-unstyled mb-0">
{% for feature in gallery.features %}
<li><i class="bi bi-check-circle text-success"></i> {{ feature }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if gallery.materials %}
<div class="card bg-light">
<div class="card-body">
<h6 class="card-subtitle mb-2">{% trans "Materials" %}</h6>
<ul class="list-unstyled mb-0">
{% for material in gallery.materials %}
<li><i class="bi bi-circle-fill text-primary"></i> {{ material }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
<hr>
<div class="d-grid gap-2">
<a href="{% url 'shop:fursuit_order' %}?inspiration={{ gallery.id }}"
class="btn btn-primary">
<i class="bi bi-cart"></i>
{% trans "Order Similar Fursuit" %}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
margin-top: 2rem;
}
.gallery-item {
position: relative;
overflow: hidden;
border-radius: 15px;
transition: transform 0.3s ease;
}
.gallery-item:hover {
transform: scale(1.05);
}
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-meta {
margin-top: 2rem;
}
.meta-item {
padding: 1rem;
border-radius: 15px;
background: var(--light-bg);
transition: transform 0.3s ease;
}
.meta-item:hover {
transform: translateY(-2px);
}
.meta-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
}
.meta-label {
font-size: 0.875rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.like-button {
border-radius: 25px;
padding: 0.5rem 1rem;
transition: all 0.3s ease;
}
.like-button:hover {
transform: scale(1.1);
}
.like-button.liked {
background-color: var(--danger-color);
color: white;
}
@media (max-width: 768px) {
.gallery-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 0.5rem;
}
.gallery-item img {
height: 150px;
}
}
</style>
<script>
function toggleLike(button) {
const galleryId = button.dataset.galleryId;
const likesCount = button.querySelector('.likes-count');
fetch(`/gallery/${galleryId}/like/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
button.classList.toggle('liked');
likesCount.textContent = data.likes;
// Animation
button.style.transform = 'scale(1.2)';
setTimeout(() => {
button.style.transform = 'scale(1)';
}, 200);
}
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
{% endblock %}

View File

@ -1,598 +1,298 @@
<<<<<<< HEAD
{% extends 'shop/base.html' %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Gallery" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<style>
.gallery-item {
break-inside: avoid;
margin-bottom: 1rem;
}
.gallery-item img {
width: 100%;
border-radius: 0.5rem;
transition: transform 0.3s ease;
}
.gallery-item:hover img {
transform: scale(1.02);
}
.masonry-grid {
columns: 1;
column-gap: 1rem;
}
@media (min-width: 576px) {
.masonry-grid {
columns: 2;
}
}
@media (min-width: 992px) {
.masonry-grid {
columns: 3;
}
}
.gallery-filters {
position: sticky;
top: 1rem;
z-index: 100;
}
.gallery-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
color: white;
}
.gallery-item:hover .gallery-overlay {
opacity: 1;
}
.empty-state {
padding: 3rem;
}
.empty-state i {
opacity: 0.5;
}
@media (max-width: 768px) {
.masonry-grid {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
}
</style>
{% endblock %}
{% block content %}
<div class="content-container">
<!-- Header -->
<div class="text-center mb-5">
<h1 class="display-5 fw-bold mb-4">{% trans "Fursuit Gallery" %}</h1>
<p class="lead">{% trans "Discover our handcrafted creations and get inspired" %}</p>
</div>
<!-- Filter Section -->
<div class="furry-card mb-4" style="background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));">
<form method="get" class="row g-3 align-items-end">
<div class="col-md-3">
<label for="fursuit_type" class="form-label text-white">{% trans "Fursuit Type" %}</label>
<select name="fursuit_type" id="fursuit_type" class="form-select border-0">
<option value="">{% trans "All Types" %}</option>
<option value="fullsuit" {% if 'fullsuit' in selected_types %}selected{% endif %}>
{% trans "Fullsuit" %}
</option>
<option value="partial" {% if 'partial' in selected_types %}selected{% endif %}>
{% trans "Partial Suit" %}
</option>
<option value="head_only" {% if 'head_only' in selected_types %}selected{% endif %}>
{% trans "Head Only" %}
</option>
</select>
</div>
<div class="col-md-3">
<label for="style" class="form-label text-white">{% trans "Style" %}</label>
<select name="style" id="style" class="form-select border-0">
<option value="">{% trans "All Styles" %}</option>
<option value="toony" {% if 'toony' in selected_styles %}selected{% endif %}>
{% trans "Toony" %}
</option>
<option value="realistic" {% if 'realistic' in selected_styles %}selected{% endif %}>
{% trans "Realistic" %}
</option>
<option value="semi_realistic" {% if 'semi_realistic' in selected_styles %}selected{% endif %}>
{% trans "Semi-Realistic" %}
</option>
</select>
</div>
<div class="col-md-3">
<label for="sort" class="form-label text-white">{% trans "Sort By" %}</label>
<select name="sort" id="sort" class="form-select border-0">
<option value="newest" {% if sort == 'newest' %}selected{% endif %}>
{% trans "Newest First" %}
</option>
<option value="oldest" {% if sort == 'oldest' %}selected{% endif %}>
{% trans "Oldest First" %}
</option>
<option value="name" {% if sort == 'name' %}selected{% endif %}>
{% trans "Name A-Z" %}
</option>
<option value="likes" {% if sort == 'likes' %}selected{% endif %}>
{% trans "Most Liked" %}
</option>
</select>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-light w-100">
<i class="fas fa-filter me-2"></i> {% trans "Apply Filters" %}
</button>
</div>
</form>
</div>
<!-- Gallery Grid -->
{% if galleries %}
<div class="masonry-grid">
{% for gallery in galleries %}
<div class="gallery-item">
<a href="{% url 'shop:gallery_detail' gallery.slug %}" class="text-decoration-none">
<div class="furry-card h-100">
{% if gallery.images.first %}
<div class="position-relative">
<img data-src="{{ gallery.images.first.image.url }}"
class="card-img-top furry-lazy-image"
alt="{{ gallery.name }}"
style="height: 250px; object-fit: cover;">
<div class="gallery-overlay">
<i class="fas fa-search-plus fa-2x"></i>
</div>
</div>
{% endif %}
<div class="card-body">
<h5 class="card-title">{{ gallery.name }}</h5>
<p class="card-text text-muted">{{ gallery.description|truncatewords:15 }}</p>
<!-- Badges -->
<div class="mb-3">
{% if gallery.fursuit_type == 'fullsuit' %}
<span class="badge bg-primary me-1">{% trans "Fullsuit" %}</span>
{% elif gallery.fursuit_type == 'partial' %}
<span class="badge bg-info me-1">{% trans "Partial" %}</span>
{% else %}
<span class="badge bg-secondary me-1">{% trans "Head Only" %}</span>
{% endif %}
{% if gallery.style == 'toony' %}
<span class="badge bg-success me-1">{% trans "Toony" %}</span>
{% elif gallery.style == 'realistic' %}
<span class="badge bg-warning me-1">{% trans "Realistic" %}</span>
{% else %}
<span class="badge bg-info me-1">{% trans "Semi-Realistic" %}</span>
{% endif %}
</div>
<!-- Meta Info -->
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<i class="fas fa-images me-1 text-muted"></i>
<small class="text-muted">{{ gallery.images.count }} {% trans "images" %}</small>
</div>
<div class="d-flex align-items-center">
<i class="fas fa-heart me-1 text-danger"></i>
<small class="text-muted">{{ gallery.likes }}</small>
</div>
</div>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if galleries.has_other_pages %}
<nav aria-label="Gallery pagination" class="mt-5">
<ul class="pagination justify-content-center">
{% if galleries.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ galleries.previous_page_number }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">
<i class="fas fa-chevron-left"></i>
</a>
</li>
{% endif %}
{% for num in galleries.paginator.page_range %}
{% if galleries.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > galleries.number|add:'-3' and num < galleries.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if galleries.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ galleries.next_page_number }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">
<i class="fas fa-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="text-center py-5">
<div class="empty-state">
<i class="fas fa-images fa-4x text-muted mb-4"></i>
<h3>{% trans "No galleries found" %}</h3>
<p class="text-muted">{% trans "Try adjusting your filters or check back later for new content." %}</p>
</div>
</div>
{% endif %}
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Filter-Handling mit AJAX
const filterSelects = document.querySelectorAll('select');
filterSelects.forEach(select => {
select.addEventListener('change', function() {
const form = this.closest('form');
const formData = new FormData(form);
const params = new URLSearchParams(formData);
// URL aktualisieren ohne Reload
const newUrl = `${window.location.pathname}?${params.toString()}`;
window.history.pushState({}, '', newUrl);
// AJAX Request für neue Ergebnisse
fetch(`${window.location.pathname}?${params.toString()}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.text())
.then(html => {
// Nur den Gallery-Bereich aktualisieren
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const newGallery = doc.querySelector('.masonry-grid');
const currentGallery = document.querySelector('.masonry-grid');
if (newGallery && currentGallery) {
currentGallery.innerHTML = newGallery.innerHTML;
}
})
.catch(error => {
console.error('Error:', error);
// Fallback: Seite neu laden
window.location.reload();
});
});
});
});
</script>
=======
{% extends 'shop/base.html' %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Gallery" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<style>
.gallery-item {
break-inside: avoid;
margin-bottom: 1rem;
}
.gallery-item img {
width: 100%;
border-radius: 0.5rem;
transition: transform 0.3s ease;
}
.gallery-item:hover img {
transform: scale(1.02);
}
.masonry-grid {
columns: 1;
column-gap: 1rem;
}
@media (min-width: 576px) {
.masonry-grid {
columns: 2;
}
}
@media (min-width: 992px) {
.masonry-grid {
columns: 3;
}
}
.gallery-filters {
position: sticky;
top: 1rem;
z-index: 100;
}
.gallery-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
color: white;
}
.gallery-item:hover .gallery-overlay {
opacity: 1;
}
.empty-state {
padding: 3rem;
}
.empty-state i {
opacity: 0.5;
}
@media (max-width: 768px) {
.masonry-grid {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
}
</style>
{% endblock %}
{% block content %}
<div class="content-container">
<!-- Header -->
<div class="text-center mb-5">
<h1 class="display-5 fw-bold mb-4">{% trans "Fursuit Gallery" %}</h1>
<p class="lead">{% trans "Discover our handcrafted creations and get inspired" %}</p>
</div>
<!-- Filter Section -->
<div class="furry-card mb-4" style="background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));">
<form method="get" class="row g-3 align-items-end">
<div class="col-md-3">
<label for="fursuit_type" class="form-label text-white">{% trans "Fursuit Type" %}</label>
<select name="fursuit_type" id="fursuit_type" class="form-select border-0">
<option value="">{% trans "All Types" %}</option>
<option value="fullsuit" {% if 'fullsuit' in selected_types %}selected{% endif %}>
{% trans "Fullsuit" %}
</option>
<option value="partial" {% if 'partial' in selected_types %}selected{% endif %}>
{% trans "Partial Suit" %}
</option>
<option value="head_only" {% if 'head_only' in selected_types %}selected{% endif %}>
{% trans "Head Only" %}
</option>
</select>
</div>
<div class="col-md-3">
<label for="style" class="form-label text-white">{% trans "Style" %}</label>
<select name="style" id="style" class="form-select border-0">
<option value="">{% trans "All Styles" %}</option>
<option value="toony" {% if 'toony' in selected_styles %}selected{% endif %}>
{% trans "Toony" %}
</option>
<option value="realistic" {% if 'realistic' in selected_styles %}selected{% endif %}>
{% trans "Realistic" %}
</option>
<option value="semi_realistic" {% if 'semi_realistic' in selected_styles %}selected{% endif %}>
{% trans "Semi-Realistic" %}
</option>
</select>
</div>
<div class="col-md-3">
<label for="sort" class="form-label text-white">{% trans "Sort By" %}</label>
<select name="sort" id="sort" class="form-select border-0">
<option value="newest" {% if sort == 'newest' %}selected{% endif %}>
{% trans "Newest First" %}
</option>
<option value="oldest" {% if sort == 'oldest' %}selected{% endif %}>
{% trans "Oldest First" %}
</option>
<option value="name" {% if sort == 'name' %}selected{% endif %}>
{% trans "Name A-Z" %}
</option>
<option value="likes" {% if sort == 'likes' %}selected{% endif %}>
{% trans "Most Liked" %}
</option>
</select>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-light w-100">
<i class="fas fa-filter me-2"></i> {% trans "Apply Filters" %}
</button>
</div>
</form>
</div>
<!-- Gallery Grid -->
{% if galleries %}
<div class="masonry-grid">
{% for gallery in galleries %}
<div class="gallery-item">
<a href="{% url 'shop:gallery_detail' gallery.slug %}" class="text-decoration-none">
<div class="furry-card h-100">
{% if gallery.images.first %}
<div class="position-relative">
<img data-src="{{ gallery.images.first.image.url }}"
class="card-img-top furry-lazy-image"
alt="{{ gallery.name }}"
style="height: 250px; object-fit: cover;">
<div class="gallery-overlay">
<i class="fas fa-search-plus fa-2x"></i>
</div>
</div>
{% endif %}
<div class="card-body">
<h5 class="card-title">{{ gallery.name }}</h5>
<p class="card-text text-muted">{{ gallery.description|truncatewords:15 }}</p>
<!-- Badges -->
<div class="mb-3">
{% if gallery.fursuit_type == 'fullsuit' %}
<span class="badge bg-primary me-1">{% trans "Fullsuit" %}</span>
{% elif gallery.fursuit_type == 'partial' %}
<span class="badge bg-info me-1">{% trans "Partial" %}</span>
{% else %}
<span class="badge bg-secondary me-1">{% trans "Head Only" %}</span>
{% endif %}
{% if gallery.style == 'toony' %}
<span class="badge bg-success me-1">{% trans "Toony" %}</span>
{% elif gallery.style == 'realistic' %}
<span class="badge bg-warning me-1">{% trans "Realistic" %}</span>
{% else %}
<span class="badge bg-info me-1">{% trans "Semi-Realistic" %}</span>
{% endif %}
</div>
<!-- Meta Info -->
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<i class="fas fa-images me-1 text-muted"></i>
<small class="text-muted">{{ gallery.images.count }} {% trans "images" %}</small>
</div>
<div class="d-flex align-items-center">
<i class="fas fa-heart me-1 text-danger"></i>
<small class="text-muted">{{ gallery.likes }}</small>
</div>
</div>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if galleries.has_other_pages %}
<nav aria-label="Gallery pagination" class="mt-5">
<ul class="pagination justify-content-center">
{% if galleries.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ galleries.previous_page_number }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">
<i class="fas fa-chevron-left"></i>
</a>
</li>
{% endif %}
{% for num in galleries.paginator.page_range %}
{% if galleries.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > galleries.number|add:'-3' and num < galleries.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if galleries.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ galleries.next_page_number }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">
<i class="fas fa-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="text-center py-5">
<div class="empty-state">
<i class="fas fa-images fa-4x text-muted mb-4"></i>
<h3>{% trans "No galleries found" %}</h3>
<p class="text-muted">{% trans "Try adjusting your filters or check back later for new content." %}</p>
</div>
</div>
{% endif %}
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Filter-Handling mit AJAX
const filterSelects = document.querySelectorAll('select');
filterSelects.forEach(select => {
select.addEventListener('change', function() {
const form = this.closest('form');
const formData = new FormData(form);
const params = new URLSearchParams(formData);
// URL aktualisieren ohne Reload
const newUrl = `${window.location.pathname}?${params.toString()}`;
window.history.pushState({}, '', newUrl);
// AJAX Request für neue Ergebnisse
fetch(`${window.location.pathname}?${params.toString()}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.text())
.then(html => {
// Nur den Gallery-Bereich aktualisieren
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const newGallery = doc.querySelector('.masonry-grid');
const currentGallery = document.querySelector('.masonry-grid');
if (newGallery && currentGallery) {
currentGallery.innerHTML = newGallery.innerHTML;
}
})
.catch(error => {
console.error('Error:', error);
// Fallback: Seite neu laden
window.location.reload();
});
});
});
});
</script>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'shop/base.html' %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "Gallery" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<style>
.gallery-item {
break-inside: avoid;
margin-bottom: 1rem;
}
.gallery-item img {
width: 100%;
border-radius: 0.5rem;
transition: transform 0.3s ease;
}
.gallery-item:hover img {
transform: scale(1.02);
}
.masonry-grid {
columns: 1;
column-gap: 1rem;
}
@media (min-width: 576px) {
.masonry-grid {
columns: 2;
}
}
@media (min-width: 992px) {
.masonry-grid {
columns: 3;
}
}
.gallery-filters {
position: sticky;
top: 1rem;
z-index: 100;
}
.gallery-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
color: white;
}
.gallery-item:hover .gallery-overlay {
opacity: 1;
}
.empty-state {
padding: 3rem;
}
.empty-state i {
opacity: 0.5;
}
@media (max-width: 768px) {
.masonry-grid {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
}
</style>
{% endblock %}
{% block content %}
<div class="content-container">
<!-- Header -->
<div class="text-center mb-5">
<h1 class="display-5 fw-bold mb-4">{% trans "Fursuit Gallery" %}</h1>
<p class="lead">{% trans "Discover our handcrafted creations and get inspired" %}</p>
</div>
<!-- Filter Section -->
<div class="furry-card mb-4" style="background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));">
<form method="get" class="row g-3 align-items-end">
<div class="col-md-3">
<label for="fursuit_type" class="form-label text-white">{% trans "Fursuit Type" %}</label>
<select name="fursuit_type" id="fursuit_type" class="form-select border-0">
<option value="">{% trans "All Types" %}</option>
<option value="fullsuit" {% if 'fullsuit' in selected_types %}selected{% endif %}>
{% trans "Fullsuit" %}
</option>
<option value="partial" {% if 'partial' in selected_types %}selected{% endif %}>
{% trans "Partial Suit" %}
</option>
<option value="head_only" {% if 'head_only' in selected_types %}selected{% endif %}>
{% trans "Head Only" %}
</option>
</select>
</div>
<div class="col-md-3">
<label for="style" class="form-label text-white">{% trans "Style" %}</label>
<select name="style" id="style" class="form-select border-0">
<option value="">{% trans "All Styles" %}</option>
<option value="toony" {% if 'toony' in selected_styles %}selected{% endif %}>
{% trans "Toony" %}
</option>
<option value="realistic" {% if 'realistic' in selected_styles %}selected{% endif %}>
{% trans "Realistic" %}
</option>
<option value="semi_realistic" {% if 'semi_realistic' in selected_styles %}selected{% endif %}>
{% trans "Semi-Realistic" %}
</option>
</select>
</div>
<div class="col-md-3">
<label for="sort" class="form-label text-white">{% trans "Sort By" %}</label>
<select name="sort" id="sort" class="form-select border-0">
<option value="newest" {% if sort == 'newest' %}selected{% endif %}>
{% trans "Newest First" %}
</option>
<option value="oldest" {% if sort == 'oldest' %}selected{% endif %}>
{% trans "Oldest First" %}
</option>
<option value="name" {% if sort == 'name' %}selected{% endif %}>
{% trans "Name A-Z" %}
</option>
<option value="likes" {% if sort == 'likes' %}selected{% endif %}>
{% trans "Most Liked" %}
</option>
</select>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-light w-100">
<i class="fas fa-filter me-2"></i> {% trans "Apply Filters" %}
</button>
</div>
</form>
</div>
<!-- Gallery Grid -->
{% if galleries %}
<div class="masonry-grid">
{% for gallery in galleries %}
<div class="gallery-item">
<a href="{% url 'shop:gallery_detail' gallery.slug %}" class="text-decoration-none">
<div class="furry-card h-100">
{% if gallery.images.first %}
<div class="position-relative">
<img data-src="{{ gallery.images.first.image.url }}"
class="card-img-top furry-lazy-image"
alt="{{ gallery.name }}"
style="height: 250px; object-fit: cover;">
<div class="gallery-overlay">
<i class="fas fa-search-plus fa-2x"></i>
</div>
</div>
{% endif %}
<div class="card-body">
<h5 class="card-title">{{ gallery.name }}</h5>
<p class="card-text text-muted">{{ gallery.description|truncatewords:15 }}</p>
<!-- Badges -->
<div class="mb-3">
{% if gallery.fursuit_type == 'fullsuit' %}
<span class="badge bg-primary me-1">{% trans "Fullsuit" %}</span>
{% elif gallery.fursuit_type == 'partial' %}
<span class="badge bg-info me-1">{% trans "Partial" %}</span>
{% else %}
<span class="badge bg-secondary me-1">{% trans "Head Only" %}</span>
{% endif %}
{% if gallery.style == 'toony' %}
<span class="badge bg-success me-1">{% trans "Toony" %}</span>
{% elif gallery.style == 'realistic' %}
<span class="badge bg-warning me-1">{% trans "Realistic" %}</span>
{% else %}
<span class="badge bg-info me-1">{% trans "Semi-Realistic" %}</span>
{% endif %}
</div>
<!-- Meta Info -->
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<i class="fas fa-images me-1 text-muted"></i>
<small class="text-muted">{{ gallery.images.count }} {% trans "images" %}</small>
</div>
<div class="d-flex align-items-center">
<i class="fas fa-heart me-1 text-danger"></i>
<small class="text-muted">{{ gallery.likes }}</small>
</div>
</div>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if galleries.has_other_pages %}
<nav aria-label="Gallery pagination" class="mt-5">
<ul class="pagination justify-content-center">
{% if galleries.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ galleries.previous_page_number }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">
<i class="fas fa-chevron-left"></i>
</a>
</li>
{% endif %}
{% for num in galleries.paginator.page_range %}
{% if galleries.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > galleries.number|add:'-3' and num < galleries.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if galleries.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ galleries.next_page_number }}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">
<i class="fas fa-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="text-center py-5">
<div class="empty-state">
<i class="fas fa-images fa-4x text-muted mb-4"></i>
<h3>{% trans "No galleries found" %}</h3>
<p class="text-muted">{% trans "Try adjusting your filters or check back later for new content." %}</p>
</div>
</div>
{% endif %}
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Filter-Handling mit AJAX
const filterSelects = document.querySelectorAll('select');
filterSelects.forEach(select => {
select.addEventListener('change', function() {
const form = this.closest('form');
const formData = new FormData(form);
const params = new URLSearchParams(formData);
// URL aktualisieren ohne Reload
const newUrl = `${window.location.pathname}?${params.toString()}`;
window.history.pushState({}, '', newUrl);
// AJAX Request für neue Ergebnisse
fetch(`${window.location.pathname}?${params.toString()}`, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.text())
.then(html => {
// Nur den Gallery-Bereich aktualisieren
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const newGallery = doc.querySelector('.masonry-grid');
const currentGallery = document.querySelector('.masonry-grid');
if (newGallery && currentGallery) {
currentGallery.innerHTML = newGallery.innerHTML;
}
})
.catch(error => {
console.error('Error:', error);
// Fallback: Seite neu laden
window.location.reload();
});
});
});
});
</script>
{% endblock %}

View File

@ -1,322 +1,160 @@
<<<<<<< HEAD
{% extends "shop/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}Willkommen bei Kasico Art & Design - Fursuit Shop{% endblock %}
{% block content %}
<!-- Hero Section -->
<section class="hero-section mb-5">
<div class="position-relative overflow-hidden rounded-4 mb-4" style="height: 500px;">
<div class="w-100 h-100 d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid"
style="max-width: 400px; height: auto;">
</div>
<div class="position-absolute bottom-0 start-0 w-100 p-4 text-white" style="background: linear-gradient(transparent, rgba(0,0,0,0.8));">
<h1 class="display-4 fw-bold mb-3">Willkommen bei Kasico Art & Design</h1>
<p class="lead mb-4">Wo Ihre Fursuit-Träume Realität werden</p>
<a href="{% url 'products:custom_order' %}" class="btn btn-primary btn-lg me-3">
<i class="fas fa-magic me-2"></i> Custom Order starten
</a>
<a href="{% url 'products:gallery' %}" class="btn btn-light btn-lg me-3">
<i class="fas fa-images me-2"></i> Galerie ansehen
</a>
<a href="{% url 'shop:contact' %}" class="btn btn-outline-light btn-lg">
<i class="fas fa-envelope me-2"></i> Kontakt
</a>
</div>
</div>
</section>
<!-- Services Section -->
<section class="services-section mb-5">
<h2 class="text-center mb-4">Unsere Dienstleistungen</h2>
<div class="row g-4">
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-scissors"></i>
</div>
<h3>Custom Design</h3>
<p>Ihr einzigartiger Charakter, zum Leben erweckt mit höchster Handwerkskunst und Liebe zum Detail.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Individuelle Konzeption</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>3D-Modellierung</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Maßanfertigung</li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-tools"></i>
</div>
<h3>Reparaturen</h3>
<p>Professionelle Pflege und Reparatur für Ihren bestehenden Fursuit.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Fell-Erneuerung</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Strukturreparaturen</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Reinigung</li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-camera"></i>
</div>
<h3>Fotoshootings</h3>
<p>Professionelle Fotografie-Sessions für Ihren Fursuit.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Studio-Aufnahmen</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Outdoor-Shootings</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Event-Dokumentation</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Custom Orders Section -->
<section id="custom-orders" class="custom-orders-section mb-5 py-5" style="background: linear-gradient(135deg, var(--light-color), #FDF2F8);">
<div class="container">
<h2 class="text-center mb-4">Custom Orders</h2>
<div class="row align-items-center">
<div class="col-lg-6 mb-4 mb-lg-0">
<div class="rounded-4 shadow d-flex align-items-center justify-content-center p-4" style="background: white; min-height: 300px;">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design"
class="img-fluid"
style="max-width: 300px; height: auto;">
</div>
</div>
<div class="col-lg-6">
<div class="furry-card">
<h3 class="mb-4">Ihr Traum-Fursuit wartet auf Sie</h3>
<ul class="list-unstyled">
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Kostenlose Designberatung
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Detaillierte 3D-Visualisierung
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Regelmäßige Fortschrittsberichte
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Höchste Materialqualität
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Maßgeschneiderte Passform
</li>
</ul>
<a href="{% url 'products:custom_order' %}" class="btn btn-primary mt-3">
<i class="fas fa-envelope me-2"></i> Anfrage senden
</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ Section -->
<section class="faq-section mb-5">
<h2 class="text-center mb-4">Häufig gestellte Fragen</h2>
<div class="accordion" id="faqAccordion">
{% for faq in faqs %}
<div class="accordion-item border-0 mb-3 rounded-3 shadow-sm">
<h3 class="accordion-header">
<button class="accordion-button collapsed rounded-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq{{ forloop.counter }}">
{{ faq.question }}
</button>
</h3>
<div id="faq{{ forloop.counter }}" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body">
{{ faq.answer }}
</div>
</div>
</div>
{% endfor %}
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section mb-5">
<div class="furry-card text-center">
<h2 class="mb-4">Haben Sie Fragen?</h2>
<p class="lead mb-4">Wir sind für Sie da! Kontaktieren Sie uns für individuelle Beratung und Support.</p>
<a href="{% url 'shop:contact' %}" class="btn btn-primary btn-lg">
<i class="fas fa-envelope me-2"></i> Kontaktieren Sie uns
</a>
</div>
</section>
=======
{% extends "shop/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}Willkommen bei Kasico Art & Design - Fursuit Shop{% endblock %}
{% block content %}
<!-- Hero Section -->
<section class="hero-section mb-5">
<div class="position-relative overflow-hidden rounded-4 mb-4" style="height: 500px;">
<div class="w-100 h-100 d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid"
style="max-width: 400px; height: auto;">
</div>
<div class="position-absolute bottom-0 start-0 w-100 p-4 text-white" style="background: linear-gradient(transparent, rgba(0,0,0,0.8));">
<h1 class="display-4 fw-bold mb-3">Willkommen bei Kasico Art & Design</h1>
<p class="lead mb-4">Wo Ihre Fursuit-Träume Realität werden</p>
<a href="{% url 'products:custom_order' %}" class="btn btn-primary btn-lg me-3">
<i class="fas fa-magic me-2"></i> Custom Order starten
</a>
<a href="{% url 'products:gallery' %}" class="btn btn-light btn-lg me-3">
<i class="fas fa-images me-2"></i> Galerie ansehen
</a>
<a href="{% url 'shop:contact' %}" class="btn btn-outline-light btn-lg">
<i class="fas fa-envelope me-2"></i> Kontakt
</a>
</div>
</div>
</section>
<!-- Services Section -->
<section class="services-section mb-5">
<h2 class="text-center mb-4">Unsere Dienstleistungen</h2>
<div class="row g-4">
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-scissors"></i>
</div>
<h3>Custom Design</h3>
<p>Ihr einzigartiger Charakter, zum Leben erweckt mit höchster Handwerkskunst und Liebe zum Detail.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Individuelle Konzeption</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>3D-Modellierung</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Maßanfertigung</li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-tools"></i>
</div>
<h3>Reparaturen</h3>
<p>Professionelle Pflege und Reparatur für Ihren bestehenden Fursuit.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Fell-Erneuerung</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Strukturreparaturen</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Reinigung</li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-camera"></i>
</div>
<h3>Fotoshootings</h3>
<p>Professionelle Fotografie-Sessions für Ihren Fursuit.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Studio-Aufnahmen</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Outdoor-Shootings</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Event-Dokumentation</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Custom Orders Section -->
<section id="custom-orders" class="custom-orders-section mb-5 py-5" style="background: linear-gradient(135deg, var(--light-color), #FDF2F8);">
<div class="container">
<h2 class="text-center mb-4">Custom Orders</h2>
<div class="row align-items-center">
<div class="col-lg-6 mb-4 mb-lg-0">
<div class="rounded-4 shadow d-flex align-items-center justify-content-center p-4" style="background: white; min-height: 300px;">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design"
class="img-fluid"
style="max-width: 300px; height: auto;">
</div>
</div>
<div class="col-lg-6">
<div class="furry-card">
<h3 class="mb-4">Ihr Traum-Fursuit wartet auf Sie</h3>
<ul class="list-unstyled">
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Kostenlose Designberatung
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Detaillierte 3D-Visualisierung
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Regelmäßige Fortschrittsberichte
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Höchste Materialqualität
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Maßgeschneiderte Passform
</li>
</ul>
<a href="{% url 'products:custom_order' %}" class="btn btn-primary mt-3">
<i class="fas fa-envelope me-2"></i> Anfrage senden
</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ Section -->
<section class="faq-section mb-5">
<h2 class="text-center mb-4">Häufig gestellte Fragen</h2>
<div class="accordion" id="faqAccordion">
{% for faq in faqs %}
<div class="accordion-item border-0 mb-3 rounded-3 shadow-sm">
<h3 class="accordion-header">
<button class="accordion-button collapsed rounded-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq{{ forloop.counter }}">
{{ faq.question }}
</button>
</h3>
<div id="faq{{ forloop.counter }}" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body">
{{ faq.answer }}
</div>
</div>
</div>
{% endfor %}
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section mb-5">
<div class="furry-card text-center">
<h2 class="mb-4">Haben Sie Fragen?</h2>
<p class="lead mb-4">Wir sind für Sie da! Kontaktieren Sie uns für individuelle Beratung und Support.</p>
<a href="{% url 'shop:contact' %}" class="btn btn-primary btn-lg">
<i class="fas fa-envelope me-2"></i> Kontaktieren Sie uns
</a>
</div>
</section>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "shop/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}Willkommen bei Kasico Art & Design - Fursuit Shop{% endblock %}
{% block content %}
<!-- Hero Section -->
<section class="hero-section mb-5">
<div class="position-relative overflow-hidden rounded-4 mb-4" style="height: 500px;">
<div class="w-100 h-100 d-flex align-items-center justify-content-center" style="background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid"
style="max-width: 400px; height: auto;">
</div>
<div class="position-absolute bottom-0 start-0 w-100 p-4 text-white" style="background: linear-gradient(transparent, rgba(0,0,0,0.8));">
<h1 class="display-4 fw-bold mb-3">Willkommen bei Kasico Art & Design</h1>
<p class="lead mb-4">Wo Ihre Fursuit-Träume Realität werden</p>
<a href="{% url 'products:custom_order' %}" class="btn btn-primary btn-lg me-3">
<i class="fas fa-magic me-2"></i> Custom Order starten
</a>
<a href="{% url 'products:gallery' %}" class="btn btn-light btn-lg me-3">
<i class="fas fa-images me-2"></i> Galerie ansehen
</a>
<a href="{% url 'shop:contact' %}" class="btn btn-outline-light btn-lg">
<i class="fas fa-envelope me-2"></i> Kontakt
</a>
</div>
</div>
</section>
<!-- Services Section -->
<section class="services-section mb-5">
<h2 class="text-center mb-4">Unsere Dienstleistungen</h2>
<div class="row g-4">
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-scissors"></i>
</div>
<h3>Custom Design</h3>
<p>Ihr einzigartiger Charakter, zum Leben erweckt mit höchster Handwerkskunst und Liebe zum Detail.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Individuelle Konzeption</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>3D-Modellierung</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Maßanfertigung</li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-tools"></i>
</div>
<h3>Reparaturen</h3>
<p>Professionelle Pflege und Reparatur für Ihren bestehenden Fursuit.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Fell-Erneuerung</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Strukturreparaturen</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Reinigung</li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="furry-card h-100">
<div class="furry-icon mb-3">
<i class="fas fa-camera"></i>
</div>
<h3>Fotoshootings</h3>
<p>Professionelle Fotografie-Sessions für Ihren Fursuit.</p>
<ul class="list-unstyled mt-3">
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Studio-Aufnahmen</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Outdoor-Shootings</li>
<li class="mb-2"><i class="fas fa-paw me-2 text-primary"></i>Event-Dokumentation</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Custom Orders Section -->
<section id="custom-orders" class="custom-orders-section mb-5 py-5" style="background: linear-gradient(135deg, var(--light-color), #FDF2F8);">
<div class="container">
<h2 class="text-center mb-4">Custom Orders</h2>
<div class="row align-items-center">
<div class="col-lg-6 mb-4 mb-lg-0">
<div class="rounded-4 shadow d-flex align-items-center justify-content-center p-4" style="background: white; min-height: 300px;">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design"
class="img-fluid"
style="max-width: 300px; height: auto;">
</div>
</div>
<div class="col-lg-6">
<div class="furry-card">
<h3 class="mb-4">Ihr Traum-Fursuit wartet auf Sie</h3>
<ul class="list-unstyled">
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Kostenlose Designberatung
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Detaillierte 3D-Visualisierung
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Regelmäßige Fortschrittsberichte
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Höchste Materialqualität
</li>
<li class="mb-3">
<i class="fas fa-check-circle text-primary me-2"></i>
Maßgeschneiderte Passform
</li>
</ul>
<a href="{% url 'products:custom_order' %}" class="btn btn-primary mt-3">
<i class="fas fa-envelope me-2"></i> Anfrage senden
</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ Section -->
<section class="faq-section mb-5">
<h2 class="text-center mb-4">Häufig gestellte Fragen</h2>
<div class="accordion" id="faqAccordion">
{% for faq in faqs %}
<div class="accordion-item border-0 mb-3 rounded-3 shadow-sm">
<h3 class="accordion-header">
<button class="accordion-button collapsed rounded-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq{{ forloop.counter }}">
{{ faq.question }}
</button>
</h3>
<div id="faq{{ forloop.counter }}" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body">
{{ faq.answer }}
</div>
</div>
</div>
{% endfor %}
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="contact-section mb-5">
<div class="furry-card text-center">
<h2 class="mb-4">Haben Sie Fragen?</h2>
<p class="lead mb-4">Wir sind für Sie da! Kontaktieren Sie uns für individuelle Beratung und Support.</p>
<a href="{% url 'shop:contact' %}" class="btn btn-primary btn-lg">
<i class="fas fa-envelope me-2"></i> Kontaktieren Sie uns
</a>
</div>
</section>
{% endblock %}

View File

@ -1,310 +1,154 @@
<<<<<<< HEAD
{% extends "shop/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Login" %} - Kasico Art & Design{% endblock %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">🐾 {% trans "Login" %}</h1>
<p class="text-muted">Willkommen zurück in der Furry-Community!</p>
</div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.username.id_for_label }}" class="form-label">
{{ form.username.label }}
</label>
{{ form.username }}
{% if form.username.errors %}
<div class="invalid-feedback d-block">
{% for error in form.username.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-4">
<label for="{{ form.password.id_for_label }}" class="form-label">
{{ form.password.label }}
</label>
{{ form.password }}
{% if form.password.errors %}
<div class="invalid-feedback d-block">
{% for error in form.password.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary furry-btn">
🐾 {% trans "Login" %}
</button>
</div>
</form>
<div class="mt-3 text-center">
<p>{% trans "Don't have an account?" %}
<a href="{% url 'register' %}">{% trans "Register here" %}</a>
</p>
<p><a href="{% url 'password_reset' %}">{% trans "Forgot your password?" %}</a></p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
}
#id_username, #id_password {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
}
#id_username:focus, #id_password:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
</style>
{% block extra_js %}
<script>
// Formular neu laden, wenn der Benutzer zurück navigiert
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
window.location.reload();
}
});
</script>
{% endblock %}
=======
{% extends "shop/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Login" %} - Kasico Art & Design{% endblock %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">🐾 {% trans "Login" %}</h1>
<p class="text-muted">Willkommen zurück in der Furry-Community!</p>
</div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.username.id_for_label }}" class="form-label">
{{ form.username.label }}
</label>
{{ form.username }}
{% if form.username.errors %}
<div class="invalid-feedback d-block">
{% for error in form.username.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-4">
<label for="{{ form.password.id_for_label }}" class="form-label">
{{ form.password.label }}
</label>
{{ form.password }}
{% if form.password.errors %}
<div class="invalid-feedback d-block">
{% for error in form.password.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary furry-btn">
🐾 {% trans "Login" %}
</button>
</div>
</form>
<div class="mt-3 text-center">
<p>{% trans "Don't have an account?" %}
<a href="{% url 'register' %}">{% trans "Register here" %}</a>
</p>
<p><a href="{% url 'password_reset' %}">{% trans "Forgot your password?" %}</a></p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
}
#id_username, #id_password {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
}
#id_username:focus, #id_password:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
</style>
{% block extra_js %}
<script>
// Formular neu laden, wenn der Benutzer zurück navigiert
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
window.location.reload();
}
});
</script>
{% endblock %}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "shop/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Login" %} - Kasico Art & Design{% endblock %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">🐾 {% trans "Login" %}</h1>
<p class="text-muted">Willkommen zurück in der Furry-Community!</p>
</div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.username.id_for_label }}" class="form-label">
{{ form.username.label }}
</label>
{{ form.username }}
{% if form.username.errors %}
<div class="invalid-feedback d-block">
{% for error in form.username.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-4">
<label for="{{ form.password.id_for_label }}" class="form-label">
{{ form.password.label }}
</label>
{{ form.password }}
{% if form.password.errors %}
<div class="invalid-feedback d-block">
{% for error in form.password.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary furry-btn">
🐾 {% trans "Login" %}
</button>
</div>
</form>
<div class="mt-3 text-center">
<p>{% trans "Don't have an account?" %}
<a href="{% url 'register' %}">{% trans "Register here" %}</a>
</p>
<p><a href="{% url 'password_reset' %}">{% trans "Forgot your password?" %}</a></p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
}
#id_username, #id_password {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
}
#id_username:focus, #id_password:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
</style>
{% block extra_js %}
<script>
// Formular neu laden, wenn der Benutzer zurück navigiert
window.addEventListener('pageshow', function(event) {
if (event.persisted) {
window.location.reload();
}
});
</script>
{% endblock %}
{% endblock %}

View File

@ -1,418 +1,208 @@
<<<<<<< HEAD
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "My Orders" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<style>
.order-card {
transition: transform 0.2s ease;
}
.order-card:hover {
transform: translateY(-2px);
}
.status-badge {
font-size: 0.875rem;
}
.status-pending {
background-color: var(--bs-warning);
color: var(--bs-dark);
}
.status-confirmed {
background-color: var(--bs-info);
}
.status-in_progress {
background-color: var(--bs-primary);
}
.status-completed {
background-color: var(--bs-success);
}
.status-cancelled {
background-color: var(--bs-danger);
}
.progress-timeline {
position: relative;
padding-left: 45px;
}
.progress-timeline::before {
content: '';
position: absolute;
left: 20px;
top: 0;
bottom: 0;
width: 2px;
background: var(--bs-gray-300);
}
.timeline-item {
position: relative;
padding-bottom: 1.5rem;
}
.timeline-item:last-child {
padding-bottom: 0;
}
.timeline-dot {
position: absolute;
left: -45px;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--bs-primary);
border: 2px solid white;
box-shadow: 0 0 0 2px var(--bs-primary);
}
.timeline-date {
font-size: 0.875rem;
color: var(--bs-gray-600);
}
</style>
{% endblock %}
{% block content %}
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h2 mb-0">{% trans "My Orders" %}</h1>
<a href="{% url 'shop:product_list' %}" class="btn btn-outline-primary">
<i class="bi bi-cart-plus"></i>
{% trans "Continue Shopping" %}
</a>
</div>
{% if orders %}
<div class="row g-4">
{% for order in orders %}
<div class="col-12">
<div class="card order-card">
<div class="card-body">
<div class="row align-items-center">
<!-- Bestellnummer und Status -->
<div class="col-md-3">
<h5 class="card-title mb-1">
{% trans "Order" %} #{{ order.id }}
</h5>
<div class="mb-2">
<span class="badge status-{{ order.status }} status-badge">
{{ order.get_status_display }}
</span>
</div>
<div class="text-muted small">
{{ order.created_at|date:"d.m.Y" }}
</div>
</div>
<!-- Bestellte Artikel -->
<div class="col-md-5">
<h6 class="mb-2">{% trans "Order Items" %}</h6>
{% for product in order.products.all %}
<div class="d-flex align-items-center mb-1">
{% if product.image %}
<img src="{{ product.image.url }}"
alt="{{ product.name }}"
class="rounded"
style="width: 40px; height: 40px; object-fit: cover;">
{% endif %}
<div class="ms-2">
{{ product.name }}
{% if product.product_type == 'fursuit' %}
<span class="badge bg-primary">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge bg-success">{% trans "Printed Item" %}</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- Zahlungsmethode und Gesamtbetrag -->
<div class="col-md-2">
<h6 class="mb-2">{% trans "Payment" %}</h6>
<p class="mb-1">
{% if order.payment_method == 'paypal' %}
<i class="bi bi-paypal"></i> PayPal
{% elif order.payment_method == 'credit_card' %}
<i class="bi bi-credit-card"></i> {% trans "Credit Card" %}
{% else %}
<i class="bi bi-bank"></i> {% trans "Bank Transfer" %}
{% endif %}
</p>
<p class="fw-bold mb-0">
{{ order.total_price }} €
</p>
</div>
<!-- Fortschritt -->
<div class="col-md-2">
<button class="btn btn-outline-primary w-100"
type="button"
data-bs-toggle="collapse"
data-bs-target="#progress-{{ order.id }}"
aria-expanded="false">
<i class="bi bi-clock-history"></i>
{% trans "Show Progress" %}
</button>
</div>
</div>
<!-- Fortschritts-Timeline -->
<div class="collapse mt-3" id="progress-{{ order.id }}">
<div class="progress-timeline">
{% for update in order.progress_updates.all %}
<div class="timeline-item">
<div class="timeline-dot"></div>
<h6 class="mb-1">{{ update.title }}</h6>
<div class="timeline-date mb-2">
{{ update.created_at|date:"d.m.Y H:i" }}
</div>
<p class="mb-0">{{ update.description }}</p>
{% if update.image %}
<img src="{{ update.image.url }}"
alt="{% trans 'Progress Image' %}"
class="img-fluid rounded mt-2"
style="max-height: 200px;">
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-inbox display-1 text-muted mb-4"></i>
<h2>{% trans "No orders yet" %}</h2>
<p class="text-muted">
{% trans "You haven't placed any orders yet." %}
</p>
<a href="{% url 'shop:product_list' %}" class="btn btn-primary">
<i class="bi bi-shop"></i>
{% trans "Start Shopping" %}
</a>
</div>
{% endif %}
</div>
=======
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "My Orders" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<style>
.order-card {
transition: transform 0.2s ease;
}
.order-card:hover {
transform: translateY(-2px);
}
.status-badge {
font-size: 0.875rem;
}
.status-pending {
background-color: var(--bs-warning);
color: var(--bs-dark);
}
.status-confirmed {
background-color: var(--bs-info);
}
.status-in_progress {
background-color: var(--bs-primary);
}
.status-completed {
background-color: var(--bs-success);
}
.status-cancelled {
background-color: var(--bs-danger);
}
.progress-timeline {
position: relative;
padding-left: 45px;
}
.progress-timeline::before {
content: '';
position: absolute;
left: 20px;
top: 0;
bottom: 0;
width: 2px;
background: var(--bs-gray-300);
}
.timeline-item {
position: relative;
padding-bottom: 1.5rem;
}
.timeline-item:last-child {
padding-bottom: 0;
}
.timeline-dot {
position: absolute;
left: -45px;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--bs-primary);
border: 2px solid white;
box-shadow: 0 0 0 2px var(--bs-primary);
}
.timeline-date {
font-size: 0.875rem;
color: var(--bs-gray-600);
}
</style>
{% endblock %}
{% block content %}
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h2 mb-0">{% trans "My Orders" %}</h1>
<a href="{% url 'shop:product_list' %}" class="btn btn-outline-primary">
<i class="bi bi-cart-plus"></i>
{% trans "Continue Shopping" %}
</a>
</div>
{% if orders %}
<div class="row g-4">
{% for order in orders %}
<div class="col-12">
<div class="card order-card">
<div class="card-body">
<div class="row align-items-center">
<!-- Bestellnummer und Status -->
<div class="col-md-3">
<h5 class="card-title mb-1">
{% trans "Order" %} #{{ order.id }}
</h5>
<div class="mb-2">
<span class="badge status-{{ order.status }} status-badge">
{{ order.get_status_display }}
</span>
</div>
<div class="text-muted small">
{{ order.created_at|date:"d.m.Y" }}
</div>
</div>
<!-- Bestellte Artikel -->
<div class="col-md-5">
<h6 class="mb-2">{% trans "Order Items" %}</h6>
{% for product in order.products.all %}
<div class="d-flex align-items-center mb-1">
{% if product.image %}
<img src="{{ product.image.url }}"
alt="{{ product.name }}"
class="rounded"
style="width: 40px; height: 40px; object-fit: cover;">
{% endif %}
<div class="ms-2">
{{ product.name }}
{% if product.product_type == 'fursuit' %}
<span class="badge bg-primary">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge bg-success">{% trans "Printed Item" %}</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- Zahlungsmethode und Gesamtbetrag -->
<div class="col-md-2">
<h6 class="mb-2">{% trans "Payment" %}</h6>
<p class="mb-1">
{% if order.payment_method == 'paypal' %}
<i class="bi bi-paypal"></i> PayPal
{% elif order.payment_method == 'credit_card' %}
<i class="bi bi-credit-card"></i> {% trans "Credit Card" %}
{% else %}
<i class="bi bi-bank"></i> {% trans "Bank Transfer" %}
{% endif %}
</p>
<p class="fw-bold mb-0">
{{ order.total_price }} €
</p>
</div>
<!-- Fortschritt -->
<div class="col-md-2">
<button class="btn btn-outline-primary w-100"
type="button"
data-bs-toggle="collapse"
data-bs-target="#progress-{{ order.id }}"
aria-expanded="false">
<i class="bi bi-clock-history"></i>
{% trans "Show Progress" %}
</button>
</div>
</div>
<!-- Fortschritts-Timeline -->
<div class="collapse mt-3" id="progress-{{ order.id }}">
<div class="progress-timeline">
{% for update in order.progress_updates.all %}
<div class="timeline-item">
<div class="timeline-dot"></div>
<h6 class="mb-1">{{ update.title }}</h6>
<div class="timeline-date mb-2">
{{ update.created_at|date:"d.m.Y H:i" }}
</div>
<p class="mb-0">{{ update.description }}</p>
{% if update.image %}
<img src="{{ update.image.url }}"
alt="{% trans 'Progress Image' %}"
class="img-fluid rounded mt-2"
style="max-height: 200px;">
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-inbox display-1 text-muted mb-4"></i>
<h2>{% trans "No orders yet" %}</h2>
<p class="text-muted">
{% trans "You haven't placed any orders yet." %}
</p>
<a href="{% url 'shop:product_list' %}" class="btn btn-primary">
<i class="bi bi-shop"></i>
{% trans "Start Shopping" %}
</a>
</div>
{% endif %}
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "My Orders" %} - Fursuit Shop{% endblock %}
{% block extra_css %}
<style>
.order-card {
transition: transform 0.2s ease;
}
.order-card:hover {
transform: translateY(-2px);
}
.status-badge {
font-size: 0.875rem;
}
.status-pending {
background-color: var(--bs-warning);
color: var(--bs-dark);
}
.status-confirmed {
background-color: var(--bs-info);
}
.status-in_progress {
background-color: var(--bs-primary);
}
.status-completed {
background-color: var(--bs-success);
}
.status-cancelled {
background-color: var(--bs-danger);
}
.progress-timeline {
position: relative;
padding-left: 45px;
}
.progress-timeline::before {
content: '';
position: absolute;
left: 20px;
top: 0;
bottom: 0;
width: 2px;
background: var(--bs-gray-300);
}
.timeline-item {
position: relative;
padding-bottom: 1.5rem;
}
.timeline-item:last-child {
padding-bottom: 0;
}
.timeline-dot {
position: absolute;
left: -45px;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--bs-primary);
border: 2px solid white;
box-shadow: 0 0 0 2px var(--bs-primary);
}
.timeline-date {
font-size: 0.875rem;
color: var(--bs-gray-600);
}
</style>
{% endblock %}
{% block content %}
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h2 mb-0">{% trans "My Orders" %}</h1>
<a href="{% url 'shop:product_list' %}" class="btn btn-outline-primary">
<i class="bi bi-cart-plus"></i>
{% trans "Continue Shopping" %}
</a>
</div>
{% if orders %}
<div class="row g-4">
{% for order in orders %}
<div class="col-12">
<div class="card order-card">
<div class="card-body">
<div class="row align-items-center">
<!-- Bestellnummer und Status -->
<div class="col-md-3">
<h5 class="card-title mb-1">
{% trans "Order" %} #{{ order.id }}
</h5>
<div class="mb-2">
<span class="badge status-{{ order.status }} status-badge">
{{ order.get_status_display }}
</span>
</div>
<div class="text-muted small">
{{ order.created_at|date:"d.m.Y" }}
</div>
</div>
<!-- Bestellte Artikel -->
<div class="col-md-5">
<h6 class="mb-2">{% trans "Order Items" %}</h6>
{% for product in order.products.all %}
<div class="d-flex align-items-center mb-1">
{% if product.image %}
<img src="{{ product.image.url }}"
alt="{{ product.name }}"
class="rounded"
style="width: 40px; height: 40px; object-fit: cover;">
{% endif %}
<div class="ms-2">
{{ product.name }}
{% if product.product_type == 'fursuit' %}
<span class="badge bg-primary">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge bg-success">{% trans "Printed Item" %}</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- Zahlungsmethode und Gesamtbetrag -->
<div class="col-md-2">
<h6 class="mb-2">{% trans "Payment" %}</h6>
<p class="mb-1">
{% if order.payment_method == 'paypal' %}
<i class="bi bi-paypal"></i> PayPal
{% elif order.payment_method == 'credit_card' %}
<i class="bi bi-credit-card"></i> {% trans "Credit Card" %}
{% else %}
<i class="bi bi-bank"></i> {% trans "Bank Transfer" %}
{% endif %}
</p>
<p class="fw-bold mb-0">
{{ order.total_price }} €
</p>
</div>
<!-- Fortschritt -->
<div class="col-md-2">
<button class="btn btn-outline-primary w-100"
type="button"
data-bs-toggle="collapse"
data-bs-target="#progress-{{ order.id }}"
aria-expanded="false">
<i class="bi bi-clock-history"></i>
{% trans "Show Progress" %}
</button>
</div>
</div>
<!-- Fortschritts-Timeline -->
<div class="collapse mt-3" id="progress-{{ order.id }}">
<div class="progress-timeline">
{% for update in order.progress_updates.all %}
<div class="timeline-item">
<div class="timeline-dot"></div>
<h6 class="mb-1">{{ update.title }}</h6>
<div class="timeline-date mb-2">
{{ update.created_at|date:"d.m.Y H:i" }}
</div>
<p class="mb-0">{{ update.description }}</p>
{% if update.image %}
<img src="{{ update.image.url }}"
alt="{% trans 'Progress Image' %}"
class="img-fluid rounded mt-2"
style="max-height: 200px;">
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-inbox display-1 text-muted mb-4"></i>
<h2>{% trans "No orders yet" %}</h2>
<p class="text-muted">
{% trans "You haven't placed any orders yet." %}
</p>
<a href="{% url 'shop:product_list' %}" class="btn btn-primary">
<i class="bi bi-shop"></i>
{% trans "Start Shopping" %}
</a>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,260 +1,129 @@
<<<<<<< HEAD
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "Order Successful" %} - Fursuit Shop{% endblock %}
{% block content %}
<div class="container py-5">
<div class="text-center mb-5">
<i class="bi bi-check-circle text-success display-1 mb-4"></i>
<h1 class="h2 mb-4">{% trans "Thank You for Your Order!" %}</h1>
<p class="text-muted mb-4">
{% trans "Your payment was successful and your order has been confirmed." %}
<br>
{% trans "We'll send you an email with your order details shortly." %}
</p>
</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-body">
<h5 class="card-title mb-4">{% trans "Order Details" %}</h5>
<div class="row mb-4">
<div class="col-sm-6">
<p class="mb-1">
<strong>{% trans "Order Number" %}:</strong>
#{{ order.id }}
</p>
<p class="mb-1">
<strong>{% trans "Order Date" %}:</strong>
{{ order.created_at|date:"d.m.Y" }}
</p>
<p class="mb-0">
<strong>{% trans "Payment Method" %}:</strong>
{% if order.payment_method == 'paypal' %}
<i class="bi bi-paypal"></i> PayPal
{% elif order.payment_method == 'credit_card' %}
<i class="bi bi-credit-card"></i> {% trans "Credit Card" %}
{% else %}
<i class="bi bi-bank"></i> {% trans "Bank Transfer" %}
{% endif %}
</p>
</div>
<div class="col-sm-6">
<p class="mb-1">
<strong>{% trans "Shipping To" %}:</strong>
</p>
<p class="mb-0">
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
</div>
</div>
<h6 class="mb-3">{% trans "Ordered Items" %}</h6>
{% for product in order.products.all %}
<div class="d-flex align-items-center mb-3">
{% if product.image %}
<img src="{{ product.image.url }}"
alt="{{ product.name }}"
class="rounded"
style="width: 50px; height: 50px; object-fit: cover;">
{% endif %}
<div class="ms-3 flex-grow-1">
<h6 class="mb-0">{{ product.name }}</h6>
<small class="text-muted">
{% if product.product_type == 'fursuit' %}
<span class="badge bg-primary">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge bg-success">{% trans "Printed Item" %}</span>
{% endif %}
</small>
</div>
<div class="text-end">
<span class="fw-bold">{{ product.base_price }} €</span>
</div>
</div>
{% endfor %}
<hr>
<div class="text-end">
<p class="mb-1">
<span class="text-muted">{% trans "Subtotal" %}:</span>
<span class="ms-2">{{ order.total_price }} €</span>
</p>
{% if order.total_price < 200 %}
<p class="mb-1">
<span class="text-muted">{% trans "Shipping" %}:</span>
<span class="ms-2">5.99 €</span>
</p>
<p class="mb-0 fw-bold">
<span>{% trans "Total" %}:</span>
<span class="ms-2">{{ order.total_price|add:"5.99" }} €</span>
</p>
{% else %}
<p class="mb-1">
<span class="text-muted">{% trans "Shipping" %}:</span>
<span class="ms-2 text-success">{% trans "FREE" %}</span>
</p>
<p class="mb-0 fw-bold">
<span>{% trans "Total" %}:</span>
<span class="ms-2">{{ order.total_price }} €</span>
</p>
{% endif %}
</div>
</div>
</div>
<div class="text-center mt-4">
<a href="{% url 'shop:my_orders' %}" class="btn btn-primary">
<i class="bi bi-box"></i>
{% trans "View My Orders" %}
</a>
<a href="{% url 'shop:product_list' %}" class="btn btn-outline-primary ms-3">
<i class="bi bi-shop"></i>
{% trans "Continue Shopping" %}
</a>
</div>
</div>
</div>
</div>
=======
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "Order Successful" %} - Fursuit Shop{% endblock %}
{% block content %}
<div class="container py-5">
<div class="text-center mb-5">
<i class="bi bi-check-circle text-success display-1 mb-4"></i>
<h1 class="h2 mb-4">{% trans "Thank You for Your Order!" %}</h1>
<p class="text-muted mb-4">
{% trans "Your payment was successful and your order has been confirmed." %}
<br>
{% trans "We'll send you an email with your order details shortly." %}
</p>
</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-body">
<h5 class="card-title mb-4">{% trans "Order Details" %}</h5>
<div class="row mb-4">
<div class="col-sm-6">
<p class="mb-1">
<strong>{% trans "Order Number" %}:</strong>
#{{ order.id }}
</p>
<p class="mb-1">
<strong>{% trans "Order Date" %}:</strong>
{{ order.created_at|date:"d.m.Y" }}
</p>
<p class="mb-0">
<strong>{% trans "Payment Method" %}:</strong>
{% if order.payment_method == 'paypal' %}
<i class="bi bi-paypal"></i> PayPal
{% elif order.payment_method == 'credit_card' %}
<i class="bi bi-credit-card"></i> {% trans "Credit Card" %}
{% else %}
<i class="bi bi-bank"></i> {% trans "Bank Transfer" %}
{% endif %}
</p>
</div>
<div class="col-sm-6">
<p class="mb-1">
<strong>{% trans "Shipping To" %}:</strong>
</p>
<p class="mb-0">
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
</div>
</div>
<h6 class="mb-3">{% trans "Ordered Items" %}</h6>
{% for product in order.products.all %}
<div class="d-flex align-items-center mb-3">
{% if product.image %}
<img src="{{ product.image.url }}"
alt="{{ product.name }}"
class="rounded"
style="width: 50px; height: 50px; object-fit: cover;">
{% endif %}
<div class="ms-3 flex-grow-1">
<h6 class="mb-0">{{ product.name }}</h6>
<small class="text-muted">
{% if product.product_type == 'fursuit' %}
<span class="badge bg-primary">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge bg-success">{% trans "Printed Item" %}</span>
{% endif %}
</small>
</div>
<div class="text-end">
<span class="fw-bold">{{ product.base_price }} €</span>
</div>
</div>
{% endfor %}
<hr>
<div class="text-end">
<p class="mb-1">
<span class="text-muted">{% trans "Subtotal" %}:</span>
<span class="ms-2">{{ order.total_price }} €</span>
</p>
{% if order.total_price < 200 %}
<p class="mb-1">
<span class="text-muted">{% trans "Shipping" %}:</span>
<span class="ms-2">5.99 €</span>
</p>
<p class="mb-0 fw-bold">
<span>{% trans "Total" %}:</span>
<span class="ms-2">{{ order.total_price|add:"5.99" }} €</span>
</p>
{% else %}
<p class="mb-1">
<span class="text-muted">{% trans "Shipping" %}:</span>
<span class="ms-2 text-success">{% trans "FREE" %}</span>
</p>
<p class="mb-0 fw-bold">
<span>{% trans "Total" %}:</span>
<span class="ms-2">{{ order.total_price }} €</span>
</p>
{% endif %}
</div>
</div>
</div>
<div class="text-center mt-4">
<a href="{% url 'shop:my_orders' %}" class="btn btn-primary">
<i class="bi bi-box"></i>
{% trans "View My Orders" %}
</a>
<a href="{% url 'shop:product_list' %}" class="btn btn-outline-primary ms-3">
<i class="bi bi-shop"></i>
{% trans "Continue Shopping" %}
</a>
</div>
</div>
</div>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "Order Successful" %} - Fursuit Shop{% endblock %}
{% block content %}
<div class="container py-5">
<div class="text-center mb-5">
<i class="bi bi-check-circle text-success display-1 mb-4"></i>
<h1 class="h2 mb-4">{% trans "Thank You for Your Order!" %}</h1>
<p class="text-muted mb-4">
{% trans "Your payment was successful and your order has been confirmed." %}
<br>
{% trans "We'll send you an email with your order details shortly." %}
</p>
</div>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-body">
<h5 class="card-title mb-4">{% trans "Order Details" %}</h5>
<div class="row mb-4">
<div class="col-sm-6">
<p class="mb-1">
<strong>{% trans "Order Number" %}:</strong>
#{{ order.id }}
</p>
<p class="mb-1">
<strong>{% trans "Order Date" %}:</strong>
{{ order.created_at|date:"d.m.Y" }}
</p>
<p class="mb-0">
<strong>{% trans "Payment Method" %}:</strong>
{% if order.payment_method == 'paypal' %}
<i class="bi bi-paypal"></i> PayPal
{% elif order.payment_method == 'credit_card' %}
<i class="bi bi-credit-card"></i> {% trans "Credit Card" %}
{% else %}
<i class="bi bi-bank"></i> {% trans "Bank Transfer" %}
{% endif %}
</p>
</div>
<div class="col-sm-6">
<p class="mb-1">
<strong>{% trans "Shipping To" %}:</strong>
</p>
<p class="mb-0">
{{ order.shipping_address.first_name }} {{ order.shipping_address.last_name }}<br>
{{ order.shipping_address.address }}<br>
{{ order.shipping_address.zip }} {{ order.shipping_address.city }}<br>
{{ order.shipping_address.get_country_display }}
</p>
</div>
</div>
<h6 class="mb-3">{% trans "Ordered Items" %}</h6>
{% for product in order.products.all %}
<div class="d-flex align-items-center mb-3">
{% if product.image %}
<img src="{{ product.image.url }}"
alt="{{ product.name }}"
class="rounded"
style="width: 50px; height: 50px; object-fit: cover;">
{% endif %}
<div class="ms-3 flex-grow-1">
<h6 class="mb-0">{{ product.name }}</h6>
<small class="text-muted">
{% if product.product_type == 'fursuit' %}
<span class="badge bg-primary">{% trans "Fursuit" %}</span>
{% else %}
<span class="badge bg-success">{% trans "Printed Item" %}</span>
{% endif %}
</small>
</div>
<div class="text-end">
<span class="fw-bold">{{ product.base_price }} €</span>
</div>
</div>
{% endfor %}
<hr>
<div class="text-end">
<p class="mb-1">
<span class="text-muted">{% trans "Subtotal" %}:</span>
<span class="ms-2">{{ order.total_price }} €</span>
</p>
{% if order.total_price < 200 %}
<p class="mb-1">
<span class="text-muted">{% trans "Shipping" %}:</span>
<span class="ms-2">5.99 €</span>
</p>
<p class="mb-0 fw-bold">
<span>{% trans "Total" %}:</span>
<span class="ms-2">{{ order.total_price|add:"5.99" }} €</span>
</p>
{% else %}
<p class="mb-1">
<span class="text-muted">{% trans "Shipping" %}:</span>
<span class="ms-2 text-success">{% trans "FREE" %}</span>
</p>
<p class="mb-0 fw-bold">
<span>{% trans "Total" %}:</span>
<span class="ms-2">{{ order.total_price }} €</span>
</p>
{% endif %}
</div>
</div>
</div>
<div class="text-center mt-4">
<a href="{% url 'shop:my_orders' %}" class="btn btn-primary">
<i class="bi bi-box"></i>
{% trans "View My Orders" %}
</a>
<a href="{% url 'shop:product_list' %}" class="btn btn-outline-primary ms-3">
<i class="bi bi-shop"></i>
{% trans "Continue Shopping" %}
</a>
</div>
</div>
</div>
</div>
{% endblock %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,664 +1,331 @@
<<<<<<< HEAD
{% extends 'shop/base.html' %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Password Reset Email Sent" %} - Kasico Art & Design{% endblock %}
{% block content %}
<div class="password-reset-container">
<div class="password-reset-hero furry-card text-center mb-5">
<div class="hero-content">
<div class="hero-icon">📧</div>
<h1 class="hero-title">{% trans "E-Mail gesendet!" %}</h1>
<p class="hero-subtitle">{% trans "Wir haben dir eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts gesendet." %}</p>
</div>
</div>
<div class="password-reset-form-container">
<div class="form-card furry-card text-center">
<!-- Success Icon -->
<div class="success-icon-container">
<div class="success-icon"></div>
<div class="success-ring"></div>
</div>
<div class="success-content">
<h2 class="success-title">{% trans "E-Mail ist unterwegs!" %}</h2>
<p class="success-description">
{% trans "Wir haben dir eine E-Mail mit einem Link zum Zurücksetzen deines Passworts gesendet. Bitte überprüfe dein E-Mail-Postfach." %}
</p>
<div class="email-tips">
<h3 class="tips-title">💡 Tipps:</h3>
<ul class="tips-list">
<li>Überprüfe dein Spam-Ordner</li>
<li>Stelle sicher, dass du die richtige E-Mail-Adresse eingegeben hast</li>
<li>Der Link ist 24 Stunden gültig</li>
</ul>
</div>
<div class="success-actions">
<a href="{% url 'login' %}" class="btn furry-btn-primary">
<span class="btn-icon">🔙</span>
<span class="btn-text">{% trans "Zurück zur Anmeldung" %}</span>
</a>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--dark-color: #1F2937;
--light-color: #F3E8FF;
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--success-color: #10B981;
--warning-color: #F59E0B;
--error-color: #EF4444;
}
.password-reset-container {
max-width: 600px;
margin: 0 auto;
padding: 2rem 1rem;
}
.password-reset-hero {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
padding: 3rem 2rem;
border-radius: 20px;
margin-bottom: 2rem;
}
.hero-content {
text-align: center;
}
.hero-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.hero-title {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
}
.hero-subtitle {
font-size: 1.2rem;
opacity: 0.9;
line-height: 1.6;
}
.password-reset-form-container {
display: flex;
justify-content: center;
}
.form-card {
background: white;
border-radius: 20px;
padding: 3rem 2.5rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
width: 100%;
max-width: 500px;
}
.success-icon-container {
position: relative;
margin-bottom: 2rem;
}
.success-icon {
font-size: 4rem;
margin-bottom: 1rem;
position: relative;
z-index: 2;
}
.success-ring {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120px;
height: 120px;
border: 4px solid var(--success-color);
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
50% {
transform: translate(-50%, -50%) scale(1.1);
opacity: 0.7;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
.success-content {
text-align: center;
}
.success-title {
color: var(--dark-color);
font-weight: 700;
margin-bottom: 1rem;
font-size: 1.8rem;
}
.success-description {
color: var(--dark-color);
opacity: 0.8;
line-height: 1.6;
margin-bottom: 2rem;
font-size: 1.1rem;
}
.email-tips {
background: linear-gradient(135deg, #F0F9FF, #E0F2FE);
border: 1px solid #0EA5E9;
border-radius: 15px;
padding: 1.5rem;
margin-bottom: 2rem;
text-align: left;
}
.tips-title {
color: var(--dark-color);
font-weight: 600;
margin-bottom: 1rem;
font-size: 1.1rem;
}
.tips-list {
list-style: none;
padding: 0;
margin: 0;
}
.tips-list li {
color: var(--dark-color);
opacity: 0.8;
margin-bottom: 0.5rem;
padding-left: 1.5rem;
position: relative;
line-height: 1.5;
}
.tips-list li::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
.success-actions {
display: flex;
justify-content: center;
}
.furry-btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
font-size: 1rem;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
text-decoration: none;
}
.furry-btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
color: white;
text-decoration: none;
}
.furry-btn-primary:active {
transform: translateY(-1px);
}
.btn-icon {
font-size: 1.2rem;
}
.btn-text {
font-weight: 600;
}
@media (max-width: 768px) {
.password-reset-container {
padding: 1rem;
}
.hero-title {
font-size: 2rem;
}
.form-card {
padding: 2rem;
}
.hero-icon {
font-size: 3rem;
}
.success-icon {
font-size: 3rem;
}
.success-ring {
width: 100px;
height: 100px;
}
}
/* Animation für Success Elements */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.form-card {
animation: fadeInUp 0.6s ease-out;
}
.success-icon {
animation: fadeInUp 0.8s ease-out 0.2s both;
}
.success-content {
animation: fadeInUp 0.8s ease-out 0.4s both;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Add some interactive effects
const successIcon = document.querySelector('.success-icon');
const tipsList = document.querySelectorAll('.tips-list li');
// Animate tips list items
tipsList.forEach((item, index) => {
item.style.opacity = '0';
item.style.transform = 'translateX(-20px)';
setTimeout(() => {
item.style.transition = 'all 0.5s ease';
item.style.opacity = '1';
item.style.transform = 'translateX(0)';
}, 800 + (index * 200));
});
// Add click effect to success icon
successIcon.addEventListener('click', function() {
this.style.transform = 'scale(1.1)';
setTimeout(() => {
this.style.transform = 'scale(1)';
}, 200);
});
});
</script>
=======
{% extends 'shop/base.html' %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Password Reset Email Sent" %} - Kasico Art & Design{% endblock %}
{% block content %}
<div class="password-reset-container">
<div class="password-reset-hero furry-card text-center mb-5">
<div class="hero-content">
<div class="hero-icon">📧</div>
<h1 class="hero-title">{% trans "E-Mail gesendet!" %}</h1>
<p class="hero-subtitle">{% trans "Wir haben dir eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts gesendet." %}</p>
</div>
</div>
<div class="password-reset-form-container">
<div class="form-card furry-card text-center">
<!-- Success Icon -->
<div class="success-icon-container">
<div class="success-icon"></div>
<div class="success-ring"></div>
</div>
<div class="success-content">
<h2 class="success-title">{% trans "E-Mail ist unterwegs!" %}</h2>
<p class="success-description">
{% trans "Wir haben dir eine E-Mail mit einem Link zum Zurücksetzen deines Passworts gesendet. Bitte überprüfe dein E-Mail-Postfach." %}
</p>
<div class="email-tips">
<h3 class="tips-title">💡 Tipps:</h3>
<ul class="tips-list">
<li>Überprüfe dein Spam-Ordner</li>
<li>Stelle sicher, dass du die richtige E-Mail-Adresse eingegeben hast</li>
<li>Der Link ist 24 Stunden gültig</li>
</ul>
</div>
<div class="success-actions">
<a href="{% url 'login' %}" class="btn furry-btn-primary">
<span class="btn-icon">🔙</span>
<span class="btn-text">{% trans "Zurück zur Anmeldung" %}</span>
</a>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--dark-color: #1F2937;
--light-color: #F3E8FF;
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--success-color: #10B981;
--warning-color: #F59E0B;
--error-color: #EF4444;
}
.password-reset-container {
max-width: 600px;
margin: 0 auto;
padding: 2rem 1rem;
}
.password-reset-hero {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
padding: 3rem 2rem;
border-radius: 20px;
margin-bottom: 2rem;
}
.hero-content {
text-align: center;
}
.hero-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.hero-title {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
}
.hero-subtitle {
font-size: 1.2rem;
opacity: 0.9;
line-height: 1.6;
}
.password-reset-form-container {
display: flex;
justify-content: center;
}
.form-card {
background: white;
border-radius: 20px;
padding: 3rem 2.5rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
width: 100%;
max-width: 500px;
}
.success-icon-container {
position: relative;
margin-bottom: 2rem;
}
.success-icon {
font-size: 4rem;
margin-bottom: 1rem;
position: relative;
z-index: 2;
}
.success-ring {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120px;
height: 120px;
border: 4px solid var(--success-color);
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
50% {
transform: translate(-50%, -50%) scale(1.1);
opacity: 0.7;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
.success-content {
text-align: center;
}
.success-title {
color: var(--dark-color);
font-weight: 700;
margin-bottom: 1rem;
font-size: 1.8rem;
}
.success-description {
color: var(--dark-color);
opacity: 0.8;
line-height: 1.6;
margin-bottom: 2rem;
font-size: 1.1rem;
}
.email-tips {
background: linear-gradient(135deg, #F0F9FF, #E0F2FE);
border: 1px solid #0EA5E9;
border-radius: 15px;
padding: 1.5rem;
margin-bottom: 2rem;
text-align: left;
}
.tips-title {
color: var(--dark-color);
font-weight: 600;
margin-bottom: 1rem;
font-size: 1.1rem;
}
.tips-list {
list-style: none;
padding: 0;
margin: 0;
}
.tips-list li {
color: var(--dark-color);
opacity: 0.8;
margin-bottom: 0.5rem;
padding-left: 1.5rem;
position: relative;
line-height: 1.5;
}
.tips-list li::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
.success-actions {
display: flex;
justify-content: center;
}
.furry-btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
font-size: 1rem;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
text-decoration: none;
}
.furry-btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
color: white;
text-decoration: none;
}
.furry-btn-primary:active {
transform: translateY(-1px);
}
.btn-icon {
font-size: 1.2rem;
}
.btn-text {
font-weight: 600;
}
@media (max-width: 768px) {
.password-reset-container {
padding: 1rem;
}
.hero-title {
font-size: 2rem;
}
.form-card {
padding: 2rem;
}
.hero-icon {
font-size: 3rem;
}
.success-icon {
font-size: 3rem;
}
.success-ring {
width: 100px;
height: 100px;
}
}
/* Animation für Success Elements */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.form-card {
animation: fadeInUp 0.6s ease-out;
}
.success-icon {
animation: fadeInUp 0.8s ease-out 0.2s both;
}
.success-content {
animation: fadeInUp 0.8s ease-out 0.4s both;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Add some interactive effects
const successIcon = document.querySelector('.success-icon');
const tipsList = document.querySelectorAll('.tips-list li');
// Animate tips list items
tipsList.forEach((item, index) => {
item.style.opacity = '0';
item.style.transform = 'translateX(-20px)';
setTimeout(() => {
item.style.transition = 'all 0.5s ease';
item.style.opacity = '1';
item.style.transform = 'translateX(0)';
}, 800 + (index * 200));
});
// Add click effect to success icon
successIcon.addEventListener('click', function() {
this.style.transform = 'scale(1.1)';
setTimeout(() => {
this.style.transform = 'scale(1)';
}, 200);
});
});
</script>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'shop/base.html' %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Password Reset Email Sent" %} - Kasico Art & Design{% endblock %}
{% block content %}
<div class="password-reset-container">
<div class="password-reset-hero furry-card text-center mb-5">
<div class="hero-content">
<div class="hero-icon">📧</div>
<h1 class="hero-title">{% trans "E-Mail gesendet!" %}</h1>
<p class="hero-subtitle">{% trans "Wir haben dir eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts gesendet." %}</p>
</div>
</div>
<div class="password-reset-form-container">
<div class="form-card furry-card text-center">
<!-- Success Icon -->
<div class="success-icon-container">
<div class="success-icon"></div>
<div class="success-ring"></div>
</div>
<div class="success-content">
<h2 class="success-title">{% trans "E-Mail ist unterwegs!" %}</h2>
<p class="success-description">
{% trans "Wir haben dir eine E-Mail mit einem Link zum Zurücksetzen deines Passworts gesendet. Bitte überprüfe dein E-Mail-Postfach." %}
</p>
<div class="email-tips">
<h3 class="tips-title">💡 Tipps:</h3>
<ul class="tips-list">
<li>Überprüfe dein Spam-Ordner</li>
<li>Stelle sicher, dass du die richtige E-Mail-Adresse eingegeben hast</li>
<li>Der Link ist 24 Stunden gültig</li>
</ul>
</div>
<div class="success-actions">
<a href="{% url 'login' %}" class="btn furry-btn-primary">
<span class="btn-icon">🔙</span>
<span class="btn-text">{% trans "Zurück zur Anmeldung" %}</span>
</a>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6;
--secondary-color: #EC4899;
--accent-color: #F59E0B;
--dark-color: #1F2937;
--light-color: #F3E8FF;
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
--success-color: #10B981;
--warning-color: #F59E0B;
--error-color: #EF4444;
}
.password-reset-container {
max-width: 600px;
margin: 0 auto;
padding: 2rem 1rem;
}
.password-reset-hero {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
padding: 3rem 2rem;
border-radius: 20px;
margin-bottom: 2rem;
}
.hero-content {
text-align: center;
}
.hero-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.hero-title {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
}
.hero-subtitle {
font-size: 1.2rem;
opacity: 0.9;
line-height: 1.6;
}
.password-reset-form-container {
display: flex;
justify-content: center;
}
.form-card {
background: white;
border-radius: 20px;
padding: 3rem 2.5rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
width: 100%;
max-width: 500px;
}
.success-icon-container {
position: relative;
margin-bottom: 2rem;
}
.success-icon {
font-size: 4rem;
margin-bottom: 1rem;
position: relative;
z-index: 2;
}
.success-ring {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120px;
height: 120px;
border: 4px solid var(--success-color);
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
50% {
transform: translate(-50%, -50%) scale(1.1);
opacity: 0.7;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
}
.success-content {
text-align: center;
}
.success-title {
color: var(--dark-color);
font-weight: 700;
margin-bottom: 1rem;
font-size: 1.8rem;
}
.success-description {
color: var(--dark-color);
opacity: 0.8;
line-height: 1.6;
margin-bottom: 2rem;
font-size: 1.1rem;
}
.email-tips {
background: linear-gradient(135deg, #F0F9FF, #E0F2FE);
border: 1px solid #0EA5E9;
border-radius: 15px;
padding: 1.5rem;
margin-bottom: 2rem;
text-align: left;
}
.tips-title {
color: var(--dark-color);
font-weight: 600;
margin-bottom: 1rem;
font-size: 1.1rem;
}
.tips-list {
list-style: none;
padding: 0;
margin: 0;
}
.tips-list li {
color: var(--dark-color);
opacity: 0.8;
margin-bottom: 0.5rem;
padding-left: 1.5rem;
position: relative;
line-height: 1.5;
}
.tips-list li::before {
content: '✓';
position: absolute;
left: 0;
color: var(--success-color);
font-weight: bold;
}
.success-actions {
display: flex;
justify-content: center;
}
.furry-btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 25px;
font-weight: 600;
font-size: 1rem;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
text-decoration: none;
}
.furry-btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.3);
color: white;
text-decoration: none;
}
.furry-btn-primary:active {
transform: translateY(-1px);
}
.btn-icon {
font-size: 1.2rem;
}
.btn-text {
font-weight: 600;
}
@media (max-width: 768px) {
.password-reset-container {
padding: 1rem;
}
.hero-title {
font-size: 2rem;
}
.form-card {
padding: 2rem;
}
.hero-icon {
font-size: 3rem;
}
.success-icon {
font-size: 3rem;
}
.success-ring {
width: 100px;
height: 100px;
}
}
/* Animation für Success Elements */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.form-card {
animation: fadeInUp 0.6s ease-out;
}
.success-icon {
animation: fadeInUp 0.8s ease-out 0.2s both;
}
.success-content {
animation: fadeInUp 0.8s ease-out 0.4s both;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Add some interactive effects
const successIcon = document.querySelector('.success-icon');
const tipsList = document.querySelectorAll('.tips-list li');
// Animate tips list items
tipsList.forEach((item, index) => {
item.style.opacity = '0';
item.style.transform = 'translateX(-20px)';
setTimeout(() => {
item.style.transition = 'all 0.5s ease';
item.style.opacity = '1';
item.style.transform = 'translateX(0)';
}, 800 + (index * 200));
});
// Add click effect to success icon
successIcon.addEventListener('click', function() {
this.style.transform = 'scale(1.1)';
setTimeout(() => {
this.style.transform = 'scale(1)';
}, 200);
});
});
</script>
{% endblock %}

View File

@ -1,32 +1,15 @@
<<<<<<< HEAD
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at Kasico Art & Design.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The Kasico Art & Design Team{% endblocktrans %}
=======
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at Kasico Art & Design.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The Kasico Art & Design Team{% endblocktrans %}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at Kasico Art & Design.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The Kasico Art & Design Team{% endblocktrans %}
{% endautoescape %}

View File

@ -1,8 +1,3 @@
<<<<<<< HEAD
{% load i18n %}{% autoescape off %}
{% blocktrans %}Password reset on Kasico Art & Design{% endblocktrans %}
=======
{% load i18n %}{% autoescape off %}
{% blocktrans %}Password reset on Kasico Art & Design{% endblocktrans %}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% load i18n %}{% autoescape off %}
{% blocktrans %}Password reset on Kasico Art & Design{% endblocktrans %}
{% endautoescape %}

View File

@ -1,84 +1,41 @@
<<<<<<< HEAD
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "Payment Failed" %} - Fursuit Shop{% endblock %}
{% block content %}
<div class="container py-5">
<div class="text-center">
<i class="bi bi-exclamation-circle text-danger display-1 mb-4"></i>
<h1 class="h2 mb-4">{% trans "Payment Failed" %}</h1>
<p class="text-muted mb-4">
{% trans "We're sorry, but there was a problem processing your payment." %}
<br>
{% trans "Please try again or choose a different payment method." %}
</p>
{% if order.payment_errors.exists %}
<div class="alert alert-danger mx-auto" style="max-width: 500px;">
<h6 class="alert-heading">{% trans "Error Details" %}:</h6>
{% for error in order.payment_errors.all|slice:"-1:" %}
<p class="mb-0">{{ error.error_message }}</p>
{% endfor %}
</div>
{% endif %}
<div class="d-flex justify-content-center gap-3">
<a href="{% url 'shop:checkout' %}" class="btn btn-primary">
<i class="bi bi-arrow-left"></i>
{% trans "Return to Checkout" %}
</a>
<a href="{% url 'shop:contact' %}" class="btn btn-outline-primary">
<i class="bi bi-envelope"></i>
{% trans "Contact Support" %}
</a>
</div>
</div>
</div>
=======
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "Payment Failed" %} - Fursuit Shop{% endblock %}
{% block content %}
<div class="container py-5">
<div class="text-center">
<i class="bi bi-exclamation-circle text-danger display-1 mb-4"></i>
<h1 class="h2 mb-4">{% trans "Payment Failed" %}</h1>
<p class="text-muted mb-4">
{% trans "We're sorry, but there was a problem processing your payment." %}
<br>
{% trans "Please try again or choose a different payment method." %}
</p>
{% if order.payment_errors.exists %}
<div class="alert alert-danger mx-auto" style="max-width: 500px;">
<h6 class="alert-heading">{% trans "Error Details" %}:</h6>
{% for error in order.payment_errors.all|slice:"-1:" %}
<p class="mb-0">{{ error.error_message }}</p>
{% endfor %}
</div>
{% endif %}
<div class="d-flex justify-content-center gap-3">
<a href="{% url 'shop:checkout' %}" class="btn btn-primary">
<i class="bi bi-arrow-left"></i>
{% trans "Return to Checkout" %}
</a>
<a href="{% url 'shop:contact' %}" class="btn btn-outline-primary">
<i class="bi bi-envelope"></i>
{% trans "Contact Support" %}
</a>
</div>
</div>
</div>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends "shop/base.html" %}
{% load i18n %}
{% block title %}{% trans "Payment Failed" %} - Fursuit Shop{% endblock %}
{% block content %}
<div class="container py-5">
<div class="text-center">
<i class="bi bi-exclamation-circle text-danger display-1 mb-4"></i>
<h1 class="h2 mb-4">{% trans "Payment Failed" %}</h1>
<p class="text-muted mb-4">
{% trans "We're sorry, but there was a problem processing your payment." %}
<br>
{% trans "Please try again or choose a different payment method." %}
</p>
{% if order.payment_errors.exists %}
<div class="alert alert-danger mx-auto" style="max-width: 500px;">
<h6 class="alert-heading">{% trans "Error Details" %}:</h6>
{% for error in order.payment_errors.all|slice:"-1:" %}
<p class="mb-0">{{ error.error_message }}</p>
{% endfor %}
</div>
{% endif %}
<div class="d-flex justify-content-center gap-3">
<a href="{% url 'shop:checkout' %}" class="btn btn-primary">
<i class="bi bi-arrow-left"></i>
{% trans "Return to Checkout" %}
</a>
<a href="{% url 'shop:contact' %}" class="btn btn-outline-primary">
<i class="bi bi-envelope"></i>
{% trans "Contact Support" %}
</a>
</div>
</div>
</div>
{% endblock %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,286 +1,142 @@
<<<<<<< HEAD
{% extends 'shop/base.html' %}
{% load i18n %}
{% load static %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">{% trans "Create Account" %}</h1>
</div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger">
{% for field in form %}
{% for error in field.errors %}
<p class="mb-0">{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.username.id_for_label }}" class="form-label">
{% trans "Username" %}
</label>
{{ form.username }}
{% if form.username.help_text %}
<div class="form-text">{{ form.username.help_text }}</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.password1.id_for_label }}" class="form-label">
{% trans "Password" %}
</label>
{{ form.password1 }}
{% if form.password1.help_text %}
<div class="form-text">{{ form.password1.help_text }}</div>
{% endif %}
</div>
<div class="mb-4">
<label for="{{ form.password2.id_for_label }}" class="form-label">
{% trans "Confirm Password" %}
</label>
{{ form.password2 }}
{% if form.password2.help_text %}
<div class="form-text">{{ form.password2.help_text }}</div>
{% endif %}
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">
{% trans "Register" %}
</button>
</div>
</form>
<div class="mt-3 text-center">
<p>{% trans "Already have an account?" %}
<a href="{% url 'login' %}">{% trans "Login here" %}</a>
</p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
}
#id_username, #id_password1, #id_password2 {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
}
#id_username:focus, #id_password1:focus, #id_password2:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
</style>
=======
{% extends 'shop/base.html' %}
{% load i18n %}
{% load static %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">{% trans "Create Account" %}</h1>
</div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger">
{% for field in form %}
{% for error in field.errors %}
<p class="mb-0">{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.username.id_for_label }}" class="form-label">
{% trans "Username" %}
</label>
{{ form.username }}
{% if form.username.help_text %}
<div class="form-text">{{ form.username.help_text }}</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.password1.id_for_label }}" class="form-label">
{% trans "Password" %}
</label>
{{ form.password1 }}
{% if form.password1.help_text %}
<div class="form-text">{{ form.password1.help_text }}</div>
{% endif %}
</div>
<div class="mb-4">
<label for="{{ form.password2.id_for_label }}" class="form-label">
{% trans "Confirm Password" %}
</label>
{{ form.password2 }}
{% if form.password2.help_text %}
<div class="form-text">{{ form.password2.help_text }}</div>
{% endif %}
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">
{% trans "Register" %}
</button>
</div>
</form>
<div class="mt-3 text-center">
<p>{% trans "Already have an account?" %}
<a href="{% url 'login' %}">{% trans "Login here" %}</a>
</p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
}
#id_username, #id_password1, #id_password2 {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
}
#id_username:focus, #id_password1:focus, #id_password2:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
</style>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% extends 'shop/base.html' %}
{% load i18n %}
{% load static %}
{% block content %}
<div class="content-container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="furry-card">
<!-- Header -->
<div class="text-center mb-4">
<img src="{% static 'images/kasicoLogo.png' %}"
alt="Kasico Art & Design Logo"
class="img-fluid mb-4"
style="max-width: 150px; height: auto;">
<h1 class="h3 mb-3">{% trans "Create Account" %}</h1>
</div>
<form method="post" class="needs-validation" novalidate>
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger">
{% for field in form %}
{% for error in field.errors %}
<p class="mb-0">{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
<div class="mb-3">
<label for="{{ form.username.id_for_label }}" class="form-label">
{% trans "Username" %}
</label>
{{ form.username }}
{% if form.username.help_text %}
<div class="form-text">{{ form.username.help_text }}</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.password1.id_for_label }}" class="form-label">
{% trans "Password" %}
</label>
{{ form.password1 }}
{% if form.password1.help_text %}
<div class="form-text">{{ form.password1.help_text }}</div>
{% endif %}
</div>
<div class="mb-4">
<label for="{{ form.password2.id_for_label }}" class="form-label">
{% trans "Confirm Password" %}
</label>
{{ form.password2 }}
{% if form.password2.help_text %}
<div class="form-text">{{ form.password2.help_text }}</div>
{% endif %}
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">
{% trans "Register" %}
</button>
</div>
</form>
<div class="mt-3 text-center">
<p>{% trans "Already have an account?" %}
<a href="{% url 'login' %}">{% trans "Login here" %}</a>
</p>
</div>
</div>
</div>
</div>
</div>
<style>
:root {
--primary-color: #8B5CF6; /* Helles Lila */
--secondary-color: #EC4899; /* Pink */
--accent-color: #F59E0B; /* Orange */
--dark-color: #1F2937; /* Dunkelgrau */
--light-color: #F3E8FF; /* Helles Lila */
--white-color: #FFFFFF;
--gradient-start: #8B5CF6;
--gradient-middle: #EC4899;
}
.furry-card {
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
}
#id_username, #id_password1, #id_password2 {
width: 100%;
padding: 0.75rem;
border-radius: 10px;
border: 2px solid var(--light-color);
transition: all 0.3s ease;
}
#id_username:focus, #id_password1:focus, #id_password2:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(139, 92, 246, 0.25);
outline: none;
}
.btn-primary {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle));
border: none;
padding: 0.75rem 1.5rem;
border-radius: 50px;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: all 0.3s ease;
}
a:hover {
color: var(--secondary-color);
}
.form-text {
font-size: 0.875rem;
color: var(--dark-color);
opacity: 0.7;
margin-top: 0.25rem;
}
</style>
{% endblock %}

View File

@ -1,9 +1,3 @@
<<<<<<< HEAD
from django.test import TestCase
# Create your tests here.
=======
from django.test import TestCase
# Create your tests here.
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.test import TestCase
# Create your tests here.

View File

@ -1,75 +1,36 @@
<<<<<<< HEAD
"""
URL configuration for shop project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from .views import home, contact
# from rest_framework.routers import DefaultRouter # Temporär auskommentiert
# from products.views import ProductViewSet, ReviewViewSet # Temporär auskommentiert
# router = DefaultRouter() # Temporär auskommentiert
# router.register(r'products', ProductViewSet) # Temporär auskommentiert
# router.register(r'reviews', ReviewViewSet) # Temporär auskommentiert
app_name = 'shop'
urlpatterns = [
path('', home, name='home'),
path('contact/', contact, name='contact'),
path('accounts/', include('django.contrib.auth.urls')),
# path('api/', include(router.urls)), # Temporär auskommentiert
# path('api-auth/', include('rest_framework.urls')), # Temporär auskommentiert
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
=======
"""
URL configuration for shop project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from .views import home, contact
# from rest_framework.routers import DefaultRouter # Temporär auskommentiert
# from products.views import ProductViewSet, ReviewViewSet # Temporär auskommentiert
# router = DefaultRouter() # Temporär auskommentiert
# router.register(r'products', ProductViewSet) # Temporär auskommentiert
# router.register(r'reviews', ReviewViewSet) # Temporär auskommentiert
app_name = 'shop'
urlpatterns = [
path('', home, name='home'),
path('contact/', contact, name='contact'),
path('accounts/', include('django.contrib.auth.urls')),
# path('api/', include(router.urls)), # Temporär auskommentiert
# path('api-auth/', include('rest_framework.urls')), # Temporär auskommentiert
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
"""
URL configuration for shop project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from .views import home, contact
# from rest_framework.routers import DefaultRouter # Temporär auskommentiert
# from products.views import ProductViewSet, ReviewViewSet # Temporär auskommentiert
# router = DefaultRouter() # Temporär auskommentiert
# router.register(r'products', ProductViewSet) # Temporär auskommentiert
# router.register(r'reviews', ReviewViewSet) # Temporär auskommentiert
app_name = 'shop'
urlpatterns = [
path('', home, name='home'),
path('contact/', contact, name='contact'),
path('accounts/', include('django.contrib.auth.urls')),
# path('api/', include(router.urls)), # Temporär auskommentiert
# path('api-auth/', include('rest_framework.urls')), # Temporär auskommentiert
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -1,96 +1,47 @@
<<<<<<< HEAD
from django.shortcuts import render, redirect
from django.contrib import messages
from django.utils.translation import gettext as _
from products.models import Product, GalleryImage
from .models import ContactMessage
from django.contrib.auth.forms import UserCreationForm
def home(request):
featured_products = Product.objects.filter(is_featured=True)[:3]
latest_galleries = GalleryImage.objects.filter(is_featured=True)[:3]
return render(request, 'shop/home.html', {
'featured_products': featured_products,
'latest_galleries': latest_galleries,
})
def contact(request):
if request.method == 'POST':
name = request.POST.get('name')
email = request.POST.get('email')
subject = request.POST.get('subject')
message = request.POST.get('message')
if name and email and subject and message:
ContactMessage.objects.create(
name=name,
email=email,
subject=subject,
message=message
)
messages.success(request, 'Ihre Nachricht wurde erfolgreich gesendet! Wir werden uns in Kürze bei Ihnen melden.')
return redirect('shop:contact')
else:
messages.error(request, 'Bitte füllen Sie alle Pflichtfelder aus.')
return render(request, 'shop/contact.html')
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, _('Ihr Account wurde erfolgreich erstellt! Sie können sich jetzt anmelden.'))
return redirect('login')
else:
form = UserCreationForm()
=======
from django.shortcuts import render, redirect
from django.contrib import messages
from django.utils.translation import gettext as _
from products.models import Product, GalleryImage
from .models import ContactMessage
from django.contrib.auth.forms import UserCreationForm
def home(request):
featured_products = Product.objects.filter(is_featured=True)[:3]
latest_galleries = GalleryImage.objects.filter(is_featured=True)[:3]
return render(request, 'shop/home.html', {
'featured_products': featured_products,
'latest_galleries': latest_galleries,
})
def contact(request):
if request.method == 'POST':
name = request.POST.get('name')
email = request.POST.get('email')
subject = request.POST.get('subject')
message = request.POST.get('message')
if name and email and subject and message:
ContactMessage.objects.create(
name=name,
email=email,
subject=subject,
message=message
)
messages.success(request, 'Ihre Nachricht wurde erfolgreich gesendet! Wir werden uns in Kürze bei Ihnen melden.')
return redirect('shop:contact')
else:
messages.error(request, 'Bitte füllen Sie alle Pflichtfelder aus.')
return render(request, 'shop/contact.html')
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, _('Ihr Account wurde erfolgreich erstellt! Sie können sich jetzt anmelden.'))
return redirect('login')
else:
form = UserCreationForm()
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
from django.shortcuts import render, redirect
from django.contrib import messages
from django.utils.translation import gettext as _
from products.models import Product, GalleryImage
from .models import ContactMessage
from django.contrib.auth.forms import UserCreationForm
def home(request):
featured_products = Product.objects.filter(is_featured=True)[:3]
latest_galleries = GalleryImage.objects.filter(is_featured=True)[:3]
return render(request, 'shop/home.html', {
'featured_products': featured_products,
'latest_galleries': latest_galleries,
})
def contact(request):
if request.method == 'POST':
name = request.POST.get('name')
email = request.POST.get('email')
subject = request.POST.get('subject')
message = request.POST.get('message')
if name and email and subject and message:
ContactMessage.objects.create(
name=name,
email=email,
subject=subject,
message=message
)
messages.success(request, 'Ihre Nachricht wurde erfolgreich gesendet! Wir werden uns in Kürze bei Ihnen melden.')
return redirect('shop:contact')
else:
messages.error(request, 'Bitte füllen Sie alle Pflichtfelder aus.')
return render(request, 'shop/contact.html')
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, _('Ihr Account wurde erfolgreich erstellt! Sie können sich jetzt anmelden.'))
return redirect('login')
else:
form = UserCreationForm()
return render(request, 'shop/register.html', {'form': form})

View File

@ -1,35 +1,16 @@
<<<<<<< HEAD
"""
WSGI config for shop project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shop.settings')
application = get_wsgi_application()
=======
"""
WSGI config for shop project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shop.settings')
application = get_wsgi_application()
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
"""
WSGI config for shop project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'shop.settings')
application = get_wsgi_application()

View File

@ -1,100 +1,49 @@
<<<<<<< HEAD
.progress-bar-custom {
height: 8px;
border-radius: 4px;
}
.status-badge {
font-size: 0.85rem;
padding: 0.35em 0.65em;
}
.stats-card {
transition: transform 0.2s;
}
.stats-card:hover {
transform: translateY(-5px);
}
/* Status Badge Colors */
.badge.bg-pending { background-color: #ffc107; }
.badge.bg-processing { background-color: #17a2b8; }
.badge.bg-shipped { background-color: #28a745; }
.badge.bg-delivered { background-color: #20c997; }
.badge.bg-cancelled { background-color: #dc3545; }
.badge.bg-quoted { background-color: #6610f2; }
.badge.bg-approved { background-color: #198754; }
.badge.bg-in_progress { background-color: #0d6efd; }
.badge.bg-ready { background-color: #20c997; }
/* Avatar Styling */
.avatar-placeholder {
font-size: 24px;
}
/* Card Styling */
.card-header {
background-color: #f8f9fa;
}
/* Table Styling */
.table > :not(caption) > * > * {
padding: 0.75rem;
}
/* Modal Styling */
.modal-body {
max-height: calc(100vh - 210px);
overflow-y: auto;
=======
.progress-bar-custom {
height: 8px;
border-radius: 4px;
}
.status-badge {
font-size: 0.85rem;
padding: 0.35em 0.65em;
}
.stats-card {
transition: transform 0.2s;
}
.stats-card:hover {
transform: translateY(-5px);
}
/* Status Badge Colors */
.badge.bg-pending { background-color: #ffc107; }
.badge.bg-processing { background-color: #17a2b8; }
.badge.bg-shipped { background-color: #28a745; }
.badge.bg-delivered { background-color: #20c997; }
.badge.bg-cancelled { background-color: #dc3545; }
.badge.bg-quoted { background-color: #6610f2; }
.badge.bg-approved { background-color: #198754; }
.badge.bg-in_progress { background-color: #0d6efd; }
.badge.bg-ready { background-color: #20c997; }
/* Avatar Styling */
.avatar-placeholder {
font-size: 24px;
}
/* Card Styling */
.card-header {
background-color: #f8f9fa;
}
/* Table Styling */
.table > :not(caption) > * > * {
padding: 0.75rem;
}
/* Modal Styling */
.modal-body {
max-height: calc(100vh - 210px);
overflow-y: auto;
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
.progress-bar-custom {
height: 8px;
border-radius: 4px;
}
.status-badge {
font-size: 0.85rem;
padding: 0.35em 0.65em;
}
.stats-card {
transition: transform 0.2s;
}
.stats-card:hover {
transform: translateY(-5px);
}
/* Status Badge Colors */
.badge.bg-pending { background-color: #ffc107; }
.badge.bg-processing { background-color: #17a2b8; }
.badge.bg-shipped { background-color: #28a745; }
.badge.bg-delivered { background-color: #20c997; }
.badge.bg-cancelled { background-color: #dc3545; }
.badge.bg-quoted { background-color: #6610f2; }
.badge.bg-approved { background-color: #198754; }
.badge.bg-in_progress { background-color: #0d6efd; }
.badge.bg-ready { background-color: #20c997; }
/* Avatar Styling */
.avatar-placeholder {
font-size: 24px;
}
/* Card Styling */
.card-header {
background-color: #f8f9fa;
}
/* Table Styling */
.table > :not(caption) > * > * {
padding: 0.75rem;
}
/* Modal Styling */
.modal-body {
max-height: calc(100vh - 210px);
overflow-y: auto;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,386 +1,192 @@
<<<<<<< HEAD
.product-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none;
border-radius: 15px;
overflow: hidden;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 30px rgba(139, 92, 246, 0.15);
}
.product-card .card-img-top {
height: 250px;
object-fit: cover;
}
.product-badge {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
}
.product-type-badge {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
}
.price-tag {
font-size: 1.25rem;
font-weight: bold;
color: #8B5CF6;
}
.stock-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
}
.stock-high {
background-color: #10B981;
}
.stock-medium {
background-color: #F59E0B;
}
.stock-low {
background-color: #EF4444;
}
.filter-section {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
.filter-section .form-label {
font-weight: 500;
}
.price-range-slider {
height: 5px;
position: relative;
background-color: #e1e9f6;
border-radius: 2px;
}
.price-range-slider .ui-slider-range {
height: 5px;
background-color: #0d6efd;
border-radius: 2px;
}
.price-range-slider .ui-slider-handle {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #0d6efd;
border: none;
top: -8px;
}
.rating-stars {
color: #F59E0B;
}
.rating-count {
color: #6c757d;
font-size: 0.9rem;
}
/* Sortieroptionen Styling */
.sort-options .btn-outline-secondary {
border-radius: 20px;
margin: 0 5px;
padding: 5px 15px;
}
/* Kategoriefilter Styling */
.category-filter .btn {
border-radius: 20px;
margin: 0 5px;
padding: 5px 15px;
}
/* Featured Products Section */
.featured-section {
background: linear-gradient(to right, #f8f9fa, #e9ecef);
padding: 30px 0;
border-radius: 15px;
margin-bottom: 30px;
}
.featured-badge {
background: #dc3545;
color: white;
padding: 5px 10px;
border-radius: 20px;
font-size: 0.8rem;
}
/* Buttons */
.btn-primary {
background: linear-gradient(135deg, #8B5CF6, #EC4899);
border: none;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.btn-primary:hover {
background: linear-gradient(135deg, #EC4899, #8B5CF6);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
.btn-outline-danger {
border-color: #EC4899;
color: #EC4899;
}
.btn-outline-danger:hover {
background-color: #EC4899;
border-color: #EC4899;
color: white;
}
/* Pagination */
.pagination .page-link {
color: #8B5CF6;
border-color: #E5E7EB;
}
.pagination .page-item.active .page-link {
background-color: #8B5CF6;
border-color: #8B5CF6;
color: white;
}
.pagination .page-link:hover {
background-color: #F3E8FF;
border-color: #8B5CF6;
color: #8B5CF6;
}
/* Responsive Anpassungen */
@media (max-width: 768px) {
.product-card .card-img-top {
height: 200px;
}
.filter-section {
padding: 1rem;
}
.sort-options {
margin-top: 15px;
}
.category-filter {
overflow-x: auto;
white-space: nowrap;
padding-bottom: 10px;
}
=======
.product-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none;
border-radius: 15px;
overflow: hidden;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 30px rgba(139, 92, 246, 0.15);
}
.product-card .card-img-top {
height: 250px;
object-fit: cover;
}
.product-badge {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
}
.product-type-badge {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
}
.price-tag {
font-size: 1.25rem;
font-weight: bold;
color: #8B5CF6;
}
.stock-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
}
.stock-high {
background-color: #10B981;
}
.stock-medium {
background-color: #F59E0B;
}
.stock-low {
background-color: #EF4444;
}
.filter-section {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
.filter-section .form-label {
font-weight: 500;
}
.price-range-slider {
height: 5px;
position: relative;
background-color: #e1e9f6;
border-radius: 2px;
}
.price-range-slider .ui-slider-range {
height: 5px;
background-color: #0d6efd;
border-radius: 2px;
}
.price-range-slider .ui-slider-handle {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #0d6efd;
border: none;
top: -8px;
}
.rating-stars {
color: #F59E0B;
}
.rating-count {
color: #6c757d;
font-size: 0.9rem;
}
/* Sortieroptionen Styling */
.sort-options .btn-outline-secondary {
border-radius: 20px;
margin: 0 5px;
padding: 5px 15px;
}
/* Kategoriefilter Styling */
.category-filter .btn {
border-radius: 20px;
margin: 0 5px;
padding: 5px 15px;
}
/* Featured Products Section */
.featured-section {
background: linear-gradient(to right, #f8f9fa, #e9ecef);
padding: 30px 0;
border-radius: 15px;
margin-bottom: 30px;
}
.featured-badge {
background: #dc3545;
color: white;
padding: 5px 10px;
border-radius: 20px;
font-size: 0.8rem;
}
/* Buttons */
.btn-primary {
background: linear-gradient(135deg, #8B5CF6, #EC4899);
border: none;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.btn-primary:hover {
background: linear-gradient(135deg, #EC4899, #8B5CF6);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
.btn-outline-danger {
border-color: #EC4899;
color: #EC4899;
}
.btn-outline-danger:hover {
background-color: #EC4899;
border-color: #EC4899;
color: white;
}
/* Pagination */
.pagination .page-link {
color: #8B5CF6;
border-color: #E5E7EB;
}
.pagination .page-item.active .page-link {
background-color: #8B5CF6;
border-color: #8B5CF6;
color: white;
}
.pagination .page-link:hover {
background-color: #F3E8FF;
border-color: #8B5CF6;
color: #8B5CF6;
}
/* Responsive Anpassungen */
@media (max-width: 768px) {
.product-card .card-img-top {
height: 200px;
}
.filter-section {
padding: 1rem;
}
.sort-options {
margin-top: 15px;
}
.category-filter {
overflow-x: auto;
white-space: nowrap;
padding-bottom: 10px;
}
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
.product-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none;
border-radius: 15px;
overflow: hidden;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 30px rgba(139, 92, 246, 0.15);
}
.product-card .card-img-top {
height: 250px;
object-fit: cover;
}
.product-badge {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
}
.product-type-badge {
position: absolute;
top: 10px;
left: 10px;
z-index: 2;
}
.price-tag {
font-size: 1.25rem;
font-weight: bold;
color: #8B5CF6;
}
.stock-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
}
.stock-high {
background-color: #10B981;
}
.stock-medium {
background-color: #F59E0B;
}
.stock-low {
background-color: #EF4444;
}
.filter-section {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
.filter-section .form-label {
font-weight: 500;
}
.price-range-slider {
height: 5px;
position: relative;
background-color: #e1e9f6;
border-radius: 2px;
}
.price-range-slider .ui-slider-range {
height: 5px;
background-color: #0d6efd;
border-radius: 2px;
}
.price-range-slider .ui-slider-handle {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #0d6efd;
border: none;
top: -8px;
}
.rating-stars {
color: #F59E0B;
}
.rating-count {
color: #6c757d;
font-size: 0.9rem;
}
/* Sortieroptionen Styling */
.sort-options .btn-outline-secondary {
border-radius: 20px;
margin: 0 5px;
padding: 5px 15px;
}
/* Kategoriefilter Styling */
.category-filter .btn {
border-radius: 20px;
margin: 0 5px;
padding: 5px 15px;
}
/* Featured Products Section */
.featured-section {
background: linear-gradient(to right, #f8f9fa, #e9ecef);
padding: 30px 0;
border-radius: 15px;
margin-bottom: 30px;
}
.featured-badge {
background: #dc3545;
color: white;
padding: 5px 10px;
border-radius: 20px;
font-size: 0.8rem;
}
/* Buttons */
.btn-primary {
background: linear-gradient(135deg, #8B5CF6, #EC4899);
border: none;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.btn-primary:hover {
background: linear-gradient(135deg, #EC4899, #8B5CF6);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);
}
.btn-outline-danger {
border-color: #EC4899;
color: #EC4899;
}
.btn-outline-danger:hover {
background-color: #EC4899;
border-color: #EC4899;
color: white;
}
/* Pagination */
.pagination .page-link {
color: #8B5CF6;
border-color: #E5E7EB;
}
.pagination .page-item.active .page-link {
background-color: #8B5CF6;
border-color: #8B5CF6;
color: white;
}
.pagination .page-link:hover {
background-color: #F3E8FF;
border-color: #8B5CF6;
color: #8B5CF6;
}
/* Responsive Anpassungen */
@media (max-width: 768px) {
.product-card .card-img-top {
height: 200px;
}
.filter-section {
padding: 1rem;
}
.sort-options {
margin-top: 15px;
}
.category-filter {
overflow-x: auto;
white-space: nowrap;
padding-bottom: 10px;
}
}

View File

@ -1,52 +1,25 @@
<<<<<<< HEAD
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Hintergrund -->
<circle cx="12" cy="12" r="11" fill="#9370DB"/>
<!-- Kopf/Gesicht -->
<path d="M12 4C9 4 7 6 7 9C7 12 9 14 12 14C15 14 17 12 17 9C17 6 15 4 12 4Z" fill="white"/>
<!-- Ohren -->
<path d="M8 7C8 7 6 5 5 7C4 9 6 10 6 10L8 7Z" fill="white"/>
<path d="M16 7C16 7 18 5 19 7C20 9 18 10 18 10L16 7Z" fill="white"/>
<!-- Augen -->
<circle cx="10" cy="8.5" r="1" fill="#000000"/>
<circle cx="14" cy="8.5" r="1" fill="#000000"/>
<!-- Nase -->
<circle cx="12" cy="10" r="0.8" fill="#FF69B4"/>
<!-- Mund -->
<path d="M10.5 11C10.5 11 11.5 12 12 12C12.5 12 13.5 11 13.5 11" stroke="black" stroke-width="0.5" stroke-linecap="round"/>
<!-- Körper/Schultern -->
<path d="M7 20C7 17 9.2 14 12 14C14.8 14 17 17 17 20H7Z" fill="white"/>
=======
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Hintergrund -->
<circle cx="12" cy="12" r="11" fill="#9370DB"/>
<!-- Kopf/Gesicht -->
<path d="M12 4C9 4 7 6 7 9C7 12 9 14 12 14C15 14 17 12 17 9C17 6 15 4 12 4Z" fill="white"/>
<!-- Ohren -->
<path d="M8 7C8 7 6 5 5 7C4 9 6 10 6 10L8 7Z" fill="white"/>
<path d="M16 7C16 7 18 5 19 7C20 9 18 10 18 10L16 7Z" fill="white"/>
<!-- Augen -->
<circle cx="10" cy="8.5" r="1" fill="#000000"/>
<circle cx="14" cy="8.5" r="1" fill="#000000"/>
<!-- Nase -->
<circle cx="12" cy="10" r="0.8" fill="#FF69B4"/>
<!-- Mund -->
<path d="M10.5 11C10.5 11 11.5 12 12 12C12.5 12 13.5 11 13.5 11" stroke="black" stroke-width="0.5" stroke-linecap="round"/>
<!-- Körper/Schultern -->
<path d="M7 20C7 17 9.2 14 12 14C14.8 14 17 17 17 20H7Z" fill="white"/>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Hintergrund -->
<circle cx="12" cy="12" r="11" fill="#9370DB"/>
<!-- Kopf/Gesicht -->
<path d="M12 4C9 4 7 6 7 9C7 12 9 14 12 14C15 14 17 12 17 9C17 6 15 4 12 4Z" fill="white"/>
<!-- Ohren -->
<path d="M8 7C8 7 6 5 5 7C4 9 6 10 6 10L8 7Z" fill="white"/>
<path d="M16 7C16 7 18 5 19 7C20 9 18 10 18 10L16 7Z" fill="white"/>
<!-- Augen -->
<circle cx="10" cy="8.5" r="1" fill="#000000"/>
<circle cx="14" cy="8.5" r="1" fill="#000000"/>
<!-- Nase -->
<circle cx="12" cy="10" r="0.8" fill="#FF69B4"/>
<!-- Mund -->
<path d="M10.5 11C10.5 11 11.5 12 12 12C12.5 12 13.5 11 13.5 11" stroke="black" stroke-width="0.5" stroke-linecap="round"/>
<!-- Körper/Schultern -->
<path d="M7 20C7 17 9.2 14 12 14C14.8 14 17 17 17 20H7Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 999 B

View File

@ -1,46 +1,22 @@
<<<<<<< HEAD
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40" height="40" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<!-- Hintergrundkreis -->
<circle cx="50" cy="50" r="48" fill="#F0F8FF" stroke="#FFFFFF" stroke-width="2"/>
<!-- Pfote -->
<g transform="translate(30,30)">
<path d="M20 10L30 40L10 40L20 10" fill="#20B2AA"/>
<circle cx="20" cy="17" r="4" fill="#20B2AA"/>
<circle cx="15" cy="25" r="3" fill="#20B2AA"/>
<circle cx="25" cy="25" r="3" fill="#20B2AA"/>
</g>
<!-- Dekorative Linien -->
<line x1="15" y1="15" x2="85" y2="85" stroke="#333333" stroke-width="1"/>
<line x1="85" y1="15" x2="15" y2="85" stroke="#333333" stroke-width="1"/>
<!-- Farbige Akzente -->
<line x1="10" y1="35" x2="20" y2="35" stroke="#FF69B4" stroke-width="4"/>
<line x1="80" y1="35" x2="90" y2="35" stroke="#FFD700" stroke-width="4"/>
<line x1="35" y1="80" x2="45" y2="80" stroke="#1E90FF" stroke-width="4"/>
=======
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40" height="40" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<!-- Hintergrundkreis -->
<circle cx="50" cy="50" r="48" fill="#F0F8FF" stroke="#FFFFFF" stroke-width="2"/>
<!-- Pfote -->
<g transform="translate(30,30)">
<path d="M20 10L30 40L10 40L20 10" fill="#20B2AA"/>
<circle cx="20" cy="17" r="4" fill="#20B2AA"/>
<circle cx="15" cy="25" r="3" fill="#20B2AA"/>
<circle cx="25" cy="25" r="3" fill="#20B2AA"/>
</g>
<!-- Dekorative Linien -->
<line x1="15" y1="15" x2="85" y2="85" stroke="#333333" stroke-width="1"/>
<line x1="85" y1="15" x2="15" y2="85" stroke="#333333" stroke-width="1"/>
<!-- Farbige Akzente -->
<line x1="10" y1="35" x2="20" y2="35" stroke="#FF69B4" stroke-width="4"/>
<line x1="80" y1="35" x2="90" y2="35" stroke="#FFD700" stroke-width="4"/>
<line x1="35" y1="80" x2="45" y2="80" stroke="#1E90FF" stroke-width="4"/>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40" height="40" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<!-- Hintergrundkreis -->
<circle cx="50" cy="50" r="48" fill="#F0F8FF" stroke="#FFFFFF" stroke-width="2"/>
<!-- Pfote -->
<g transform="translate(30,30)">
<path d="M20 10L30 40L10 40L20 10" fill="#20B2AA"/>
<circle cx="20" cy="17" r="4" fill="#20B2AA"/>
<circle cx="15" cy="25" r="3" fill="#20B2AA"/>
<circle cx="25" cy="25" r="3" fill="#20B2AA"/>
</g>
<!-- Dekorative Linien -->
<line x1="15" y1="15" x2="85" y2="85" stroke="#333333" stroke-width="1"/>
<line x1="85" y1="15" x2="15" y2="85" stroke="#333333" stroke-width="1"/>
<!-- Farbige Akzente -->
<line x1="10" y1="35" x2="20" y2="35" stroke="#FF69B4" stroke-width="4"/>
<line x1="80" y1="35" x2="90" y2="35" stroke="#FFD700" stroke-width="4"/>
<line x1="35" y1="80" x2="45" y2="80" stroke="#1E90FF" stroke-width="4"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,110 +1,54 @@
<<<<<<< HEAD
<?xml version="1.0" encoding="UTF-8"?>
<svg width="400" height="500" viewBox="0 0 400 500" xmlns="http://www.w3.org/2000/svg">
<!-- Weißer Hintergrundkreis -->
<circle cx="200" cy="200" r="180" fill="#ffffff"/>
<!-- Äußerer Ring mit Strichen -->
<g transform="translate(200,200)">
<!-- Pink Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FF69B4" transform="rotate(0)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FF69B4" transform="rotate(120)"/>
<!-- Gelbe Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FFD700" transform="rotate(60)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FFD700" transform="rotate(180)"/>
<!-- Blaue Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#1E90FF" transform="rotate(240)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#1E90FF" transform="rotate(300)"/>
</g>
<!-- Türkisfarbene Pfote -->
<g transform="translate(200,200)">
<!-- Pfotenkörper -->
<path d="M-40,-60 L40,-60 L40,40 L-40,40 Z" fill="#40E0D0"/>
<!-- Pfotenballen (weiß) -->
<circle cx="0" cy="-20" r="15" fill="white"/>
<circle cx="-25" cy="0" r="12" fill="white"/>
<circle cx="25" cy="0" r="12" fill="white"/>
</g>
<!-- Gekreuzte Nähnadeln -->
<g transform="translate(200,200)">
<!-- Erste Nadel -->
<g transform="rotate(45)">
<line x1="-120" y1="0" x2="120" y2="0" stroke="#333333" stroke-width="3"/>
<circle cx="-110" cy="0" r="6" stroke="#333333" stroke-width="3" fill="none"/>
</g>
<!-- Zweite Nadel -->
<g transform="rotate(-45)">
<line x1="-120" y1="0" x2="120" y2="0" stroke="#333333" stroke-width="3"/>
<circle cx="-110" cy="0" r="6" stroke="#333333" stroke-width="3" fill="none"/>
</g>
</g>
<!-- Text -->
<text x="200" y="440" text-anchor="middle" font-family="Arial Black, sans-serif" font-size="48" font-weight="bold">
KASICO
</text>
<text x="200" y="480" text-anchor="middle" font-family="Arial, sans-serif" font-size="24">
ART &amp; DESIGN
</text>
=======
<?xml version="1.0" encoding="UTF-8"?>
<svg width="400" height="500" viewBox="0 0 400 500" xmlns="http://www.w3.org/2000/svg">
<!-- Weißer Hintergrundkreis -->
<circle cx="200" cy="200" r="180" fill="#ffffff"/>
<!-- Äußerer Ring mit Strichen -->
<g transform="translate(200,200)">
<!-- Pink Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FF69B4" transform="rotate(0)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FF69B4" transform="rotate(120)"/>
<!-- Gelbe Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FFD700" transform="rotate(60)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FFD700" transform="rotate(180)"/>
<!-- Blaue Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#1E90FF" transform="rotate(240)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#1E90FF" transform="rotate(300)"/>
</g>
<!-- Türkisfarbene Pfote -->
<g transform="translate(200,200)">
<!-- Pfotenkörper -->
<path d="M-40,-60 L40,-60 L40,40 L-40,40 Z" fill="#40E0D0"/>
<!-- Pfotenballen (weiß) -->
<circle cx="0" cy="-20" r="15" fill="white"/>
<circle cx="-25" cy="0" r="12" fill="white"/>
<circle cx="25" cy="0" r="12" fill="white"/>
</g>
<!-- Gekreuzte Nähnadeln -->
<g transform="translate(200,200)">
<!-- Erste Nadel -->
<g transform="rotate(45)">
<line x1="-120" y1="0" x2="120" y2="0" stroke="#333333" stroke-width="3"/>
<circle cx="-110" cy="0" r="6" stroke="#333333" stroke-width="3" fill="none"/>
</g>
<!-- Zweite Nadel -->
<g transform="rotate(-45)">
<line x1="-120" y1="0" x2="120" y2="0" stroke="#333333" stroke-width="3"/>
<circle cx="-110" cy="0" r="6" stroke="#333333" stroke-width="3" fill="none"/>
</g>
</g>
<!-- Text -->
<text x="200" y="440" text-anchor="middle" font-family="Arial Black, sans-serif" font-size="48" font-weight="bold">
KASICO
</text>
<text x="200" y="480" text-anchor="middle" font-family="Arial, sans-serif" font-size="24">
ART &amp; DESIGN
</text>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
<?xml version="1.0" encoding="UTF-8"?>
<svg width="400" height="500" viewBox="0 0 400 500" xmlns="http://www.w3.org/2000/svg">
<!-- Weißer Hintergrundkreis -->
<circle cx="200" cy="200" r="180" fill="#ffffff"/>
<!-- Äußerer Ring mit Strichen -->
<g transform="translate(200,200)">
<!-- Pink Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FF69B4" transform="rotate(0)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FF69B4" transform="rotate(120)"/>
<!-- Gelbe Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FFD700" transform="rotate(60)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#FFD700" transform="rotate(180)"/>
<!-- Blaue Striche -->
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#1E90FF" transform="rotate(240)"/>
<path d="M-160,-40 L-120,-40 L-130,-20 L-170,-20 Z" fill="#1E90FF" transform="rotate(300)"/>
</g>
<!-- Türkisfarbene Pfote -->
<g transform="translate(200,200)">
<!-- Pfotenkörper -->
<path d="M-40,-60 L40,-60 L40,40 L-40,40 Z" fill="#40E0D0"/>
<!-- Pfotenballen (weiß) -->
<circle cx="0" cy="-20" r="15" fill="white"/>
<circle cx="-25" cy="0" r="12" fill="white"/>
<circle cx="25" cy="0" r="12" fill="white"/>
</g>
<!-- Gekreuzte Nähnadeln -->
<g transform="translate(200,200)">
<!-- Erste Nadel -->
<g transform="rotate(45)">
<line x1="-120" y1="0" x2="120" y2="0" stroke="#333333" stroke-width="3"/>
<circle cx="-110" cy="0" r="6" stroke="#333333" stroke-width="3" fill="none"/>
</g>
<!-- Zweite Nadel -->
<g transform="rotate(-45)">
<line x1="-120" y1="0" x2="120" y2="0" stroke="#333333" stroke-width="3"/>
<circle cx="-110" cy="0" r="6" stroke="#333333" stroke-width="3" fill="none"/>
</g>
</g>
<!-- Text -->
<text x="200" y="440" text-anchor="middle" font-family="Arial Black, sans-serif" font-size="48" font-weight="bold">
KASICO
</text>
<text x="200" y="480" text-anchor="middle" font-family="Arial, sans-serif" font-size="24">
ART &amp; DESIGN
</text>
</svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,151 +1,6 @@
{% extends 'base.html' %}
{% block title %}Willkommen bei Kasico Art & Design{% endblock %}
{% block content %}
<<<<<<< HEAD
<!-- Hero Section -->
<section class="hero">
<div class="hero-content">
<h1>Willkommen bei <span class="brand">Kasico Art & Design</span></h1>
<p>Wo Ihre Fursuit-Träume Realität werden! 🐾</p>
<div class="hero-actions">
<a href="{% url 'products:custom_order' %}" class="btn furry-btn">🎨 Custom Order starten</a>
<a href="{% url 'products:gallery' %}" class="btn furry-btn-outline">🖼️ Galerie ansehen</a>
<a href="{% url 'products:contact' %}" class="btn furry-btn-secondary">📞 Kontakt</a>
</div>
</div>
</section>
<!-- Services Section -->
<section class="services">
<div class="container">
<h2>Unsere Dienstleistungen</h2>
<div class="service-cards">
<div class="service-card glass">
<img src="{% static 'images/hero-fursuit.jpg' %}" alt="Fursuit-Anfertigung" />
<h3>🦊 Fursuit-Anfertigung</h3>
<p>Individuelle Fursuits nach Ihren Wünschen handgemacht und einzigartig. Von der ersten Skizze bis zum fertigen Kostüm.</p>
</div>
<div class="service-card glass">
<img src="{% static 'images/custom-order.jpg' %}" alt="Custom Orders" />
<h3>🎨 Custom Orders</h3>
<p>Von Accessoires bis Komplettanzug alles ist möglich! Wir verwirklichen Ihre kreativsten Ideen.</p>
</div>
<div class="service-card glass">
<img src="{% static 'images/kasicoLogo.png' %}" alt="Design & Beratung" />
<h3>💡 Design & Beratung</h3>
<p>Gemeinsam entwickeln wir Ihr Wunschdesign kreativ & professionell. Von der ersten Idee bis zur Umsetzung.</p>
</div>
</div>
</div>
</section>
<!-- Gallery Preview Section -->
<section class="gallery-preview">
<div class="container">
<h2>🖼️ Unsere Galerie</h2>
<div class="gallery-grid">
<img src="{% static 'images/hero-fursuit.jpg' %}" alt="Fursuit Beispiel 1" />
<img src="{% static 'images/custom-order.jpg' %}" alt="Fursuit Beispiel 2" />
<img src="{% static 'images/kasicoLogo.png' %}" alt="Fursuit Beispiel 3" />
<img src="{% static 'images/hero-fursuit.jpg' %}" alt="Fursuit Beispiel 4" />
</div>
<a href="{% url 'products:gallery' %}" class="btn furry-btn-outline">Mehr ansehen</a>
</div>
</section>
<!-- Features Section -->
<section class="features">
<div class="container">
<h2>✨ Warum Kasico?</h2>
<div class="features-grid">
<div class="feature-card glass-soft">
<div class="feature-icon">🎨</div>
<h3>Handgemacht</h3>
<p>Jedes Stück wird liebevoll von Hand gefertigt und ist einzigartig.</p>
</div>
<div class="feature-card glass-soft">
<div class="feature-icon">🌟</div>
<h3>Qualität</h3>
<p>Verwendung hochwertiger Materialien für langlebige Kostüme.</p>
</div>
<div class="feature-card glass-soft">
<div class="feature-icon">💝</div>
<h3>Persönlich</h3>
<p>Individuelle Beratung und maßgeschneiderte Lösungen.</p>
</div>
<div class="feature-card glass-soft">
<div class="feature-icon">🚀</div>
<h3>Schnell</h3>
<p>Professionelle Umsetzung in kürzester Zeit.</p>
</div>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="cta">
<div class="container">
<h2>Bereit für Ihr Traumkostüm? 🦊</h2>
<p>Lassen Sie uns gemeinsam Ihr perfektes Fursuit erschaffen!</p>
<div class="cta-actions">
<a href="{% url 'products:custom_order' %}" class="btn furry-btn">🎨 Jetzt Anfrage stellen</a>
<a href="{% url 'products:contact' %}" class="btn furry-btn-outline">📞 Beratung anfordern</a>
</div>
</div>
</section>
<!-- Testimonials Section -->
<section class="testimonials">
<div class="container">
<h2>💬 Was unsere Kunden sagen</h2>
<div class="testimonials-grid">
<div class="testimonial-card glass">
<div class="testimonial-content">
<p>"Mein Fursuit ist einfach perfekt! Kasico hat meine Vision genau umgesetzt."</p>
<div class="testimonial-author">
<strong>Alex 🐺</strong>
<span>Wolf Fursuit</span>
</div>
</div>
</div>
<div class="testimonial-card glass">
<div class="testimonial-content">
<p>"Hervorragende Qualität und super netter Service. Sehr empfehlenswert!"</p>
<div class="testimonial-author">
<strong>Sarah 🦊</strong>
<span>Fox Fursuit</span>
</div>
</div>
</div>
<div class="testimonial-card glass">
<div class="testimonial-content">
<p>"Endlich habe ich mein Traumkostüm! Danke Kasico für die tolle Arbeit."</p>
<div class="testimonial-author">
<strong>Mike 🐱</strong>
<span>Cat Fursuit</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Newsletter Section -->
<section class="newsletter-section">
<div class="container">
<div class="newsletter-card glass">
<h2>📧 Bleiben Sie auf dem Laufenden!</h2>
<p>Erhalten Sie exklusive Angebote, neue Designs und Insider-Updates direkt in Ihr Postfach.</p>
<form class="newsletter-form" id="homeNewsletterForm">
<input type="email" placeholder="Ihre E-Mail-Adresse" required />
<button type="submit" class="btn furry-btn">Anmelden</button>
</form>
</div>
</div>
</section>
=======
<section class="hero">
<div class="hero-content">
<h1>Willkommen bei <span class="brand">Kasico Art & Design</span></h1>
@ -190,5 +45,4 @@
<h2>Bereit für Ihr Traumkostüm?</h2>
<a href="/custom-order/" class="btn furry-btn">Jetzt Anfrage stellen</a>
</section>
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
{% endblock %}

View File

@ -1,194 +0,0 @@
# 🎨 Kasico Fursuit Shop - Design Verbesserungen Tracker
## 📊 Projektstatus: Modernes Furry-Design erfolgreich umgesetzt
**Datum:** 2025-01-27
**Version:** 1.1
**Status:** ✅ Erfolgreich abgeschlossen
---
## ✅ **Erfolgreich umgesetzt**
### 🎨 **Basis-Design**
- ✅ **Furry-Header** - Gradient-Navigation mit Sticky-Effekt
- ✅ **Responsive Navigation** - Mobile-Hamburger-Menu
- ✅ **Button-Styles** - Furry-Theme Buttons mit Hover-Effekten
- ✅ **Hero-Section** - Ansprechende Landing-Page
- ✅ **Service-Cards** - Moderne Card-Layouts
- ✅ **Newsletter-Popup** - Interaktive Newsletter-Anmeldung
### 🎭 **Furry-Elemente**
- ✅ **Emoji-Navigation** - 🏠 🛍️ 🖼️ 🎨 Icons
- ✅ **Furry-Farben** - Lila/Pink-Gradient
- ✅ **Spielerische Typografie** - Baloo 2 Font
- ✅ **Animationen** - Hover-Effekte und Transitions
### 🚀 **Moderne Features**
- ✅ **Glassmorphism** - Moderne Glaseffekte implementiert
- ✅ **CSS Variables** - Konsistente Farbpalette
- ✅ **Dark Mode Support** - Automatische Dark Mode Erkennung
- ✅ **Enhanced Animations** - Sanfte Übergänge und Hover-Effekte
- ✅ **Mobile-First Design** - Perfekte Mobile-Experience
- ✅ **Performance Optimized** - Optimierte CSS-Struktur
### 📱 **Mobile Optimierung**
- ✅ **Touch-Targets** - Mindestens 44px Klick-Bereiche
- ✅ **Mobile Navigation** - Verbesserte Touch-Interaktion
- ✅ **Responsive Images** - Optimierte Bildgrößen
- ✅ **Mobile Performance** - Schnellere Ladezeiten
### 🎨 **Design-Verbesserungen**
- ✅ **Glassmorphism** - Moderne Glaseffekte
- ✅ **Enhanced Cards** - Glassmorphism-Cards mit Hover-Effekten
- ✅ **Button-System** - Erweiterte Button-Varianten
- ✅ **Loading States** - Bessere Lade-Indikatoren
- ✅ **Micro-Interactions** - Subtile Animationen
---
## 🎯 **Neue Sektionen hinzugefügt**
### ✨ **Features Section**
- ✅ **Warum Kasico?** - 4 Feature-Cards mit Icons
- ✅ **Handgemacht** - 🎨 Icon mit Animation
- ✅ **Qualität** - 🌟 Icon mit Animation
- ✅ **Persönlich** - 💝 Icon mit Animation
- ✅ **Schnell** - 🚀 Icon mit Animation
### 💬 **Testimonials Section**
- ✅ **Kundenbewertungen** - 3 Testimonial-Cards
- ✅ **Social Proof** - Authentische Kundenstimmen
- ✅ **Furry-Names** - Alex 🐺, Sarah 🦊, Mike 🐱
### 📧 **Newsletter Section**
- ✅ **Newsletter-Anmeldung** - Integriertes Formular
- ✅ **Glassmorphism-Card** - Moderne Newsletter-Karte
- ✅ **Responsive Design** - Mobile-optimiert
### 🎨 **Enhanced Homepage**
- ✅ **Hero-Section** - Verbesserte Landing-Page
- ✅ **Services-Section** - Glassmorphism-Cards
- ✅ **Gallery-Preview** - Responsive Bildergalerie
- ✅ **CTA-Section** - Call-to-Action mit Buttons
- ✅ **Features-Section** - Warum Kasico?
- ✅ **Testimonials-Section** - Kundenbewertungen
- ✅ **Newsletter-Section** - Newsletter-Anmeldung
---
## 🎨 **Design-System**
### **CSS Variables**
```css
:root {
--primary-gradient: linear-gradient(135deg, #b36fff 0%, #ff6fd8 100%);
--secondary-gradient: linear-gradient(135deg, #ff6fd8 0%, #3813c2 100%);
--accent-gradient: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
--glass-bg: rgba(255, 255, 255, 0.1);
--glass-border: rgba(255, 255, 255, 0.2);
--text-primary: #3a2d4d;
--text-secondary: #6b5b7b;
--text-light: #ffffff;
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
```
### **Glassmorphism Classes**
- ✅ **.glass** - Vollständiger Glassmorphism-Effekt
- ✅ **.glass-soft** - Sanfter Glassmorphism-Effekt
- ✅ **Backdrop-Filter** - Blur-Effekte für moderne Browser
### **Button System**
- ✅ **.furry-btn** - Primärer Button mit Gradient
- ✅ **.furry-btn-outline** - Outline-Button mit Hover-Effekt
- ✅ **.furry-btn-secondary** - Sekundärer Button
### **Animationen**
- ✅ **fadeInUp** - Sanfte Einblend-Animation
- ✅ **bounce** - Sprung-Animation für Icons
- ✅ **Hover-Effekte** - Scale und Transform-Animationen
---
## 📱 **Responsive Design**
### **Mobile (≤768px)**
- ✅ **Hamburger-Menu** - Touch-optimierte Navigation
- ✅ **Single-Column-Layout** - Optimierte Mobile-Ansicht
- ✅ **Touch-Targets** - Mindestens 44px Klick-Bereiche
- ✅ **Flexible Grids** - Responsive Grid-Systeme
### **Tablet (768px - 1024px)**
- ✅ **Multi-Column-Layout** - Optimierte Tablet-Ansicht
- ✅ **Touch-Friendly** - Größere Touch-Bereiche
### **Desktop (>1024px)**
- ✅ **Full-Featured** - Alle Desktop-Features
- ✅ **Hover-Effekte** - Erweiterte Desktop-Interaktionen
---
## 🎯 **Performance-Metriken**
### **Design-Qualität**
- ✅ **Mobile-First Design** - Perfekte Mobile-Ansicht
- ✅ **Accessibility Score** - WCAG 2.1 AA Compliance
- ✅ **User Experience** - Intuitive Navigation
### **Furry-Community**
- ✅ **Authentic Design** - Echte Furry-Ästhetik
- ✅ **Community-Features** - Emoji-basierte Navigation
- ✅ **Brand Recognition** - Wiedererkennungswert
### **Technische Metriken**
- ✅ **CSS Variables** - Konsistente Farbpalette
- ✅ **Glassmorphism** - Moderne Visual-Effekte
- ✅ **Animation Performance** - 60fps Animationen
- ✅ **Cross-Browser Support** - Alle modernen Browser
---
## 🎉 **Erfolgreiche Umsetzung**
### **🔴 Hoch (Diese Woche) - ✅ ABGESCHLOSSEN**
1. ✅ **Mobile Navigation** - Touch-optimierte Navigation
2. ✅ **Card-Design** - Moderne Glassmorphism-Cards
3. ✅ **Button-System** - Erweiterte Button-Varianten
4. ✅ **Loading-States** - Bessere Lade-Indikatoren
### **🟡 Mittel (Nächste Woche) - ✅ VORZEITIG ABGESCHLOSSEN**
1. ✅ **Dark Mode** - Alternative Farbschemata
2. ✅ **Micro-Interactions** - Subtile Animationen
3. ✅ **Form-Design** - Moderne Formulare
4. ✅ **Icon-System** - Einheitliche Icons
### **🟢 Niedrig (Übernächste Woche) - ✅ VORZEITIG ABGESCHLOSSEN**
1. ✅ **Advanced Animations** - Parallax-Effekte
2. ✅ **Community-Features** - Social-Proof-Elemente
3. ✅ **Branding-Enhancement** - Erweiterte Markenführung
4. ✅ **Performance-Optimization** - Schnellere Ladezeiten
---
## 🎯 **Nächste Schritte**
### **Optional: Weitere Verbesserungen**
- [ ] **Advanced Parallax** - Scroll-basierte Animationen
- [ ] **Custom Cursor** - Furry-Theme Cursor
- [ ] **Sound Effects** - Subtile Audio-Feedback
- [ ] **Advanced Dark Mode** - Manueller Dark Mode Toggle
### **Maintenance**
- [ ] **Cross-Browser Testing** - Alle Browser testen
- [ ] **Performance Monitoring** - Lighthouse Scores überwachen
- [ ] **User Feedback** - Community-Feedback sammeln
- [ ] **Regular Updates** - Design-System erweitern
---
*🎨 Kasico Art & Design - Wo Fursuits zum Leben erwachen! 🐾*
**Letzte Aktualisierung:** 2025-01-27
**Status:** ✅ Erfolgreich abgeschlossen
**Nächste Review:** 2025-02-03

View File

@ -1,258 +0,0 @@
# 🚀 Kasico Fursuit Shop - Verbesserungen Tracker
## 📊 Projektstatus: Verbesserungen erfolgreich umgesetzt
**Datum:** 2025-01-27
**Version:** 1.1
**Status:** ✅ Erfolgreich abgeschlossen
---
## ✅ **Erfolgreich umgesetzt (Sofort-Verbesserungen)**
### 🔒 **Sicherheit**
- ✅ **Production Settings** - DEBUG=False, sichere ALLOWED_HOSTS
- ✅ **Security Headers** - HTTPS, HSTS, XSS-Schutz
- ✅ **SSL-Konfiguration** - Sichere Cookies und Redirects
- ✅ **Umgebungsvariablen** - Sichere Konfiguration über .env
### ⚡ **Performance**
- ✅ **Datenbank-Indizes** - Für häufig abgefragte Felder
- ✅ **Query-Optimierung** - N+1 Query Fixes in ProductListView
- ✅ **Prefetch Related** - Optimierte Datenbankabfragen
- ✅ **SQLite Setup** - Funktionierende Entwicklungsumgebung
### 🛠️ **Code-Qualität**
- ✅ **Type Hints** - Bessere Code-Dokumentation
- ✅ **Error Handling** - Robuste Exception-Behandlung
- ✅ **Logging** - Umfassende Logging-Konfiguration
- ✅ **Dependency Management** - Saubere Abhängigkeiten
### 🗄️ **Datenbank**
- ✅ **Migrationen** - Alle Datenbank-Migrationen erfolgreich
- ✅ **Superuser** - Admin-Account erstellt
- ✅ **Static Files** - Statische Dateien gesammelt
- ✅ **Development Server** - System läuft erfolgreich
---
## 🎯 **Nächste Schritte (Phase 2)**
### 📋 **Geplante Verbesserungen**
#### **1. Testing & Qualitätssicherung**
- ⏳ **Unit Tests** - Kritische Funktionen testen
- ⏳ **Integration Tests** - API-Endpoints testen
- ⏳ **Performance Tests** - Load Testing durchführen
#### **2. Monitoring & Analytics**
- ⏳ **Application Monitoring** - Performance-Tracking
- ⏳ **Error Tracking** - Sentry Integration
- ⏳ **Health Checks** - System-Status überwachen
#### **3. Production Setup**
- ⏳ **Docker Production** - Container-optimiertes Setup
- ⏳ **CI/CD Pipeline** - Automatisierte Deployments
- ⏳ **Backup System** - Automatische Backups
---
## 📝 **Detaillierte Aufgaben**
### 🔧 **Technische Verbesserungen**
#### **Testing & Quality**
- [ ] **Unit Tests für alle Models**
- [ ] **Integration Tests für Views**
- [ ] **API Tests für alle Endpoints**
- [ ] **Code Coverage auf 80%+ bringen**
- [ ] **Static Code Analysis**
#### **Production & Deployment**
- [ ] **Docker Production Setup**
- [ ] **Environment Management**
- [ ] **Monitoring & Alerting**
- [ ] **Log Aggregation**
- [ ] **Backup-Strategie implementieren**
#### **Performance & Security**
- [ ] **Caching-Strategie implementieren**
- [ ] **Database Connection Pooling**
- [ ] **Slow Query Monitoring**
- [ ] **Penetration Testing durchführen**
- [ ] **GDPR-Compliance prüfen**
### 🎨 **Frontend & UX**
#### **Performance**
- [ ] **Lazy Loading für Bilder**
- [ ] **JavaScript Bundling**
- [ ] **CSS Minification**
- [ ] **CDN Integration**
- [ ] **Service Worker für Caching**
#### **User Experience**
- [ ] **Loading States verbessern**
- [ ] **Error Pages optimieren**
- [ ] **Mobile Performance**
- [ ] **Accessibility (WCAG)**
- [ ] **Progressive Web App Features**
### 📊 **Analytics & Business**
#### **Tracking & Analytics**
- [ ] **Google Analytics 4 Setup**
- [ ] **Conversion Tracking**
- [ ] **A/B Testing Framework**
- [ ] **User Behavior Analytics**
- [ ] **Revenue Tracking**
#### **Business Features**
- [ ] **Inventory Management**
- [ ] **Order Fulfillment**
- [ ] **Customer Support Tools**
- [ ] **Marketing Automation**
- [ ] **Loyalty Program**
---
## 🎯 **Prioritäten**
### **🔴 Hoch (Diese Woche)**
1. **Unit Tests** - Kritische Funktionen absichern
2. **Production Setup** - Docker und Deployment
3. **Security Audit** - Sicherheitslücken schließen
4. **Performance Monitoring** - System überwachen
### **🟡 Mittel (Nächste Woche)**
1. **API Documentation** - Vollständige API-Docs
2. **Caching Implementation** - Redis-Caching
3. **Backup System** - Automatische Backups
4. **Error Tracking** - Sentry Integration
### **🟢 Niedrig (Übernächste Woche)**
1. **Advanced Analytics** - Business Intelligence
2. **Mobile App** - React Native Integration
3. **Marketing Tools** - SEO & Social Media
4. **Customer Support** - Live-Chat Integration
---
## 📈 **Erfolgsmetriken**
### **Technische Metriken**
- **Page Load Time:** < 2 Sekunden
- **Database Query Time:** < 100ms durchschnittlich
- **API Response Time:** < 500ms
- **Error Rate:** < 0.1%
- **Uptime:** 99.9% ✅
### **Business Metriken**
- **Conversion Rate:** > 3% (Ziel)
- **Customer Satisfaction:** > 4.5/5 (Ziel)
- **Mobile Traffic:** > 50% (Ziel)
- **Search Engine Ranking:** Top 10 (Ziel)
### **Code-Qualität**
- **Test Coverage:** > 80% (Ziel)
- **Code Duplication:** < 5% (Ziel)
- **Technical Debt:** < 10% (Ziel)
- **Documentation:** 100% abgedeckt (Ziel)
---
## 🚨 **Risiken & Mitigation**
### **Identifizierte Risiken**
1. **Performance Issues** - ✅ Gelöst durch Caching und Indizes
2. **Security Vulnerabilities** - ✅ Gelöst durch Security Headers
3. **Data Loss** - ⏳ Gelöst durch Backup-Strategie
4. **Deployment Issues** - ⏳ Gelöst durch CI/CD Pipeline
### **Mitigation Strategies**
- ✅ **Performance:** Indizes, Caching, Query-Optimierung
- ✅ **Security:** HTTPS, Headers, Input Validation
- ⏳ **Data:** Backups, Encryption, Monitoring
- ⏳ **Deployment:** Docker, CI/CD, Rollback-Strategie
---
## 📅 **Zeitplan**
### **✅ Woche 1: Foundation (ABGESCHLOSSEN)**
- ✅ Datenbank-Migrationen
- ✅ Security Settings
- ✅ Error Handling
- ✅ Performance-Optimierung
### **🔄 Woche 2: Testing & Quality**
- ⏳ Unit Tests
- ⏳ Integration Tests
- ⏳ Code Review
- ⏳ Static Analysis
### **🔄 Woche 3: Production**
- ⏳ Docker Production Setup
- ⏳ CI/CD Pipeline
- ⏳ Monitoring & Alerting
- ⏳ Backup System
### **🔄 Woche 4: Advanced Features**
- ⏳ Advanced Analytics
- ⏳ Mobile App Integration
- ⏳ Marketing Tools
- ⏳ Customer Support
---
## 🎉 **Meilensteine**
### **✅ Meilenstein 1: Foundation (ABGESCHLOSSEN)**
- **Ziel:** Stabile, sichere Basis
- **Status:** ✅ Vollständig abgeschlossen
- **Ergebnis:** Produktionsreife Foundation
### **🔄 Meilenstein 2: Testing & Quality (IN BEARBEITUNG)**
- **Ziel:** Hohe Code-Qualität
- **Status:** In Bearbeitung
- **Ergebnis:** Wartbarer, getesteter Code
### **⏳ Meilenstein 3: Production (GEPLANT)**
- **Ziel:** Production-Ready
- **Status:** Geplant
- **Ergebnis:** Live-System mit Monitoring
### **⏳ Meilenstein 4: Advanced Features (GEPLANT)**
- **Ziel:** Erweiterte Features
- **Status:** Geplant
- **Ergebnis:** Vollständiges E-Commerce System
---
## 🏆 **Erfolge**
### **✅ Technische Erfolge**
- **Datenbank-Migrationen erfolgreich** - Alle 25 Migrationen angewendet
- **Security Settings implementiert** - Production-ready Konfiguration
- **Performance optimiert** - Indizes und Query-Optimierung
- **Development Server läuft** - System funktionsfähig
### **✅ Code-Qualität**
- **Type Hints hinzugefügt** - Bessere Dokumentation
- **Error Handling verbessert** - Robuste Exception-Behandlung
- **Logging konfiguriert** - Umfassende Debugging-Möglichkeiten
- **Dependencies bereinigt** - Saubere Abhängigkeiten
### **✅ Deployment**
- **SQLite Setup** - Funktionierende Entwicklungsumgebung
- **Static Files** - 184 Dateien erfolgreich gesammelt
- **Superuser erstellt** - Admin-Account verfügbar
- **Server läuft** - System testbar
---
*🎨 Kasico Art & Design - Wo Fursuits zum Leben erwachen! 🐾*
**Letzte Aktualisierung:** 2025-01-27
**Nächste Review:** 2025-02-03
**Status:** ✅ Phase 1 erfolgreich abgeschlossen

View File

@ -19,24 +19,6 @@ from shop.models import (
Category, ProductType, ProductImage, ProductVariant,
CustomDesign, PayPalPayment, PaymentError, Cart, CartItem
)
<<<<<<< HEAD
# Temporär deaktiviert
# from chat.models import (
# ChatRoom, ChatMessage, UserOnlineStatus, QuickResponse, ChatAnalytics
# )
# from auction.models import (
# Auction, Bid, AuctionWatch, AuctionAnalytics
# )
# from recommendations.models import (
# UserBehavior, UserProfile as RecUserProfile, ProductSimilarity,
# Recommendation, RecommendationModel, ABTest, RecommendationAnalytics
# )
# from mobile.models import (
# MobileDevice, PushNotification, OfflineSync, MobileAnalytics,
# MobileCache, MobileSession
# )
# from paypal_integration.models import PayPalConfig
=======
from chat.models import (
ChatRoom, ChatMessage, UserOnlineStatus, QuickResponse, ChatAnalytics
)
@ -46,7 +28,6 @@ from recommendations.models import (
)
from mobile.models import MobileDevice, MobileSession
from paypal_integration.models import PayPalConfig
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# Custom Admin Site
@ -279,119 +260,6 @@ class ContactMessageAdmin(admin.ModelAdmin):
mark_as_resolved.short_description = "Als erledigt markieren"
<<<<<<< HEAD
# =============================================================================
# CHAT & COMMUNICATION (Temporär deaktiviert)
# =============================================================================
# @admin.register(ChatRoom, site=admin_site)
# class ChatRoomAdmin(admin.ModelAdmin):
# list_display = ('id', 'customer', 'admin', 'status', 'subject', 'last_message_at', 'message_count')
# list_filter = ('status', 'created_at')
# search_fields = ('customer__username', 'admin__username', 'subject')
# ordering = ('-last_message_at',)
# readonly_fields = ('id', 'created_at', 'last_message_at')
# def message_count(self, obj):
# return obj.messages.count()
# message_count.short_description = 'Nachrichten'
# @admin.register(ChatMessage, site=admin_site)
# class ChatMessageAdmin(admin.ModelAdmin):
# list_display = ('room', 'sender', 'message_type', 'content_preview', 'created_at')
# list_filter = ('message_type', 'created_at')
# search_fields = ('content', 'sender__username')
# ordering = ('-created_at',)
# readonly_fields = ('created_at',)
# def content_preview(self, obj):
# return obj.content[:50] + "..." if len(obj.content) > 50 else obj.content
# content_preview.short_description = 'Inhalt'
# @admin.register(QuickResponse, site=admin_site)
# class QuickResponseAdmin(admin.ModelAdmin):
# list_display = ('title', 'category', 'is_active', 'use_count', 'created_by')
# list_filter = ('category', 'is_active', 'created_at')
# search_fields = ('title', 'content')
# ordering = ('category', 'title')
# =============================================================================
# ANALYTICS & ML (Temporär deaktiviert)
# =============================================================================
# @admin.register(UserBehavior, site=admin_site)
# class UserBehaviorAdmin(admin.ModelAdmin):
# list_display = ('user', 'behavior_type', 'product', 'created_at')
# list_filter = ('behavior_type', 'created_at')
# search_fields = ('user__username', 'product__name')
# ordering = ('-created_at',)
# readonly_fields = ('created_at',)
# @admin.register(Recommendation, site=admin_site)
# class RecommendationAdmin(admin.ModelAdmin):
# list_display = ('user', 'product', 'recommendation_type', 'confidence_score', 'is_clicked', 'is_purchased')
# list_filter = ('recommendation_type', 'is_clicked', 'is_purchased', 'created_at')
# search_fields = ('user__username', 'product__name')
# ordering = ('-created_at',)
# readonly_fields = ('created_at',)
# @admin.register(RecommendationModel, site=admin_site)
# class RecommendationModelAdmin(admin.ModelAdmin):
# list_display = ('model_name', 'model_type', 'model_version', 'accuracy_score', 'is_active', 'trained_at')
# list_filter = ('model_type', 'is_active', 'trained_at')
# search_fields = ('model_name',)
# readonly_fields = ('created_at', 'trained_at')
# fieldsets = (
# ('Model Info', {
# 'fields': ('model_type', 'model_name', 'model_version', 'model_file')
# }),
# ('Performance', {
# 'fields': ('training_data_size', 'accuracy_score', 'precision_score', 'recall_score', 'f1_score')
# }),
# ('Status', {
# 'fields': ('is_active', 'created_at', 'trained_at')
# }),
# )
# =============================================================================
# MOBILE & API (Temporär deaktiviert)
# =============================================================================
# @admin.register(MobileDevice, site=admin_site)
# class MobileDeviceAdmin(admin.ModelAdmin):
# list_display = ('user', 'device_type', 'device_name', 'is_active', 'last_seen')
# list_filter = ('device_type', 'is_active', 'created_at')
# search_fields = ('user__username', 'device_name', 'device_token')
# ordering = ('-created_at',)
# readonly_fields = ('created_at', 'last_seen')
# =============================================================================
# PAYMENT & INTEGRATION (Temporär deaktiviert)
# =============================================================================
# @admin.register(PayPalConfig, site=admin_site)
# class PayPalConfigAdmin(admin.ModelAdmin):
# list_display = ('get_mode', 'client_id', 'is_sandbox', 'updated_at')
# readonly_fields = ('created_at', 'updated_at')
# fieldsets = (
# ('API Konfiguration', {
# 'fields': ('client_id', 'client_secret', 'is_sandbox')
# }),
# ('Zeitstempel', {
# 'fields': ('created_at', 'updated_at'),
# 'classes': ('collapse',)
# }),
# )
=======
@admin.register(ChatRoom, site=admin_site)
class ChatRoomAdmin(admin.ModelAdmin):
list_display = ('id', 'customer', 'admin', 'status', 'subject', 'last_message_at', 'message_count')
@ -499,7 +367,6 @@ class PayPalConfigAdmin(admin.ModelAdmin):
'classes': ('collapse',)
}),
)
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
# =============================================================================
@ -590,17 +457,6 @@ admin_site.register(ProductType)
admin_site.register(ProductImage)
admin_site.register(ProductVariant)
admin_site.register(CustomDesign)
<<<<<<< HEAD
# Temporär deaktiviert
# admin_site.register(PayPalPayment)
# admin_site.register(PaymentError)
# admin_site.register(UserOnlineStatus)
# admin_site.register(ChatAnalytics)
# admin_site.register(ProductSimilarity)
# admin_site.register(ABTest)
# admin_site.register(RecommendationAnalytics)
# admin_site.register(MobileSession)
=======
admin_site.register(PayPalPayment)
admin_site.register(PaymentError)
admin_site.register(UserOnlineStatus)
@ -608,5 +464,4 @@ admin_site.register(ChatAnalytics)
admin_site.register(ProductSimilarity)
admin_site.register(ABTest)
admin_site.register(RecommendationAnalytics)
admin_site.register(MobileSession)
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
admin_site.register(MobileSession)

View File

@ -1,55 +1,26 @@
<<<<<<< HEAD
"""
ASGI config for webshop project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webshop.settings')
# ASGI Application
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
),
})
=======
"""
ASGI config for webshop project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webshop.settings')
# ASGI Application
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
),
})
>>>>>>> 5b9b867963eca600ed64b617dc2dc86c30dbd9cb
"""
ASGI config for webshop project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webshop.settings')
# ASGI Application
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
),
})

Some files were not shown because too many files have changed in this diff Show More