From 33072057c23267ebb4c8207cbb3fdbf803e268d9 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Mon, 26 Jan 2026 22:55:12 +0100 Subject: [PATCH] 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 --- app/routes/admin_pages.py | 63 +++++ app/templates/admin/module-info.html | 274 ++++++++++++++++++++++ app/templates/admin/platform-modules.html | 30 +++ static/admin/js/module-info.js | 166 +++++++++++++ static/admin/js/platform-modules.js | 6 + 5 files changed, 539 insertions(+) create mode 100644 app/templates/admin/module-info.html create mode 100644 static/admin/js/module-info.js diff --git a/app/routes/admin_pages.py b/app/routes/admin_pages.py index 01625a15..d3486bb8 100644 --- a/app/routes/admin_pages.py +++ b/app/routes/admin_pages.py @@ -1274,6 +1274,69 @@ async def admin_platform_modules( ) +@router.get( + "/platforms/{platform_code}/modules/{module_code}", + response_class=HTMLResponse, + include_in_schema=False, +) +async def admin_module_info( + request: Request, + platform_code: str = Path(..., description="Platform code"), + module_code: str = Path(..., description="Module code"), + current_user: User = Depends(require_menu_access("platforms", FrontendType.ADMIN)), + db: Session = Depends(get_db), +): + """ + Render module info/detail page. + Shows module details including features, menu items, dependencies, + and self-contained module information. + """ + # Only super admins can access module details + if not current_user.is_super_admin: + return RedirectResponse(url=f"/admin/platforms/{platform_code}", status_code=302) + + return templates.TemplateResponse( + "admin/module-info.html", + { + "request": request, + "user": current_user, + "platform_code": platform_code, + "module_code": module_code, + }, + ) + + +@router.get( + "/platforms/{platform_code}/modules/{module_code}/config", + response_class=HTMLResponse, + include_in_schema=False, +) +async def admin_module_config( + request: Request, + platform_code: str = Path(..., description="Platform code"), + module_code: str = Path(..., description="Module code"), + current_user: User = Depends(require_menu_access("platforms", FrontendType.ADMIN)), + db: Session = Depends(get_db), +): + """ + Render module configuration page. + Allows configuring module-specific settings. + """ + # Only super admins can access module configuration + if not current_user.is_super_admin: + return RedirectResponse(url=f"/admin/platforms/{platform_code}", status_code=302) + + return templates.TemplateResponse( + "admin/module-config.html", + { + "request": request, + "user": current_user, + "platform_code": platform_code, + "module_code": module_code, + }, + ) + + # ============================================================================ # CONTENT MANAGEMENT SYSTEM (CMS) ROUTES # ============================================================================ diff --git a/app/templates/admin/module-info.html b/app/templates/admin/module-info.html new file mode 100644 index 00000000..0a2af979 --- /dev/null +++ b/app/templates/admin/module-info.html @@ -0,0 +1,274 @@ +{# app/templates/admin/module-info.html #} +{% extends "admin/base.html" %} +{% from 'shared/macros/alerts.html' import alert_dynamic, error_state, loading_state %} +{% from 'shared/macros/headers.html' import page_header %} + +{% block title %}Module Details{% endblock %} + +{% block alpine_data %}adminModuleInfo('{{ platform_code }}', '{{ module_code }}'){% endblock %} + +{% block content %} + +
+
+ + + +
+
+ +

+ +
+

+
+
+
+ + + + Configure + + +
+ + Enabled + + + Disabled + + + Core + + + +
+
+
+ +{{ alert_dynamic(type='success', title='Success', message_var='successMessage', show_condition='successMessage') }} +{{ error_state('Error', show_condition='error') }} +{{ loading_state('Loading module details...') }} + + +
+ + +
+

Description

+

+
+ + +
+

Features

+
+ +
+

No features defined for this module.

+
+ + +
+ +
+

+ + Admin Menu Items +

+
+ +
+

No admin menu items.

+
+ + +
+

+ + Vendor Menu Items +

+
+ +
+

No vendor menu items.

+
+
+ + +
+ +
+

+ + Dependencies + (requires) +

+
+ +
+

No dependencies.

+
+ + +
+

+ + Dependents + (required by) +

+
+ +
+

No modules depend on this one.

+
+
+ + +
+

+ + Self-Contained Module +

+
+ +
+ + +
+

Services

+ +

Not defined

+
+
+ + +
+ + +
+

Models

+ +

Not defined

+
+
+ + +
+ + +
+

Templates

+ +

Not defined

+
+
+ + +
+ + +
+

Locales

+ +

Not defined

+
+
+ + +
+ + +
+

Static Files

+ +

Not defined

+
+
+ + +
+ + +
+

API Routes

+ +

Not defined

+
+
+
+
+ + +
+

Module Information

+
+
+ Module Type + Core + Optional +
+
+ Self-Contained + Yes + No +
+
+ Has Configuration + Yes + No +
+
+
+ +
+ +{% endblock %} + +{% block extra_scripts %} + +{% endblock %} diff --git a/app/templates/admin/platform-modules.html b/app/templates/admin/platform-modules.html index bc7968fb..cd52707d 100644 --- a/app/templates/admin/platform-modules.html +++ b/app/templates/admin/platform-modules.html @@ -136,6 +136,21 @@
+ + + + + + + + + + Enabled @@ -202,6 +217,21 @@
+ + + + + + + + + + diff --git a/static/admin/js/module-info.js b/static/admin/js/module-info.js new file mode 100644 index 00000000..6512321e --- /dev/null +++ b/static/admin/js/module-info.js @@ -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; + } + } + }; +} diff --git a/static/admin/js/platform-modules.js b/static/admin/js/platform-modules.js index 511e1d4c..999db5db 100644 --- a/static/admin/js/platform-modules.js +++ b/static/admin/js/platform-modules.js @@ -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) {