Some checks failed
- Add redis-exporter container to docker-compose (oliver006/redis_exporter, 32MB) - Add Redis scrape target to Prometheus config - Add 4 Redis alert rules: RedisDown, HighMemory, HighConnections, RejectedConnections - Document Step 19b (Sentry Error Tracking) in Hetzner deployment guide - Document Step 19c (Redis Monitoring) in Hetzner deployment guide - Update resource budget and port reference tables Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
361 lines
8.9 KiB
Markdown
361 lines
8.9 KiB
Markdown
# THEME PRESETS USAGE GUIDE
|
|
|
|
## What Changed in Your Presets
|
|
|
|
### ✅ What You Had Right
|
|
- Good preset structure with colors, fonts, layout
|
|
- Clean `apply_preset()` function
|
|
- Good preset names (modern, classic, minimal, vibrant)
|
|
|
|
### 🔧 What We Added
|
|
1. **Missing color fields:** `background`, `text`, `border`
|
|
2. **Missing layout field:** `product_card` style
|
|
3. **"default" preset:** Your platform's default theme
|
|
4. **Extra presets:** "elegant" and "nature" themes
|
|
5. **Helper functions:** `get_preset()`, `get_available_presets()`, `get_preset_preview()`
|
|
6. **Custom preset builder:** `create_custom_preset()`
|
|
|
|
---
|
|
|
|
## Usage Examples
|
|
|
|
### 1. Apply Preset to New Store
|
|
|
|
```python
|
|
from models.database.store_theme import StoreTheme
|
|
from app.core.theme_presets import apply_preset
|
|
from app.core.database import SessionLocal
|
|
|
|
# Create theme for store
|
|
db = SessionLocal()
|
|
store_id = 1
|
|
|
|
# Create and apply preset
|
|
theme = StoreTheme(store_id=store_id)
|
|
apply_preset(theme, "modern")
|
|
|
|
db.add(theme)
|
|
db.commit()
|
|
```
|
|
|
|
### 2. Change Store's Theme
|
|
|
|
```python
|
|
from models.database.store_theme import StoreTheme
|
|
from app.core.theme_presets import apply_preset
|
|
|
|
# Get existing theme
|
|
theme = db.query(StoreTheme).filter(
|
|
StoreTheme.store_id == store_id
|
|
).first()
|
|
|
|
if theme:
|
|
# Update to new preset
|
|
apply_preset(theme, "classic")
|
|
else:
|
|
# Create new theme
|
|
theme = StoreTheme(store_id=store_id)
|
|
apply_preset(theme, "classic")
|
|
db.add(theme)
|
|
|
|
db.commit()
|
|
```
|
|
|
|
### 3. Get Available Presets (For UI Dropdown)
|
|
|
|
```python
|
|
from app.core.theme_presets import get_available_presets, get_preset_preview
|
|
|
|
# Get list of preset names
|
|
presets = get_available_presets()
|
|
# Returns: ['default', 'modern', 'classic', 'minimal', 'vibrant', 'elegant', 'nature']
|
|
|
|
# Get preview info for UI
|
|
previews = []
|
|
for preset_name in presets:
|
|
preview = get_preset_preview(preset_name)
|
|
previews.append(preview)
|
|
|
|
# Returns list of dicts with:
|
|
# {
|
|
# "name": "modern",
|
|
# "description": "Contemporary tech-inspired design...",
|
|
# "primary_color": "#6366f1",
|
|
# "secondary_color": "#8b5cf6",
|
|
# ...
|
|
# }
|
|
```
|
|
|
|
### 4. API Endpoint to Apply Preset
|
|
|
|
```python
|
|
# In your API route
|
|
from fastapi import APIRouter, Depends
|
|
from app.core.theme_presets import apply_preset, get_available_presets
|
|
|
|
@router.put("/theme/preset")
|
|
def apply_theme_preset(
|
|
preset_name: str,
|
|
store: Store = Depends(require_store_context()),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""Apply a theme preset to store."""
|
|
|
|
# Validate preset name
|
|
if preset_name not in get_available_presets():
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Invalid preset. Available: {get_available_presets()}"
|
|
)
|
|
|
|
# Get or create store theme
|
|
theme = db.query(StoreTheme).filter(
|
|
StoreTheme.store_id == store.id
|
|
).first()
|
|
|
|
if not theme:
|
|
theme = StoreTheme(store_id=store.id)
|
|
db.add(theme)
|
|
|
|
# Apply preset
|
|
apply_preset(theme, preset_name)
|
|
db.commit()
|
|
db.refresh(theme)
|
|
|
|
return {
|
|
"message": f"Theme preset '{preset_name}' applied successfully",
|
|
"theme": theme.to_dict()
|
|
}
|
|
```
|
|
|
|
### 5. Get All Presets for Theme Selector
|
|
|
|
```python
|
|
@router.get("/theme/presets")
|
|
def get_theme_presets():
|
|
"""Get all available theme presets with previews."""
|
|
from app.core.theme_presets import get_available_presets, get_preset_preview
|
|
|
|
presets = []
|
|
for preset_name in get_available_presets():
|
|
preview = get_preset_preview(preset_name)
|
|
presets.append(preview)
|
|
|
|
return {"presets": presets}
|
|
|
|
# Returns:
|
|
# {
|
|
# "presets": [
|
|
# {
|
|
# "name": "default",
|
|
# "description": "Clean and professional...",
|
|
# "primary_color": "#6366f1",
|
|
# "secondary_color": "#8b5cf6",
|
|
# "accent_color": "#ec4899",
|
|
# "heading_font": "Inter, sans-serif",
|
|
# "body_font": "Inter, sans-serif",
|
|
# "layout_style": "grid"
|
|
# },
|
|
# ...
|
|
# ]
|
|
# }
|
|
```
|
|
|
|
### 6. Create Custom Theme (Not from Preset)
|
|
|
|
```python
|
|
from app.core.theme_presets import create_custom_preset
|
|
|
|
# User provides custom colors
|
|
custom_preset = create_custom_preset(
|
|
colors={
|
|
"primary": "#ff0000",
|
|
"secondary": "#00ff00",
|
|
"accent": "#0000ff",
|
|
"background": "#ffffff",
|
|
"text": "#000000",
|
|
"border": "#cccccc"
|
|
},
|
|
fonts={
|
|
"heading": "Arial, sans-serif",
|
|
"body": "Verdana, sans-serif"
|
|
},
|
|
layout={
|
|
"style": "grid",
|
|
"header": "fixed",
|
|
"product_card": "modern"
|
|
},
|
|
name="my_custom"
|
|
)
|
|
|
|
# Apply to store theme
|
|
theme = StoreTheme(store_id=store_id)
|
|
theme.theme_name = "custom"
|
|
theme.colors = custom_preset["colors"]
|
|
theme.font_family_heading = custom_preset["fonts"]["heading"]
|
|
theme.font_family_body = custom_preset["fonts"]["body"]
|
|
theme.layout_style = custom_preset["layout"]["style"]
|
|
theme.header_style = custom_preset["layout"]["header"]
|
|
theme.product_card_style = custom_preset["layout"]["product_card"]
|
|
theme.is_active = True
|
|
|
|
db.add(theme)
|
|
db.commit()
|
|
```
|
|
|
|
---
|
|
|
|
## Available Presets
|
|
|
|
| Preset | Description | Primary Color | Use Case |
|
|
|--------|-------------|---------------|----------|
|
|
| `default` | Clean & professional | Indigo (#6366f1) | General purpose |
|
|
| `modern` | Tech-inspired | Indigo (#6366f1) | Tech products |
|
|
| `classic` | Traditional | Dark Blue (#1e40af) | Established brands |
|
|
| `minimal` | Ultra-clean B&W | Black (#000000) | Minimalist brands |
|
|
| `vibrant` | Bold & energetic | Orange (#f59e0b) | Creative brands |
|
|
| `elegant` | Sophisticated | Gray (#6b7280) | Luxury products |
|
|
| `nature` | Eco-friendly | Green (#059669) | Organic/eco brands |
|
|
|
|
---
|
|
|
|
## Complete Preset Structure
|
|
|
|
Each preset includes:
|
|
|
|
```python
|
|
{
|
|
"colors": {
|
|
"primary": "#6366f1", # Main brand color
|
|
"secondary": "#8b5cf6", # Supporting color
|
|
"accent": "#ec4899", # Call-to-action color
|
|
"background": "#ffffff", # Page background
|
|
"text": "#1f2937", # Text color
|
|
"border": "#e5e7eb" # Border/divider color
|
|
},
|
|
"fonts": {
|
|
"heading": "Inter, sans-serif", # Headings (h1-h6)
|
|
"body": "Inter, sans-serif" # Body text
|
|
},
|
|
"layout": {
|
|
"style": "grid", # grid | list | masonry
|
|
"header": "fixed", # fixed | static | transparent
|
|
"product_card": "modern" # modern | classic | minimal
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Integration with Admin Panel
|
|
|
|
### Theme Editor UI Flow
|
|
|
|
1. **Preset Selector**
|
|
```javascript
|
|
// Fetch available presets
|
|
fetch('/api/v1/store/theme/presets')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
// Display preset cards with previews
|
|
data.presets.forEach(preset => {
|
|
showPresetCard(preset.name, preset.primary_color, preset.description)
|
|
})
|
|
})
|
|
```
|
|
|
|
2. **Apply Preset Button**
|
|
```javascript
|
|
function applyPreset(presetName) {
|
|
fetch('/api/v1/store/theme/preset', {
|
|
method: 'PUT',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({preset_name: presetName})
|
|
})
|
|
.then(() => {
|
|
alert('Theme updated!')
|
|
location.reload() // Refresh to show new theme
|
|
})
|
|
}
|
|
```
|
|
|
|
3. **Custom Color Picker** (After applying preset)
|
|
```javascript
|
|
// User can then customize colors
|
|
function updateColors(colors) {
|
|
fetch('/api/v1/store/theme/colors', {
|
|
method: 'PUT',
|
|
body: JSON.stringify({colors})
|
|
})
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Presets
|
|
|
|
```python
|
|
# Test script
|
|
from app.core.theme_presets import apply_preset, get_available_presets
|
|
from models.database.store_theme import StoreTheme
|
|
|
|
def test_all_presets():
|
|
"""Test applying all presets"""
|
|
presets = get_available_presets()
|
|
|
|
for preset_name in presets:
|
|
theme = StoreTheme(store_id=999) # Test store
|
|
apply_preset(theme, preset_name)
|
|
|
|
assert theme.theme_name == preset_name
|
|
assert theme.colors is not None
|
|
assert theme.font_family_heading is not None
|
|
assert theme.is_active == True
|
|
|
|
print(f"✅ {preset_name} preset OK")
|
|
|
|
test_all_presets()
|
|
```
|
|
|
|
---
|
|
|
|
## CSS Variables Generation
|
|
|
|
Your middleware already handles this via `StoreTheme.to_dict()`, which includes:
|
|
|
|
```python
|
|
"css_variables": {
|
|
"--color-primary": "#6366f1",
|
|
"--color-secondary": "#8b5cf6",
|
|
"--color-accent": "#ec4899",
|
|
"--color-background": "#ffffff",
|
|
"--color-text": "#1f2937",
|
|
"--color-border": "#e5e7eb",
|
|
"--font-heading": "Inter, sans-serif",
|
|
"--font-body": "Inter, sans-serif",
|
|
}
|
|
```
|
|
|
|
Use in templates:
|
|
```html
|
|
<style>
|
|
:root {
|
|
{% for key, value in theme.css_variables.items() %}
|
|
{{ key }}: {{ value }};
|
|
{% endfor %}
|
|
}
|
|
</style>
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. ✅ Copy `theme_presets.py` to `app/core/theme_presets.py`
|
|
2. ✅ Create API endpoints for applying presets
|
|
3. ✅ Build theme selector UI in admin panel
|
|
4. ✅ Test all presets work correctly
|
|
5. ✅ Add custom color picker for fine-tuning
|
|
|
|
Perfect! Your presets are now complete and production-ready! 🎨
|