feat: add module info and configuration pages to admin panel

Add dedicated pages for viewing module details and configuring module
settings. Includes routes, templates, and JS components for module
info page showing features, menu items, dependencies, and self-contained
module paths. Also adds View/Configure navigation buttons to the platform
modules list.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-26 22:55:12 +01:00
parent ec4ec045fc
commit 33072057c2
5 changed files with 539 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
// static/admin/js/module-info.js
// Module info/detail page management
const moduleInfoLog = window.LogConfig?.loggers?.moduleInfo || window.LogConfig?.createLogger?.('moduleInfo') || console;
function adminModuleInfo(platformCode, moduleCode) {
return {
// Inherit base layout functionality from init-alpine.js
...data(),
// Page-specific state
currentPage: 'platforms',
platformCode: platformCode,
moduleCode: moduleCode,
loading: true,
error: null,
successMessage: null,
saving: false,
// Data
platform: null,
module: null,
// Module icons mapping (must match icons.js definitions)
getModuleIcon(code) {
const icons = {
'core': 'home',
'platform-admin': 'office-building',
'billing': 'credit-card',
'inventory': 'archive',
'orders': 'shopping-cart',
'marketplace': 'shopping-bag',
'customers': 'users',
'cms': 'document-text',
'analytics': 'chart-bar',
'messaging': 'chat',
'dev-tools': 'code',
'monitoring': 'chart-pie'
};
return icons[code] || 'puzzle-piece';
},
// Modules with configuration options
hasConfig(code) {
return ['billing', 'inventory', 'orders', 'marketplace',
'customers', 'cms', 'analytics', 'messaging', 'monitoring'].includes(code);
},
async init() {
// Guard against duplicate initialization
if (window._moduleInfoInitialized) {
moduleInfoLog.warn('Already initialized, skipping');
return;
}
window._moduleInfoInitialized = true;
moduleInfoLog.info('=== MODULE INFO PAGE INITIALIZING ===');
moduleInfoLog.info('Platform code:', this.platformCode);
moduleInfoLog.info('Module code:', this.moduleCode);
try {
await this.loadPlatform();
await this.loadModuleInfo();
moduleInfoLog.info('=== MODULE INFO PAGE INITIALIZED ===');
} catch (error) {
moduleInfoLog.error('Failed to initialize module info page:', error);
this.error = 'Failed to load page data. Please refresh.';
}
},
async refresh() {
this.error = null;
this.successMessage = null;
await this.loadModuleInfo();
},
async loadPlatform() {
try {
this.platform = await apiClient.get(`/admin/platforms/${this.platformCode}`);
moduleInfoLog.info('Loaded platform:', this.platform?.name);
} catch (error) {
moduleInfoLog.error('Failed to load platform:', error);
throw error;
}
},
async loadModuleInfo() {
this.loading = true;
this.error = null;
try {
const platformId = this.platform?.id;
if (!platformId) {
throw new Error('Platform not loaded');
}
// Get all modules and find the specific one
const moduleConfig = await apiClient.get(`/admin/modules/platforms/${platformId}`);
this.module = moduleConfig.modules?.find(m => m.code === this.moduleCode);
if (!this.module) {
throw new Error(`Module '${this.moduleCode}' not found`);
}
moduleInfoLog.info('Loaded module info:', {
code: this.module.code,
name: this.module.name,
is_enabled: this.module.is_enabled,
is_core: this.module.is_core
});
} catch (error) {
moduleInfoLog.error('Failed to load module info:', error);
this.error = error.message || 'Failed to load module information';
} finally {
this.loading = false;
}
},
async toggleModule() {
if (this.module?.is_core) {
moduleInfoLog.warn('Cannot toggle core module:', this.moduleCode);
return;
}
this.saving = true;
this.error = null;
this.successMessage = null;
const action = this.module.is_enabled ? 'disable' : 'enable';
try {
const platformId = this.platform?.id;
const endpoint = `/admin/modules/platforms/${platformId}/${action}`;
const result = await apiClient.post(endpoint, {
module_code: this.moduleCode
});
moduleInfoLog.info(`${action}d module:`, this.moduleCode, result);
// Show success message
if (result.also_enabled?.length > 0) {
this.successMessage = `Module '${this.module.name}' enabled. Also enabled dependencies: ${result.also_enabled.join(', ')}`;
} else if (result.also_disabled?.length > 0) {
this.successMessage = `Module '${this.module.name}' disabled. Also disabled dependents: ${result.also_disabled.join(', ')}`;
} else {
this.successMessage = `Module '${this.module.name}' ${action}d successfully`;
}
// Reload module info to get updated state
await this.loadModuleInfo();
// Clear success message after delay
setTimeout(() => {
this.successMessage = null;
}, 5000);
} catch (error) {
moduleInfoLog.error(`Failed to ${action} module:`, error);
this.error = error.message || `Failed to ${action} module`;
} finally {
this.saving = false;
}
}
};
}

View File

@@ -58,6 +58,12 @@ function adminPlatformModules(platformCode) {
return icons[moduleCode] || 'puzzle-piece';
},
// Modules with configuration options
hasConfig(moduleCode) {
return ['billing', 'inventory', 'orders', 'marketplace',
'customers', 'cms', 'analytics', 'messaging', 'monitoring'].includes(moduleCode);
},
async init() {
// Guard against duplicate initialization
if (window._platformModulesInitialized) {