diff --git a/backend/app/api/routers/tasks.py b/backend/app/api/routers/tasks.py index 14ee50f..4701d83 100644 --- a/backend/app/api/routers/tasks.py +++ b/backend/app/api/routers/tasks.py @@ -168,7 +168,6 @@ async def commit_drafts( session: Session = Depends(get_db), ) -> TasksCreateResponse: tasks = task_crud.commit_drafts(task_ids, user_id, session) - session.commit() return TasksCreateResponse( task_ids=[t.id for t in tasks if t.id], created_count=len(tasks), @@ -184,10 +183,20 @@ async def update_task( ) -> TaskRead: user_timezone: str = get_user_timezone(user_id, session) updated_task = task_crud.update_task(task_id, task_update, user_id, session) - session.commit() return TaskRead.from_model(updated_task, user_timezone) +@router.post("/deschedule", status_code=status.HTTP_200_OK) +async def deschedule_tasks( + task_ids: TasksDelete = Body(...), + user_id: int = Depends(get_current_user_id), + session: Session = Depends(get_db), +) -> dict[str, Any]: + """Deschedule tasks by removing scheduled_at timestamp and deleting schedule items.""" + task_crud.deschedule_tasks(task_ids.task_ids, user_id, session) + return {"descheduled_count": len(task_ids.task_ids)} + + @router.get("/jobs/{job_id}", status_code=status.HTTP_200_OK) async def get_job_status( job_id: str, diff --git a/backend/app/crud/schedule_item_crud.py b/backend/app/crud/schedule_item_crud.py index dcd484f..c5e1c3f 100644 --- a/backend/app/crud/schedule_item_crud.py +++ b/backend/app/crud/schedule_item_crud.py @@ -54,7 +54,7 @@ def create_schedule_items_from_blocks( schedule_items.append(schedule_item) session.add(schedule_item) - session.commit() + session.flush() for item in schedule_items: session.refresh(item) diff --git a/backend/app/crud/task_crud.py b/backend/app/crud/task_crud.py index 26af21c..9cffa22 100644 --- a/backend/app/crud/task_crud.py +++ b/backend/app/crud/task_crud.py @@ -157,3 +157,43 @@ def commit_drafts(draft_ids: list[int], user_id: int, session: Session) -> list[ session.add(draft) session.flush() return list(drafts) + + +def deschedule_tasks(task_ids: list[int], user_id: int, session: Session) -> None: + """ + Deschedule tasks by setting scheduled_at to None and deleting their schedule items. + + Args: + task_ids: List of task IDs to deschedule + user_id: User ID to ensure ownership + session: Database session + """ + if not task_ids: + return + + from app.models.schedule_item import ScheduleItem + + tasks = session.exec( + select(Task) + .where(Task.id.in_(task_ids)) # type: ignore[union-attr] + .where(Task.user_id == user_id) + ).all() + + if not tasks: + return + + schedule_items = session.exec( + select(ScheduleItem) + .where(ScheduleItem.task_id.in_(task_ids)) # type: ignore[union-attr] + .where(ScheduleItem.user_id == user_id) + .where(ScheduleItem.source == "task") + ).all() + + for schedule_item in schedule_items: + session.delete(schedule_item) + + for task in tasks: + task.scheduled_at = None + session.add(task) + + session.flush() diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 1b95a0a..3cdaa48 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -18,7 +18,7 @@ export default function RootLayout({