fix(ui): inject window.FRONTEND_TYPE from server + rename SHOP→STOREFRONT

Server now injects window.FRONTEND_TYPE in all base templates via
get_context_for_frontend(). Both log-config.js and dev-toolbar.js read
this instead of guessing from URL paths, fixing:
- UNKNOWN prefix on merchant pages
- Incorrect detection on custom domains/subdomains in prod

Also adds frontend_type to login page contexts (admin, merchant, store).

Renames all [SHOP] logger prefixes to [STOREFRONT] across 7 files
(storefront-layout.js + 6 storefront templates).

Adds 'merchant' and 'storefront' to log-config.js frontend detection,
log levels, and logger selection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 21:08:59 +01:00
parent 9bceeaac9c
commit b4f01210d9
16 changed files with 99 additions and 79 deletions

View File

@@ -208,7 +208,7 @@ document.addEventListener('alpine:init', () => {
// Initialize // Initialize
async init() { async init() {
console.log('[SHOP] Cart page initializing...'); console.log('[STOREFRONT] Cart page initializing...');
// Call parent init to set up sessionId // Call parent init to set up sessionId
if (baseData.init) { if (baseData.init) {
@@ -223,17 +223,17 @@ document.addEventListener('alpine:init', () => {
this.loading = true; this.loading = true;
try { try {
console.log(`[SHOP] Loading cart for session ${this.sessionId}...`); console.log(`[STOREFRONT] Loading cart for session ${this.sessionId}...`);
const response = await fetch(`/api/v1/storefront/cart/${this.sessionId}`); const response = await fetch(`/api/v1/storefront/cart/${this.sessionId}`);
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
this.items = data.items || []; this.items = data.items || [];
this.cartCount = this.totalItems; this.cartCount = this.totalItems;
console.log('[SHOP] Cart loaded:', this.items.length, 'items'); console.log('[STOREFRONT] Cart loaded:', this.items.length, 'items');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Failed to load cart:', error); console.error('[STOREFRONT] Failed to load cart:', error);
this.showToast('Failed to load cart', 'error'); this.showToast('Failed to load cart', 'error');
} finally { } finally {
this.loading = false; this.loading = false;
@@ -249,7 +249,7 @@ document.addEventListener('alpine:init', () => {
this.updating = true; this.updating = true;
try { try {
console.log('[SHOP] Updating quantity:', productId, newQuantity); console.log('[STOREFRONT] Updating quantity:', productId, newQuantity);
const response = await fetch( const response = await fetch(
`/api/v1/storefront/cart/${this.sessionId}/items/${productId}`, `/api/v1/storefront/cart/${this.sessionId}/items/${productId}`,
{ {
@@ -268,7 +268,7 @@ document.addEventListener('alpine:init', () => {
throw new Error('Failed to update quantity'); throw new Error('Failed to update quantity');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Update quantity error:', error); console.error('[STOREFRONT] Update quantity error:', error);
this.showToast('Failed to update quantity', 'error'); this.showToast('Failed to update quantity', 'error');
} finally { } finally {
this.updating = false; this.updating = false;
@@ -280,7 +280,7 @@ document.addEventListener('alpine:init', () => {
this.updating = true; this.updating = true;
try { try {
console.log('[SHOP] Removing item:', productId); console.log('[STOREFRONT] Removing item:', productId);
const response = await fetch( const response = await fetch(
`/api/v1/storefront/cart/${this.sessionId}/items/${productId}`, `/api/v1/storefront/cart/${this.sessionId}/items/${productId}`,
{ {
@@ -295,7 +295,7 @@ document.addEventListener('alpine:init', () => {
throw new Error('Failed to remove item'); throw new Error('Failed to remove item');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Remove item error:', error); console.error('[STOREFRONT] Remove item error:', error);
this.showToast('Failed to remove item', 'error'); this.showToast('Failed to remove item', 'error');
} finally { } finally {
this.updating = false; this.updating = false;

View File

@@ -187,8 +187,8 @@ document.addEventListener('alpine:init', () => {
}, },
async init() { async init() {
console.log('[SHOP] Category page initializing...'); console.log('[STOREFRONT] Category page initializing...');
console.log('[SHOP] Category slug:', this.categorySlug); console.log('[STOREFRONT] Category slug:', this.categorySlug);
// Convert slug to display name // Convert slug to display name
this.categoryName = this.categorySlug this.categoryName = this.categorySlug
@@ -213,7 +213,7 @@ document.addEventListener('alpine:init', () => {
params.append('sort', this.sortBy); params.append('sort', this.sortBy);
} }
console.log(`[SHOP] Loading category products from /api/v1/storefront/products?${params}`); console.log(`[STOREFRONT] Loading category products from /api/v1/storefront/products?${params}`);
const response = await fetch(`/api/v1/storefront/products?${params}`); const response = await fetch(`/api/v1/storefront/products?${params}`);
@@ -223,12 +223,12 @@ document.addEventListener('alpine:init', () => {
const data = await response.json(); const data = await response.json();
console.log(`[SHOP] Loaded ${data.products.length} products (total: ${data.total})`); console.log(`[STOREFRONT] Loaded ${data.products.length} products (total: ${data.total})`);
this.products = data.products; this.products = data.products;
this.total = data.total; this.total = data.total;
} catch (error) { } catch (error) {
console.error('[SHOP] Failed to load category products:', error); console.error('[STOREFRONT] Failed to load category products:', error);
this.showToast('Failed to load products', 'error'); this.showToast('Failed to load products', 'error');
} finally { } finally {
this.loading = false; this.loading = false;
@@ -243,7 +243,7 @@ document.addEventListener('alpine:init', () => {
}, },
async addToCart(product) { async addToCart(product) {
console.log('[SHOP] Adding to cart:', product); console.log('[STOREFRONT] Adding to cart:', product);
try { try {
const url = `/api/v1/storefront/cart/${this.sessionId}/items`; const url = `/api/v1/storefront/cart/${this.sessionId}/items`;
@@ -262,16 +262,16 @@ document.addEventListener('alpine:init', () => {
if (response.ok) { if (response.ok) {
const result = await response.json(); const result = await response.json();
console.log('[SHOP] Add to cart success:', result); console.log('[STOREFRONT] Add to cart success:', result);
this.cartCount += 1; this.cartCount += 1;
this.showToast(`${product.marketplace_product.title} added to cart`, 'success'); this.showToast(`${product.marketplace_product.title} added to cart`, 'success');
} else { } else {
const error = await response.json(); const error = await response.json();
console.error('[SHOP] Add to cart error:', error); console.error('[STOREFRONT] Add to cart error:', error);
this.showToast(error.message || 'Failed to add to cart', 'error'); this.showToast(error.message || 'Failed to add to cart', 'error');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Add to cart exception:', error); console.error('[STOREFRONT] Add to cart exception:', error);
this.showToast('Failed to add to cart', 'error'); this.showToast('Failed to add to cart', 'error');
} }
} }

View File

@@ -256,16 +256,16 @@ document.addEventListener('alpine:init', () => {
// Initialize // Initialize
async init() { async init() {
console.log('[SHOP] Product detail page initializing...'); console.log('[STOREFRONT] Product detail page initializing...');
// Call parent init to set up sessionId // Call parent init to set up sessionId
if (baseData.init) { if (baseData.init) {
baseData.init.call(this); baseData.init.call(this);
} }
console.log('[SHOP] Product ID:', this.productId); console.log('[STOREFRONT] Product ID:', this.productId);
console.log('[SHOP] Store ID:', this.storeId); console.log('[STOREFRONT] Store ID:', this.storeId);
console.log('[SHOP] Session ID:', this.sessionId); console.log('[STOREFRONT] Session ID:', this.sessionId);
await this.loadProduct(); await this.loadProduct();
}, },
@@ -275,7 +275,7 @@ document.addEventListener('alpine:init', () => {
this.loading = true; this.loading = true;
try { try {
console.log(`[SHOP] Loading product ${this.productId}...`); console.log(`[STOREFRONT] Loading product ${this.productId}...`);
const response = await fetch(`/api/v1/storefront/products/${this.productId}`); const response = await fetch(`/api/v1/storefront/products/${this.productId}`);
if (!response.ok) { if (!response.ok) {
@@ -283,7 +283,7 @@ document.addEventListener('alpine:init', () => {
} }
this.product = await response.json(); this.product = await response.json();
console.log('[SHOP] Product loaded:', this.product); console.log('[STOREFRONT] Product loaded:', this.product);
// Set default image // Set default image
if (this.product?.marketplace_product?.image_link) { if (this.product?.marketplace_product?.image_link) {
@@ -297,7 +297,7 @@ document.addEventListener('alpine:init', () => {
await this.loadRelatedProducts(); await this.loadRelatedProducts();
} catch (error) { } catch (error) {
console.error('[SHOP] Failed to load product:', error); console.error('[STOREFRONT] Failed to load product:', error);
this.showToast('Failed to load product', 'error'); this.showToast('Failed to load product', 'error');
// Redirect back to products after error // Redirect back to products after error
setTimeout(() => { setTimeout(() => {
@@ -320,10 +320,10 @@ document.addEventListener('alpine:init', () => {
.filter(p => p.id !== parseInt(this.productId)) .filter(p => p.id !== parseInt(this.productId))
.slice(0, 4); .slice(0, 4);
console.log('[SHOP] Loaded related products:', this.relatedProducts.length); console.log('[STOREFRONT] Loaded related products:', this.relatedProducts.length);
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Failed to load related products:', error); console.error('[STOREFRONT] Failed to load related products:', error);
} }
}, },
@@ -356,7 +356,7 @@ document.addEventListener('alpine:init', () => {
// Add to cart // Add to cart
async addToCart() { async addToCart() {
if (!this.canAddToCart) { if (!this.canAddToCart) {
console.warn('[SHOP] Cannot add to cart:', { console.warn('[STOREFRONT] Cannot add to cart:', {
canAddToCart: this.canAddToCart, canAddToCart: this.canAddToCart,
isActive: this.product?.is_active, isActive: this.product?.is_active,
inventory: this.product?.available_inventory, inventory: this.product?.available_inventory,
@@ -374,7 +374,7 @@ document.addEventListener('alpine:init', () => {
quantity: this.quantity quantity: this.quantity
}; };
console.log('[SHOP] Adding to cart:', { console.log('[STOREFRONT] Adding to cart:', {
url, url,
sessionId: this.sessionId, sessionId: this.sessionId,
productId: this.productId, productId: this.productId,
@@ -390,14 +390,14 @@ document.addEventListener('alpine:init', () => {
body: JSON.stringify(payload) body: JSON.stringify(payload)
}); });
console.log('[SHOP] Add to cart response:', { console.log('[STOREFRONT] Add to cart response:', {
status: response.status, status: response.status,
ok: response.ok ok: response.ok
}); });
if (response.ok) { if (response.ok) {
const result = await response.json(); const result = await response.json();
console.log('[SHOP] Add to cart success:', result); console.log('[STOREFRONT] Add to cart success:', result);
this.cartCount += this.quantity; this.cartCount += this.quantity;
this.showToast( this.showToast(
@@ -409,11 +409,11 @@ document.addEventListener('alpine:init', () => {
this.quantity = this.product?.min_quantity || 1; this.quantity = this.product?.min_quantity || 1;
} else { } else {
const error = await response.json(); const error = await response.json();
console.error('[SHOP] Add to cart error response:', error); console.error('[STOREFRONT] Add to cart error response:', error);
throw new Error(error.detail || 'Failed to add to cart'); throw new Error(error.detail || 'Failed to add to cart');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Add to cart exception:', error); console.error('[STOREFRONT] Add to cart exception:', error);
this.showToast(error.message || 'Failed to add to cart', 'error'); this.showToast(error.message || 'Failed to add to cart', 'error');
} finally { } finally {
this.addingToCart = false; this.addingToCart = false;

View File

@@ -160,7 +160,7 @@ document.addEventListener('alpine:init', () => {
}, },
async init() { async init() {
console.log('[SHOP] Products page initializing...'); console.log('[STOREFRONT] Products page initializing...');
await this.loadProducts(); await this.loadProducts();
}, },
@@ -178,7 +178,7 @@ document.addEventListener('alpine:init', () => {
params.append('search', this.filters.search); params.append('search', this.filters.search);
} }
console.log(`[SHOP] Loading products from /api/v1/storefront/products?${params}`); console.log(`[STOREFRONT] Loading products from /api/v1/storefront/products?${params}`);
const response = await fetch(`/api/v1/storefront/products?${params}`); const response = await fetch(`/api/v1/storefront/products?${params}`);
@@ -188,12 +188,12 @@ document.addEventListener('alpine:init', () => {
const data = await response.json(); const data = await response.json();
console.log(`[SHOP] Loaded ${data.products.length} products (total: ${data.total})`); console.log(`[STOREFRONT] Loaded ${data.products.length} products (total: ${data.total})`);
this.products = data.products; this.products = data.products;
this.pagination.total = data.total; this.pagination.total = data.total;
} catch (error) { } catch (error) {
console.error('[SHOP] Failed to load products:', error); console.error('[STOREFRONT] Failed to load products:', error);
this.showToast('Failed to load products', 'error'); this.showToast('Failed to load products', 'error');
} finally { } finally {
this.loading = false; this.loading = false;
@@ -208,7 +208,7 @@ document.addEventListener('alpine:init', () => {
// formatPrice is inherited from storefrontLayoutData() via spread operator // formatPrice is inherited from storefrontLayoutData() via spread operator
async addToCart(product) { async addToCart(product) {
console.log('[SHOP] Adding to cart:', product); console.log('[STOREFRONT] Adding to cart:', product);
try { try {
const url = `/api/v1/storefront/cart/${this.sessionId}/items`; const url = `/api/v1/storefront/cart/${this.sessionId}/items`;
@@ -227,16 +227,16 @@ document.addEventListener('alpine:init', () => {
if (response.ok) { if (response.ok) {
const result = await response.json(); const result = await response.json();
console.log('[SHOP] Add to cart success:', result); console.log('[STOREFRONT] Add to cart success:', result);
this.cartCount += 1; this.cartCount += 1;
this.showToast(`${product.marketplace_product.title} added to cart`, 'success'); this.showToast(`${product.marketplace_product.title} added to cart`, 'success');
} else { } else {
const error = await response.json(); const error = await response.json();
console.error('[SHOP] Add to cart error:', error); console.error('[STOREFRONT] Add to cart error:', error);
this.showToast(error.message || 'Failed to add to cart', 'error'); this.showToast(error.message || 'Failed to add to cart', 'error');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Add to cart exception:', error); console.error('[STOREFRONT] Add to cart exception:', error);
this.showToast('Failed to add to cart', 'error'); this.showToast('Failed to add to cart', 'error');
} }
} }

View File

@@ -212,7 +212,7 @@ document.addEventListener('alpine:init', () => {
}, },
async init() { async init() {
console.log('[SHOP] Search page initializing...'); console.log('[STOREFRONT] Search page initializing...');
// Check for query parameter in URL // Check for query parameter in URL
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
@@ -254,7 +254,7 @@ document.addEventListener('alpine:init', () => {
limit: this.perPage limit: this.perPage
}); });
console.log(`[SHOP] Searching: /api/v1/storefront/products/search?${params}`); console.log(`[STOREFRONT] Searching: /api/v1/storefront/products/search?${params}`);
const response = await fetch(`/api/v1/storefront/products/search?${params}`); const response = await fetch(`/api/v1/storefront/products/search?${params}`);
@@ -264,12 +264,12 @@ document.addEventListener('alpine:init', () => {
const data = await response.json(); const data = await response.json();
console.log(`[SHOP] Search found ${data.total} results`); console.log(`[STOREFRONT] Search found ${data.total} results`);
this.products = data.products; this.products = data.products;
this.total = data.total; this.total = data.total;
} catch (error) { } catch (error) {
console.error('[SHOP] Search failed:', error); console.error('[STOREFRONT] Search failed:', error);
this.showToast('Search failed. Please try again.', 'error'); this.showToast('Search failed. Please try again.', 'error');
this.products = []; this.products = [];
this.total = 0; this.total = 0;
@@ -289,7 +289,7 @@ document.addEventListener('alpine:init', () => {
}, },
async addToCart(product) { async addToCart(product) {
console.log('[SHOP] Adding to cart:', product); console.log('[STOREFRONT] Adding to cart:', product);
try { try {
const url = `/api/v1/storefront/cart/${this.sessionId}/items`; const url = `/api/v1/storefront/cart/${this.sessionId}/items`;
@@ -308,16 +308,16 @@ document.addEventListener('alpine:init', () => {
if (response.ok) { if (response.ok) {
const result = await response.json(); const result = await response.json();
console.log('[SHOP] Add to cart success:', result); console.log('[STOREFRONT] Add to cart success:', result);
this.cartCount += 1; this.cartCount += 1;
this.showToast(`${product.marketplace_product?.title || 'Product'} added to cart`, 'success'); this.showToast(`${product.marketplace_product?.title || 'Product'} added to cart`, 'success');
} else { } else {
const error = await response.json(); const error = await response.json();
console.error('[SHOP] Add to cart error:', error); console.error('[STOREFRONT] Add to cart error:', error);
this.showToast(error.message || 'Failed to add to cart', 'error'); this.showToast(error.message || 'Failed to add to cart', 'error');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Add to cart exception:', error); console.error('[STOREFRONT] Add to cart exception:', error);
this.showToast('Failed to add to cart', 'error'); this.showToast('Failed to add to cart', 'error');
} }
} }

View File

@@ -143,7 +143,7 @@ document.addEventListener('alpine:init', () => {
isLoggedIn: false, isLoggedIn: false,
async init() { async init() {
console.log('[SHOP] Wishlist page initializing...'); console.log('[STOREFRONT] Wishlist page initializing...');
// Check if user is logged in // Check if user is logged in
this.isLoggedIn = await this.checkLoginStatus(); this.isLoggedIn = await this.checkLoginStatus();
@@ -168,7 +168,7 @@ document.addEventListener('alpine:init', () => {
this.loading = true; this.loading = true;
try { try {
console.log('[SHOP] Loading wishlist...'); console.log('[STOREFRONT] Loading wishlist...');
const response = await fetch('/api/v1/storefront/wishlist'); const response = await fetch('/api/v1/storefront/wishlist');
@@ -182,11 +182,11 @@ document.addEventListener('alpine:init', () => {
const data = await response.json(); const data = await response.json();
console.log(`[SHOP] Loaded ${data.items?.length || 0} wishlist items`); console.log(`[STOREFRONT] Loaded ${data.items?.length || 0} wishlist items`);
this.items = data.items || []; this.items = data.items || [];
} catch (error) { } catch (error) {
console.error('[SHOP] Failed to load wishlist:', error); console.error('[STOREFRONT] Failed to load wishlist:', error);
this.showToast('Failed to load wishlist', 'error'); this.showToast('Failed to load wishlist', 'error');
} finally { } finally {
this.loading = false; this.loading = false;
@@ -195,7 +195,7 @@ document.addEventListener('alpine:init', () => {
async removeFromWishlist(item) { async removeFromWishlist(item) {
try { try {
console.log('[SHOP] Removing from wishlist:', item); console.log('[STOREFRONT] Removing from wishlist:', item);
const response = await fetch(`/api/v1/storefront/wishlist/${item.id}`, { const response = await fetch(`/api/v1/storefront/wishlist/${item.id}`, {
method: 'DELETE' method: 'DELETE'
@@ -208,13 +208,13 @@ document.addEventListener('alpine:init', () => {
throw new Error('Failed to remove from wishlist'); throw new Error('Failed to remove from wishlist');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Failed to remove from wishlist:', error); console.error('[STOREFRONT] Failed to remove from wishlist:', error);
this.showToast('Failed to remove from wishlist', 'error'); this.showToast('Failed to remove from wishlist', 'error');
} }
}, },
async addToCart(product) { async addToCart(product) {
console.log('[SHOP] Adding to cart:', product); console.log('[STOREFRONT] Adding to cart:', product);
try { try {
const url = `/api/v1/storefront/cart/${this.sessionId}/items`; const url = `/api/v1/storefront/cart/${this.sessionId}/items`;
@@ -233,16 +233,16 @@ document.addEventListener('alpine:init', () => {
if (response.ok) { if (response.ok) {
const result = await response.json(); const result = await response.json();
console.log('[SHOP] Add to cart success:', result); console.log('[STOREFRONT] Add to cart success:', result);
this.cartCount += 1; this.cartCount += 1;
this.showToast(`${product.marketplace_product?.title || 'Product'} added to cart`, 'success'); this.showToast(`${product.marketplace_product?.title || 'Product'} added to cart`, 'success');
} else { } else {
const error = await response.json(); const error = await response.json();
console.error('[SHOP] Add to cart error:', error); console.error('[STOREFRONT] Add to cart error:', error);
this.showToast(error.message || 'Failed to add to cart', 'error'); this.showToast(error.message || 'Failed to add to cart', 'error');
} }
} catch (error) { } catch (error) {
console.error('[SHOP] Add to cart exception:', error); console.error('[STOREFRONT] Add to cart exception:', error);
this.showToast('Failed to add to cart', 'error'); this.showToast('Failed to add to cart', 'error');
} }
} }

View File

@@ -62,6 +62,7 @@ async def admin_login_page(
context = { context = {
"request": request, "request": request,
"current_language": language, "current_language": language,
"frontend_type": "admin",
**get_jinja2_globals(language), **get_jinja2_globals(language),
} }
return templates.TemplateResponse("tenancy/admin/login.html", context) return templates.TemplateResponse("tenancy/admin/login.html", context)

View File

@@ -72,6 +72,7 @@ async def merchant_login_page(
context = { context = {
"request": request, "request": request,
"current_language": language, "current_language": language,
"frontend_type": "merchant",
**get_jinja2_globals(language), **get_jinja2_globals(language),
} }
return templates.TemplateResponse("tenancy/merchant/login.html", context) return templates.TemplateResponse("tenancy/merchant/login.html", context)

View File

@@ -5,11 +5,11 @@
* Works with store-specific themes * Works with store-specific themes
*/ */
const shopLog = { const shopLog = window.LogConfig?.createLogger('STOREFRONT') || {
info: (...args) => console.info('🛒 [SHOP]', ...args), info: (...args) => console.info('🛒 [STOREFRONT]', ...args),
warn: (...args) => console.warn('⚠️ [SHOP]', ...args), warn: (...args) => console.warn('⚠️ [STOREFRONT]', ...args),
error: (...args) => console.error('❌ [SHOP]', ...args), error: (...args) => console.error('❌ [STOREFRONT]', ...args),
debug: (...args) => console.log('🔍 [SHOP]', ...args) debug: (...args) => console.log('🔍 [STOREFRONT]', ...args)
}; };
/** /**

View File

@@ -149,6 +149,9 @@ def get_context_for_frontend(
# Pass enabled module codes to templates for conditional rendering # Pass enabled module codes to templates for conditional rendering
context["enabled_modules"] = enabled_module_codes context["enabled_modules"] = enabled_module_codes
# Pass frontend type to templates (used by JS for logging, dev toolbar, etc.)
context["frontend_type"] = frontend_type.value
# For storefront, build nav menu structure from module declarations # For storefront, build nav menu structure from module declarations
if frontend_type == FrontendType.STOREFRONT: if frontend_type == FrontendType.STOREFRONT:
from app.modules.core.services.menu_discovery_service import ( from app.modules.core.services.menu_discovery_service import (

View File

@@ -100,6 +100,9 @@
<!-- Core Scripts - ORDER MATTERS! --> <!-- Core Scripts - ORDER MATTERS! -->
<!-- 0. Frontend type (server-injected, used by log-config and dev-toolbar) -->
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("admin") }}';</script>
<!-- 1. FIRST: Log Configuration --> <!-- 1. FIRST: Log Configuration -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script> <script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>

View File

@@ -50,6 +50,9 @@
<!-- Core Scripts - ORDER MATTERS! --> <!-- Core Scripts - ORDER MATTERS! -->
<!-- 0. Frontend type (server-injected, used by log-config and dev-toolbar) -->
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("merchant") }}';</script>
<!-- 1. FIRST: Log Configuration --> <!-- 1. FIRST: Log Configuration -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script> <script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>

View File

@@ -54,6 +54,9 @@
<!-- Core Scripts - ORDER MATTERS! --> <!-- Core Scripts - ORDER MATTERS! -->
<!-- 0. Frontend type (server-injected, used by log-config and dev-toolbar) -->
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("store") }}';</script>
<!-- 1. FIRST: Log Configuration --> <!-- 1. FIRST: Log Configuration -->
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script> <script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>

View File

@@ -349,6 +349,9 @@
{# JavaScript Loading Order (CRITICAL - must be in this order) #} {# JavaScript Loading Order (CRITICAL - must be in this order) #}
{# 0. Frontend type (server-injected, used by log-config and dev-toolbar) #}
<script>window.FRONTEND_TYPE = '{{ frontend_type | default("storefront") }}';</script>
{# 1. Log Configuration (must load first) #} {# 1. Log Configuration (must load first) #}
<script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script> <script defer src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>

View File

@@ -286,11 +286,11 @@
} }
function detectFrontend() { function detectFrontend() {
// Prefer server-injected value (set in base templates)
if (window.FRONTEND_TYPE) return window.FRONTEND_TYPE;
// Fallback for pages without base template (e.g., API docs)
var path = window.location.pathname; var path = window.location.pathname;
if (path.startsWith('/store/') || path === '/store') return 'store';
if (path.startsWith('/admin/') || path === '/admin') return 'admin';
if (path.indexOf('/merchants/') !== -1) return 'merchant';
if (path.indexOf('/storefront/') !== -1) return 'storefront';
if (path.startsWith('/api/')) return 'api'; if (path.startsWith('/api/')) return 'api';
return 'unknown'; return 'unknown';
} }

View File

@@ -43,14 +43,11 @@ const LOG_LEVELS = {
/** /**
* Detect which frontend we're in based on URL path * Detect which frontend we're in based on URL path
* @returns {string} 'admin' | 'store' | 'shop' | 'unknown' * @returns {string} 'admin' | 'store' | 'merchant' | 'storefront' | 'unknown'
*/ */
function detectFrontend() { function detectFrontend() {
const path = window.location.pathname; // Prefer server-injected value (set in base templates before this script loads)
if (window.FRONTEND_TYPE) return window.FRONTEND_TYPE;
if (path.startsWith('/admin')) return 'admin';
if (path.startsWith('/store')) return 'store';
if (path.startsWith('/shop')) return 'shop';
return 'unknown'; return 'unknown';
} }
@@ -94,9 +91,13 @@ const DEFAULT_LOG_LEVELS = {
development: LOG_LEVELS.DEBUG, development: LOG_LEVELS.DEBUG,
production: LOG_LEVELS.INFO // Stores might need more logging production: LOG_LEVELS.INFO // Stores might need more logging
}, },
shop: { merchant: {
development: LOG_LEVELS.DEBUG, development: LOG_LEVELS.DEBUG,
production: LOG_LEVELS.ERROR // Shop frontend: minimal logging in production production: LOG_LEVELS.INFO // Merchant portal: same as store
},
storefront: {
development: LOG_LEVELS.DEBUG,
production: LOG_LEVELS.ERROR // Storefront: minimal logging in production
}, },
unknown: { unknown: {
development: LOG_LEVELS.DEBUG, development: LOG_LEVELS.DEBUG,
@@ -275,10 +276,10 @@ const storeLoggers = {
}; };
// ============================================================================ // ============================================================================
// PRE-CONFIGURED LOGGERS FOR SHOP FRONTEND // PRE-CONFIGURED LOGGERS FOR STOREFRONT
// ============================================================================ // ============================================================================
const shopLoggers = { const storefrontLoggers = {
// Product browsing // Product browsing
catalog: createLogger('CATALOG', ACTIVE_LOG_LEVEL), catalog: createLogger('CATALOG', ACTIVE_LOG_LEVEL),
product: createLogger('PRODUCT', ACTIVE_LOG_LEVEL), product: createLogger('PRODUCT', ACTIVE_LOG_LEVEL),
@@ -309,8 +310,10 @@ function getLoggers() {
return adminLoggers; return adminLoggers;
case 'store': case 'store':
return storeLoggers; return storeLoggers;
case 'shop': case 'merchant':
return shopLoggers; return storeLoggers; // Merchant portal reuses store logger set
case 'storefront':
return storefrontLoggers;
default: default:
return {}; // Empty object, use createLogger instead return {}; // Empty object, use createLogger instead
} }