// static/admin/js/store-theme.js (FIXED VERSION) /** * Store Theme Editor - Alpine.js Component * Manages theme customization for store 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.storeTheme; // ============================================================================ // ALPINE.JS COMPONENT // ============================================================================ function adminStoreTheme() { return { // ✅ CRITICAL: Inherit base layout functionality ...data(), // ✅ CRITICAL: Set page identifier currentPage: 'store-theme', // Page state storeCode: null, store: null, loading: true, saving: false, error: null, showResetThemeModal: false, // Theme data structure matching StoreTheme 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() { // Load i18n translations await I18n.loadModule('tenancy'); // Guard against multiple initialization if (window._adminStoreThemeInitialized) return; window._adminStoreThemeInitialized = true; themeLog.info('Initializing store theme editor'); // Start performance timer const startTime = performance.now(); try { // Extract store code from URL const urlParts = window.location.pathname.split('/'); this.storeCode = urlParts[urlParts.indexOf('stores') + 1]; themeLog.debug('Store code from URL:', this.storeCode); if (!this.storeCode) { throw new Error('Store code not found in URL'); } // Load data in parallel themeLog.group('Loading theme data'); await Promise.all([ this.loadStore(), 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 loadStore() { themeLog.info('Loading store data'); const url = `/admin/stores/${this.storeCode}`; 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.store = response; window.LogConfig.logApiCall('GET', url, this.store, 'response'); themeLog.debug('Store loaded:', this.store); } catch (error) { themeLog.error('Failed to load store:', error); throw error; } }, async loadTheme() { themeLog.info('Loading theme data'); const url = `/admin/store-themes/${this.storeCode}`; 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/store-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/store-themes/${this.storeCode}`; 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(I18n.t('tenancy.messages.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/store-themes/${this.storeCode}/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(I18n.t('tenancy.messages.failed_to_apply_preset'), 'error'); } finally { this.saving = false; } }, async resetTheme() { themeLog.warn('Resetting theme to default'); this.saving = true; try { const url = `/admin/store-themes/${this.storeCode}`; 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(I18n.t('tenancy.messages.theme_reset_to_default'), 'success'); } catch (error) { window.LogConfig.logError(error, 'Reset Theme'); Utils.showToast(I18n.t('tenancy.messages.failed_to_reset_theme'), 'error'); } finally { this.saving = false; } }, // ==================================================================== // UTILITY METHODS // ==================================================================== previewTheme() { themeLog.debug('Opening theme preview'); const previewUrl = `/store/${this.store?.subdomain || this.storeCode}`; 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('Store theme editor module loaded');