fix: tab state persists in url hash. Settings save nolonger reload the page. Scheduler management now cascades to individual events.
This commit is contained in:
@@ -330,19 +330,35 @@ async def disable_schedule(
|
|||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Disable a schedule.
|
Disable a schedule and cancel all its pending actions.
|
||||||
"""
|
"""
|
||||||
service = get_recurring_schedule_service(db)
|
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)
|
schedule = service.disable_schedule(schedule_id)
|
||||||
|
|
||||||
if not schedule:
|
if not schedule:
|
||||||
raise HTTPException(status_code=404, detail="Schedule not found")
|
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 {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"schedule_id": schedule.id,
|
"schedule_id": schedule.id,
|
||||||
"enabled": schedule.enabled,
|
"enabled": schedule.enabled,
|
||||||
"message": "Schedule disabled",
|
"cancelled_actions": pending_count,
|
||||||
|
"message": message,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -169,8 +169,25 @@ class RecurringScheduleService:
|
|||||||
return self.update_schedule(schedule_id, enabled=True)
|
return self.update_schedule(schedule_id, enabled=True)
|
||||||
|
|
||||||
def disable_schedule(self, schedule_id: str) -> Optional[RecurringSchedule]:
|
def disable_schedule(self, schedule_id: str) -> Optional[RecurringSchedule]:
|
||||||
"""Disable a schedule."""
|
"""Disable a schedule and cancel its pending actions."""
|
||||||
return self.update_schedule(schedule_id, enabled=False)
|
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(
|
def generate_actions_for_schedule(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -311,6 +311,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="settings-success" class="hidden text-sm text-green-600 dark:text-green-400"></div>
|
||||||
<div id="settings-error" class="hidden text-sm text-red-600"></div>
|
<div id="settings-error" class="hidden text-sm text-red-600"></div>
|
||||||
|
|
||||||
<div class="flex justify-end gap-3 pt-2">
|
<div class="flex justify-end gap-3 pt-2">
|
||||||
@@ -606,6 +607,9 @@ function switchTab(tabName) {
|
|||||||
button.classList.remove('border-transparent', 'text-gray-600', 'dark:text-gray-400');
|
button.classList.remove('border-transparent', 'text-gray-600', 'dark:text-gray-400');
|
||||||
button.classList.add('border-seismo-orange', 'text-seismo-orange');
|
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
|
// Load project details
|
||||||
@@ -677,12 +681,20 @@ document.getElementById('project-settings-form').addEventListener('submit', asyn
|
|||||||
throw new Error('Failed to update project');
|
throw new Error('Failed to update project');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload page to show updated data
|
// Refresh header and dashboard without full page reload
|
||||||
window.location.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) {
|
} catch (err) {
|
||||||
const errorEl = document.getElementById('settings-error');
|
const errorEl = document.getElementById('settings-error');
|
||||||
errorEl.textContent = err.message || 'Failed to update project.';
|
errorEl.textContent = err.message || 'Failed to update project.';
|
||||||
errorEl.classList.remove('hidden');
|
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() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
loadProjectDetails();
|
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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user