revamping frontend logging system and reorganising documentation
This commit is contained in:
@@ -1,22 +1,17 @@
|
||||
// static/admin/js/vendor-theme.js
|
||||
// static/admin/js/vendor-theme.js (FIXED VERSION)
|
||||
/**
|
||||
* Vendor Theme Editor - Alpine.js Component
|
||||
* Manages theme customization for vendor shops
|
||||
*
|
||||
* REQUIRES: log-config.js to be loaded first
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// LOGGING CONFIGURATION
|
||||
// LOGGING CONFIGURATION (using centralized logger)
|
||||
// ============================================================================
|
||||
|
||||
const THEME_LOG_LEVEL = 3; // 1=error, 2=warn, 3=info, 4=debug
|
||||
|
||||
const themeLog = {
|
||||
error: (...args) => THEME_LOG_LEVEL >= 1 && console.error('❌ [THEME ERROR]', ...args),
|
||||
warn: (...args) => THEME_LOG_LEVEL >= 2 && console.warn('⚠️ [THEME WARN]', ...args),
|
||||
info: (...args) => THEME_LOG_LEVEL >= 3 && console.info('ℹ️ [THEME INFO]', ...args),
|
||||
debug: (...args) => THEME_LOG_LEVEL >= 4 && console.log('🔍 [THEME DEBUG]', ...args)
|
||||
};
|
||||
|
||||
// Use the pre-configured theme logger from centralized log-config.js
|
||||
const themeLog = window.LogConfig.loggers.vendorTheme;
|
||||
|
||||
// ============================================================================
|
||||
// ALPINE.JS COMPONENT
|
||||
@@ -50,254 +45,278 @@ function adminVendorTheme() {
|
||||
},
|
||||
fonts: {
|
||||
heading: 'Inter, sans-serif',
|
||||
body: 'Inter, sans-serif'
|
||||
body: 'Inter, sans-serif',
|
||||
size_base: '16px',
|
||||
size_heading: '2rem'
|
||||
},
|
||||
layout: {
|
||||
style: 'grid',
|
||||
header: 'fixed',
|
||||
product_card: 'modern'
|
||||
header_position: 'fixed',
|
||||
product_card_style: 'card',
|
||||
sidebar_position: 'left'
|
||||
},
|
||||
branding: {
|
||||
logo: null,
|
||||
logo_dark: null,
|
||||
favicon: null,
|
||||
banner: null
|
||||
logo_url: '',
|
||||
favicon_url: '',
|
||||
banner_url: ''
|
||||
},
|
||||
custom_css: ''
|
||||
custom_css: '',
|
||||
social_links: {
|
||||
facebook: '',
|
||||
instagram: '',
|
||||
twitter: '',
|
||||
linkedin: ''
|
||||
}
|
||||
},
|
||||
|
||||
// Available presets
|
||||
presets: [],
|
||||
selectedPreset: null,
|
||||
|
||||
// ============================================================================
|
||||
// ====================================================================
|
||||
// INITIALIZATION
|
||||
// ============================================================================
|
||||
// ====================================================================
|
||||
|
||||
async init() {
|
||||
themeLog.info('=== VENDOR THEME EDITOR INITIALIZING ===');
|
||||
themeLog.info('Initializing vendor theme editor');
|
||||
|
||||
// ✅ CRITICAL: Prevent multiple initializations
|
||||
if (window._vendorThemeInitialized) {
|
||||
themeLog.warn('Theme editor already initialized, skipping...');
|
||||
return;
|
||||
}
|
||||
window._vendorThemeInitialized = true;
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
// Get vendor code from URL
|
||||
this.vendorCode = this.getVendorCodeFromURL();
|
||||
themeLog.info('Vendor code:', this.vendorCode);
|
||||
|
||||
// Load data
|
||||
await Promise.all([
|
||||
this.loadVendorData(),
|
||||
this.loadTheme(),
|
||||
this.loadPresets()
|
||||
]);
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
themeLog.info(`=== THEME EDITOR INITIALIZATION COMPLETE (${duration}ms) ===`);
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
// URL HELPERS
|
||||
// ============================================================================
|
||||
|
||||
getVendorCodeFromURL() {
|
||||
const pathParts = window.location.pathname.split('/');
|
||||
const vendorIndex = pathParts.indexOf('vendors');
|
||||
return pathParts[vendorIndex + 1];
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
// DATA LOADING
|
||||
// ============================================================================
|
||||
|
||||
async loadVendorData() {
|
||||
themeLog.info('Loading vendor data...');
|
||||
// Start performance timer
|
||||
const startTime = performance.now();
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const response = await apiClient.get(`/admin/vendors/${this.vendorCode}`);
|
||||
const duration = Date.now() - startTime;
|
||||
// Extract vendor code from URL
|
||||
const urlParts = window.location.pathname.split('/');
|
||||
this.vendorCode = urlParts[urlParts.indexOf('vendors') + 1];
|
||||
|
||||
this.vendor = response;
|
||||
themeLog.info(`Vendor loaded in ${duration}ms:`, this.vendor.name);
|
||||
themeLog.debug('Vendor code from URL:', this.vendorCode);
|
||||
|
||||
} catch (error) {
|
||||
themeLog.error('Failed to load vendor:', error);
|
||||
this.error = 'Failed to load vendor data';
|
||||
Utils.showToast('Failed to load vendor data', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
async loadTheme() {
|
||||
themeLog.info('Loading theme...');
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const response = await apiClient.get(`/admin/vendor-themes/${this.vendorCode}`);
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
if (response) {
|
||||
// Merge loaded theme with defaults
|
||||
this.themeData = {
|
||||
theme_name: response.theme_name || 'default',
|
||||
colors: {
|
||||
...this.themeData.colors,
|
||||
...(response.colors || {})
|
||||
},
|
||||
fonts: {
|
||||
heading: response.fonts?.heading || this.themeData.fonts.heading,
|
||||
body: response.fonts?.body || this.themeData.fonts.body
|
||||
},
|
||||
layout: {
|
||||
style: response.layout?.style || this.themeData.layout.style,
|
||||
header: response.layout?.header || this.themeData.layout.header,
|
||||
product_card: response.layout?.product_card || this.themeData.layout.product_card
|
||||
},
|
||||
branding: {
|
||||
...this.themeData.branding,
|
||||
...(response.branding || {})
|
||||
},
|
||||
custom_css: response.custom_css || ''
|
||||
};
|
||||
|
||||
themeLog.info(`Theme loaded in ${duration}ms:`, this.themeData.theme_name);
|
||||
if (!this.vendorCode) {
|
||||
throw new Error('Vendor code not found in URL');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
themeLog.error('Failed to load theme:', error);
|
||||
this.error = 'Failed to load theme';
|
||||
Utils.showToast('Failed to load theme', 'error');
|
||||
// Load data in parallel
|
||||
themeLog.group('Loading theme data');
|
||||
|
||||
await Promise.all([
|
||||
this.loadVendor(),
|
||||
this.loadTheme(),
|
||||
this.loadPresets()
|
||||
]);
|
||||
|
||||
themeLog.groupEnd();
|
||||
|
||||
// Log performance
|
||||
const duration = performance.now() - startTime;
|
||||
window.LogConfig.logPerformance('Theme Editor Init', duration);
|
||||
|
||||
themeLog.info('Theme editor initialized successfully');
|
||||
|
||||
} catch (error) {
|
||||
// Use centralized error logger
|
||||
window.LogConfig.logError(error, 'Theme Editor Init');
|
||||
|
||||
this.error = error.message || 'Failed to initialize theme editor';
|
||||
Utils.showToast(this.error, 'error');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadPresets() {
|
||||
themeLog.info('Loading presets...');
|
||||
// ====================================================================
|
||||
// DATA LOADING
|
||||
// ====================================================================
|
||||
|
||||
async loadVendor() {
|
||||
themeLog.info('Loading vendor data');
|
||||
|
||||
const url = `/admin/vendors/${this.vendorCode}`;
|
||||
window.LogConfig.logApiCall('GET', url, null, 'request');
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const response = await apiClient.get('/admin/vendor-themes/presets');
|
||||
const duration = Date.now() - startTime;
|
||||
// ✅ FIX: apiClient returns data directly, not response.data
|
||||
const response = await apiClient.get(url);
|
||||
|
||||
this.presets = response.presets || [];
|
||||
themeLog.info(`${this.presets.length} presets loaded in ${duration}ms`);
|
||||
// ✅ Direct assignment - response IS the data
|
||||
this.vendor = response;
|
||||
|
||||
window.LogConfig.logApiCall('GET', url, this.vendor, 'response');
|
||||
themeLog.debug('Vendor loaded:', this.vendor);
|
||||
|
||||
} catch (error) {
|
||||
themeLog.warn('Failed to load presets:', error);
|
||||
// Non-critical error, continue without presets
|
||||
themeLog.error('Failed to load vendor:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
// PRESET OPERATIONS
|
||||
// ============================================================================
|
||||
async loadTheme() {
|
||||
themeLog.info('Loading theme data');
|
||||
|
||||
const url = `/admin/vendor-themes/${this.vendorCode}`;
|
||||
window.LogConfig.logApiCall('GET', url, null, 'request');
|
||||
|
||||
try {
|
||||
// ✅ FIX: apiClient returns data directly
|
||||
const response = await apiClient.get(url);
|
||||
|
||||
// Merge with default theme data
|
||||
this.themeData = {
|
||||
...this.themeData,
|
||||
...response
|
||||
};
|
||||
|
||||
window.LogConfig.logApiCall('GET', url, this.themeData, 'response');
|
||||
themeLog.debug('Theme loaded:', this.themeData);
|
||||
|
||||
} catch (error) {
|
||||
themeLog.warn('Failed to load theme, using defaults:', error);
|
||||
// Continue with default theme
|
||||
}
|
||||
},
|
||||
|
||||
async loadPresets() {
|
||||
themeLog.info('Loading theme presets');
|
||||
|
||||
const url = '/admin/vendor-themes/presets';
|
||||
window.LogConfig.logApiCall('GET', url, null, 'request');
|
||||
|
||||
try {
|
||||
// ✅ FIX: apiClient returns data directly
|
||||
const response = await apiClient.get(url);
|
||||
|
||||
// ✅ Access presets directly from response, not response.data.presets
|
||||
this.presets = response.presets || [];
|
||||
|
||||
window.LogConfig.logApiCall('GET', url, response, 'response');
|
||||
themeLog.debug(`Loaded ${this.presets.length} presets`);
|
||||
|
||||
} catch (error) {
|
||||
themeLog.error('Failed to load presets:', error);
|
||||
this.presets = [];
|
||||
}
|
||||
},
|
||||
|
||||
// ====================================================================
|
||||
// THEME OPERATIONS
|
||||
// ====================================================================
|
||||
|
||||
async saveTheme() {
|
||||
if (this.saving) return;
|
||||
|
||||
themeLog.info('Saving theme changes');
|
||||
this.saving = true;
|
||||
this.error = null;
|
||||
|
||||
const startTime = performance.now();
|
||||
|
||||
try {
|
||||
const url = `/admin/vendor-themes/${this.vendorCode}`;
|
||||
window.LogConfig.logApiCall('PUT', url, this.themeData, 'request');
|
||||
|
||||
// ✅ FIX: apiClient returns data directly
|
||||
const response = await apiClient.put(url, this.themeData);
|
||||
|
||||
window.LogConfig.logApiCall('PUT', url, response, 'response');
|
||||
|
||||
const duration = performance.now() - startTime;
|
||||
window.LogConfig.logPerformance('Save Theme', duration);
|
||||
|
||||
themeLog.info('Theme saved successfully');
|
||||
Utils.showToast('Theme saved successfully', 'success');
|
||||
|
||||
} catch (error) {
|
||||
window.LogConfig.logError(error, 'Save Theme');
|
||||
this.error = 'Failed to save theme';
|
||||
Utils.showToast(this.error, 'error');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
|
||||
async applyPreset(presetName) {
|
||||
themeLog.info(`Applying preset: ${presetName}`);
|
||||
this.saving = true;
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const response = await apiClient.post(
|
||||
`/admin/vendor-themes/${this.vendorCode}/preset/${presetName}`
|
||||
);
|
||||
const duration = Date.now() - startTime;
|
||||
const url = `/admin/vendor-themes/${this.vendorCode}/preset/${presetName}`;
|
||||
window.LogConfig.logApiCall('POST', url, null, 'request');
|
||||
|
||||
// ✅ FIX: apiClient returns data directly
|
||||
const response = await apiClient.post(url);
|
||||
|
||||
window.LogConfig.logApiCall('POST', url, response, 'response');
|
||||
|
||||
// ✅ FIX: Access theme directly from response, not response.data.theme
|
||||
if (response && response.theme) {
|
||||
// Update theme data with preset
|
||||
this.themeData = {
|
||||
theme_name: response.theme.theme_name,
|
||||
colors: response.theme.colors || this.themeData.colors,
|
||||
fonts: response.theme.fonts || this.themeData.fonts,
|
||||
layout: response.theme.layout || this.themeData.layout,
|
||||
branding: response.theme.branding || this.themeData.branding,
|
||||
custom_css: response.theme.custom_css || ''
|
||||
...this.themeData,
|
||||
...response.theme
|
||||
};
|
||||
|
||||
Utils.showToast(`Applied ${presetName} preset successfully`, 'success');
|
||||
themeLog.info(`Preset applied in ${duration}ms`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
themeLog.error('Failed to apply preset:', error);
|
||||
const message = error.response?.data?.detail || 'Failed to apply preset';
|
||||
Utils.showToast(message, 'error');
|
||||
themeLog.info(`Preset '${presetName}' applied successfully`);
|
||||
Utils.showToast(`Applied ${presetName} preset`, 'success');
|
||||
|
||||
} catch (error) {
|
||||
window.LogConfig.logError(error, 'Apply Preset');
|
||||
Utils.showToast('Failed to apply preset', 'error');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
|
||||
async resetToDefault() {
|
||||
if (!confirm('Are you sure you want to reset to default theme? This will discard all customizations.')) {
|
||||
async resetTheme() {
|
||||
if (!confirm('Reset theme to default? This cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
themeLog.info('Resetting to default theme');
|
||||
await this.applyPreset('default');
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
// SAVE OPERATIONS
|
||||
// ============================================================================
|
||||
|
||||
async saveTheme() {
|
||||
themeLog.info('Saving theme:', this.themeData);
|
||||
themeLog.warn('Resetting theme to default');
|
||||
this.saving = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const response = await apiClient.put(
|
||||
`/admin/vendor-themes/${this.vendorCode}`,
|
||||
this.themeData
|
||||
);
|
||||
const duration = Date.now() - startTime;
|
||||
const url = `/admin/vendor-themes/${this.vendorCode}`;
|
||||
window.LogConfig.logApiCall('DELETE', url, null, 'request');
|
||||
|
||||
if (response) {
|
||||
Utils.showToast('Theme saved successfully', 'success');
|
||||
themeLog.info(`Theme saved in ${duration}ms`);
|
||||
}
|
||||
await apiClient.delete(url);
|
||||
|
||||
window.LogConfig.logApiCall('DELETE', url, null, 'response');
|
||||
|
||||
// Reload theme data
|
||||
await this.loadTheme();
|
||||
|
||||
themeLog.info('Theme reset successfully');
|
||||
Utils.showToast('Theme reset to default', 'success');
|
||||
|
||||
} catch (error) {
|
||||
themeLog.error('Failed to save theme:', error);
|
||||
const message = error.response?.data?.detail || 'Failed to save theme';
|
||||
Utils.showToast(message, 'error');
|
||||
this.error = message;
|
||||
|
||||
window.LogConfig.logError(error, 'Reset Theme');
|
||||
Utils.showToast('Failed to reset theme', 'error');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
|
||||
// ============================================================================
|
||||
// HELPER METHODS
|
||||
// ============================================================================
|
||||
// ====================================================================
|
||||
// UTILITY METHODS
|
||||
// ====================================================================
|
||||
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '-';
|
||||
return Utils.formatDate(dateString);
|
||||
previewTheme() {
|
||||
themeLog.debug('Opening theme preview');
|
||||
const previewUrl = `/vendor/${this.vendor?.subdomain || this.vendorCode}`;
|
||||
window.open(previewUrl, '_blank');
|
||||
},
|
||||
|
||||
getPreviewStyle() {
|
||||
return {
|
||||
'--color-primary': this.themeData.colors.primary,
|
||||
'--color-secondary': this.themeData.colors.secondary,
|
||||
'--color-accent': this.themeData.colors.accent,
|
||||
'--color-background': this.themeData.colors.background,
|
||||
'--color-text': this.themeData.colors.text,
|
||||
'--color-border': this.themeData.colors.border,
|
||||
'--font-heading': this.themeData.fonts.heading,
|
||||
'--font-body': this.themeData.fonts.body,
|
||||
};
|
||||
updateColor(key, value) {
|
||||
themeLog.debug(`Color updated: ${key} = ${value}`);
|
||||
this.themeData.colors[key] = value;
|
||||
},
|
||||
|
||||
updateFont(type, value) {
|
||||
themeLog.debug(`Font updated: ${type} = ${value}`);
|
||||
this.themeData.fonts[type] = value;
|
||||
},
|
||||
|
||||
updateLayout(key, value) {
|
||||
themeLog.debug(`Layout updated: ${key} = ${value}`);
|
||||
this.themeData.layout[key] = value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ const Icons = {
|
||||
'folder-open': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 19a2 2 0 01-2-2V7a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1M5 19h14a2 2 0 002-2v-5a2 2 0 00-2-2H9a2 2 0 00-2 2v5a2 2 0 01-2 2z"/></svg>`,
|
||||
'download': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>`,
|
||||
'upload': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/></svg>`,
|
||||
'save': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>`,
|
||||
|
||||
// Settings & Tools
|
||||
'cog': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>`,
|
||||
@@ -77,6 +78,10 @@ const Icons = {
|
||||
'moon': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>`,
|
||||
'sun': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>`,
|
||||
|
||||
// Design & Theming
|
||||
'palette': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"/></svg>`,
|
||||
'color-swatch': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"/></svg>`,
|
||||
|
||||
// Location
|
||||
'location-marker': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>`,
|
||||
'globe': `<svg class="{{classes}}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>`,
|
||||
|
||||
490
static/shared/js/log-config.js
Normal file
490
static/shared/js/log-config.js
Normal file
@@ -0,0 +1,490 @@
|
||||
// static/shared/js/log-config.js
|
||||
/**
|
||||
* Centralized Logging Configuration for ALL Frontends
|
||||
*
|
||||
* This file provides a consistent logging system across:
|
||||
* - Admin Frontend
|
||||
* - Vendor Frontend
|
||||
* - Shop Frontend
|
||||
*
|
||||
* Each frontend can customize log levels while sharing the same logging infrastructure.
|
||||
*
|
||||
* Usage in any frontend:
|
||||
* ```javascript
|
||||
* // Use the global logger
|
||||
* log.info('Page loaded');
|
||||
* log.error('Something went wrong', error);
|
||||
*
|
||||
* // Or use a pre-configured logger
|
||||
* const vendorLog = window.LogConfig.loggers.vendors;
|
||||
* vendorLog.info('Vendors loaded');
|
||||
*
|
||||
* // Or create a custom logger
|
||||
* const pageLog = window.LogConfig.createLogger('MY-PAGE', 3);
|
||||
* pageLog.info('Page initialized');
|
||||
* ```
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// LOG LEVELS
|
||||
// ============================================================================
|
||||
|
||||
const LOG_LEVELS = {
|
||||
ERROR: 1, // Only errors
|
||||
WARN: 2, // Errors and warnings
|
||||
INFO: 3, // Errors, warnings, and info (default)
|
||||
DEBUG: 4 // Everything including debug messages
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// FRONTEND DETECTION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Detect which frontend we're in based on URL path
|
||||
* @returns {string} 'admin' | 'vendor' | 'shop' | 'unknown'
|
||||
*/
|
||||
function detectFrontend() {
|
||||
const path = window.location.pathname;
|
||||
|
||||
if (path.startsWith('/admin')) return 'admin';
|
||||
if (path.startsWith('/vendor')) return 'vendor';
|
||||
if (path.startsWith('/shop')) return 'shop';
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ENVIRONMENT DETECTION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Detect environment based on hostname
|
||||
* @returns {string} 'development' | 'production'
|
||||
*/
|
||||
function detectEnvironment() {
|
||||
const hostname = window.location.hostname;
|
||||
|
||||
// Development environments
|
||||
if (hostname === 'localhost' ||
|
||||
hostname === '127.0.0.1' ||
|
||||
hostname.startsWith('192.168.') ||
|
||||
hostname.endsWith('.local')) {
|
||||
return 'development';
|
||||
}
|
||||
|
||||
return 'production';
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// LOG LEVEL CONFIGURATION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Default log levels per frontend
|
||||
* Can be overridden by environment
|
||||
*/
|
||||
const DEFAULT_LOG_LEVELS = {
|
||||
admin: {
|
||||
development: LOG_LEVELS.DEBUG, // Show everything in development
|
||||
production: LOG_LEVELS.WARN // Only warnings and errors in production
|
||||
},
|
||||
vendor: {
|
||||
development: LOG_LEVELS.DEBUG,
|
||||
production: LOG_LEVELS.INFO // Vendors might need more logging
|
||||
},
|
||||
shop: {
|
||||
development: LOG_LEVELS.DEBUG,
|
||||
production: LOG_LEVELS.ERROR // Shop frontend: minimal logging in production
|
||||
},
|
||||
unknown: {
|
||||
development: LOG_LEVELS.DEBUG,
|
||||
production: LOG_LEVELS.WARN
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ACTIVE LOG LEVEL DETERMINATION
|
||||
// ============================================================================
|
||||
|
||||
const CURRENT_FRONTEND = detectFrontend();
|
||||
const CURRENT_ENVIRONMENT = detectEnvironment();
|
||||
const ACTIVE_LOG_LEVEL = DEFAULT_LOG_LEVELS[CURRENT_FRONTEND][CURRENT_ENVIRONMENT];
|
||||
|
||||
// ============================================================================
|
||||
// LOGGING UTILITIES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Create a logger with a specific prefix and log level
|
||||
*
|
||||
* @param {string} prefix - Logger prefix (e.g., 'VENDORS', 'THEME', 'PRODUCTS')
|
||||
* @param {number} level - Log level (1-4, defaults to ACTIVE_LOG_LEVEL)
|
||||
* @param {string} context - Optional frontend context (defaults to CURRENT_FRONTEND)
|
||||
* @returns {Object} Logger object with error, warn, info, debug methods
|
||||
*/
|
||||
function createLogger(prefix = 'APP', level = ACTIVE_LOG_LEVEL, context = CURRENT_FRONTEND) {
|
||||
const frontendPrefix = context.toUpperCase();
|
||||
const formatPrefix = (emoji, label) => `${emoji} [${frontendPrefix}:${prefix} ${label}]`;
|
||||
|
||||
return {
|
||||
error: (...args) => {
|
||||
if (level >= LOG_LEVELS.ERROR) {
|
||||
console.error(formatPrefix('❌', 'ERROR'), ...args);
|
||||
}
|
||||
},
|
||||
|
||||
warn: (...args) => {
|
||||
if (level >= LOG_LEVELS.WARN) {
|
||||
console.warn(formatPrefix('⚠️', 'WARN'), ...args);
|
||||
}
|
||||
},
|
||||
|
||||
info: (...args) => {
|
||||
if (level >= LOG_LEVELS.INFO) {
|
||||
console.info(formatPrefix('ℹ️', 'INFO'), ...args);
|
||||
}
|
||||
},
|
||||
|
||||
debug: (...args) => {
|
||||
if (level >= LOG_LEVELS.DEBUG) {
|
||||
console.log(formatPrefix('🔍', 'DEBUG'), ...args);
|
||||
}
|
||||
},
|
||||
|
||||
// Additional utility methods
|
||||
group: (label) => {
|
||||
if (level >= LOG_LEVELS.INFO) {
|
||||
console.group(formatPrefix('📂', 'GROUP') + ' ' + label);
|
||||
}
|
||||
},
|
||||
|
||||
groupEnd: () => {
|
||||
if (level >= LOG_LEVELS.INFO) {
|
||||
console.groupEnd();
|
||||
}
|
||||
},
|
||||
|
||||
table: (data) => {
|
||||
if (level >= LOG_LEVELS.DEBUG) {
|
||||
console.table(data);
|
||||
}
|
||||
},
|
||||
|
||||
time: (label) => {
|
||||
if (level >= LOG_LEVELS.DEBUG) {
|
||||
console.time(formatPrefix('⏱️', 'TIME') + ' ' + label);
|
||||
}
|
||||
},
|
||||
|
||||
timeEnd: (label) => {
|
||||
if (level >= LOG_LEVELS.DEBUG) {
|
||||
console.timeEnd(formatPrefix('⏱️', 'TIME') + ' ' + label);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DEFAULT GLOBAL LOGGER
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Default logger for general operations
|
||||
* Automatically prefixed with current frontend
|
||||
*/
|
||||
const log = createLogger('APP', ACTIVE_LOG_LEVEL);
|
||||
|
||||
// ============================================================================
|
||||
// PRE-CONFIGURED LOGGERS FOR ADMIN FRONTEND
|
||||
// ============================================================================
|
||||
|
||||
const adminLoggers = {
|
||||
// Vendor management
|
||||
vendors: createLogger('VENDORS', ACTIVE_LOG_LEVEL),
|
||||
vendorTheme: createLogger('THEME', ACTIVE_LOG_LEVEL),
|
||||
vendorUsers: createLogger('VENDOR-USERS', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Product management
|
||||
products: createLogger('PRODUCTS', ACTIVE_LOG_LEVEL),
|
||||
inventory: createLogger('INVENTORY', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Order management
|
||||
orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// User management
|
||||
users: createLogger('USERS', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Admin operations
|
||||
audit: createLogger('AUDIT', ACTIVE_LOG_LEVEL),
|
||||
dashboard: createLogger('DASHBOARD', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Import operations
|
||||
imports: createLogger('IMPORTS', ACTIVE_LOG_LEVEL)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// PRE-CONFIGURED LOGGERS FOR VENDOR FRONTEND
|
||||
// ============================================================================
|
||||
|
||||
const vendorLoggers = {
|
||||
// Vendor dashboard
|
||||
dashboard: createLogger('DASHBOARD', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Product management
|
||||
products: createLogger('PRODUCTS', ACTIVE_LOG_LEVEL),
|
||||
inventory: createLogger('INVENTORY', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Order management
|
||||
orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Theme customization
|
||||
theme: createLogger('THEME', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Settings
|
||||
settings: createLogger('SETTINGS', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Analytics
|
||||
analytics: createLogger('ANALYTICS', ACTIVE_LOG_LEVEL)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// PRE-CONFIGURED LOGGERS FOR SHOP FRONTEND
|
||||
// ============================================================================
|
||||
|
||||
const shopLoggers = {
|
||||
// Product browsing
|
||||
catalog: createLogger('CATALOG', ACTIVE_LOG_LEVEL),
|
||||
product: createLogger('PRODUCT', ACTIVE_LOG_LEVEL),
|
||||
search: createLogger('SEARCH', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Shopping cart
|
||||
cart: createLogger('CART', ACTIVE_LOG_LEVEL),
|
||||
checkout: createLogger('CHECKOUT', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// User account
|
||||
account: createLogger('ACCOUNT', ACTIVE_LOG_LEVEL),
|
||||
orders: createLogger('ORDERS', ACTIVE_LOG_LEVEL),
|
||||
|
||||
// Wishlist
|
||||
wishlist: createLogger('WISHLIST', ACTIVE_LOG_LEVEL)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// SMART LOGGER SELECTION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Get the appropriate logger set for current frontend
|
||||
*/
|
||||
function getLoggers() {
|
||||
switch (CURRENT_FRONTEND) {
|
||||
case 'admin':
|
||||
return adminLoggers;
|
||||
case 'vendor':
|
||||
return vendorLoggers;
|
||||
case 'shop':
|
||||
return shopLoggers;
|
||||
default:
|
||||
return {}; // Empty object, use createLogger instead
|
||||
}
|
||||
}
|
||||
|
||||
// Export frontend-specific loggers
|
||||
const loggers = getLoggers();
|
||||
|
||||
// ============================================================================
|
||||
// API CALL LOGGING
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Log API calls with consistent formatting
|
||||
*
|
||||
* @param {string} method - HTTP method (GET, POST, PUT, DELETE)
|
||||
* @param {string} url - API endpoint URL
|
||||
* @param {*} data - Request/response data (optional)
|
||||
* @param {string} status - 'request' or 'response'
|
||||
*/
|
||||
function logApiCall(method, url, data = null, status = 'request') {
|
||||
const apiLogger = createLogger('API', ACTIVE_LOG_LEVEL);
|
||||
|
||||
const emoji = status === 'request' ? '📤' : '📥';
|
||||
const message = `${emoji} ${method} ${url}`;
|
||||
|
||||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG) {
|
||||
if (data) {
|
||||
apiLogger.debug(message, data);
|
||||
} else {
|
||||
apiLogger.debug(message);
|
||||
}
|
||||
} else if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.INFO) {
|
||||
apiLogger.info(message);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ERROR LOGGING
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Log errors with stack traces
|
||||
*
|
||||
* @param {Error} error - Error object
|
||||
* @param {string} context - Context where error occurred
|
||||
*/
|
||||
function logError(error, context = 'Unknown') {
|
||||
const errorLogger = createLogger('ERROR', ACTIVE_LOG_LEVEL);
|
||||
|
||||
errorLogger.error(`Error in ${context}:`, error.message);
|
||||
|
||||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG && error.stack) {
|
||||
console.error('Stack trace:', error.stack);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PERFORMANCE LOGGING
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Log performance metrics
|
||||
*
|
||||
* @param {string} operation - Operation name
|
||||
* @param {number} duration - Duration in milliseconds
|
||||
*/
|
||||
function logPerformance(operation, duration) {
|
||||
const perfLogger = createLogger('PERF', ACTIVE_LOG_LEVEL);
|
||||
|
||||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.DEBUG) {
|
||||
const emoji = duration < 100 ? '⚡' : duration < 500 ? '⏱️' : '🐌';
|
||||
perfLogger.debug(`${emoji} ${operation} took ${duration}ms`);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// EXPORTS (for modules)
|
||||
// ============================================================================
|
||||
|
||||
// For pages that use module imports
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
LOG_LEVELS,
|
||||
log,
|
||||
loggers,
|
||||
createLogger,
|
||||
logApiCall,
|
||||
logError,
|
||||
logPerformance,
|
||||
detectFrontend,
|
||||
detectEnvironment
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GLOBAL ACCESS (for inline scripts)
|
||||
// ============================================================================
|
||||
|
||||
// Make available globally for inline scripts
|
||||
window.LogConfig = {
|
||||
LOG_LEVELS,
|
||||
log,
|
||||
loggers,
|
||||
createLogger,
|
||||
logApiCall,
|
||||
logError,
|
||||
logPerformance,
|
||||
|
||||
// Expose frontend/environment info
|
||||
frontend: CURRENT_FRONTEND,
|
||||
environment: CURRENT_ENVIRONMENT,
|
||||
logLevel: ACTIVE_LOG_LEVEL
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// INITIALIZATION
|
||||
// ============================================================================
|
||||
|
||||
// Log that logging system is initialized
|
||||
if (ACTIVE_LOG_LEVEL >= LOG_LEVELS.INFO) {
|
||||
const frontendName = CURRENT_FRONTEND.charAt(0).toUpperCase() + CURRENT_FRONTEND.slice(1);
|
||||
const envName = CURRENT_ENVIRONMENT.charAt(0).toUpperCase() + CURRENT_ENVIRONMENT.slice(1);
|
||||
const levelName = Object.keys(LOG_LEVELS).find(k => LOG_LEVELS[k] === ACTIVE_LOG_LEVEL);
|
||||
|
||||
console.log(
|
||||
`%c🎛️ ${frontendName} Frontend Logging System Initialized`,
|
||||
'font-weight: bold; font-size: 14px; color: #6366f1;'
|
||||
);
|
||||
console.log(
|
||||
`%c Environment: ${envName}`,
|
||||
'color: #8b5cf6;'
|
||||
);
|
||||
console.log(
|
||||
`%c Log Level: ${ACTIVE_LOG_LEVEL} (${levelName})`,
|
||||
'color: #8b5cf6;'
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// USAGE EXAMPLES (for developers)
|
||||
// ============================================================================
|
||||
|
||||
/*
|
||||
|
||||
EXAMPLE 1: Use global logger
|
||||
=============================
|
||||
window.LogConfig.log.info('Application started');
|
||||
window.LogConfig.log.error('Failed to load data', error);
|
||||
|
||||
|
||||
EXAMPLE 2: Use pre-configured logger (RECOMMENDED)
|
||||
===================================================
|
||||
// In admin frontend
|
||||
const themeLog = window.LogConfig.loggers.vendorTheme;
|
||||
themeLog.info('Theme editor loaded');
|
||||
|
||||
// In vendor frontend
|
||||
const dashLog = window.LogConfig.loggers.dashboard;
|
||||
dashLog.info('Dashboard initialized');
|
||||
|
||||
// In shop frontend
|
||||
const cartLog = window.LogConfig.loggers.cart;
|
||||
cartLog.info('Cart updated');
|
||||
|
||||
|
||||
EXAMPLE 3: Create custom logger
|
||||
================================
|
||||
const myLog = window.LogConfig.createLogger('MY-FEATURE', 4);
|
||||
myLog.info('Feature initialized');
|
||||
|
||||
|
||||
EXAMPLE 4: Check current frontend
|
||||
==================================
|
||||
if (window.LogConfig.frontend === 'admin') {
|
||||
// Admin-specific code
|
||||
}
|
||||
|
||||
|
||||
EXAMPLE 5: API call logging
|
||||
============================
|
||||
window.LogConfig.logApiCall('GET', '/api/vendors', null, 'request');
|
||||
const data = await apiClient.get('/api/vendors');
|
||||
window.LogConfig.logApiCall('GET', '/api/vendors', data, 'response');
|
||||
|
||||
|
||||
EXAMPLE 6: Performance logging
|
||||
===============================
|
||||
const start = performance.now();
|
||||
await loadData();
|
||||
const duration = performance.now() - start;
|
||||
window.LogConfig.logPerformance('Load Data', duration);
|
||||
|
||||
|
||||
EXAMPLE 7: Error logging
|
||||
=========================
|
||||
try {
|
||||
await saveData();
|
||||
} catch (error) {
|
||||
window.LogConfig.logError(error, 'Save Operation');
|
||||
}
|
||||
|
||||
*/
|
||||
Reference in New Issue
Block a user