Some checks failed
- 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>
265 lines
7.9 KiB
JavaScript
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',
|
|
});
|
|
},
|
|
};
|
|
}
|