From 99863ad80b3cccdb19e8dcf74cb386b0007bbba0 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 26 Oct 2025 19:59:53 +0100 Subject: [PATCH] compiling project documentation --- docs/__temp/10.stripe_payment_integration.md | 617 ++++++ docs/__temp/12.project_readme_final.md | 781 +++++++ .../13.updated_application_workflows_final.md | 313 +++ ...pdated_complete_project_structure_final.md | 510 +++++ docs/__temp/6.complete_naming_convention.md | 392 ++++ .../STATS_SERVICE_ARCHITECTURE.md | 6 + .../ADMIN_FEATURE_INTEGRATION_GUIDE.md | 1799 +++++++++++++++++ .../__temp/BACKEND/admin_integration_guide.md | 649 ++++++ .../FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md | 331 +++ .../FRONTEND_ARCHITECTURE_OVERVIEW.txt | 239 +++ ...D_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md | 444 ++++ .../__temp/FRONTEND/FRONTEND_UI_COMPONENTS.md | 305 +++ .../FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md | 228 +++ .../FRONTEND/PAGINATION_DOCUMENTATION.md | 297 +++ .../FRONTEND/PAGINATION_QUICK_START.txt | 201 ++ docs/__temp/FRONTEND/frontend-structure.txt | 128 ++ .../MULTITENANT_ARCHITECTURE_DIAGRAMS.md | 416 ++++ ...NANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md | 489 +++++ .../MULTITENANT_EXECUTIVE_SUMMARY.md | 400 ++++ .../MULTITENANT_IMPLEMENTATION_CHECKLIST.md | 466 +++++ .../MULTITENANT_INDEX.md | 460 +++++ .../MULTITENANT_QUICK_START.md | 341 ++++ .../MULTITENANT_README.md | 425 ++++ .../MULTI_THEME_SHOP_GUIDE.md | 698 +++++++ .../THEME_INTEGRATION_YOUR_ARCHITECTURE.md | 509 +++++ .../shop_base_template.html | 246 +++ .../MULTITHEMES_SHOP_GUIDE/shop_layout.js | 228 +++ .../theme_context_middleware.py | 124 ++ .../vendor_theme_model.py | 143 ++ .../VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md | 478 +++++ .../VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md | 567 ++++++ .../19_migration_plan_FINAL.md | 916 +++++++++ .../FRONTEND_DOCUMENTATION_PLAN.md | 752 +++++++ .../15.web-architecture-revamping.md | 1210 +++++++++++ .../16.jinja2_migration_progress-2.md | 399 ++++ .../16.jinja2_migration_progress-3.md | 1132 +++++++++++ .../16.jinja2_migration_progress.md | 520 +++++ .../ROUTE_MIGRATION_SUMMARY.txt | 284 +++ .../implementation_roadmap.md | 512 +++++ docs/__temp/__PROJECT_ROADMAP/slice1_doc.md | 1070 ++++++++++ docs/__temp/__PROJECT_ROADMAP/slice2_doc.md | 808 ++++++++ docs/__temp/__PROJECT_ROADMAP/slice3_doc.md | 624 ++++++ docs/__temp/__PROJECT_ROADMAP/slice4_doc.md | 887 ++++++++ docs/__temp/__PROJECT_ROADMAP/slice5_doc.md | 1628 +++++++++++++++ .../__PROJECT_ROADMAP/slice_overview.md | 306 +++ 45 files changed, 24278 insertions(+) create mode 100644 docs/__temp/10.stripe_payment_integration.md create mode 100644 docs/__temp/12.project_readme_final.md create mode 100644 docs/__temp/13.updated_application_workflows_final.md create mode 100644 docs/__temp/14.updated_complete_project_structure_final.md create mode 100644 docs/__temp/6.complete_naming_convention.md create mode 100644 docs/__temp/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md create mode 100644 docs/__temp/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md create mode 100644 docs/__temp/BACKEND/admin_integration_guide.md create mode 100644 docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md create mode 100644 docs/__temp/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt create mode 100644 docs/__temp/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md create mode 100644 docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS.md create mode 100644 docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md create mode 100644 docs/__temp/FRONTEND/PAGINATION_DOCUMENTATION.md create mode 100644 docs/__temp/FRONTEND/PAGINATION_QUICK_START.txt create mode 100644 docs/__temp/FRONTEND/frontend-structure.txt create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_ARCHITECTURE_DIAGRAMS.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_CUSTOM_DOMAIN_IMPLEMENTATION_GUIDE.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_EXECUTIVE_SUMMARY.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_IMPLEMENTATION_CHECKLIST.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_INDEX.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_QUICK_START.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITENANT_ARCHITECTURE/MULTITENANT_README.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/MULTI_THEME_SHOP_GUIDE.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/THEME_INTEGRATION_YOUR_ARCHITECTURE.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_base_template.html create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/shop_layout.js create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/theme_context_middleware.py create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/MULTITHEMES_SHOP_GUIDE/vendor_theme_model.py create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_ARCHITECTURE_DIAGRAMS.md create mode 100644 docs/__temp/FRONT_TO_BACK_MULTITENANT/VENDOR_DOMAIN/VENDOR_DOMAIN_IMPLEMENTATION_GUIDE.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/19_migration_plan_FINAL.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/FRONTEND_DOCUMENTATION_PLAN.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/15.web-architecture-revamping.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-2.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress-3.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/JINJA_MIGRATION/16.jinja2_migration_progress.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/ROUTE_MIGRATION_SUMMARY.txt create mode 100644 docs/__temp/__PROJECT_ROADMAP/implementation_roadmap.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/slice1_doc.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/slice2_doc.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/slice3_doc.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/slice4_doc.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/slice5_doc.md create mode 100644 docs/__temp/__PROJECT_ROADMAP/slice_overview.md diff --git a/docs/__temp/10.stripe_payment_integration.md b/docs/__temp/10.stripe_payment_integration.md new file mode 100644 index 00000000..83392032 --- /dev/null +++ b/docs/__temp/10.stripe_payment_integration.md @@ -0,0 +1,617 @@ +# Stripe Payment Integration - Multi-Tenant Ecommerce Platform + +## Architecture Overview + +The payment integration uses **Stripe Connect** to handle multi-vendor payments, enabling: +- Each vendor to receive payments directly +- Platform to collect fees/commissions +- Proper financial isolation between vendors +- Compliance with financial regulations + +## Payment Models + +### Database Models + +```python +# models/database/payment.py +from decimal import Decimal +from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, Numeric +from sqlalchemy.orm import relationship +from app.core.database import Base +from .base import TimestampMixin + + +class VendorPaymentConfig(Base, TimestampMixin): + """Vendor-specific payment configuration.""" + __tablename__ = "vendor_payment_configs" + + id = Column(Integer, primary_key=True, index=True) + vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False, unique=True) + + # Stripe Connect configuration + stripe_account_id = Column(String(255)) # Stripe Connect account ID + stripe_account_status = Column(String(50)) # pending, active, restricted, inactive + stripe_onboarding_url = Column(Text) # Onboarding link for vendor + stripe_dashboard_url = Column(Text) # Vendor's Stripe dashboard + + # Payment settings + accepts_payments = Column(Boolean, default=False) + currency = Column(String(3), default="EUR") + platform_fee_percentage = Column(Numeric(5, 2), default=2.5) # Platform commission + + # Payout settings + payout_schedule = Column(String(20), default="weekly") # daily, weekly, monthly + minimum_payout = Column(Numeric(10, 2), default=20.00) + + # Relationships + vendor = relationship("Vendor", back_populates="payment_config") + + def __repr__(self): + return f"" + + +class Payment(Base, TimestampMixin): + """Payment records for orders.""" + __tablename__ = "payments" + + id = Column(Integer, primary_key=True, index=True) + vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False) + order_id = Column(Integer, ForeignKey("orders.id"), nullable=False) + customer_id = Column(Integer, ForeignKey("customers.id"), nullable=False) + + # Stripe payment details + stripe_payment_intent_id = Column(String(255), unique=True, index=True) + stripe_charge_id = Column(String(255), index=True) + stripe_transfer_id = Column(String(255)) # Transfer to vendor account + + # Payment amounts (in cents to avoid floating point issues) + amount_total = Column(Integer, nullable=False) # Total customer payment + amount_vendor = Column(Integer, nullable=False) # Amount to vendor + amount_platform_fee = Column(Integer, nullable=False) # Platform commission + currency = Column(String(3), default="EUR") + + # Payment status + status = Column(String(50), nullable=False) # pending, succeeded, failed, refunded + payment_method = Column(String(50)) # card, bank_transfer, etc. + + # Metadata + stripe_metadata = Column(Text) # JSON string of Stripe metadata + failure_reason = Column(Text) + refund_reason = Column(Text) + + # Timestamps + paid_at = Column(DateTime) + refunded_at = Column(DateTime) + + # Relationships + vendor = relationship("Vendor") + order = relationship("Order", back_populates="payment") + customer = relationship("Customer") + + def __repr__(self): + return f"" + + @property + def amount_total_euros(self): + """Convert cents to euros for display.""" + return self.amount_total / 100 + + @property + def amount_vendor_euros(self): + """Convert cents to euros for display.""" + return self.amount_vendor / 100 + + +class PaymentMethod(Base, TimestampMixin): + """Saved customer payment methods.""" + __tablename__ = "payment_methods" + + id = Column(Integer, primary_key=True, index=True) + vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False) + customer_id = Column(Integer, ForeignKey("customers.id"), nullable=False) + + # Stripe payment method details + stripe_payment_method_id = Column(String(255), nullable=False, index=True) + payment_method_type = Column(String(50), nullable=False) # card, sepa_debit, etc. + + # Card details (if applicable) + card_brand = Column(String(50)) # visa, mastercard, etc. + card_last4 = Column(String(4)) + card_exp_month = Column(Integer) + card_exp_year = Column(Integer) + + # Settings + is_default = Column(Boolean, default=False) + is_active = Column(Boolean, default=True) + + # Relationships + vendor = relationship("Vendor") + customer = relationship("Customer") + + def __repr__(self): + return f"" +``` + +### Updated Order Model + +```python +# Update models/database/order.py +class Order(Base, TimestampMixin): + # ... existing fields ... + + # Payment integration + payment_status = Column(String(50), default="pending") # pending, paid, failed, refunded + payment_intent_id = Column(String(255)) # Stripe PaymentIntent ID + total_amount_cents = Column(Integer, nullable=False) # Amount in cents + + # Relationships + payment = relationship("Payment", back_populates="order", uselist=False) + + @property + def total_amount_euros(self): + """Convert cents to euros for display.""" + return self.total_amount_cents / 100 if self.total_amount_cents else 0 +``` + +## Payment Service Integration + +### Stripe Service + +```python +# services/payment_service.py +import stripe +import json +import logging +from decimal import Decimal +from typing import Dict, Optional +from sqlalchemy.orm import Session + +from app.core.config import settings +from models.database.payment import Payment, VendorPaymentConfig +from models.database.order import Order +from models.database.vendor import Vendor +from app.exceptions.payment import * + +logger = logging.getLogger(__name__) + +# Configure Stripe +stripe.api_key = settings.stripe_secret_key + + +class PaymentService: + """Service for handling Stripe payments in multi-tenant environment.""" + + def __init__(self, db: Session): + self.db = db + + def create_payment_intent( + self, + vendor_id: int, + order_id: int, + amount_euros: Decimal, + customer_email: str, + metadata: Optional[Dict] = None + ) -> Dict: + """Create Stripe PaymentIntent for vendor order.""" + + # Get vendor payment configuration + payment_config = self.get_vendor_payment_config(vendor_id) + if not payment_config.accepts_payments: + raise PaymentNotConfiguredException(f"Vendor {vendor_id} not configured for payments") + + # Calculate amounts + amount_cents = int(amount_euros * 100) + platform_fee_cents = int(amount_cents * (payment_config.platform_fee_percentage / 100)) + vendor_amount_cents = amount_cents - platform_fee_cents + + try: + # Create PaymentIntent with Stripe Connect + payment_intent = stripe.PaymentIntent.create( + amount=amount_cents, + currency=payment_config.currency.lower(), + application_fee_amount=platform_fee_cents, + transfer_data={ + 'destination': payment_config.stripe_account_id, + }, + metadata={ + 'vendor_id': str(vendor_id), + 'order_id': str(order_id), + 'platform': 'multi_tenant_ecommerce', + **(metadata or {}) + }, + receipt_email=customer_email, + description=f"Order payment for vendor {vendor_id}" + ) + + # Create payment record + payment = Payment( + vendor_id=vendor_id, + order_id=order_id, + customer_id=self.get_order_customer_id(order_id), + stripe_payment_intent_id=payment_intent.id, + amount_total=amount_cents, + amount_vendor=vendor_amount_cents, + amount_platform_fee=platform_fee_cents, + currency=payment_config.currency, + status='pending', + stripe_metadata=json.dumps(payment_intent.metadata) + ) + + self.db.add(payment) + + # Update order + order = self.db.query(Order).filter(Order.id == order_id).first() + if order: + order.payment_intent_id = payment_intent.id + order.payment_status = 'pending' + + self.db.commit() + + return { + 'payment_intent_id': payment_intent.id, + 'client_secret': payment_intent.client_secret, + 'amount_total': amount_euros, + 'amount_vendor': vendor_amount_cents / 100, + 'platform_fee': platform_fee_cents / 100, + 'currency': payment_config.currency + } + + except stripe.error.StripeError as e: + logger.error(f"Stripe error creating PaymentIntent: {e}") + raise PaymentProcessingException(f"Payment processing failed: {str(e)}") + + def confirm_payment(self, payment_intent_id: str) -> Payment: + """Confirm payment and update records.""" + + try: + # Retrieve PaymentIntent from Stripe + payment_intent = stripe.PaymentIntent.retrieve(payment_intent_id) + + # Find payment record + payment = self.db.query(Payment).filter( + Payment.stripe_payment_intent_id == payment_intent_id + ).first() + + if not payment: + raise PaymentNotFoundException(f"Payment not found for intent {payment_intent_id}") + + # Update payment status based on Stripe status + if payment_intent.status == 'succeeded': + payment.status = 'succeeded' + payment.stripe_charge_id = payment_intent.charges.data[0].id if payment_intent.charges.data else None + payment.paid_at = datetime.utcnow() + + # Update order status + order = self.db.query(Order).filter(Order.id == payment.order_id).first() + if order: + order.payment_status = 'paid' + order.status = 'processing' # Move order to processing + + elif payment_intent.status == 'payment_failed': + payment.status = 'failed' + payment.failure_reason = payment_intent.last_payment_error.message if payment_intent.last_payment_error else "Unknown error" + + # Update order status + order = self.db.query(Order).filter(Order.id == payment.order_id).first() + if order: + order.payment_status = 'failed' + + self.db.commit() + + return payment + + except stripe.error.StripeError as e: + logger.error(f"Stripe error confirming payment: {e}") + raise PaymentProcessingException(f"Payment confirmation failed: {str(e)}") + + def create_vendor_stripe_account(self, vendor_id: int, vendor_data: Dict) -> str: + """Create Stripe Connect account for vendor.""" + + try: + # Create Stripe Connect Express account + account = stripe.Account.create( + type='express', + country='LU', # Luxembourg + email=vendor_data.get('business_email'), + capabilities={ + 'card_payments': {'requested': True}, + 'transfers': {'requested': True}, + }, + business_type='company', + company={ + 'name': vendor_data.get('business_name'), + 'phone': vendor_data.get('business_phone'), + 'address': { + 'line1': vendor_data.get('address_line1'), + 'city': vendor_data.get('city'), + 'postal_code': vendor_data.get('postal_code'), + 'country': 'LU' + } + }, + metadata={ + 'vendor_id': str(vendor_id), + 'platform': 'multi_tenant_ecommerce' + } + ) + + # Update or create payment configuration + payment_config = self.get_or_create_vendor_payment_config(vendor_id) + payment_config.stripe_account_id = account.id + payment_config.stripe_account_status = account.charges_enabled and account.payouts_enabled and 'active' or 'pending' + + self.db.commit() + + return account.id + + except stripe.error.StripeError as e: + logger.error(f"Stripe error creating account: {e}") + raise PaymentConfigurationException(f"Failed to create payment account: {str(e)}") + + def create_onboarding_link(self, vendor_id: int) -> str: + """Create Stripe onboarding link for vendor.""" + + payment_config = self.get_vendor_payment_config(vendor_id) + if not payment_config.stripe_account_id: + raise PaymentNotConfiguredException("Vendor does not have Stripe account") + + try: + account_link = stripe.AccountLink.create( + account=payment_config.stripe_account_id, + refresh_url=f"{settings.frontend_url}/vendor/admin/payments/refresh", + return_url=f"{settings.frontend_url}/vendor/admin/payments/success", + type='account_onboarding', + ) + + # Update onboarding URL + payment_config.stripe_onboarding_url = account_link.url + self.db.commit() + + return account_link.url + + except stripe.error.StripeError as e: + logger.error(f"Stripe error creating onboarding link: {e}") + raise PaymentConfigurationException(f"Failed to create onboarding link: {str(e)}") + + def get_vendor_payment_config(self, vendor_id: int) -> VendorPaymentConfig: + """Get vendor payment configuration.""" + config = self.db.query(VendorPaymentConfig).filter( + VendorPaymentConfig.vendor_id == vendor_id + ).first() + + if not config: + raise PaymentNotConfiguredException(f"No payment configuration for vendor {vendor_id}") + + return config + + def webhook_handler(self, event_type: str, event_data: Dict) -> None: + """Handle Stripe webhook events.""" + + if event_type == 'payment_intent.succeeded': + payment_intent_id = event_data['object']['id'] + self.confirm_payment(payment_intent_id) + + elif event_type == 'payment_intent.payment_failed': + payment_intent_id = event_data['object']['id'] + self.confirm_payment(payment_intent_id) + + elif event_type == 'account.updated': + # Update vendor account status + account_id = event_data['object']['id'] + self.update_vendor_account_status(account_id, event_data['object']) + + # Add more webhook handlers as needed +``` + +## API Endpoints + +### Payment APIs + +```python +# app/api/v1/vendor/payments.py +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session + +from app.core.database import get_db +from middleware.vendor_context import require_vendor_context +from models.database.vendor import Vendor +from services.payment_service import PaymentService + +router = APIRouter(prefix="/payments", tags=["vendor-payments"]) + + +@router.get("/config") +async def get_payment_config( + vendor: Vendor = Depends(require_vendor_context()), + db: Session = Depends(get_db) +): + """Get vendor payment configuration.""" + payment_service = PaymentService(db) + + try: + config = payment_service.get_vendor_payment_config(vendor.id) + return { + "stripe_account_id": config.stripe_account_id, + "account_status": config.stripe_account_status, + "accepts_payments": config.accepts_payments, + "currency": config.currency, + "platform_fee_percentage": float(config.platform_fee_percentage), + "needs_onboarding": config.stripe_account_status != 'active' + } + except Exception: + return { + "stripe_account_id": None, + "account_status": "not_configured", + "accepts_payments": False, + "needs_setup": True + } + + +@router.post("/setup") +async def setup_payments( + setup_data: dict, + vendor: Vendor = Depends(require_vendor_context()), + db: Session = Depends(get_db) +): + """Set up Stripe payments for vendor.""" + payment_service = PaymentService(db) + + vendor_data = { + "business_name": vendor.name, + "business_email": vendor.business_email, + "business_phone": vendor.business_phone, + **setup_data + } + + account_id = payment_service.create_vendor_stripe_account(vendor.id, vendor_data) + onboarding_url = payment_service.create_onboarding_link(vendor.id) + + return { + "stripe_account_id": account_id, + "onboarding_url": onboarding_url, + "message": "Payment setup initiated. Complete onboarding to accept payments." + } + + +# app/api/v1/public/vendors/payments.py +@router.post("/{vendor_id}/payments/create-intent") +async def create_payment_intent( + vendor_id: int, + payment_data: dict, + db: Session = Depends(get_db) +): + """Create payment intent for customer order.""" + payment_service = PaymentService(db) + + payment_intent = payment_service.create_payment_intent( + vendor_id=vendor_id, + order_id=payment_data['order_id'], + amount_euros=Decimal(str(payment_data['amount'])), + customer_email=payment_data['customer_email'], + metadata=payment_data.get('metadata', {}) + ) + + return payment_intent + + +@router.post("/webhooks/stripe") +async def stripe_webhook( + request: Request, + db: Session = Depends(get_db) +): + """Handle Stripe webhook events.""" + import stripe + + payload = await request.body() + sig_header = request.headers.get('stripe-signature') + + try: + event = stripe.Webhook.construct_event( + payload, sig_header, settings.stripe_webhook_secret + ) + except ValueError: + raise HTTPException(status_code=400, detail="Invalid payload") + except stripe.error.SignatureVerificationError: + raise HTTPException(status_code=400, detail="Invalid signature") + + payment_service = PaymentService(db) + payment_service.webhook_handler(event['type'], event['data']) + + return {"status": "success"} +``` + +## Frontend Integration + +### Checkout Process + +```javascript +// frontend/js/shop/checkout.js +class CheckoutManager { + constructor(vendorId) { + this.vendorId = vendorId; + this.stripe = Stripe(STRIPE_PUBLISHABLE_KEY); + this.elements = this.stripe.elements(); + this.paymentElement = null; + } + + async initializePayment(orderData) { + // Create payment intent + const response = await fetch(`/api/v1/public/vendors/${this.vendorId}/payments/create-intent`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + order_id: orderData.orderId, + amount: orderData.total, + customer_email: orderData.customerEmail + }) + }); + + const { client_secret, amount_total, platform_fee } = await response.json(); + + // Display payment breakdown + this.displayPaymentBreakdown(amount_total, platform_fee); + + // Create payment element + this.paymentElement = this.elements.create('payment', { + clientSecret: client_secret + }); + + this.paymentElement.mount('#payment-element'); + } + + async confirmPayment(orderData) { + const { error } = await this.stripe.confirmPayment({ + elements: this.elements, + confirmParams: { + return_url: `${window.location.origin}/shop/order-confirmation`, + receipt_email: orderData.customerEmail + } + }); + + if (error) { + this.showPaymentError(error.message); + } + } +} +``` + +## Updated Workflow Integration + +### Enhanced Customer Purchase Workflow + +``` +Customer adds products to cart + ↓ +Customer proceeds to checkout + ↓ +System creates Order (payment_status: pending) + ↓ +Frontend calls POST /api/v1/public/vendors/{vendor_id}/payments/create-intent + ↓ +PaymentService creates Stripe PaymentIntent with vendor destination + ↓ +Customer completes payment with Stripe Elements + ↓ +Stripe webhook confirms payment + ↓ +PaymentService updates Order (payment_status: paid, status: processing) + ↓ +Vendor receives order for fulfillment +``` + +### Payment Configuration Workflow + +``` +Vendor accesses payment settings + ↓ +POST /api/v1/vendor/payments/setup + ↓ +System creates Stripe Connect account + ↓ +Vendor completes Stripe onboarding + ↓ +Webhook updates account status to 'active' + ↓ +Vendor can now accept payments +``` + +This integration provides secure, compliant payment processing while maintaining vendor isolation and enabling proper revenue distribution between vendors and the platform. \ No newline at end of file diff --git a/docs/__temp/12.project_readme_final.md b/docs/__temp/12.project_readme_final.md new file mode 100644 index 00000000..026514d0 --- /dev/null +++ b/docs/__temp/12.project_readme_final.md @@ -0,0 +1,781 @@ +# Multi-Tenant Ecommerce Platform + +A production-ready, multi-tenant ecommerce platform that enables vendors to operate independent webshops while integrating with external marketplaces. Built with complete vendor isolation, comprehensive business features, and modern reactive frontend. + +## 🚀 Features + +### Core Business Features + +- **Multi-Vendor Marketplace**: Complete vendor isolation with independent webshops +- **Marketplace Integration**: Import and curate products from external marketplaces (Letzshop CSV) +- **Product Catalog Management**: Vendor-scoped product publishing from marketplace imports +- **Inventory Management**: Real-time stock tracking with location-based inventory +- **Order Management**: Complete order lifecycle with status tracking and fulfillment +- **Customer Management**: Vendor-scoped customer accounts with order history +- **Team Management**: Role-based access control with granular permissions +- **Shopping Cart**: Session-based cart with real-time updates + +### Technical Features + +- **Modern Frontend Stack**: Alpine.js for reactive UI with zero build step +- **RESTful API Architecture**: FastAPI with comprehensive OpenAPI documentation +- **Service Layer Pattern**: Clean separation of business logic and data access +- **Exception-First Error Handling**: Frontend-friendly error responses with consistent error codes +- **Multi-tenant Security**: Complete data isolation with vendor context detection +- **Background Job Processing**: Async marketplace imports with status tracking +- **Comprehensive API**: Admin, Vendor, and Public (Customer) endpoints + +### Security & Compliance + +- **Complete Data Isolation**: Chinese wall between vendor data +- **JWT Authentication**: Secure token-based authentication for all user types +- **Role-Based Access Control**: Granular permissions (Owner, Manager, Editor, Viewer) +- **Vendor Context Detection**: Subdomain and path-based tenant isolation +- **Input Validation**: Pydantic models for all API requests +- **Exception Handling**: Structured error responses with proper HTTP status codes + +## 🏗️ Architecture + +### Technology Stack + +- **Backend**: Python 3.13+ with FastAPI +- **Database**: PostgreSQL with SQLAlchemy ORM +- **Frontend**: Vanilla HTML, CSS, JavaScript with Alpine.js (CDN-based, no build step) +- **Authentication**: JWT tokens with role-based permissions +- **Background Jobs**: Async CSV import processing +- **API Documentation**: Auto-generated OpenAPI/Swagger + +### Multi-Tenancy Model + +``` +Platform +├── Admin Portal (admin.platform.com or /admin) +│ ├── Vendor management +│ ├── User administration +│ ├── Platform statistics +│ └── Import job monitoring +├── Vendor A (vendor-a.platform.com or /vendor/{code}) +│ ├── Marketplace product imports +│ ├── Product catalog publishing +│ ├── Order management +│ ├── Customer management +│ ├── Team management +│ └── Inventory tracking +├── Vendor B (vendor-b.platform.com or /vendor/{code}) +│ └── Completely isolated from Vendor A +└── Customer Shop (/shop or subdomain) + ├── Product browsing + ├── Shopping cart + ├── Order placement + └── Order history +``` + +### Data Flow + +``` +Marketplace CSV → Import Job → MarketplaceProduct (Staging) → Product (Catalog) → Order → Customer + ↓ ↓ ↓ + Job Status Product Selection Inventory Tracking +``` + +## 📁 Project Structure + +``` +├── main.py # FastAPI application entry point +├── app/ +│ ├── api/ +│ │ ├── main.py # API router aggregation +│ │ ├── deps.py # Dependency injection (auth, context) +│ │ └── v1/ # API version 1 +│ │ ├── admin/ # Admin endpoints +│ │ │ ├── __init__.py +│ │ │ ├── auth.py +│ │ │ ├── vendors.py +│ │ │ ├── users.py +│ │ │ ├── marketplace.py +│ │ │ └── dashboard.py +│ │ ├── vendor/ # Vendor endpoints +│ │ │ ├── __init__.py +│ │ │ ├── auth.py +│ │ │ ├── dashboard.py +│ │ │ ├── products.py +│ │ │ ├── orders.py +│ │ │ ├── marketplace.py +│ │ │ ├── inventory.py +│ │ │ └── vendor.py +│ │ └── public/ # Customer endpoints +│ │ ├── __init__.py +│ │ └── vendors/ +│ │ ├── auth.py +│ │ ├── products.py +│ │ ├── cart.py +│ │ └── orders.py +│ ├── core/ +│ │ ├── database.py # Database configuration +│ │ ├── security.py # JWT and password utilities +│ │ └── config.py # Application settings +│ ├── exceptions/ # Custom exceptions +│ │ ├── __init__.py +│ │ ├── base.py +│ │ ├── auth.py +│ │ ├── vendor.py +│ │ ├── customer.py +│ │ ├── product.py +│ │ ├── order.py +│ │ ├── inventory.py +│ │ ├── team.py +│ │ ├── marketplace_product.py +│ │ ├── marketplace_import_job.py +│ │ └── admin.py +│ └── services/ # Business logic layer +│ ├── auth_service.py +│ ├── admin_service.py +│ ├── vendor_service.py +│ ├── customer_service.py +│ ├── product_service.py +│ ├── order_service.py +│ ├── cart_service.py +│ ├── inventory_service.py +│ ├── team_service.py +│ ├── marketplace_service.py +│ └── stats_service.py +├── models/ +│ ├── database/ # SQLAlchemy ORM models +│ │ ├── base.py +│ │ ├── user.py +│ │ ├── vendor.py +│ │ ├── customer.py +│ │ ├── product.py +│ │ ├── order.py +│ │ ├── inventory.py +│ │ ├── marketplace_product.py +│ │ └── marketplace_import_job.py +│ └── schemas/ # Pydantic validation models +│ ├── auth.py +│ ├── vendor.py +│ ├── customer.py +│ ├── product.py +│ ├── order.py +│ ├── inventory.py +│ ├── marketplace_product.py +│ ├── marketplace_import_job.py +│ └── stats.py +├── middleware/ +│ ├── auth.py # JWT authentication +│ ├── vendor_context.py # Multi-tenant context detection +│ ├── rate_limiter.py # API rate limiting +│ └── decorators.py # Utility decorators +├── static/ # Frontend assets (no build step required) +│ ├── admin/ # Admin interface +│ │ ├── login.html +│ │ ├── dashboard.html +│ │ └── vendors.html +│ ├── vendor/ # Vendor management UI +│ │ ├── login.html +│ │ ├── dashboard.html +│ │ └── admin/ +│ │ ├── products.html +│ │ ├── orders.html +│ │ └── marketplace.html +│ ├── shop/ # Customer shop interface +│ │ ├── products.html +│ │ ├── product.html # Alpine.js product detail +│ │ ├── cart.html +│ │ └── account/ +│ │ ├── register.html +│ │ ├── login.html +│ │ └── orders.html +│ ├── css/ +│ │ ├── shared/ +│ │ │ ├── base.css # CSS variables, utility classes +│ │ │ └── auth.css +│ │ ├── admin/ +│ │ │ └── admin.css +│ │ ├── vendor/ +│ │ │ └── vendor.css +│ │ └── shop/ +│ │ └── shop.css +│ └── js/ +│ └── shared/ +│ ├── api-client.js +│ ├── vendor-context.js +│ └── utils.js +├── scripts/ +│ ├── init_db.py # Database initialization +│ └── create_admin.py # Admin user creation +└── tests/ + ├── unit/ + ├── integration/ + └── e2e/ +``` + +## 🚀 Quick Start + +### Prerequisites + +- Python 3.13+ +- PostgreSQL 14+ (SQLite for development) +- Node.js (optional, only for development tools) + +### Development Setup + +#### 1. Clone and Setup Environment + +```bash +git clone +cd multi-tenant-ecommerce +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +pip install -r requirements.txt +``` + +#### 2. Database Setup + +```bash +# Create database +createdb ecommerce_db + +# Run migrations +python scripts/init_db.py + +# Create initial admin user +python scripts/create_admin.py +``` + +#### 3. Environment Configuration + +```bash +cp .env.example .env +# Edit .env with your configuration +``` + +Minimal `.env`: + +```env +DATABASE_URL=postgresql://user:pass@localhost:5432/ecommerce_db +SECRET_KEY=your-secret-key-here-generate-with-openssl +ALGORITHM=HS256 +ACCESS_TOKEN_EXPIRE_MINUTES=30 +DEVELOPMENT_MODE=true +``` + +#### 4. Start Application + +```bash +# Start FastAPI application +uvicorn main:app --reload --port 8000 +``` + +#### 5. Access the Platform + +- **Admin Panel**: http://localhost:8000/admin/ +- **Vendor Login**: http://localhost:8000/vendor/login +- **Customer Shop**: http://localhost:8000/shop/ +- **API Documentation**: http://localhost:8000/docs +- **Health Check**: http://localhost:8000/health + +### First Steps + +1. **Login to Admin Panel** + - URL: http://localhost:8000/admin/ + - Credentials: Created via `create_admin.py` + +2. **Create First Vendor** + - Navigate to Admin → Vendors + - Click "Create Vendor" + - Fill in vendor details (name, code, subdomain) + - System creates vendor + owner user account + - Note the temporary password + +3. **Login as Vendor Owner** + - URL: http://localhost:8000/vendor/login (or subdomain) + - Use vendor owner credentials + +4. **Import Products from Marketplace** + - Navigate to Vendor → Marketplace Import + - Configure Letzshop CSV URL + - Trigger import job + - Monitor import status + +5. **Publish Products to Catalog** + - Review imported products in staging + - Select products to publish + - Configure pricing and inventory + - Publish to customer-facing catalog + +## 📋 API Structure + +### Admin APIs (`/api/v1/admin`) + +**Authentication:** +``` +POST /auth/login # Admin login +``` + +**Vendor Management:** +``` +GET /vendors # List all vendors +POST /vendors # Create vendor with owner +GET /vendors/{id} # Get vendor details +PUT /vendors/{id}/verify # Verify/unverify vendor +PUT /vendors/{id}/status # Toggle active status +DELETE /vendors/{id} # Delete vendor +``` + +**User Management:** +``` +GET /users # List all users +PUT /users/{id}/status # Toggle user status +``` + +**Marketplace Monitoring:** +``` +GET /marketplace-import-jobs # Monitor all import jobs +``` + +**Dashboard & Statistics:** +``` +GET /dashboard # Admin dashboard +GET /dashboard/stats # Comprehensive statistics +GET /dashboard/stats/marketplace # Marketplace breakdown +GET /dashboard/stats/platform # Platform-wide metrics +``` + +### Vendor APIs (`/api/v1/vendor`) + +**Authentication:** +``` +POST /auth/login # Vendor team login +POST /auth/logout # Logout +``` + +**Dashboard:** +``` +GET /dashboard/stats # Vendor-specific statistics +``` + +**Product Management:** +``` +GET /products # List catalog products +POST /products # Add product to catalog +GET /products/{id} # Get product details +PUT /products/{id} # Update product +DELETE /products/{id} # Remove from catalog +POST /products/from-import/{id} # Publish from marketplace +PUT /products/{id}/toggle-active # Toggle product active +PUT /products/{id}/toggle-featured # Toggle featured status +``` + +**Order Management:** +``` +GET /orders # List vendor orders +GET /orders/{id} # Get order details +PUT /orders/{id}/status # Update order status +``` + +**Marketplace Integration:** +``` +POST /marketplace/import # Trigger import job +GET /marketplace/jobs # List import jobs +GET /marketplace/jobs/{id} # Get job status +GET /marketplace/products # List staged products +POST /marketplace/products/publish # Bulk publish to catalog +``` + +**Inventory Management:** +``` +GET /inventory # List inventory items +POST /inventory # Add inventory +PUT /inventory/{id} # Update inventory +GET /inventory/movements # Inventory movement history +``` + +### Public/Customer APIs (`/api/v1/public/vendors`) + +**Authentication:** +``` +POST /{vendor_id}/auth/register # Customer registration +POST /{vendor_id}/auth/login # Customer login +POST /{vendor_id}/auth/logout # Customer logout +``` + +**Product Browsing:** +``` +GET /{vendor_id}/products # Browse product catalog +GET /{vendor_id}/products/{id} # Product details +GET /{vendor_id}/products/search # Search products +``` + +**Shopping Cart:** +``` +GET /{vendor_id}/cart/{session} # Get cart +POST /{vendor_id}/cart/{session}/items # Add to cart +PUT /{vendor_id}/cart/{session}/items/{id} # Update quantity +DELETE /{vendor_id}/cart/{session}/items/{id} # Remove item +DELETE /{vendor_id}/cart/{session} # Clear cart +``` + +**Order Placement:** +``` +POST /{vendor_id}/orders # Place order +GET /{vendor_id}/customers/{id}/orders # Order history +GET /{vendor_id}/customers/{id}/orders/{id} # Order details +``` + +## 🎨 Frontend Architecture + +### Alpine.js Integration + +#### Why Alpine.js? + +- ✅ Lightweight (15KB) - perfect for multi-tenant platform +- ✅ No build step required - works directly in HTML +- ✅ Reactive state management - modern UX without complexity +- ✅ Perfect Jinja2 integration - server + client harmony +- ✅ Scoped components - natural vendor isolation + +#### Example: Product Detail Page + +```html +
+ +

