// static/vendor/js/settings.js /** * Vendor settings management page logic * Configure vendor preferences and integrations */ const vendorSettingsLog = window.LogConfig.loggers.vendorSettings || window.LogConfig.createLogger('vendorSettings', false); vendorSettingsLog.info('Loading...'); function vendorSettings() { vendorSettingsLog.info('vendorSettings() called'); return { // Inherit base layout state ...data(), // Set page identifier currentPage: 'settings', // Loading states loading: true, error: '', saving: false, // Settings data from API settings: null, // Active section activeSection: 'general', // Sections for navigation sections: [ { id: 'general', label: 'General', icon: 'cog' }, { id: 'business', label: 'Business Info', icon: 'office-building' }, { id: 'localization', label: 'Localization', icon: 'globe' }, { id: 'marketplace', label: 'Marketplace', icon: 'shopping-cart' }, { id: 'invoices', label: 'Invoices', icon: 'document-text' }, { id: 'branding', label: 'Branding', icon: 'color-swatch' }, { id: 'domains', label: 'Domains', icon: 'globe-alt' }, { id: 'api', label: 'API & Payments', icon: 'key' }, { id: 'notifications', label: 'Notifications', icon: 'bell' }, { id: 'email', label: 'Email', icon: 'envelope' } ], // Forms for different sections generalForm: { subdomain: '', is_active: true }, businessForm: { name: '', description: '', contact_email: '', contact_phone: '', website: '', business_address: '', tax_number: '' }, // Track which fields are inherited from company businessInherited: { contact_email: false, contact_phone: false, website: false, business_address: false, tax_number: false }, // Company name for display companyName: '', marketplaceForm: { letzshop_csv_url_fr: '', letzshop_csv_url_en: '', letzshop_csv_url_de: '', letzshop_default_tax_rate: null, letzshop_boost_sort: '', letzshop_delivery_method: '', letzshop_preorder_days: null }, notificationForm: { email_notifications: true, order_notifications: true, marketing_emails: false }, localizationForm: { default_language: 'fr', dashboard_language: 'fr', storefront_language: 'fr', storefront_languages: ['fr', 'de', 'en'], storefront_locale: '' }, // Email settings emailSettings: null, emailSettingsLoading: false, emailProviders: [], emailForm: { from_email: '', from_name: '', reply_to_email: '', signature_text: '', signature_html: '', provider: 'smtp', // SMTP smtp_host: '', smtp_port: 587, smtp_username: '', smtp_password: '', smtp_use_tls: true, smtp_use_ssl: false, // SendGrid sendgrid_api_key: '', // Mailgun mailgun_api_key: '', mailgun_domain: '', // SES ses_access_key_id: '', ses_secret_access_key: '', ses_region: 'eu-west-1' }, testEmailAddress: '', sendingTestEmail: false, hasEmailChanges: false, // Track changes per section hasChanges: false, hasBusinessChanges: false, hasLocalizationChanges: false, hasMarketplaceChanges: false, async init() { vendorSettingsLog.info('Settings init() called'); // Guard against multiple initialization if (window._vendorSettingsInitialized) { vendorSettingsLog.warn('Already initialized, skipping'); return; } window._vendorSettingsInitialized = true; // IMPORTANT: Call parent init first to set vendorCode from URL const parentInit = data().init; if (parentInit) { await parentInit.call(this); } try { await this.loadSettings(); } catch (error) { vendorSettingsLog.error('Init failed:', error); this.error = 'Failed to initialize settings page'; } vendorSettingsLog.info('Settings initialization complete'); }, /** * Load vendor settings */ async loadSettings() { this.loading = true; this.error = ''; try { const response = await apiClient.get(`/vendor/settings`); this.settings = response; // Populate general form this.generalForm = { subdomain: response.subdomain || '', is_active: response.is_active !== false }; // Populate business info form with inheritance tracking const biz = response.business_info || {}; this.businessForm = { name: response.name || '', description: response.description || '', contact_email: biz.contact_email_override || '', contact_phone: biz.contact_phone_override || '', website: biz.website_override || '', business_address: biz.business_address_override || '', tax_number: biz.tax_number_override || '' }; this.businessInherited = { contact_email: biz.contact_email_inherited || false, contact_phone: biz.contact_phone_inherited || false, website: biz.website_inherited || false, business_address: biz.business_address_inherited || false, tax_number: biz.tax_number_inherited || false }; this.companyName = biz.company_name || ''; // Populate localization form from nested structure const loc = response.localization || {}; this.localizationForm = { default_language: loc.default_language || 'fr', dashboard_language: loc.dashboard_language || 'fr', storefront_language: loc.storefront_language || 'fr', storefront_languages: loc.storefront_languages || ['fr', 'de', 'en'], storefront_locale: loc.storefront_locale || '' }; // Populate marketplace form from nested structure const lz = response.letzshop || {}; this.marketplaceForm = { letzshop_csv_url_fr: lz.csv_url_fr || '', letzshop_csv_url_en: lz.csv_url_en || '', letzshop_csv_url_de: lz.csv_url_de || '', letzshop_default_tax_rate: lz.default_tax_rate, letzshop_boost_sort: lz.boost_sort || '', letzshop_delivery_method: lz.delivery_method || '', letzshop_preorder_days: lz.preorder_days }; // Reset all change flags this.hasChanges = false; this.hasBusinessChanges = false; this.hasLocalizationChanges = false; this.hasMarketplaceChanges = false; vendorSettingsLog.info('Loaded settings'); } catch (error) { vendorSettingsLog.error('Failed to load settings:', error); this.error = error.message || 'Failed to load settings'; } finally { this.loading = false; } }, /** * Mark general form as changed */ markChanged() { this.hasChanges = true; }, /** * Mark business form as changed */ markBusinessChanged() { this.hasBusinessChanges = true; }, /** * Mark localization form as changed */ markLocalizationChanged() { this.hasLocalizationChanges = true; }, /** * Mark marketplace form as changed */ markMarketplaceChanged() { this.hasMarketplaceChanges = true; }, /** * Get effective value for a business field (override or inherited) */ getEffectiveBusinessValue(field) { const override = this.businessForm[field]; if (override) return override; // Return the effective value from settings (includes company inheritance) return this.settings?.business_info?.[field] || ''; }, /** * Check if field is using inherited value */ isFieldInherited(field) { return this.businessInherited[field] && !this.businessForm[field]; }, /** * Reset a business field to inherit from company */ resetToCompany(field) { this.businessForm[field] = ''; this.hasBusinessChanges = true; }, /** * Save business info */ async saveBusinessInfo() { this.saving = true; try { // Determine which fields should be reset to company values const resetFields = []; for (const field of ['contact_email', 'contact_phone', 'website', 'business_address', 'tax_number']) { if (!this.businessForm[field] && this.settings?.business_info?.[field]) { resetFields.push(field); } } const payload = { name: this.businessForm.name, description: this.businessForm.description, contact_email: this.businessForm.contact_email || null, contact_phone: this.businessForm.contact_phone || null, website: this.businessForm.website || null, business_address: this.businessForm.business_address || null, tax_number: this.businessForm.tax_number || null, reset_to_company: resetFields }; await apiClient.put(`/vendor/settings/business-info`, payload); Utils.showToast('Business info saved', 'success'); vendorSettingsLog.info('Business info updated'); // Reload to get updated inheritance flags await this.loadSettings(); this.hasBusinessChanges = false; } catch (error) { vendorSettingsLog.error('Failed to save business info:', error); Utils.showToast(error.message || 'Failed to save business info', 'error'); } finally { this.saving = false; } }, /** * Save marketplace settings (Letzshop) */ async saveMarketplaceSettings() { this.saving = true; try { await apiClient.put(`/vendor/settings/letzshop`, this.marketplaceForm); Utils.showToast('Marketplace settings saved', 'success'); vendorSettingsLog.info('Marketplace settings updated'); this.hasMarketplaceChanges = false; } catch (error) { vendorSettingsLog.error('Failed to save marketplace settings:', error); Utils.showToast(error.message || 'Failed to save settings', 'error'); } finally { this.saving = false; } }, /** * Test Letzshop CSV URL */ async testLetzshopUrl(lang) { const url = this.marketplaceForm[`letzshop_csv_url_${lang}`]; if (!url) { Utils.showToast('Please enter a URL first', 'error'); return; } this.saving = true; try { // Try to fetch the URL to validate it const response = await fetch(url, { method: 'HEAD', mode: 'no-cors' }); Utils.showToast(`URL appears to be valid`, 'success'); } catch (error) { Utils.showToast('Could not validate URL - it may still work', 'warning'); } finally { this.saving = false; } }, /** * Reset settings to saved values */ resetSettings() { this.loadSettings(); }, /** * Switch active section */ setSection(sectionId) { this.activeSection = sectionId; }, /** * Toggle a storefront language */ toggleStorefrontLanguage(langCode) { const index = this.localizationForm.storefront_languages.indexOf(langCode); if (index === -1) { this.localizationForm.storefront_languages.push(langCode); } else { this.localizationForm.storefront_languages.splice(index, 1); } this.hasLocalizationChanges = true; }, /** * Save localization settings */ async saveLocalizationSettings() { this.saving = true; try { await apiClient.put(`/vendor/settings/localization`, this.localizationForm); Utils.showToast('Localization settings saved', 'success'); vendorSettingsLog.info('Localization settings updated'); this.hasLocalizationChanges = false; } catch (error) { vendorSettingsLog.error('Failed to save localization settings:', error); Utils.showToast(error.message || 'Failed to save settings', 'error'); } finally { this.saving = false; } }, // ===================================================================== // EMAIL SETTINGS // ===================================================================== /** * Load email settings when email tab is activated */ async loadEmailSettings() { if (this.emailSettings !== null) { return; // Already loaded } this.emailSettingsLoading = true; try { // Load settings and providers in parallel const [settingsResponse, providersResponse] = await Promise.all([ apiClient.get('/vendor/email-settings'), apiClient.get('/vendor/email-settings/providers') ]); this.emailProviders = providersResponse.providers || []; if (settingsResponse.configured && settingsResponse.settings) { this.emailSettings = settingsResponse.settings; this.populateEmailForm(settingsResponse.settings); } else { this.emailSettings = { is_configured: false, is_verified: false }; } vendorSettingsLog.info('Loaded email settings'); } catch (error) { vendorSettingsLog.error('Failed to load email settings:', error); Utils.showToast('Failed to load email settings', 'error'); } finally { this.emailSettingsLoading = false; } }, /** * Populate email form from settings */ populateEmailForm(settings) { this.emailForm = { from_email: settings.from_email || '', from_name: settings.from_name || '', reply_to_email: settings.reply_to_email || '', signature_text: settings.signature_text || '', signature_html: settings.signature_html || '', provider: settings.provider || 'smtp', // SMTP - don't populate password smtp_host: settings.smtp_host || '', smtp_port: settings.smtp_port || 587, smtp_username: settings.smtp_username || '', smtp_password: '', // Never populate password smtp_use_tls: settings.smtp_use_tls !== false, smtp_use_ssl: settings.smtp_use_ssl || false, // SendGrid - don't populate API key sendgrid_api_key: '', // Mailgun - don't populate API key mailgun_api_key: '', mailgun_domain: settings.mailgun_domain || '', // SES - don't populate secrets ses_access_key_id: '', ses_secret_access_key: '', ses_region: settings.ses_region || 'eu-west-1' }; this.hasEmailChanges = false; }, /** * Mark email form as changed */ markEmailChanged() { this.hasEmailChanges = true; }, /** * Save email settings */ async saveEmailSettings() { // Validate required fields if (!this.emailForm.from_email || !this.emailForm.from_name) { Utils.showToast('From Email and From Name are required', 'error'); return; } this.saving = true; try { const response = await apiClient.put('/vendor/email-settings', this.emailForm); if (response.success) { Utils.showToast('Email settings saved', 'success'); vendorSettingsLog.info('Email settings updated'); // Update local state this.emailSettings = response.settings; this.hasEmailChanges = false; } else { Utils.showToast(response.message || 'Failed to save email settings', 'error'); } } catch (error) { vendorSettingsLog.error('Failed to save email settings:', error); Utils.showToast(error.message || 'Failed to save email settings', 'error'); } finally { this.saving = false; } }, /** * Send test email */ async sendTestEmail() { if (!this.testEmailAddress) { Utils.showToast('Please enter a test email address', 'error'); return; } if (!this.emailSettings?.is_configured) { Utils.showToast('Please save your email settings first', 'error'); return; } this.sendingTestEmail = true; try { const response = await apiClient.post('/vendor/email-settings/verify', { test_email: this.testEmailAddress }); if (response.success) { Utils.showToast('Test email sent! Check your inbox.', 'success'); // Update verification status this.emailSettings.is_verified = true; } else { Utils.showToast(response.message || 'Failed to send test email', 'error'); } } catch (error) { vendorSettingsLog.error('Failed to send test email:', error); Utils.showToast(error.message || 'Failed to send test email', 'error'); } finally { this.sendingTestEmail = false; } }, /** * Switch active section - with email loading hook */ setSection(sectionId) { this.activeSection = sectionId; // Load email settings when email tab is activated if (sectionId === 'email' && this.emailSettings === null) { this.loadEmailSettings(); } } }; }