Files
orion/app/modules/tenancy/static/admin/js/platform-edit.js
Samir Boulahtit 820ab1aaa4
Some checks failed
CI / ruff (push) Successful in 11s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled
feat(i18n): add multilingual platform descriptions and HostWizard demo data
- Add description_translations JSON column to Platform model + migration
- Add language tabs to platform admin edit form for multilingual descriptions
- Update API schemas to include description_translations in request/response
- Translate pricing section UI labels via _t() macro (monthly/annual/CTA/etc.)
- Add Luxembourgish (lb) support to all platforms (OMS, Main, Loyalty, Hosting)
- Seed description_translations, contact emails, and social links for all platforms
- Add LuxWeb Agency demo merchant with hosting stores, team, and content pages
- Fix language code typo: lu → lb in platform-edit.js availableLanguages
- Fix store content pages to use correct primary platform instead of hardcoded OMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 23:38:52 +01:00

265 lines
7.9 KiB
JavaScript

/**
* Platform Edit - Alpine.js Component
*
* Handles platform editing for multi-platform CMS.
*/
const platformEditLog = window.LogConfig.createLogger('PLATFORM_EDIT');
function platformEdit() {
return {
// Inherit base layout functionality from init-alpine.js
...data(),
// Page identification
currentPage: 'platform-edit',
// State
platform: null,
loading: true,
saving: false,
error: null,
success: null,
platformCode: null,
// Language editing
currentLang: 'fr',
languageNames: {
fr: 'Fran\u00e7ais',
de: 'Deutsch',
en: 'English',
lb: 'L\u00ebtzebuergesch',
},
// Form data
formData: {
name: '',
description: '',
description_translations: {},
domain: '',
path_prefix: '',
logo: '',
logo_dark: '',
favicon: '',
default_language: 'fr',
supported_languages: ['fr', 'de', 'en'],
is_active: true,
is_public: true,
theme_config: {},
settings: {},
},
errors: {},
// Available languages
availableLanguages: [
{ code: 'fr', name: 'French' },
{ code: 'de', name: 'German' },
{ code: 'en', name: 'English' },
{ code: 'lb', name: 'Luxembourgish' },
],
// Lifecycle
async init() {
platformEditLog.info('=== PLATFORM EDIT PAGE INITIALIZING ===');
// Duplicate initialization guard
if (window._platformEditInitialized) {
platformEditLog.warn('Platform edit page already initialized, skipping...');
return;
}
window._platformEditInitialized = true;
try {
// Extract platform code from URL
const path = window.location.pathname;
const match = path.match(/\/admin\/platforms\/([^\/]+)\/edit/);
if (match) {
this.platformCode = match[1];
platformEditLog.info('Editing platform:', this.platformCode);
await this.loadPlatform();
} else {
platformEditLog.error('No platform code in URL');
this.error = 'Platform code not found in URL';
this.loading = false;
}
platformEditLog.info('=== PLATFORM EDIT PAGE INITIALIZATION COMPLETE ===');
} catch (error) {
window.LogConfig.logError(error, 'Platform Edit Init');
this.error = 'Failed to initialize page';
this.loading = false;
}
},
// API Methods
async loadPlatform() {
this.loading = true;
this.error = null;
try {
const response = await apiClient.get(`/admin/platforms/${this.platformCode}`);
this.platform = response;
// Build description_translations with empty strings for all supported languages
const langs = response.supported_languages || ['fr', 'de', 'en'];
const descTranslations = {};
for (const lang of langs) {
descTranslations[lang] = (response.description_translations && response.description_translations[lang]) || '';
}
// Populate form data
this.formData = {
name: response.name || '',
description: response.description || '',
description_translations: descTranslations,
domain: response.domain || '',
path_prefix: response.path_prefix || '',
logo: response.logo || '',
logo_dark: response.logo_dark || '',
favicon: response.favicon || '',
default_language: response.default_language || 'fr',
supported_languages: langs,
is_active: response.is_active ?? true,
is_public: response.is_public ?? true,
theme_config: response.theme_config || {},
settings: response.settings || {},
};
// Set current language tab to default language
this.currentLang = response.default_language || 'fr';
platformEditLog.info(`Loaded platform: ${this.platformCode}`);
} catch (err) {
platformEditLog.error('Error loading platform:', err);
this.error = err.message || 'Failed to load platform';
} finally {
this.loading = false;
}
},
async handleSubmit() {
this.saving = true;
this.error = null;
this.success = null;
this.errors = {};
try {
// Build update payload (only changed fields)
// Sync base description from default language translation
const defaultLang = this.formData.default_language || 'fr';
const baseDesc = this.formData.description_translations[defaultLang] || this.formData.description;
const payload = {
name: this.formData.name,
description: baseDesc || null,
description_translations: this.formData.description_translations,
domain: this.formData.domain || null,
path_prefix: this.formData.path_prefix || null,
logo: this.formData.logo || null,
logo_dark: this.formData.logo_dark || null,
favicon: this.formData.favicon || null,
default_language: this.formData.default_language,
supported_languages: this.formData.supported_languages,
is_active: this.formData.is_active,
is_public: this.formData.is_public,
};
const response = await apiClient.put(
`/admin/platforms/${this.platformCode}`,
payload
);
this.platform = response;
this.success = 'Platform updated successfully';
platformEditLog.info(`Updated platform: ${this.platformCode}`);
// Clear success message after 3 seconds
setTimeout(() => {
this.success = null;
}, 3000);
} catch (err) {
platformEditLog.error('Error updating platform:', err);
this.error = err.message || 'Failed to update platform';
// Handle validation errors
if (err.details) {
this.errors = err.details;
}
} finally {
this.saving = false;
}
},
async toggleActive() {
try {
this.formData.is_active = !this.formData.is_active;
await this.handleSubmit();
} catch (err) {
platformEditLog.error('Error toggling active status:', err);
// Revert on error
this.formData.is_active = !this.formData.is_active;
}
},
async togglePublic() {
try {
this.formData.is_public = !this.formData.is_public;
await this.handleSubmit();
} catch (err) {
platformEditLog.error('Error toggling public status:', err);
// Revert on error
this.formData.is_public = !this.formData.is_public;
}
},
// Helper Methods
isLanguageSupported(code) {
return this.formData.supported_languages.includes(code);
},
toggleLanguage(code) {
const index = this.formData.supported_languages.indexOf(code);
if (index > -1) {
// Don't allow removing the last language
if (this.formData.supported_languages.length > 1) {
this.formData.supported_languages.splice(index, 1);
// Switch tab if the removed language was active
if (this.currentLang === code) {
this.currentLang = this.formData.supported_languages[0];
}
}
} else {
this.formData.supported_languages.push(code);
// Initialize empty translation for new language
if (!this.formData.description_translations[code]) {
this.formData.description_translations[code] = '';
}
}
},
getPlatformIcon(code) {
const icons = {
main: 'home',
oms: 'clipboard-list',
loyalty: 'star',
sitebuilder: 'template',
};
return icons[code] || 'globe-alt';
},
formatDate(dateString) {
if (!dateString) return '—';
const date = new Date(dateString);
return date.toLocaleDateString('fr-LU', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
},
};
}