diff --git a/backend/services/auto_cycle.py b/backend/services/auto_cycle.py index a0b1cb4..443bf01 100644 --- a/backend/services/auto_cycle.py +++ b/backend/services/auto_cycle.py @@ -744,9 +744,10 @@ def _scheduler_loop(stop_event: threading.Event): interval_hours = 3.0 _current_status["interval_hours"] = interval_hours - next_run = datetime.utcnow().isoformat() + from datetime import timedelta + next_run = (datetime.utcnow() + timedelta(hours=interval_hours)).isoformat() _current_status["next_run_at"] = next_run - logger.info(f"[Scheduler] Next cycle in {interval_hours}h") + logger.info(f"[Scheduler] Next cycle in {interval_hours}h (at {next_run[:16]} UTC)") # Wait for the interval (or until stop is signalled) stop_event.wait(timeout=interval_hours * 3600) diff --git a/frontend/src/pages/Config.tsx b/frontend/src/pages/Config.tsx index 64dc0cb..67157f0 100644 --- a/frontend/src/pages/Config.tsx +++ b/frontend/src/pages/Config.tsx @@ -39,6 +39,34 @@ const PROFILE_COLORS = [ { value: '#eab308', label: 'Jaune' }, ] +function NextRunCountdown({ nextRunAt }: { nextRunAt: string }) { + const [remaining, setRemaining] = useState('') + + useEffect(() => { + function update() { + const diff = new Date(nextRunAt + 'Z').getTime() - Date.now() + if (diff <= 0) { setRemaining('Imminent…'); return } + const h = Math.floor(diff / 3_600_000) + const m = Math.floor((diff % 3_600_000) / 60_000) + const s = Math.floor((diff % 60_000) / 1_000) + setRemaining(h > 0 ? `${h}h ${m.toString().padStart(2,'0')}min` : `${m}min ${s.toString().padStart(2,'0')}s`) + } + update() + const id = setInterval(update, 1_000) + return () => clearInterval(id) + }, [nextRunAt]) + + const absTime = new Date(nextRunAt + 'Z').toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) + + return ( +