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:
139
static/admin/js/platform-detail.js
Normal file
139
static/admin/js/platform-detail.js
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Platform Detail - Alpine.js Component
|
||||
*
|
||||
* Displays platform overview, stats, and quick actions.
|
||||
*/
|
||||
|
||||
const platformDetailLog = window.LogConfig.createLogger('PLATFORM_DETAIL');
|
||||
|
||||
function platformDetail() {
|
||||
return {
|
||||
// Inherit base layout functionality from init-alpine.js
|
||||
...data(),
|
||||
|
||||
// Page identification
|
||||
currentPage: 'platform-detail',
|
||||
|
||||
// State
|
||||
platform: null,
|
||||
stats: null,
|
||||
recentPages: [],
|
||||
loading: true,
|
||||
error: null,
|
||||
platformCode: null,
|
||||
|
||||
// Lifecycle
|
||||
async init() {
|
||||
platformDetailLog.info('=== PLATFORM DETAIL PAGE INITIALIZING ===');
|
||||
|
||||
// Duplicate initialization guard
|
||||
if (window._platformDetailInitialized) {
|
||||
platformDetailLog.warn('Platform detail page already initialized, skipping...');
|
||||
return;
|
||||
}
|
||||
window._platformDetailInitialized = true;
|
||||
|
||||
try {
|
||||
// Extract platform code from URL
|
||||
const path = window.location.pathname;
|
||||
const match = path.match(/\/admin\/platforms\/([^\/]+)$/);
|
||||
|
||||
if (match) {
|
||||
this.platformCode = match[1];
|
||||
platformDetailLog.info('Viewing platform:', this.platformCode);
|
||||
await Promise.all([
|
||||
this.loadPlatform(),
|
||||
this.loadRecentPages(),
|
||||
]);
|
||||
} else {
|
||||
platformDetailLog.error('No platform code in URL');
|
||||
this.error = 'Platform code not found in URL';
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
platformDetailLog.info('=== PLATFORM DETAIL PAGE INITIALIZATION COMPLETE ===');
|
||||
} catch (error) {
|
||||
window.LogConfig.logError(error, 'Platform Detail Init');
|
||||
this.error = 'Failed to initialize page';
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// API Methods
|
||||
async loadPlatform() {
|
||||
try {
|
||||
const response = await apiClient.get(`/admin/platforms/${this.platformCode}`);
|
||||
this.platform = response;
|
||||
platformDetailLog.info(`Loaded platform: ${this.platformCode}`);
|
||||
} catch (err) {
|
||||
platformDetailLog.error('Error loading platform:', err);
|
||||
this.error = err.message || 'Failed to load platform';
|
||||
throw err;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadRecentPages() {
|
||||
try {
|
||||
// Load recent content pages for this platform
|
||||
const response = await apiClient.get(`/admin/content-pages?platform_code=${this.platformCode}&limit=5`);
|
||||
this.recentPages = response.items || response || [];
|
||||
platformDetailLog.info(`Loaded ${this.recentPages.length} recent pages`);
|
||||
} catch (err) {
|
||||
platformDetailLog.error('Error loading recent pages:', err);
|
||||
// Non-fatal - don't throw
|
||||
this.recentPages = [];
|
||||
}
|
||||
},
|
||||
|
||||
// Helper Methods
|
||||
getPlatformIcon(code) {
|
||||
const icons = {
|
||||
main: 'home',
|
||||
oms: 'clipboard-list',
|
||||
loyalty: 'star',
|
||||
sitebuilder: 'template',
|
||||
};
|
||||
return icons[code] || 'globe-alt';
|
||||
},
|
||||
|
||||
getPageTypeLabel(page) {
|
||||
if (page.is_platform_page) return 'Marketing';
|
||||
if (page.vendor_id) return 'Vendor Override';
|
||||
return 'Vendor Default';
|
||||
},
|
||||
|
||||
getPageTypeBadgeClass(page) {
|
||||
if (page.is_platform_page) {
|
||||
return 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200';
|
||||
}
|
||||
if (page.vendor_id) {
|
||||
return 'bg-teal-100 text-teal-800 dark:bg-teal-900 dark:text-teal-200';
|
||||
}
|
||||
return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200';
|
||||
},
|
||||
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '—';
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('fr-LU', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
});
|
||||
},
|
||||
|
||||
getPlatformUrl() {
|
||||
if (!this.platform) return '#';
|
||||
if (this.platform.domain) {
|
||||
return `https://${this.platform.domain}`;
|
||||
}
|
||||
// Development URL
|
||||
if (this.platform.code === 'main') {
|
||||
return '/';
|
||||
}
|
||||
return `/platforms/${this.platform.code}/`;
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user