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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user