- Add routes for vendor content pages list, create, and edit - Create content-pages.html with tabs for Platform Defaults and My Pages - Create content-page-edit.html for creating/editing pages - Add JavaScript for both list and edit views - Add "Content Pages" link to vendor sidebar under new "Shop" section - Add show_in_legal field to vendor content pages API schemas - Platform Defaults tab shows pages that can be overridden - My Pages tab shows vendor's custom pages and overrides Vendors can now: - View platform default pages and override them with custom content - Create entirely new custom pages for their shop - Manage navigation placement (header, footer, legal) - Publish/unpublish pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
193 lines
6.9 KiB
JavaScript
193 lines
6.9 KiB
JavaScript
// static/vendor/js/content-pages.js
|
|
|
|
// Use centralized logger
|
|
const contentPagesLog = window.LogConfig.loggers.contentPages || window.LogConfig.createLogger('contentPages');
|
|
|
|
// ============================================
|
|
// VENDOR CONTENT PAGES MANAGER
|
|
// ============================================
|
|
function vendorContentPagesManager() {
|
|
return {
|
|
// Inherit base layout functionality from init-alpine.js
|
|
...data(),
|
|
|
|
// Page identifier for sidebar active state
|
|
currentPage: 'content-pages',
|
|
|
|
// State
|
|
loading: false,
|
|
error: null,
|
|
activeTab: 'platform',
|
|
searchQuery: '',
|
|
|
|
// Data
|
|
platformPages: [], // Platform default pages
|
|
customPages: [], // Vendor's own pages (overrides + custom)
|
|
overrideMap: {}, // Map of slug -> page id for quick lookup
|
|
|
|
// Initialize
|
|
async init() {
|
|
contentPagesLog.info('=== VENDOR CONTENT PAGES MANAGER INITIALIZING ===');
|
|
|
|
// Prevent multiple initializations
|
|
if (window._vendorContentPagesInitialized) {
|
|
contentPagesLog.warn('Content pages manager already initialized, skipping...');
|
|
return;
|
|
}
|
|
window._vendorContentPagesInitialized = true;
|
|
|
|
await this.loadPages();
|
|
|
|
contentPagesLog.info('=== VENDOR CONTENT PAGES MANAGER INITIALIZATION COMPLETE ===');
|
|
},
|
|
|
|
// Load all pages
|
|
async loadPages() {
|
|
this.loading = true;
|
|
this.error = null;
|
|
|
|
try {
|
|
contentPagesLog.info('Loading content pages...');
|
|
|
|
// Load platform defaults and vendor pages in parallel
|
|
const [platformResponse, vendorResponse] = await Promise.all([
|
|
apiClient.get('/vendor/content-pages/'),
|
|
apiClient.get('/vendor/content-pages/overrides')
|
|
]);
|
|
|
|
// Platform pages - filter to only show actual platform defaults
|
|
const allPages = platformResponse.data || platformResponse || [];
|
|
this.platformPages = allPages.filter(p => p.is_platform_default);
|
|
|
|
// Vendor's custom pages (includes overrides)
|
|
this.customPages = vendorResponse.data || vendorResponse || [];
|
|
|
|
// Build override map for quick lookups
|
|
this.overrideMap = {};
|
|
this.customPages.forEach(page => {
|
|
if (page.is_vendor_override) {
|
|
this.overrideMap[page.slug] = page.id;
|
|
}
|
|
});
|
|
|
|
contentPagesLog.info(`Loaded ${this.platformPages.length} platform pages, ${this.customPages.length} vendor pages`);
|
|
|
|
} catch (err) {
|
|
contentPagesLog.error('Error loading pages:', err);
|
|
this.error = err.message || 'Failed to load pages';
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
// Check if vendor has overridden a platform page
|
|
hasOverride(slug) {
|
|
return slug in this.overrideMap;
|
|
},
|
|
|
|
// Get override page ID
|
|
getOverrideId(slug) {
|
|
return this.overrideMap[slug];
|
|
},
|
|
|
|
// Create an override for a platform page
|
|
async createOverride(platformPage) {
|
|
contentPagesLog.info('Creating override for:', platformPage.slug);
|
|
|
|
try {
|
|
// Create a new vendor page with the same slug as the platform page
|
|
const payload = {
|
|
slug: platformPage.slug,
|
|
title: platformPage.title,
|
|
content: platformPage.content,
|
|
content_format: platformPage.content_format || 'html',
|
|
meta_description: platformPage.meta_description,
|
|
meta_keywords: platformPage.meta_keywords,
|
|
is_published: true,
|
|
show_in_header: platformPage.show_in_header,
|
|
show_in_footer: platformPage.show_in_footer,
|
|
display_order: platformPage.display_order
|
|
};
|
|
|
|
const response = await apiClient.post('/vendor/content-pages/', payload);
|
|
const newPage = response.data || response;
|
|
|
|
contentPagesLog.info('Override created:', newPage.id);
|
|
|
|
// Redirect to edit the new page
|
|
window.location.href = `/vendor/${this.vendorCode}/content-pages/${newPage.id}/edit`;
|
|
|
|
} catch (err) {
|
|
contentPagesLog.error('Error creating override:', err);
|
|
this.error = err.message || 'Failed to create override';
|
|
}
|
|
},
|
|
|
|
// Delete a page
|
|
async deletePage(page) {
|
|
const message = page.is_vendor_override
|
|
? `Are you sure you want to delete your override for "${page.title}"? The platform default will be shown instead.`
|
|
: `Are you sure you want to delete "${page.title}"? This cannot be undone.`;
|
|
|
|
if (!confirm(message)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
contentPagesLog.info('Deleting page:', page.id);
|
|
|
|
await apiClient.delete(`/vendor/content-pages/${page.id}`);
|
|
|
|
// Remove from local state
|
|
this.customPages = this.customPages.filter(p => p.id !== page.id);
|
|
|
|
// Update override map
|
|
if (page.is_vendor_override) {
|
|
delete this.overrideMap[page.slug];
|
|
}
|
|
|
|
contentPagesLog.info('Page deleted successfully');
|
|
|
|
} catch (err) {
|
|
contentPagesLog.error('Error deleting page:', err);
|
|
this.error = err.message || 'Failed to delete page';
|
|
}
|
|
},
|
|
|
|
// Filtered platform pages based on search
|
|
get filteredPlatformPages() {
|
|
if (!this.searchQuery) {
|
|
return this.platformPages;
|
|
}
|
|
const query = this.searchQuery.toLowerCase();
|
|
return this.platformPages.filter(page =>
|
|
page.title.toLowerCase().includes(query) ||
|
|
page.slug.toLowerCase().includes(query)
|
|
);
|
|
},
|
|
|
|
// Filtered custom pages based on search
|
|
get filteredCustomPages() {
|
|
if (!this.searchQuery) {
|
|
return this.customPages;
|
|
}
|
|
const query = this.searchQuery.toLowerCase();
|
|
return this.customPages.filter(page =>
|
|
page.title.toLowerCase().includes(query) ||
|
|
page.slug.toLowerCase().includes(query)
|
|
);
|
|
},
|
|
|
|
// Format date for display
|
|
formatDate(dateStr) {
|
|
if (!dateStr) return '—';
|
|
const date = new Date(dateStr);
|
|
return date.toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: 'short',
|
|
year: 'numeric'
|
|
});
|
|
}
|
|
};
|
|
}
|