"""Task item API — AP1.5.""" from __future__ import annotations from datetime import datetime from typing import Literal, Optional from capabilities import require_capability from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from services import tasks as task_service from tenant_context import TenantContext router = APIRouter(prefix="/api/tasks", tags=["tasks"]) class TaskUpdateRequest(BaseModel): title: Optional[str] = Field(default=None, min_length=1, max_length=255) description: Optional[str] = None status: Optional[Literal["open", "in_progress", "done", "discarded"]] = None roadmap_item_id: Optional[str] = None clear_roadmap_item: bool = False sort_order: Optional[int] = None due_at: Optional[str] = None clear_due_at: bool = False @router.patch("/{task_id}") def update_task( task_id: str, body: TaskUpdateRequest, ctx: TenantContext = Depends(require_capability("kairo.action.manage")), ): due_at = None if body.due_at is not None: try: due_at = datetime.fromisoformat(body.due_at.replace("Z", "+00:00")) except ValueError as exc: raise HTTPException(status_code=400, detail="Ungültiges due_at") from exc try: item = task_service.update_task( tenant_id=ctx.tenant_id, task_id=task_id, user_id=ctx.user_id, title=body.title, description=body.description, status=body.status, roadmap_item_id=body.roadmap_item_id, clear_roadmap_item=body.clear_roadmap_item, sort_order=body.sort_order, due_at=due_at, clear_due_at=body.clear_due_at, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc if not item: raise HTTPException(status_code=404, detail="Task nicht gefunden") return item @router.delete("/{task_id}", status_code=204) def delete_task( task_id: str, ctx: TenantContext = Depends(require_capability("kairo.action.manage")), ): if not task_service.delete_task( tenant_id=ctx.tenant_id, task_id=task_id, user_id=ctx.user_id ): raise HTTPException(status_code=404, detail="Task nicht gefunden")