Files
orion/static/admin/js/vendor-theme.js

309 lines
11 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// static/admin/js/vendor-theme.js
/**
* Vendor Theme Editor - Alpine.js Component
* Manages theme customization for vendor shops
*/
// ============================================================================
// LOGGING CONFIGURATION
// ============================================================================
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)
};
// ============================================================================
// ALPINE.JS COMPONENT
// ============================================================================
function adminVendorTheme() {
return {
// ✅ CRITICAL: Inherit base layout functionality
...data(),
// ✅ CRITICAL: Set page identifier
currentPage: 'vendor-theme',
// Page state
vendorCode: null,
vendor: null,
loading: true,
saving: false,
error: null,
// Theme data structure matching VendorTheme model
themeData: {
theme_name: 'default',
colors: {
primary: '#6366f1',
secondary: '#8b5cf6',
accent: '#ec4899',
background: '#ffffff',
text: '#1f2937',
border: '#e5e7eb'
},
fonts: {
heading: 'Inter, sans-serif',
body: 'Inter, sans-serif'
},
layout: {
style: 'grid',
header: 'fixed',
product_card: 'modern'
},
branding: {
logo: null,
logo_dark: null,
favicon: null,
banner: null
},
custom_css: ''
},
// Available presets
presets: [],
// ============================================================================
// INITIALIZATION
// ============================================================================
async init() {
themeLog.info('=== VENDOR THEME EDITOR INITIALIZING ===');
// ✅ 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...');
try {
const startTime = Date.now();
const response = await apiClient.get(`/api/v1/admin/vendors/${this.vendorCode}`);
const duration = Date.now() - startTime;
this.vendor = response;
themeLog.info(`Vendor loaded in ${duration}ms:`, this.vendor.name);
} 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(`/api/v1/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);
}
} catch (error) {
themeLog.error('Failed to load theme:', error);
this.error = 'Failed to load theme';
Utils.showToast('Failed to load theme', 'error');
} finally {
this.loading = false;
}
},
async loadPresets() {
themeLog.info('Loading presets...');
try {
const startTime = Date.now();
const response = await apiClient.get('/api/v1/admin/vendor-themes/presets');
const duration = Date.now() - startTime;
this.presets = response.presets || [];
themeLog.info(`${this.presets.length} presets loaded in ${duration}ms`);
} catch (error) {
themeLog.warn('Failed to load presets:', error);
// Non-critical error, continue without presets
}
},
// ============================================================================
// PRESET OPERATIONS
// ============================================================================
async applyPreset(presetName) {
themeLog.info(`Applying preset: ${presetName}`);
this.saving = true;
try {
const startTime = Date.now();
const response = await apiClient.post(
`/api/v1/admin/vendor-themes/${this.vendorCode}/preset/${presetName}`
);
const duration = Date.now() - startTime;
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 || ''
};
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');
} finally {
this.saving = false;
}
},
async resetToDefault() {
if (!confirm('Are you sure you want to reset to default theme? This will discard all customizations.')) {
return;
}
themeLog.info('Resetting to default theme');
await this.applyPreset('default');
},
// ============================================================================
// SAVE OPERATIONS
// ============================================================================
async saveTheme() {
themeLog.info('Saving theme:', this.themeData);
this.saving = true;
this.error = null;
try {
const startTime = Date.now();
const response = await apiClient.put(
`/api/v1/admin/vendor-themes/${this.vendorCode}`,
this.themeData
);
const duration = Date.now() - startTime;
if (response) {
Utils.showToast('Theme saved successfully', 'success');
themeLog.info(`Theme saved in ${duration}ms`);
}
} 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;
} finally {
this.saving = false;
}
},
// ============================================================================
// HELPER METHODS
// ============================================================================
formatDate(dateString) {
if (!dateString) return '-';
return Utils.formatDate(dateString);
},
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,
};
}
};
}
// ============================================================================
// MODULE LOADED
// ============================================================================
themeLog.info('Vendor theme editor module loaded');