278 lines
8.4 KiB
JavaScript
278 lines
8.4 KiB
JavaScript
// static/admin/js/vendor-theme.js
|
||
/**
|
||
* Vendor Theme Management Component
|
||
* Follows the established Alpine.js pattern from FRONTEND_ALPINE_PAGE_TEMPLATE.md
|
||
*/
|
||
|
||
const THEME_LOG_LEVEL = 3;
|
||
|
||
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)
|
||
};
|
||
|
||
// Theme presets
|
||
const THEME_PRESETS = {
|
||
modern: {
|
||
colors: {
|
||
primary: "#6366f1",
|
||
secondary: "#8b5cf6",
|
||
accent: "#ec4899"
|
||
},
|
||
fonts: {
|
||
heading: "Inter, sans-serif",
|
||
body: "Inter, sans-serif"
|
||
},
|
||
layout: {
|
||
style: "grid",
|
||
header: "fixed"
|
||
}
|
||
},
|
||
classic: {
|
||
colors: {
|
||
primary: "#1e40af",
|
||
secondary: "#7c3aed",
|
||
accent: "#dc2626"
|
||
},
|
||
fonts: {
|
||
heading: "Georgia, serif",
|
||
body: "Arial, sans-serif"
|
||
},
|
||
layout: {
|
||
style: "list",
|
||
header: "static"
|
||
}
|
||
},
|
||
minimal: {
|
||
colors: {
|
||
primary: "#000000",
|
||
secondary: "#404040",
|
||
accent: "#666666"
|
||
},
|
||
fonts: {
|
||
heading: "Helvetica, sans-serif",
|
||
body: "Helvetica, sans-serif"
|
||
},
|
||
layout: {
|
||
style: "grid",
|
||
header: "transparent"
|
||
}
|
||
},
|
||
vibrant: {
|
||
colors: {
|
||
primary: "#f59e0b",
|
||
secondary: "#ef4444",
|
||
accent: "#8b5cf6"
|
||
},
|
||
fonts: {
|
||
heading: "Poppins, sans-serif",
|
||
body: "Open Sans, sans-serif"
|
||
},
|
||
layout: {
|
||
style: "masonry",
|
||
header: "fixed"
|
||
}
|
||
}
|
||
};
|
||
|
||
function vendorThemeData() {
|
||
return {
|
||
// ✅ CRITICAL: Inherit base layout functionality
|
||
...data(),
|
||
|
||
// ✅ CRITICAL: Set page identifier
|
||
currentPage: 'vendor-theme',
|
||
|
||
// Page state
|
||
loading: false,
|
||
saving: false,
|
||
vendor: null,
|
||
vendorCode: window.location.pathname.split('/')[3], // Extract from /admin/vendors/{code}/theme
|
||
|
||
// Theme data
|
||
themeData: {
|
||
colors: {
|
||
primary: "#6366f1",
|
||
secondary: "#8b5cf6",
|
||
accent: "#ec4899"
|
||
},
|
||
fonts: {
|
||
heading: "Inter, sans-serif",
|
||
body: "Inter, sans-serif"
|
||
},
|
||
layout: {
|
||
style: "grid",
|
||
header: "fixed"
|
||
},
|
||
custom_css: ""
|
||
},
|
||
|
||
originalTheme: null, // For detecting changes
|
||
|
||
// ✅ CRITICAL: Proper initialization with guard
|
||
async init() {
|
||
themeLog.info('=== VENDOR THEME PAGE INITIALIZING ===');
|
||
|
||
// Prevent multiple initializations
|
||
if (window._vendorThemeInitialized) {
|
||
themeLog.warn('Page already initialized, skipping...');
|
||
return;
|
||
}
|
||
window._vendorThemeInitialized = true;
|
||
|
||
// Load data
|
||
await this.loadVendor();
|
||
await this.loadTheme();
|
||
|
||
themeLog.info('=== VENDOR THEME PAGE INITIALIZATION COMPLETE ===');
|
||
},
|
||
|
||
// Load vendor info
|
||
async loadVendor() {
|
||
themeLog.info('Loading vendor:', this.vendorCode);
|
||
|
||
try {
|
||
// ✅ CRITICAL: Use lowercase apiClient
|
||
const response = await apiClient.get(`/api/v1/admin/vendors/${this.vendorCode}`);
|
||
this.vendor = response;
|
||
themeLog.info('Vendor loaded:', this.vendor.name);
|
||
} catch (error) {
|
||
themeLog.error('Failed to load vendor:', error);
|
||
Utils.showToast('Failed to load vendor', 'error');
|
||
}
|
||
},
|
||
|
||
// Load theme configuration
|
||
async loadTheme() {
|
||
themeLog.info('Loading theme...');
|
||
this.loading = true;
|
||
|
||
try {
|
||
const startTime = Date.now();
|
||
|
||
// Get vendor's theme config from vendor object
|
||
if (this.vendor && this.vendor.theme_config) {
|
||
this.themeData = {
|
||
colors: this.vendor.theme_config.colors || this.themeData.colors,
|
||
fonts: this.vendor.theme_config.fonts || this.themeData.fonts,
|
||
layout: this.vendor.theme_config.layout || this.themeData.layout,
|
||
custom_css: this.vendor.theme_config.custom_css || ""
|
||
};
|
||
} else {
|
||
themeLog.info('No theme config found, using defaults');
|
||
}
|
||
|
||
// Store original for change detection
|
||
this.originalTheme = JSON.parse(JSON.stringify(this.themeData));
|
||
|
||
const duration = Date.now() - startTime;
|
||
themeLog.info(`Theme loaded in ${duration}ms`, this.themeData);
|
||
|
||
} catch (error) {
|
||
themeLog.error('Failed to load theme:', error);
|
||
Utils.showToast('Failed to load theme', 'error');
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
|
||
// Save theme configuration
|
||
async saveTheme() {
|
||
themeLog.info('Saving theme...');
|
||
this.saving = true;
|
||
|
||
try {
|
||
const startTime = Date.now();
|
||
|
||
// Update vendor with new theme_config
|
||
const updateData = {
|
||
theme_config: this.themeData
|
||
};
|
||
|
||
const response = await apiClient.put(
|
||
`/api/v1/admin/vendors/${this.vendorCode}`,
|
||
updateData
|
||
);
|
||
|
||
const duration = Date.now() - startTime;
|
||
themeLog.info(`Theme saved in ${duration}ms`);
|
||
|
||
// Update vendor data
|
||
this.vendor = response;
|
||
this.originalTheme = JSON.parse(JSON.stringify(this.themeData));
|
||
|
||
Utils.showToast('Theme saved successfully', 'success');
|
||
|
||
} catch (error) {
|
||
themeLog.error('Failed to save theme:', error);
|
||
Utils.showToast('Failed to save theme', 'error');
|
||
} finally {
|
||
this.saving = false;
|
||
}
|
||
},
|
||
|
||
// Apply preset theme
|
||
applyPreset(presetName) {
|
||
themeLog.info('Applying preset:', presetName);
|
||
|
||
if (!THEME_PRESETS[presetName]) {
|
||
themeLog.error('Unknown preset:', presetName);
|
||
return;
|
||
}
|
||
|
||
const preset = THEME_PRESETS[presetName];
|
||
|
||
// Apply preset values
|
||
this.themeData.colors = { ...preset.colors };
|
||
this.themeData.fonts = { ...preset.fonts };
|
||
this.themeData.layout = { ...preset.layout };
|
||
|
||
Utils.showToast(`Applied ${presetName} theme preset`, 'success');
|
||
},
|
||
|
||
// Reset to default theme
|
||
resetToDefault() {
|
||
themeLog.info('Resetting to default theme');
|
||
|
||
// Confirm with user
|
||
if (!confirm('Are you sure you want to reset to the default theme? This will discard all customizations.')) {
|
||
return;
|
||
}
|
||
|
||
this.themeData = {
|
||
colors: {
|
||
primary: "#6366f1",
|
||
secondary: "#8b5cf6",
|
||
accent: "#ec4899"
|
||
},
|
||
fonts: {
|
||
heading: "Inter, sans-serif",
|
||
body: "Inter, sans-serif"
|
||
},
|
||
layout: {
|
||
style: "grid",
|
||
header: "fixed"
|
||
},
|
||
custom_css: ""
|
||
};
|
||
|
||
Utils.showToast('Theme reset to default', 'info');
|
||
},
|
||
|
||
// Check if theme has unsaved changes
|
||
hasChanges() {
|
||
if (!this.originalTheme) return false;
|
||
return JSON.stringify(this.themeData) !== JSON.stringify(this.originalTheme);
|
||
},
|
||
|
||
// Format date helper
|
||
formatDate(dateString) {
|
||
if (!dateString) return '-';
|
||
return Utils.formatDate(dateString);
|
||
}
|
||
};
|
||
}
|
||
|
||
themeLog.info('Vendor theme module loaded'); |