Files
orion/app/templates/admin/platform-modules.html
Samir Boulahtit 33072057c2 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>
2026-01-26 22:55:12 +01:00

283 lines
16 KiB
HTML

{# app/templates/admin/platform-modules.html #}
{% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import alert_dynamic, error_state %}
{% from 'shared/macros/headers.html' import page_header %}
{% block title %}Module Configuration{% endblock %}
{% block alpine_data %}adminPlatformModules('{{ platform_code }}'){% endblock %}
{% block content %}
{{ page_header('Module Configuration', back_url='/admin/platforms/' + platform_code) }}
{{ alert_dynamic(type='success', title='Success', message_var='successMessage', show_condition='successMessage') }}
{{ error_state('Error', show_condition='error') }}
<!-- Platform Info -->
<div class="mb-6 p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="flex items-center justify-between">
<div>
<h2 class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="platform?.name || 'Loading...'"></h2>
<p class="text-sm text-gray-500 dark:text-gray-400">
Enable or disable feature modules for this platform. Core modules cannot be disabled.
</p>
</div>
<span class="px-3 py-1 text-sm font-medium rounded-full bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200" x-text="platform?.code?.toUpperCase()"></span>
</div>
</div>
<!-- Stats Cards -->
<div class="grid gap-4 mb-6 md:grid-cols-4">
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="p-3 mr-4 text-blue-500 bg-blue-100 rounded-full dark:text-blue-100 dark:bg-blue-500">
<span x-html="$icon('puzzle', 'w-5 h-5')"></span>
</div>
<div>
<p class="mb-1 text-sm font-medium text-gray-600 dark:text-gray-400">Total Modules</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="moduleConfig?.total || 0"></p>
</div>
</div>
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="p-3 mr-4 text-green-500 bg-green-100 rounded-full dark:text-green-100 dark:bg-green-500">
<span x-html="$icon('check-circle', 'w-5 h-5')"></span>
</div>
<div>
<p class="mb-1 text-sm font-medium text-gray-600 dark:text-gray-400">Enabled</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="moduleConfig?.enabled || 0"></p>
</div>
</div>
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="p-3 mr-4 text-gray-500 bg-gray-100 rounded-full dark:text-gray-100 dark:bg-gray-600">
<span x-html="$icon('x-circle', 'w-5 h-5')"></span>
</div>
<div>
<p class="mb-1 text-sm font-medium text-gray-600 dark:text-gray-400">Disabled</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="moduleConfig?.disabled || 0"></p>
</div>
</div>
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="p-3 mr-4 text-purple-500 bg-purple-100 rounded-full dark:text-purple-100 dark:bg-purple-500">
<span x-html="$icon('shield', 'w-5 h-5')"></span>
</div>
<div>
<p class="mb-1 text-sm font-medium text-gray-600 dark:text-gray-400">Core Modules</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="coreModulesCount"></p>
</div>
</div>
</div>
<!-- Actions -->
<div class="mb-4 flex items-center justify-between">
<p class="text-sm text-gray-500 dark:text-gray-400">
Toggle modules on/off. Dependencies are resolved automatically.
</p>
<div class="flex gap-2">
<button
@click="enableAll()"
:disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
>
<span x-html="$icon('check-circle', 'w-4 h-4 mr-2')"></span>
Enable All
</button>
<button
@click="disableOptional()"
:disabled="saving"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600"
>
<span x-html="$icon('x-circle', 'w-4 h-4 mr-2')"></span>
Core Only
</button>
</div>
</div>
<!-- Loading State -->
<div x-show="loading" class="flex items-center justify-center py-12 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<span x-html="$icon('refresh', 'w-8 h-8 animate-spin text-purple-600')"></span>
<span class="ml-3 text-gray-500 dark:text-gray-400">Loading module configuration...</span>
</div>
<!-- Module Groups -->
<div x-show="!loading" class="space-y-6">
<!-- Core Modules -->
<div class="bg-white rounded-lg shadow-xs dark:bg-gray-800 overflow-hidden">
<div class="px-4 py-3 bg-purple-50 dark:bg-purple-900/20 border-b border-purple-200 dark:border-purple-800">
<div class="flex items-center justify-between">
<div class="flex items-center">
<span x-html="$icon('shield', 'w-5 h-5 text-purple-600 dark:text-purple-400 mr-2')"></span>
<h3 class="text-sm font-semibold text-purple-800 dark:text-purple-200">Core Modules</h3>
<span class="ml-2 px-2 py-0.5 text-xs font-medium rounded bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
Always Enabled
</span>
</div>
<span class="text-xs text-purple-600 dark:text-purple-400" x-text="`${coreModulesCount} modules`"></span>
</div>
</div>
<div class="divide-y divide-gray-100 dark:divide-gray-700">
<template x-for="module in coreModules" :key="module.code">
<div class="flex items-center justify-between px-4 py-4 hover:bg-gray-50 dark:hover:bg-gray-700/30">
<div class="flex items-center flex-1">
<div class="p-2 mr-3 bg-purple-100 dark:bg-purple-900/30 rounded-lg">
<span x-html="$icon(getModuleIcon(module.code), 'w-5 h-5 text-purple-600 dark:text-purple-400')"></span>
</div>
<div class="flex-1">
<p class="text-sm font-medium text-gray-700 dark:text-gray-200" x-text="module.name"></p>
<p class="text-xs text-gray-500 dark:text-gray-400" x-text="module.description"></p>
<!-- Features -->
<div x-show="module.features?.length > 0" class="mt-1 flex flex-wrap gap-1">
<template x-for="feature in module.features.slice(0, 3)" :key="feature">
<span class="px-1.5 py-0.5 text-xs rounded bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400" x-text="feature"></span>
</template>
<span x-show="module.features?.length > 3" class="text-xs text-gray-400" x-text="`+${module.features.length - 3} more`"></span>
</div>
</div>
</div>
<div class="flex items-center gap-3 ml-4">
<!-- View Details Button -->
<a :href="`/admin/platforms/${platformCode}/modules/${module.code}`"
class="p-1.5 rounded-lg text-gray-500 hover:text-purple-600 hover:bg-purple-50 dark:text-gray-400 dark:hover:text-purple-400 dark:hover:bg-purple-900/20 transition-colors"
title="View Details">
<span x-html="$icon('eye', 'w-4 h-4')"></span>
</a>
<!-- Configure Button (if has config) -->
<a x-show="hasConfig(module.code)"
:href="`/admin/platforms/${platformCode}/modules/${module.code}/config`"
class="p-1.5 rounded-lg text-gray-500 hover:text-blue-600 hover:bg-blue-50 dark:text-gray-400 dark:hover:text-blue-400 dark:hover:bg-blue-900/20 transition-colors"
title="Configure">
<span x-html="$icon('cog', 'w-4 h-4')"></span>
</a>
<span 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">
Enabled
</span>
</div>
</div>
</template>
</div>
</div>
<!-- Optional Modules -->
<div class="bg-white rounded-lg shadow-xs dark:bg-gray-800 overflow-hidden">
<div class="px-4 py-3 bg-gray-50 dark:bg-gray-700/50 border-b border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<div class="flex items-center">
<span x-html="$icon('puzzle', 'w-5 h-5 text-gray-600 dark:text-gray-400 mr-2')"></span>
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-200">Optional Modules</h3>
</div>
<span class="text-xs text-gray-500 dark:text-gray-400" x-text="`${enabledOptionalCount}/${optionalModules.length} enabled`"></span>
</div>
</div>
<div class="divide-y divide-gray-100 dark:divide-gray-700">
<template x-for="module in optionalModules" :key="module.code">
<div class="flex items-center justify-between px-4 py-4 hover:bg-gray-50 dark:hover:bg-gray-700/30">
<div class="flex items-center flex-1">
<div class="p-2 mr-3 rounded-lg"
:class="module.is_enabled ? 'bg-green-100 dark:bg-green-900/30' : 'bg-gray-100 dark:bg-gray-700'">
<span x-html="$icon(getModuleIcon(module.code), 'w-5 h-5')"
:class="module.is_enabled ? 'text-green-600 dark:text-green-400' : 'text-gray-400'"></span>
</div>
<div class="flex-1">
<div class="flex items-center gap-2">
<p class="text-sm font-medium text-gray-700 dark:text-gray-200" x-text="module.name"></p>
<!-- Dependencies Badge -->
<template x-if="module.requires?.length > 0">
<span class="px-1.5 py-0.5 text-xs rounded bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"
:title="`Requires: ${module.requires.join(', ')}`">
<span x-html="$icon('link', 'w-3 h-3 inline')"></span>
<span x-text="module.requires.length"></span>
</span>
</template>
<!-- Dependents Badge -->
<template x-if="module.dependent_modules?.length > 0">
<span class="px-1.5 py-0.5 text-xs rounded bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"
:title="`Required by: ${module.dependent_modules.join(', ')}`">
<span x-html="$icon('users', 'w-3 h-3 inline')"></span>
<span x-text="module.dependent_modules.length"></span>
</span>
</template>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400" x-text="module.description"></p>
<!-- Dependencies Info -->
<div x-show="module.requires?.length > 0" class="mt-1">
<span class="text-xs text-amber-600 dark:text-amber-400">
Requires: <span x-text="module.requires.join(', ')"></span>
</span>
</div>
<!-- Features -->
<div x-show="module.features?.length > 0" class="mt-1 flex flex-wrap gap-1">
<template x-for="feature in module.features.slice(0, 3)" :key="feature">
<span class="px-1.5 py-0.5 text-xs rounded bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400" x-text="feature"></span>
</template>
<span x-show="module.features?.length > 3" class="text-xs text-gray-400" x-text="`+${module.features.length - 3} more`"></span>
</div>
</div>
</div>
<div class="flex items-center gap-3 ml-4">
<!-- View Details Button -->
<a :href="`/admin/platforms/${platformCode}/modules/${module.code}`"
class="p-1.5 rounded-lg text-gray-500 hover:text-purple-600 hover:bg-purple-50 dark:text-gray-400 dark:hover:text-purple-400 dark:hover:bg-purple-900/20 transition-colors"
title="View Details">
<span x-html="$icon('eye', 'w-4 h-4')"></span>
</a>
<!-- Configure Button (if has config) -->
<a x-show="hasConfig(module.code)"
:href="`/admin/platforms/${platformCode}/modules/${module.code}/config`"
class="p-1.5 rounded-lg text-gray-500 hover:text-blue-600 hover:bg-blue-50 dark:text-gray-400 dark:hover:text-blue-400 dark:hover:bg-blue-900/20 transition-colors"
title="Configure">
<span x-html="$icon('cog', 'w-4 h-4')"></span>
</a>
<!-- Status Badge -->
<span x-show="module.is_enabled"
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">
Enabled
</span>
<span x-show="!module.is_enabled"
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">
Disabled
</span>
<!-- Toggle Switch -->
<button
@click="toggleModule(module)"
: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>
</template>
</div>
</div>
<!-- Empty State -->
<div x-show="moduleConfig?.modules?.length === 0" class="bg-white rounded-lg shadow-xs dark:bg-gray-800 p-8 text-center">
<span x-html="$icon('puzzle', 'w-12 h-12 mx-auto text-gray-400')"></span>
<p class="mt-4 text-gray-500 dark:text-gray-400">No modules available.</p>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script src="{{ url_for('static', path='admin/js/platform-modules.js') }}"></script>
{% endblock %}