Files
orion/app/templates/admin/background-tasks.html
Samir Boulahtit 013d8e3d10 feat: integrate Quill rich text editor in admin templates
- Add Quill editor to content-page-edit.html
- Add Quill editor to vendor-product-edit.html
- Add Quill snow theme CSS
- Update background-tasks.html and platform-homepage.html templates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 20:37:54 +01:00

283 lines
16 KiB
HTML

{# app/templates/admin/background-tasks.html #}
{% extends "admin/base.html" %}
{% from 'shared/macros/alerts.html' import loading_state, error_state %}
{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %}
{% block title %}Background Tasks{% endblock %}
{% block alpine_data %}backgroundTasks(){% endblock %}
{% block extra_scripts %}
<script src="/static/admin/js/background-tasks.js"></script>
{% endblock %}
{% block content %}
{% call page_header_flex(title='Background Tasks', subtitle='Monitor running and completed background tasks') %}
<!-- Flower Dashboard Link (Celery Monitoring) -->
<a href="{{ flower_url }}"
target="_blank"
rel="noopener noreferrer"
class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple mr-2"
title="Open Flower dashboard for detailed Celery task monitoring">
<span x-html="$icon('chart-bar', 'w-4 h-4 mr-2')"></span>
Flower Dashboard
</a>
{{ refresh_button(variant='secondary') }}
{% endcall %}
{{ loading_state('Loading tasks...') }}
{{ error_state('Error loading tasks') }}
<!-- Dashboard Content -->
<div x-show="!loading && !error">
<!-- Stats Cards -->
<div class="grid gap-6 mb-8 md:grid-cols-2 xl:grid-cols-4">
<!-- Running Tasks -->
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="p-3 mr-4 text-yellow-500 bg-yellow-100 rounded-full dark:text-yellow-100 dark:bg-yellow-500">
<span x-html="$icon('refresh', 'w-5 h-5 animate-spin')"></span>
</div>
<div>
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">Running</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.running">0</p>
</div>
</div>
<!-- Completed Today -->
<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-2 text-sm font-medium text-gray-600 dark:text-gray-400">Today</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.tasks_today">0</p>
</div>
</div>
<!-- Failed -->
<div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<div class="p-3 mr-4 text-red-500 bg-red-100 rounded-full dark:text-red-100 dark:bg-red-500">
<span x-html="$icon('x-circle', 'w-5 h-5')"></span>
</div>
<div>
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">Failed</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.failed">0</p>
</div>
</div>
<!-- Total -->
<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('collection', 'w-5 h-5')"></span>
</div>
<div>
<p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">Total</p>
<p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.total_tasks">0</p>
</div>
</div>
</div>
<!-- Running Tasks Section -->
<div class="mb-8" x-show="runningTasks.length > 0">
<div class="p-6 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<h4 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200 flex items-center">
<span x-html="$icon('refresh', 'w-5 h-5 mr-2 animate-spin text-yellow-500')"></span>
Currently Running
</h4>
<div class="space-y-3">
<template x-for="task in runningTasks" :key="task.task_type + '-' + task.id">
<div class="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-800">
<div class="flex items-center justify-between">
<div class="flex items-center">
<span class="px-2 py-1 text-xs font-semibold rounded-full mr-3"
:class="{
'bg-blue-100 text-blue-700 dark:bg-blue-700 dark:text-blue-100': task.task_type === 'import',
'bg-purple-100 text-purple-700 dark:bg-purple-700 dark:text-purple-100': task.task_type === 'test_run'
}"
x-text="task.task_type === 'import' ? 'Import' : 'Test Run'">
</span>
<div>
<p class="font-medium text-gray-800 dark:text-gray-200" x-text="task.description"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">
Started by <span x-text="task.triggered_by || 'system'"></span>
at <span x-text="task.started_at ? new Date(task.started_at).toLocaleTimeString() : 'N/A'"></span>
</p>
</div>
</div>
<div class="text-right">
<p class="text-lg font-bold text-yellow-600 dark:text-yellow-400" x-text="formatDuration(task.duration_seconds)"></p>
<p class="text-xs text-gray-500">elapsed</p>
</div>
</div>
</div>
</template>
</div>
</div>
</div>
<!-- Task Type Stats -->
<div class="grid gap-6 mb-8 md:grid-cols-2">
<!-- Import Jobs Stats -->
<div class="p-6 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<h4 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200 flex items-center">
<span x-html="$icon('cube', 'w-5 h-5 mr-2 text-blue-500')"></span>
Import Jobs
</h4>
<div class="grid grid-cols-2 gap-4">
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-gray-700 dark:text-gray-200" x-text="stats.import_jobs?.total || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Total</p>
</div>
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-yellow-600" x-text="stats.import_jobs?.running || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Running</p>
</div>
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-green-600" x-text="stats.import_jobs?.completed || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Completed</p>
</div>
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-red-600" x-text="stats.import_jobs?.failed || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Failed</p>
</div>
</div>
<div class="mt-4 text-center">
<a href="/admin/imports" class="text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400">
View Import Jobs &rarr;
</a>
</div>
</div>
<!-- Test Runs Stats -->
<div class="p-6 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<h4 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200 flex items-center">
<span x-html="$icon('beaker', 'w-5 h-5 mr-2 text-purple-500')"></span>
Test Runs
</h4>
<div class="grid grid-cols-2 gap-4">
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-gray-700 dark:text-gray-200" x-text="stats.test_runs?.total || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Total</p>
</div>
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-yellow-600" x-text="stats.test_runs?.running || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Running</p>
</div>
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-green-600" x-text="stats.test_runs?.completed || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Passed</p>
</div>
<div class="text-center p-3 bg-gray-50 dark:bg-gray-700 rounded-lg">
<p class="text-2xl font-bold text-red-600" x-text="stats.test_runs?.failed || 0"></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Failed</p>
</div>
</div>
<div class="mt-4 text-center">
<a href="/admin/testing" class="text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400">
View Test Dashboard &rarr;
</a>
</div>
</div>
</div>
<!-- Filter Tabs -->
<div class="mb-4">
<div class="flex space-x-2">
<button @click="filterType = null; loadTasks()"
:class="filterType === null ? 'bg-purple-600 text-white' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300'"
class="px-4 py-2 text-sm font-medium rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700">
All Tasks
</button>
<button @click="filterType = 'import'; loadTasks()"
:class="filterType === 'import' ? 'bg-blue-600 text-white' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300'"
class="px-4 py-2 text-sm font-medium rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700">
Imports
</button>
<button @click="filterType = 'test_run'; loadTasks()"
:class="filterType === 'test_run' ? 'bg-purple-600 text-white' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300'"
class="px-4 py-2 text-sm font-medium rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700">
Test Runs
</button>
</div>
</div>
<!-- Tasks Table -->
<div class="p-6 bg-white rounded-lg shadow-xs dark:bg-gray-800">
<h4 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
Recent Tasks
</h4>
<div class="overflow-x-auto">
<table class="w-full whitespace-nowrap">
<thead>
<tr class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800">
<th class="px-4 py-3">Type</th>
<th class="px-4 py-3">Description</th>
<th class="px-4 py-3">Started</th>
<th class="px-4 py-3">Duration</th>
<th class="px-4 py-3">Triggered By</th>
<th class="px-4 py-3">Status</th>
<th class="px-4 py-3">Celery</th>
</tr>
</thead>
<tbody class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800">
<template x-for="task in tasks" :key="task.task_type + '-' + task.id">
<tr class="text-gray-700 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700">
<td class="px-4 py-3">
<span class="px-2 py-1 text-xs font-semibold rounded-full"
:class="{
'bg-blue-100 text-blue-700 dark:bg-blue-700 dark:text-blue-100': task.task_type === 'import',
'bg-purple-100 text-purple-700 dark:bg-purple-700 dark:text-purple-100': task.task_type === 'test_run'
}"
x-text="task.task_type === 'import' ? 'Import' : 'Test Run'">
</span>
</td>
<td class="px-4 py-3">
<p class="font-medium truncate max-w-xs" x-text="task.description"></p>
<p x-show="task.error_message" class="text-xs text-red-500 truncate max-w-xs" x-text="task.error_message"></p>
</td>
<td class="px-4 py-3 text-sm" x-text="task.started_at ? new Date(task.started_at).toLocaleString() : 'N/A'"></td>
<td class="px-4 py-3 text-sm" x-text="formatDuration(task.duration_seconds)"></td>
<td class="px-4 py-3 text-sm" x-text="task.triggered_by || 'system'"></td>
<td class="px-4 py-3">
<span class="px-2 py-1 text-xs font-semibold rounded-full"
:class="{
'bg-green-100 text-green-700 dark:bg-green-700 dark:text-green-100': task.status === 'completed' || task.status === 'passed',
'bg-yellow-100 text-yellow-700 dark:bg-yellow-700 dark:text-yellow-100': task.status === 'running' || task.status === 'processing' || task.status === 'pending',
'bg-red-100 text-red-700 dark:bg-red-700 dark:text-red-100': task.status === 'failed' || task.status === 'error',
'bg-orange-100 text-orange-700 dark:bg-orange-700 dark:text-orange-100': task.status === 'completed_with_errors',
'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-100': !['completed', 'passed', 'running', 'processing', 'pending', 'failed', 'error', 'completed_with_errors'].includes(task.status)
}"
x-text="task.status">
</span>
</td>
<td class="px-4 py-3 text-sm">
<template x-if="task.celery_task_id">
<a :href="'{{ flower_url }}/task/' + task.celery_task_id"
target="_blank"
rel="noopener noreferrer"
class="text-purple-600 hover:text-purple-800 dark:text-purple-400 dark:hover:text-purple-300 underline"
title="View in Flower">
<span x-html="$icon('external-link', 'w-4 h-4 inline')"></span>
<span x-text="task.celery_task_id.substring(0, 8) + '...'"></span>
</a>
</template>
<template x-if="!task.celery_task_id">
<span class="text-gray-400 text-xs">-</span>
</template>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<template x-if="tasks.length === 0">
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
<span x-html="$icon('collection', 'w-12 h-12 mx-auto mb-2 text-gray-400')"></span>
<p>No tasks found</p>
</div>
</template>
</div>
</div>
{% endblock %}