diff --git a/app/api/v1/admin/__init__.py b/app/api/v1/admin/__init__.py index 10504e3c..9355adb2 100644 --- a/app/api/v1/admin/__init__.py +++ b/app/api/v1/admin/__init__.py @@ -2,7 +2,7 @@ """ Admin API router aggregation. -This module combines all admin-related API endpoints: +This module combines all admin-related JSON API endpoints: - Authentication (login/logout) - Vendor management (CRUD, bulk operations) - Vendor domains management (custom domains, DNS verification) @@ -13,7 +13,11 @@ This module combines all admin-related API endpoints: - Audit logging - Platform settings - Notifications and alerts -- HTML Pages - Server-rendered pages using Jinja2 + +IMPORTANT: +- This router is for JSON API endpoints only +- HTML page routes are mounted separately in main.py at /vendor/* +- Do NOT include pages.router here - it causes route conflicts """ from fastapi import APIRouter @@ -30,8 +34,7 @@ from . import ( monitoring, audit, settings, - notifications, - pages + notifications ) # Create admin router @@ -100,14 +103,5 @@ router.include_router(settings.router, tags=["admin-settings"]) # Include notifications and alerts endpoints router.include_router(notifications.router, tags=["admin-notifications"]) - -# ============================================================================ -# HTML Page Routes (Jinja2 Templates) -# ============================================================================ - -# Include HTML page routes (these return rendered templates, not JSON) -router.include_router(pages.router, tags=["admin-pages"]) - - # Export the router __all__ = ["router"] diff --git a/app/api/v1/vendor/__init__.py b/app/api/v1/vendor/__init__.py index 5b9ac4f0..37b1f281 100644 --- a/app/api/v1/vendor/__init__.py +++ b/app/api/v1/vendor/__init__.py @@ -1,6 +1,6 @@ # app/api/v1/vendor/__init__.py """ -Vendor API endpoints. +Vendor API router aggregation. This module aggregates all vendor-related JSON API endpoints. @@ -14,7 +14,7 @@ from fastapi import APIRouter # Import all sub-routers (JSON API only) from . import ( - info, # NEW: Vendor info endpoint + info, auth, dashboard, profile, @@ -29,7 +29,6 @@ from . import ( media, notifications, analytics, - # NOTE: pages is NOT imported here - it's mounted separately in main.py ) @@ -66,14 +65,4 @@ router.include_router(media.router, tags=["vendor-media"]) router.include_router(notifications.router, tags=["vendor-notifications"]) router.include_router(analytics.router, tags=["vendor-analytics"]) -# ============================================================================ -# NOTE: HTML Page Routes -# ============================================================================ -# HTML page routes (pages.router) are NOT included here. -# They are mounted separately in main.py at /vendor/* to avoid conflicts. -# -# This separation ensures: -# - JSON API: /api/v1/vendor/* (this router) -# - HTML Pages: /vendor/* (mounted in main.py) - __all__ = ["router"] diff --git a/app/routes/__init__.py b/app/routes/__init__.py index bb97db1d..e69de29b 100644 --- a/app/routes/__init__.py +++ b/app/routes/__init__.py @@ -1,8 +0,0 @@ -# app/routes/__init__.py -""" -Frontend route handlers. -""" - -from .frontend import router - -__all__ = ["router"] diff --git a/app/api/v1/admin/pages.py b/app/routes/admin_pages.py similarity index 99% rename from app/api/v1/admin/pages.py rename to app/routes/admin_pages.py index 3ff25df4..91732de2 100644 --- a/app/api/v1/admin/pages.py +++ b/app/routes/admin_pages.py @@ -1,4 +1,4 @@ -# app/api/v1/admin/pages.py +# app/routes/admin_pages.py """ Admin HTML page routes using Jinja2 templates. diff --git a/app/routes/frontend.py b/app/routes/frontend.py deleted file mode 100644 index 53186466..00000000 --- a/app/routes/frontend.py +++ /dev/null @@ -1,183 +0,0 @@ -# app/routes/frontend.py -""" -Frontend HTML route handlers. - -Serves static HTML files for admin, vendor, and customer interfaces. -Supports both path-based (/vendor/{vendor_code}/) and query-based access. -""" - -from fastapi import APIRouter, Path -from fastapi.responses import FileResponse - -router = APIRouter(include_in_schema=False) - - -# ============================================================================ -# ADMIN ROUTES - DISABLED (Now using Jinja2 templates in pages.py) -# ============================================================================ - -# @router.get("/admin/") -# @router.get("/admin/login") -# async def admin_login(): -# """Serve admin login page""" -# return FileResponse("static/admin/login.html") - - -# @router.get("/admin/dashboard") -# async def admin_dashboard(): -# """Serve admin dashboard page""" -# return FileResponse("static/admin/dashboard.html") - - -# @router.get("/admin/vendors") -# async def admin_vendors(): -# """Serve admin vendors management page""" -# return FileResponse("static/admin/vendors.html") - -# @router.get("/admin/vendor-edit") -# async def admin_vendor_edit(): -# """Serve admin vendor edit page""" -# return FileResponse("static/admin/vendor-edit.html") - -# ============================================================================ -# VENDOR ROUTES (with vendor code in path) -# ============================================================================ - -@router.get("/vendor/{vendor_code}/") -@router.get("/vendor/{vendor_code}/login") -async def vendor_login_with_code(vendor_code: str = Path(...)): - """Serve vendor login page with vendor code in path""" - return FileResponse("static/vendor/login.html") - - -@router.get("/vendor/{vendor_code}/dashboard") -async def vendor_dashboard_with_code(vendor_code: str = Path(...)): - """Serve vendor dashboard page with vendor code in path""" - return FileResponse("static/vendor/dashboard.html") - - -@router.get("/vendor/{vendor_code}/products") -@router.get("/vendor/{vendor_code}/admin/products") -async def vendor_products_with_code(vendor_code: str = Path(...)): - """Serve vendor products management page""" - return FileResponse("static/vendor/admin/products.html") - - -@router.get("/vendor/{vendor_code}/orders") -@router.get("/vendor/{vendor_code}/admin/orders") -async def vendor_orders_with_code(vendor_code: str = Path(...)): - """Serve vendor orders management page""" - return FileResponse("static/vendor/admin/orders.html") - - -@router.get("/vendor/{vendor_code}/marketplace") -@router.get("/vendor/{vendor_code}/admin/marketplace") -async def vendor_marketplace_with_code(vendor_code: str = Path(...)): - """Serve vendor marketplace import page""" - return FileResponse("static/vendor/admin/marketplace.html") - - -@router.get("/vendor/{vendor_code}/customers") -@router.get("/vendor/{vendor_code}/admin/customers") -async def vendor_customers_with_code(vendor_code: str = Path(...)): - """Serve vendor customers management page""" - return FileResponse("static/vendor/admin/customers.html") - - -@router.get("/vendor/{vendor_code}/inventory") -@router.get("/vendor/{vendor_code}/admin/inventory") -async def vendor_inventory_with_code(vendor_code: str = Path(...)): - """Serve vendor inventory management page""" - return FileResponse("static/vendor/admin/inventory.html") - - -@router.get("/vendor/{vendor_code}/team") -@router.get("/vendor/{vendor_code}/admin/team") -async def vendor_team_with_code(vendor_code: str = Path(...)): - """Serve vendor team management page""" - return FileResponse("static/vendor/admin/team.html") - - -# Fallback vendor routes (without vendor code - for query parameter access) -@router.get("/vendor/") -@router.get("/vendor/login") -async def vendor_login(): - """Serve vendor login page (query parameter based)""" - return FileResponse("static/vendor/login.html") - - -@router.get("/vendor/dashboard") -async def vendor_dashboard(): - """Serve vendor dashboard page (query parameter based)""" - return FileResponse("static/vendor/dashboard.html") - - -# ============================================================================ -# CUSTOMER/SHOP ROUTES -# ============================================================================ - -@router.get("/shop/") -@router.get("/shop/products") -async def shop_products(): - """Serve shop products catalog page""" - return FileResponse("static/shop/products.html") - - -@router.get("/shop/products/{product_id}") -async def shop_product_detail(product_id: int): - """Serve product detail page""" - return FileResponse("static/shop/product.html") - - -@router.get("/shop/cart") -async def shop_cart(): - """Serve shopping cart page""" - return FileResponse("static/shop/cart.html") - - -@router.get("/shop/checkout") -async def shop_checkout(): - """Serve checkout page""" - return FileResponse("static/shop/checkout.html") - - -@router.get("/shop/account/register") -async def shop_register(): - """Serve customer registration page""" - return FileResponse("static/shop/account/register.html") - - -@router.get("/shop/account/login") -async def shop_login(): - """Serve customer login page""" - return FileResponse("static/shop/account/login.html") - - -@router.get("/shop/account/dashboard") -async def shop_account_dashboard(): - """Serve customer account dashboard""" - return FileResponse("static/shop/account/dashboard.html") - - -@router.get("/shop/account/orders") -async def shop_orders(): - """Serve customer orders history page""" - return FileResponse("static/shop/account/orders.html") - - -@router.get("/shop/account/orders/{order_id}") -async def shop_order_detail(order_id: int): - """Serve customer order detail page""" - return FileResponse("static/shop/account/order-detail.html") - - -@router.get("/shop/account/profile") -async def shop_profile(): - """Serve customer profile page""" - return FileResponse("static/shop/account/profile.html") - - -@router.get("/shop/account/addresses") -async def shop_addresses(): - """Serve customer addresses management page""" - return FileResponse("static/shop/account/addresses.html") diff --git a/app/api/v1/public/vendors/pages.py b/app/routes/shop_pages.py similarity index 99% rename from app/api/v1/public/vendors/pages.py rename to app/routes/shop_pages.py index d171590a..74e98830 100644 --- a/app/api/v1/public/vendors/pages.py +++ b/app/routes/shop_pages.py @@ -1,4 +1,4 @@ -# app/api/v1/shop/pages.py +# app/routes/shop_pages.py """ Shop/Customer HTML page routes using Jinja2 templates. diff --git a/app/api/v1/vendor/pages.py b/app/routes/vendor_pages.py similarity index 99% rename from app/api/v1/vendor/pages.py rename to app/routes/vendor_pages.py index a13a7e73..4ae356ff 100644 --- a/app/api/v1/vendor/pages.py +++ b/app/routes/vendor_pages.py @@ -1,4 +1,4 @@ -# app/api/v1/vendor/pages.py +# app/routes/vendor_pages.py """ Vendor HTML page routes using Jinja2 templates. diff --git a/app/templates/admin/vendor-create.html b/app/templates/admin/vendor-create.html new file mode 100644 index 00000000..566549bd --- /dev/null +++ b/app/templates/admin/vendor-create.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file diff --git a/app/templates/vendor/admin/dashboard.html b/app/templates/vendor/admin/dashboard.html index 21403ab8..8b5616d8 100644 --- a/app/templates/vendor/admin/dashboard.html +++ b/app/templates/vendor/admin/dashboard.html @@ -4,8 +4,6 @@ Vendor Dashboard - Multi-Tenant Ecommerce Platform - - @@ -155,6 +153,6 @@ - + \ No newline at end of file diff --git a/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/admin/15.web-architecture-revamping.md b/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/admin/15.web-architecture-revamping.md index 89c60a42..0b925d60 100644 --- a/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/admin/15.web-architecture-revamping.md +++ b/docs/__REVAMPING/__PROJECT_ROADMAP/JINJA_MIGRATION/admin/15.web-architecture-revamping.md @@ -986,7 +986,7 @@ from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from app.api.v1.admin import routes as admin_api_routes -from app.api.v1.admin import pages as admin_page_routes +from app.routes import pages as admin_page_routes app = FastAPI(title="Multi-Tenant Platform") @@ -1010,6 +1010,7 @@ app.include_router( tags=["admin-pages"] ) + @app.get("/") async def root(): return {"message": "Multi-Tenant Platform API"} diff --git a/main.py b/main.py index f1ca6c4d..c1cd52cc 100644 --- a/main.py +++ b/main.py @@ -22,9 +22,7 @@ from sqlalchemy.orm import Session from app.api.main import api_router # Import page routers -from app.api.v1.admin import pages as admin_pages -from app.api.v1.vendor import pages as vendor_pages -from app.api.v1.public.vendors import pages as shop_pages +from app.routes import admin_pages, vendor_pages, shop_pages from app.core.config import settings from app.core.database import get_db from app.core.lifespan import lifespan @@ -128,6 +126,7 @@ async def vendor_favicon(): # ============================================================================ # HTML PAGE ROUTES (Jinja2 Templates) # ============================================================================ +# Include HTML page routes (these return rendered templates, not JSON) # Admin pages app.include_router( diff --git a/static/vendor/js/login.js b/static/vendor/js/login.js index 4d74f213..b3db7beb 100644 --- a/static/vendor/js/login.js +++ b/static/vendor/js/login.js @@ -3,9 +3,8 @@ * Vendor login page logic */ -// ✅ Use centralized logger - ONE LINE! -// Create custom logger for login page -const loginLog = window.LogConfig.createLogger('LOGIN'); +// Create custom logger for vendor login page +const vendorLoginLog = window.LogConfig.createLogger('VENDOR-LOGIN'); function vendorLogin() { return { @@ -23,29 +22,38 @@ function vendorLogin() { dark: false, async init() { + vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZING ==='); + // Load theme const theme = localStorage.getItem('theme'); if (theme === 'dark') { this.dark = true; } + vendorLoginLog.debug('Dark mode:', this.dark); // Get vendor code from URL path const pathSegments = window.location.pathname.split('/').filter(Boolean); if (pathSegments[0] === 'vendor' && pathSegments[1]) { this.vendorCode = pathSegments[1]; + vendorLoginLog.debug('Vendor code from URL:', this.vendorCode); await this.loadVendor(); } this.checked = true; + vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZATION COMPLETE ==='); }, async loadVendor() { + vendorLoginLog.info('Loading vendor information...'); this.loading = true; try { const response = await apiClient.get(`/vendor/${this.vendorCode}`); this.vendor = response; - logInfo('Vendor loaded', this.vendor); + vendorLoginLog.info('Vendor loaded successfully:', { + code: this.vendor.code, + name: this.vendor.name + }); } catch (error) { - logError('Failed to load vendor', error); + window.LogConfig.logError(error, 'Load Vendor'); this.error = 'Failed to load vendor information'; } finally { this.loading = false; @@ -53,6 +61,7 @@ function vendorLogin() { }, async handleLogin() { + vendorLoginLog.info('=== VENDOR LOGIN ATTEMPT STARTED ==='); this.clearErrors(); this.loading = true; @@ -65,30 +74,50 @@ function vendorLogin() { } if (Object.keys(this.errors).length > 0) { + vendorLoginLog.warn('Validation failed:', this.errors); this.loading = false; return; } + vendorLoginLog.info('Calling vendor login API...'); + vendorLoginLog.debug('Username:', this.credentials.username); + vendorLoginLog.debug('Vendor code:', this.vendorCode); + + window.LogConfig.logApiCall('POST', '/vendor/auth/login', { + username: this.credentials.username, + vendor_code: this.vendorCode + }, 'request'); + + const startTime = performance.now(); const response = await apiClient.post('/vendor/auth/login', { username: this.credentials.username, password: this.credentials.password, vendor_code: this.vendorCode }); + const duration = performance.now() - startTime; - logInfo('Login successful', response); + window.LogConfig.logApiCall('POST', '/vendor/auth/login', { + hasToken: !!response.access_token, + user: response.user?.username + }, 'response'); + window.LogConfig.logPerformance('Vendor Login', duration); + + vendorLoginLog.info('Login successful!'); + vendorLoginLog.debug('Storing authentication data...'); localStorage.setItem('accessToken', response.access_token); localStorage.setItem('currentUser', JSON.stringify(response.user)); localStorage.setItem('vendorCode', this.vendorCode); this.success = 'Login successful! Redirecting...'; + vendorLoginLog.info('Redirecting to vendor dashboard...'); setTimeout(() => { window.location.href = `/vendor/${this.vendorCode}/dashboard`; }, 1000); } catch (error) { - logError('Login failed', error); + window.LogConfig.logError(error, 'Vendor Login'); if (error.status === 401) { this.error = 'Invalid username or password'; @@ -97,14 +126,26 @@ function vendorLogin() { } else { this.error = error.message || 'Login failed. Please try again.'; } + vendorLoginLog.info('Error message displayed to user:', this.error); } finally { this.loading = false; + vendorLoginLog.info('=== VENDOR LOGIN ATTEMPT FINISHED ==='); } }, clearErrors() { + vendorLoginLog.debug('Clearing form errors'); this.error = ''; this.errors = {}; + }, + + toggleDarkMode() { + vendorLoginLog.debug('Toggling dark mode...'); + this.dark = !this.dark; + localStorage.setItem('theme', this.dark ? 'dark' : 'light'); + vendorLoginLog.info('Dark mode:', this.dark ? 'ON' : 'OFF'); } }; -} \ No newline at end of file +} + +vendorLoginLog.info('Vendor login module loaded');