Show open Vikunja task count as badge on To-Do sidebar link

Fetched async via /api/todos/count so it doesn't block page render.
Badge hidden when count is zero.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martin Hohenberg
2026-06-20 19:16:19 +02:00
parent f713fbe2fb
commit 38e6d38a2f
2 changed files with 16 additions and 1 deletions

View File

@@ -4,6 +4,12 @@ from app.vikunja import create_task, delete_task, get_tasks, update_task
router = APIRouter(prefix="/todos", tags=["todos"])
@router.get("/count")
def count_todos():
tasks = get_tasks()
return {"open": sum(1 for t in tasks if not t.get("done"))}
@router.get("/")
def list_todos():
return get_tasks()

View File

@@ -60,7 +60,7 @@
<li><a href="/printers" class="nav-link {% if active == 'printers' %}active{% endif %}"><i class="bi bi-printer me-2"></i>Printers</a></li>
<li><a href="/jobs" class="nav-link {% if active == 'jobs' %}active{% endif %}"><i class="bi bi-list-task me-2"></i>Jobs</a></li>
<li><a href="/brackets" class="nav-link {% if active == 'brackets' %}active{% endif %}"><i class="bi bi-collection me-2"></i>Brackets</a></li>
<li><a href="/todos" class="nav-link {% if active == 'todos' %}active{% endif %}"><i class="bi bi-check2-square me-2"></i>To-Do</a></li>
<li><a href="/todos" class="nav-link {% if active == 'todos' %}active{% endif %} d-flex align-items-center justify-content-between"><span><i class="bi bi-check2-square me-2"></i>To-Do</span><span id="todo-count-badge" class="badge rounded-pill bg-primary ms-1" style="display:none!important"></span></a></li>
<li><a href="/pricing" class="nav-link {% if active == 'pricing' %}active{% endif %}"><i class="bi bi-currency-euro me-2"></i>Pricing</a></li>
</ul>
</nav>
@@ -71,5 +71,14 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
fetch('/api/todos/count').then(r => r.json()).then(d => {
if (d.open > 0) {
const b = document.getElementById('todo-count-badge');
b.textContent = d.open;
b.style.removeProperty('display');
}
}).catch(() => {});
</script>
</body>
</html>