Remove UI/UX zip file and add comprehensive scheduler functionality with APScheduler integration, cron support, and detailed documentation
This commit is contained in:
+1249
-1
File diff suppressed because it is too large
Load Diff
+432
@@ -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>
|
||||
|
||||
+1203
-97
File diff suppressed because it is too large
Load Diff
@@ -7,4 +7,7 @@ websockets>=14.0
|
||||
aiofiles>=24.1.0
|
||||
python-dotenv>=1.0.1
|
||||
requests>=2.32.0
|
||||
httpx>=0.28.0
|
||||
httpx>=0.28.0
|
||||
apscheduler>=3.10.0
|
||||
croniter>=2.0.0
|
||||
pytz>=2024.1
|
||||
Reference in New Issue
Block a user