- Added POST /api/tasks/toggle endpoint to mark tasks as done/undone
- Added toggle_task_done() in vikunja_service (POST /tasks/{id})
- Cache invalidated after toggle for immediate refresh
- Checkbox click toggles done state with visual feedback
- Click on task row opens Vikunja in new tab (/tasks/{id})
- ExternalLink icon appears on hover as affordance
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
64 lines
1.6 KiB
Python
64 lines
1.6 KiB
Python
"""Vikunja tasks router."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any, Dict
|
|
|
|
from fastapi import APIRouter
|
|
from pydantic import BaseModel
|
|
|
|
from server.cache import cache
|
|
from server.config import get_settings
|
|
from server.services.vikunja_service import fetch_tasks, toggle_task_done
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api", tags=["tasks"])
|
|
|
|
CACHE_KEY = "tasks"
|
|
|
|
|
|
@router.get("/tasks")
|
|
async def get_tasks() -> Dict[str, Any]:
|
|
"""Return Vikunja task data."""
|
|
|
|
cached = await cache.get(CACHE_KEY)
|
|
if cached is not None:
|
|
return cached
|
|
|
|
try:
|
|
data: Dict[str, Any] = await fetch_tasks(
|
|
get_settings().vikunja_url,
|
|
get_settings().vikunja_token,
|
|
)
|
|
except Exception as exc:
|
|
logger.exception("Failed to fetch Vikunja tasks")
|
|
return {"error": True, "message": str(exc)}
|
|
|
|
await cache.set(CACHE_KEY, data, get_settings().vikunja_cache_ttl)
|
|
return data
|
|
|
|
|
|
class TaskToggleRequest(BaseModel):
|
|
task_id: int
|
|
done: bool
|
|
|
|
|
|
@router.post("/tasks/toggle")
|
|
async def toggle_task(body: TaskToggleRequest) -> Dict[str, Any]:
|
|
"""Toggle a Vikunja task's done state."""
|
|
settings = get_settings()
|
|
result = await toggle_task_done(
|
|
settings.vikunja_url,
|
|
settings.vikunja_token,
|
|
body.task_id,
|
|
body.done,
|
|
)
|
|
|
|
if result.get("ok"):
|
|
# Invalidate cache so next fetch reflects the change
|
|
await cache.invalidate(CACHE_KEY)
|
|
logger.info("[TASKS] task/%d → done=%s ✓", body.task_id, body.done)
|
|
|
|
return result
|