Files
orion/static/vendor/js/settings.js
Samir Boulahtit c87bdfa129 feat: add configurable currency locale and fix vendor JS init
Currency Locale Configuration:
- Add platform-level storefront settings (locale, currency)
- Create PlatformSettingsService with resolution chain:
  vendor → AdminSetting → environment → hardcoded fallback
- Add storefront_locale nullable field to Vendor model
- Update shop routes to resolve and pass locale to templates
- Add window.SHOP_CONFIG for frontend JavaScript access
- Centralize formatPrice() in shop-layout.js using SHOP_CONFIG
- Remove local formatPrice functions from shop templates

Vendor JS Bug Fix:
- Fix vendorCode being null on all vendor pages
- Root cause: page components overriding init() without calling parent
- Add parent init call to 14 vendor JS files
- Add JS-013 architecture rule to prevent future regressions
- Validator now checks vendor JS files for parent init pattern

Files changed:
- New: app/services/platform_settings_service.py
- New: alembic/versions/s7a8b9c0d1e2_add_storefront_locale_to_vendors.py
- Modified: 14 vendor JS files, shop templates, validation scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-02 21:26:12 +01:00

185 lines
5.4 KiB
JavaScript

// 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
settings: null,
// Active section
activeSection: 'general',
// Sections for navigation
sections: [
{ id: 'general', label: 'General', icon: 'cog' },
{ id: 'marketplace', label: 'Marketplace', icon: 'shopping-cart' },
{ id: 'notifications', label: 'Notifications', icon: 'bell' }
],
// Forms for different sections
generalForm: {
subdomain: '',
is_active: true
},
marketplaceForm: {
letzshop_csv_url_fr: '',
letzshop_csv_url_en: '',
letzshop_csv_url_de: ''
},
notificationForm: {
email_notifications: true,
order_notifications: true,
marketing_emails: false
},
// Track changes
hasChanges: 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/${this.vendorCode}/settings`);
this.settings = response;
// Populate forms
this.generalForm = {
subdomain: response.subdomain || '',
is_active: response.is_active !== false
};
this.marketplaceForm = {
letzshop_csv_url_fr: response.letzshop_csv_url_fr || '',
letzshop_csv_url_en: response.letzshop_csv_url_en || '',
letzshop_csv_url_de: response.letzshop_csv_url_de || ''
};
this.hasChanges = 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 form as changed
*/
markChanged() {
this.hasChanges = true;
},
/**
* Save marketplace settings
*/
async saveMarketplaceSettings() {
this.saving = true;
try {
await apiClient.put(`/vendor/${this.vendorCode}/settings/marketplace`, this.marketplaceForm);
Utils.showToast('Marketplace settings saved', 'success');
vendorSettingsLog.info('Marketplace settings updated');
this.hasChanges = 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;
}
};
}