feat: interactive Vikunja tasks — checkbox toggles done, click opens task

- 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>
This commit is contained in:
Sam 2026-03-02 23:43:01 +01:00
parent c6db0ab569
commit bc2dcb5589
5 changed files with 134 additions and 25 deletions

View file

@ -171,6 +171,46 @@ async def fetch_tasks(base_url: str, token: str) -> Dict[str, Any]:
return result
async def toggle_task_done(
base_url: str,
token: str,
task_id: int,
done: bool,
) -> Dict[str, Any]:
"""Toggle the done state of a single Vikunja task.
Args:
base_url: Vikunja instance base URL.
token: API token for authentication.
task_id: The task ID to update.
done: New done state.
Returns:
Dictionary with ``ok`` and optionally ``error``.
"""
if not base_url or not token:
return {"ok": False, "error": "Missing Vikunja base URL or token"}
clean_url = base_url.rstrip("/")
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
try:
async with httpx.AsyncClient(timeout=10, headers=headers) as client:
resp = await client.post(
f"{clean_url}/tasks/{task_id}",
json={"done": done},
)
resp.raise_for_status()
return {"ok": True, "task": {"id": task_id, "done": done}}
except httpx.HTTPStatusError as exc:
return {"ok": False, "error": f"HTTP {exc.response.status_code}"}
except Exception as exc:
return {"ok": False, "error": str(exc)}
async def fetch_single_project(
base_url: str,
token: str,