feat: implement dynamic sidebar with server-side menu translation
- Add server-side translation for menu labels using user's preferred_language - Replace hardcoded sidebar template with dynamic rendering from menu API - Remove hardcoded section/page mappings in favor of menu discovery - Fix locale file structure (move menu keys to top level to avoid double-nesting) - Sidebar now fully driven by /admin/menu-config/render/admin API endpoint Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,33 +22,21 @@ function data() {
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// Sidebar sections persistence
|
||||
// Dynamic - section IDs come from menu discovery API
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
const SIDEBAR_STORAGE_KEY = 'admin_sidebar_sections';
|
||||
|
||||
// Default state: Platform Administration open, others closed
|
||||
const defaultSections = {
|
||||
superAdmin: true, // Super admin section (only visible to super admins)
|
||||
platformAdmin: true,
|
||||
vendorOps: false,
|
||||
marketplace: false,
|
||||
billing: false,
|
||||
contentMgmt: false,
|
||||
devTools: false,
|
||||
platformHealth: false,
|
||||
monitoring: false,
|
||||
settingsSection: false
|
||||
};
|
||||
|
||||
function getSidebarSectionsFromStorage() {
|
||||
try {
|
||||
const stored = window.localStorage.getItem(SIDEBAR_STORAGE_KEY);
|
||||
if (stored) {
|
||||
return { ...defaultSections, ...JSON.parse(stored) };
|
||||
return JSON.parse(stored);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to parse sidebar sections from localStorage:', e);
|
||||
}
|
||||
return { ...defaultSections };
|
||||
// Default: first section open (will be set dynamically)
|
||||
return {};
|
||||
}
|
||||
|
||||
function saveSidebarSectionsToStorage(sections) {
|
||||
@@ -92,43 +80,18 @@ function data() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Map pages to their parent sections
|
||||
const pageSectionMap = {
|
||||
// Super Admin section
|
||||
'admin-users': 'superAdmin',
|
||||
// Platform Administration
|
||||
companies: 'platformAdmin',
|
||||
vendors: 'platformAdmin',
|
||||
messages: 'platformAdmin',
|
||||
// Vendor Operations (Products, Customers, Inventory, Orders, Shipping)
|
||||
'marketplace-products': 'vendorOps',
|
||||
'vendor-products': 'vendorOps',
|
||||
customers: 'vendorOps',
|
||||
inventory: 'vendorOps',
|
||||
orders: 'vendorOps',
|
||||
// Future: shipping will map to 'vendorOps'
|
||||
// Marketplace
|
||||
'marketplace-letzshop': 'marketplace',
|
||||
// Content Management
|
||||
'platform-homepage': 'contentMgmt',
|
||||
'content-pages': 'contentMgmt',
|
||||
'vendor-theme': 'contentMgmt',
|
||||
// Developer Tools
|
||||
components: 'devTools',
|
||||
icons: 'devTools',
|
||||
// Platform Health
|
||||
testing: 'platformHealth',
|
||||
'code-quality': 'platformHealth',
|
||||
// Platform Monitoring
|
||||
imports: 'monitoring',
|
||||
'background-tasks': 'monitoring',
|
||||
logs: 'monitoring',
|
||||
'notifications-settings': 'monitoring',
|
||||
// Platform Settings
|
||||
settings: 'settingsSection',
|
||||
profile: 'settingsSection',
|
||||
'api-keys': 'settingsSection'
|
||||
};
|
||||
// Helper to find section ID for a page from menu data
|
||||
function findSectionForPage(menuData, pageId) {
|
||||
if (!menuData?.sections) return null;
|
||||
for (const section of menuData.sections) {
|
||||
for (const item of section.items || []) {
|
||||
if (item.id === pageId) {
|
||||
return section.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
@@ -191,9 +154,10 @@ function data() {
|
||||
saveSidebarSectionsToStorage(this.openSections);
|
||||
},
|
||||
|
||||
// Auto-expand section containing current page
|
||||
// Auto-expand section containing current page (uses menu API data)
|
||||
expandSectionForCurrentPage() {
|
||||
const section = pageSectionMap[this.currentPage];
|
||||
if (!this.menuData) return;
|
||||
const section = findSectionForPage(this.menuData, this.currentPage);
|
||||
if (section && !this.openSections[section]) {
|
||||
this.openSections[section] = true;
|
||||
saveSidebarSectionsToStorage(this.openSections);
|
||||
@@ -242,12 +206,21 @@ function data() {
|
||||
this.menuData = await apiClient.get('/admin/menu-config/render/admin');
|
||||
// Build a set of visible menu item IDs for quick lookup
|
||||
this.visibleMenuItems = new Set();
|
||||
for (const section of (this.menuData?.sections || [])) {
|
||||
const sections = this.menuData?.sections || [];
|
||||
for (const section of sections) {
|
||||
for (const item of (section.items || [])) {
|
||||
this.visibleMenuItems.add(item.id);
|
||||
}
|
||||
// Initialize openSections for new sections (default: first section open)
|
||||
if (this.openSections[section.id] === undefined) {
|
||||
// Default: first section open, rest closed
|
||||
this.openSections[section.id] = (sections.indexOf(section) === 0);
|
||||
}
|
||||
}
|
||||
console.debug('Menu config loaded:', this.visibleMenuItems.size, 'items');
|
||||
|
||||
// Auto-expand section containing current page
|
||||
this.expandSectionForCurrentPage();
|
||||
} catch (e) {
|
||||
// Silently fail - menu will show all items as fallback
|
||||
console.debug('Menu config not loaded, using defaults:', e?.message || e);
|
||||
|
||||
@@ -57,9 +57,6 @@ function adminMyMenuConfig() {
|
||||
},
|
||||
|
||||
async init() {
|
||||
// Load i18n translations
|
||||
await I18n.loadModule('core');
|
||||
|
||||
// Guard against multiple initialization
|
||||
if (window._adminMyMenuConfigInitialized) {
|
||||
myMenuConfigLog.warn('Already initialized, skipping');
|
||||
@@ -70,6 +67,8 @@ function adminMyMenuConfig() {
|
||||
myMenuConfigLog.info('=== MY MENU CONFIG PAGE INITIALIZING ===');
|
||||
|
||||
try {
|
||||
// Load core translations for confirmations
|
||||
await I18n.loadModule('core');
|
||||
await this.loadMenuConfig();
|
||||
myMenuConfigLog.info('=== MY MENU CONFIG PAGE INITIALIZED ===');
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user