Compare commits
No commits in common. "1c92c56e822cef2c98c23b8c0921d2e969ac7d95" and "5b9b867963eca600ed64b617dc2dc86c30dbd9cb" have entirely different histories.
1c92c56e82
...
5b9b867963
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
93
init_data.py
93
init_data.py
|
|
@ -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()
|
||||
129
init_db.py
129
init_db.py
|
|
@ -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!")
|
||||
69
manage.py
69
manage.py
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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.'
|
||||
}
|
||||
|
|
@ -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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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')},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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')},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
),
|
||||
]
|
||||
|
|
|
|||
1263
products/models.py
1263
products/models.py
File diff suppressed because it is too large
Load Diff
|
|
@ -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']
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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 %}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
45
shop/apps.py
45
shop/apps.py
|
|
@ -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
|
||||
|
|
|
|||
51
shop/asgi.py
51
shop/asgi.py
|
|
@ -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()
|
||||
|
|
|
|||
618
shop/emails.py
618
shop/emails.py
|
|
@ -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()
|
||||
|
|
@ -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.')
|
||||
}
|
||||
)
|
||||
|
|
@ -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!'))
|
||||
|
|
@ -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'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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')},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
1098
shop/models.py
1098
shop/models.py
File diff suppressed because it is too large
Load Diff
432
shop/settings.py
432
shop/settings.py
|
|
@ -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
|
||||
# }
|
||||
|
|
|
|||
207
shop/signals.py
207
shop/signals.py
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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>
|
||||
|
|
@ -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 }}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 }}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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." %}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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." %}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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
|
|
@ -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 %}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
111
shop/urls.py
111
shop/urls.py
|
|
@ -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)
|
||||
|
|
|
|||
141
shop/views.py
141
shop/views.py
|
|
@ -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})
|
||||
51
shop/wsgi.py
51
shop/wsgi.py
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 & 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 & 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 & DESIGN
|
||||
</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 2.3 KiB |
1014
templates/base.html
1014
templates/base.html
File diff suppressed because it is too large
Load Diff
|
|
@ -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 %}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
147
webshop/admin.py
147
webshop/admin.py
|
|
@ -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)
|
||||
|
|
@ -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
Loading…
Reference in New Issue