+

+ + + + + + +
+``` + +### CSS Architecture + +#### CSS Variables for Multi-Tenant Theming + +```css +/* Base variables in base.css */ +:root { + --primary-color: #3b82f6; + --primary-dark: #2563eb; + --success-color: #10b981; + --danger-color: #ef4444; + --warning-color: #f59e0b; + + /* Typography */ + --font-base: 16px; + --font-sm: 0.875rem; + --font-xl: 1.25rem; + + /* Spacing */ + --spacing-sm: 0.5rem; + --spacing-md: 1rem; + --spacing-lg: 1.5rem; + + /* Borders & Shadows */ + --radius-md: 0.375rem; + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +/* Vendor-specific overrides */ +[data-vendor-theme="dark"] { + --primary-color: #1f2937; + --background-color: #111827; +} +``` + +## 🔒 Security Implementation + +### Authentication Flow + +**Admin Login:** +``` +1. POST /api/v1/admin/auth/login +2. Verify credentials + admin role +3. Generate JWT token +4. Store token in localStorage +5. Include in Authorization header for protected routes +``` + +**Vendor Team Login:** +``` +1. POST /api/v1/vendor/auth/login +2. Detect vendor context (subdomain or path) +3. Verify credentials + vendor membership +4. Generate JWT token with vendor context +5. All subsequent requests validated against vendor +``` + +**Customer Login:** +``` +1. POST /api/v1/public/vendors/{id}/auth/login +2. Verify customer credentials for specific vendor +3. Generate JWT token with customer context +4. Customer can only access their own data +``` + +### Vendor Context Detection + +```python +# Automatic vendor detection from: +1. Subdomain: vendor-a.platform.com +2. Path parameter: /vendor/VENDOR_A/ +3. JWT token: Embedded vendor_id in claims + +# Complete data isolation: +- All queries automatically scoped to vendor_id +- Cross-vendor access prevented at service layer +- Exception raised if vendor mismatch detected +``` + +### Exception Handling Pattern + +```python +# Frontend-friendly error responses +{ + "detail": "Human-readable error message", + "error_code": "PRODUCT_NOT_FOUND", + "status_code": 404, + "timestamp": "2025-01-10T12:00:00Z", + "request_id": "abc123" +} + +# Consistent error codes across platform +- VENDOR_NOT_FOUND +- PRODUCT_NOT_IN_CATALOG +- INSUFFICIENT_INVENTORY +- INVALID_ORDER_STATUS +- UNAUTHORIZED_VENDOR_ACCESS +``` + +## 📊 Database Schema + +### Core Tables + +**Multi-Tenant Foundation:** +```sql +vendors # Vendor accounts +├── users # Platform/admin users +├── vendor_users # Vendor team members (many-to-many) +└── roles # Role definitions per vendor + +customers # Vendor-scoped customers +└── customer_addresses +``` + +**Product & Inventory:** +```sql +marketplace_products # Imported from marketplaces (staging) +└── marketplace_import_jobs # Import tracking + +products # Published vendor catalog +└── inventory # Stock tracking by location + └── inventory_movements +``` + +**Orders & Commerce:** +```sql +orders +├── order_items +├── shipping_address (FK to customer_addresses) +└── billing_address (FK to customer_addresses) +``` + +### Key Relationships + +``` +Vendor (1) ──→ (N) Products +Vendor (1) ──→ (N) Customers +Vendor (1) ──→ (N) Orders +Vendor (1) ──→ (N) MarketplaceProducts + +Product (1) ──→ (N) Inventory +Product (1) ──→ (1) MarketplaceProduct + +Order (1) ──→ (N) OrderItems +Order (1) ──→ (1) Customer +Order (1) ──→ (1) ShippingAddress +Order (1) ──→ (1) BillingAddress +``` + +## 🧪 Testing + +### Current Test Coverage + +```bash +# Run all tests +pytest tests/ + +# Run with coverage +pytest --cov=app --cov=models --cov=middleware tests/ + +# Run specific test category +pytest tests/unit/ +pytest tests/integration/ +pytest tests/e2e/ +``` + +### Test Structure + +- **Unit Tests**: Service layer logic, model validation +- **Integration Tests**: API endpoints, database operations +- **E2E Tests**: Complete user workflows (admin creates vendor → vendor imports products → customer places order) + +## 🚦 Development Status + +### ✅ Completed Features + +**Slice 1: Multi-Tenant Foundation** +- ✅ Admin creates vendors through admin interface +- ✅ Vendor owner login with context detection +- ✅ Complete vendor data isolation +- ✅ Role-based access control +- ✅ JWT authentication system + +**Slice 2: Marketplace Integration** +- ✅ CSV import from Letzshop +- ✅ Background job processing +- ✅ Import status tracking +- ✅ Product staging area +- 🚧 Real-time Alpine.js status updates + +**Slice 3: Product Catalog** +- ✅ Product publishing from marketplace staging +- ✅ Vendor product catalog management +- ✅ Product CRUD operations +- ✅ Inventory tracking +- ✅ Product filtering and search + +**Slice 4: Customer Shopping** +- ✅ Customer service implementation +- ✅ Customer registration/login +- ✅ Product browsing interface +- ✅ Shopping cart with Alpine.js +- ✅ Product detail page (Alpine.js) + +**Slice 5: Order Processing** +- ✅ Order creation from cart +- ✅ Order management (vendor) +- ✅ Order history (customer) +- ✅ Order status tracking +- ✅ Inventory reservation + +### 🚧 In Progress (BOOTS) + +**Current Sprint:** +- 🚧 Customer account dashboard (Alpine.js) +- 🚧 Multi-step checkout flow +- 🚧 Payment integration placeholder (Stripe ready) +- 🚧 Order confirmation page +- 🚧 Email notifications (order confirmations) + +## 📋 Roadmap + +### Phase 1: Core Platform (90% Complete) + +- ✅ Multi-tenant architecture +- ✅ Vendor management +- ✅ Product catalog system +- ✅ Order processing +- ✅ Customer management +- 🚧 Payment integration (ready for Stripe) +- 🚧 Email notifications + +### Phase 2: Advanced Features (Next) + +- Persistent cart storage (Redis/Database) +- Order search and filtering +- Advanced inventory management +- Product variants support +- Customer reviews and ratings +- Vendor analytics dashboard + +### Phase 3: Enterprise Features (Future) + +- Multi-language support +- Advanced reporting and exports +- Webhook integrations +- API rate limiting enhancements +- Performance monitoring +- Automated backups + +## 📝 Naming Conventions + +The project follows strict naming conventions for consistency: + +### Files + +- **API files**: Plural (`products.py`, `orders.py`) +- **Model files**: Singular (`product.py`, `order.py`) +- **Service files**: Singular + service (`product_service.py`) +- **Exception files**: Singular (`product.py`, `order.py`) + +### Terminology + +- **inventory** (not stock) +- **vendor** (not shop) +- **customer** (not user for end-users) +- **team** (not staff) + +See `docs/6.complete_naming_convention.md` for full details. + +## 🤝 Contributing + +### Development Workflow + +1. Fork the repository +2. Create feature branch: `git checkout -b feature/amazing-feature` +3. Follow existing patterns (service layer, exceptions, Alpine.js) +4. Add tests for new features +5. Update API documentation +6. Submit pull request + +### Code Quality + +```bash +# Format code +black app/ models/ middleware/ + +# Sort imports +isort app/ models/ middleware/ + +# Lint +flake8 app/ models/ middleware/ +``` + +## 📄 License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## 🆘 Support + +### Documentation + +- **API Reference**: http://localhost:8000/docs +- **Development Guides**: `/docs/` +- **Naming Conventions**: `/docs/6.complete_naming_convention.md` +- **Vertical Slice Plan**: `/docs/3.vertical_slice_roadmap.md` + +### Key Features + +- **Zero Build Step**: Frontend works without compilation +- **Alpine.js Reactive UI**: Modern UX without framework complexity +- **Service Layer Pattern**: Clean, testable business logic +- **Exception-First**: Consistent error handling +- **Multi-Tenant by Design**: Complete vendor isolation + +--- + +Built with FastAPI, PostgreSQL, Alpine.js, and modern Python patterns for a scalable, maintainable multi-tenant ecommerce platform. 🚀 diff --git a/docs/__temp/13.updated_application_workflows_final.md b/docs/__temp/13.updated_application_workflows_final.md new file mode 100644 index 00000000..bccc0d72 --- /dev/null +++ b/docs/__temp/13.updated_application_workflows_final.md @@ -0,0 +1,313 @@ +# Multi-Tenant Ecommerce Platform - Complete Application Workflows + +## Overview + +This document describes the complete workflows for the production-ready multi-tenant ecommerce platform, from marketplace import to customer analytics. Each workflow shows the interaction between different user types and the data flow through all system components including notifications, payments, media management, and monitoring. + +## Core Data Flow Architecture + +``` +Marketplace CSV → MarketplaceProduct (staging) → Product (catalog) → Customer Orders → Analytics + ↓ ↓ ↓ + Email Notifications Media Files Payment Processing + ↓ ↓ ↓ + Audit Logging Search Index Performance Monitoring +``` + +## Workflow 1: Platform Setup and Vendor Onboarding + +### Participants +- **Admin**: Platform administrator +- **New Vendor**: Business owner registering +- **System**: Automated onboarding processes + +### Workflow Steps + +#### 1.1 Admin Creates Vendor +``` +Admin → Access admin panel (admin.platform.com) + ↓ +POST /api/v1/admin/auth/login + ↓ +Admin dashboard loads with platform metrics + ↓ +Admin → Create new vendor + ↓ +POST /api/v1/admin/vendors + ↓ +System creates: + - Vendor record with subdomain + - Owner user account + - Default role structure (Owner, Manager, Editor, Viewer) + - Default notification templates + - Vendor payment configuration (inactive) + - Initial search index + - Audit log entry + ↓ +Email notification sent to vendor owner + ↓ +Vendor appears in admin vendor list +``` + +#### 1.2 Vendor Owner Account Activation +``` +Vendor Owner → Receives welcome email + ↓ +Click activation link → vendor.platform.com/admin/login + ↓ +POST /api/v1/vendor/auth/login + ↓ +Vendor context middleware detects vendor from subdomain + ↓ +Dashboard loads with vendor setup checklist: + - ✅ Account created + - ⏸️ Payment setup pending + - ⏸️ Marketplace integration pending + - ⏸️ First product pending +``` + +#### 1.3 Payment Configuration +``` +Vendor → Configure payments + ↓ +GET /api/v1/vendor/payments/config (returns needs_setup: true) + ↓ +POST /api/v1/vendor/payments/setup + ↓ +System creates Stripe Connect account + ↓ +Vendor redirected to Stripe onboarding + ↓ +Stripe webhook updates account status + ↓ +VendorPaymentConfig.accepts_payments = true + ↓ +Audit log: payment configuration completed +``` + +### Data States After Onboarding +- **Vendor**: Active with configured payments +- **User**: Owner with full permissions +- **Notifications**: Welcome sequence completed +- **Audit Trail**: Complete onboarding history + +--- + +## Workflow 2: Marketplace Import and Product Curation + +### Participants +- **Vendor**: Store owner/manager +- **Background System**: Import processing +- **Notification System**: Status updates + +### Workflow Steps + +#### 2.1 Marketplace Configuration +``` +Vendor → Configure Letzshop integration + ↓ +POST /api/v1/vendor/settings/marketplace + ↓ +Update Vendor.letzshop_csv_url + ↓ +Configuration validated and saved + ↓ +Audit log: marketplace configuration updated +``` + +#### 2.2 Import Execution +``` +Vendor → Trigger import + ↓ +POST /api/v1/vendor/marketplace/import + ↓ +System creates MarketplaceImportJob (status: pending) + ↓ +Background task queued: process_marketplace_import.delay(job_id) + ↓ +TaskLog created with progress tracking + ↓ +Celery worker processes import: + - Downloads CSV from marketplace + - Validates data format + - Creates MarketplaceProduct records (staging) + - Updates SearchIndex for browsing + - Generates product image thumbnails + - Updates job status with progress + ↓ +Email notification: import completed + ↓ +Audit log: import job completed +``` + +#### 2.3 Product Discovery and Selection +``` +Vendor → Browse imported products + ↓ +GET /api/v1/vendor/marketplace/imports/{job_id}/products + ↓ +Cache check for search results + ↓ +Display MarketplaceProduct records with: + - Search and filtering capabilities + - Thumbnail images + - Selection status indicators + - Bulk selection options + ↓ +Vendor → Select products for review + ↓ +POST /api/v1/vendor/marketplace/products/{id}/select + ↓ +MarketplaceProduct updated: + - is_selected: true + - selected_at: timestamp + ↓ +Search index updated + ↓ +Cache invalidation for product lists +``` + +#### 2.4 Product Customization and Publishing +``` +Vendor → Customize selected product + ↓ +Vendor uploads custom images: + ↓ +POST /api/v1/vendor/media/upload + ↓ +MediaService processes: + - Creates vendor-scoped file path + - Generates image variants (thumbnail, small, medium, large) + - Uploads to storage backend (local/S3) + - Creates MediaFile record + ↓ +Vendor customizes: + - SKU (vendor-specific) + - Price (markup from cost) + - Description (enhanced/localized) + - Images (vendor-uploaded + marketplace) + - Categories (vendor taxonomy) + - Inventory settings + ↓ +Vendor → Publish to catalog + ↓ +POST /api/v1/vendor/marketplace/products/{id}/publish + ↓ +System creates Product record: + - Vendor-customized data + - marketplace_product_id link + - is_active: true + ↓ +Updates: + - MarketplaceProduct: is_published: true + - SearchIndex: product catalog entry + - Cache invalidation: product catalogs + ↓ +Product now visible in vendor catalog + ↓ +Audit log: product published to catalog +``` + +### Data States After Publication +- **MarketplaceProduct**: Selected and published +- **Product**: Active in vendor catalog +- **MediaFile**: Vendor-specific product images +- **SearchIndex**: Searchable in catalog +- **Cache**: Invalidated and refreshed + +--- + +## Workflow 3: Customer Shopping Experience + +### Participants +- **Customer**: End user shopping +- **Vendor**: Store owner (indirect) +- **Search System**: Product discovery +- **Payment System**: Transaction processing + +### Workflow Steps + +#### 3.1 Store Discovery and Browsing +``` +Customer → Access vendor store + ↓ +vendor.platform.com OR platform.com/vendor/vendorname + ↓ +Vendor context middleware: + - Identifies vendor from URL + - Loads vendor-specific theme + - Sets vendor_id context for all operations + ↓ +GET /api/v1/public/vendors/{vendor_id}/shop-info + ↓ +Cache check for vendor theme and configuration + ↓ +Store homepage loads with vendor branding +``` + +#### 3.2 Product Search and Discovery +``` +Customer → Search for products + ↓ +GET /api/v1/public/vendors/{vendor_id}/products/search?q=query + ↓ +SearchService processes query: + - Cache check for search results + - Elasticsearch query (if available) OR database search + - Vendor-scoped results only + - Logs search query for analytics + ↓ +Results include: + - Product details with vendor customizations + - Media files (images with variants) + - Real-time inventory levels + - Vendor-specific pricing + ↓ +Search analytics updated + ↓ +Cache results for future queries +``` + +#### 3.3 Product Details and Media +``` +Customer → View product details + ↓ +GET /api/v1/public/vendors/{vendor_id}/products/{id} + ↓ +System returns: + - Product information from vendor catalog + - Media gallery with all variants + - Inventory availability + - Vendor-specific descriptions and pricing + ↓ +Media files served from CDN for performance +``` + +#### 3.4 Shopping Cart Management +``` +Customer → Add to cart + ↓ +POST /api/v1/public/vendors/{vendor_id}/cart/{session_id}/items + ↓ +System: + - Validates product availability + - Checks inventory levels + - Creates/updates session-based cart + - Price validation against current Product prices + ↓ +Cart data cached for session +``` + +#### 3.5 Customer Account Management +``` +Customer → Create account + ↓ +POST /api/v1/public/vendors/{vendor_id}/customers/register + ↓ +System creates: + - Customer record (vendor_id scoped) + - Email unique within vendor only + - Vendor-specific customer number + - Default notification preferences + ↓ +Welcome email sent using vendor \ No newline at end of file diff --git a/docs/__temp/14.updated_complete_project_structure_final.md b/docs/__temp/14.updated_complete_project_structure_final.md new file mode 100644 index 00000000..83ae1e61 --- /dev/null +++ b/docs/__temp/14.updated_complete_project_structure_final.md @@ -0,0 +1,510 @@ +# Multi-Tenant Ecommerce Platform - Complete Project Structure + +## Project Overview + +This document outlines the complete project structure for a production-ready multi-tenant ecommerce platform with marketplace integration. The platform implements complete vendor isolation with comprehensive business features including notifications, media management, search, caching, audit logging, and monitoring. + +## Technology Stack + +- **Backend**: Python FastAPI with PostgreSQL +- **Frontend**: Vanilla HTML, CSS, JavaScript with Alpine.js (CDN-based, no build step) +- **Background Jobs**: Celery with Redis +- **Search**: Elasticsearch with database fallback +- **Caching**: Redis multi-layer caching +- **Storage**: Local/S3 with CDN integration +- **Monitoring**: Custom monitoring with alerting +- **Deployment**: Docker with environment-based configuration + +## Complete Directory Structure + +``` +├── main.py # FastAPI application entry point +├── app/ +│ ├── api/ +│ │ ├── deps.py # Common dependencies (auth, context) +│ │ ├── main.py # API router setup +│ │ └── v1/ # API version 1 routes +│ │ ├── admin/ # Super admin endpoints +│ │ │ ├── __init__.py # Admin router aggregation +│ │ │ ├── auth.py # Admin authentication +│ │ │ ├── vendors.py # Vendor management (CRUD, bulk operations) +│ │ │ ├── dashboard.py # Admin dashboard & statistics +│ │ │ ├── users.py # User management across vendors +│ │ │ ├── marketplace.py # System-wide marketplace monitoring +│ │ │ ├── audit.py # Audit log endpoints +│ │ │ ├── settings.py # Platform settings management +│ │ │ ├── notifications.py # Admin notifications & alerts +│ │ │ └── monitoring.py # Platform monitoring & health checks +│ │ ├── vendor/ # Vendor-scoped endpoints +│ │ │ ├── __init__.py +│ │ │ ├── auth.py # Vendor team authentication +│ │ │ ├── dashboard.py # Vendor dashboard & statistics +│ │ │ ├── products.py # Vendor catalog management (Product table) +│ │ │ ├── marketplace.py # Marketplace import & selection (MarketplaceProduct) +│ │ │ ├── orders.py # Vendor order management +│ │ │ ├── customers.py # Vendor customer management +│ │ │ ├── teams.py # Team member management +│ │ │ ├── inventory.py # Inventory operations (vendor catalog) +│ │ │ ├── payments.py # Payment configuration & processing +│ │ │ ├── media.py # File and media management +│ │ │ ├── notifications.py # Vendor notification management +│ │ │ └── settings.py # Vendor settings & configuration +│ │ ├── public/ # Public customer-facing endpoints +│ │ │ ├── __init__.py +│ │ │ └── vendors/ # Vendor-specific public APIs +│ │ │ ├── shop.py # Public shop info +│ │ │ ├── products.py # Public product catalog (Product table only) +│ │ │ ├── search.py # Product search functionality +│ │ │ ├── cart.py # Shopping cart operations +│ │ │ ├── orders.py # Order placement +│ │ │ ├── payments.py # Payment processing +│ │ │ └── auth.py # Customer authentication +│ │ └── shared/ # Shared/utility endpoints +│ │ ├── health.py # Health checks +│ │ ├── webhooks.py # External webhooks (Stripe, etc.) +│ │ └── uploads.py # File upload handling +│ ├── core/ +│ │ ├── config.py # Configuration settings +│ │ ├── database.py # Database setup +│ │ ├── security.py # Security utilities (JWT, passwords) +│ │ └── lifespan.py # App lifecycle management +│ ├── exceptions/ # Custom exception handling +│ │ ├── __init__.py # All exception exports +│ │ ├── base.py # Base exception classes +│ │ ├── handler.py # Unified FastAPI exception handlers +│ │ ├── auth.py # Authentication/authorization exceptions +│ │ ├── admin.py # Admin operation exceptions +│ │ ├── marketplace.py # Import/marketplace exceptions +│ │ ├── marketplace_product.py # Marketplace staging exceptions +│ │ ├── product.py # Vendor catalog exceptions +│ │ ├── vendor.py # Vendor management exceptions +│ │ ├── customer.py # Customer management exceptions +│ │ ├── order.py # Order management exceptions +│ │ ├── payment.py # Payment processing exceptions +│ │ ├── inventory.py # Inventory management exceptions +│ │ ├── media.py # Media/file management exceptions +│ │ ├── notification.py # Notification exceptions +│ │ ├── search.py # Search exceptions +│ │ ├── monitoring.py # Monitoring exceptions +│ │ └── backup.py # Backup/recovery exceptions +│ └── services/ # Business logic layer +│ ├── auth_service.py # Authentication/authorization services +│ ├── admin_service.py # Admin services (vendor/user management) +│ ├── admin_audit_service.py # Audit logging services +│ ├── admin_settings_service.py # Platform settings services +│ ├── vendor_service.py # Vendor management services +│ ├── customer_service.py # Customer services (vendor-scoped) +│ ├── team_service.py # Team management services +│ ├── marketplace_service.py # Marketplace import services +│ ├── marketplace_product_service.py # Marketplace staging services +│ ├── product_service.py # Vendor catalog services (Product) +│ ├── order_service.py # Order services (vendor-scoped) +│ ├── payment_service.py # Payment processing services +│ ├── inventory_service.py # Inventory services (vendor catalog) +│ ├── media_service.py # File and media management services +│ ├── notification_service.py # Email/notification services +│ ├── search_service.py # Search and indexing services +│ ├── cache_service.py # Caching services +│ ├── monitoring_service.py # Application monitoring services +│ ├── backup_service.py # Backup and recovery services +│ ├── configuration_service.py # Configuration management services +│ └── stats_service.py # Statistics services (vendor-aware) +├── tasks/ # Background task processing +│ ├── __init__.py +│ ├── task_manager.py # Celery configuration and task management +│ ├── marketplace_import.py # Marketplace CSV import tasks +│ ├── email_tasks.py # Email sending tasks +│ ├── media_processing.py # Image processing and optimization tasks +│ ├── search_indexing.py # Search index maintenance tasks +│ ├── analytics_tasks.py # Analytics and reporting tasks +│ ├── cleanup_tasks.py # Data cleanup and maintenance tasks +│ └── backup_tasks.py # Backup and recovery tasks +├── models/ +│ ├── database/ # SQLAlchemy ORM models +│ │ ├── __init__.py # Import all models for easy access +│ │ ├── base.py # Base model class and common mixins (TimestampMixin) +│ │ ├── user.py # User model (with vendor relationships) +│ │ ├── vendor.py # Vendor, VendorUser, Role models +│ │ ├── customer.py # Customer, CustomerAddress models (vendor-scoped) +│ │ ├── marketplace_product.py # MarketplaceProduct model (staging data) +│ │ ├── product.py # Product model (vendor catalog) +│ │ ├── order.py # Order, OrderItem models (vendor-scoped) +│ │ ├── payment.py # Payment, PaymentMethod, VendorPaymentConfig models +│ │ ├── inventory.py # Inventory, InventoryMovement models (catalog products) +│ │ ├── marketplace.py # MarketplaceImportJob model +│ │ ├── media.py # MediaFile, ProductMedia models +│ │ ├── notification.py # NotificationTemplate, NotificationQueue, NotificationLog +│ │ ├── search.py # SearchIndex, SearchQuery models +│ │ ├── audit.py # AuditLog, DataExportLog models +│ │ ├── monitoring.py # PerformanceMetric, ErrorLog, SystemAlert models +│ │ ├── backup.py # BackupLog, RestoreLog models +│ │ ├── configuration.py # PlatformConfig, VendorConfig, FeatureFlag models +│ │ ├── task.py # TaskLog model +│ │ └── admin.py # Admin-specific models +│ │ # - AdminAuditLog: Track all admin actions +│ │ # - AdminNotification: System alerts for admins +│ │ # - AdminSetting: Platform-wide settings +│ │ # - PlatformAlert: System health alerts +│ │ # - AdminSession: Admin login session tracking +│ └── schema/ # Pydantic models for API validation +│ ├── __init__.py # Common imports +│ ├── base.py # Base Pydantic models +│ ├── auth.py # Login, Token, User response models +│ ├── vendor.py # Vendor management models +│ ├── customer.py # Customer request/response models +│ ├── team.py # Team management models +│ ├── marketplace_product.py # Marketplace staging models +│ ├── product.py # Vendor catalog models +│ ├── order.py # Order models (vendor-scoped) +│ ├── payment.py # Payment models +│ ├── inventory.py # Inventory operation models +│ ├── marketplace.py # Marketplace import job models +│ ├── media.py # Media/file management models +│ ├── notification.py # Notification models +│ ├── search.py # Search models +│ ├── monitoring.py # Monitoring models +│ ├── admin.py # Admin operation models +│ │ # - AdminAuditLog schemas (Response, Filters, List) +│ │ # - AdminNotification schemas (Create, Response, Update, List) +│ │ # - AdminSetting schemas (Create, Response, Update, List) +│ │ # - PlatformAlert schemas (Create, Response, Resolve, List) +│ │ # - BulkOperation schemas (BulkVendorAction, BulkUserAction) +│ │ # - AdminDashboardStats, SystemHealthResponse +│ │ # - AdminSession schemas +│ └── stats.py # Statistics response models +├── middleware/ +│ ├── auth.py # JWT authentication +│ ├── vendor_context.py # Vendor context detection and injection +│ ├── rate_limiter.py # Rate limiting +│ ├── logging_middleware.py # Request logging +│ └── decorators.py # Cross-cutting concern decorators +├── storage/ # Storage backends +│ ├── __init__.py +│ ├── backends.py # Storage backend implementations +│ └── utils.py # Storage utilities +├── static/ # Frontend assets (No build step!) +│ ├── admin/ # Super admin interface +│ │ ├── login.html # Admin login page +│ │ ├── dashboard.html # Admin dashboard +│ │ ├── vendors.html # Vendor management +│ │ ├── users.html # User management +│ │ ├── marketplace.html # System-wide marketplace monitoring +│ │ ├── audit_logs.html # Audit log viewer (NEW - TO CREATE) +│ │ ├── settings.html # Platform settings (NEW - TO CREATE) +│ │ ├── notifications.html # Admin notifications (NEW - TO CREATE) +│ │ └── monitoring.html # System monitoring +│ ├── vendor/ # Vendor admin interface +│ │ ├── login.html # Vendor team login (TO COMPLETE) +│ │ ├── dashboard.html # Vendor dashboard (TO COMPLETE) +│ │ └── admin/ # Vendor admin pages +│ │ ├── products.html # Catalog management (Product table) +│ │ ├── marketplace/ # Marketplace integration +│ │ │ ├── imports.html # Import jobs & history +│ │ │ ├── browse.html # Browse marketplace products (staging) +│ │ │ ├── selected.html # Selected products (pre-publish) +│ │ │ └── config.html # Marketplace configuration +│ │ ├── orders.html # Order management +│ │ ├── customers.html # Customer management +│ │ ├── teams.html # Team management +│ │ ├── inventory.html # Inventory management (catalog products) +│ │ ├── payments.html # Payment configuration +│ │ ├── media.html # Media library +│ │ ├── notifications.html # Notification templates & logs +│ │ └── settings.html # Vendor settings +│ ├── shop/ # Customer-facing shop interface +│ │ ├── home.html # Shop homepage +│ │ ├── products.html # Product catalog (Product table only) +│ │ ├── product.html # Product detail page +│ │ ├── search.html # Search results page +│ │ ├── cart.html # Shopping cart +│ │ ├── checkout.html # Checkout process +│ │ └── account/ # Customer account pages +│ │ ├── login.html # Customer login +│ │ ├── register.html # Customer registration +│ │ ├── profile.html # Customer profile +│ │ ├── orders.html # Order history +│ │ └── addresses.html # Address management +│ ├── css/ +│ │ ├── admin/ # Admin interface styles +│ │ │ └── admin.css +│ │ ├── vendor/ # Vendor interface styles +│ │ │ └── vendor.css +│ │ ├── shop/ # Customer shop styles +│ │ │ └── shop.css +│ │ ├── shared/ # Common styles (base.css, auth.css) +│ │ │ ├── base.css # CSS variables, utility classes +│ │ │ └── auth.css # Login/auth page styles +│ │ └── themes/ # Vendor-specific themes (future) +│ └── js/ +│ ├── shared/ # Common JavaScript utilities +│ │ ├── vendor-context.js # Vendor context detection & management +│ │ ├── api-client.js # API communication utilities +│ │ ├── notification.js # Notification handling +│ │ ├── media-upload.js # File upload utilities +│ │ └── utils.js # General utilities +│ ├── admin/ # Admin interface scripts (Alpine.js components) +│ │ ├── dashboard.js # Admin dashboard +│ │ ├── vendors.js # Vendor management +│ │ ├── audit-logs.js # Audit log viewer (NEW - TO CREATE) +│ │ ├── settings.js # Platform settings (NEW - TO CREATE) +│ │ ├── monitoring.js # System monitoring +│ │ └── analytics.js # Admin analytics +│ ├── vendor/ # Vendor interface scripts (Alpine.js components) +│ │ ├── products.js # Catalog management +│ │ ├── marketplace.js # Marketplace integration +│ │ ├── orders.js # Order management +│ │ ├── payments.js # Payment configuration +│ │ ├── media.js # Media management +│ │ └── dashboard.js # Vendor dashboard +│ └── shop/ # Customer shop scripts (Alpine.js components) +│ ├── catalog.js # Product browsing +│ ├── search.js # Product search +│ ├── cart.js # Shopping cart +│ ├── checkout.js # Checkout process +│ └── account.js # Customer account +├── tests/ # Comprehensive test suite +│ ├── unit/ # Unit tests +│ │ ├── services/ # Service layer tests +│ │ │ ├── test_admin_service.py +│ │ │ ├── test_admin_audit_service.py # NEW +│ │ │ ├── test_admin_settings_service.py # NEW +│ │ │ ├── test_marketplace_service.py +│ │ │ ├── test_product_service.py +│ │ │ ├── test_payment_service.py +│ │ │ ├── test_notification_service.py +│ │ │ ├── test_search_service.py +│ │ │ ├── test_media_service.py +│ │ │ └── test_cache_service.py +│ │ ├── models/ # Model tests +│ │ │ ├── test_admin_models.py # NEW +│ │ │ ├── test_marketplace_product.py +│ │ │ ├── test_product.py +│ │ │ ├── test_payment.py +│ │ │ └── test_vendor.py +│ │ └── api/ # API endpoint tests +│ │ ├── test_admin_api.py +│ │ ├── test_admin_audit_api.py # NEW +│ │ ├── test_admin_settings_api.py # NEW +│ │ ├── test_vendor_api.py +│ │ └── test_public_api.py +│ ├── integration/ # Integration tests +│ │ ├── test_marketplace_workflow.py +│ │ ├── test_order_workflow.py +│ │ ├── test_payment_workflow.py +│ │ ├── test_audit_workflow.py # NEW +│ │ └── test_notification_workflow.py +│ ├── e2e/ # End-to-end tests +│ │ ├── test_vendor_onboarding.py +│ │ ├── test_customer_journey.py +│ │ └── test_admin_operations.py +│ └── fixtures/ # Test data fixtures +│ ├── marketplace_data.py # Sample marketplace import data +│ ├── catalog_data.py # Sample vendor catalog data +│ ├── order_data.py # Sample order data +│ └── user_data.py # Sample user and vendor data +├── scripts/ # Utility scripts +│ ├── init_db.py # Database initialization +│ ├── create_admin.py # Create initial admin user +│ ├── init_platform_settings.py # Create default platform settings +│ ├── backup_database.py # Manual backup script +│ ├── seed_data.py # Development data seeding +│ └── migrate_data.py # Data migration utilities +├── docker/ # Docker configuration +│ ├── Dockerfile # Application container +│ ├── docker-compose.yml # Development environment +│ ├── docker-compose.prod.yml # Production environment +│ └── nginx.conf # Nginx configuration +├── docs/ # Documentation +│ ├── slices/ # Vertical slice documentation +│ │ ├── 00_slices_overview.md +│ │ ├── 00_implementation_roadmap.md +│ │ ├── 01_slice1_admin_vendor_foundation.md +│ │ ├── 02_slice2_marketplace_import.md +│ │ ├── 03_slice3_product_catalog.md +│ │ ├── 04_slice4_customer_shopping.md +│ │ └── 05_slice5_order_processing.md +│ ├── api/ # API documentation +│ ├── deployment/ # Deployment guides +│ ├── development/ # Development setup +│ ├── user_guides/ # User manuals +│ ├── 6.complete_naming_convention.md +│ ├── 10.stripe_payment_integration.md +│ ├── 12.project_readme_final.md +│ ├── 13.updated_application_workflows_final.md +│ └── 14.updated_complete_project_structure_final.md +├── .env.example # Environment variables template +├── requirements.txt # Python dependencies +├── requirements-dev.txt # Development dependencies +├── README.md # Project documentation +└── DEPLOYMENT.md # Deployment instructions +``` + +## Key Changes from Previous Version + +### Admin Infrastructure + +**Database Models** (`models/database/admin.py`): +- ✅ `AdminAuditLog` - Complete audit trail of all admin actions +- ✅ `AdminNotification` - System notifications for admins +- ✅ `AdminSetting` - Platform-wide configuration with encryption support +- ✅ `PlatformAlert` - System health and issue tracking +- ✅ `AdminSession` - Admin login session tracking + +**Pydantic Schemas** (`models/schema/admin.py`): +- ✅ Comprehensive request/response models for all admin operations +- ✅ Filtering and pagination schemas +- ✅ Bulk operation schemas (vendor/user actions) +- ✅ System health monitoring schemas + +**Services**: +- ✅ `admin_audit_service.py` - Audit logging functionality +- ✅ `admin_settings_service.py` - Platform settings with type conversion + +**API Endpoints**: +- ✅ `/api/v1/admin/audit/*` - Audit log querying and filtering +- ✅ `/api/v1/admin/settings/*` - Settings CRUD operations +- ✅ `/api/v1/admin/notifications/*` - Notifications & alerts (structure ready) + +### Naming Convention Fixes +- ✅ Changed `models/schemas/` to `models/schema/` (singular) +- ✅ All schema files now consistently use singular naming + +### Current Development Status (Slice 1) + +**Completed (✅)**: +- Backend database models (User, Vendor, Role, VendorUser, Admin models) +- JWT authentication with bcrypt +- Admin service layer with audit logging capability +- Admin API endpoints (CRUD, dashboard, audit, settings) +- Vendor context middleware +- Admin login page (Alpine.js) +- Admin dashboard (Alpine.js) +- Admin vendor creation page (Alpine.js) + +**In Progress (⏳)**: +- Vendor login page (frontend) +- Vendor dashboard page (frontend) +- Admin audit logs page (frontend) +- Admin platform settings page (frontend) + +**To Do (📋)**: +- Complete vendor login/dashboard pages +- Integrate audit logging into existing admin operations +- Create admin audit logs frontend +- Create platform settings frontend +- Full testing of Slice 1 +- Deployment to staging + +## Architecture Principles + +### Multi-Tenancy Model +- **Complete Vendor Isolation**: Each vendor operates independently with no data sharing +- **Chinese Wall**: Strict separation between vendor data and operations +- **Self-Service**: Vendors manage their own teams, products, and marketplace integrations +- **Scalable**: Single codebase serves all vendors with vendor-specific customization + +### Data Flow Architecture +``` +Marketplace CSV → MarketplaceProduct (staging) → Product (catalog) → Order → Analytics + ↓ + Admin Audit Logs → Platform Settings → Notifications → Monitoring +``` + +### API Structure +- **Admin APIs** (`/api/v1/admin/`): Platform-level administration + - Vendor management, user management, audit logs, settings, alerts +- **Vendor APIs** (`/api/v1/vendor/`): Vendor-scoped operations (requires vendor context) + - Products, orders, customers, teams, marketplace integration +- **Public APIs** (`/api/v1/public/vendors/{vendor_id}/`): Customer-facing operations + - Shop, products, cart, orders, checkout +- **Shared APIs** (`/api/v1/shared/`): Utility endpoints (health, webhooks) + +### Service Layer Architecture +- **Domain Services**: Each service handles one business domain +- **Cross-Cutting Services**: Audit, cache, monitoring, notifications +- **Integration Services**: Payments, search, storage + +### Frontend Architecture +- **Zero Build Step**: Alpine.js from CDN, vanilla CSS, no compilation +- **Server-Side Rendering**: Jinja2 templates with Alpine.js enhancement +- **Context-Aware**: Automatic vendor detection via subdomain or path +- **Dynamic Theming**: Vendor-specific customization via CSS variables +- **Role-Based UI**: Permission-driven interface elements + +## Key Features + +### Core Business Features +- Multi-vendor marketplace with complete isolation +- Marketplace product import and curation workflow +- Comprehensive order and payment processing +- Customer management with vendor-scoped accounts +- Team management with role-based permissions +- Inventory tracking and management + +### Advanced Features +- Email notification system with vendor branding +- File and media management with CDN integration +- Advanced search with Elasticsearch +- Multi-layer caching for performance +- Comprehensive audit logging for compliance +- Real-time monitoring and alerting +- Automated backup and disaster recovery +- Configuration management with feature flags + +### Security Features +- JWT-based authentication with vendor context +- Role-based access control with granular permissions +- Complete vendor data isolation at all layers +- Audit trails for all operations +- Secure file storage with access controls +- Rate limiting and abuse prevention + +### Integration Capabilities +- Stripe Connect for multi-vendor payments +- Marketplace integrations (Letzshop with extensible framework) +- Multiple storage backends (Local, S3, GCS) +- Search engines (Elasticsearch with database fallback) +- Email providers (SendGrid, SMTP) +- Monitoring and alerting systems + +## Technology Integration + +### Database Layer +- **PostgreSQL**: Primary database with ACID compliance (SQLite for development) +- **Redis**: Caching and session storage +- **Elasticsearch**: Search and analytics (optional) + +### Background Processing +- **Celery**: Distributed task processing +- **Redis**: Message broker and result backend +- **Monitoring**: Task progress and error tracking + +### External Services +- **Stripe Connect**: Multi-vendor payment processing +- **SendGrid/SMTP**: Email delivery with vendor branding +- **AWS S3/GCS**: Cloud storage for media files +- **CloudFront/CloudFlare**: CDN for static assets + +### Monitoring & Compliance +- **Admin Audit Logs**: Complete trail of all admin actions +- **Platform Settings**: Centralized configuration management +- **System Alerts**: Automated health monitoring +- **Error Tracking**: Comprehensive error logging + +## Deployment Modes + +### Development Mode +- **Path-based routing**: `localhost:8000/vendor/vendorname/` +- **Admin access**: `localhost:8000/admin/` +- **Easy context switching**: Clear vendor separation in URLs +- **Local storage**: Files stored locally with hot reload + +### Production Mode +- **Subdomain routing**: `vendorname.platform.com` +- **Admin subdomain**: `admin.platform.com` +- **Custom domains**: `customdomain.com` (enterprise feature) +- **CDN integration**: Optimized asset delivery +- **Distributed caching**: Redis cluster for performance +- **Automated backups**: Scheduled database and file backups + +This structure provides a robust foundation for a scalable, multi-tenant ecommerce platform with enterprise-grade features while maintaining clean separation of concerns and supporting multiple deployment modes. \ No newline at end of file diff --git a/docs/__temp/6.complete_naming_convention.md b/docs/__temp/6.complete_naming_convention.md new file mode 100644 index 00000000..a3a02f7c --- /dev/null +++ b/docs/__temp/6.complete_naming_convention.md @@ -0,0 +1,392 @@ +# Multi-Tenant Ecommerce Platform - Complete Naming Convention Guide + +## Overview + +This document establishes consistent naming conventions across the entire multi-tenant ecommerce platform. Consistent naming improves code readability, reduces developer confusion, and ensures maintainable architecture. + +## Core Principles + +### 1. Context-Based Naming +- **Collections/Endpoints**: Use PLURAL (handle multiple items) +- **Entities/Models**: Use SINGULAR (represent individual items) +- **Domains/Services**: Use SINGULAR (focus on one domain area) + +### 2. Terminology Standardization +- Use **"inventory"** not "stock" (more business-friendly) +- Use **"vendor"** not "shop" (multi-tenant architecture) +- Use **"customer"** not "user" for end customers (clarity) + +### 3. File Naming Patterns +- **API files**: `entities.py` (plural) +- **Model files**: `entity.py` (singular) +- **Service files**: `entity_service.py` (singular + service) +- **Exception files**: `entity.py` (singular domain) + +## Detailed Naming Rules + +### API Endpoint Files (PLURAL) +**Rule**: API files handle collections of resources, so use plural names. + +**Location**: `app/api/v1/*/` + +**Examples**: +``` +app/api/v1/admin/ +├── vendors.py # Handles multiple vendors +├── users.py # Handles multiple users +└── dashboard.py # Exception: not a resource collection + +app/api/v1/vendor/ +├── products.py # Handles vendor's products +├── orders.py # Handles vendor's orders +├── customers.py # Handles vendor's customers +├── teams.py # Handles team members +├── inventory.py # Handles inventory items +└── settings.py # Exception: not a resource collection + +app/api/v1/public/vendors/ +├── products.py # Public product catalog +├── orders.py # Order placement +└── auth.py # Exception: authentication service +``` + +**Rationale**: REST endpoints typically operate on collections (`GET /products`, `POST /orders`). + +### Database Model Files (SINGULAR) +**Rule**: Model files represent individual entity definitions, so use singular names. + +**Location**: `models/database/` + +**Examples**: +``` +models/database/ +├── user.py # User, UserProfile classes +├── vendor.py # Vendor, VendorUser, Role classes +├── customer.py # Customer, CustomerAddress classes +├── product.py # Product, ProductVariant classes +├── order.py # Order, OrderItem classes +├── inventory.py # Inventory, InventoryMovement classes +├── marketplace.py # MarketplaceImportJob class +└── admin.py # Admin-specific models +``` + +**Class Names Within Files**: +```python +# models/database/product.py +class Product(Base): # Singular +class ProductVariant(Base): # Singular + +# models/database/inventory.py +class Inventory(Base): # Singular +class InventoryMovement(Base): # Singular +``` + +**Rationale**: Each model class represents a single entity instance in the database. + +### Schema/Pydantic Model Files (SINGULAR) +**Rule**: Schema files define validation for individual entities, so use singular names. + +**Location**: `models/schema/` + +**Examples**: +``` +models/schema/ +├── user.py # UserCreate, UserResponse classes +├── vendor.py # VendorCreate, VendorResponse classes +├── customer.py # CustomerCreate, CustomerResponse classes +├── product.py # ProductCreate, ProductResponse classes +├── order.py # OrderCreate, OrderResponse classes +├── inventory.py # InventoryCreate, InventoryResponse classes +├── marketplace.py # MarketplaceImportRequest class +└── admin.py # Admin operation schemas +``` + +**Class Names Within Files**: +```python +# models/schema/product.py +class ProductCreate(BaseModel): # Singular entity +class ProductUpdate(BaseModel): # Singular entity +class ProductResponse(BaseModel): # Singular entity +``` + +**Rationale**: Schema models validate individual entity data structures. + +### Service Files (SINGULAR + "service") +**Rule**: Service files handle business logic for one domain area, so use singular + "service". + +**Location**: `services/` + +**Examples**: +``` +services/ +├── auth_service.py # Authentication domain +├── admin_service.py # Admin operations domain +├── vendor_service.py # Vendor management domain +├── customer_service.py # Customer operations domain +├── team_service.py # Team management domain +├── product_service.py # Product operations domain +├── order_service.py # Order operations domain +├── inventory_service.py # Inventory operations domain +├── marketplace_service.py # Marketplace integration domain +└── stats_service.py # Statistics domain +``` + +**Class Names Within Files**: +```python +# services/product_service.py +class ProductService: # Singular domain focus + def create_product() # Operates on single product + def get_products() # Can return multiple, but service is singular +``` + +**Rationale**: Each service focuses on one business domain area. + +### Exception Files (SINGULAR) +**Rule**: Exception files handle errors for one domain area, so use singular names. + +**Location**: `app/exceptions/` + +**Examples**: +``` +app/exceptions/ +├── base.py # Base exception classes +├── handler.py # Exception handlers +├── auth.py # Authentication domain exceptions +├── admin.py # Admin domain exceptions +├── vendor.py # Vendor domain exceptions +├── customer.py # Customer domain exceptions +├── product.py # Product domain exceptions +├── order.py # Order domain exceptions +├── inventory.py # Inventory domain exceptions +└── marketplace.py # Marketplace domain exceptions +``` + +**Class Names Within Files**: +```python +# app/exceptions/product.py +class ProductNotFoundException(ResourceNotFoundException): +class ProductAlreadyExistsException(ConflictException): +class ProductValidationException(ValidationException): +``` + +**Rationale**: Exception files are domain-focused, not collection-focused. + +### Middleware Files (DESCRIPTIVE) +**Rule**: Middleware files use descriptive names based on their function. + +**Location**: `middleware/` + +**Examples**: +``` +middleware/ +├── auth.py # Authentication middleware +├── vendor_context.py # Vendor context detection +├── rate_limiter.py # Rate limiting functionality +├── logging_middleware.py # Request logging +└── decorators.py # Cross-cutting decorators +``` + +**Rationale**: Middleware serves specific cross-cutting functions. + +### Frontend Files +**Rule**: Frontend files use context-appropriate naming. + +**Location**: `frontend/` + +**Examples**: +``` +frontend/ +├── admin/ +│ ├── vendors.html # PLURAL - lists multiple vendors +│ ├── users.html # PLURAL - lists multiple users +│ └── dashboard.html # SINGULAR - one dashboard +├── vendor/admin/ +│ ├── products.html # PLURAL - lists multiple products +│ ├── orders.html # PLURAL - lists multiple orders +│ ├── teams.html # PLURAL - lists team members +│ └── dashboard.html # SINGULAR - one dashboard +└── shop/ + ├── products.html # PLURAL - product catalog + ├── product.html # SINGULAR - single product detail + ├── orders.html # PLURAL - order history + └── cart.html # SINGULAR - one shopping cart +``` + +**Rationale**: +- List views are plural (show collections) +- Detail views are singular (show individual items) +- Functional views use descriptive names + +## Terminology Standards + +### Core Business Terms + +| Use This | Not This | Context | +|----------|----------|---------| +| inventory | stock | All inventory management | +| vendor | shop | Multi-tenant architecture | +| customer | user | End customers (buyers) | +| user | member | Platform/vendor team members | +| team | staff | Vendor team members | +| order | purchase | Customer orders | +| product | item | Catalog products | + +### Database Naming + +**Table Names**: Use singular, lowercase with underscores +```sql +-- Correct +inventory +inventory_movements +vendor_users + +-- Incorrect +inventories +inventory_movement +vendorusers +``` + +**Column Names**: Use singular, descriptive names +```sql +-- Correct +vendor_id +inventory_level +created_at + +-- Incorrect +vendors_id +inventory_levels +creation_time +``` + +### API Endpoint Patterns + +**Resource Collections**: Use plural nouns +``` +GET /api/v1/vendor/products # List products +POST /api/v1/vendor/products # Create product +GET /api/v1/vendor/orders # List orders +POST /api/v1/vendor/orders # Create order +``` + +**Individual Resources**: Use singular in URL structure +``` +GET /api/v1/vendor/products/{id} # Get single product +PUT /api/v1/vendor/products/{id} # Update single product +DELETE /api/v1/vendor/products/{id} # Delete single product +``` + +**Non-Resource Endpoints**: Use descriptive names +``` +GET /api/v1/vendor/dashboard/stats # Dashboard statistics +POST /api/v1/vendor/auth/login # Authentication +GET /api/v1/vendor/settings # Vendor settings +``` + +## Variable and Function Naming + +### Function Names +```python +# Correct - verb + singular object +def create_product() +def get_customer() +def update_order() +def delete_inventory_item() + +# Correct - verb + plural when operating on collections +def get_products() +def list_customers() +def bulk_update_orders() + +# Incorrect +def create_products() # Creates one product +def get_customers() # Gets one customer +``` + +### Variable Names +```python +# Correct - context-appropriate singular/plural +product = get_product(id) +products = get_products() +customer_list = get_all_customers() +inventory_count = len(inventory_items) + +# Incorrect +products = get_product(id) # Single item, should be singular +product = get_products() # Multiple items, should be plural +``` + +### Class Attributes +```python +# Correct - descriptive and consistent +class Vendor: + id: int + name: str + subdomain: str + owner_user_id: int # Singular reference + created_at: datetime + +class Customer: + vendor_id: int # Belongs to one vendor + total_orders: int # Aggregate count + last_order_date: datetime # Most recent +``` + +## Migration Checklist + +When applying these naming conventions to existing code: + +### File Renames Required +- [ ] `app/api/v1/stock.py` → `app/api/v1/inventory.py` +- [ ] `models/database/stock.py` → `models/database/inventory.py` +- [ ] `models/schema/stock.py` → `models/schema/inventory.py` +- [ ] `services/stock_service.py` → `services/inventory_service.py` +- [ ] `app/exceptions/stock.py` → `app/exceptions/inventory.py` + +### Import Statement Updates +- [ ] Update all `from models.database.stock import` statements +- [ ] Update all `from services.stock_service import` statements +- [ ] Update all `stock_` variable prefixes to `inventory_` + +### Class Name Updates +- [ ] `Stock` → `Inventory` +- [ ] `StockMovement` → `InventoryMovement` +- [ ] `StockService` → `InventoryService` + +### Database Updates +- [ ] Rename `stock` table to `inventory` +- [ ] Rename `stock_movements` table to `inventory_movements` +- [ ] Update all `stock_id` foreign keys to `inventory_id` + +### Frontend Updates +- [ ] Update all HTML files with stock terminology +- [ ] Update JavaScript variable names +- [ ] Update CSS class names if applicable + +## Benefits of Consistent Naming + +1. **Developer Productivity**: Predictable file locations and naming patterns +2. **Code Readability**: Clear understanding of file purposes and contents +3. **Team Communication**: Shared vocabulary and terminology +4. **Maintenance**: Easier to locate and update related functionality +5. **Onboarding**: New developers quickly understand the codebase structure +6. **Documentation**: Consistent terminology across all documentation +7. **API Usability**: Predictable and intuitive API endpoint structures + +## Enforcement + +### Code Review Checklist +- [ ] File names follow singular/plural conventions +- [ ] Class names use appropriate terminology (inventory vs stock) +- [ ] API endpoints use plural resource names +- [ ] Database models use singular names +- [ ] Variables names match their content (singular vs plural) + +### Automated Checks +Consider implementing linting rules or pre-commit hooks to enforce: +- File naming patterns +- Import statement consistency +- Variable naming conventions +- API endpoint patterns + +This naming convention guide ensures consistent, maintainable, and intuitive code across the entire multi-tenant ecommerce platform. \ No newline at end of file diff --git a/docs/__temp/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md b/docs/__temp/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md new file mode 100644 index 00000000..d6276a31 --- /dev/null +++ b/docs/__temp/ARCHITECTURE_TEMP/STATS_SERVICE_ARCHITECTURE.md @@ -0,0 +1,6 @@ +# Recommended Statistics Service Architecture + +## Principle: Single Responsibility + +**stats_service** should handle ALL statistics - it's the single source of truth for metrics. +**admin_service** should handle admin operations - user management, vendor management, etc. \ No newline at end of file diff --git a/docs/__temp/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md b/docs/__temp/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md new file mode 100644 index 00000000..77900948 --- /dev/null +++ b/docs/__temp/BACKEND/ADMIN_FEATURE_INTEGRATION_GUIDE.md @@ -0,0 +1,1799 @@ +# Admin Feature Integration Guide + +## Overview + +This guide provides a step-by-step process for adding new admin features to the multi-tenant e-commerce platform. It ensures consistency with the established architecture patterns, naming conventions, and best practices. + +## Table of Contents + +1. [Planning Phase](#planning-phase) +2. [Database Layer](#database-layer) +3. [Schema Layer (Pydantic)](#schema-layer-pydantic) +4. [Exception Layer](#exception-layer) +5. [Service Layer](#service-layer) +6. [API Layer (Endpoints)](#api-layer-endpoints) +7. [Frontend Layer](#frontend-layer) +8. [Testing](#testing) +9. [Documentation](#documentation) +10. [Deployment Checklist](#deployment-checklist) + +--- + +## Planning Phase + +### 1. Define the Feature + +**Questions to Answer:** +- What is the feature name? (Use singular for entity, plural for collections) +- What business problem does it solve? +- What are the CRUD operations needed? +- What relationships exist with other entities? +- What validation rules apply? +- What permissions are required? + +**Example: Adding "Vendor Domains" Feature** +``` +Feature Name: Vendor Domains +Purpose: Allow vendors to use custom domains +Operations: Create, Read, Update, Delete, Verify +Relationships: Belongs to Vendor +Validation: Domain format, uniqueness, DNS verification +Permissions: Admin only +``` + +### 2. Review Naming Conventions + +**Consult:** `6_complete_naming_convention.md` + +**Key Rules:** +- **Collections/Endpoints**: PLURAL (`vendors.py`, `domains.py`) +- **Entities/Models**: SINGULAR (`vendor.py`, `domain.py`) +- **Services**: SINGULAR + "service" (`vendor_service.py`, `domain_service.py`) +- **Exceptions**: SINGULAR domain name (`vendor.py`, `domain.py`) + +### 3. Identify Dependencies + +**Check existing modules:** +- Related database models +- Existing services to reuse +- Exception types needed +- Authentication requirements + +--- + +## Database Layer + +### Step 1: Create Database Model + +**Location:** `models/database/{entity}.py` (SINGULAR) + +**File Naming:** Use singular entity name +- ✅ `models/database/vendor_domain.py` +- ❌ `models/database/vendor_domains.py` + +**Template:** + +```python +# models/database/vendor_domain.py +""" +VendorDomain database model. + +This model represents custom domains for vendors. +""" + +from datetime import datetime, timezone +from sqlalchemy import ( + Column, Integer, String, Boolean, DateTime, + ForeignKey, UniqueConstraint, Index +) +from sqlalchemy.orm import relationship + +from app.core.database import Base +from models.database.base import TimestampMixin + + +class VendorDomain(Base, TimestampMixin): + """ + Custom domain mapping for vendors. + + Allows vendors to use their own domains (e.g., myshop.com) + instead of subdomains (vendor1.platform.com). + """ + __tablename__ = "vendor_domains" # PLURAL table name + + # Primary Key + id = Column(Integer, primary_key=True, index=True) + + # Foreign Keys + vendor_id = Column( + Integer, + ForeignKey("vendors.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + # Domain Configuration + domain = Column(String(255), nullable=False, unique=True, index=True) + is_primary = Column(Boolean, default=False, nullable=False) + is_active = Column(Boolean, default=True, nullable=False) + + # Verification + is_verified = Column(Boolean, default=False, nullable=False) + verification_token = Column(String(100), unique=True, nullable=True) + verified_at = Column(DateTime(timezone=True), nullable=True) + + # SSL Status + ssl_status = Column(String(50), default="pending") + ssl_verified_at = Column(DateTime(timezone=True), nullable=True) + + # Relationships + vendor = relationship("Vendor", back_populates="domains") + + # Constraints and Indexes + __table_args__ = ( + UniqueConstraint('vendor_id', 'domain', name='uq_vendor_domain'), + Index('idx_domain_active', 'domain', 'is_active'), + Index('idx_vendor_primary', 'vendor_id', 'is_primary'), + ) + + def __repr__(self): + return f"" + + # Helper Methods + @classmethod + def normalize_domain(cls, domain: str) -> str: + """Normalize domain for consistent storage.""" + domain = domain.replace("https://", "").replace("http://", "") + domain = domain.rstrip("/") + domain = domain.lower() + return domain + + @property + def full_url(self): + """Return full URL with https.""" + return f"https://{self.domain}" +``` + +**Key Points:** +- Use `TimestampMixin` for `created_at`/`updated_at` +- Add proper indexes for query performance +- Include foreign key constraints with `ondelete` behavior +- Use singular class name, plural table name +- Add docstrings for class and complex methods +- Include `__repr__` for debugging + +### Step 2: Update Related Models + +**Location:** Update the parent model (e.g., `models/database/vendor.py`) + +```python +# models/database/vendor.py + +class Vendor(Base, TimestampMixin): + __tablename__ = "vendors" + + # ... existing fields ... + + # Add relationship + domains = relationship( + "VendorDomain", + back_populates="vendor", + cascade="all, delete-orphan", + order_by="VendorDomain.is_primary.desc()" + ) + + # Helper method + @property + def primary_domain(self): + """Get the primary custom domain for this vendor.""" + for domain in self.domains: + if domain.is_primary and domain.is_active: + return domain.domain + return None +``` + +### Step 3: Create Database Migration + +```bash +# Create migration +alembic revision --autogenerate -m "Add vendor_domains table" + +# Review the generated migration file +# Edit if needed for data migrations or complex changes + +# Apply migration +alembic upgrade head +``` + +**Migration Checklist:** +- [ ] Table created with correct name +- [ ] All columns present with correct types +- [ ] Indexes created +- [ ] Foreign keys configured +- [ ] Unique constraints added +- [ ] Default values set + +--- + +## Schema Layer (Pydantic) + +### Step 1: Create Pydantic Schemas + +**Location:** `models/schema/{entity}.py` (SINGULAR) + +**File Naming:** Use singular entity name +- ✅ `models/schema/vendor_domain.py` +- ❌ `models/schema/vendor_domains.py` + +**Template:** + +```python +# models/schema/vendor_domain.py +""" +Pydantic schemas for VendorDomain operations. + +Schemas include: +- VendorDomainCreate: For adding custom domains +- VendorDomainUpdate: For updating domain settings +- VendorDomainResponse: Standard domain response +- VendorDomainListResponse: Paginated domain list +""" + +import re +from datetime import datetime +from typing import List, Optional, Dict +from pydantic import BaseModel, ConfigDict, Field, field_validator + + +# ============================================================================ +# Request Schemas (Input) +# ============================================================================ + +class VendorDomainCreate(BaseModel): + """Schema for adding a custom domain to vendor.""" + + domain: str = Field( + ..., + description="Custom domain (e.g., myshop.com)", + min_length=3, + max_length=255, + examples=["myshop.com", "shop.mybrand.com"] + ) + is_primary: bool = Field( + default=False, + description="Set as primary domain for the vendor" + ) + + @field_validator('domain') + @classmethod + def validate_domain(cls, v: str) -> str: + """Validate and normalize domain.""" + # Remove protocol if present + domain = v.replace("https://", "").replace("http://", "") + + # Remove trailing slash + domain = domain.rstrip("/") + + # Convert to lowercase + domain = domain.lower().strip() + + # Basic validation + if not domain or '/' in domain: + raise ValueError("Invalid domain format") + + if '.' not in domain: + raise ValueError("Domain must have at least one dot") + + # Check for reserved subdomains + reserved = ['www', 'admin', 'api', 'mail', 'smtp', 'ftp'] + first_part = domain.split('.')[0] + if first_part in reserved: + raise ValueError(f"Domain cannot start with reserved subdomain: {first_part}") + + # Validate domain format (basic regex) + pattern = r'^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$' + if not re.match(pattern, domain): + raise ValueError("Invalid domain format") + + return domain + + +class VendorDomainUpdate(BaseModel): + """Schema for updating vendor domain settings.""" + + is_primary: Optional[bool] = Field(None, description="Set as primary domain") + is_active: Optional[bool] = Field(None, description="Activate or deactivate domain") + + model_config = ConfigDict(from_attributes=True) + + +# ============================================================================ +# Response Schemas (Output) +# ============================================================================ + +class VendorDomainResponse(BaseModel): + """Standard schema for vendor domain response.""" + model_config = ConfigDict(from_attributes=True) + + id: int + vendor_id: int + domain: str + is_primary: bool + is_active: bool + is_verified: bool + ssl_status: str + verification_token: Optional[str] = None + verified_at: Optional[datetime] = None + ssl_verified_at: Optional[datetime] = None + created_at: datetime + updated_at: datetime + + +class VendorDomainListResponse(BaseModel): + """Schema for paginated vendor domain list.""" + + domains: List[VendorDomainResponse] + total: int + + +# ============================================================================ +# Specialized Schemas +# ============================================================================ + +class DomainVerificationInstructions(BaseModel): + """DNS verification instructions for domain ownership.""" + + domain: str + verification_token: str + instructions: Dict[str, str] + txt_record: Dict[str, str] + common_registrars: Dict[str, str] + + +class DomainVerificationResponse(BaseModel): + """Response after domain verification.""" + + message: str + domain: str + verified_at: datetime + is_verified: bool +``` + +**Key Points:** +- Separate request schemas (Create, Update) from response schemas +- Use `Field()` for descriptions and examples +- Add validators with `@field_validator` +- Use `ConfigDict(from_attributes=True)` for ORM compatibility +- Document all fields with descriptions +- Use appropriate types (Optional, List, Dict) + +--- + +## Exception Layer + +### Step 1: Create Custom Exceptions + +**Location:** `app/exceptions/{domain}.py` (SINGULAR) + +**File Naming:** Use singular domain name +- ✅ `app/exceptions/vendor_domain.py` +- ❌ `app/exceptions/vendor_domains.py` + +**Template:** + +```python +# app/exceptions/vendor_domain.py +""" +Vendor domain management specific exceptions. + +All exceptions follow the pattern: +- Inherit from appropriate base exception +- Include relevant context in details dict +- Use proper HTTP status codes +- Provide clear, actionable error messages +""" + +from typing import Any, Dict, Optional +from .base import ( + ResourceNotFoundException, + ConflictException, + ValidationException, + BusinessLogicException, + ExternalServiceException +) + + +# ============================================================================ +# Resource Not Found (404) +# ============================================================================ + +class VendorDomainNotFoundException(ResourceNotFoundException): + """Raised when a vendor domain is not found.""" + + def __init__(self, domain_identifier: str, identifier_type: str = "ID"): + if identifier_type.lower() == "domain": + message = f"Domain '{domain_identifier}' not found" + else: + message = f"Domain with ID '{domain_identifier}' not found" + + super().__init__( + resource_type="VendorDomain", + identifier=domain_identifier, + message=message, + error_code="VENDOR_DOMAIN_NOT_FOUND", + ) + + +# ============================================================================ +# Conflicts (409) +# ============================================================================ + +class VendorDomainAlreadyExistsException(ConflictException): + """Raised when trying to add a domain that already exists.""" + + def __init__(self, domain: str, existing_vendor_id: Optional[int] = None): + details = {"domain": domain} + if existing_vendor_id: + details["existing_vendor_id"] = existing_vendor_id + + super().__init__( + message=f"Domain '{domain}' is already registered", + error_code="VENDOR_DOMAIN_ALREADY_EXISTS", + details=details, + ) + + +# ============================================================================ +# Validation Errors (422) +# ============================================================================ + +class InvalidDomainFormatException(ValidationException): + """Raised when domain format is invalid.""" + + def __init__(self, domain: str, reason: str = "Invalid domain format"): + super().__init__( + message=f"{reason}: {domain}", + field="domain", + details={"domain": domain, "reason": reason}, + ) + self.error_code = "INVALID_DOMAIN_FORMAT" + + +class ReservedDomainException(ValidationException): + """Raised when trying to use a reserved domain.""" + + def __init__(self, domain: str, reserved_part: str): + super().__init__( + message=f"Domain cannot use reserved subdomain: {reserved_part}", + field="domain", + details={"domain": domain, "reserved_part": reserved_part}, + ) + self.error_code = "RESERVED_DOMAIN" + + +# ============================================================================ +# Business Logic Errors (400) +# ============================================================================ + +class DomainNotVerifiedException(BusinessLogicException): + """Raised when trying to activate an unverified domain.""" + + def __init__(self, domain_id: int, domain: str): + super().__init__( + message=f"Domain '{domain}' must be verified before activation", + error_code="DOMAIN_NOT_VERIFIED", + details={"domain_id": domain_id, "domain": domain}, + ) + + +class DomainVerificationFailedException(BusinessLogicException): + """Raised when domain verification fails.""" + + def __init__(self, domain: str, reason: str): + super().__init__( + message=f"Domain verification failed for '{domain}': {reason}", + error_code="DOMAIN_VERIFICATION_FAILED", + details={"domain": domain, "reason": reason}, + ) + + +class MaxDomainsReachedException(BusinessLogicException): + """Raised when vendor tries to add more domains than allowed.""" + + def __init__(self, vendor_id: int, max_domains: int): + super().__init__( + message=f"Maximum number of domains reached ({max_domains})", + error_code="MAX_DOMAINS_REACHED", + details={"vendor_id": vendor_id, "max_domains": max_domains}, + ) + + +# ============================================================================ +# External Service Errors (502) +# ============================================================================ + +class DNSVerificationException(ExternalServiceException): + """Raised when DNS verification service fails.""" + + def __init__(self, domain: str, reason: str): + super().__init__( + service_name="DNS", + message=f"DNS verification failed for '{domain}': {reason}", + error_code="DNS_VERIFICATION_ERROR", + details={"domain": domain, "reason": reason}, + ) +``` + +**Exception Categories:** + +| HTTP Status | Base Exception | Use Case | +|-------------|----------------|----------| +| 400 | `BusinessLogicException` | Business rule violations | +| 401 | `AuthenticationException` | Authentication failures | +| 403 | `AuthorizationException` | Permission denied | +| 404 | `ResourceNotFoundException` | Resource not found | +| 409 | `ConflictException` | Resource conflicts | +| 422 | `ValidationException` | Input validation errors | +| 429 | `RateLimitException` | Rate limiting | +| 500 | `LetzShopException` | Generic errors | +| 502 | `ExternalServiceException` | Third-party failures | + +### Step 2: Update Exception Exports + +**Location:** `app/exceptions/__init__.py` + +```python +# app/exceptions/__init__.py + +# ... existing imports ... + +# Vendor domain exceptions +from .vendor_domain import ( + VendorDomainNotFoundException, + VendorDomainAlreadyExistsException, + InvalidDomainFormatException, + ReservedDomainException, + DomainNotVerifiedException, + DomainVerificationFailedException, + MaxDomainsReachedException, + DNSVerificationException, +) + +__all__ = [ + # ... existing exports ... + + # Vendor Domain + "VendorDomainNotFoundException", + "VendorDomainAlreadyExistsException", + "InvalidDomainFormatException", + "ReservedDomainException", + "DomainNotVerifiedException", + "DomainVerificationFailedException", + "MaxDomainsReachedException", + "DNSVerificationException", +] +``` + +--- + +## Service Layer + +### Step 1: Create Service Class + +**Location:** `app/services/{entity}_service.py` (SINGULAR + service) + +**File Naming:** Use singular entity name + "_service" +- ✅ `app/services/vendor_domain_service.py` +- ❌ `app/services/vendor_domains_service.py` + +**Template:** + +```python +# app/services/vendor_domain_service.py +""" +Vendor domain service for managing custom domain operations. + +This module provides classes and functions for: +- Adding and removing custom domains +- Domain verification via DNS +- Domain activation and deactivation +- Setting primary domains +- Domain validation and normalization +""" + +import logging +import secrets +from typing import List, Tuple, Optional +from datetime import datetime, timezone + +from sqlalchemy.orm import Session + +from app.exceptions import ( + VendorNotFoundException, + VendorDomainNotFoundException, + VendorDomainAlreadyExistsException, + InvalidDomainFormatException, + DomainNotVerifiedException, + DomainVerificationFailedException, + MaxDomainsReachedException, + ValidationException, +) +from models.schema.vendor_domain import VendorDomainCreate, VendorDomainUpdate +from models.database.vendor import Vendor +from models.database.vendor_domain import VendorDomain + +logger = logging.getLogger(__name__) + + +class VendorDomainService: + """Service class for vendor domain operations.""" + + def __init__(self): + """Initialize service with configuration.""" + self.max_domains_per_vendor = 10 + self.reserved_subdomains = ['www', 'admin', 'api', 'mail', 'smtp', 'ftp'] + + def add_domain( + self, + db: Session, + vendor_id: int, + domain_data: VendorDomainCreate + ) -> VendorDomain: + """ + Add a custom domain to vendor. + + Args: + db: Database session + vendor_id: Vendor ID to add domain to + domain_data: Domain creation data + + Returns: + Created VendorDomain object + + Raises: + VendorNotFoundException: If vendor not found + VendorDomainAlreadyExistsException: If domain already registered + MaxDomainsReachedException: If vendor has reached max domains + InvalidDomainFormatException: If domain format is invalid + """ + try: + # 1. Verify vendor exists + vendor = self._get_vendor_by_id_or_raise(db, vendor_id) + + # 2. Check domain limit + self._check_domain_limit(db, vendor_id) + + # 3. Normalize domain + normalized_domain = VendorDomain.normalize_domain(domain_data.domain) + + # 4. Validate domain format + self._validate_domain_format(normalized_domain) + + # 5. Check if domain already exists + if self._domain_exists(db, normalized_domain): + existing = db.query(VendorDomain).filter( + VendorDomain.domain == normalized_domain + ).first() + raise VendorDomainAlreadyExistsException( + normalized_domain, + existing.vendor_id if existing else None + ) + + # 6. If setting as primary, unset other primary domains + if domain_data.is_primary: + self._unset_primary_domains(db, vendor_id) + + # 7. Create domain record + new_domain = VendorDomain( + vendor_id=vendor_id, + domain=normalized_domain, + is_primary=domain_data.is_primary, + verification_token=secrets.token_urlsafe(32), + is_verified=False, + is_active=False, + ssl_status="pending" + ) + + db.add(new_domain) + db.commit() + db.refresh(new_domain) + + logger.info(f"Domain {normalized_domain} added to vendor {vendor_id}") + return new_domain + + except ( + VendorNotFoundException, + VendorDomainAlreadyExistsException, + MaxDomainsReachedException, + InvalidDomainFormatException + ): + db.rollback() + raise + except Exception as e: + db.rollback() + logger.error(f"Error adding domain: {str(e)}") + raise ValidationException("Failed to add domain") + + def get_vendor_domains( + self, + db: Session, + vendor_id: int + ) -> List[VendorDomain]: + """ + Get all domains for a vendor. + + Args: + db: Database session + vendor_id: Vendor ID + + Returns: + List of VendorDomain objects + + Raises: + VendorNotFoundException: If vendor not found + """ + try: + # Verify vendor exists + self._get_vendor_by_id_or_raise(db, vendor_id) + + domains = db.query(VendorDomain).filter( + VendorDomain.vendor_id == vendor_id + ).order_by( + VendorDomain.is_primary.desc(), + VendorDomain.created_at.desc() + ).all() + + return domains + + except VendorNotFoundException: + raise + except Exception as e: + logger.error(f"Error getting vendor domains: {str(e)}") + raise ValidationException("Failed to retrieve domains") + + def update_domain( + self, + db: Session, + domain_id: int, + domain_update: VendorDomainUpdate + ) -> VendorDomain: + """ + Update domain settings. + + Args: + db: Database session + domain_id: Domain ID + domain_update: Update data + + Returns: + Updated VendorDomain object + + Raises: + VendorDomainNotFoundException: If domain not found + DomainNotVerifiedException: If trying to activate unverified domain + """ + try: + domain = self._get_domain_by_id_or_raise(db, domain_id) + + # If setting as primary, unset other primary domains + if domain_update.is_primary: + self._unset_primary_domains(db, domain.vendor_id, exclude_domain_id=domain_id) + domain.is_primary = True + + # If activating, check verification + if domain_update.is_active is True and not domain.is_verified: + raise DomainNotVerifiedException(domain_id, domain.domain) + + # Update fields + if domain_update.is_active is not None: + domain.is_active = domain_update.is_active + + db.commit() + db.refresh(domain) + + logger.info(f"Domain {domain.domain} updated") + return domain + + except (VendorDomainNotFoundException, DomainNotVerifiedException): + db.rollback() + raise + except Exception as e: + db.rollback() + logger.error(f"Error updating domain: {str(e)}") + raise ValidationException("Failed to update domain") + + def delete_domain( + self, + db: Session, + domain_id: int + ) -> str: + """ + Delete a custom domain. + + Args: + db: Database session + domain_id: Domain ID + + Returns: + Success message + + Raises: + VendorDomainNotFoundException: If domain not found + """ + try: + domain = self._get_domain_by_id_or_raise(db, domain_id) + domain_name = domain.domain + vendor_id = domain.vendor_id + + db.delete(domain) + db.commit() + + logger.info(f"Domain {domain_name} deleted from vendor {vendor_id}") + return f"Domain {domain_name} deleted successfully" + + except VendorDomainNotFoundException: + db.rollback() + raise + except Exception as e: + db.rollback() + logger.error(f"Error deleting domain: {str(e)}") + raise ValidationException("Failed to delete domain") + + # ======================================================================== + # Private Helper Methods (use _ prefix) + # ======================================================================== + + def _get_vendor_by_id_or_raise(self, db: Session, vendor_id: int) -> Vendor: + """Get vendor by ID or raise exception.""" + vendor = db.query(Vendor).filter(Vendor.id == vendor_id).first() + if not vendor: + raise VendorNotFoundException(str(vendor_id), identifier_type="id") + return vendor + + def _get_domain_by_id_or_raise(self, db: Session, domain_id: int) -> VendorDomain: + """Get domain by ID or raise exception.""" + domain = db.query(VendorDomain).filter(VendorDomain.id == domain_id).first() + if not domain: + raise VendorDomainNotFoundException(str(domain_id)) + return domain + + def _check_domain_limit(self, db: Session, vendor_id: int) -> None: + """Check if vendor has reached maximum domain limit.""" + domain_count = db.query(VendorDomain).filter( + VendorDomain.vendor_id == vendor_id + ).count() + + if domain_count >= self.max_domains_per_vendor: + raise MaxDomainsReachedException(vendor_id, self.max_domains_per_vendor) + + def _domain_exists(self, db: Session, domain: str) -> bool: + """Check if domain already exists in system.""" + return db.query(VendorDomain).filter( + VendorDomain.domain == domain + ).first() is not None + + def _validate_domain_format(self, domain: str) -> None: + """Validate domain format and check for reserved subdomains.""" + first_part = domain.split('.')[0] + if first_part in self.reserved_subdomains: + from app.exceptions import ReservedDomainException + raise ReservedDomainException(domain, first_part) + + def _unset_primary_domains( + self, + db: Session, + vendor_id: int, + exclude_domain_id: Optional[int] = None + ) -> None: + """Unset all primary domains for vendor.""" + query = db.query(VendorDomain).filter( + VendorDomain.vendor_id == vendor_id, + VendorDomain.is_primary == True + ) + + if exclude_domain_id: + query = query.filter(VendorDomain.id != exclude_domain_id) + + query.update({"is_primary": False}) + + +# Create service instance +vendor_domain_service = VendorDomainService() +``` + +**Service Layer Best Practices:** +- ✅ All business logic in service layer +- ✅ Raise custom exceptions (not HTTPException) +- ✅ Transaction management (commit/rollback) +- ✅ Comprehensive logging +- ✅ Helper methods with `_` prefix +- ✅ Detailed docstrings +- ✅ Type hints for all methods +- ✅ Create singleton instance at bottom + +--- + +## API Layer (Endpoints) + +### Step 1: Create API Endpoints + +**Location:** `app/api/v1/admin/{entities}.py` (PLURAL) + +**File Naming:** Use plural entity name +- ✅ `app/api/v1/admin/vendor_domains.py` +- ❌ `app/api/v1/admin/vendor_domain.py` + +**Template:** + +```python +# app/api/v1/admin/vendor_domains.py +""" +Admin endpoints for managing vendor custom domains. + +Follows the architecture pattern: +- Endpoints only handle HTTP layer +- Business logic in service layer +- Proper exception handling +- Pydantic schemas for validation +""" + +import logging +from typing import List + +from fastapi import APIRouter, Depends, Path, Body, Query +from sqlalchemy.orm import Session + +from app.api.deps import get_current_admin_user, get_db +from app.services.vendor_domain_service import vendor_domain_service +from app.exceptions import VendorNotFoundException +from models.schema.vendor_domain import ( + VendorDomainCreate, + VendorDomainUpdate, + VendorDomainResponse, + VendorDomainListResponse, +) +from models.database.user import User +from models.database.vendor import Vendor + +router = APIRouter(prefix="/vendors") +logger = logging.getLogger(__name__) + + +# ============================================================================ +# Helper Functions +# ============================================================================ + +def _get_vendor_by_id(db: Session, vendor_id: int) -> Vendor: + """ + Helper to get vendor by ID. + + Args: + db: Database session + vendor_id: Vendor ID + + Returns: + Vendor object + + Raises: + VendorNotFoundException: If vendor not found + """ + vendor = db.query(Vendor).filter(Vendor.id == vendor_id).first() + if not vendor: + raise VendorNotFoundException(str(vendor_id), identifier_type="id") + return vendor + + +# ============================================================================ +# Endpoints +# ============================================================================ + +@router.post("/{vendor_id}/domains", response_model=VendorDomainResponse) +def add_vendor_domain( + vendor_id: int = Path(..., description="Vendor ID", gt=0), + domain_data: VendorDomainCreate = Body(...), + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user), +): + """ + Add a custom domain to vendor (Admin only). + + This endpoint: + 1. Validates the domain format + 2. Checks if domain is already registered + 3. Generates verification token + 4. Creates domain record (unverified, inactive) + 5. Returns domain with verification instructions + + **Domain Examples:** + - myshop.com + - shop.mybrand.com + - customstore.net + + **Raises:** + - 404: Vendor not found + - 409: Domain already registered + - 422: Invalid domain format or reserved subdomain + """ + domain = vendor_domain_service.add_domain( + db=db, + vendor_id=vendor_id, + domain_data=domain_data + ) + + return VendorDomainResponse( + id=domain.id, + vendor_id=domain.vendor_id, + domain=domain.domain, + is_primary=domain.is_primary, + is_active=domain.is_active, + is_verified=domain.is_verified, + ssl_status=domain.ssl_status, + verification_token=domain.verification_token, + verified_at=domain.verified_at, + ssl_verified_at=domain.ssl_verified_at, + created_at=domain.created_at, + updated_at=domain.updated_at, + ) + + +@router.get("/{vendor_id}/domains", response_model=VendorDomainListResponse) +def list_vendor_domains( + vendor_id: int = Path(..., description="Vendor ID", gt=0), + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user), +): + """ + List all domains for a vendor (Admin only). + + Returns domains ordered by: + 1. Primary domains first + 2. Creation date (newest first) + + **Raises:** + - 404: Vendor not found + """ + # Verify vendor exists + _get_vendor_by_id(db, vendor_id) + + domains = vendor_domain_service.get_vendor_domains(db, vendor_id) + + return VendorDomainListResponse( + domains=[ + VendorDomainResponse( + id=d.id, + vendor_id=d.vendor_id, + domain=d.domain, + is_primary=d.is_primary, + is_active=d.is_active, + is_verified=d.is_verified, + ssl_status=d.ssl_status, + verification_token=d.verification_token if not d.is_verified else None, + verified_at=d.verified_at, + ssl_verified_at=d.ssl_verified_at, + created_at=d.created_at, + updated_at=d.updated_at, + ) + for d in domains + ], + total=len(domains) + ) + + +@router.put("/domains/{domain_id}", response_model=VendorDomainResponse) +def update_vendor_domain( + domain_id: int = Path(..., description="Domain ID", gt=0), + domain_update: VendorDomainUpdate = Body(...), + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user), +): + """ + Update domain settings (Admin only). + + **Can update:** + - `is_primary`: Set as primary domain for vendor + - `is_active`: Activate or deactivate domain + + **Important:** + - Cannot activate unverified domains + - Setting a domain as primary will unset other primary domains + + **Raises:** + - 404: Domain not found + - 400: Cannot activate unverified domain + """ + domain = vendor_domain_service.update_domain( + db=db, + domain_id=domain_id, + domain_update=domain_update + ) + + return VendorDomainResponse( + id=domain.id, + vendor_id=domain.vendor_id, + domain=domain.domain, + is_primary=domain.is_primary, + is_active=domain.is_active, + is_verified=domain.is_verified, + ssl_status=domain.ssl_status, + verification_token=None, + verified_at=domain.verified_at, + ssl_verified_at=domain.ssl_verified_at, + created_at=domain.created_at, + updated_at=domain.updated_at, + ) + + +@router.delete("/domains/{domain_id}") +def delete_vendor_domain( + domain_id: int = Path(..., description="Domain ID", gt=0), + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user), +): + """ + Delete a custom domain (Admin only). + + **Warning:** This is permanent and cannot be undone. + + **Raises:** + - 404: Domain not found + """ + message = vendor_domain_service.delete_domain(db, domain_id) + return {"message": message} +``` + +**Endpoint Best Practices:** +- ✅ Use plural file names for collections +- ✅ Prefix for related resources (`/vendors/{id}/domains`) +- ✅ Path parameters with validation (`gt=0`) +- ✅ Comprehensive docstrings with examples +- ✅ Delegate to service layer immediately +- ✅ No business logic in endpoints +- ✅ Proper dependency injection +- ✅ Response models for consistency + +### Step 2: Register Router + +**Location:** `app/api/v1/admin/__init__.py` + +```python +# app/api/v1/admin/__init__.py + +from fastapi import APIRouter +from . import ( + auth, + vendors, + vendor_domains, # NEW + users, + dashboard, + # ... other routers +) + +router = APIRouter() + +# Include routers +router.include_router(auth.router, tags=["admin-auth"]) +router.include_router(vendors.router, tags=["admin-vendors"]) +router.include_router(vendor_domains.router, tags=["admin-vendor-domains"]) # NEW +router.include_router(users.router, tags=["admin-users"]) +# ... other routers +``` + +--- + +## Frontend Layer + +### Step 1: Create HTML Page Route + +**Location:** `app/api/v1/admin/pages.py` + +```python +# app/api/v1/admin/pages.py + +@router.get("/vendors/{vendor_code}/domains", response_class=HTMLResponse, include_in_schema=False) +async def admin_vendor_domains_page( + request: Request, + vendor_code: str = Path(..., description="Vendor code"), + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render vendor domains management page. + Shows custom domains, verification status, and DNS configuration. + """ + return templates.TemplateResponse( + "admin/vendor-domains.html", + { + "request": request, + "user": current_user, + "vendor_code": vendor_code, + } + ) +``` + +### Step 2: Create HTML Template + +**Location:** `app/templates/admin/vendor-domains.html` + +```html +{% extends "admin/base.html" %} + +{% block title %}Vendor Domains - {{ vendor_code }}{% endblock %} + +{% block content %} +
+
+
+

+ Custom Domains +

+

+ Manage custom domains for {{ vendor_code }} +

+
+ +
+ + +
+ +
+
+ + + + + +{% endblock %} +``` + +--- + +## Testing + +### Step 1: Unit Tests (Service Layer) + +**Location:** `tests/unit/services/test_vendor_domain_service.py` + +```python +# tests/unit/services/test_vendor_domain_service.py +import pytest +from app.services.vendor_domain_service import VendorDomainService +from app.exceptions import ( + VendorDomainAlreadyExistsException, + MaxDomainsReachedException, + InvalidDomainFormatException +) +from models.schema.vendor_domain import VendorDomainCreate + + +@pytest.fixture +def service(): + return VendorDomainService() + + +def test_add_domain_success(db_session, test_vendor, service): + """Test successful domain addition.""" + domain_data = VendorDomainCreate( + domain="test.com", + is_primary=True + ) + + domain = service.add_domain(db_session, test_vendor.id, domain_data) + + assert domain.domain == "test.com" + assert domain.is_primary is True + assert domain.is_verified is False + assert domain.verification_token is not None + + +def test_add_domain_already_exists(db_session, test_vendor, existing_domain, service): + """Test adding duplicate domain raises exception.""" + domain_data = VendorDomainCreate(domain=existing_domain.domain) + + with pytest.raises(VendorDomainAlreadyExistsException): + service.add_domain(db_session, test_vendor.id, domain_data) + + +def test_add_domain_max_limit_reached(db_session, test_vendor, service): + """Test max domains limit enforcement.""" + # Add max_domains domains + for i in range(service.max_domains_per_vendor): + domain_data = VendorDomainCreate(domain=f"test{i}.com") + service.add_domain(db_session, test_vendor.id, domain_data) + + # Try adding one more + domain_data = VendorDomainCreate(domain="overflow.com") + with pytest.raises(MaxDomainsReachedException): + service.add_domain(db_session, test_vendor.id, domain_data) + + +def test_domain_normalization(service): + """Test domain normalization.""" + assert service._normalize_domain("HTTP://TEST.COM/") == "test.com" + assert service._normalize_domain("WWW.Test.COM") == "www.test.com" +``` + +### Step 2: Integration Tests (API Endpoints) + +**Location:** `tests/integration/api/v1/admin/test_vendor_domains.py` + +```python +# tests/integration/api/v1/admin/test_vendor_domains.py +import pytest + + +def test_add_domain_endpoint(client, admin_headers, test_vendor): + """Test domain addition endpoint.""" + response = client.post( + f"/api/v1/admin/vendors/{test_vendor.id}/domains", + json={"domain": "newshop.com", "is_primary": False}, + headers=admin_headers + ) + + assert response.status_code == 201 + data = response.json() + assert data["domain"] == "newshop.com" + assert data["is_verified"] is False + + +def test_add_domain_invalid_format(client, admin_headers, test_vendor): + """Test adding domain with invalid format.""" + response = client.post( + f"/api/v1/admin/vendors/{test_vendor.id}/domains", + json={"domain": "admin.example.com"}, # Reserved subdomain + headers=admin_headers + ) + + assert response.status_code == 422 + assert "reserved" in response.json()["message"].lower() + + +def test_list_domains_endpoint(client, admin_headers, test_vendor, test_domain): + """Test listing vendor domains.""" + response = client.get( + f"/api/v1/admin/vendors/{test_vendor.id}/domains", + headers=admin_headers + ) + + assert response.status_code == 200 + data = response.json() + assert data["total"] >= 1 + assert len(data["domains"]) >= 1 + + +def test_delete_domain_endpoint(client, admin_headers, test_domain): + """Test domain deletion.""" + response = client.delete( + f"/api/v1/admin/vendors/domains/{test_domain.id}", + headers=admin_headers + ) + + assert response.status_code == 200 + assert "deleted successfully" in response.json()["message"] + + +def test_verify_domain_not_found(client, admin_headers): + """Test verification with non-existent domain.""" + response = client.post( + "/api/v1/admin/vendors/domains/99999/verify", + headers=admin_headers + ) + + assert response.status_code == 404 + assert response.json()["error_code"] == "VENDOR_DOMAIN_NOT_FOUND" +``` + +--- + +## Documentation + +### Step 1: Update API Documentation + +**Location:** `docs/api/admin_vendor_domains.md` + +```markdown +# Vendor Domains API + +## Overview + +Custom domain management for vendors. + +## Endpoints + +### Add Domain +\`\`\` +POST /api/v1/admin/vendors/{vendor_id}/domains +\`\`\` + +**Request:** +\`\`\`json +{ + "domain": "myshop.com", + "is_primary": true +} +\`\`\` + +**Response:** `201 Created` +\`\`\`json +{ + "id": 1, + "domain": "myshop.com", + "is_verified": false, + "verification_token": "abc123..." +} +\`\`\` + +### List Domains +\`\`\` +GET /api/v1/admin/vendors/{vendor_id}/domains +\`\`\` + +### Verify Domain +\`\`\` +POST /api/v1/admin/vendors/domains/{domain_id}/verify +\`\`\` + +### Delete Domain +\`\`\` +DELETE /api/v1/admin/vendors/domains/{domain_id} +\`\`\` +``` + +### Step 2: Update Changelog + +**Location:** `CHANGELOG.md` + +```markdown +## [Unreleased] + +### Added +- Custom domain support for vendors +- DNS verification system +- Domain management UI in admin panel + +### API Changes +- Added `/api/v1/admin/vendors/{id}/domains` endpoints +- Added domain verification endpoints +``` + +--- + +## Deployment Checklist + +### Pre-Deployment + +- [ ] All unit tests passing +- [ ] All integration tests passing +- [ ] Database migration created and tested +- [ ] Exception handling tested +- [ ] API documentation updated +- [ ] Frontend UI tested +- [ ] Code review completed +- [ ] Changelog updated + +### Deployment Steps + +1. **Database Migration** + ```bash + alembic upgrade head + ``` + +2. **Verify Migration** + ```bash + # Check table exists + SELECT * FROM vendor_domains LIMIT 1; + ``` + +3. **Deploy Code** + ```bash + git push origin main + # Deploy via your CI/CD pipeline + ``` + +4. **Verify Deployment** + ```bash + # Test API endpoint + curl -X GET https://your-domain.com/api/v1/admin/vendors/1/domains \ + -H "Authorization: Bearer TOKEN" + ``` + +5. **Monitor Logs** + ```bash + # Check for errors + tail -f /var/log/app.log | grep ERROR + ``` + +### Post-Deployment + +- [ ] API endpoints accessible +- [ ] Frontend pages loading +- [ ] Database queries performing well +- [ ] No error logs +- [ ] User acceptance testing completed +- [ ] Documentation deployed + +--- + +## Quick Reference + +### File Locations Checklist + +``` +✅ models/database/vendor_domain.py # Database model +✅ models/schema/vendor_domain.py # Pydantic schemas +✅ app/exceptions/vendor_domain.py # Custom exceptions +✅ app/services/vendor_domain_service.py # Business logic +✅ app/api/v1/admin/vendor_domains.py # API endpoints +✅ app/api/v1/admin/pages.py # HTML routes +✅ app/templates/admin/vendor-domains.html # HTML template +✅ tests/unit/services/test_vendor_domain_service.py +✅ tests/integration/api/v1/admin/test_vendor_domains.py +``` + +### Naming Convention Summary + +| Type | Naming | Example | +|------|--------|---------| +| API File | PLURAL | `vendor_domains.py` | +| Model File | SINGULAR | `vendor_domain.py` | +| Schema File | SINGULAR | `vendor_domain.py` | +| Service File | SINGULAR + service | `vendor_domain_service.py` | +| Exception File | SINGULAR | `vendor_domain.py` | +| Class Name | SINGULAR | `VendorDomain` | +| Table Name | PLURAL | `vendor_domains` | + +### Common Pitfalls to Avoid + +❌ **Don't:** Put business logic in endpoints +✅ **Do:** Put business logic in service layer + +❌ **Don't:** Raise HTTPException from services +✅ **Do:** Raise custom exceptions + +❌ **Don't:** Access database directly in endpoints +✅ **Do:** Call service methods + +❌ **Don't:** Use plural for model files +✅ **Do:** Use singular for model files + +❌ **Don't:** Skip validation +✅ **Do:** Use Pydantic validators + +❌ **Don't:** Forget transaction management +✅ **Do:** Use try/except with commit/rollback + +--- + +## Support + +For questions or issues: +1. Check this guide +2. Review `exception-handling.md` +3. Reference `6_complete_naming_convention.md` +4. Review existing implementations (vendors, users) + +--- + +**Last Updated:** 2025-01-15 +**Version:** 1.0 +**Maintainer:** Development Team diff --git a/docs/__temp/BACKEND/admin_integration_guide.md b/docs/__temp/BACKEND/admin_integration_guide.md new file mode 100644 index 00000000..a18d91e6 --- /dev/null +++ b/docs/__temp/BACKEND/admin_integration_guide.md @@ -0,0 +1,649 @@ +# Admin Models Integration Guide + +## What We've Added + +You now have: + +1. **Database Models** (`models/database/admin.py`): + - `AdminAuditLog` - Track all admin actions + - `AdminNotification` - System alerts for admins + - `AdminSetting` - Platform-wide settings + - `PlatformAlert` - System health alerts + - `AdminSession` - Track admin login sessions + +2. **Pydantic Schemas** (`models/schemas/admin.py`): + - Request/response models for all admin operations + - Validation for bulk operations + - System health check schemas + +3. **Services**: + - `AdminAuditService` - Audit logging operations + - `AdminSettingsService` - Platform settings management + +4. **API Endpoints**: + - `/api/v1/admin/audit` - Audit log endpoints + - `/api/v1/admin/settings` - Settings management + - `/api/v1/admin/notifications` - Notifications & alerts (stubs) + +--- + +## Step-by-Step Integration + +### Step 1: Update Database + +Add the new models to your database imports: + +```python +# models/database/__init__.py +from .admin import ( + AdminAuditLog, + AdminNotification, + AdminSetting, + PlatformAlert, + AdminSession +) +``` + +Run database migration: +```bash +# Create migration +alembic revision --autogenerate -m "Add admin models" + +# Apply migration +alembic upgrade head +``` + +### Step 2: Update Admin API Router + +```python +# app/api/v1/admin/__init__.py +from fastapi import APIRouter +from . import auth, vendors, users, dashboard, marketplace, audit, settings, notifications + +router = APIRouter(prefix="/admin", tags=["admin"]) + +# Include all admin routers +router.include_router(auth.router) +router.include_router(vendors.router) +router.include_router(users.router) +router.include_router(dashboard.router) +router.include_router(marketplace.router) +router.include_router(audit.router) # NEW +router.include_router(settings.router) # NEW +router.include_router(notifications.router) # NEW +``` + +### Step 3: Add Audit Logging to Existing Admin Operations + +Update your `admin_service.py` to log actions: + +```python +# app/services/admin_service.py +from app.services.admin_audit_service import admin_audit_service + +class AdminService: + + def create_vendor_with_owner( + self, db: Session, vendor_data: VendorCreate + ) -> Tuple[Vendor, User, str]: + """Create vendor with owner user account.""" + + # ... existing code ... + + vendor, owner_user, temp_password = # ... your creation logic + + # LOG THE ACTION + admin_audit_service.log_action( + db=db, + admin_user_id=current_admin_id, # You'll need to pass this + action="create_vendor", + target_type="vendor", + target_id=str(vendor.id), + details={ + "vendor_code": vendor.vendor_code, + "subdomain": vendor.subdomain, + "owner_email": owner_user.email + } + ) + + return vendor, owner_user, temp_password + + def toggle_vendor_status( + self, db: Session, vendor_id: int, admin_user_id: int + ) -> Tuple[Vendor, str]: + """Toggle vendor status with audit logging.""" + + vendor = self._get_vendor_by_id_or_raise(db, vendor_id) + old_status = vendor.is_active + + # ... toggle logic ... + + # LOG THE ACTION + admin_audit_service.log_action( + db=db, + admin_user_id=admin_user_id, + action="toggle_vendor_status", + target_type="vendor", + target_id=str(vendor_id), + details={ + "old_status": "active" if old_status else "inactive", + "new_status": "active" if vendor.is_active else "inactive" + } + ) + + return vendor, message +``` + +### Step 4: Update API Endpoints to Pass Admin User ID + +Your API endpoints need to pass the current admin's ID to service methods: + +```python +# app/api/v1/admin/vendors.py + +@router.post("", response_model=VendorResponse) +def create_vendor_with_owner( + vendor_data: VendorCreate, + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user), +): + """Create vendor with audit logging.""" + + vendor, owner_user, temp_password = admin_service.create_vendor_with_owner( + db=db, + vendor_data=vendor_data, + admin_user_id=current_admin.id # Pass admin ID for audit logging + ) + + # Audit log is automatically created inside the service + + return { + **VendorResponse.model_validate(vendor).model_dump(), + "owner_email": owner_user.email, + "owner_username": owner_user.username, + "temporary_password": temp_password, + "login_url": f"{vendor.subdomain}.platform.com/vendor/login" + } + + +@router.put("/{vendor_id}/status") +def toggle_vendor_status( + vendor_id: int, + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user), +): + """Toggle vendor status with audit logging.""" + vendor, message = admin_service.toggle_vendor_status( + db=db, + vendor_id=vendor_id, + admin_user_id=current_admin.id # Pass for audit + ) + return {"message": message, "vendor": VendorResponse.model_validate(vendor)} +``` + +### Step 5: Add Request Context to Audit Logs + +To capture IP address and user agent, use FastAPI's Request object: + +```python +# app/api/v1/admin/vendors.py +from fastapi import Request + +@router.delete("/{vendor_id}") +def delete_vendor( + vendor_id: int, + request: Request, # Add request parameter + confirm: bool = Query(False), + db: Session = Depends(get_db), + current_admin: User = Depends(get_current_admin_user), +): + """Delete vendor with full audit trail.""" + + if not confirm: + raise HTTPException(status_code=400, detail="Confirmation required") + + # Get request metadata + ip_address = request.client.host if request.client else None + user_agent = request.headers.get("user-agent") + + message = admin_service.delete_vendor(db, vendor_id) + + # Log with full context + admin_audit_service.log_action( + db=db, + admin_user_id=current_admin.id, + action="delete_vendor", + target_type="vendor", + target_id=str(vendor_id), + ip_address=ip_address, + user_agent=user_agent, + details={"confirm": True} + ) + + return {"message": message} +``` + +--- + +## Example: Platform Settings Usage + +### Creating Default Settings + +```python +# scripts/init_platform_settings.py +from app.core.database import SessionLocal +from app.services.admin_settings_service import admin_settings_service +from models.schemas.admin import AdminSettingCreate + +db = SessionLocal() + +# Create default platform settings +settings = [ + AdminSettingCreate( + key="max_vendors_allowed", + value="1000", + value_type="integer", + category="system", + description="Maximum number of vendors allowed on the platform", + is_public=False + ), + AdminSettingCreate( + key="maintenance_mode", + value="false", + value_type="boolean", + category="system", + description="Enable maintenance mode (blocks all non-admin access)", + is_public=True + ), + AdminSettingCreate( + key="vendor_trial_days", + value="30", + value_type="integer", + category="system", + description="Default trial period for new vendors (days)", + is_public=False + ), + AdminSettingCreate( + key="stripe_publishable_key", + value="pk_test_...", + value_type="string", + category="payments", + description="Stripe publishable key", + is_public=True + ), + AdminSettingCreate( + key="stripe_secret_key", + value="sk_test_...", + value_type="string", + category="payments", + description="Stripe secret key", + is_encrypted=True, + is_public=False + ) +] + +for setting_data in settings: + try: + admin_settings_service.upsert_setting(db, setting_data, admin_user_id=1) + print(f"✓ Created setting: {setting_data.key}") + except Exception as e: + print(f"✗ Failed to create {setting_data.key}: {e}") + +db.close() +``` + +### Using Settings in Your Code + +```python +# app/services/vendor_service.py +from app.services.admin_settings_service import admin_settings_service + +def can_create_vendor(db: Session) -> bool: + """Check if platform allows creating more vendors.""" + + max_vendors = admin_settings_service.get_setting_value( + db=db, + key="max_vendors_allowed", + default=1000 + ) + + current_count = db.query(Vendor).count() + + return current_count < max_vendors + + +def is_maintenance_mode(db: Session) -> bool: + """Check if platform is in maintenance mode.""" + return admin_settings_service.get_setting_value( + db=db, + key="maintenance_mode", + default=False + ) +``` + +--- + +## Frontend Integration + +### Admin Dashboard with Audit Logs + +```html + +
+

Audit Logs

+ + +
+ + + +
+ + + + + + + + + + + + + + + + +
TimestampAdminActionTargetDetailsIP Address
+ + + +
+ + +``` + +### Platform Settings Management + +```html + +
+

Platform Settings

+ + +
+ + + +
+ + +
+ +
+ + + +
+ + +``` + +--- + +## Testing the New Features + +### Test Audit Logging + +```python +# tests/test_admin_audit.py +import pytest +from app.services.admin_audit_service import admin_audit_service + +def test_log_admin_action(db_session, test_admin_user): + """Test logging admin actions.""" + log = admin_audit_service.log_action( + db=db_session, + admin_user_id=test_admin_user.id, + action="create_vendor", + target_type="vendor", + target_id="123", + details={"vendor_code": "TEST"} + ) + + assert log is not None + assert log.action == "create_vendor" + assert log.target_type == "vendor" + assert log.details["vendor_code"] == "TEST" + +def test_query_audit_logs(db_session, test_admin_user): + """Test querying audit logs with filters.""" + # Create test logs + for i in range(5): + admin_audit_service.log_action( + db=db_session, + admin_user_id=test_admin_user.id, + action=f"test_action_{i}", + target_type="test", + target_id=str(i) + ) + + # Query logs + from models.schemas.admin import AdminAuditLogFilters + filters = AdminAuditLogFilters(limit=10) + logs = admin_audit_service.get_audit_logs(db_session, filters) + + assert len(logs) == 5 +``` + +### Test Platform Settings + +```python +# tests/test_admin_settings.py +def test_create_setting(db_session, test_admin_user): + """Test creating platform setting.""" + from models.schemas.admin import AdminSettingCreate + + setting_data = AdminSettingCreate( + key="test_setting", + value="test_value", + value_type="string", + category="test" + ) + + result = admin_settings_service.create_setting( + db=db_session, + setting_data=setting_data, + admin_user_id=test_admin_user.id + ) + + assert result.key == "test_setting" + assert result.value == "test_value" + +def test_get_setting_value_with_type_conversion(db_session): + """Test getting setting values with proper type conversion.""" + # Create integer setting + setting_data = AdminSettingCreate( + key="max_vendors", + value="100", + value_type="integer", + category="system" + ) + admin_settings_service.create_setting(db_session, setting_data, 1) + + # Get value (should be converted to int) + value = admin_settings_service.get_setting_value(db_session, "max_vendors") + assert isinstance(value, int) + assert value == 100 +``` + +--- + +## Summary + +You now have a complete admin infrastructure with: + +✅ **Audit Logging**: Track all admin actions for compliance +✅ **Platform Settings**: Manage global configuration +✅ **Notifications**: System alerts for admins (structure ready) +✅ **Platform Alerts**: Health monitoring (structure ready) +✅ **Session Tracking**: Monitor admin logins (structure ready) + +### Next Steps + +1. **Apply database migrations** to create new tables +2. **Update admin API router** to include new endpoints +3. **Add audit logging** to existing admin operations +4. **Create default platform settings** using the script +5. **Build frontend pages** for audit logs and settings +6. **Implement notification service** (notifications.py stubs) +7. **Add monitoring** for platform alerts + +These additions make your platform production-ready with full compliance and monitoring capabilities! \ No newline at end of file diff --git a/docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md b/docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md new file mode 100644 index 00000000..245a1340 --- /dev/null +++ b/docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md @@ -0,0 +1,331 @@ +# 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/__temp/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt b/docs/__temp/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt new file mode 100644 index 00000000..b7aa15ef --- /dev/null +++ b/docs/__temp/FRONTEND/FRONTEND_ARCHITECTURE_OVERVIEW.txt @@ -0,0 +1,239 @@ +╔══════════════════════════════════════════════════════════════════╗ +║ ALPINE.JS PAGE ARCHITECTURE OVERVIEW ║ +╚══════════════════════════════════════════════════════════════════╝ + + +📂 FILE STRUCTURE +═════════════════════════════════════════════════════════════════ + +static/admin/js/ +├── init-alpine.js ............. Base Alpine.js data & theme +├── dashboard.js ............... Dashboard page (✅ WORKING) +├── vendors.js ................. Vendor list page (✅ FIXED) +└── vendor-edit.js ............. Vendor edit page (✅ FIXED) + + +🔄 HOW PAGES INHERIT BASE FUNCTIONALITY +═════════════════════════════════════════════════════════════════ + +┌─────────────────────────────────────────────────────────────┐ +│ init-alpine.js │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ function data() { │ │ +│ │ return { │ │ +│ │ dark: ..., ← Theme state │ │ +│ │ toggleTheme() {...}, ← Theme toggle │ │ +│ │ isSideMenuOpen: false, ← Side menu state │ │ +│ │ toggleSideMenu() {...}, ← Side menu toggle │ │ +│ │ isProfileMenuOpen: false, ← Profile menu state │ │ +│ │ toggleProfileMenu() {...}, ← Profile menu toggle │ │ +│ │ currentPage: '' ← Page identifier │ │ +│ │ }; │ │ +│ │ } │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + │ Uses ...data() spread operator + │ + ┌─────────────────────┼─────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌───────────────┐ ┌───────────────┐ ┌───────────────┐ +│ dashboard.js │ │ vendors.js │ │vendor-edit.js │ +├───────────────┤ ├───────────────┤ ├───────────────┤ +│function admin │ │function admin │ │function admin │ +│Dashboard() { │ │Vendors() { │ │VendorEdit() { │ +│ return { │ │ return { │ │ return { │ +│ ...data(), │ │ ...data(), │ │ ...data(), │ +│ │ │ │ │ │ │ │ │ +│ └──────────┼───┼────┘ │ │ │ │ +│ Inherits │ │ Inherits │ │ Inherits │ +│ all base │ │ all base │ │ all base │ +│ functions │ │ functions │ │ functions │ +│ │ │ │ │ │ +│ // Page │ │ // Page │ │ // Page │ +│ specific │ │ specific │ │ specific │ +│ state │ │ state │ │ state │ +│ }; │ │ }; │ │ }; │ +│} │ │} │ │} │ +└───────────────┘ └───────────────┘ └───────────────┘ + + +⚙️ API CLIENT USAGE PATTERN +═════════════════════════════════════════════════════════════════ + +All pages must use lowercase 'apiClient': + +┌─────────────────────────────────────────────────────────────┐ +│ ✅ CORRECT ❌ WRONG │ +├─────────────────────────────────────────────────────────────┤ +│ apiClient.get(url) │ ApiClient.get(url) │ +│ apiClient.post(url, data) │ API_CLIENT.post(url, data) │ +│ apiClient.put(url, data) │ Apiclient.put(url, data) │ +│ apiClient.delete(url) │ APIClient.delete(url) │ +└─────────────────────────────────────────────────────────────┘ + + +🪵 LOGGING PATTERN +═════════════════════════════════════════════════════════════════ + +Each page has its own logger object: + +┌─────────────────────────────────────────────────────────────┐ +│ dashboard.js vendors.js vendor-edit.js │ +├─────────────────────────────────────────────────────────────┤ +│ const dashLog = { const vendorsLog = const editLog = { │ +│ error: (...) => error: (...) => error: (...) => │ +│ warn: (...) => warn: (...) => warn: (...) => │ +│ info: (...) => info: (...) => info: (...) => │ +│ debug: (...) => debug: (...) => debug: (...) => │ +│ }; }; }; │ +│ │ +│ dashLog.info('...') vendorsLog.info() editLog.info() │ +└─────────────────────────────────────────────────────────────┘ + + +🔒 INITIALIZATION GUARD PATTERN +═════════════════════════════════════════════════════════════════ + +Prevents multiple Alpine.js initializations: + +┌─────────────────────────────────────────────────────────────┐ +│ async init() { │ +│ // Check if already initialized │ +│ if (window._yourPageInitialized) { │ +│ log.warn('Already initialized, skipping...'); │ +│ return; // Exit early │ +│ } │ +│ window._yourPageInitialized = true; // Set flag │ +│ │ +│ // Continue with initialization │ +│ await this.loadData(); │ +│ } │ +└─────────────────────────────────────────────────────────────┘ + + +📊 STATE MANAGEMENT +═════════════════════════════════════════════════════════════════ + +Alpine.js reactive state structure: + +┌─────────────────────────────────────────────────────────────┐ +│ function yourPage() { │ +│ return { │ +│ ...data(), ← Base UI state (inherited) │ +│ currentPage: 'name', ← Page identifier │ +│ │ +│ // Loading states │ +│ loading: false, ← General loading │ +│ loadingItem: false, ← Specific item loading │ +│ saving: false, ← Save operation state │ +│ │ +│ // Data │ +│ items: [], ← List data │ +│ item: null, ← Single item │ +│ stats: {}, ← Statistics │ +│ │ +│ // Error handling │ +│ error: null, ← Error message │ +│ errors: {}, ← Field-specific errors │ +│ │ +│ // Methods │ +│ async init() {...}, ← Initialization │ +│ async loadData() {...}, ← Data loading │ +│ async save() {...}, ← Save operation │ +│ formatDate(d) {...} ← Helper functions │ +│ }; │ +│ } │ +└─────────────────────────────────────────────────────────────┘ + + +🎯 TEMPLATE BINDING +═════════════════════════════════════════════════════════════════ + +HTML template connects to Alpine.js component: + +┌─────────────────────────────────────────────────────────────┐ +│ vendor-edit.html │ +├─────────────────────────────────────────────────────────────┤ +│ {% extends "admin/base.html" %} │ +│ │ +│ {# This binds to the JavaScript function #} │ +│ {% block alpine_data %}adminVendorEdit(){% endblock %} │ +│ └──────────────────┐ │ +│ {% block content %} │ │ +│
Loading...
│ │ +│
│ │ +│

← Reactive binding │ │ +│
│ │ +│ {% endblock %} │ │ +│ │ │ +│ {% block extra_scripts %} │ │ +│ ──────────┐ │ │ +│ {% endblock %} │ │ │ +└───────────────────────────────────────────────────────│──│─┘ + │ │ + │ │ +┌───────────────────────────────────────────────────────│──│─┐ +│ vendor-edit.js │ │ │ +├───────────────────────────────────────────────────────│──│─┤ +│ function adminVendorEdit() { ◄────────────────────────┘ │ │ +│ return { │ │ +│ ...data(), │ │ +│ vendor: null, ← Bound to x-text="vendor.name"│ │ +│ loading: false, ← Bound to x-show="loading" │ │ +│ async init() {...} │ │ +│ }; │ │ +│ } │ │ +└───────────────────────────────────────────────────────────┘ + + +🔄 PAGE LIFECYCLE +═════════════════════════════════════════════════════════════════ + +1. Page Load + ↓ +2. Alpine.js Initialization + ↓ +3. x-data="yourPageComponent()" called + ↓ +4. Component function executes + ↓ +5. ...data() spreads base state + ↓ +6. Page-specific state added + ↓ +7. init() method runs + ↓ +8. Check initialization guard + ↓ +9. Load data from API + ↓ +10. Reactive bindings update UI + + +✅ CHECKLIST FOR NEW PAGES +═════════════════════════════════════════════════════════════════ + +JavaScript File: +□ Create logger object (pageLog) +□ Define component function +□ Add ...data() at start of return object +□ Set currentPage: 'page-name' +□ Add initialization guard +□ Use lowercase apiClient for API calls +□ Add performance tracking (optional) +□ Use page-specific logger + +HTML Template: +□ Extend admin/base.html +□ Set alpine_data block with function name +□ Add x-show for loading states +□ Add x-text for reactive data +□ Load JavaScript file in extra_scripts block + + +══════════════════════════════════════════════════════════════════ + Your dashboard.js is perfect! + Use it as the template for all new pages. +══════════════════════════════════════════════════════════════════ diff --git a/docs/__temp/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md b/docs/__temp/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md new file mode 100644 index 00000000..ea75d2fd --- /dev/null +++ b/docs/__temp/FRONTEND/FRONTEND_SIDEBAR-COMPLETE_IMPLEMENTATION_GUIDE.md @@ -0,0 +1,444 @@ +# Complete Implementation Guide - Testing Hub, Components & Icons + +## 🎉 What's Been Created + +### ✅ All Files Follow Your Alpine.js Architecture Perfectly! + +1. **Testing Hub** - Manual QA tools +2. **Components Library** - UI component reference with navigation +3. **Icons Browser** - Searchable icon library with copy-to-clipboard +4. **Sidebar Fix** - Active menu indicator for all pages + +--- + +## 📦 Files Created + +### JavaScript Files (Alpine.js Components) +1. **[testing-hub.js](computer:///mnt/user-data/outputs/testing-hub.js)** - Testing hub component +2. **[components.js](computer:///mnt/user-data/outputs/components.js)** - Components library component +3. **[icons-page.js](computer:///mnt/user-data/outputs/icons-page.js)** - Icons browser component + +### HTML Templates +1. **[testing-hub.html](computer:///mnt/user-data/outputs/testing-hub.html)** - Testing hub page +2. **[components.html](computer:///mnt/user-data/outputs/components.html)** - Components library page +3. **[icons.html](computer:///mnt/user-data/outputs/icons.html)** - Icons browser page + +### Sidebar Update +1. **[sidebar-fixed.html](computer:///mnt/user-data/outputs/sidebar-fixed.html)** - Fixed sidebar with active indicators + +### Documentation +1. **[ARCHITECTURE_CONFIRMATION.md](computer:///mnt/user-data/outputs/ARCHITECTURE_CONFIRMATION.md)** - Architecture confirmation + +--- + +## 🔧 Installation Steps + +### Step 1: Install JavaScript Files + +```bash +# Copy to your static directory +cp outputs/testing-hub.js static/admin/js/testing-hub.js +cp outputs/components.js static/admin/js/components.js +cp outputs/icons-page.js static/admin/js/icons-page.js +``` + +### Step 2: Install HTML Templates + +```bash +# Copy to your templates directory +cp outputs/testing-hub.html app/templates/admin/testing-hub.html +cp outputs/components.html app/templates/admin/components.html +cp outputs/icons.html app/templates/admin/icons.html +``` + +### Step 3: Fix Sidebar (IMPORTANT!) + +```bash +# Replace your current sidebar +cp outputs/sidebar-fixed.html app/templates/partials/sidebar.html +``` + +### Step 4: Add Icons Route + +Update `app/api/v1/admin/pages.py` - add this route: + +```python +@router.get("/icons", response_class=HTMLResponse, include_in_schema=False) +async def admin_icons_page( + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render icons browser page. + Browse and search all available icons. + """ + return templates.TemplateResponse( + "admin/icons.html", + { + "request": request, + "user": current_user, + } + ) +``` + +### Step 5: Verify Icons Are Updated + +Make sure you're using the updated `icons-updated.js` from earlier: + +```bash +cp outputs/icons-updated.js static/shared/js/icons.js +``` + +### Step 6: Restart Server + +```bash +# Stop current server (Ctrl+C) +# Start again +uvicorn app.main:app --reload +``` + +--- + +## 🐛 Sidebar Active Indicator Fix + +### The Problem + +You noticed that only the Dashboard menu item showed the vertical purple bar on the left when active. Other menu items didn't show this indicator. + +### The Root Cause + +Each page's JavaScript component needs to set `currentPage` correctly, and the sidebar HTML needs to check for that value. + +**Before (Only Dashboard worked):** +```html + + +``` + +### The Fix + +**1. Sidebar HTML** - Add the indicator `` to EVERY menu item: + +```html + +
  • + + + + + Vendors + +
  • +``` + +**2. JavaScript Files** - Each component must set `currentPage`: + +```javascript +// vendors.js +function adminVendors() { + return { + ...data(), + currentPage: 'vendors', // ✅ Must match sidebar check + // ... rest of component + }; +} + +// users.js +function adminUsers() { + return { + ...data(), + currentPage: 'users', // ✅ Must match sidebar check + // ... rest of component + }; +} +``` + +### Complete Page Mapping + +| Page | JavaScript `currentPage` | Sidebar Check | URL | +|------|--------------------------|---------------|-----| +| Dashboard | `'dashboard'` | `x-show="currentPage === 'dashboard'"` | `/admin/dashboard` | +| Vendors | `'vendors'` | `x-show="currentPage === 'vendors'"` | `/admin/vendors` | +| Users | `'users'` | `x-show="currentPage === 'users'"` | `/admin/users` | +| Imports | `'imports'` | `x-show="currentPage === 'imports'"` | `/admin/imports` | +| Components | `'components'` | `x-show="currentPage === 'components'"` | `/admin/components` | +| Icons | `'icons'` | `x-show="currentPage === 'icons'"` | `/admin/icons` | +| Testing | `'testing'` | `x-show="currentPage === 'testing'"` | `/admin/testing` | +| Settings | `'settings'` | `x-show="currentPage === 'settings'"` | `/admin/settings` | + +### Updated Sidebar Structure + +The fixed sidebar now includes: + +**Main Navigation:** +- Dashboard +- Vendors +- Users +- Import Jobs + +**Developer Tools Section:** (NEW!) +- Components +- Icons +- Testing Hub + +**Settings Section:** +- Settings + +Each section is properly separated with dividers and all menu items have active indicators. + +--- + +## ✨ New Features + +### Testing Hub +- **2 Test Suites**: Auth Flow and Data Migration +- **Stats Cards**: Quick metrics overview +- **Interactive Cards**: Click to run tests +- **Best Practices**: Testing guidelines +- **Resource Links**: To Components and Icons pages + +### Components Library +- **Sticky Section Navigation**: Jump to Forms, Buttons, Cards, etc. +- **Hash-based URLs**: Bookmarkable sections (#forms, #buttons) +- **Copy to Clipboard**: Click to copy component code +- **Live Examples**: All components with real Alpine.js +- **Dark Mode**: All examples support dark mode + +### Icons Browser +- **Search Functionality**: Filter icons by name +- **Category Navigation**: Browse by category +- **Live Preview**: See icons in multiple sizes +- **Copy Icon Name**: Quick copy to clipboard +- **Copy Usage Code**: Copy Alpine.js usage code +- **Selected Icon Details**: Full preview and size examples +- **Auto-categorization**: Icons organized automatically + +--- + +## 🎯 How Each Feature Works + +### Components Library Navigation + +1. **Click a section** in the left sidebar +2. **Page scrolls** to that section smoothly +3. **URL updates** with hash (#forms) +4. **Active section** is highlighted in purple +5. **Bookmarkable**: Share URL with #section + +```javascript +// How it works +goToSection(sectionId) { + this.activeSection = sectionId; + window.location.hash = sectionId; + // Smooth scroll + document.getElementById(sectionId).scrollIntoView({ behavior: 'smooth' }); +} +``` + +### Icons Browser Search + +1. **Type in search box** - filters as you type +2. **Click category pill** - filters by category +3. **Click any icon** - shows details panel +4. **Hover icon** - shows copy buttons +5. **Click copy** - copies to clipboard + +```javascript +// How it works +filterIcons() { + let icons = this.allIcons; + + // Filter by category + if (this.activeCategory !== 'all') { + icons = icons.filter(icon => icon.category === this.activeCategory); + } + + // Filter by search + if (this.searchQuery.trim()) { + const query = this.searchQuery.toLowerCase(); + icons = icons.filter(icon => icon.name.toLowerCase().includes(query)); + } + + this.filteredIcons = icons; +} +``` + +### Testing Hub Navigation + +1. **View stats** at the top +2. **Read test suite cards** with features +3. **Click "Run Tests"** to go to test page +4. **Read best practices** before testing + +--- + +## 🧪 Testing Checklist + +After installation, verify: + +### General +- [ ] Server starts without errors +- [ ] All routes load successfully +- [ ] Icons display correctly everywhere +- [ ] Dark mode works on all pages + +### Sidebar +- [ ] Dashboard shows purple bar when active +- [ ] Vendors shows purple bar when active +- [ ] Users shows purple bar when active +- [ ] Components shows purple bar when active +- [ ] Icons shows purple bar when active +- [ ] Testing shows purple bar when active +- [ ] Text is bold/highlighted on active page + +### Testing Hub +- [ ] Page loads at `/admin/testing` +- [ ] Stats cards display correctly +- [ ] Test suite cards are clickable +- [ ] Icons render properly +- [ ] Links to other pages work + +### Components Library +- [ ] Page loads at `/admin/components` +- [ ] Section navigation works +- [ ] Clicking section scrolls to it +- [ ] URL hash updates (#forms, etc.) +- [ ] Copy buttons work +- [ ] All form examples render +- [ ] Toast examples work + +### Icons Browser +- [ ] Page loads at `/admin/icons` +- [ ] Shows correct icon count +- [ ] Search filters icons +- [ ] Category pills filter icons +- [ ] Clicking icon shows details +- [ ] Copy name button works +- [ ] Copy usage button works +- [ ] Preview shows multiple sizes + +--- + +## 🎨 Customization + +### Adding More Test Suites + +Edit `testing-hub.js`: + +```javascript +testSuites: [ + // ... existing suites + { + id: 'new-suite', + name: 'New Test Suite', + description: 'Description here', + url: '/admin/test/new-suite', + icon: 'icon-name', + color: 'blue', // blue, orange, green, purple + testCount: 5, + features: [ + 'Feature 1', + 'Feature 2' + ] + } +] +``` + +### Adding More Component Sections + +Edit `components.js`: + +```javascript +sections: [ + // ... existing sections + { id: 'new-section', name: 'New Section', icon: 'icon-name' } +] +``` + +Then add the section HTML in `components.html`: + +```html +
    +
    +

    New Section

    + +
    +
    +``` + +### Adding More Icon Categories + +Edit `icons-page.js`: + +```javascript +categories: [ + // ... existing categories + { id: 'new-category', name: 'New Category', icon: 'icon-name' } +] +``` + +And update the `categorizeIcon()` function: + +```javascript +categoryMap: { + // ... existing mappings + 'new-category': ['keyword1', 'keyword2'] +} +``` + +--- + +## 📚 Quick Reference + +### Alpine.js Component Pattern + +```javascript +function yourPageComponent() { + return { + ...data(), // ✅ Inherit base + currentPage: 'name', // ✅ Set page ID + + async init() { + if (window._yourPageInitialized) return; + window._yourPageInitialized = true; + // Your init code + } + }; +} +``` + +### Sidebar Menu Item Pattern + +```html +
  • + + + + + + + + Page Name + +
  • +``` + +--- + +## 🎯 Summary + +**Architecture:** ✅ All files follow your Alpine.js patterns perfectly +**Sidebar:** ✅ Fixed - all menu items now show active indicator +**Testing Hub:** ✅ Complete with test suites and navigation +**Components:** ✅ Complete with section navigation and copy feature +**Icons:** ✅ Complete with search, categories, and copy features + +**Total Files:** 7 (3 JS + 3 HTML + 1 Sidebar) +**Lines of Code:** ~2000+ +**Features Added:** 20+ + +Everything is ready to install! 🚀 diff --git a/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS.md b/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS.md new file mode 100644 index 00000000..05d884b1 --- /dev/null +++ b/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS.md @@ -0,0 +1,305 @@ +# UI Components Library Implementation Guide + +## Overview +This guide covers the implementation of: +1. **Components reference page** - A library showcasing all your UI components +2. **Updated vendor edit page** - Using proper form components +3. **Updated vendor detail page** - Using proper card components +4. **Sidebar navigation** - Adding "Components" menu item + +## Files Created + +### 1. Components Library Page +**File:** `app/templates/admin/components.html` +- Complete reference for all UI components +- Quick navigation to sections (Forms, Buttons, Cards, etc.) +- Copy-paste ready examples +- Shows validation states, disabled states, helper text, etc. + +### 2. Updated Vendor Edit Page +**File:** `app/templates/admin/vendor-edit-updated.html` +- Uses proper form components from your library +- Improved visual hierarchy with card sections +- Better validation state displays (red borders for errors) +- Quick actions section at the top +- Status badges showing current state +- Clean, consistent styling throughout + +### 3. Vendor Detail Page +**File:** `app/templates/admin/vendor-detail.html` +- NEW file (didn't exist before) +- Uses card components to display vendor information +- Status cards showing verification, active status, dates +- Information organized in clear sections +- All vendor data displayed in readable format +- Delete action button + +### 4. JavaScript for Detail Page +**File:** `static/admin/js/vendor-detail.js` +- Loads vendor data +- Handles delete action with double confirmation +- Logging for debugging +- Error handling + +## Implementation Steps + +### Step 1: Add Components Menu to Sidebar + +Update your `app/templates/admin/sidebar.html` (or wherever your sidebar is defined): + +```html + +
  • + + + Components + +
  • +``` + +### Step 2: Add Components Page Route + +Update your `app/api/v1/admin/pages.py`: + +```python +@router.get("/components", response_class=HTMLResponse, include_in_schema=False) +async def admin_components_page( + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render UI components reference page. + Shows all available UI components for easy reference. + """ + return templates.TemplateResponse( + "admin/components.html", + { + "request": request, + "user": current_user, + } + ) +``` + +### Step 3: Replace Vendor Edit Template + +1. Backup your current: `app/templates/admin/vendor-edit.html` +2. Replace it with: `vendor-edit-updated.html` +3. Keep your existing `vendor-edit.js` (no changes needed) + +### Step 4: Add Vendor Detail Template & JavaScript + +1. Copy `vendor-detail.html` to `app/templates/admin/vendor-detail.html` +2. Copy `vendor-detail.js` to `static/admin/js/vendor-detail.js` + +## Component Usage Guide + +### Form Components + +#### Basic Input +```html + +``` + +#### Input with Validation Error +```html + +``` + +#### Disabled Input +```html + +``` + +#### Textarea +```html + +``` + +### Card Components + +#### Stats Card +```html +
    +
    + +
    +
    +

    + Total Users +

    +

    + 1,234 +

    +
    +
    +``` + +#### Info Card +```html +
    +

    + Card Title +

    +
    +
    +

    Field Label

    +

    Field Value

    +
    +
    +
    +``` + +### Button Components + +#### Primary Button +```html + +``` + +#### Button with Icon +```html + +``` + +#### Secondary Button +```html + +``` + +## Features of Updated Pages + +### Vendor Edit Page Improvements + +1. **Quick Actions Section** + - Verify/Unverify button + - Activate/Deactivate button + - Status badges showing current state + +2. **Better Form Organization** + - Clear sections with headers + - Two-column layout on desktop + - Helper text for all fields + - Proper validation states + +3. **Visual Consistency** + - Uses standard form components + - Consistent spacing and sizing + - Dark mode support + +4. **User Experience** + - Disabled states for read-only fields + - Clear indication of required fields + - Loading states + - Error messages inline with fields + +### Vendor Detail Page Features + +1. **Status Overview** + - 4 stats cards at top showing key metrics + - Visual status indicators (colors, icons) + +2. **Information Organization** + - Basic info card + - Contact info card + - Business details section + - Owner information section + - Marketplace URLs (if available) + +3. **Actions** + - Edit button (goes to edit page) + - Delete button (with double confirmation) + - Back to list button + +## Quick Reference: Where to Find Components + +When you need a component, visit `/admin/components` and you'll find: + +- **Forms Section**: All input types, validation states, helper text +- **Buttons Section**: All button styles and states +- **Cards Section**: Stats cards, info cards +- **Tables Section**: (from your tables.html) +- **Modals Section**: (from your modals.html) +- **Charts Section**: (from your charts.html) + +## Testing Checklist + +- [ ] `/admin/components` page loads and displays all components +- [ ] Components menu item appears in sidebar +- [ ] `/admin/vendors/{vendor_code}/edit` displays correctly +- [ ] Form validation shows errors in red +- [ ] Quick actions (verify/activate) work +- [ ] `/admin/vendors/{vendor_code}` displays all vendor data +- [ ] Status cards show correct information +- [ ] Edit button navigates to edit page +- [ ] Delete button shows double confirmation +- [ ] All pages work in dark mode +- [ ] All pages are responsive on mobile + +## Color Scheme Reference + +Your component library uses these color schemes: + +- **Primary**: Purple (`bg-purple-600`, `text-purple-600`) +- **Success**: Green (`bg-green-600`, `text-green-600`) +- **Warning**: Orange (`bg-orange-600`, `text-orange-600`) +- **Danger**: Red (`bg-red-600`, `text-red-600`) +- **Info**: Blue (`bg-blue-600`, `text-blue-600`) + +## Next Steps + +1. Implement the components page route +2. Add menu item to sidebar +3. Replace vendor-edit.html +4. Add vendor-detail.html and .js +5. Test all pages +6. Apply same patterns to other admin pages (users, imports, etc.) + +## Tips + +- Always reference `/admin/components` when building new pages +- Copy component HTML directly from the components page +- Maintain consistent spacing and styling +- Use Alpine.js x-model for form bindings +- Use your icon system with `x-html="$icon('icon-name', 'w-5 h-5')"` +- Test in both light and dark modes + +Enjoy your new component library! 🎨 diff --git a/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md b/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md new file mode 100644 index 00000000..d9979497 --- /dev/null +++ b/docs/__temp/FRONTEND/FRONTEND_UI_COMPONENTS_QUICK_REFERENCE.md @@ -0,0 +1,228 @@ +# UI Components Quick Reference + +## Most Common Patterns + +### 📝 Form Field (Basic) +```html + +``` + +### 📝 Required Field with Error +```html + +``` + +### 📝 Read-Only Field +```html + +``` + +### 🃏 Stats Card +```html +
    +
    + +
    +
    +

    Label

    +

    Value

    +
    +
    +``` + +### 🃏 Info Card +```html +
    +

    Title

    +
    +
    +

    Label

    +

    Value

    +
    +
    +
    +``` + +### 🔘 Primary Button +```html + +``` + +### 🔘 Button with Icon +```html + +``` + +### 🔘 Secondary Button +```html + +``` + +### 🏷️ Status Badge (Success) +```html + + + Active + +``` + +### 🏷️ Status Badge (Warning) +```html + + + Pending + +``` + +### 🏷️ Status Badge (Danger) +```html + + + Inactive + +``` + +## Grid Layouts + +### 2 Columns (Desktop) +```html +
    + +
    ...
    + +
    ...
    +
    +``` + +### 4 Columns (Responsive) +```html +
    + +
    +``` + +## Color Classes + +### Background Colors +- Primary: `bg-purple-600` +- Success: `bg-green-600` +- Warning: `bg-orange-600` +- Danger: `bg-red-600` +- Info: `bg-blue-600` + +### Text Colors +- Primary: `text-purple-600` +- Success: `text-green-600` +- Warning: `text-orange-600` +- Danger: `text-red-600` +- Info: `text-blue-600` + +### Icon Colors +- Primary: `text-purple-500 bg-purple-100` +- Success: `text-green-500 bg-green-100` +- Warning: `text-orange-500 bg-orange-100` +- Danger: `text-red-500 bg-red-100` +- Info: `text-blue-500 bg-blue-100` + +## Common Icons +- `user-group` - Users/Teams +- `badge-check` - Verified +- `check-circle` - Success +- `x-circle` - Error/Inactive +- `clock` - Pending +- `calendar` - Dates +- `refresh` - Update +- `edit` - Edit +- `delete` - Delete +- `plus` - Add +- `arrow-left` - Back +- `exclamation` - Warning + +## Spacing +- Small gap: `gap-3` +- Medium gap: `gap-6` +- Large gap: `gap-8` +- Margin bottom: `mb-4`, `mb-6`, `mb-8` +- Padding: `p-3`, `p-4`, `px-4 py-3` + +## Quick Copy-Paste: Page Structure + +```html +{# app/templates/admin/your-page.html #} +{% extends "admin/base.html" %} + +{% block title %}Your Page{% endblock %} + +{% block alpine_data %}yourPageData(){% endblock %} + +{% block content %} + +
    +

    + Page Title +

    +
    + + +
    + +

    Loading...

    +
    + + +
    +
    + +
    +
    +{% endblock %} +``` + +## Remember + +1. Always use `dark:` variants for dark mode +2. Add `:disabled="saving"` to buttons during operations +3. Use `x-show` for conditional display +4. Use `x-text` for dynamic text +5. Use `x-html="$icon(...)"` for icons +6. Validation errors: `border-red-600` class +7. Helper text: `text-xs text-gray-600` +8. Error text: `text-xs text-red-600` + +## Reference Page + +Visit `/admin/components` for full component library with live examples! diff --git a/docs/__temp/FRONTEND/PAGINATION_DOCUMENTATION.md b/docs/__temp/FRONTEND/PAGINATION_DOCUMENTATION.md new file mode 100644 index 00000000..13b82521 --- /dev/null +++ b/docs/__temp/FRONTEND/PAGINATION_DOCUMENTATION.md @@ -0,0 +1,297 @@ +# Vendor Page Pagination Implementation + +## 🎯 What's New + +Your vendors page now includes: +1. ✅ **Edit button** - Already present (pencil icon next to view icon) +2. ✅ **Pagination** - New pagination controls at the bottom of the table + +--- + +## 📦 Files Updated + +### 1. vendors.html +**Changes:** +- Changed `x-for="vendor in vendors"` → `x-for="vendor in paginatedVendors"` +- Added pagination footer with controls +- Added "Showing X-Y of Z" results display + +### 2. vendors.js +**Changes:** +- Added pagination state: `currentPage`, `itemsPerPage` +- Added computed properties: + - `paginatedVendors` - Returns current page's vendors + - `totalPages` - Calculates total number of pages + - `startIndex` / `endIndex` - For "Showing X-Y" display + - `pageNumbers` - Generates smart page number array with ellipsis +- Added pagination methods: + - `goToPage(page)` - Navigate to specific page + - `nextPage()` - Go to next page + - `previousPage()` - Go to previous page + +--- + +## 🎨 Pagination Features + +### Smart Page Number Display +The pagination intelligently shows page numbers: + +**Example 1: Few pages (≤7)** +``` +← 1 2 3 4 5 6 7 → +``` + +**Example 2: Many pages, current = 1** +``` +← 1 2 3 ... 9 10 → +``` + +**Example 3: Many pages, current = 5** +``` +← 1 ... 4 5 6 ... 10 → +``` + +**Example 4: Many pages, current = 10** +``` +← 1 ... 8 9 10 → +``` + +### Configurable Items Per Page +Default: 10 vendors per page + +To change, edit in `vendors.js`: +```javascript +itemsPerPage: 10, // Change this number +``` + +--- + +## 🔧 How It Works + +### 1. Computed Properties (Alpine.js) +Alpine.js computes these values reactively: + +```javascript +get paginatedVendors() { + const start = (this.currentPage - 1) * this.itemsPerPage; + const end = start + this.itemsPerPage; + return this.vendors.slice(start, end); +} +``` + +When `currentPage` changes, `paginatedVendors` automatically updates! + +### 2. Template Binding +The HTML template uses the computed property: +```html + +``` + +### 3. Pagination Controls +Buttons call the pagination methods: +```html +