feat: add platform detail/edit admin UI and service enhancements
- Add platform detail and edit admin pages with templates and JS - Add ContentPageService methods: list_all_platform_pages, list_all_vendor_defaults - Deprecate /admin/platform-homepage route (redirects to /admin/platforms) - Add migration to fix content_page nullable columns - Refine platform and vendor context middleware - Add platform context middleware unit tests - Update platforms.js with improved functionality - Add section-based homepage plan documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
230
static/admin/js/platform-edit.js
Normal file
230
static/admin/js/platform-edit.js
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* 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,
|
||||
|
||||
// Form data
|
||||
formData: {
|
||||
name: '',
|
||||
description: '',
|
||||
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: 'lu', 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;
|
||||
|
||||
// Populate form data
|
||||
this.formData = {
|
||||
name: response.name || '',
|
||||
description: response.description || '',
|
||||
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: response.supported_languages || ['fr', 'de', 'en'],
|
||||
is_active: response.is_active ?? true,
|
||||
is_public: response.is_public ?? true,
|
||||
theme_config: response.theme_config || {},
|
||||
settings: response.settings || {},
|
||||
};
|
||||
|
||||
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)
|
||||
const payload = {
|
||||
name: this.formData.name,
|
||||
description: this.formData.description || null,
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
this.formData.supported_languages.push(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',
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user