Internal Tool · Team Dashboard
Taskdesk
Internal task and management dashboard — server-side filtering, sorting and pagination, with a comment thread on every task.
Status
Live
List ops
Server-side
Endpoints
10 REST + Swagger
Tables
users · tasks · comments
Migrations
Alembic
The Problem
Internal task tracking tends to load the whole table into the browser and filter it there, which stops working as the table grows. Sorting by priority alphabetically puts high above urgent, filter state is lost on refresh, and a shared link cannot carry the view the sender was looking at.
The Solution
Filters, search, sorting and pagination are all resolved in SQL, so the response stays the same size whether the table holds 24 rows or 24,000. Priority sorts through a SQL CASE that ranks urgent above high rather than sorting the label text. Filter state lives in the URL, and the search box debounces before writing to it so typing stays responsive. The assignee is embedded in each task row, so a list of twenty tasks costs one request rather than twenty-one.
Features
Dashboard & Task List
- Readout strip of total, pending, in progress, completed, blocked and overdue counts
- Every figure is a shortcut — selecting one opens the list filtered to it
- Previews of the current user’s next tasks by due date and everything overdue
- Search across title and description, plus status, priority and assignee filters
- Sortable columns and pagination, with the active view encoded in the URL
- Priority rail on each row’s leading edge for triage at a glance
Tasks, Team & Comments
- Create, edit and delete tasks with assignee, priority, status, due date and description
- Four statuses (pending, in progress, completed, blocked) and four priorities
- Comment thread per task, with status changeable straight from the task panel
- Team roster with role and join date, and a form to add a member
- is_overdue computed server-side — past due and not completed
- Removing a person leaves their tasks intact via ON DELETE SET NULL
API & Data Model
- 10 REST endpoints under /api with interactive Swagger docs at /docs
- Generic Page[T] envelope returning items, total, page, limit and pages
- Declarative Pydantic validation — bad enum, blank title or limit=500 rejected at the edge
- SORT_FIELDS whitelist maps sort keys to columns, preventing column injection
- status, priority, assigned_to, due_date and title indexed — each backs a filter or sort
- Schema changes managed through Alembic migrations
Reusable Layer & Delivery
- components/ui primitives with no knowledge of tasks — DataTable, Modal, Pagination, Badge
- One fetch wrapper handling base URL, query building, 204s and a flattened ApiError
- One service module per resource, so an endpoint change is a one-line edit
- useApi collapses the load/error/data/reload cycle; useDebounced throttles search
- Current user isolated in a single context file so real auth drops in without touching pages
- Two containers behind Nginx with Let’s Encrypt TLS, self-hosted on GCP
Architecture
“FastAPI backend split into routers per resource (tasks, users, dashboard) over SQLAlchemy models for User, Task and Comment, with a shared get_db session dependency and get_task_or_404 / check_user_exists guards. Every list query applies filters, the SORT_FIELDS whitelist and LIMIT/OFFSET in SQL, with counts from a separate COUNT query. A React 19 + Vite frontend calls it through one fetch wrapper and per-resource service modules. Both run as containers on a private Docker network — Nginx serves the built bundle and proxies /api to the backend, which reaches PostgreSQL over a separate network, fronted by Nginx with Let’s Encrypt TLS on GCP.”