Merge dev v0.5.1 before 0.6 update with calender. #25

Merged
serversdown merged 11 commits from dev into main 2026-02-06 14:56:13 -05:00
3 changed files with 59 additions and 7 deletions
Showing only changes of commit e515bff1a9 - Show all commits

View File

@@ -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,
} }

View File

@@ -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,

View File

@@ -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 %}