From e515bff1a94e58e8222e0139b60a095572fb15ad Mon Sep 17 00:00:00 2001 From: serversdwn Date: Wed, 4 Feb 2026 18:12:18 +0000 Subject: [PATCH] fix: tab state persists in url hash. Settings save nolonger reload the page. Scheduler management now cascades to individual events. --- backend/routers/recurring_schedules.py | 20 +++++++++++++-- .../services/recurring_schedule_service.py | 21 ++++++++++++++-- templates/projects/detail.html | 25 ++++++++++++++++--- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/backend/routers/recurring_schedules.py b/backend/routers/recurring_schedules.py index b784c5d..9a992c4 100644 --- a/backend/routers/recurring_schedules.py +++ b/backend/routers/recurring_schedules.py @@ -330,19 +330,35 @@ async def disable_schedule( db: Session = Depends(get_db), ): """ - Disable a schedule. + Disable a schedule and cancel all its pending actions. """ service = get_recurring_schedule_service(db) + + # Count pending actions before disabling (for response message) + from sqlalchemy import and_ + from backend.models import ScheduledAction + pending_count = db.query(ScheduledAction).filter( + and_( + ScheduledAction.execution_status == "pending", + ScheduledAction.notes.like(f'%"schedule_id": "{schedule_id}"%'), + ) + ).count() + schedule = service.disable_schedule(schedule_id) if not schedule: raise HTTPException(status_code=404, detail="Schedule not found") + message = "Schedule disabled" + if pending_count > 0: + message += f" and {pending_count} pending action(s) cancelled" + return { "success": True, "schedule_id": schedule.id, "enabled": schedule.enabled, - "message": "Schedule disabled", + "cancelled_actions": pending_count, + "message": message, } diff --git a/backend/services/recurring_schedule_service.py b/backend/services/recurring_schedule_service.py index f1da36a..3f53210 100644 --- a/backend/services/recurring_schedule_service.py +++ b/backend/services/recurring_schedule_service.py @@ -169,8 +169,25 @@ class RecurringScheduleService: return self.update_schedule(schedule_id, enabled=True) def disable_schedule(self, schedule_id: str) -> Optional[RecurringSchedule]: - """Disable a schedule.""" - return self.update_schedule(schedule_id, enabled=False) + """Disable a schedule and cancel its pending actions.""" + schedule = self.update_schedule(schedule_id, enabled=False) + if schedule: + # Cancel all pending actions generated by this schedule + pending_actions = self.db.query(ScheduledAction).filter( + and_( + ScheduledAction.execution_status == "pending", + ScheduledAction.notes.like(f'%"schedule_id": "{schedule_id}"%'), + ) + ).all() + + for action in pending_actions: + action.execution_status = "cancelled" + + if pending_actions: + self.db.commit() + logger.info(f"Cancelled {len(pending_actions)} pending actions for disabled schedule {schedule.name}") + + return schedule def generate_actions_for_schedule( self, diff --git a/templates/projects/detail.html b/templates/projects/detail.html index 9b6021e..fecf085 100644 --- a/templates/projects/detail.html +++ b/templates/projects/detail.html @@ -311,6 +311,7 @@ +
@@ -606,6 +607,9 @@ function switchTab(tabName) { button.classList.remove('border-transparent', 'text-gray-600', 'dark:text-gray-400'); button.classList.add('border-seismo-orange', 'text-seismo-orange'); } + + // Persist active tab in URL hash so refresh stays on this tab + history.replaceState(null, '', `#${tabName}`); } // Load project details @@ -677,12 +681,20 @@ document.getElementById('project-settings-form').addEventListener('submit', asyn throw new Error('Failed to update project'); } - // Reload page to show updated data - window.location.reload(); + // Refresh header and dashboard without full page reload + refreshProjectDashboard(); + + // Show success feedback + const successEl = document.getElementById('settings-success'); + successEl.textContent = 'Settings saved.'; + successEl.classList.remove('hidden'); + document.getElementById('settings-error').classList.add('hidden'); + setTimeout(() => successEl.classList.add('hidden'), 3000); } catch (err) { const errorEl = document.getElementById('settings-error'); errorEl.textContent = err.message || 'Failed to update project.'; errorEl.classList.remove('hidden'); + document.getElementById('settings-success').classList.add('hidden'); } }); @@ -1251,9 +1263,16 @@ document.getElementById('schedule-modal')?.addEventListener('click', function(e) } }); -// Load project details on page load +// Load project details on page load and restore active tab from URL hash document.addEventListener('DOMContentLoaded', function() { loadProjectDetails(); + + // Restore tab from URL hash (e.g. #schedules, #settings) + const hash = window.location.hash.replace('#', ''); + const validTabs = ['overview', 'locations', 'units', 'schedules', 'sessions', 'data', 'settings']; + if (hash && validTabs.includes(hash)) { + switchTab(hash); + } }); {% endblock %}