diff --git a/.env b/.env
index 708119f1..4b92403e 100644
--- a/.env
+++ b/.env
@@ -6,9 +6,9 @@ DESCRIPTION=Advanced product management system with JWT authentication
VERSION=0.0.1
# Database Configuration
-# DATABASE_URL=postgresql://username:password@localhost:5432/ecommerce_db
+# DATABASE_URL=postgresql://username:password@localhost:5432/wizamart_db
# For development, you can use SQLite:
-DATABASE_URL=sqlite:///./ecommerce.db
+DATABASE_URL=sqlite:///./wizamart.db
# Documentation
# .env.development
@@ -49,5 +49,5 @@ SSL_PROVIDER=letsencrypt # or "cloudflare", "manual"
AUTO_PROVISION_SSL=False # Set to True if using automated SSL
# DNS verification
-DNS_VERIFICATION_PREFIX=_letzshop-verify
+DNS_VERIFICATION_PREFIX=_wizamart-verify
DNS_VERIFICATION_TTL=3600
\ No newline at end of file
diff --git a/.env.example b/.env.example
index 708119f1..4b92403e 100644
--- a/.env.example
+++ b/.env.example
@@ -6,9 +6,9 @@ DESCRIPTION=Advanced product management system with JWT authentication
VERSION=0.0.1
# Database Configuration
-# DATABASE_URL=postgresql://username:password@localhost:5432/ecommerce_db
+# DATABASE_URL=postgresql://username:password@localhost:5432/wizamart_db
# For development, you can use SQLite:
-DATABASE_URL=sqlite:///./ecommerce.db
+DATABASE_URL=sqlite:///./wizamart.db
# Documentation
# .env.development
@@ -49,5 +49,5 @@ SSL_PROVIDER=letsencrypt # or "cloudflare", "manual"
AUTO_PROVISION_SSL=False # Set to True if using automated SSL
# DNS verification
-DNS_VERIFICATION_PREFIX=_letzshop-verify
+DNS_VERIFICATION_PREFIX=_wizamart-verify
DNS_VERIFICATION_TTL=3600
\ No newline at end of file
diff --git a/alembic/env.py b/alembic/env.py
index 99cc7b75..2e1dbbe2 100644
--- a/alembic/env.py
+++ b/alembic/env.py
@@ -1,4 +1,15 @@
# alembic/env.py
+"""
+Alembic migration environment configuration.
+
+This file is responsible for:
+1. Importing ALL database models so Alembic can detect schema changes
+2. Configuring the database connection
+3. Running migrations in online/offline mode
+
+CRITICAL: Every model in models/database/__init__.py must be imported here!
+"""
+
import os
import sys
from logging.config import fileConfig
@@ -12,93 +23,160 @@ sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from app.core.config import settings
from models.database.base import Base
-# === IMPORTANT: Import all your DATABASE models here ===
-# Only import SQLAlchemy models, not Pydantic API models
-print("[ALEMBIC] Importing database models...")
+# ============================================================================
+# IMPORT ALL DATABASE MODELS
+# ============================================================================
+# CRITICAL: Every model must be imported here so Alembic can detect tables!
+# If a model is not imported, Alembic will not create/update its table.
+#
+# Models list must match: models/database/__init__.py
+# ============================================================================
-# Core models
+print("[ALEMBIC] Importing database models...")
+print("=" * 70)
+
+# ----------------------------------------------------------------------------
+# ADMIN MODELS
+# ----------------------------------------------------------------------------
+try:
+ from models.database.admin import (
+ AdminAuditLog,
+ AdminNotification,
+ AdminSetting,
+ PlatformAlert,
+ AdminSession
+ )
+
+ print(" ✓ Admin models imported (5 models)")
+ print(" - AdminAuditLog")
+ print(" - AdminNotification")
+ print(" - AdminSetting")
+ print(" - PlatformAlert")
+ print(" - AdminSession")
+except ImportError as e:
+ print(f" ✗ Admin models failed: {e}")
+
+# ----------------------------------------------------------------------------
+# USER MODEL
+# ----------------------------------------------------------------------------
try:
from models.database.user import User
+
print(" ✓ User model imported")
except ImportError as e:
print(f" ✗ User model failed: {e}")
+# ----------------------------------------------------------------------------
+# VENDOR MODELS
+# ----------------------------------------------------------------------------
try:
from models.database.vendor import Vendor, VendorUser, Role
- print(" ✓ Vendor models imported (Vendor, VendorUser, Role)")
+
+ print(" ✓ Vendor models imported (3 models)")
+ print(" - Vendor")
+ print(" - VendorUser")
+ print(" - Role")
except ImportError as e:
print(f" ✗ Vendor models failed: {e}")
try:
from models.database.vendor_domain import VendorDomain
+
print(" ✓ VendorDomain model imported")
except ImportError as e:
print(f" ✗ VendorDomain model failed: {e}")
try:
from models.database.vendor_theme import VendorTheme
+
print(" ✓ VendorTheme model imported")
except ImportError as e:
print(f" ✗ VendorTheme model failed: {e}")
-# Product models
-try:
- from models.database.marketplace_product import MarketplaceProduct
- print(" ✓ MarketplaceProduct model imported")
-except ImportError as e:
- print(f" ✗ MarketplaceProduct model failed: {e}")
-
+# ----------------------------------------------------------------------------
+# PRODUCT MODELS
+# ----------------------------------------------------------------------------
try:
from models.database.product import Product
+
print(" ✓ Product model imported")
except ImportError as e:
print(f" ✗ Product model failed: {e}")
-# Inventory
+try:
+ from models.database.marketplace_product import MarketplaceProduct
+
+ print(" ✓ MarketplaceProduct model imported")
+except ImportError as e:
+ print(f" ✗ MarketplaceProduct model failed: {e}")
+
+# ----------------------------------------------------------------------------
+# INVENTORY MODEL
+# ----------------------------------------------------------------------------
try:
from models.database.inventory import Inventory
+
print(" ✓ Inventory model imported")
except ImportError as e:
print(f" ✗ Inventory model failed: {e}")
-# Marketplace imports
+# ----------------------------------------------------------------------------
+# MARKETPLACE IMPORT
+# ----------------------------------------------------------------------------
try:
from models.database.marketplace_import_job import MarketplaceImportJob
+
print(" ✓ MarketplaceImportJob model imported")
except ImportError as e:
print(f" ✗ MarketplaceImportJob model failed: {e}")
-# Customer models
+# ----------------------------------------------------------------------------
+# CUSTOMER MODELS
+# ----------------------------------------------------------------------------
try:
from models.database.customer import Customer, CustomerAddress
- print(" ✓ Customer models imported (Customer, CustomerAddress)")
+
+ print(" ✓ Customer models imported (2 models)")
+ print(" - Customer")
+ print(" - CustomerAddress")
except ImportError as e:
print(f" ✗ Customer models failed: {e}")
-# Order models
+# ----------------------------------------------------------------------------
+# ORDER MODELS
+# ----------------------------------------------------------------------------
try:
from models.database.order import Order, OrderItem
- print(" ✓ Order models imported (Order, OrderItem)")
+
+ print(" ✓ Order models imported (2 models)")
+ print(" - Order")
+ print(" - OrderItem")
except ImportError as e:
print(f" ✗ Order models failed: {e}")
-# Payment models (if you have them)
-try:
- from models.database.payment import Payment
- print(" ✓ Payment model imported")
-except ImportError as e:
- print(f" - Payment model not found (optional)")
+# ============================================================================
+# SUMMARY
+# ============================================================================
+print("=" * 70)
+print(f"[ALEMBIC] Model import completed")
+print(f"[ALEMBIC] Tables detected in metadata:")
+print("=" * 70)
-# Shipping models (if you have them)
-try:
- from models.database.shipping import ShippingAddress
- print(" ✓ Shipping model imported")
-except ImportError as e:
- print(f" - Shipping model not found (optional)")
+if Base.metadata.tables:
+ for i, table_name in enumerate(sorted(Base.metadata.tables.keys()), 1):
+ print(f" {i:2d}. {table_name}")
+ print("=" * 70)
+ print(f"[ALEMBIC] Total tables: {len(Base.metadata.tables)}")
+else:
+ print(" ⚠️ WARNING: No tables found in metadata!")
+ print(" This usually means models are not being imported correctly.")
-print(f"\n[ALEMBIC] Model import completed.")
-print(f"[ALEMBIC] Tables found: {list(Base.metadata.tables.keys())}")
-print(f"[ALEMBIC] Total tables to create: {len(Base.metadata.tables)}\n")
+print("=" * 70)
+print()
+
+# ============================================================================
+# ALEMBIC CONFIGURATION
+# ============================================================================
# Alembic Config object
config = context.config
@@ -109,11 +187,20 @@ config.set_main_option("sqlalchemy.url", settings.database_url)
if config.config_file_name is not None:
fileConfig(config.config_file_name)
+# Set target metadata from Base
target_metadata = Base.metadata
def run_migrations_offline() -> None:
- """Run migrations in 'offline' mode."""
+ """
+ Run migrations in 'offline' mode.
+
+ This configures the context with just a URL and not an Engine,
+ though an Engine is acceptable here as well. By skipping the Engine
+ creation we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the script output.
+ """
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
@@ -127,7 +214,12 @@ def run_migrations_offline() -> None:
def run_migrations_online() -> None:
- """Run migrations in 'online' mode."""
+ """
+ Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine and associate a connection
+ with the context.
+ """
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
@@ -135,12 +227,19 @@ def run_migrations_online() -> None:
)
with connectable.connect() as connection:
- context.configure(connection=connection, target_metadata=target_metadata)
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata
+ )
with context.begin_transaction():
context.run_migrations()
+# ============================================================================
+# MAIN EXECUTION
+# ============================================================================
+
if context.is_offline_mode():
run_migrations_offline()
else:
diff --git a/alembic/versions/6fe45d3d84c4_initial_schema_with_proper_relationships.py b/alembic/versions/4951b2e50581_initial_migration_all_tables.py
similarity index 64%
rename from alembic/versions/6fe45d3d84c4_initial_schema_with_proper_relationships.py
rename to alembic/versions/4951b2e50581_initial_migration_all_tables.py
index b7466a41..36b6fe27 100644
--- a/alembic/versions/6fe45d3d84c4_initial_schema_with_proper_relationships.py
+++ b/alembic/versions/4951b2e50581_initial_migration_all_tables.py
@@ -1,8 +1,8 @@
-"""initial_schema_with_proper_relationships
+"""Initial migration - all tables
-Revision ID: 6fe45d3d84c4
+Revision ID: 4951b2e50581
Revises:
-Create Date: 2025-10-07 22:11:56.036486
+Create Date: 2025-10-27 22:28:33.137564
"""
from typing import Sequence, Union
@@ -12,7 +12,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision: str = '6fe45d3d84c4'
+revision: str = '4951b2e50581'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
@@ -92,6 +92,112 @@ def upgrade() -> None:
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True)
+ op.create_table('admin_audit_logs',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('admin_user_id', sa.Integer(), nullable=False),
+ sa.Column('action', sa.String(length=100), nullable=False),
+ sa.Column('target_type', sa.String(length=50), nullable=False),
+ sa.Column('target_id', sa.String(length=100), nullable=False),
+ sa.Column('details', sa.JSON(), nullable=True),
+ sa.Column('ip_address', sa.String(length=45), nullable=True),
+ sa.Column('user_agent', sa.Text(), nullable=True),
+ sa.Column('request_id', sa.String(length=100), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['admin_user_id'], ['users.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_admin_audit_logs_action'), 'admin_audit_logs', ['action'], unique=False)
+ op.create_index(op.f('ix_admin_audit_logs_admin_user_id'), 'admin_audit_logs', ['admin_user_id'], unique=False)
+ op.create_index(op.f('ix_admin_audit_logs_id'), 'admin_audit_logs', ['id'], unique=False)
+ op.create_index(op.f('ix_admin_audit_logs_target_id'), 'admin_audit_logs', ['target_id'], unique=False)
+ op.create_index(op.f('ix_admin_audit_logs_target_type'), 'admin_audit_logs', ['target_type'], unique=False)
+ op.create_table('admin_notifications',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('type', sa.String(length=50), nullable=False),
+ sa.Column('priority', sa.String(length=20), nullable=True),
+ sa.Column('title', sa.String(length=200), nullable=False),
+ sa.Column('message', sa.Text(), nullable=False),
+ sa.Column('is_read', sa.Boolean(), nullable=True),
+ sa.Column('read_at', sa.DateTime(), nullable=True),
+ sa.Column('read_by_user_id', sa.Integer(), nullable=True),
+ sa.Column('action_required', sa.Boolean(), nullable=True),
+ sa.Column('action_url', sa.String(length=500), nullable=True),
+ sa.Column('notification_metadata', sa.JSON(), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['read_by_user_id'], ['users.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_admin_notifications_action_required'), 'admin_notifications', ['action_required'], unique=False)
+ op.create_index(op.f('ix_admin_notifications_id'), 'admin_notifications', ['id'], unique=False)
+ op.create_index(op.f('ix_admin_notifications_is_read'), 'admin_notifications', ['is_read'], unique=False)
+ op.create_index(op.f('ix_admin_notifications_priority'), 'admin_notifications', ['priority'], unique=False)
+ op.create_index(op.f('ix_admin_notifications_type'), 'admin_notifications', ['type'], unique=False)
+ op.create_table('admin_sessions',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('admin_user_id', sa.Integer(), nullable=False),
+ sa.Column('session_token', sa.String(length=255), nullable=False),
+ sa.Column('ip_address', sa.String(length=45), nullable=False),
+ sa.Column('user_agent', sa.Text(), nullable=True),
+ sa.Column('login_at', sa.DateTime(), nullable=False),
+ sa.Column('last_activity_at', sa.DateTime(), nullable=False),
+ sa.Column('logout_at', sa.DateTime(), nullable=True),
+ sa.Column('is_active', sa.Boolean(), nullable=True),
+ sa.Column('logout_reason', sa.String(length=50), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['admin_user_id'], ['users.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_admin_sessions_admin_user_id'), 'admin_sessions', ['admin_user_id'], unique=False)
+ op.create_index(op.f('ix_admin_sessions_id'), 'admin_sessions', ['id'], unique=False)
+ op.create_index(op.f('ix_admin_sessions_is_active'), 'admin_sessions', ['is_active'], unique=False)
+ op.create_index(op.f('ix_admin_sessions_login_at'), 'admin_sessions', ['login_at'], unique=False)
+ op.create_index(op.f('ix_admin_sessions_session_token'), 'admin_sessions', ['session_token'], unique=True)
+ op.create_table('admin_settings',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('key', sa.String(length=100), nullable=False),
+ sa.Column('value', sa.Text(), nullable=False),
+ sa.Column('value_type', sa.String(length=20), nullable=True),
+ sa.Column('category', sa.String(length=50), nullable=True),
+ sa.Column('description', sa.Text(), nullable=True),
+ sa.Column('is_encrypted', sa.Boolean(), nullable=True),
+ sa.Column('is_public', sa.Boolean(), nullable=True),
+ sa.Column('last_modified_by_user_id', sa.Integer(), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['last_modified_by_user_id'], ['users.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_admin_settings_category'), 'admin_settings', ['category'], unique=False)
+ op.create_index(op.f('ix_admin_settings_id'), 'admin_settings', ['id'], unique=False)
+ op.create_index(op.f('ix_admin_settings_key'), 'admin_settings', ['key'], unique=True)
+ op.create_table('platform_alerts',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('alert_type', sa.String(length=50), nullable=False),
+ sa.Column('severity', sa.String(length=20), nullable=False),
+ sa.Column('title', sa.String(length=200), nullable=False),
+ sa.Column('description', sa.Text(), nullable=True),
+ sa.Column('affected_vendors', sa.JSON(), nullable=True),
+ sa.Column('affected_systems', sa.JSON(), nullable=True),
+ sa.Column('is_resolved', sa.Boolean(), nullable=True),
+ sa.Column('resolved_at', sa.DateTime(), nullable=True),
+ sa.Column('resolved_by_user_id', sa.Integer(), nullable=True),
+ sa.Column('resolution_notes', sa.Text(), nullable=True),
+ sa.Column('auto_generated', sa.Boolean(), nullable=True),
+ sa.Column('occurrence_count', sa.Integer(), nullable=True),
+ sa.Column('first_occurred_at', sa.DateTime(), nullable=False),
+ sa.Column('last_occurred_at', sa.DateTime(), nullable=False),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['resolved_by_user_id'], ['users.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_platform_alerts_alert_type'), 'platform_alerts', ['alert_type'], unique=False)
+ op.create_index(op.f('ix_platform_alerts_id'), 'platform_alerts', ['id'], unique=False)
+ op.create_index(op.f('ix_platform_alerts_is_resolved'), 'platform_alerts', ['is_resolved'], unique=False)
+ op.create_index(op.f('ix_platform_alerts_severity'), 'platform_alerts', ['severity'], unique=False)
op.create_table('vendors',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('vendor_code', sa.String(), nullable=False),
@@ -99,7 +205,6 @@ def upgrade() -> None:
sa.Column('name', sa.String(), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('owner_user_id', sa.Integer(), nullable=False),
- sa.Column('theme_config', sa.JSON(), nullable=True),
sa.Column('contact_email', sa.String(), nullable=True),
sa.Column('contact_phone', sa.String(), nullable=True),
sa.Column('website', sa.String(), nullable=True),
@@ -203,6 +308,54 @@ def upgrade() -> None:
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_roles_id'), 'roles', ['id'], unique=False)
+ op.create_table('vendor_domains',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('vendor_id', sa.Integer(), nullable=False),
+ sa.Column('domain', sa.String(length=255), nullable=False),
+ sa.Column('is_primary', sa.Boolean(), nullable=False),
+ sa.Column('is_active', sa.Boolean(), nullable=False),
+ sa.Column('ssl_status', sa.String(length=50), nullable=True),
+ sa.Column('ssl_verified_at', sa.DateTime(timezone=True), nullable=True),
+ sa.Column('verification_token', sa.String(length=100), nullable=True),
+ sa.Column('is_verified', sa.Boolean(), nullable=False),
+ sa.Column('verified_at', sa.DateTime(timezone=True), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id'),
+ sa.UniqueConstraint('vendor_id', 'domain', name='uq_vendor_domain'),
+ sa.UniqueConstraint('verification_token')
+ )
+ op.create_index('idx_domain_active', 'vendor_domains', ['domain', 'is_active'], unique=False)
+ op.create_index('idx_vendor_primary', 'vendor_domains', ['vendor_id', 'is_primary'], unique=False)
+ op.create_index(op.f('ix_vendor_domains_domain'), 'vendor_domains', ['domain'], unique=True)
+ op.create_index(op.f('ix_vendor_domains_id'), 'vendor_domains', ['id'], unique=False)
+ op.create_table('vendor_themes',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('vendor_id', sa.Integer(), nullable=False),
+ sa.Column('theme_name', sa.String(length=100), nullable=True),
+ sa.Column('is_active', sa.Boolean(), nullable=True),
+ sa.Column('colors', sa.JSON(), nullable=True),
+ sa.Column('font_family_heading', sa.String(length=100), nullable=True),
+ sa.Column('font_family_body', sa.String(length=100), nullable=True),
+ sa.Column('logo_url', sa.String(length=500), nullable=True),
+ sa.Column('logo_dark_url', sa.String(length=500), nullable=True),
+ sa.Column('favicon_url', sa.String(length=500), nullable=True),
+ sa.Column('banner_url', sa.String(length=500), nullable=True),
+ sa.Column('layout_style', sa.String(length=50), nullable=True),
+ sa.Column('header_style', sa.String(length=50), nullable=True),
+ sa.Column('product_card_style', sa.String(length=50), nullable=True),
+ sa.Column('custom_css', sa.Text(), nullable=True),
+ sa.Column('social_links', sa.JSON(), nullable=True),
+ sa.Column('meta_title_template', sa.String(length=200), nullable=True),
+ sa.Column('meta_description', sa.Text(), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id'),
+ sa.UniqueConstraint('vendor_id')
+ )
+ op.create_index(op.f('ix_vendor_themes_id'), 'vendor_themes', ['id'], unique=False)
op.create_table('customer_addresses',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('vendor_id', sa.Integer(), nullable=False),
@@ -342,6 +495,13 @@ def downgrade() -> None:
op.drop_table('inventory')
op.drop_index(op.f('ix_customer_addresses_id'), table_name='customer_addresses')
op.drop_table('customer_addresses')
+ op.drop_index(op.f('ix_vendor_themes_id'), table_name='vendor_themes')
+ op.drop_table('vendor_themes')
+ op.drop_index(op.f('ix_vendor_domains_id'), table_name='vendor_domains')
+ op.drop_index(op.f('ix_vendor_domains_domain'), table_name='vendor_domains')
+ op.drop_index('idx_vendor_primary', table_name='vendor_domains')
+ op.drop_index('idx_domain_active', table_name='vendor_domains')
+ op.drop_table('vendor_domains')
op.drop_index(op.f('ix_roles_id'), table_name='roles')
op.drop_table('roles')
op.drop_index(op.f('ix_products_id'), table_name='products')
@@ -363,6 +523,33 @@ def downgrade() -> None:
op.drop_index(op.f('ix_vendors_subdomain'), table_name='vendors')
op.drop_index(op.f('ix_vendors_id'), table_name='vendors')
op.drop_table('vendors')
+ op.drop_index(op.f('ix_platform_alerts_severity'), table_name='platform_alerts')
+ op.drop_index(op.f('ix_platform_alerts_is_resolved'), table_name='platform_alerts')
+ op.drop_index(op.f('ix_platform_alerts_id'), table_name='platform_alerts')
+ op.drop_index(op.f('ix_platform_alerts_alert_type'), table_name='platform_alerts')
+ op.drop_table('platform_alerts')
+ op.drop_index(op.f('ix_admin_settings_key'), table_name='admin_settings')
+ op.drop_index(op.f('ix_admin_settings_id'), table_name='admin_settings')
+ op.drop_index(op.f('ix_admin_settings_category'), table_name='admin_settings')
+ op.drop_table('admin_settings')
+ op.drop_index(op.f('ix_admin_sessions_session_token'), table_name='admin_sessions')
+ op.drop_index(op.f('ix_admin_sessions_login_at'), table_name='admin_sessions')
+ op.drop_index(op.f('ix_admin_sessions_is_active'), table_name='admin_sessions')
+ op.drop_index(op.f('ix_admin_sessions_id'), table_name='admin_sessions')
+ op.drop_index(op.f('ix_admin_sessions_admin_user_id'), table_name='admin_sessions')
+ op.drop_table('admin_sessions')
+ op.drop_index(op.f('ix_admin_notifications_type'), table_name='admin_notifications')
+ op.drop_index(op.f('ix_admin_notifications_priority'), table_name='admin_notifications')
+ op.drop_index(op.f('ix_admin_notifications_is_read'), table_name='admin_notifications')
+ op.drop_index(op.f('ix_admin_notifications_id'), table_name='admin_notifications')
+ op.drop_index(op.f('ix_admin_notifications_action_required'), table_name='admin_notifications')
+ op.drop_table('admin_notifications')
+ op.drop_index(op.f('ix_admin_audit_logs_target_type'), table_name='admin_audit_logs')
+ op.drop_index(op.f('ix_admin_audit_logs_target_id'), table_name='admin_audit_logs')
+ op.drop_index(op.f('ix_admin_audit_logs_id'), table_name='admin_audit_logs')
+ op.drop_index(op.f('ix_admin_audit_logs_admin_user_id'), table_name='admin_audit_logs')
+ op.drop_index(op.f('ix_admin_audit_logs_action'), table_name='admin_audit_logs')
+ op.drop_table('admin_audit_logs')
op.drop_index(op.f('ix_users_username'), table_name='users')
op.drop_index(op.f('ix_users_id'), table_name='users')
op.drop_index(op.f('ix_users_email'), table_name='users')
diff --git a/alembic/versions/9189d3baaea1_add_vendor_and_role_tables_for_slice_1.py b/alembic/versions/9189d3baaea1_add_vendor_and_role_tables_for_slice_1.py
deleted file mode 100644
index 86da8fee..00000000
--- a/alembic/versions/9189d3baaea1_add_vendor_and_role_tables_for_slice_1.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""Add vendor and role tables for slice 1
-
-Revision ID: 9189d3baaea1
-Revises: 6fe45d3d84c4
-Create Date: 2025-10-08 22:39:53.101668
-
-"""
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision: str = '9189d3baaea1'
-down_revision: Union[str, None] = '6fe45d3d84c4'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- pass
- # ### end Alembic commands ###
-
-
-def downgrade() -> None:
- # ### commands auto generated by Alembic - please adjust! ###
- pass
- # ### end Alembic commands ###
diff --git a/alembic/versions/__init__.py b/alembic/versions/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/app/api/v1/admin/vendor_domains.py b/app/api/v1/admin/vendor_domains.py
index b9d470b2..4f0a0c8c 100644
--- a/app/api/v1/admin/vendor_domains.py
+++ b/app/api/v1/admin/vendor_domains.py
@@ -266,14 +266,14 @@ def verify_domain_ownership(
Verify domain ownership via DNS TXT record (Admin only).
**Verification Process:**
- 1. Queries DNS for TXT record: `_letzshop-verify.{domain}`
+ 1. Queries DNS for TXT record: `_wizamart-verify.{domain}`
2. Checks if verification token matches
3. If found, marks domain as verified
**Requirements:**
- Vendor must have added TXT record to their DNS
- DNS propagation may take 5-15 minutes
- - Record format: `_letzshop-verify.domain.com` TXT `{token}`
+ - Record format: `_wizamart-verify.domain.com` TXT `{token}`
**After verification:**
- Domain can be activated
diff --git a/app/core/config.py b/app/core/config.py
index df3cea86..570194e6 100644
--- a/app/core/config.py
+++ b/app/core/config.py
@@ -73,7 +73,7 @@ class Settings(BaseSettings):
auto_provision_ssl: bool = False # Set to True if using automated SSL
# DNS verification
- dns_verification_prefix: str = "_letzshop-verify"
+ dns_verification_prefix: str = "_wizamart-verify"
dns_verification_ttl: int = 3600
model_config = {"env_file": ".env"} # Updated syntax for Pydantic v2
diff --git a/app/services/vendor_domain_service.py b/app/services/vendor_domain_service.py
index 7716d0df..73fd9da3 100644
--- a/app/services/vendor_domain_service.py
+++ b/app/services/vendor_domain_service.py
@@ -286,7 +286,7 @@ class VendorDomainService:
Verify domain ownership via DNS TXT record.
The vendor must add a TXT record:
- Name: _letzshop-verify.{domain}
+ Name: _wizamart-verify.{domain}
Value: {verification_token}
Args:
@@ -313,7 +313,7 @@ class VendorDomainService:
# Query DNS TXT records
try:
txt_records = dns.resolver.resolve(
- f"_letzshop-verify.{domain.domain}",
+ f"_wizamart-verify.{domain.domain}",
'TXT'
)
@@ -339,7 +339,7 @@ class VendorDomainService:
except dns.resolver.NXDOMAIN:
raise DomainVerificationFailedException(
domain.domain,
- f"DNS record _letzshop-verify.{domain.domain} not found"
+ f"DNS record _wizamart-verify.{domain.domain} not found"
)
except dns.resolver.NoAnswer:
raise DomainVerificationFailedException(
@@ -394,7 +394,7 @@ class VendorDomainService:
},
"txt_record": {
"type": "TXT",
- "name": "_letzshop-verify",
+ "name": "_wizamart-verify",
"value": domain.verification_token,
"ttl": 3600
},
diff --git a/app/templates/admin/base.html b/app/templates/admin/base.html
index d26635f3..11ff6b00 100644
--- a/app/templates/admin/base.html
+++ b/app/templates/admin/base.html
@@ -40,7 +40,7 @@
-
+
diff --git a/app/templates/admin/vendor-theme.html b/app/templates/admin/vendor-theme.html
index 7b598981..7b16c105 100644
--- a/app/templates/admin/vendor-theme.html
+++ b/app/templates/admin/vendor-theme.html
@@ -243,7 +243,7 @@
-
+
Typography
diff --git a/docs/__temp/10.stripe_payment_integration.md b/docs/__REVAMPING/10.stripe_payment_integration.md
similarity index 100%
rename from docs/__temp/10.stripe_payment_integration.md
rename to docs/__REVAMPING/10.stripe_payment_integration.md
diff --git a/docs/__temp/12.project_readme_final.md b/docs/__REVAMPING/12.project_readme_final.md
similarity index 100%
rename from docs/__temp/12.project_readme_final.md
rename to docs/__REVAMPING/12.project_readme_final.md
diff --git a/docs/__temp/13.updated_application_workflows_final.md b/docs/__REVAMPING/13.updated_application_workflows_final.md
similarity index 100%
rename from docs/__temp/13.updated_application_workflows_final.md
rename to docs/__REVAMPING/13.updated_application_workflows_final.md
diff --git a/docs/__temp/14.updated_complete_project_structure_final.md b/docs/__REVAMPING/14.updated_complete_project_structure_final.md
similarity index 100%
rename from docs/__temp/14.updated_complete_project_structure_final.md
rename to docs/__REVAMPING/14.updated_complete_project_structure_final.md
diff --git a/docs/__temp/6.complete_naming_convention.md b/docs/__REVAMPING/6.complete_naming_convention.md
similarity index 100%
rename from docs/__temp/6.complete_naming_convention.md
rename to docs/__REVAMPING/6.complete_naming_convention.md
diff --git a/docs/__temp/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md b/docs/__REVAMPING/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md
similarity index 100%
rename from docs/__temp/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md
rename to docs/__REVAMPING/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md
diff --git a/docs/__temp/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md b/docs/__REVAMPING/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md
similarity index 100%
rename from docs/__temp/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md
rename to docs/__REVAMPING/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md
diff --git a/docs/__temp/BACKEND/admin_integration_guide.md b/docs/__REVAMPING/BACKEND/admin_integration_guide.md
similarity index 100%
rename from docs/__temp/BACKEND/admin_integration_guide.md
rename to docs/__REVAMPING/BACKEND/admin_integration_guide.md
diff --git a/docs/__REVAMPING/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md b/docs/__REVAMPING/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md
new file mode 100644
index 00000000..8e2b182d
--- /dev/null
+++ b/docs/__REVAMPING/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md
@@ -0,0 +1,562 @@
+# Alpine.js Page Template - Quick Reference (WITH CENTRALIZED LOGGING)
+
+## ✅ Correct Page Structure
+
+```javascript
+// static/admin/js/your-page.js (or vendor/shop)
+
+// 1. ✅ Use centralized logger (ONE LINE!)
+const yourPageLog = window.LogConfig.loggers.yourPage;
+// OR create custom if not pre-configured
+// const yourPageLog = window.LogConfig.createLogger('YOUR-PAGE', window.LogConfig.logLevel);
+
+// 2. Create your Alpine.js component
+function yourPageComponent() {
+ return {
+ // ✅ CRITICAL: Inherit base layout functionality
+ ...data(),
+
+ // ✅ CRITICAL: Set page identifier
+ currentPage: 'your-page',
+
+ // Your page-specific state
+ items: [],
+ loading: false,
+ error: null,
+
+ // ✅ CRITICAL: Proper initialization with guard
+ async init() {
+ yourPageLog.info('=== YOUR PAGE INITIALIZING ===');
+
+ // Prevent multiple initializations
+ if (window._yourPageInitialized) {
+ yourPageLog.warn('Page already initialized, skipping...');
+ return;
+ }
+ window._yourPageInitialized = true;
+
+ // Load your data
+ await this.loadData();
+
+ yourPageLog.info('=== YOUR PAGE INITIALIZATION COMPLETE ===');
+ },
+
+ // Your methods
+ async loadData() {
+ yourPageLog.info('Loading data...');
+ this.loading = true;
+ this.error = null;
+
+ try {
+ const startTime = performance.now();
+
+ // Log API request
+ const url = '/your/endpoint';
+ window.LogConfig.logApiCall('GET', url, null, 'request');
+
+ // ✅ CRITICAL: Use lowercase apiClient
+ const response = await apiClient.get(url);
+
+ // Log API response
+ window.LogConfig.logApiCall('GET', url, response, 'response');
+
+ this.items = response.items || [];
+
+ // Log performance
+ const duration = performance.now() - startTime;
+ window.LogConfig.logPerformance('Load Data', duration);
+
+ yourPageLog.info(`Data loaded successfully`, {
+ count: this.items.length,
+ duration: `${duration}ms`
+ });
+
+ } catch (error) {
+ // Use centralized error logging
+ window.LogConfig.logError(error, 'Load Data');
+ this.error = error.message;
+ Utils.showToast('Failed to load data', 'error');
+ } finally {
+ this.loading = false;
+ }
+ },
+
+ // Format date helper (if needed)
+ formatDate(dateString) {
+ if (!dateString) return '-';
+ return Utils.formatDate(dateString);
+ },
+
+ // Your other methods...
+ };
+}
+
+yourPageLog.info('Your page module loaded');
+```
+
+---
+
+## 🎯 Checklist for New Pages
+
+### HTML Template
+```jinja2
+{# app/templates/admin/your-page.html #}
+{% extends "admin/base.html" %}
+
+{% block title %}Your Page{% endblock %}
+
+{# ✅ CRITICAL: Link to your Alpine.js component #}
+{% block alpine_data %}yourPageComponent(){% endblock %}
+
+{% block content %}
+
+{% endblock %}
+
+{% block extra_scripts %}
+{# ✅ CRITICAL: Load your JavaScript file #}
+
+{% endblock %}
+```
+
+### JavaScript File Checklist
+
+- [ ] ✅ Use centralized logger (ONE line instead of 15!)
+- [ ] ✅ Function name matches `alpine_data` in template
+- [ ] ✅ `...data(),` at start of return object
+- [ ] ✅ `currentPage: 'your-page'` set
+- [ ] ✅ Initialization guard in `init()`
+- [ ] ✅ Use lowercase `apiClient` for API calls
+- [ ] ✅ Use `window.LogConfig.logApiCall()` for API logging
+- [ ] ✅ Use `window.LogConfig.logPerformance()` for performance
+- [ ] ✅ Use `window.LogConfig.logError()` for errors
+- [ ] ✅ Module loaded log at end
+
+---
+
+## 📦 Pre-configured Loggers by Frontend
+
+### Admin Frontend
+```javascript
+window.LogConfig.loggers.vendors // Vendor management
+window.LogConfig.loggers.vendorTheme // Theme customization
+window.LogConfig.loggers.vendorUsers // Vendor users
+window.LogConfig.loggers.products // Product management
+window.LogConfig.loggers.inventory // Inventory
+window.LogConfig.loggers.orders // Order management
+window.LogConfig.loggers.users // User management
+window.LogConfig.loggers.audit // Audit logs
+window.LogConfig.loggers.dashboard // Dashboard
+window.LogConfig.loggers.imports // Import operations
+```
+
+### Vendor Frontend
+```javascript
+window.LogConfig.loggers.dashboard // Vendor dashboard
+window.LogConfig.loggers.products // Product management
+window.LogConfig.loggers.inventory // Inventory
+window.LogConfig.loggers.orders // Order management
+window.LogConfig.loggers.theme // Theme customization
+window.LogConfig.loggers.settings // Settings
+window.LogConfig.loggers.analytics // Analytics
+```
+
+### Shop Frontend
+```javascript
+window.LogConfig.loggers.catalog // Product browsing
+window.LogConfig.loggers.product // Product details
+window.LogConfig.loggers.search // Search
+window.LogConfig.loggers.cart // Shopping cart
+window.LogConfig.loggers.checkout // Checkout
+window.LogConfig.loggers.account // User account
+window.LogConfig.loggers.orders // Order history
+window.LogConfig.loggers.wishlist // Wishlist
+```
+
+---
+
+## ❌ Common Mistakes to Avoid
+
+### 1. Old Way vs New Way
+```javascript
+// ❌ OLD WAY - 15 lines of duplicate code
+const YOUR_PAGE_LOG_LEVEL = 3;
+const yourPageLog = {
+ error: (...args) => YOUR_PAGE_LOG_LEVEL >= 1 && console.error('❌ [YOUR_PAGE ERROR]', ...args),
+ warn: (...args) => YOUR_PAGE_LOG_LEVEL >= 2 && console.warn('⚠️ [YOUR_PAGE WARN]', ...args),
+ info: (...args) => YOUR_PAGE_LOG_LEVEL >= 3 && console.info('ℹ️ [YOUR_PAGE INFO]', ...args),
+ debug: (...args) => YOUR_PAGE_LOG_LEVEL >= 4 && console.log('🔍 [YOUR_PAGE DEBUG]', ...args)
+};
+
+// ✅ NEW WAY - 1 line!
+const yourPageLog = window.LogConfig.loggers.yourPage;
+```
+
+### 2. Missing Base Inheritance
+```javascript
+// ❌ WRONG
+function myPage() {
+ return {
+ items: [],
+ // Missing ...data()
+ };
+}
+
+// ✅ CORRECT
+function myPage() {
+ return {
+ ...data(), // Must be first!
+ items: [],
+ };
+}
+```
+
+### 3. Wrong API Client Name
+```javascript
+// ❌ WRONG - Capital letters
+await ApiClient.get('/endpoint');
+await API_CLIENT.get('/endpoint');
+
+// ✅ CORRECT - lowercase
+await apiClient.get('/endpoint');
+```
+
+### 4. Missing Init Guard
+```javascript
+// ❌ WRONG
+async init() {
+ await this.loadData();
+}
+
+// ✅ CORRECT
+async init() {
+ if (window._myPageInitialized) return;
+ window._myPageInitialized = true;
+ await this.loadData();
+}
+```
+
+### 5. Missing currentPage
+```javascript
+// ❌ WRONG
+return {
+ ...data(),
+ items: [],
+ // Missing currentPage
+};
+
+// ✅ CORRECT
+return {
+ ...data(),
+ currentPage: 'my-page', // Must set this!
+ items: [],
+};
+```
+
+---
+
+## 🔧 API Client Pattern
+
+### GET Request
+```javascript
+try {
+ const response = await apiClient.get('/endpoint');
+ this.data = response;
+} catch (error) {
+ console.error('Failed:', error);
+ Utils.showToast('Failed to load', 'error');
+}
+```
+
+### POST Request
+```javascript
+try {
+ const response = await apiClient.post('/endpoint', {
+ name: 'value',
+ // ... data
+ });
+ Utils.showToast('Created successfully', 'success');
+} catch (error) {
+ console.error('Failed:', error);
+ Utils.showToast('Failed to create', 'error');
+}
+```
+
+### PUT Request
+```javascript
+try {
+ const response = await apiClient.put('/endpoint/123', {
+ name: 'updated value'
+ });
+ Utils.showToast('Updated successfully', 'success');
+} catch (error) {
+ console.error('Failed:', error);
+ Utils.showToast('Failed to update', 'error');
+}
+```
+
+### DELETE Request
+```javascript
+try {
+ await apiClient.delete('/endpoint/123');
+ Utils.showToast('Deleted successfully', 'success');
+ await this.reloadData();
+} catch (error) {
+ console.error('Failed:', error);
+ Utils.showToast('Failed to delete', 'error');
+}
+```
+
+---
+
+## 🔧 Centralized Logging Patterns
+
+### Basic Logging
+```javascript
+const log = window.LogConfig.loggers.yourPage;
+
+log.info('Page loaded');
+log.warn('Connection slow');
+log.error('Failed to load data', error);
+log.debug('User data:', userData);
+```
+
+### Grouped Logging
+```javascript
+log.group('Loading Theme Data');
+log.info('Fetching vendor...');
+log.info('Fetching theme...');
+log.info('Fetching presets...');
+log.groupEnd();
+```
+
+### API Call Logging
+```javascript
+const url = '/api/vendors';
+
+// Before request
+window.LogConfig.logApiCall('GET', url, null, 'request');
+
+// Make request
+const data = await apiClient.get(url);
+
+// After response
+window.LogConfig.logApiCall('GET', url, data, 'response');
+```
+
+### Error Logging
+```javascript
+try {
+ await saveTheme();
+} catch (error) {
+ window.LogConfig.logError(error, 'Save Theme');
+}
+```
+
+### Performance Logging
+```javascript
+const start = performance.now();
+await loadThemeData();
+const duration = performance.now() - start;
+window.LogConfig.logPerformance('Load Theme Data', duration);
+```
+
+### Table Logging
+```javascript
+log.table([
+ { id: 1, name: 'Vendor A', status: 'active' },
+ { id: 2, name: 'Vendor B', status: 'inactive' }
+]);
+```
+
+---
+
+## 📚 Benefits of Centralized Logging
+
+| Aspect | Old Way | New Way |
+|--------|---------|---------|
+| **Lines of code** | ~15 per file | 1 line per file |
+| **Consistency** | Varies by file | Unified across all frontends |
+| **Maintenance** | Update each file | Update one shared file |
+| **Features** | Basic logging | Advanced (groups, perf, API) |
+| **Environment** | Manual config | Auto-detected |
+| **Frontend aware** | No | Yes (admin/vendor/shop) |
+| **Log levels** | Per file | Per frontend + environment |
+
+---
+
+## 🎨 Common UI Patterns
+
+### Loading State
+```javascript
+async loadData() {
+ this.loading = true;
+ try {
+ const data = await apiClient.get('/endpoint');
+ this.items = data;
+ } finally {
+ this.loading = false;
+ }
+}
+```
+
+### Refresh/Reload
+```javascript
+async refresh() {
+ console.info('Refreshing...');
+ await this.loadData();
+ Utils.showToast('Refreshed successfully', 'success');
+}
+```
+
+---
+
+## 📚 Available Utilities
+
+### From `init-alpine.js` (via `...data()`)
+- `this.dark` - Dark mode state
+- `this.toggleTheme()` - Toggle theme
+- `this.isSideMenuOpen` - Side menu state
+- `this.toggleSideMenu()` - Toggle side menu
+- `this.closeSideMenu()` - Close side menu
+- `this.isNotificationsMenuOpen` - Notifications menu state
+- `this.toggleNotificationsMenu()` - Toggle notifications
+- `this.closeNotificationsMenu()` - Close notifications
+- `this.isProfileMenuOpen` - Profile menu state
+- `this.toggleProfileMenu()` - Toggle profile menu
+- `this.closeProfileMenu()` - Close profile menu
+- `this.isPagesMenuOpen` - Pages menu state
+- `this.togglePagesMenu()` - Toggle pages menu
+
+### From `Utils` (global)
+- `Utils.showToast(message, type, duration)` - Show toast notification
+- `Utils.formatDate(dateString)` - Format date for display
+- `Utils.confirm(message, title)` - Show confirmation dialog (if available)
+
+### From `apiClient` (global)
+- `apiClient.get(url)` - GET request
+- `apiClient.post(url, data)` - POST request
+- `apiClient.put(url, data)` - PUT request
+- `apiClient.delete(url)` - DELETE request
+
+---
+
+## 🎨 Complete Example
+
+```javascript
+// static/admin/js/vendor-theme.js
+
+// 1. Use centralized logger
+const themeLog = window.LogConfig.loggers.vendorTheme;
+
+// 2. Create component
+function adminVendorTheme() {
+ return {
+ ...data(),
+ currentPage: 'vendor-theme',
+
+ vendor: null,
+ themeData: {},
+ loading: true,
+
+ async init() {
+ themeLog.info('Initializing vendor theme editor');
+
+ if (window._vendorThemeInitialized) {
+ themeLog.warn('Already initialized, skipping...');
+ return;
+ }
+ window._vendorThemeInitialized = true;
+
+ const startTime = performance.now();
+
+ try {
+ themeLog.group('Loading theme data');
+
+ await Promise.all([
+ this.loadVendor(),
+ this.loadTheme()
+ ]);
+
+ themeLog.groupEnd();
+
+ const duration = performance.now() - startTime;
+ window.LogConfig.logPerformance('Theme Editor Init', duration);
+
+ themeLog.info('Theme editor initialized successfully');
+
+ } catch (error) {
+ window.LogConfig.logError(error, 'Theme Editor Init');
+ Utils.showToast('Failed to initialize', 'error');
+ } finally {
+ this.loading = false;
+ }
+ },
+
+ async loadVendor() {
+ const url = `/admin/vendors/${this.vendorCode}`;
+ window.LogConfig.logApiCall('GET', url, null, 'request');
+
+ const response = await apiClient.get(url);
+ this.vendor = response;
+
+ window.LogConfig.logApiCall('GET', url, response, 'response');
+ themeLog.debug('Vendor loaded:', this.vendor);
+ },
+
+ async saveTheme() {
+ themeLog.info('Saving theme changes');
+
+ try {
+ const url = `/admin/vendor-themes/${this.vendorCode}`;
+ window.LogConfig.logApiCall('PUT', url, this.themeData, 'request');
+
+ const response = await apiClient.put(url, this.themeData);
+
+ window.LogConfig.logApiCall('PUT', url, response, 'response');
+
+ themeLog.info('Theme saved successfully');
+ Utils.showToast('Theme saved', 'success');
+
+ } catch (error) {
+ window.LogConfig.logError(error, 'Save Theme');
+ Utils.showToast('Failed to save theme', 'error');
+ }
+ }
+ };
+}
+
+themeLog.info('Vendor theme editor module loaded');
+```
+
+---
+
+## 🚀 Quick Start Template Files
+
+Copy these to create a new page:
+
+1. **Copy base file:** `dashboard.js` → rename to `your-page.js`
+2. **Update logger:**
+ ```javascript
+ // Change from:
+ const dashLog = window.LogConfig.loggers.dashboard;
+ // To:
+ const yourPageLog = window.LogConfig.loggers.yourPage;
+ // Or create custom:
+ const yourPageLog = window.LogConfig.createLogger('YOUR-PAGE');
+ ```
+3. **Replace function name:** `adminDashboard()` → `yourPageComponent()`
+4. **Update init flag:** `_dashboardInitialized` → `_yourPageInitialized`
+5. **Update page identifier:** `currentPage: 'dashboard'` → `currentPage: 'your-page'`
+6. **Replace data loading logic** with your API endpoints
+7. **Update HTML template** to use your function name:
+ ```jinja2
+ {% block alpine_data %}yourPageComponent(){% endblock %}
+ ```
+8. **Load your script** in the template:
+ ```jinja2
+ {% block extra_scripts %}
+
+ {% endblock %}
+ ```
+
+Done! ✅
diff --git a/docs/__temp/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt b/docs/__REVAMPING/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt
similarity index 100%
rename from docs/__temp/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt
rename to docs/__REVAMPING/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt
diff --git a/docs/__REVAMPING/FRONTEND/FRONTEND_LOGGING_SYSTEM_QUICK_REFERENCE.md b/docs/__REVAMPING/FRONTEND/FRONTEND_LOGGING_SYSTEM_QUICK_REFERENCE.md
new file mode 100644
index 00000000..0b650c6f
--- /dev/null
+++ b/docs/__REVAMPING/FRONTEND/FRONTEND_LOGGING_SYSTEM_QUICK_REFERENCE.md
@@ -0,0 +1,410 @@
+# Quick Reference: Centralized Logging System
+
+## 🚀 Quick Start
+
+### In Any Page File (Admin/Vendor/Shop):
+
+```javascript
+// 1. Use pre-configured logger (RECOMMENDED)
+const pageLog = window.LogConfig.loggers.yourPage;
+
+// 2. Or create custom logger
+const pageLog = window.LogConfig.createLogger('PAGE-NAME');
+
+// 3. Use it
+pageLog.info('Page loaded');
+pageLog.debug('Data:', data);
+pageLog.warn('Warning message');
+pageLog.error('Error occurred', error);
+```
+
+---
+
+## 📦 Pre-configured Loggers
+
+### Admin Frontend
+```javascript
+window.LogConfig.loggers.vendors
+window.LogConfig.loggers.vendorTheme
+window.LogConfig.loggers.products
+window.LogConfig.loggers.orders
+window.LogConfig.loggers.users
+window.LogConfig.loggers.dashboard
+window.LogConfig.loggers.imports
+```
+
+### Vendor Frontend
+```javascript
+window.LogConfig.loggers.dashboard
+window.LogConfig.loggers.products
+window.LogConfig.loggers.orders
+window.LogConfig.loggers.theme
+window.LogConfig.loggers.analytics
+```
+
+### Shop Frontend
+```javascript
+window.LogConfig.loggers.catalog
+window.LogConfig.loggers.cart
+window.LogConfig.loggers.checkout
+window.LogConfig.loggers.product
+```
+
+---
+
+## 🎨 Basic Logging
+
+```javascript
+const log = window.LogConfig.loggers.myPage;
+
+// Simple messages
+log.info('Operation started');
+log.warn('Slow connection detected');
+log.error('Operation failed');
+log.debug('Debug data:', { user: 'John', id: 123 });
+
+// With multiple arguments
+log.info('User logged in:', username, 'at', timestamp);
+```
+
+---
+
+## 🔥 Advanced Features
+
+### API Call Logging
+```javascript
+const url = '/api/vendors';
+
+// Before request
+window.LogConfig.logApiCall('GET', url, null, 'request');
+
+// Make request
+const data = await apiClient.get(url);
+
+// After response
+window.LogConfig.logApiCall('GET', url, data, 'response');
+```
+
+### Performance Logging
+```javascript
+const start = performance.now();
+await expensiveOperation();
+const duration = performance.now() - start;
+
+window.LogConfig.logPerformance('Operation Name', duration);
+// Output: ⚡ Operation Name took 45ms (fast)
+// Output: ⏱️ Operation Name took 250ms (medium)
+// Output: 🐌 Operation Name took 750ms (slow)
+```
+
+### Error Logging
+```javascript
+try {
+ await riskyOperation();
+} catch (error) {
+ window.LogConfig.logError(error, 'Operation Context');
+ // Automatically logs error message and stack trace
+}
+```
+
+### Grouped Logging
+```javascript
+log.group('Loading Data');
+log.info('Fetching users...');
+log.info('Fetching products...');
+log.info('Fetching orders...');
+log.groupEnd();
+```
+
+### Table Logging
+```javascript
+const users = [
+ { id: 1, name: 'John', status: 'active' },
+ { id: 2, name: 'Jane', status: 'inactive' }
+];
+log.table(users);
+```
+
+### Timer Logging
+```javascript
+log.time('Data Processing');
+// ... long operation ...
+log.timeEnd('Data Processing');
+// Output: ⏱️ [ADMIN:PAGE TIME] Data Processing: 1234.56ms
+```
+
+---
+
+## 📋 Complete Page Template
+
+```javascript
+// static/admin/js/my-page.js
+
+// 1. Setup logger
+const pageLog = window.LogConfig.loggers.myPage;
+
+// 2. Create component
+function myPageComponent() {
+ return {
+ ...data(),
+ currentPage: 'my-page',
+
+ items: [],
+ loading: false,
+
+ async init() {
+ pageLog.info('Initializing page');
+
+ // Prevent double init
+ if (window._myPageInitialized) return;
+ window._myPageInitialized = true;
+
+ const start = performance.now();
+
+ try {
+ pageLog.group('Loading Data');
+ await this.loadData();
+ pageLog.groupEnd();
+
+ const duration = performance.now() - start;
+ window.LogConfig.logPerformance('Page Init', duration);
+
+ pageLog.info('Page initialized successfully');
+ } catch (error) {
+ window.LogConfig.logError(error, 'Page Init');
+ }
+ },
+
+ async loadData() {
+ const url = '/api/my-data';
+ window.LogConfig.logApiCall('GET', url, null, 'request');
+
+ const data = await apiClient.get(url);
+
+ window.LogConfig.logApiCall('GET', url, data, 'response');
+ this.items = data;
+ }
+ };
+}
+
+pageLog.info('Page module loaded');
+```
+
+---
+
+## 🎯 Log Levels
+
+```javascript
+LOG_LEVELS = {
+ ERROR: 1, // Only errors
+ WARN: 2, // Errors + warnings
+ INFO: 3, // Errors + warnings + info (default)
+ DEBUG: 4 // Everything
+}
+```
+
+**Auto-detected:**
+- `localhost` → DEBUG (4)
+- Production → Varies by frontend
+
+**Manual override:**
+```javascript
+const myLog = window.LogConfig.createLogger('MY-PAGE', 4); // Force DEBUG
+```
+
+---
+
+## 🔍 Debugging Tips
+
+### Check Current Config
+```javascript
+console.log(window.LogConfig.frontend); // 'admin' | 'vendor' | 'shop'
+console.log(window.LogConfig.environment); // 'development' | 'production'
+console.log(window.LogConfig.logLevel); // 1 | 2 | 3 | 4
+```
+
+### Test Logger
+```javascript
+const test = window.LogConfig.createLogger('TEST', 4);
+test.info('This is a test');
+test.debug('Debug info:', { test: true });
+```
+
+### Enable Debug Mode
+```javascript
+// In browser console
+window.LogConfig.loggers.myPage = window.LogConfig.createLogger('MY-PAGE', 4);
+// Now reload page to see debug logs
+```
+
+---
+
+## ⚠️ Common Mistakes
+
+### ❌ WRONG
+```javascript
+// Old way (don't do this!)
+const LOG_LEVEL = 3;
+const log = {
+ info: (...args) => LOG_LEVEL >= 3 && console.log(...args)
+};
+
+// Wrong case
+ApiClient.get(url) // Should be apiClient
+Logger.info() // Should be window.LogConfig
+```
+
+### ✅ CORRECT
+```javascript
+// New way
+const log = window.LogConfig.loggers.myPage;
+
+// Correct case
+apiClient.get(url)
+window.LogConfig.loggers.myPage.info()
+```
+
+---
+
+## 🎨 Console Output Examples
+
+### Development Mode
+```
+🎛️ Admin Frontend Logging System Initialized
+ Environment: Development
+ Log Level: 4 (DEBUG)
+ℹ️ [ADMIN:VENDORS INFO] Vendors module loaded
+ℹ️ [ADMIN:VENDORS INFO] Initializing vendors page
+📤 [ADMIN:API DEBUG] GET /admin/vendors
+📥 [ADMIN:API DEBUG] GET /admin/vendors {data...}
+⚡ [ADMIN:PERF DEBUG] Load Vendors took 45ms
+ℹ️ [ADMIN:VENDORS INFO] Vendors loaded: 25 items
+```
+
+### Production Mode (Admin)
+```
+🎛️ Admin Frontend Logging System Initialized
+ Environment: Production
+ Log Level: 2 (WARN)
+⚠️ [ADMIN:API WARN] Slow API response: 2.5s
+❌ [ADMIN:VENDORS ERROR] Failed to load vendor
+```
+
+---
+
+## 📱 Frontend Detection
+
+The system automatically detects which frontend based on URL:
+
+| URL Path | Frontend | Prefix |
+|----------|----------|--------|
+| `/admin/*` | admin | `ADMIN:` |
+| `/vendor/*` | vendor | `VENDOR:` |
+| `/shop/*` | shop | `SHOP:` |
+
+---
+
+## 🛠️ Customization
+
+### Add New Pre-configured Logger
+
+Edit `log-config.js`:
+
+```javascript
+const adminLoggers = {
+ // ... existing loggers
+ myNewPage: createLogger('MY-NEW-PAGE', ACTIVE_LOG_LEVEL)
+};
+```
+
+### Change Log Level for Frontend
+
+Edit `log-config.js`:
+
+```javascript
+const DEFAULT_LOG_LEVELS = {
+ admin: {
+ development: LOG_LEVELS.DEBUG,
+ production: LOG_LEVELS.INFO // Change this
+ }
+};
+```
+
+---
+
+## 📚 Full API Reference
+
+```javascript
+window.LogConfig = {
+ // Log levels
+ LOG_LEVELS: { ERROR: 1, WARN: 2, INFO: 3, DEBUG: 4 },
+
+ // Current config
+ frontend: 'admin' | 'vendor' | 'shop',
+ environment: 'development' | 'production',
+ logLevel: 1 | 2 | 3 | 4,
+
+ // Default logger
+ log: { error(), warn(), info(), debug(), group(), groupEnd(), table(), time(), timeEnd() },
+
+ // Pre-configured loggers
+ loggers: { vendors, products, orders, ... },
+
+ // Create custom logger
+ createLogger(prefix, level?),
+
+ // Utility functions
+ logApiCall(method, url, data?, status),
+ logError(error, context),
+ logPerformance(operation, duration)
+}
+```
+
+---
+
+## 💡 Tips & Tricks
+
+1. **Use Pre-configured Loggers**
+ - Faster to write
+ - Consistent naming
+ - Already configured
+
+2. **Log API Calls**
+ - Easy to track requests
+ - See request/response data
+ - Debug API issues
+
+3. **Track Performance**
+ - Find slow operations
+ - Optimize bottlenecks
+ - Monitor page load times
+
+4. **Group Related Logs**
+ - Cleaner console
+ - Easier to follow
+ - Better debugging
+
+5. **Use Debug Level in Development**
+ - See everything
+ - Find issues faster
+ - Learn how code flows
+
+---
+
+## 📖 More Documentation
+
+- [Migration Guide](MIGRATION_GUIDE.md)
+- [Alpine.js Template V2](ALPINE_PAGE_TEMPLATE_V2.md)
+- [Recommendation Summary](RECOMMENDATION_SUMMARY.md)
+
+---
+
+**Print this page and keep it handy!** 📌
+
+Quick pattern to remember:
+```javascript
+const log = window.LogConfig.loggers.myPage;
+log.info('Hello from my page!');
+```
+
+That's it! 🎉
diff --git a/docs/__temp/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md b/docs/__REVAMPING/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md
similarity index 100%
rename from docs/__temp/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md
rename to docs/__REVAMPING/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md
diff --git a/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS.md b/docs/__REVAMPING/FRONTEND/FRONTEND_UI_COMPONENTS.md
similarity index 100%
rename from docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS.md
rename to docs/__REVAMPING/FRONTEND/FRONTEND_UI_COMPONENTS.md
diff --git a/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md b/docs/__REVAMPING/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md
similarity index 100%
rename from docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md
rename to docs/__REVAMPING/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md
diff --git a/docs/__temp/FRONTEND/PAGINATION_DOCUMENTATION.md b/docs/__REVAMPING/FRONTEND/PAGINATION_DOCUMENTATION.md
similarity index 100%
rename from docs/__temp/FRONTEND/PAGINATION_DOCUMENTATION.md
rename to docs/__REVAMPING/FRONTEND/PAGINATION_DOCUMENTATION.md
diff --git a/docs/__temp/FRONTEND/PAGINATION_QUICK_START.txt b/docs/__REVAMPING/FRONTEND/PAGINATION_QUICK_START.txt
similarity index 100%
rename from docs/__temp/FRONTEND/PAGINATION_QUICK_START.txt
rename to docs/__REVAMPING/FRONTEND/PAGINATION_QUICK_START.txt
diff --git a/docs/__temp/FRONTEND/frontend-structure.txt b/docs/__REVAMPING/FRONTEND/frontend-structure.txt
similarity index 100%
rename from docs/__temp/FRONTEND/frontend-structure.txt
rename to docs/__REVAMPING/FRONTEND/frontend-structure.txt
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_ARCHITECTURE_DIAGRAMS.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_ARCHITECTURE_DIAGRAMS.md
similarity index 99%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_ARCHITECTURE_DIAGRAMS.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_ARCHITECTURE_DIAGRAMS.md
index d5a12ac4..4478ec1c 100644
--- a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_ARCHITECTURE_DIAGRAMS.md
+++ b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_ARCHITECTURE_DIAGRAMS.md
@@ -402,7 +402,7 @@ Value: 123.45.67.89
TTL: 3600
Type: TXT
-Name: _letzshop-verify
+Name: _wizamart-verify
Value: abc123xyz (verification token from your platform)
TTL: 3600
```
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md
similarity index 99%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md
index af37895e..0f3023e9 100644
--- a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md
+++ b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md
@@ -161,7 +161,7 @@ To prevent domain hijacking, verify the vendor owns the domain:
2. System generates verification token: `abc123xyz`
3. Vendor adds DNS TXT record:
```
- Name: _letzshop-verify.customdomain1.com
+ Name: _wizamart-verify.customdomain1.com
Type: TXT
Value: abc123xyz
```
@@ -177,7 +177,7 @@ def verify_domain(domain_id: int, db: Session):
# Query DNS for TXT record
txt_records = dns.resolver.resolve(
- f"_letzshop-verify.{domain.domain}",
+ f"_wizamart-verify.{domain.domain}",
'TXT'
)
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_EXECUTIVE_SUMMARY.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_EXECUTIVE_SUMMARY.md
similarity index 99%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_EXECUTIVE_SUMMARY.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_EXECUTIVE_SUMMARY.md
index c9e51ccd..4783c609 100644
--- a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_EXECUTIVE_SUMMARY.md
+++ b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_EXECUTIVE_SUMMARY.md
@@ -116,7 +116,7 @@ Name: @
Value: 123.45.67.89 (your server IP)
Type: TXT
-Name: _letzshop-verify
+Name: _wizamart-verify
Value: abc123xyz
```
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_IMPLEMENTATION_CHECKLIST.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_IMPLEMENTATION_CHECKLIST.md
similarity index 98%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_IMPLEMENTATION_CHECKLIST.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_IMPLEMENTATION_CHECKLIST.md
index e41a4ea7..532b3fbb 100644
--- a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_IMPLEMENTATION_CHECKLIST.md
+++ b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_IMPLEMENTATION_CHECKLIST.md
@@ -125,7 +125,7 @@ pip install dnspython
import dns.resolver
# Test querying TXT record
-answers = dns.resolver.resolve("_letzshop-verify.example.com", "TXT")
+answers = dns.resolver.resolve("_wizamart-verify.example.com", "TXT")
for txt in answers:
print(txt.to_text())
```
@@ -259,7 +259,7 @@ TTL: 3600
**Verification TXT Record:**
```
Type: TXT
-Name: _letzshop-verify
+Name: _wizamart-verify
Value: [token from step 9.1]
TTL: 3600
```
@@ -351,7 +351,7 @@ HAVING COUNT(*) > 1;
**Check DNS propagation:**
```bash
# Check if TXT record exists
-dig _letzshop-verify.customdomain1.com TXT
+dig _wizamart-verify.customdomain1.com TXT
# Should show verification token
```
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_INDEX.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_INDEX.md
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_INDEX.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_INDEX.md
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_QUICK_START.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_QUICK_START.md
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_QUICK_START.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_QUICK_START.md
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_README.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_README.md
similarity index 99%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_README.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_README.md
index 9f683968..c2d19b09 100644
--- a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_README.md
+++ b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_README.md
@@ -203,7 +203,7 @@ When a vendor wants to use `customdomain1.com`:
Value: 123.45.67.89 (your server IP)
2. Verification TXT Record:
- Name: _letzshop-verify
+ Name: _wizamart-verify
Value: [token from your platform]
3. Wait 5-15 minutes for DNS propagation
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/MULTI_THEME_SHOP_GUIDE.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/MULTI_THEME_SHOP_GUIDE.md
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/MULTI_THEME_SHOP_GUIDE.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/MULTI_THEME_SHOP_GUIDE.md
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_ARCHITECTURE_COMPLIANT_GUIDE.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_ARCHITECTURE_COMPLIANT_GUIDE.md
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_ARCHITECTURE_COMPLIANT_GUIDE.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_ARCHITECTURE_COMPLIANT_GUIDE.md
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_INTEGRATION_YOUR_ARCHITECTURE.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_INTEGRATION_YOUR_ARCHITECTURE.md
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_INTEGRATION_YOUR_ARCHITECTURE.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_INTEGRATION_YOUR_ARCHITECTURE.md
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PRESETS_GUIDE.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PRESETS_GUIDE.md
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PRESETS_GUIDE.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PRESETS_GUIDE.md
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PROPER_ARCHITECTURE_GUIDE.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PROPER_ARCHITECTURE_GUIDE.md
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PROPER_ARCHITECTURE_GUIDE.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_PROPER_ARCHITECTURE_GUIDE.md
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_base_template.html b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_base_template.html
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_base_template.html
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_base_template.html
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_layout.js b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_layout.js
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_layout.js
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_layout.js
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/theme_context_middleware.py b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/theme_context_middleware.py
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/theme_context_middleware.py
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/theme_context_middleware.py
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/vendor_theme_model.py b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/vendor_theme_model.py
similarity index 100%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/vendor_theme_model.py
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/vendor_theme_model.py
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md
similarity index 99%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md
index 656ae717..6d17e5da 100644
--- a/docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md
+++ b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md
@@ -331,7 +331,7 @@ Step 2: Get Instructions
┌────────────────────────────────────┐
│ System returns instructions: │
│ "Add TXT record: │
-│ _letzshop-verify.myshop.com │
+│ _wizamart-verify.myshop.com │
│ Value: abc123..." │
└────────────────────────────────────┘
@@ -343,7 +343,7 @@ Step 3: Vendor Adds DNS Record
▼
┌────────────────────────────────────┐
│ DNS Provider (GoDaddy/etc) │
-│ _letzshop-verify.myshop.com TXT │
+│ _wizamart-verify.myshop.com TXT │
│ "abc123..." │
└────────────────────────────────────┘
diff --git a/docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md
similarity index 99%
rename from docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md
rename to docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md
index 5ad19b5f..604d94e5 100644
--- a/docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md
+++ b/docs/__REVAMPING/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md
@@ -356,14 +356,14 @@ GET /api/v1/admin/vendors/domains/1/verification-instructions
},
"txt_record": {
"type": "TXT",
- "name": "_letzshop-verify",
+ "name": "_wizamart-verify",
"value": "abc123xyz...",
"ttl": 3600
}
}
# Step 2: Vendor adds DNS record
-# _letzshop-verify.myshop.com TXT "abc123xyz..."
+# _wizamart-verify.myshop.com TXT "abc123xyz..."
# Step 3: Verify domain
POST /api/v1/admin/vendors/domains/1/verify
diff --git a/docs/__temp/__PROJECT_ROADMAP/19_migration_plan_FINAL.md b/docs/__REVAMPING/__PROJECT_ROADMAP/19_migration_plan_FINAL.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/19_migration_plan_FINAL.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/19_migration_plan_FINAL.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/FRONTEND_DOCUMENTATION_PLAN.md b/docs/__REVAMPING/__PROJECT_ROADMAP/FRONTEND_DOCUMENTATION_PLAN.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/FRONTEND_DOCUMENTATION_PLAN.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/FRONTEND_DOCUMENTATION_PLAN.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/15.web-architecture-revamping.md b/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/15.web-architecture-revamping.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/15.web-architecture-revamping.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/15.web-architecture-revamping.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-2.md b/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-2.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-2.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-2.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-3.md b/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-3.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-3.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-3.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress.md b/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/ROUTE_MIGRATION_SUMMARY.txt b/docs/__REVAMPING/__PROJECT_ROADMAP/ROUTE_MIGRATION_SUMMARY.txt
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/ROUTE_MIGRATION_SUMMARY.txt
rename to docs/__REVAMPING/__PROJECT_ROADMAP/ROUTE_MIGRATION_SUMMARY.txt
diff --git a/docs/__temp/__PROJECT_ROADMAP/implementation_roadmap.md b/docs/__REVAMPING/__PROJECT_ROADMAP/implementation_roadmap.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/implementation_roadmap.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/implementation_roadmap.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/slice1_doc.md b/docs/__REVAMPING/__PROJECT_ROADMAP/slice1_doc.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/slice1_doc.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/slice1_doc.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/slice2_doc.md b/docs/__REVAMPING/__PROJECT_ROADMAP/slice2_doc.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/slice2_doc.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/slice2_doc.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/slice3_doc.md b/docs/__REVAMPING/__PROJECT_ROADMAP/slice3_doc.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/slice3_doc.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/slice3_doc.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/slice4_doc.md b/docs/__REVAMPING/__PROJECT_ROADMAP/slice4_doc.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/slice4_doc.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/slice4_doc.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/slice5_doc.md b/docs/__REVAMPING/__PROJECT_ROADMAP/slice5_doc.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/slice5_doc.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/slice5_doc.md
diff --git a/docs/__temp/__PROJECT_ROADMAP/slice_overview.md b/docs/__REVAMPING/__PROJECT_ROADMAP/slice_overview.md
similarity index 100%
rename from docs/__temp/__PROJECT_ROADMAP/slice_overview.md
rename to docs/__REVAMPING/__PROJECT_ROADMAP/slice_overview.md
diff --git a/docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md b/docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md
deleted file mode 100644
index 245a1340..00000000
--- a/docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md
+++ /dev/null
@@ -1,331 +0,0 @@
-# Alpine.js Page Template - Quick Reference
-
-## ✅ Correct Page Structure
-
-```javascript
-// static/admin/js/your-page.js
-
-// 1. Setup logging (optional but recommended)
-const YOUR_PAGE_LOG_LEVEL = 3;
-
-const yourPageLog = {
- error: (...args) => YOUR_PAGE_LOG_LEVEL >= 1 && console.error('❌ [YOUR_PAGE ERROR]', ...args),
- warn: (...args) => YOUR_PAGE_LOG_LEVEL >= 2 && console.warn('⚠️ [YOUR_PAGE WARN]', ...args),
- info: (...args) => YOUR_PAGE_LOG_LEVEL >= 3 && console.info('ℹ️ [YOUR_PAGE INFO]', ...args),
- debug: (...args) => YOUR_PAGE_LOG_LEVEL >= 4 && console.log('🔍 [YOUR_PAGE DEBUG]', ...args)
-};
-
-// 2. Create your Alpine.js component
-function yourPageComponent() {
- return {
- // ✅ CRITICAL: Inherit base layout functionality
- ...data(),
-
- // ✅ CRITICAL: Set page identifier
- currentPage: 'your-page',
-
- // Your page-specific state
- items: [],
- loading: false,
- error: null,
-
- // ✅ CRITICAL: Proper initialization with guard
- async init() {
- yourPageLog.info('=== YOUR PAGE INITIALIZING ===');
-
- // Prevent multiple initializations
- if (window._yourPageInitialized) {
- yourPageLog.warn('Page already initialized, skipping...');
- return;
- }
- window._yourPageInitialized = true;
-
- // Load your data
- await this.loadData();
-
- yourPageLog.info('=== YOUR PAGE INITIALIZATION COMPLETE ===');
- },
-
- // Your methods
- async loadData() {
- yourPageLog.info('Loading data...');
- this.loading = true;
- this.error = null;
-
- try {
- const startTime = Date.now();
- // ✅ CRITICAL: Use lowercase apiClient
- const response = await apiClient.get('/your/endpoint');
- const duration = Date.now() - startTime;
-
- this.items = response.items || [];
-
- yourPageLog.info(`Data loaded in ${duration}ms`, {
- count: this.items.length
- });
-
- } catch (error) {
- yourPageLog.error('Failed to load data:', error);
- this.error = error.message;
- Utils.showToast('Failed to load data', 'error');
- } finally {
- this.loading = false;
- }
- },
-
- // Format date helper (if needed)
- formatDate(dateString) {
- if (!dateString) return '-';
- return Utils.formatDate(dateString);
- },
-
- // Your other methods...
- };
-}
-
-yourPageLog.info('Your page module loaded');
-```
-
----
-
-## 🎯 Checklist for New Pages
-
-### HTML Template
-```jinja2
-{# app/templates/admin/your-page.html #}
-{% extends "admin/base.html" %}
-
-{% block title %}Your Page{% endblock %}
-
-{# ✅ CRITICAL: Link to your Alpine.js component #}
-{% block alpine_data %}yourPageComponent(){% endblock %}
-
-{% block content %}
-
-{% endblock %}
-
-{% block extra_scripts %}
-{# ✅ CRITICAL: Load your JavaScript file #}
-
-{% endblock %}
-```
-
-### JavaScript File Checklist
-
-- [ ] ✅ Logging setup (optional)
-- [ ] ✅ Function name matches `alpine_data` in template
-- [ ] ✅ `...data(),` at start of return object
-- [ ] ✅ `currentPage: 'your-page'` set
-- [ ] ✅ Initialization guard in `init()`
-- [ ] ✅ Use lowercase `apiClient` for API calls
-- [ ] ✅ Use your custom logger (not `Logger`)
-- [ ] ✅ Performance tracking with `Date.now()` (optional)
-- [ ] ✅ Module loaded log at end
-
----
-
-## ❌ Common Mistakes to Avoid
-
-### 1. Missing Base Inheritance
-```javascript
-// ❌ WRONG
-function myPage() {
- return {
- items: [],
- // Missing ...data()
- };
-}
-
-// ✅ CORRECT
-function myPage() {
- return {
- ...data(), // Must be first!
- items: [],
- };
-}
-```
-
-### 2. Wrong API Client Name
-```javascript
-// ❌ WRONG - Capital letters
-await ApiClient.get('/endpoint');
-await API_CLIENT.get('/endpoint');
-
-// ✅ CORRECT - lowercase
-await apiClient.get('/endpoint');
-```
-
-### 3. Missing Init Guard
-```javascript
-// ❌ WRONG
-async init() {
- await this.loadData();
-}
-
-// ✅ CORRECT
-async init() {
- if (window._myPageInitialized) return;
- window._myPageInitialized = true;
- await this.loadData();
-}
-```
-
-### 4. Missing currentPage
-```javascript
-// ❌ WRONG
-return {
- ...data(),
- items: [],
- // Missing currentPage
-};
-
-// ✅ CORRECT
-return {
- ...data(),
- currentPage: 'my-page', // Must set this!
- items: [],
-};
-```
-
----
-
-## 🔧 API Client Pattern
-
-### GET Request
-```javascript
-try {
- const response = await apiClient.get('/endpoint');
- this.data = response;
-} catch (error) {
- console.error('Failed:', error);
- Utils.showToast('Failed to load', 'error');
-}
-```
-
-### POST Request
-```javascript
-try {
- const response = await apiClient.post('/endpoint', {
- name: 'value',
- // ... data
- });
- Utils.showToast('Created successfully', 'success');
-} catch (error) {
- console.error('Failed:', error);
- Utils.showToast('Failed to create', 'error');
-}
-```
-
-### PUT Request
-```javascript
-try {
- const response = await apiClient.put('/endpoint/123', {
- name: 'updated value'
- });
- Utils.showToast('Updated successfully', 'success');
-} catch (error) {
- console.error('Failed:', error);
- Utils.showToast('Failed to update', 'error');
-}
-```
-
-### DELETE Request
-```javascript
-try {
- await apiClient.delete('/endpoint/123');
- Utils.showToast('Deleted successfully', 'success');
- await this.reloadData();
-} catch (error) {
- console.error('Failed:', error);
- Utils.showToast('Failed to delete', 'error');
-}
-```
-
----
-
-## 🎨 Common UI Patterns
-
-### Loading State
-```javascript
-async loadData() {
- this.loading = true;
- try {
- const data = await apiClient.get('/endpoint');
- this.items = data;
- } finally {
- this.loading = false;
- }
-}
-```
-
-### Error Handling
-```javascript
-async loadData() {
- this.loading = true;
- this.error = null;
- try {
- const data = await apiClient.get('/endpoint');
- this.items = data;
- } catch (error) {
- this.error = error.message;
- Utils.showToast('Failed to load', 'error');
- } finally {
- this.loading = false;
- }
-}
-```
-
-### Refresh/Reload
-```javascript
-async refresh() {
- console.info('Refreshing...');
- await this.loadData();
- Utils.showToast('Refreshed successfully', 'success');
-}
-```
-
----
-
-## 📚 Available Utilities
-
-### From `init-alpine.js` (via `...data()`)
-- `this.dark` - Dark mode state
-- `this.toggleTheme()` - Toggle theme
-- `this.isSideMenuOpen` - Side menu state
-- `this.toggleSideMenu()` - Toggle side menu
-- `this.closeSideMenu()` - Close side menu
-- `this.isNotificationsMenuOpen` - Notifications menu state
-- `this.toggleNotificationsMenu()` - Toggle notifications
-- `this.closeNotificationsMenu()` - Close notifications
-- `this.isProfileMenuOpen` - Profile menu state
-- `this.toggleProfileMenu()` - Toggle profile menu
-- `this.closeProfileMenu()` - Close profile menu
-- `this.isPagesMenuOpen` - Pages menu state
-- `this.togglePagesMenu()` - Toggle pages menu
-
-### From `Utils` (global)
-- `Utils.showToast(message, type, duration)` - Show toast notification
-- `Utils.formatDate(dateString)` - Format date for display
-- `Utils.confirm(message, title)` - Show confirmation dialog (if available)
-
-### From `apiClient` (global)
-- `apiClient.get(url)` - GET request
-- `apiClient.post(url, data)` - POST request
-- `apiClient.put(url, data)` - PUT request
-- `apiClient.delete(url)` - DELETE request
-
----
-
-## 🚀 Quick Start Template Files
-
-Copy these to create a new page:
-
-1. Copy `dashboard.js` → rename to `your-page.js`
-2. Replace function name: `adminDashboard()` → `yourPageComponent()`
-3. Update logging prefix: `dashLog` → `yourPageLog`
-4. Update init flag: `_dashboardInitialized` → `_yourPageInitialized`
-5. Update `currentPage`: `'dashboard'` → `'your-page'`
-6. Replace data loading logic with your endpoints
-7. Update HTML template to use your function name
-
-Done! ✅
diff --git a/docs/getting-started/DATABASE_QUICK_REFERENCE.md b/docs/getting-started/DATABASE_QUICK_REFERENCE.md
new file mode 100644
index 00000000..5776358b
--- /dev/null
+++ b/docs/getting-started/DATABASE_QUICK_REFERENCE.md
@@ -0,0 +1,235 @@
+# Database Quick Reference Card
+
+Quick reference for common database operations. See `DATABASE_SETUP_GUIDE.md` for detailed instructions.
+
+---
+
+## 🚀 Quick Start (New Developer)
+
+```bash
+# 1. Clone and setup
+git clone
+cd letzshop
+python -m venv venv
+venv\Scripts\activate # Windows
+pip install -r requirements.txt
+
+# 2. Initialize database
+alembic upgrade head
+python scripts/seed_database.py
+
+# 3. Start server
+python -m uvicorn app.main:app --reload
+
+# 4. Login
+# http://localhost:8000/admin/login
+# Username: admin | Password: admin123
+```
+
+---
+
+## 🔄 Reset Database (Clean Slate)
+
+**Windows:**
+```powershell
+.\scripts\reset_database.ps1
+```
+
+**Linux/Mac:**
+```bash
+./scripts/reset_database.sh
+```
+
+**Manual:**
+```bash
+# 1. Delete migrations
+Remove-Item alembic\versions\*.py -Exclude __init__.py
+
+# 2. Delete database
+Remove-Item wizamart.db
+
+# 3. Regenerate
+alembic revision --autogenerate -m "Initial migration"
+alembic upgrade head
+python scripts/seed_database.py
+```
+
+---
+
+## 📝 Creating Migrations
+
+```bash
+# After modifying models in models/database/
+
+# 1. Generate migration
+alembic revision --autogenerate -m "Add field to vendor_themes"
+
+# 2. Review the file
+# Check: alembic/versions/_add_field_to_vendor_themes.py
+
+# 3. Apply migration
+alembic upgrade head
+```
+
+---
+
+## 🔍 Checking Status
+
+```bash
+# Current migration version
+alembic current
+
+# Migration history
+alembic history --verbose
+
+# List all tables
+python -c "import sqlite3; conn = sqlite3.connect('wizamart.db'); cursor = conn.cursor(); cursor.execute('SELECT name FROM sqlite_master WHERE type=\"table\" ORDER BY name;'); [print(t[0]) for t in cursor.fetchall()]"
+
+# Check specific table
+python -c "import sqlite3; conn = sqlite3.connect('wizamart.db'); cursor = conn.cursor(); cursor.execute('PRAGMA table_info(vendor_themes)'); [print(col) for col in cursor.fetchall()]"
+```
+
+---
+
+## ⬆️ Upgrading Database
+
+```bash
+# Upgrade to latest
+alembic upgrade head
+
+# Upgrade one version
+alembic upgrade +1
+
+# Upgrade to specific revision
+alembic upgrade
+```
+
+---
+
+## ⬇️ Rolling Back
+
+```bash
+# Downgrade one version
+alembic downgrade -1
+
+# Downgrade to specific revision
+alembic downgrade
+
+# Downgrade to empty database
+alembic downgrade base
+```
+
+---
+
+## 🌱 Seeding Data
+
+```bash
+# Seed development data
+python scripts/seed_database.py
+
+# Manual seed (Python shell)
+python
+>>> from scripts.seed_database import seed_database
+>>> seed_database()
+```
+
+---
+
+## 🐛 Common Fixes
+
+### "No such table" Error
+```bash
+# Check model imported in alembic/env.py
+# Then regenerate:
+alembic revision --autogenerate -m "Add missing table"
+alembic upgrade head
+```
+
+### "Database locked"
+```bash
+# Stop server
+taskkill /F /IM python.exe # Windows
+pkill python # Linux/Mac
+
+# Try again
+alembic upgrade head
+```
+
+### "Target database not up to date"
+```bash
+alembic stamp head
+alembic upgrade head
+```
+
+### "Multiple head revisions"
+```bash
+alembic merge heads -m "Merge migrations"
+alembic upgrade head
+```
+
+---
+
+## 📦 Adding New Models
+
+**Checklist:**
+
+```bash
+# 1. Create model file
+# models/database/my_model.py
+
+# 2. Import in alembic/env.py
+# from models.database.my_model import MyModel
+
+# 3. Generate migration
+alembic revision --autogenerate -m "Add MyModel table"
+
+# 4. Review migration file
+# alembic/versions/_add_mymodel_table.py
+
+# 5. Apply migration
+alembic upgrade head
+
+# 6. Test
+python -c "from models.database.my_model import MyModel; print('✓ Import works')"
+```
+
+---
+
+## 🔗 Important Files
+
+```
+├── alembic/
+│ ├── env.py ← Import ALL models here!
+│ └── versions/ ← Migration files
+├── app/core/
+│ └── database.py ← Database connection
+├── models/database/ ← SQLAlchemy models
+├── scripts/
+│ ├── seed_database.py ← Seed script
+│ └── reset_database.ps1 ← Reset script
+├── alembic.ini ← Alembic config
+└── wizamart.db ← SQLite database
+```
+
+---
+
+## 🆘 Getting Help
+
+1. Check full guide: `docs/DATABASE_SETUP_GUIDE.md`
+2. Check Alembic docs: https://alembic.sqlalchemy.org/
+3. Ask team lead
+
+---
+
+## ⚠️ Important Notes
+
+- ✅ Always review migrations before applying
+- ✅ Test on local database first
+- ✅ Keep migrations in version control
+- ✅ Backup before destructive operations
+- ❌ Never edit applied migrations
+- ❌ Never delete migration files
+
+---
+
+**Last Updated:** 2025-10-27
diff --git a/docs/getting-started/DATABASE_SETUP_GUIDE.md b/docs/getting-started/DATABASE_SETUP_GUIDE.md
new file mode 100644
index 00000000..4456e4e3
--- /dev/null
+++ b/docs/getting-started/DATABASE_SETUP_GUIDE.md
@@ -0,0 +1,883 @@
+# Database Setup & Initialization Guide
+
+## Overview
+
+This guide walks you through setting up the LetzShop database from scratch. Whether you're a new developer joining the team or need to reset your local database, this document covers everything you need to know.
+
+---
+
+## Table of Contents
+
+1. [Prerequisites](#prerequisites)
+2. [First-Time Setup](#first-time-setup)
+3. [Database Reset (Clean Slate)](#database-reset-clean-slate)
+4. [Verifying Your Setup](#verifying-your-setup)
+5. [Common Issues & Solutions](#common-issues--solutions)
+6. [Database Migrations](#database-migrations)
+7. [Seeding Data](#seeding-data)
+
+---
+
+## Prerequisites
+
+### Required Software
+
+- **Python 3.11+**
+- **Alembic** (installed via `requirements.txt`)
+- **SQLite** (built into Python) or **PostgreSQL** (for production-like setup)
+
+### Environment Setup
+
+```bash
+# 1. Clone the repository
+git clone
+cd letzshop
+
+# 2. Create virtual environment
+python -m venv venv
+
+# 3. Activate virtual environment
+# Windows:
+venv\Scripts\activate
+# Linux/Mac:
+source venv/bin/activate
+
+# 4. Install dependencies
+pip install -r requirements.txt
+```
+
+---
+
+## First-Time Setup
+
+### Step 1: Configure Database Connection
+
+Create or update `.env` file in project root:
+
+```env
+# Database Configuration
+DATABASE_URL=sqlite:///./wizamart.db
+
+# For PostgreSQL (production):
+# DATABASE_URL=postgresql://user:password@localhost:5432/letzshop
+
+# Other required settings
+SECRET_KEY=your-secret-key-here-change-in-production
+ALGORITHM=HS256
+ACCESS_TOKEN_EXPIRE_MINUTES=30
+```
+
+---
+
+### Step 2: Initialize Alembic (First Time Only)
+
+**If `alembic/` folder doesn't exist:**
+
+```bash
+# Initialize Alembic
+alembic init alembic
+```
+
+**Configure `alembic.ini`:**
+
+```ini
+# alembic.ini
+# Find this line and update:
+sqlalchemy.url = sqlite:///./wizamart.db
+
+# Or for PostgreSQL:
+# sqlalchemy.url = postgresql://user:password@localhost:5432/letzshop
+```
+
+**Configure `alembic/env.py`:**
+
+```python
+# alembic/env.py
+
+# Add this near the top (after imports)
+import sys
+from pathlib import Path
+
+# Add project root to Python path
+sys.path.append(str(Path(__file__).parents[1]))
+
+# ============================================================================
+# CRITICAL: Import ALL database models here
+# ============================================================================
+from models.database.user import User
+from models.database.vendor import Vendor
+from models.database.vendor_domain import VendorDomain
+from models.database.vendor_theme import VendorTheme
+from models.database.customer import Customer
+from models.database.team import Team, TeamMember, TeamInvitation
+from models.database.product import Product
+from models.database.marketplace_product import MarketplaceProduct
+from models.database.inventory import Inventory
+from models.database.order import Order
+from models.database.marketplace_import_job import MarketplaceImportJob
+
+# Import Base
+from app.core.database import Base
+
+# Set target metadata
+target_metadata = Base.metadata
+```
+
+---
+
+### Step 3: Generate Initial Migration
+
+```bash
+# Generate migration from your models
+alembic revision --autogenerate -m "Initial migration - all tables"
+
+# You should see output listing all detected tables:
+# INFO [alembic.autogenerate.compare] Detected added table 'users'
+# INFO [alembic.autogenerate.compare] Detected added table 'vendors'
+# INFO [alembic.autogenerate.compare] Detected added table 'vendor_themes'
+# etc.
+```
+
+---
+
+### Step 4: Apply Migration
+
+```bash
+# Apply the migration to create all tables
+alembic upgrade head
+
+# You should see:
+# INFO [alembic.runtime.migration] Running upgrade -> abc123def456, Initial migration - all tables
+```
+
+---
+
+### Step 5: Seed Initial Data
+
+Create an admin user and test vendor:
+
+```bash
+python scripts/seed_database.py
+```
+
+**Or manually:**
+
+```python
+# Run Python interactive shell
+python
+
+# Then execute:
+from app.core.database import SessionLocal
+from models.database.user import User
+from models.database.vendor import Vendor
+from middleware.auth import AuthManager
+from datetime import datetime, timezone
+
+db = SessionLocal()
+auth_manager = AuthManager()
+
+# Create admin user
+admin = User(
+ username="admin",
+ email="admin@letzshop.com",
+ hashed_password=auth_manager.hash_password("admin123"),
+ role="admin",
+ is_active=True,
+ created_at=datetime.now(timezone.utc),
+ updated_at=datetime.now(timezone.utc)
+)
+db.add(admin)
+db.flush()
+
+# Create test vendor
+vendor = Vendor(
+ vendor_code="TESTVENDOR",
+ subdomain="testvendor",
+ name="Test Vendor",
+ description="Development test vendor",
+ owner_user_id=admin.id,
+ contact_email="contact@testvendor.com",
+ is_active=True,
+ is_verified=True,
+ created_at=datetime.now(timezone.utc),
+ updated_at=datetime.now(timezone.utc)
+)
+db.add(vendor)
+
+db.commit()
+print("✅ Admin user and test vendor created!")
+db.close()
+exit()
+```
+
+---
+
+### Step 6: Start the Application
+
+```bash
+# Start FastAPI server
+python -m uvicorn app.main:app --reload
+
+# Server should start on http://localhost:8000
+```
+
+**Test the setup:**
+- Admin login: http://localhost:8000/admin/login
+- Username: `admin`
+- Password: `admin123`
+
+---
+
+## Database Reset (Clean Slate)
+
+Use this when you want to completely reset your database and start fresh.
+
+⚠️ **WARNING:** This will delete ALL data! Make a backup if needed.
+
+---
+
+### Quick Reset Script (Windows PowerShell)
+
+**Create `scripts/reset_database.ps1`:**
+
+```powershell
+# scripts/reset_database.ps1
+Write-Host "🗑️ Database Reset Script" -ForegroundColor Cyan
+Write-Host "This will delete ALL data!" -ForegroundColor Red
+$confirm = Read-Host "Type 'YES' to continue"
+
+if ($confirm -ne "YES") {
+ Write-Host "Aborted." -ForegroundColor Yellow
+ exit
+}
+
+Write-Host ""
+Write-Host "Step 1: Backing up current database..." -ForegroundColor Yellow
+if (Test-Path wizamart.db) {
+ $backupName = "wizamart_backup_$(Get-Date -Format 'yyyyMMdd_HHmmss').db"
+ Copy-Item wizamart.db $backupName
+ Write-Host "✓ Backup created: $backupName" -ForegroundColor Green
+}
+
+Write-Host ""
+Write-Host "Step 2: Removing old migrations..." -ForegroundColor Yellow
+Remove-Item alembic\versions\*.py -Exclude __init__.py -ErrorAction SilentlyContinue
+Remove-Item -Recurse -Force alembic\versions\__pycache__ -ErrorAction SilentlyContinue
+
+# Ensure __init__.py exists
+if (-not (Test-Path alembic\versions\__init__.py)) {
+ New-Item -Path alembic\versions\__init__.py -ItemType File | Out-Null
+}
+Write-Host "✓ Old migrations removed" -ForegroundColor Green
+
+Write-Host ""
+Write-Host "Step 3: Deleting database..." -ForegroundColor Yellow
+Remove-Item wizamart.db -ErrorAction SilentlyContinue
+Write-Host "✓ Database deleted" -ForegroundColor Green
+
+Write-Host ""
+Write-Host "Step 4: Generating new migration..." -ForegroundColor Yellow
+alembic revision --autogenerate -m "Initial migration - all tables"
+Write-Host "✓ Migration generated" -ForegroundColor Green
+
+Write-Host ""
+Write-Host "Step 5: Applying migration..." -ForegroundColor Yellow
+alembic upgrade head
+Write-Host "✓ Migration applied" -ForegroundColor Green
+
+Write-Host ""
+Write-Host "Step 6: Verifying tables..." -ForegroundColor Yellow
+python -c @"
+import sqlite3
+conn = sqlite3.connect('wizamart.db')
+cursor = conn.cursor()
+cursor.execute('SELECT name FROM sqlite_master WHERE type=\"table\" ORDER BY name;')
+tables = [t[0] for t in cursor.fetchall()]
+print('\n📊 Tables created:')
+for t in tables:
+ print(f' ✓ {t}')
+print(f'\n✅ Total: {len(tables)} tables')
+if 'vendor_themes' in tables:
+ print('✅ vendor_themes table confirmed!')
+conn.close()
+"@
+
+Write-Host ""
+Write-Host "Step 7: Seeding initial data..." -ForegroundColor Yellow
+python scripts/seed_database.py
+
+Write-Host ""
+Write-Host "✅ Database reset complete!" -ForegroundColor Green
+Write-Host ""
+Write-Host "Next steps:" -ForegroundColor Cyan
+Write-Host " 1. Start server: python -m uvicorn app.main:app --reload" -ForegroundColor White
+Write-Host " 2. Login: http://localhost:8000/admin/login" -ForegroundColor White
+Write-Host " 3. Username: admin | Password: admin123" -ForegroundColor White
+```
+
+**Run it:**
+
+```powershell
+# First time only - allow script execution
+Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+# Run the reset
+.\scripts\reset_database.ps1
+```
+
+---
+
+### Quick Reset Script (Linux/Mac Bash)
+
+**Create `scripts/reset_database.sh`:**
+
+```bash
+#!/bin/bash
+# scripts/reset_database.sh
+
+echo "🗑️ Database Reset Script"
+echo "⚠️ This will delete ALL data!"
+read -p "Type 'YES' to continue: " confirm
+
+if [ "$confirm" != "YES" ]; then
+ echo "Aborted."
+ exit 1
+fi
+
+echo ""
+echo "Step 1: Backing up current database..."
+if [ -f wizamart.db ]; then
+ backup_name="wizamart_backup_$(date +%Y%m%d_%H%M%S).db"
+ cp wizamart.db "$backup_name"
+ echo "✓ Backup created: $backup_name"
+fi
+
+echo ""
+echo "Step 2: Removing old migrations..."
+find alembic/versions -type f -name "*.py" ! -name "__init__.py" -delete
+rm -rf alembic/versions/__pycache__
+touch alembic/versions/__init__.py
+echo "✓ Old migrations removed"
+
+echo ""
+echo "Step 3: Deleting database..."
+rm -f wizamart.db
+echo "✓ Database deleted"
+
+echo ""
+echo "Step 4: Generating new migration..."
+alembic revision --autogenerate -m "Initial migration - all tables"
+echo "✓ Migration generated"
+
+echo ""
+echo "Step 5: Applying migration..."
+alembic upgrade head
+echo "✓ Migration applied"
+
+echo ""
+echo "Step 6: Verifying tables..."
+python -c "
+import sqlite3
+conn = sqlite3.connect('wizamart.db')
+cursor = conn.cursor()
+cursor.execute('SELECT name FROM sqlite_master WHERE type=\"table\" ORDER BY name;')
+tables = [t[0] for t in cursor.fetchall()]
+print('\n📊 Tables created:')
+for t in tables:
+ print(f' ✓ {t}')
+print(f'\n✅ Total: {len(tables)} tables')
+if 'vendor_themes' in tables:
+ print('✅ vendor_themes table confirmed!')
+conn.close()
+"
+
+echo ""
+echo "Step 7: Seeding initial data..."
+python scripts/seed_database.py
+
+echo ""
+echo "✅ Database reset complete!"
+echo ""
+echo "Next steps:"
+echo " 1. Start server: python -m uvicorn app.main:app --reload"
+echo " 2. Login: http://localhost:8000/admin/login"
+echo " 3. Username: admin | Password: admin123"
+```
+
+**Run it:**
+
+```bash
+chmod +x scripts/reset_database.sh
+./scripts/reset_database.sh
+```
+
+---
+
+### Manual Reset Steps
+
+If you prefer to run commands manually:
+
+```bash
+# 1. Backup (optional)
+cp wizamart.db wizamart_backup.db # Windows: copy wizamart.db wizamart_backup.db
+
+# 2. Remove migrations
+rm alembic/versions/*.py # Windows: Remove-Item alembic\versions\*.py -Exclude __init__.py
+touch alembic/versions/__init__.py # Windows: New-Item -Path alembic\versions\__init__.py
+
+# 3. Delete database
+rm wizamart.db # Windows: Remove-Item wizamart.db
+
+# 4. Generate migration
+alembic revision --autogenerate -m "Initial migration - all tables"
+
+# 5. Apply migration
+alembic upgrade head
+
+# 6. Seed data
+python scripts/seed_database.py
+```
+
+---
+
+## Verifying Your Setup
+
+### Check Alembic Status
+
+```bash
+# Check current migration version
+alembic current
+
+# Should show:
+# (head)
+```
+
+### Verify Tables Exist
+
+**Using Python:**
+
+```python
+import sqlite3
+
+conn = sqlite3.connect('wizamart.db')
+cursor = conn.cursor()
+
+# List all tables
+cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;")
+tables = cursor.fetchall()
+
+print("📊 Database Tables:")
+for table in tables:
+ print(f" ✓ {table[0]}")
+
+# Check specific table structure
+cursor.execute("PRAGMA table_info(vendor_themes);")
+columns = cursor.fetchall()
+
+print("\n📋 vendor_themes columns:")
+for col in columns:
+ print(f" - {col[1]} ({col[2]})")
+
+conn.close()
+```
+
+### Test Database Connection
+
+```bash
+# Start Python shell
+python
+
+# Test connection
+from app.core.database import SessionLocal, engine
+from sqlalchemy import text
+
+db = SessionLocal()
+result = db.execute(text("SELECT name FROM sqlite_master WHERE type='table';"))
+tables = [row[0] for row in result]
+
+print("✅ Database connection successful!")
+print(f"📊 Tables found: {len(tables)}")
+for table in tables:
+ print(f" - {table}")
+
+db.close()
+exit()
+```
+
+### Test Application Startup
+
+```bash
+# Start server
+python -m uvicorn app.main:app --reload
+
+# Should see:
+# INFO: Uvicorn running on http://127.0.0.1:8000
+# INFO: Application startup complete.
+```
+
+**No errors = successful setup!**
+
+---
+
+## Common Issues & Solutions
+
+### Issue 1: "No such table" Error
+
+**Symptom:**
+```
+sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: vendor_themes
+```
+
+**Solution:**
+
+```bash
+# 1. Check if model is imported in alembic/env.py
+# Open alembic/env.py and verify:
+from models.database.vendor_theme import VendorTheme
+
+# 2. Regenerate migration
+alembic revision --autogenerate -m "Add missing tables"
+
+# 3. Apply migration
+alembic upgrade head
+```
+
+---
+
+### Issue 2: "Target database is not up to date"
+
+**Symptom:**
+```
+ERROR [alembic.util.messaging] Target database is not up to date.
+```
+
+**Solution:**
+
+```bash
+# Option 1: Stamp to current head
+alembic stamp head
+
+# Option 2: Check and upgrade
+alembic current # See current version
+alembic upgrade head # Upgrade to latest
+
+# Option 3: Full reset (see Database Reset section)
+```
+
+---
+
+### Issue 3: Database Locked
+
+**Symptom:**
+```
+sqlite3.OperationalError: database is locked
+```
+
+**Solution:**
+
+```bash
+# 1. Stop the FastAPI server (Ctrl+C)
+
+# 2. Kill any Python processes
+# Windows:
+taskkill /F /IM python.exe
+
+# Linux/Mac:
+pkill python
+
+# 3. Close any database browser applications
+
+# 4. Try again
+alembic upgrade head
+```
+
+---
+
+### Issue 4: Migration File Conflicts
+
+**Symptom:**
+```
+Multiple head revisions are present
+```
+
+**Solution:**
+
+```bash
+# Option 1: Merge migrations
+alembic merge heads -m "Merge migrations"
+alembic upgrade head
+
+# Option 2: Clean reset (recommended for development)
+# See Database Reset section above
+```
+
+---
+
+### Issue 5: Import Errors in alembic/env.py
+
+**Symptom:**
+```
+ModuleNotFoundError: No module named 'models'
+```
+
+**Solution:**
+
+Check `alembic/env.py` has correct path setup:
+
+```python
+# alembic/env.py (top of file)
+import sys
+from pathlib import Path
+
+# Add project root to Python path
+sys.path.append(str(Path(__file__).parents[1]))
+```
+
+---
+
+## Database Migrations
+
+### Creating New Migrations
+
+When you add/modify models:
+
+```bash
+# 1. Update your model file (e.g., models/database/vendor_theme.py)
+
+# 2. Generate migration
+alembic revision --autogenerate -m "Add new field to vendor_themes"
+
+# 3. Review the generated migration
+# Check: alembic/versions/_add_new_field_to_vendor_themes.py
+
+# 4. Apply migration
+alembic upgrade head
+```
+
+---
+
+### Migration Best Practices
+
+✅ **DO:**
+- Review generated migrations before applying
+- Use descriptive migration messages
+- Test migrations on development database first
+- Keep migrations small and focused
+- Commit migrations to version control
+
+❌ **DON'T:**
+- Edit applied migrations (create new ones instead)
+- Delete migration files from version control
+- Skip migration review
+- Combine unrelated changes in one migration
+
+---
+
+### Rolling Back Migrations
+
+```bash
+# Downgrade one version
+alembic downgrade -1
+
+# Downgrade to specific revision
+alembic downgrade
+
+# Downgrade to base (empty database)
+alembic downgrade base
+```
+
+---
+
+## Seeding Data
+
+### Development Data Seed
+
+**Create `scripts/seed_database.py`:**
+
+```python
+# scripts/seed_database.py
+"""
+Seed the database with initial development data.
+"""
+
+from app.core.database import SessionLocal
+from models.database.user import User
+from models.database.vendor import Vendor
+from app.core.security import get_password_hash
+from datetime import datetime, timezone
+
+
+def seed_database():
+ """Seed database with initial data."""
+ db = SessionLocal()
+
+ try:
+ print("🌱 Seeding database...")
+
+ # Check if admin already exists
+ existing_admin = db.query(User).filter(User.username == "admin").first()
+ if existing_admin:
+ print("⚠️ Admin user already exists, skipping...")
+ return
+
+ # Create admin user
+ admin = User(
+ username="admin",
+ email="admin@letzshop.com",
+ hashed_password=get_password_hash("admin123"),
+ is_admin=True,
+ is_active=True,
+ created_at=datetime.now(timezone.utc),
+ updated_at=datetime.now(timezone.utc)
+ )
+ db.add(admin)
+ db.flush()
+ print(f"✓ Admin user created (ID: {admin.id})")
+
+ # Create test vendor
+ vendor = Vendor(
+ vendor_code="TESTVENDOR",
+ subdomain="testvendor",
+ name="Test Vendor",
+ description="Development test vendor",
+ owner_user_id=admin.id,
+ contact_email="contact@testvendor.com",
+ is_active=True,
+ is_verified=True,
+ created_at=datetime.now(timezone.utc),
+ updated_at=datetime.now(timezone.utc)
+ )
+ db.add(vendor)
+ db.flush()
+ print(f"✓ Test vendor created: {vendor.vendor_code}")
+
+ db.commit()
+
+ print("\n✅ Database seeded successfully!")
+ print("\n📝 Login Credentials:")
+ print(" URL: http://localhost:8000/admin/login")
+ print(" Username: admin")
+ print(" Password: admin123")
+
+ except Exception as e:
+ db.rollback()
+ print(f"❌ Error seeding database: {e}")
+ raise
+ finally:
+ db.close()
+
+
+if __name__ == "__main__":
+ seed_database()
+```
+
+**Run it:**
+
+```bash
+python scripts/seed_database.py
+```
+
+---
+
+### Production Data Migration
+
+For production, create separate seed scripts:
+
+```python
+# scripts/seed_production.py
+# - Create only essential admin user
+# - No test data
+# - Strong passwords from environment variables
+```
+
+---
+
+## Quick Reference
+
+### Essential Commands
+
+```bash
+# Check migration status
+alembic current
+
+# Generate new migration
+alembic revision --autogenerate -m "Description"
+
+# Apply migrations
+alembic upgrade head
+
+# Rollback one migration
+alembic downgrade -1
+
+# View migration history
+alembic history
+
+# Seed database
+python scripts/seed_database.py
+
+# Start server
+python -m uvicorn app.main:app --reload
+```
+
+---
+
+### File Locations
+
+```
+project/
+├── alembic/
+│ ├── versions/ # Migration files here
+│ │ └── __init__.py
+│ ├── env.py # Alembic configuration (import models here!)
+│ └── script.py.mako
+├── app/
+│ └── core/
+│ └── database.py # Database connection
+├── models/
+│ └── database/ # SQLAlchemy models
+│ ├── user.py
+│ ├── vendor.py
+│ ├── vendor_theme.py
+│ └── ...
+├── scripts/
+│ ├── seed_database.py # Development seed
+│ └── reset_database.ps1 # Reset script
+├── alembic.ini # Alembic config
+├── wizamart.db # SQLite database (gitignored)
+└── .env # Environment variables (gitignored)
+```
+
+---
+
+## Getting Help
+
+### Internal Resources
+
+- **Architecture Guide:** `docs/PROPER_ARCHITECTURE_GUIDE.md`
+- **Exception Handling:** `docs/EXCEPTION_PATTERN_EXPLAINED.md`
+- **Admin Feature Guide:** `docs/ADMIN_FEATURE_INTEGRATION_GUIDE.md`
+
+### External Resources
+
+- [Alembic Documentation](https://alembic.sqlalchemy.org/)
+- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/)
+- [FastAPI Documentation](https://fastapi.tiangolo.com/)
+
+---
+
+## Contributing
+
+When adding new models:
+
+1. ✅ Create the model in `models/database/`
+2. ✅ Import it in `alembic/env.py`
+3. ✅ Generate migration: `alembic revision --autogenerate -m "Add XYZ model"`
+4. ✅ Review the migration file
+5. ✅ Test locally: `alembic upgrade head`
+6. ✅ Commit both model and migration files
+
+---
+
+**Last Updated:** 2025-10-27
+**Maintainer:** Development Team
+**Questions?** Contact the team lead or check internal documentation.
\ No newline at end of file
diff --git a/models/database/__init__.py b/models/database/__init__.py
index 12d2f464..24ed1b4e 100644
--- a/models/database/__init__.py
+++ b/models/database/__init__.py
@@ -1,29 +1,38 @@
# models/database/__init__.py
"""Database models package."""
-
+from .admin import AdminAuditLog, AdminNotification, AdminSetting, PlatformAlert, AdminSession
from .base import Base
-from .customer import Customer
-from .order import Order
+from .customer import Customer, CustomerAddress
+from .order import Order, OrderItem
from .user import User
from .marketplace_product import MarketplaceProduct
from .inventory import Inventory
-from .vendor import Vendor
+from .vendor import Vendor, Role, VendorUser
from .vendor_domain import VendorDomain
from .vendor_theme import VendorTheme
from .product import Product
from .marketplace_import_job import MarketplaceImportJob
__all__ = [
+ # Admin-specific models
+ "AdminAuditLog",
+ "AdminNotification",
+ "AdminSetting",
+ "PlatformAlert",
+ "AdminSession",
"Base",
"User",
- "MarketplaceProduct",
"Inventory",
"Customer",
+ "CustomerAddress",
"Order",
+ "OrderItem",
"Vendor",
+ "VendorUser",
+ "Role",
"Product",
"MarketplaceImportJob",
+ "MarketplaceProduct",
"VendorDomain",
"VendorTheme"
]
-
diff --git a/scripts/seed_database.py b/scripts/seed_database.py
new file mode 100644
index 00000000..4c07013e
--- /dev/null
+++ b/scripts/seed_database.py
@@ -0,0 +1,319 @@
+#!/usr/bin/env python3
+"""
+Seed the database with initial development data.
+
+Creates:
+- Admin user (admin/admin123)
+- Test vendors (TESTVENDOR, WIZAMART)
+
+Usage:
+ python scripts/seed_database.py
+
+This script is idempotent - safe to run multiple times.
+"""
+
+import sys
+from pathlib import Path
+
+# Add project root to path
+project_root = Path(__file__).parent.parent
+sys.path.insert(0, str(project_root))
+
+from sqlalchemy.orm import Session
+from sqlalchemy import select
+
+from app.core.database import SessionLocal, engine
+from models.database.user import User
+from models.database.vendor import Vendor
+from middleware.auth import AuthManager
+from datetime import datetime, timezone
+
+# Default credentials
+DEFAULT_ADMIN_EMAIL = "admin@letzshop.com"
+DEFAULT_ADMIN_USERNAME = "admin"
+DEFAULT_ADMIN_PASSWORD = "admin123" # Change in production!
+
+
+def verify_database_ready() -> bool:
+ """
+ Verify that database tables exist.
+
+ Returns:
+ bool: True if database is ready, False otherwise
+ """
+ try:
+ with engine.connect() as conn:
+ from sqlalchemy import text
+ # Check for users table
+ result = conn.execute(
+ text("SELECT name FROM sqlite_master WHERE type='table' AND name='users'")
+ )
+ users_table = result.fetchall()
+
+ # Check for vendors table
+ result = conn.execute(
+ text("SELECT name FROM sqlite_master WHERE type='table' AND name='vendors'")
+ )
+ vendors_table = result.fetchall()
+
+ return len(users_table) > 0 and len(vendors_table) > 0
+
+ except Exception as e:
+ print(f"❌ Error checking database: {e}")
+ return False
+
+
+def create_admin_user(db: Session, auth_manager: AuthManager) -> tuple[User, bool]:
+ """
+ Create admin user if it doesn't exist.
+
+ Args:
+ db: Database session
+ auth_manager: AuthManager instance for password hashing
+
+ Returns:
+ tuple: (User object, was_created boolean)
+ """
+ # Check if admin already exists
+ existing_admin = db.execute(
+ select(User).where(User.username == DEFAULT_ADMIN_USERNAME)
+ ).scalar_one_or_none()
+
+ if existing_admin:
+ return existing_admin, False
+
+ # Create new admin user
+ admin = User(
+ email=DEFAULT_ADMIN_EMAIL,
+ username=DEFAULT_ADMIN_USERNAME,
+ hashed_password=auth_manager.hash_password(DEFAULT_ADMIN_PASSWORD),
+ role="admin",
+ is_active=True,
+ created_at=datetime.now(timezone.utc),
+ updated_at=datetime.now(timezone.utc)
+ )
+
+ db.add(admin)
+ db.flush() # Get the ID
+
+ return admin, True
+
+
+def create_vendor(
+ db: Session,
+ vendor_code: str,
+ name: str,
+ subdomain: str,
+ owner_user_id: int,
+ description: str = None
+) -> tuple[Vendor, bool]:
+ """
+ Create vendor if it doesn't exist.
+
+ Args:
+ db: Database session
+ vendor_code: Unique vendor code
+ name: Vendor name
+ subdomain: Subdomain for the vendor
+ owner_user_id: ID of the owner user
+ description: Optional description
+
+ Returns:
+ tuple: (Vendor object, was_created boolean)
+ """
+ # Check if vendor already exists
+ existing_vendor = db.execute(
+ select(Vendor).where(Vendor.vendor_code == vendor_code)
+ ).scalar_one_or_none()
+
+ if existing_vendor:
+ return existing_vendor, False
+
+ # Create new vendor
+ vendor = Vendor(
+ vendor_code=vendor_code,
+ subdomain=subdomain,
+ name=name,
+ description=description or f"{name} - Development vendor",
+ owner_user_id=owner_user_id,
+ contact_email=f"contact@{subdomain}.com",
+ contact_phone="+352 123 456 789",
+ website=f"https://{subdomain}.com",
+ is_active=True,
+ is_verified=True,
+ created_at=datetime.now(timezone.utc),
+ updated_at=datetime.now(timezone.utc)
+ )
+
+ db.add(vendor)
+ db.flush()
+
+ return vendor, True
+
+
+def seed_database():
+ """Main seeding function."""
+
+ print("\n" + "╔" + "═" * 68 + "╗")
+ print("║" + " " * 18 + "DATABASE SEEDING SCRIPT" + " " * 27 + "║")
+ print("╚" + "═" * 68 + "╝\n")
+
+ # ========================================================================
+ # STEP 1: VERIFY DATABASE
+ # ========================================================================
+
+ print("STEP 1: Verifying database...")
+
+ if not verify_database_ready():
+ print("\n❌ ERROR: Database not ready!")
+ print("\n Required tables don't exist yet.")
+ print(" Please run database migrations first:\n")
+ print(" alembic upgrade head\n")
+ sys.exit(1)
+
+ print(" ✓ Database tables verified\n")
+
+ # ========================================================================
+ # STEP 2: CREATE ADMIN USER
+ # ========================================================================
+
+ db = SessionLocal()
+ auth_manager = AuthManager()
+
+ try:
+ print("STEP 2: Creating admin user...")
+
+ admin, admin_created = create_admin_user(db, auth_manager)
+
+ if admin_created:
+ print(f" ✓ Admin user created (ID: {admin.id})")
+ print(f" Username: {admin.username}")
+ print(f" Email: {admin.email}")
+ else:
+ print(f" ℹ️ Admin user already exists (ID: {admin.id})")
+ print(f" Username: {admin.username}")
+ print(f" Email: {admin.email}")
+
+ # ====================================================================
+ # STEP 3: CREATE VENDORS
+ # ====================================================================
+
+ print("\nSTEP 3: Creating vendors...")
+
+ # Create TESTVENDOR
+ test_vendor, test_created = create_vendor(
+ db,
+ vendor_code="TESTVENDOR",
+ name="Test Vendor",
+ subdomain="testvendor",
+ owner_user_id=admin.id,
+ description="Development test vendor for general testing"
+ )
+
+ if test_created:
+ print(f" ✓ TESTVENDOR created (ID: {test_vendor.id})")
+ else:
+ print(f" ℹ️ TESTVENDOR already exists (ID: {test_vendor.id})")
+
+ # Create WIZAMART
+ wizamart, wizamart_created = create_vendor(
+ db,
+ vendor_code="WIZAMART",
+ name="WizaMart",
+ subdomain="wizamart",
+ owner_user_id=admin.id,
+ description="Primary development vendor for theme customization"
+ )
+
+ if wizamart_created:
+ print(f" ✓ WIZAMART created (ID: {wizamart.id})")
+ else:
+ print(f" ℹ️ WIZAMART already exists (ID: {wizamart.id})")
+
+ # Commit all changes
+ db.commit()
+
+ # ====================================================================
+ # SUMMARY
+ # ====================================================================
+
+ print("\n" + "╔" + "═" * 68 + "╗")
+ print("║" + " " * 22 + "SEEDING COMPLETE!" + " " * 28 + "║")
+ print("╚" + "═" * 68 + "╝\n")
+
+ if admin_created or test_created or wizamart_created:
+ print("✅ New items created:")
+ if admin_created:
+ print(" • Admin user")
+ if test_created:
+ print(" • TESTVENDOR")
+ if wizamart_created:
+ print(" • WIZAMART")
+ else:
+ print("ℹ️ All items already existed - no changes made")
+
+ print("\n" + "─" * 70)
+ print("📝 ADMIN LOGIN CREDENTIALS")
+ print("─" * 70)
+ print(f" URL: http://localhost:8000/admin/login")
+ print(f" Username: {DEFAULT_ADMIN_USERNAME}")
+ print(f" Password: {DEFAULT_ADMIN_PASSWORD}")
+ print("─" * 70)
+
+ print("\n" + "─" * 70)
+ print("🏪 VENDORS")
+ print("─" * 70)
+
+ vendors = db.execute(select(Vendor)).scalars().all()
+ for v in vendors:
+ print(f"\n {v.vendor_code}")
+ print(f" Name: {v.name}")
+ print(f" Subdomain: {v.subdomain}.letzshop.com")
+ print(f" Theme URL: http://localhost:8000/admin/vendors/{v.vendor_code}/theme")
+ print(f" Verified: {'✓' if v.is_verified else '✗'}")
+ print(f" Active: {'✓' if v.is_active else '✗'}")
+
+ print("\n" + "─" * 70)
+ print("🚀 NEXT STEPS")
+ print("─" * 70)
+ print(" 1. Start the server:")
+ print(" python -m uvicorn app.main:app --reload\n")
+ print(" 2. Login to admin panel:")
+ print(" http://localhost:8000/admin/login\n")
+ print(" 3. Test theme editor:")
+ print(" http://localhost:8000/admin/vendors/WIZAMART/theme\n")
+
+ if admin_created:
+ print("⚠️ SECURITY: Change the default admin password after first login!")
+
+ print()
+
+ except Exception as e:
+ db.rollback()
+ print("\n" + "╔" + "═" * 68 + "╗")
+ print("║" + " " * 28 + "ERROR" + " " * 35 + "║")
+ print("╚" + "═" * 68 + "╝\n")
+ print(f"❌ Failed to seed database: {e}")
+ print(f"\nError type: {type(e).__name__}")
+ print("\nCommon issues:")
+ print(" - Database migrations not run (run: alembic upgrade head)")
+ print(" - Database connection issues")
+ print(" - Foreign key constraint violations")
+ print()
+ import traceback
+ traceback.print_exc()
+ sys.exit(1)
+
+ finally:
+ db.close()
+
+
+if __name__ == "__main__":
+ try:
+ seed_database()
+ except KeyboardInterrupt:
+ print("\n\n⚠️ Seeding interrupted by user")
+ sys.exit(1)
+ except Exception as e:
+ print(f"\n❌ Fatal error: {e}")
+ sys.exit(1)
diff --git a/static/admin/js/vendor-theme.js b/static/admin/js/vendor-theme.js
index 036f3c1e..ab896793 100644
--- a/static/admin/js/vendor-theme.js
+++ b/static/admin/js/vendor-theme.js
@@ -1,22 +1,17 @@
-// static/admin/js/vendor-theme.js
+// static/admin/js/vendor-theme.js (FIXED VERSION)
/**
* Vendor Theme Editor - Alpine.js Component
* Manages theme customization for vendor shops
+ *
+ * REQUIRES: log-config.js to be loaded first
*/
// ============================================================================
-// LOGGING CONFIGURATION
+// LOGGING CONFIGURATION (using centralized logger)
// ============================================================================
-const THEME_LOG_LEVEL = 3; // 1=error, 2=warn, 3=info, 4=debug
-
-const themeLog = {
- error: (...args) => THEME_LOG_LEVEL >= 1 && console.error('❌ [THEME ERROR]', ...args),
- warn: (...args) => THEME_LOG_LEVEL >= 2 && console.warn('⚠️ [THEME WARN]', ...args),
- info: (...args) => THEME_LOG_LEVEL >= 3 && console.info('ℹ️ [THEME INFO]', ...args),
- debug: (...args) => THEME_LOG_LEVEL >= 4 && console.log('🔍 [THEME DEBUG]', ...args)
-};
-
+// Use the pre-configured theme logger from centralized log-config.js
+const themeLog = window.LogConfig.loggers.vendorTheme;
// ============================================================================
// ALPINE.JS COMPONENT
@@ -50,254 +45,278 @@ function adminVendorTheme() {
},
fonts: {
heading: 'Inter, sans-serif',
- body: 'Inter, sans-serif'
+ body: 'Inter, sans-serif',
+ size_base: '16px',
+ size_heading: '2rem'
},
layout: {
style: 'grid',
- header: 'fixed',
- product_card: 'modern'
+ header_position: 'fixed',
+ product_card_style: 'card',
+ sidebar_position: 'left'
},
branding: {
- logo: null,
- logo_dark: null,
- favicon: null,
- banner: null
+ logo_url: '',
+ favicon_url: '',
+ banner_url: ''
},
- custom_css: ''
+ custom_css: '',
+ social_links: {
+ facebook: '',
+ instagram: '',
+ twitter: '',
+ linkedin: ''
+ }
},
// Available presets
presets: [],
+ selectedPreset: null,
- // ============================================================================
+ // ====================================================================
// INITIALIZATION
- // ============================================================================
+ // ====================================================================
async init() {
- themeLog.info('=== VENDOR THEME EDITOR INITIALIZING ===');
+ themeLog.info('Initializing vendor theme editor');
- // ✅ CRITICAL: Prevent multiple initializations
- if (window._vendorThemeInitialized) {
- themeLog.warn('Theme editor already initialized, skipping...');
- return;
- }
- window._vendorThemeInitialized = true;
-
- const startTime = Date.now();
-
- // Get vendor code from URL
- this.vendorCode = this.getVendorCodeFromURL();
- themeLog.info('Vendor code:', this.vendorCode);
-
- // Load data
- await Promise.all([
- this.loadVendorData(),
- this.loadTheme(),
- this.loadPresets()
- ]);
-
- const duration = Date.now() - startTime;
- themeLog.info(`=== THEME EDITOR INITIALIZATION COMPLETE (${duration}ms) ===`);
- },
-
- // ============================================================================
- // URL HELPERS
- // ============================================================================
-
- getVendorCodeFromURL() {
- const pathParts = window.location.pathname.split('/');
- const vendorIndex = pathParts.indexOf('vendors');
- return pathParts[vendorIndex + 1];
- },
-
- // ============================================================================
- // DATA LOADING
- // ============================================================================
-
- async loadVendorData() {
- themeLog.info('Loading vendor data...');
+ // Start performance timer
+ const startTime = performance.now();
try {
- const startTime = Date.now();
- const response = await apiClient.get(`/admin/vendors/${this.vendorCode}`);
- const duration = Date.now() - startTime;
+ // Extract vendor code from URL
+ const urlParts = window.location.pathname.split('/');
+ this.vendorCode = urlParts[urlParts.indexOf('vendors') + 1];
- this.vendor = response;
- themeLog.info(`Vendor loaded in ${duration}ms:`, this.vendor.name);
+ themeLog.debug('Vendor code from URL:', this.vendorCode);
- } catch (error) {
- themeLog.error('Failed to load vendor:', error);
- this.error = 'Failed to load vendor data';
- Utils.showToast('Failed to load vendor data', 'error');
- }
- },
-
- async loadTheme() {
- themeLog.info('Loading theme...');
- this.loading = true;
- this.error = null;
-
- try {
- const startTime = Date.now();
- const response = await apiClient.get(`/admin/vendor-themes/${this.vendorCode}`);
- const duration = Date.now() - startTime;
-
- if (response) {
- // Merge loaded theme with defaults
- this.themeData = {
- theme_name: response.theme_name || 'default',
- colors: {
- ...this.themeData.colors,
- ...(response.colors || {})
- },
- fonts: {
- heading: response.fonts?.heading || this.themeData.fonts.heading,
- body: response.fonts?.body || this.themeData.fonts.body
- },
- layout: {
- style: response.layout?.style || this.themeData.layout.style,
- header: response.layout?.header || this.themeData.layout.header,
- product_card: response.layout?.product_card || this.themeData.layout.product_card
- },
- branding: {
- ...this.themeData.branding,
- ...(response.branding || {})
- },
- custom_css: response.custom_css || ''
- };
-
- themeLog.info(`Theme loaded in ${duration}ms:`, this.themeData.theme_name);
+ if (!this.vendorCode) {
+ throw new Error('Vendor code not found in URL');
}
- } catch (error) {
- themeLog.error('Failed to load theme:', error);
- this.error = 'Failed to load theme';
- Utils.showToast('Failed to load theme', 'error');
+ // Load data in parallel
+ themeLog.group('Loading theme data');
+ await Promise.all([
+ this.loadVendor(),
+ this.loadTheme(),
+ this.loadPresets()
+ ]);
+
+ themeLog.groupEnd();
+
+ // Log performance
+ const duration = performance.now() - startTime;
+ window.LogConfig.logPerformance('Theme Editor Init', duration);
+
+ themeLog.info('Theme editor initialized successfully');
+
+ } catch (error) {
+ // Use centralized error logger
+ window.LogConfig.logError(error, 'Theme Editor Init');
+
+ this.error = error.message || 'Failed to initialize theme editor';
+ Utils.showToast(this.error, 'error');
} finally {
this.loading = false;
}
},
- async loadPresets() {
- themeLog.info('Loading presets...');
+ // ====================================================================
+ // DATA LOADING
+ // ====================================================================
+
+ async loadVendor() {
+ themeLog.info('Loading vendor data');
+
+ const url = `/admin/vendors/${this.vendorCode}`;
+ window.LogConfig.logApiCall('GET', url, null, 'request');
try {
- const startTime = Date.now();
- const response = await apiClient.get('/admin/vendor-themes/presets');
- const duration = Date.now() - startTime;
+ // ✅ FIX: apiClient returns data directly, not response.data
+ const response = await apiClient.get(url);
- this.presets = response.presets || [];
- themeLog.info(`${this.presets.length} presets loaded in ${duration}ms`);
+ // ✅ Direct assignment - response IS the data
+ this.vendor = response;
+
+ window.LogConfig.logApiCall('GET', url, this.vendor, 'response');
+ themeLog.debug('Vendor loaded:', this.vendor);
} catch (error) {
- themeLog.warn('Failed to load presets:', error);
- // Non-critical error, continue without presets
+ themeLog.error('Failed to load vendor:', error);
+ throw error;
}
},
- // ============================================================================
- // PRESET OPERATIONS
- // ============================================================================
+ async loadTheme() {
+ themeLog.info('Loading theme data');
+
+ const url = `/admin/vendor-themes/${this.vendorCode}`;
+ window.LogConfig.logApiCall('GET', url, null, 'request');
+
+ try {
+ // ✅ FIX: apiClient returns data directly
+ const response = await apiClient.get(url);
+
+ // Merge with default theme data
+ this.themeData = {
+ ...this.themeData,
+ ...response
+ };
+
+ window.LogConfig.logApiCall('GET', url, this.themeData, 'response');
+ themeLog.debug('Theme loaded:', this.themeData);
+
+ } catch (error) {
+ themeLog.warn('Failed to load theme, using defaults:', error);
+ // Continue with default theme
+ }
+ },
+
+ async loadPresets() {
+ themeLog.info('Loading theme presets');
+
+ const url = '/admin/vendor-themes/presets';
+ window.LogConfig.logApiCall('GET', url, null, 'request');
+
+ try {
+ // ✅ FIX: apiClient returns data directly
+ const response = await apiClient.get(url);
+
+ // ✅ Access presets directly from response, not response.data.presets
+ this.presets = response.presets || [];
+
+ window.LogConfig.logApiCall('GET', url, response, 'response');
+ themeLog.debug(`Loaded ${this.presets.length} presets`);
+
+ } catch (error) {
+ themeLog.error('Failed to load presets:', error);
+ this.presets = [];
+ }
+ },
+
+ // ====================================================================
+ // THEME OPERATIONS
+ // ====================================================================
+
+ async saveTheme() {
+ if (this.saving) return;
+
+ themeLog.info('Saving theme changes');
+ this.saving = true;
+ this.error = null;
+
+ const startTime = performance.now();
+
+ try {
+ const url = `/admin/vendor-themes/${this.vendorCode}`;
+ window.LogConfig.logApiCall('PUT', url, this.themeData, 'request');
+
+ // ✅ FIX: apiClient returns data directly
+ const response = await apiClient.put(url, this.themeData);
+
+ window.LogConfig.logApiCall('PUT', url, response, 'response');
+
+ const duration = performance.now() - startTime;
+ window.LogConfig.logPerformance('Save Theme', duration);
+
+ themeLog.info('Theme saved successfully');
+ Utils.showToast('Theme saved successfully', 'success');
+
+ } catch (error) {
+ window.LogConfig.logError(error, 'Save Theme');
+ this.error = 'Failed to save theme';
+ Utils.showToast(this.error, 'error');
+ } finally {
+ this.saving = false;
+ }
+ },
async applyPreset(presetName) {
themeLog.info(`Applying preset: ${presetName}`);
this.saving = true;
try {
- const startTime = Date.now();
- const response = await apiClient.post(
- `/admin/vendor-themes/${this.vendorCode}/preset/${presetName}`
- );
- const duration = Date.now() - startTime;
+ const url = `/admin/vendor-themes/${this.vendorCode}/preset/${presetName}`;
+ window.LogConfig.logApiCall('POST', url, null, 'request');
+ // ✅ FIX: apiClient returns data directly
+ const response = await apiClient.post(url);
+
+ window.LogConfig.logApiCall('POST', url, response, 'response');
+
+ // ✅ FIX: Access theme directly from response, not response.data.theme
if (response && response.theme) {
- // Update theme data with preset
this.themeData = {
- theme_name: response.theme.theme_name,
- colors: response.theme.colors || this.themeData.colors,
- fonts: response.theme.fonts || this.themeData.fonts,
- layout: response.theme.layout || this.themeData.layout,
- branding: response.theme.branding || this.themeData.branding,
- custom_css: response.theme.custom_css || ''
+ ...this.themeData,
+ ...response.theme
};
-
- Utils.showToast(`Applied ${presetName} preset successfully`, 'success');
- themeLog.info(`Preset applied in ${duration}ms`);
}
- } catch (error) {
- themeLog.error('Failed to apply preset:', error);
- const message = error.response?.data?.detail || 'Failed to apply preset';
- Utils.showToast(message, 'error');
+ themeLog.info(`Preset '${presetName}' applied successfully`);
+ Utils.showToast(`Applied ${presetName} preset`, 'success');
+ } catch (error) {
+ window.LogConfig.logError(error, 'Apply Preset');
+ Utils.showToast('Failed to apply preset', 'error');
} finally {
this.saving = false;
}
},
- async resetToDefault() {
- if (!confirm('Are you sure you want to reset to default theme? This will discard all customizations.')) {
+ async resetTheme() {
+ if (!confirm('Reset theme to default? This cannot be undone.')) {
return;
}
- themeLog.info('Resetting to default theme');
- await this.applyPreset('default');
- },
-
- // ============================================================================
- // SAVE OPERATIONS
- // ============================================================================
-
- async saveTheme() {
- themeLog.info('Saving theme:', this.themeData);
+ themeLog.warn('Resetting theme to default');
this.saving = true;
- this.error = null;
try {
- const startTime = Date.now();
- const response = await apiClient.put(
- `/admin/vendor-themes/${this.vendorCode}`,
- this.themeData
- );
- const duration = Date.now() - startTime;
+ const url = `/admin/vendor-themes/${this.vendorCode}`;
+ window.LogConfig.logApiCall('DELETE', url, null, 'request');
- if (response) {
- Utils.showToast('Theme saved successfully', 'success');
- themeLog.info(`Theme saved in ${duration}ms`);
- }
+ await apiClient.delete(url);
+
+ window.LogConfig.logApiCall('DELETE', url, null, 'response');
+
+ // Reload theme data
+ await this.loadTheme();
+
+ themeLog.info('Theme reset successfully');
+ Utils.showToast('Theme reset to default', 'success');
} catch (error) {
- themeLog.error('Failed to save theme:', error);
- const message = error.response?.data?.detail || 'Failed to save theme';
- Utils.showToast(message, 'error');
- this.error = message;
-
+ window.LogConfig.logError(error, 'Reset Theme');
+ Utils.showToast('Failed to reset theme', 'error');
} finally {
this.saving = false;
}
},
- // ============================================================================
- // HELPER METHODS
- // ============================================================================
+ // ====================================================================
+ // UTILITY METHODS
+ // ====================================================================
- formatDate(dateString) {
- if (!dateString) return '-';
- return Utils.formatDate(dateString);
+ previewTheme() {
+ themeLog.debug('Opening theme preview');
+ const previewUrl = `/vendor/${this.vendor?.subdomain || this.vendorCode}`;
+ window.open(previewUrl, '_blank');
},
- getPreviewStyle() {
- return {
- '--color-primary': this.themeData.colors.primary,
- '--color-secondary': this.themeData.colors.secondary,
- '--color-accent': this.themeData.colors.accent,
- '--color-background': this.themeData.colors.background,
- '--color-text': this.themeData.colors.text,
- '--color-border': this.themeData.colors.border,
- '--font-heading': this.themeData.fonts.heading,
- '--font-body': this.themeData.fonts.body,
- };
+ updateColor(key, value) {
+ themeLog.debug(`Color updated: ${key} = ${value}`);
+ this.themeData.colors[key] = value;
+ },
+
+ updateFont(type, value) {
+ themeLog.debug(`Font updated: ${type} = ${value}`);
+ this.themeData.fonts[type] = value;
+ },
+
+ updateLayout(key, value) {
+ themeLog.debug(`Layout updated: ${key} = ${value}`);
+ this.themeData.layout[key] = value;
}
};
}
diff --git a/static/shared/js/icons.js b/static/shared/js/icons.js
index 402b8a5d..eae56a77 100644
--- a/static/shared/js/icons.js
+++ b/static/shared/js/icons.js
@@ -69,6 +69,7 @@ const Icons = {
'folder-open': ``,
'download': ``,
'upload': ``,
+ 'save': ``,
// Settings & Tools
'cog': ``,
@@ -77,6 +78,10 @@ const Icons = {
'moon': ``,
'sun': ``,
+ // Design & Theming
+ 'palette': ``,
+ 'color-swatch': ``,
+
// Location
'location-marker': ``,
'globe': ``,
diff --git a/static/shared/js/log-config.js b/static/shared/js/log-config.js
new file mode 100644
index 00000000..f7a192d4
--- /dev/null
+++ b/static/shared/js/log-config.js
@@ -0,0 +1,490 @@
+// static/shared/js/log-config.js
+/**
+ * Centralized Logging Configuration for ALL Frontends
+ *
+ * This file provides a consistent logging system across:
+ * - Admin Frontend
+ * - Vendor Frontend
+ * - Shop Frontend
+ *
+ * Each frontend can customize log levels while sharing the same logging infrastructure.
+ *
+ * Usage in any frontend:
+ * ```javascript
+ * // Use the global logger
+ * log.info('Page loaded');
+ * log.error('Something went wrong', error);
+ *
+ * // Or use a pre-configured logger
+ * const vendorLog = window.LogConfig.loggers.vendors;
+ * vendorLog.info('Vendors loaded');
+ *
+ * // Or create a custom logger
+ * const pageLog = window.LogConfig.createLogger('MY-PAGE', 3);
+ * pageLog.info('Page initialized');
+ * ```
+ */
+
+// ============================================================================
+// LOG LEVELS
+// ============================================================================
+
+const LOG_LEVELS = {
+ ERROR: 1, // Only errors
+ WARN: 2, // Errors and warnings
+ INFO: 3, // Errors, warnings, and info (default)
+ DEBUG: 4 // Everything including debug messages
+};
+
+// ============================================================================
+// FRONTEND DETECTION
+// ============================================================================
+
+/**
+ * Detect which frontend we're in based on URL path
+ * @returns {string} 'admin' | 'vendor' | 'shop' | 'unknown'
+ */
+function detectFrontend() {
+ const path = window.location.pathname;
+
+ if (path.startsWith('/admin')) return 'admin';
+ if (path.startsWith('/vendor')) return 'vendor';
+ if (path.startsWith('/shop')) return 'shop';
+
+ return 'unknown';
+}
+
+// ============================================================================
+// ENVIRONMENT DETECTION
+// ============================================================================
+
+/**
+ * Detect environment based on hostname
+ * @returns {string} 'development' | 'production'
+ */
+function detectEnvironment() {
+ const hostname = window.location.hostname;
+
+ // Development environments
+ if (hostname === 'localhost' ||
+ hostname === '127.0.0.1' ||
+ hostname.startsWith('192.168.') ||
+ hostname.endsWith('.local')) {
+ return 'development';
+ }
+
+ return 'production';
+}
+
+// ============================================================================
+// LOG LEVEL CONFIGURATION
+// ============================================================================
+
+/**
+ * Default log levels per frontend
+ * Can be overridden by environment
+ */
+const DEFAULT_LOG_LEVELS = {
+ admin: {
+ development: LOG_LEVELS.DEBUG, // Show everything in development
+ production: LOG_LEVELS.WARN // Only warnings and errors in production
+ },
+ vendor: {
+ development: LOG_LEVELS.DEBUG,
+ production: LOG_LEVELS.INFO // Vendors might need more logging
+ },
+ shop: {
+ development: LOG_LEVELS.DEBUG,
+ production: LOG_LEVELS.ERROR // Shop frontend: minimal logging in production
+ },
+ unknown: {
+ development: LOG_LEVELS.DEBUG,
+ production: LOG_LEVELS.WARN
+ }
+};
+
+// ============================================================================
+// ACTIVE LOG LEVEL DETERMINATION
+// ============================================================================
+
+const CURRENT_FRONTEND = detectFrontend();
+const CURRENT_ENVIRONMENT = detectEnvironment();
+const ACTIVE_LOG_LEVEL = DEFAULT_LOG_LEVELS[CURRENT_FRONTEND][CURRENT_ENVIRONMENT];
+
+// ============================================================================
+// LOGGING UTILITIES
+// ============================================================================
+
+/**
+ * Create a logger with a specific prefix and log level
+ *
+ * @param {string} prefix - Logger prefix (e.g., 'VENDORS', 'THEME', 'PRODUCTS')
+ * @param {number} level - Log level (1-4, defaults to ACTIVE_LOG_LEVEL)
+ * @param {string} context - Optional frontend context (defaults to CURRENT_FRONTEND)
+ * @returns {Object} Logger object with error, warn, info, debug methods
+ */
+function createLogger(prefix = 'APP', level = ACTIVE_LOG_LEVEL, context = CURRENT_FRONTEND) {
+ const frontendPrefix = context.toUpperCase();
+ const formatPrefix = (emoji, label) => `${emoji} [${frontendPrefix}:${prefix} ${label}]`;
+
+ return {
+ error: (...args) => {
+ if (level >= LOG_LEVELS.ERROR) {
+ console.error(formatPrefix('❌', 'ERROR'), ...args);
+ }
+ },
+
+ warn: (...args) => {
+ if (level >= LOG_LEVELS.WARN) {
+ console.warn(formatPrefix('⚠️', 'WARN'), ...args);
+ }
+ },
+
+ info: (...args) => {
+ if (level >= LOG_LEVELS.INFO) {
+ console.info(formatPrefix('ℹ️', 'INFO'), ...args);
+ }
+ },
+
+ debug: (...args) => {
+ if (level >= LOG_LEVELS.DEBUG) {
+ console.log(formatPrefix('🔍', 'DEBUG'), ...args);
+ }
+ },
+
+ // Additional utility methods
+ group: (label) => {
+ if (level >= LOG_LEVELS.INFO) {
+ console.group(formatPrefix('📂', 'GROUP') + ' ' + label);
+ }
+ },
+
+ groupEnd: () => {
+ if (level >= LOG_LEVELS.INFO) {
+ console.groupEnd();
+ }
+ },
+
+ table: (data) => {
+ if (level >= LOG_LEVELS.DEBUG) {
+ console.table(data);
+ }
+ },
+
+ time: (label) => {
+ if (level >= LOG_LEVELS.DEBUG) {
+ console.time(formatPrefix('⏱️', 'TIME') + ' ' + label);
+ }
+ },
+
+ timeEnd: (label) => {
+ if (level >= LOG_LEVELS.DEBUG) {
+ console.timeEnd(formatPrefix('⏱️', 'TIME') + ' ' + label);
+ }
+ }
+ };
+}
+
+// ============================================================================
+// DEFAULT GLOBAL LOGGER
+// ============================================================================
+
+/**
+ * Default logger for general operations
+ * Automatically prefixed with current frontend
+ */
+const log = createLogger('APP', ACTIVE_LOG_LEVEL);
+
+// ============================================================================
+// PRE-CONFIGURED LOGGERS FOR ADMIN FRONTEND
+// ============================================================================
+
+const adminLoggers = {
+ // Vendor management
+ vendors: createLogger('VENDORS', ACTIVE_LOG_LEVEL),
+ vendorTheme: createLogger('THEME', ACTIVE_LOG_LEVEL),
+ vendorUsers: createLogger('VENDOR-USERS', ACTIVE_LOG_LEVEL),
+
+ // Product management
+ products: createLogger('PRODUCTS', ACTIVE_LOG_LEVEL),
+ inventory: createLogger('INVENTORY', ACTIVE_LOG_LEVEL),
+
+ // Order management
+ orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
+
+ // User management
+ users: createLogger('USERS', ACTIVE_LOG_LEVEL),
+
+ // Admin operations
+ audit: createLogger('AUDIT', ACTIVE_LOG_LEVEL),
+ dashboard: createLogger('DASHBOARD', ACTIVE_LOG_LEVEL),
+
+ // Import operations
+ imports: createLogger('IMPORTS', ACTIVE_LOG_LEVEL)
+};
+
+// ============================================================================
+// PRE-CONFIGURED LOGGERS FOR VENDOR FRONTEND
+// ============================================================================
+
+const vendorLoggers = {
+ // Vendor dashboard
+ dashboard: createLogger('DASHBOARD', ACTIVE_LOG_LEVEL),
+
+ // Product management
+ products: createLogger('PRODUCTS', ACTIVE_LOG_LEVEL),
+ inventory: createLogger('INVENTORY', ACTIVE_LOG_LEVEL),
+
+ // Order management
+ orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
+
+ // Theme customization
+ theme: createLogger('THEME', ACTIVE_LOG_LEVEL),
+
+ // Settings
+ settings: createLogger('SETTINGS', ACTIVE_LOG_LEVEL),
+
+ // Analytics
+ analytics: createLogger('ANALYTICS', ACTIVE_LOG_LEVEL)
+};
+
+// ============================================================================
+// PRE-CONFIGURED LOGGERS FOR SHOP FRONTEND
+// ============================================================================
+
+const shopLoggers = {
+ // Product browsing
+ catalog: createLogger('CATALOG', ACTIVE_LOG_LEVEL),
+ product: createLogger('PRODUCT', ACTIVE_LOG_LEVEL),
+ search: createLogger('SEARCH', ACTIVE_LOG_LEVEL),
+
+ // Shopping cart
+ cart: createLogger('CART', ACTIVE_LOG_LEVEL),
+ checkout: createLogger('CHECKOUT', ACTIVE_LOG_LEVEL),
+
+ // User account
+ account: createLogger('ACCOUNT', ACTIVE_LOG_LEVEL),
+ orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
+
+ // Wishlist
+ wishlist: createLogger('WISHLIST', ACTIVE_LOG_LEVEL)
+};
+
+// ============================================================================
+// SMART LOGGER SELECTION
+// ============================================================================
+
+/**
+ * Get the appropriate logger set for current frontend
+ */
+function getLoggers() {
+ switch (CURRENT_FRONTEND) {
+ case 'admin':
+ return adminLoggers;
+ case 'vendor':
+ return vendorLoggers;
+ case 'shop':
+ return shopLoggers;
+ default:
+ return {}; // Empty object, use createLogger instead
+ }
+}
+
+// Export frontend-specific loggers
+const loggers = getLoggers();
+
+// ============================================================================
+// API CALL LOGGING
+// ============================================================================
+
+/**
+ * Log API calls with consistent formatting
+ *
+ * @param {string} method - HTTP method (GET, POST, PUT, DELETE)
+ * @param {string} url - API endpoint URL
+ * @param {*} data - Request/response data (optional)
+ * @param {string} status - 'request' or 'response'
+ */
+function logApiCall(method, url, data = null, status = 'request') {
+ const apiLogger = createLogger('API', ACTIVE_LOG_LEVEL);
+
+ const emoji = status === 'request' ? '📤' : '📥';
+ const message = `${emoji} ${method} ${url}`;
+
+ if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG) {
+ if (data) {
+ apiLogger.debug(message, data);
+ } else {
+ apiLogger.debug(message);
+ }
+ } else if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.INFO) {
+ apiLogger.info(message);
+ }
+}
+
+// ============================================================================
+// ERROR LOGGING
+// ============================================================================
+
+/**
+ * Log errors with stack traces
+ *
+ * @param {Error} error - Error object
+ * @param {string} context - Context where error occurred
+ */
+function logError(error, context = 'Unknown') {
+ const errorLogger = createLogger('ERROR', ACTIVE_LOG_LEVEL);
+
+ errorLogger.error(`Error in ${context}:`, error.message);
+
+ if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG && error.stack) {
+ console.error('Stack trace:', error.stack);
+ }
+}
+
+// ============================================================================
+// PERFORMANCE LOGGING
+// ============================================================================
+
+/**
+ * Log performance metrics
+ *
+ * @param {string} operation - Operation name
+ * @param {number} duration - Duration in milliseconds
+ */
+function logPerformance(operation, duration) {
+ const perfLogger = createLogger('PERF', ACTIVE_LOG_LEVEL);
+
+ if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG) {
+ const emoji = duration < 100 ? '⚡' : duration < 500 ? '⏱️' : '🐌';
+ perfLogger.debug(`${emoji} ${operation} took ${duration}ms`);
+ }
+}
+
+// ============================================================================
+// EXPORTS (for modules)
+// ============================================================================
+
+// For pages that use module imports
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = {
+ LOG_LEVELS,
+ log,
+ loggers,
+ createLogger,
+ logApiCall,
+ logError,
+ logPerformance,
+ detectFrontend,
+ detectEnvironment
+ };
+}
+
+// ============================================================================
+// GLOBAL ACCESS (for inline scripts)
+// ============================================================================
+
+// Make available globally for inline scripts
+window.LogConfig = {
+ LOG_LEVELS,
+ log,
+ loggers,
+ createLogger,
+ logApiCall,
+ logError,
+ logPerformance,
+
+ // Expose frontend/environment info
+ frontend: CURRENT_FRONTEND,
+ environment: CURRENT_ENVIRONMENT,
+ logLevel: ACTIVE_LOG_LEVEL
+};
+
+// ============================================================================
+// INITIALIZATION
+// ============================================================================
+
+// Log that logging system is initialized
+if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.INFO) {
+ const frontendName = CURRENT_FRONTEND.charAt(0).toUpperCase() + CURRENT_FRONTEND.slice(1);
+ const envName = CURRENT_ENVIRONMENT.charAt(0).toUpperCase() + CURRENT_ENVIRONMENT.slice(1);
+ const levelName = Object.keys(LOG_LEVELS).find(k => LOG_LEVELS[k] === ACTIVE_LOG_LEVEL);
+
+ console.log(
+ `%c🎛️ ${frontendName} Frontend Logging System Initialized`,
+ 'font-weight: bold; font-size: 14px; color: #6366f1;'
+ );
+ console.log(
+ `%c Environment: ${envName}`,
+ 'color: #8b5cf6;'
+ );
+ console.log(
+ `%c Log Level: ${ACTIVE_LOG_LEVEL} (${levelName})`,
+ 'color: #8b5cf6;'
+ );
+}
+
+// ============================================================================
+// USAGE EXAMPLES (for developers)
+// ============================================================================
+
+/*
+
+EXAMPLE 1: Use global logger
+=============================
+window.LogConfig.log.info('Application started');
+window.LogConfig.log.error('Failed to load data', error);
+
+
+EXAMPLE 2: Use pre-configured logger (RECOMMENDED)
+===================================================
+// In admin frontend
+const themeLog = window.LogConfig.loggers.vendorTheme;
+themeLog.info('Theme editor loaded');
+
+// In vendor frontend
+const dashLog = window.LogConfig.loggers.dashboard;
+dashLog.info('Dashboard initialized');
+
+// In shop frontend
+const cartLog = window.LogConfig.loggers.cart;
+cartLog.info('Cart updated');
+
+
+EXAMPLE 3: Create custom logger
+================================
+const myLog = window.LogConfig.createLogger('MY-FEATURE', 4);
+myLog.info('Feature initialized');
+
+
+EXAMPLE 4: Check current frontend
+==================================
+if (window.LogConfig.frontend === 'admin') {
+ // Admin-specific code
+}
+
+
+EXAMPLE 5: API call logging
+============================
+window.LogConfig.logApiCall('GET', '/api/vendors', null, 'request');
+const data = await apiClient.get('/api/vendors');
+window.LogConfig.logApiCall('GET', '/api/vendors', data, 'response');
+
+
+EXAMPLE 6: Performance logging
+===============================
+const start = performance.now();
+await loadData();
+const duration = performance.now() - start;
+window.LogConfig.logPerformance('Load Data', duration);
+
+
+EXAMPLE 7: Error logging
+=========================
+try {
+ await saveData();
+} catch (error) {
+ window.LogConfig.logError(error, 'Save Operation');
+}
+
+*/
\ No newline at end of file