// 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 (using centralized logger) // ============================================================================ // Use the pre-configured theme logger from centralized log-config.js const themeLog = window.LogConfig.loggers.vendorTheme; // ============================================================================ // 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', size_base: '16px', size_heading: '2rem' }, layout: { style: 'grid', header_position: 'fixed', product_card_style: 'card', sidebar_position: 'left' }, branding: { logo_url: '', favicon_url: '', banner_url: '' }, custom_css: '', social_links: { facebook: '', instagram: '', twitter: '', linkedin: '' } }, // Available presets presets: [], selectedPreset: null, // ==================================================================== // INITIALIZATION // ==================================================================== async init() { themeLog.info('Initializing vendor theme editor'); // Start performance timer const startTime = performance.now(); try { // Extract vendor code from URL const urlParts = window.location.pathname.split('/'); this.vendorCode = urlParts[urlParts.indexOf('vendors') + 1]; themeLog.debug('Vendor code from URL:', this.vendorCode); if (!this.vendorCode) { throw new Error('Vendor code not found in URL'); } // 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; } }, // ==================================================================== // DATA LOADING // ==================================================================== async loadVendor() { themeLog.info('Loading vendor data'); const url = `/admin/vendors/${this.vendorCode}`; window.LogConfig.logApiCall('GET', url, null, 'request'); try { // ✅ FIX: apiClient returns data directly, not response.data const response = await apiClient.get(url); // ✅ 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.error('Failed to load vendor:', error); throw error; } }, 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 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) { this.themeData = { ...this.themeData, ...response.theme }; } 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 resetTheme() { if (!confirm('Reset theme to default? This cannot be undone.')) { return; } themeLog.warn('Resetting theme to default'); this.saving = true; try { const url = `/admin/vendor-themes/${this.vendorCode}`; window.LogConfig.logApiCall('DELETE', url, null, 'request'); 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) { window.LogConfig.logError(error, 'Reset Theme'); Utils.showToast('Failed to reset theme', 'error'); } finally { this.saving = false; } }, // ==================================================================== // UTILITY METHODS // ==================================================================== previewTheme() { themeLog.debug('Opening theme preview'); const previewUrl = `/vendor/${this.vendor?.subdomain || this.vendorCode}`; window.open(previewUrl, '_blank'); }, 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; } }; } // ============================================================================ // MODULE LOADED // ============================================================================ themeLog.info('Vendor theme editor module loaded');