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>
275 lines
17 KiB
HTML
275 lines
17 KiB
HTML
{# 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 %}
|
|
<!-- Header with Back Button -->
|
|
<div class="flex items-center justify-between my-6">
|
|
<div class="flex items-center">
|
|
<a :href="`/admin/platforms/${platformCode}/modules`" class="mr-4 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
|
<span x-html="$icon('arrow-left', 'w-5 h-5 text-gray-600 dark:text-gray-400')"></span>
|
|
</a>
|
|
<div>
|
|
<div class="flex items-center">
|
|
<span x-html="$icon(getModuleIcon(moduleCode), 'w-8 h-8 text-purple-600 dark:text-purple-400 mr-3')"></span>
|
|
<h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200" x-text="module?.name || 'Loading...'"></h2>
|
|
<code class="ml-3 text-sm bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded" x-text="moduleCode"></code>
|
|
</div>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1" x-text="module?.description || ''"></p>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<!-- Configure Button -->
|
|
<a x-show="hasConfig(moduleCode)"
|
|
:href="`/admin/platforms/${platformCode}/modules/${moduleCode}/config`"
|
|
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 focus:outline-none focus:ring focus:ring-purple-400">
|
|
<span x-html="$icon('cog', 'w-4 h-4 mr-2')"></span>
|
|
Configure
|
|
</a>
|
|
<!-- Status Badge & Toggle -->
|
|
<div class="flex items-center gap-3">
|
|
<span x-show="module?.is_enabled"
|
|
class="px-3 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
|
|
Enabled
|
|
</span>
|
|
<span x-show="!module?.is_enabled && !module?.is_core"
|
|
class="px-3 py-1 text-xs font-semibold rounded-full bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400">
|
|
Disabled
|
|
</span>
|
|
<span x-show="module?.is_core"
|
|
class="px-3 py-1 text-xs font-semibold rounded-full bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
|
|
Core
|
|
</span>
|
|
<!-- Toggle (only for non-core modules) -->
|
|
<button
|
|
x-show="!module?.is_core"
|
|
@click="toggleModule()"
|
|
:disabled="saving"
|
|
:class="{
|
|
'bg-purple-600': module?.is_enabled,
|
|
'bg-gray-200 dark:bg-gray-600': !module?.is_enabled
|
|
}"
|
|
class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2"
|
|
role="switch"
|
|
:aria-checked="module?.is_enabled"
|
|
>
|
|
<span
|
|
:class="{
|
|
'translate-x-5': module?.is_enabled,
|
|
'translate-x-0': !module?.is_enabled
|
|
}"
|
|
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
|
|
></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{ alert_dynamic(type='success', title='Success', message_var='successMessage', show_condition='successMessage') }}
|
|
{{ error_state('Error', show_condition='error') }}
|
|
{{ loading_state('Loading module details...') }}
|
|
|
|
<!-- Main Content -->
|
|
<div x-show="!loading && module" class="space-y-6">
|
|
|
|
<!-- Description Card -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-3">Description</h3>
|
|
<p class="text-gray-600 dark:text-gray-400" x-text="module?.description || 'No description available.'"></p>
|
|
</div>
|
|
|
|
<!-- Features -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Features</h3>
|
|
<div x-show="module?.features?.length > 0" class="flex flex-wrap gap-2">
|
|
<template x-for="feature in module?.features" :key="feature">
|
|
<span class="px-3 py-1 text-sm rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300" x-text="feature"></span>
|
|
</template>
|
|
</div>
|
|
<p x-show="!module?.features?.length" class="text-gray-500 dark:text-gray-400 text-sm">No features defined for this module.</p>
|
|
</div>
|
|
|
|
<!-- Menu Items -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- Admin Menu Items -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
<span x-html="$icon('office-building', 'w-5 h-5 inline mr-2 text-purple-600 dark:text-purple-400')"></span>
|
|
Admin Menu Items
|
|
</h3>
|
|
<div x-show="module?.admin_menu_items?.length > 0" class="space-y-2">
|
|
<template x-for="item in module?.admin_menu_items" :key="item">
|
|
<div class="flex items-center p-2 bg-gray-50 dark:bg-gray-700/50 rounded">
|
|
<span x-html="$icon('menu-alt-2', 'w-4 h-4 text-gray-500 mr-2')"></span>
|
|
<span class="text-sm text-gray-700 dark:text-gray-300" x-text="item"></span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<p x-show="!module?.admin_menu_items?.length" class="text-gray-500 dark:text-gray-400 text-sm">No admin menu items.</p>
|
|
</div>
|
|
|
|
<!-- Vendor Menu Items -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
<span x-html="$icon('building-storefront', 'w-5 h-5 inline mr-2 text-teal-600 dark:text-teal-400')"></span>
|
|
Vendor Menu Items
|
|
</h3>
|
|
<div x-show="module?.vendor_menu_items?.length > 0" class="space-y-2">
|
|
<template x-for="item in module?.vendor_menu_items" :key="item">
|
|
<div class="flex items-center p-2 bg-gray-50 dark:bg-gray-700/50 rounded">
|
|
<span x-html="$icon('menu-alt-2', 'w-4 h-4 text-gray-500 mr-2')"></span>
|
|
<span class="text-sm text-gray-700 dark:text-gray-300" x-text="item"></span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<p x-show="!module?.vendor_menu_items?.length" class="text-gray-500 dark:text-gray-400 text-sm">No vendor menu items.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dependencies -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- Requires (Dependencies) -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
<span x-html="$icon('link', 'w-5 h-5 inline mr-2 text-amber-600 dark:text-amber-400')"></span>
|
|
Dependencies
|
|
<span class="text-xs font-normal text-gray-500 dark:text-gray-400 ml-2">(requires)</span>
|
|
</h3>
|
|
<div x-show="module?.requires?.length > 0" class="flex flex-wrap gap-2">
|
|
<template x-for="dep in module?.requires" :key="dep">
|
|
<a :href="`/admin/platforms/${platformCode}/modules/${dep}`"
|
|
class="px-3 py-1 text-sm rounded-full bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300 hover:bg-amber-200 dark:hover:bg-amber-900/50 transition-colors"
|
|
x-text="dep"></a>
|
|
</template>
|
|
</div>
|
|
<p x-show="!module?.requires?.length" class="text-gray-500 dark:text-gray-400 text-sm">No dependencies.</p>
|
|
</div>
|
|
|
|
<!-- Dependents (Required By) -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
<span x-html="$icon('users', 'w-5 h-5 inline mr-2 text-blue-600 dark:text-blue-400')"></span>
|
|
Dependents
|
|
<span class="text-xs font-normal text-gray-500 dark:text-gray-400 ml-2">(required by)</span>
|
|
</h3>
|
|
<div x-show="module?.dependent_modules?.length > 0" class="flex flex-wrap gap-2">
|
|
<template x-for="dep in module?.dependent_modules" :key="dep">
|
|
<a :href="`/admin/platforms/${platformCode}/modules/${dep}`"
|
|
class="px-3 py-1 text-sm rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300 hover:bg-blue-200 dark:hover:bg-blue-900/50 transition-colors"
|
|
x-text="dep"></a>
|
|
</template>
|
|
</div>
|
|
<p x-show="!module?.dependent_modules?.length" class="text-gray-500 dark:text-gray-400 text-sm">No modules depend on this one.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Self-Contained Module Info (if applicable) -->
|
|
<div x-show="module?.is_self_contained" class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
<span x-html="$icon('puzzle', 'w-5 h-5 inline mr-2 text-green-600 dark:text-green-400')"></span>
|
|
Self-Contained Module
|
|
</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<!-- Services -->
|
|
<div class="flex items-center p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
<span x-show="module?.self_contained_info?.services_path" x-html="$icon('check-circle', 'w-5 h-5 text-green-500 mr-3')"></span>
|
|
<span x-show="!module?.self_contained_info?.services_path" x-html="$icon('x-circle', 'w-5 h-5 text-gray-400 mr-3')"></span>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Services</p>
|
|
<code x-show="module?.self_contained_info?.services_path" class="text-xs text-gray-500 dark:text-gray-400" x-text="module?.self_contained_info?.services_path"></code>
|
|
<p x-show="!module?.self_contained_info?.services_path" class="text-xs text-gray-400">Not defined</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Models -->
|
|
<div class="flex items-center p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
<span x-show="module?.self_contained_info?.models_path" x-html="$icon('check-circle', 'w-5 h-5 text-green-500 mr-3')"></span>
|
|
<span x-show="!module?.self_contained_info?.models_path" x-html="$icon('x-circle', 'w-5 h-5 text-gray-400 mr-3')"></span>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Models</p>
|
|
<code x-show="module?.self_contained_info?.models_path" class="text-xs text-gray-500 dark:text-gray-400" x-text="module?.self_contained_info?.models_path"></code>
|
|
<p x-show="!module?.self_contained_info?.models_path" class="text-xs text-gray-400">Not defined</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Templates -->
|
|
<div class="flex items-center p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
<span x-show="module?.self_contained_info?.templates_path" x-html="$icon('check-circle', 'w-5 h-5 text-green-500 mr-3')"></span>
|
|
<span x-show="!module?.self_contained_info?.templates_path" x-html="$icon('x-circle', 'w-5 h-5 text-gray-400 mr-3')"></span>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Templates</p>
|
|
<code x-show="module?.self_contained_info?.templates_path" class="text-xs text-gray-500 dark:text-gray-400" x-text="module?.self_contained_info?.templates_path"></code>
|
|
<p x-show="!module?.self_contained_info?.templates_path" class="text-xs text-gray-400">Not defined</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Locales -->
|
|
<div class="flex items-center p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
<span x-show="module?.self_contained_info?.locales_path" x-html="$icon('check-circle', 'w-5 h-5 text-green-500 mr-3')"></span>
|
|
<span x-show="!module?.self_contained_info?.locales_path" x-html="$icon('x-circle', 'w-5 h-5 text-gray-400 mr-3')"></span>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Locales</p>
|
|
<code x-show="module?.self_contained_info?.locales_path" class="text-xs text-gray-500 dark:text-gray-400" x-text="module?.self_contained_info?.locales_path"></code>
|
|
<p x-show="!module?.self_contained_info?.locales_path" class="text-xs text-gray-400">Not defined</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Static Files -->
|
|
<div class="flex items-center p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
<span x-show="module?.self_contained_info?.static_path" x-html="$icon('check-circle', 'w-5 h-5 text-green-500 mr-3')"></span>
|
|
<span x-show="!module?.self_contained_info?.static_path" x-html="$icon('x-circle', 'w-5 h-5 text-gray-400 mr-3')"></span>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">Static Files</p>
|
|
<code x-show="module?.self_contained_info?.static_path" class="text-xs text-gray-500 dark:text-gray-400" x-text="module?.self_contained_info?.static_path"></code>
|
|
<p x-show="!module?.self_contained_info?.static_path" class="text-xs text-gray-400">Not defined</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- API Routes -->
|
|
<div class="flex items-center p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
<span x-show="module?.self_contained_info?.api_path" x-html="$icon('check-circle', 'w-5 h-5 text-green-500 mr-3')"></span>
|
|
<span x-show="!module?.self_contained_info?.api_path" x-html="$icon('x-circle', 'w-5 h-5 text-gray-400 mr-3')"></span>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">API Routes</p>
|
|
<code x-show="module?.self_contained_info?.api_path" class="text-xs text-gray-500 dark:text-gray-400" x-text="module?.self_contained_info?.api_path"></code>
|
|
<p x-show="!module?.self_contained_info?.api_path" class="text-xs text-gray-400">Not defined</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Module Type Info -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Module Information</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div class="flex justify-between items-center py-2 border-b border-gray-200 dark:border-gray-700">
|
|
<span class="text-gray-600 dark:text-gray-400">Module Type</span>
|
|
<span x-show="module?.is_core" class="px-2 py-1 text-xs font-medium rounded-full bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">Core</span>
|
|
<span x-show="!module?.is_core" class="px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400">Optional</span>
|
|
</div>
|
|
<div class="flex justify-between items-center py-2 border-b border-gray-200 dark:border-gray-700">
|
|
<span class="text-gray-600 dark:text-gray-400">Self-Contained</span>
|
|
<span x-show="module?.is_self_contained" class="px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">Yes</span>
|
|
<span x-show="!module?.is_self_contained" class="px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400">No</span>
|
|
</div>
|
|
<div class="flex justify-between items-center py-2 border-b border-gray-200 dark:border-gray-700">
|
|
<span class="text-gray-600 dark:text-gray-400">Has Configuration</span>
|
|
<span x-show="hasConfig(moduleCode)" class="px-2 py-1 text-xs font-medium rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">Yes</span>
|
|
<span x-show="!hasConfig(moduleCode)" class="px-2 py-1 text-xs font-medium rounded-full bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400">No</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block extra_scripts %}
|
|
<script src="{{ url_for('static', path='admin/js/module-info.js') }}"></script>
|
|
{% endblock %}
|