Remove UI/UX zip file and add comprehensive scheduler functionality with APScheduler integration, cron support, and detailed documentation

This commit is contained in:
2025-12-04 16:01:10 -05:00
parent 21bee026ff
commit 28276f326b
9 changed files with 3306 additions and 99 deletions
+432
View File
@@ -1273,6 +1273,257 @@
border-color: #d1d5db;
color: #111827;
}
/* ===== SCHEDULER PAGE STYLES ===== */
.schedule-card {
background: rgba(42, 42, 42, 0.4);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 16px 20px;
transition: all 0.2s ease;
}
.schedule-card:hover {
background: rgba(42, 42, 42, 0.7);
border-color: rgba(124, 58, 237, 0.3);
transform: translateX(4px);
}
.schedule-card.paused {
opacity: 0.7;
border-left: 3px solid #f59e0b;
}
.schedule-card.active {
border-left: 3px solid #10b981;
}
.schedule-status-chip {
font-size: 0.7rem;
padding: 2px 8px;
border-radius: 9999px;
font-weight: 500;
}
.schedule-status-chip.active {
background-color: rgba(16, 185, 129, 0.2);
color: #10b981;
}
.schedule-status-chip.paused {
background-color: rgba(245, 158, 11, 0.2);
color: #f59e0b;
}
.schedule-status-chip.running {
background-color: rgba(59, 130, 246, 0.2);
color: #3b82f6;
}
.schedule-status-chip.success {
background-color: rgba(16, 185, 129, 0.2);
color: #10b981;
}
.schedule-status-chip.failed {
background-color: rgba(239, 68, 68, 0.2);
color: #ef4444;
}
.schedule-status-chip.scheduled {
background-color: rgba(124, 58, 237, 0.2);
color: #a78bfa;
}
.schedule-tag {
font-size: 0.65rem;
padding: 2px 6px;
border-radius: 4px;
background-color: rgba(107, 114, 128, 0.3);
color: #9ca3af;
}
.schedule-action-btn {
padding: 6px 10px;
border-radius: 6px;
font-size: 0.75rem;
transition: all 0.15s ease;
}
.schedule-action-btn:hover {
transform: scale(1.05);
}
.schedule-action-btn.run {
background: rgba(16, 185, 129, 0.2);
color: #10b981;
}
.schedule-action-btn.run:hover {
background: rgba(16, 185, 129, 0.4);
}
.schedule-action-btn.pause {
background: rgba(245, 158, 11, 0.2);
color: #f59e0b;
}
.schedule-action-btn.pause:hover {
background: rgba(245, 158, 11, 0.4);
}
.schedule-action-btn.edit {
background: rgba(59, 130, 246, 0.2);
color: #60a5fa;
}
.schedule-action-btn.edit:hover {
background: rgba(59, 130, 246, 0.4);
}
.schedule-action-btn.delete {
background: rgba(239, 68, 68, 0.2);
color: #f87171;
}
.schedule-action-btn.delete:hover {
background: rgba(239, 68, 68, 0.4);
}
.schedule-filter-btn.active {
background-color: #7c3aed !important;
color: white !important;
}
/* Calendar styles */
.schedule-calendar-day {
min-height: 80px;
background: rgba(42, 42, 42, 0.3);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 8px;
transition: all 0.2s ease;
}
.schedule-calendar-day:hover {
background: rgba(42, 42, 42, 0.6);
border-color: rgba(124, 58, 237, 0.3);
}
.schedule-calendar-day.today {
border-color: #7c3aed;
background: rgba(124, 58, 237, 0.1);
}
.schedule-calendar-day.other-month {
opacity: 0.4;
}
.schedule-calendar-event {
font-size: 0.65rem;
padding: 2px 4px;
border-radius: 4px;
margin-top: 2px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.schedule-calendar-event.success {
background-color: rgba(16, 185, 129, 0.3);
color: #10b981;
}
.schedule-calendar-event.failed {
background-color: rgba(239, 68, 68, 0.3);
color: #ef4444;
}
.schedule-calendar-event.scheduled {
background-color: rgba(59, 130, 246, 0.3);
color: #60a5fa;
}
/* Modal multi-step */
.schedule-modal-step {
display: none;
}
.schedule-modal-step.active {
display: block;
animation: fadeIn 0.3s ease;
}
.schedule-step-indicator {
display: flex;
justify-content: center;
gap: 8px;
margin-bottom: 24px;
}
.schedule-step-dot {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
font-weight: 600;
background: rgba(107, 114, 128, 0.3);
color: #9ca3af;
transition: all 0.3s ease;
}
.schedule-step-dot.active {
background: #7c3aed;
color: white;
}
.schedule-step-dot.completed {
background: #10b981;
color: white;
}
.schedule-step-connector {
width: 40px;
height: 2px;
background: rgba(107, 114, 128, 0.3);
align-self: center;
}
.schedule-step-connector.active {
background: #7c3aed;
}
/* Recurrence preview */
.recurrence-preview {
background: rgba(124, 58, 237, 0.1);
border: 1px solid rgba(124, 58, 237, 0.3);
border-radius: 8px;
padding: 12px 16px;
font-size: 0.875rem;
}
/* Light theme overrides */
body.light-theme .schedule-card {
background: rgba(255, 255, 255, 0.6);
border-color: #d1d5db;
}
body.light-theme .schedule-card:hover {
background: rgba(255, 255, 255, 0.9);
}
body.light-theme .schedule-calendar-day {
background: rgba(255, 255, 255, 0.6);
border-color: #e5e7eb;
}
body.light-theme .schedule-calendar-day:hover {
background: rgba(255, 255, 255, 0.9);
}
</style>
</head>
<body>
@@ -1291,6 +1542,7 @@
<a href="#" data-page="hosts" class="nav-link text-gray-300 hover:text-white transition-colors">Hosts</a>
<a href="#" data-page="playbooks" class="nav-link text-gray-300 hover:text-white transition-colors">Playbooks</a>
<a href="#" data-page="tasks" class="nav-link text-gray-300 hover:text-white transition-colors">Tasks</a>
<a href="#" data-page="schedules" class="nav-link text-gray-300 hover:text-white transition-colors">Schedules</a>
<a href="#" data-page="logs" class="nav-link text-gray-300 hover:text-white transition-colors">Logs</a>
<a href="#" data-page="help" class="nav-link text-gray-300 hover:text-white transition-colors">Aide</a>
<button id="theme-toggle" class="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 transition-colors">
@@ -1423,6 +1675,41 @@
</button>
</div>
</div>
<!-- Schedules Widget -->
<div class="glass-card p-6 fade-in">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">
<i class="fas fa-calendar-alt text-purple-400 mr-2"></i>Planificateur
</h3>
<a href="#" data-page="schedules" class="nav-link text-purple-400 text-sm hover:underline">Voir tout →</a>
</div>
<!-- Stats mini -->
<div class="grid grid-cols-3 gap-2 mb-4">
<div class="text-center p-2 bg-gray-800/50 rounded">
<div class="text-lg font-bold text-green-400" id="dashboard-schedules-active">0</div>
<div class="text-xs text-gray-500">Actifs</div>
</div>
<div class="text-center p-2 bg-gray-800/50 rounded">
<div class="text-lg font-bold text-blue-400" id="dashboard-schedules-next">--</div>
<div class="text-xs text-gray-500">Prochaine</div>
</div>
<div class="text-center p-2 bg-gray-800/50 rounded">
<div class="text-lg font-bold text-red-400" id="dashboard-schedules-failures">0</div>
<div class="text-xs text-gray-500">Échecs 24h</div>
</div>
</div>
<!-- Next executions -->
<div id="dashboard-upcoming-schedules" class="space-y-2 text-sm">
<p class="text-gray-500 text-center py-4">Chargement...</p>
</div>
<button onclick="showCreateScheduleModal()" class="w-full mt-4 p-2 bg-purple-600 hover:bg-purple-500 rounded-lg text-sm transition-colors">
<i class="fas fa-plus mr-2"></i>Nouveau schedule
</button>
</div>
</div>
</div>
</div>
@@ -1702,6 +1989,141 @@
</section>
<!-- END PAGE: TASKS -->
<!-- ==================== PAGE: SCHEDULES ==================== -->
<section id="page-schedules" class="page-section">
<div class="pt-24 pb-16 min-h-screen bg-gradient-to-b from-gray-900 to-black">
<div class="max-w-7xl mx-auto px-6">
<!-- Header -->
<div class="text-center mb-8">
<h1 class="text-4xl font-bold mb-4 gradient-text">
<i class="fas fa-calendar-alt mr-3"></i>Planificateur des Playbooks
</h1>
<p class="text-gray-400 max-w-2xl mx-auto">Planifiez et orchestrez vos playbooks dans le temps - Exécutions automatiques ponctuelles ou récurrentes</p>
</div>
<!-- Actions Header -->
<div class="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4 mb-6">
<div class="flex items-center gap-4">
<button onclick="showCreateScheduleModal()" class="btn-primary">
<i class="fas fa-plus mr-2"></i>Nouveau Schedule
</button>
<button onclick="dashboard.refreshSchedules()" class="px-4 py-2 bg-gray-700 rounded-lg hover:bg-gray-600 transition-colors">
<i class="fas fa-sync-alt mr-2"></i>Rafraîchir
</button>
</div>
<div class="flex items-center gap-3">
<select id="schedule-view-toggle" onchange="dashboard.toggleScheduleView(this.value)" class="px-4 py-2 bg-gray-700 rounded-lg border border-gray-600 text-sm">
<option value="list">Vue : Liste</option>
<option value="calendar">Vue : Calendrier</option>
</select>
</div>
</div>
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
<div class="glass-card p-4 text-center">
<div class="text-2xl font-bold text-green-400" id="schedules-active-count">0</div>
<div class="text-sm text-gray-400">Schedules actifs</div>
</div>
<div class="glass-card p-4 text-center">
<div class="text-2xl font-bold text-orange-400" id="schedules-paused-count">0</div>
<div class="text-sm text-gray-400">En pause</div>
</div>
<div class="glass-card p-4 text-center">
<div class="text-2xl font-bold text-blue-400" id="schedules-next-run">--:--</div>
<div class="text-sm text-gray-400">Prochaine exécution</div>
</div>
<div class="glass-card p-4 text-center">
<div class="text-2xl font-bold text-red-400" id="schedules-failures-24h">0</div>
<div class="text-sm text-gray-400">Échecs 24h</div>
</div>
</div>
<!-- Filters -->
<div class="glass-card p-4 mb-6">
<div class="flex flex-wrap items-center gap-4">
<div class="flex items-center gap-2">
<span class="text-sm text-gray-400"><i class="fas fa-filter mr-1"></i>Filtres:</span>
<button onclick="dashboard.filterSchedules('all')" class="schedule-filter-btn px-3 py-1.5 text-xs rounded-lg bg-purple-600 text-white" data-filter="all">
Tous
</button>
<button onclick="dashboard.filterSchedules('active')" class="schedule-filter-btn px-3 py-1.5 text-xs rounded-lg bg-gray-700 text-gray-300 hover:bg-gray-600" data-filter="active">
Actifs
</button>
<button onclick="dashboard.filterSchedules('paused')" class="schedule-filter-btn px-3 py-1.5 text-xs rounded-lg bg-gray-700 text-gray-300 hover:bg-gray-600" data-filter="paused">
En pause
</button>
</div>
<div class="flex-1"></div>
<div class="relative">
<i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"></i>
<input type="text" id="schedule-search" placeholder="Rechercher..."
class="pl-10 pr-4 py-2 bg-gray-800 border border-gray-700 rounded-lg text-sm w-64"
oninput="dashboard.searchSchedules(this.value)">
</div>
</div>
</div>
<!-- List View -->
<div id="schedules-list-view" class="glass-card p-6">
<div id="schedules-list" class="space-y-3">
<!-- Schedules will be populated by JavaScript -->
</div>
<!-- Empty State -->
<div id="schedules-empty" class="hidden text-center py-16">
<div class="w-20 h-20 mx-auto mb-6 bg-purple-600/20 rounded-full flex items-center justify-center">
<i class="fas fa-calendar-plus text-4xl text-purple-400"></i>
</div>
<h3 class="text-xl font-semibold mb-2">Aucun schedule configuré</h3>
<p class="text-gray-400 mb-6 max-w-md mx-auto">
Créez votre premier schedule pour automatiser l'exécution de vos playbooks Ansible.
</p>
<button onclick="showCreateScheduleModal()" class="btn-primary">
<i class="fas fa-plus mr-2"></i>Créer votre premier schedule
</button>
</div>
</div>
<!-- Calendar View (Hidden by default) -->
<div id="schedules-calendar-view" class="glass-card p-6 hidden">
<div class="flex items-center justify-between mb-6">
<button onclick="dashboard.prevCalendarMonth()" class="p-2 bg-gray-700 rounded-lg hover:bg-gray-600">
<i class="fas fa-chevron-left"></i>
</button>
<h3 id="schedule-calendar-title" class="text-xl font-semibold">Décembre 2025</h3>
<button onclick="dashboard.nextCalendarMonth()" class="p-2 bg-gray-700 rounded-lg hover:bg-gray-600">
<i class="fas fa-chevron-right"></i>
</button>
</div>
<div class="grid grid-cols-7 gap-1 mb-2">
<div class="text-center text-xs text-gray-500 py-2">Lun</div>
<div class="text-center text-xs text-gray-500 py-2">Mar</div>
<div class="text-center text-xs text-gray-500 py-2">Mer</div>
<div class="text-center text-xs text-gray-500 py-2">Jeu</div>
<div class="text-center text-xs text-gray-500 py-2">Ven</div>
<div class="text-center text-xs text-gray-500 py-2">Sam</div>
<div class="text-center text-xs text-gray-500 py-2">Dim</div>
</div>
<div id="schedule-calendar-grid" class="grid grid-cols-7 gap-1">
<!-- Calendar days will be populated by JavaScript -->
</div>
</div>
<!-- Upcoming Executions -->
<div class="glass-card p-6 mt-6">
<h3 class="text-lg font-semibold mb-4">
<i class="fas fa-clock text-blue-400 mr-2"></i>Prochaines exécutions
</h3>
<div id="schedules-upcoming" class="space-y-2">
<!-- Upcoming executions will be populated by JavaScript -->
</div>
</div>
</div>
</div>
</section>
<!-- END PAGE: SCHEDULES -->
<!-- ==================== PAGE: LOGS ==================== -->
<section id="page-logs" class="page-section">
<div id="logs" class="pt-24 pb-16 min-h-screen bg-gradient-to-b from-black to-gray-900">
@@ -2385,6 +2807,16 @@ homelab-automation/
</div>
<script src="/static/main.js"></script>
<script>
// Fallback global helper in case main.js didn't expose it
if (typeof window.showCreateScheduleModal === 'undefined') {
window.showCreateScheduleModal = function(prefilledPlaybook = null) {
if (window.dashboard && typeof dashboard.showCreateScheduleModal === 'function') {
dashboard.showCreateScheduleModal(prefilledPlaybook);
}
};
}
</script>
<!-- Navigation & Help Page Scripts -->
<script>