- Replace black, isort, and flake8 with Ruff (all-in-one linter and formatter) - Add comprehensive pyproject.toml configuration - Simplify Makefile code quality targets - Configure exclusions for venv/.venv in pyproject.toml - Auto-fix 1,359 linting issues across codebase Benefits: - Much faster builds (Ruff is written in Rust) - Single tool replaces multiple tools - More comprehensive rule set (UP, B, C4, SIM, PIE, RET, Q) - All configuration centralized in pyproject.toml - Better import sorting and formatting consistency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
137 lines
4.6 KiB
Python
137 lines
4.6 KiB
Python
# models/database/vendor_theme.py
|
|
"""
|
|
Vendor Theme Configuration Model
|
|
Allows each vendor to customize their shop's appearance
|
|
"""
|
|
|
|
from sqlalchemy import JSON, Boolean, Column, ForeignKey, Integer, String, Text
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from app.core.database import Base
|
|
from models.database.base import TimestampMixin
|
|
|
|
|
|
class VendorTheme(Base, TimestampMixin):
|
|
"""
|
|
Stores theme configuration for each vendor's shop.
|
|
|
|
Each vendor can have ONE active theme:
|
|
- Custom colors (primary, secondary, accent)
|
|
- Custom fonts
|
|
- Custom logo and favicon
|
|
- Custom CSS overrides
|
|
- Layout preferences
|
|
|
|
Theme presets available: default, modern, classic, minimal, vibrant
|
|
"""
|
|
|
|
__tablename__ = "vendor_themes"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
vendor_id = Column(
|
|
Integer,
|
|
ForeignKey("vendors.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
unique=True, # ONE vendor = ONE theme
|
|
)
|
|
|
|
# Basic Theme Settings
|
|
theme_name = Column(
|
|
String(100), default="default"
|
|
) # default, modern, classic, minimal, vibrant
|
|
is_active = Column(Boolean, default=True)
|
|
|
|
# Color Scheme (JSON for flexibility)
|
|
colors = Column(
|
|
JSON,
|
|
default={
|
|
"primary": "#6366f1", # Indigo
|
|
"secondary": "#8b5cf6", # Purple
|
|
"accent": "#ec4899", # Pink
|
|
"background": "#ffffff", # White
|
|
"text": "#1f2937", # Gray-800
|
|
"border": "#e5e7eb", # Gray-200
|
|
},
|
|
)
|
|
|
|
# Typography
|
|
font_family_heading = Column(String(100), default="Inter, sans-serif")
|
|
font_family_body = Column(String(100), default="Inter, sans-serif")
|
|
|
|
# Branding Assets
|
|
logo_url = Column(String(500), nullable=True) # Path to vendor logo
|
|
logo_dark_url = Column(String(500), nullable=True) # Dark mode logo
|
|
favicon_url = Column(String(500), nullable=True) # Favicon
|
|
banner_url = Column(String(500), nullable=True) # Homepage banner
|
|
|
|
# Layout Preferences
|
|
layout_style = Column(String(50), default="grid") # grid, list, masonry
|
|
header_style = Column(String(50), default="fixed") # fixed, static, transparent
|
|
product_card_style = Column(
|
|
String(50), default="modern"
|
|
) # modern, classic, minimal
|
|
|
|
# Custom CSS (for advanced customization)
|
|
custom_css = Column(Text, nullable=True)
|
|
|
|
# Social Media Links
|
|
social_links = Column(JSON, default={}) # {facebook: "url", instagram: "url", etc.}
|
|
|
|
# SEO & Meta
|
|
meta_title_template = Column(
|
|
String(200), nullable=True
|
|
) # e.g., "{product_name} - {shop_name}"
|
|
meta_description = Column(Text, nullable=True)
|
|
|
|
# Relationships - FIXED: back_populates must match the relationship name in Vendor model
|
|
vendor = relationship("Vendor", back_populates="vendor_theme")
|
|
|
|
def __repr__(self):
|
|
return (
|
|
f"<VendorTheme(vendor_id={self.vendor_id}, theme_name='{self.theme_name}')>"
|
|
)
|
|
|
|
@property
|
|
def primary_color(self):
|
|
"""Get primary color from JSON"""
|
|
return self.colors.get("primary", "#6366f1")
|
|
|
|
@property
|
|
def css_variables(self):
|
|
"""Generate CSS custom properties from theme config"""
|
|
return {
|
|
"--color-primary": self.colors.get("primary", "#6366f1"),
|
|
"--color-secondary": self.colors.get("secondary", "#8b5cf6"),
|
|
"--color-accent": self.colors.get("accent", "#ec4899"),
|
|
"--color-background": self.colors.get("background", "#ffffff"),
|
|
"--color-text": self.colors.get("text", "#1f2937"),
|
|
"--color-border": self.colors.get("border", "#e5e7eb"),
|
|
"--font-heading": self.font_family_heading,
|
|
"--font-body": self.font_family_body,
|
|
}
|
|
|
|
def to_dict(self):
|
|
"""Convert theme to dictionary for template rendering"""
|
|
return {
|
|
"theme_name": self.theme_name,
|
|
"colors": self.colors,
|
|
"fonts": {
|
|
"heading": self.font_family_heading,
|
|
"body": self.font_family_body,
|
|
},
|
|
"branding": {
|
|
"logo": self.logo_url,
|
|
"logo_dark": self.logo_dark_url,
|
|
"favicon": self.favicon_url,
|
|
"banner": self.banner_url,
|
|
},
|
|
"layout": {
|
|
"style": self.layout_style,
|
|
"header": self.header_style,
|
|
"product_card": self.product_card_style,
|
|
},
|
|
"social_links": self.social_links,
|
|
"custom_css": self.custom_css,
|
|
"css_variables": self.css_variables,
|
|
}
|