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>
This commit is contained in:
@@ -462,13 +462,7 @@ function shopOrderDetailPage() {
|
||||
return this.statuses[status]?.class || 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200';
|
||||
},
|
||||
|
||||
formatPrice(amount) {
|
||||
if (!amount && amount !== 0) return '-';
|
||||
return new Intl.NumberFormat('fr-LU', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount);
|
||||
},
|
||||
// formatPrice is inherited from shopLayoutData() via spread operator
|
||||
|
||||
formatDateTime(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
|
||||
@@ -217,13 +217,7 @@ function shopOrdersPage() {
|
||||
return this.statuses[status]?.class || 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200';
|
||||
},
|
||||
|
||||
formatPrice(amount) {
|
||||
if (!amount && amount !== 0) return '-';
|
||||
return new Intl.NumberFormat('fr-LU', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount);
|
||||
},
|
||||
// formatPrice is inherited from shopLayoutData() via spread operator
|
||||
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
|
||||
@@ -523,13 +523,7 @@ function shopProfilePage() {
|
||||
}
|
||||
},
|
||||
|
||||
formatPrice(amount) {
|
||||
if (!amount && amount !== 0) return '-';
|
||||
return new Intl.NumberFormat('fr-LU', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount);
|
||||
},
|
||||
// formatPrice is inherited from shopLayoutData() via spread operator
|
||||
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return '-';
|
||||
|
||||
@@ -308,19 +308,28 @@
|
||||
{# 1. Log Configuration (must load first) #}
|
||||
<script src="{{ url_for('static', path='shared/js/log-config.js') }}"></script>
|
||||
|
||||
{# 2. Icon System #}
|
||||
{# 2. Global Shop Configuration (currency/locale settings) #}
|
||||
<script>
|
||||
window.SHOP_CONFIG = {
|
||||
locale: '{{ storefront_locale | default("fr-LU") }}',
|
||||
currency: '{{ storefront_currency | default("EUR") }}',
|
||||
language: '{{ request.state.language|default("fr") }}'
|
||||
};
|
||||
</script>
|
||||
|
||||
{# 3. Icon System #}
|
||||
<script src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
|
||||
{# 3. Base Shop Layout (Alpine.js component - must load before Alpine) #}
|
||||
{# 4. Base Shop Layout (Alpine.js component - must load before Alpine) #}
|
||||
<script src="{{ url_for('static', path='shop/js/shop-layout.js') }}"></script>
|
||||
|
||||
{# 4. Utilities #}
|
||||
{# 5. Utilities #}
|
||||
<script src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
|
||||
{# 5. API Client #}
|
||||
{# 6. API Client #}
|
||||
<script src="{{ url_for('static', path='shared/js/api-client.js') }}"></script>
|
||||
|
||||
{# 6. Alpine.js with CDN fallback (deferred - loads last) #}
|
||||
{# 7. Alpine.js with CDN fallback (deferred - loads last) #}
|
||||
<script>
|
||||
(function() {
|
||||
var script = document.createElement('script');
|
||||
@@ -339,7 +348,7 @@
|
||||
})();
|
||||
</script>
|
||||
|
||||
{# 7. Page-specific JavaScript #}
|
||||
{# 8. Page-specific JavaScript #}
|
||||
{% block extra_scripts %}{% endblock %}
|
||||
|
||||
{# Toast notification container #}
|
||||
|
||||
@@ -207,13 +207,7 @@ document.addEventListener('alpine:init', () => {
|
||||
this.loadProducts();
|
||||
},
|
||||
|
||||
formatPrice(amount) {
|
||||
if (!amount && amount !== 0) return '';
|
||||
return new Intl.NumberFormat('fr-LU', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount);
|
||||
},
|
||||
// formatPrice is inherited from shopLayoutData() via spread operator
|
||||
|
||||
async addToCart(product) {
|
||||
console.log('[SHOP] Adding to cart:', product);
|
||||
|
||||
Reference in New Issue
Block a user