# TaskTracker Web UI: React to Razor Pages Migration ## Date: 2026-03-01 ## Motivation - **Single tech stack:** Eliminate the npm/Node toolchain; everything in C#/.NET - **Single-binary deployment:** UI bundled into the API project — one process, one port, no separate build step ## Approach: Razor Pages + htmx Server-rendered HTML with Razor syntax. htmx handles partial page updates via AJAX. Vanilla JS for drag-and-drop (SortableJS), charts (Chart.js), and keyboard shortcuts. ## Architecture ### Single Process Razor Pages are added directly to the existing `TaskTracker.Api` project. No separate web project. - One `Program.cs`, one binary, one port - Razor Pages call repositories directly (same DI container) — no API round-trips for the UI - API controllers remain at `/api/*` for external consumers (MCP, WindowWatcher, Chrome extension) - `wwwroot/` hosts static JS/CSS files ### File Structure ``` TaskTracker.Api/ ├── Pages/ │ ├── _ViewImports.cshtml │ ├── _ViewStart.cshtml │ ├── Shared/ │ │ └── _Layout.cshtml (shell: nav bar, search modal, script tags) │ ├── Board.cshtml + Board.cshtml.cs │ ├── Analytics.cshtml + Analytics.cshtml.cs │ └── Mappings.cshtml + Mappings.cshtml.cs ├── Pages/Partials/ │ ├── _KanbanColumn.cshtml (single column, htmx swappable) │ ├── _TaskCard.cshtml (single card) │ ├── _TaskDetail.cshtml (slide-in detail panel) │ ├── _FilterBar.cshtml (category chip bar) │ ├── _SubtaskList.cshtml │ ├── _NotesList.cshtml │ ├── _CreateTaskForm.cshtml │ ├── _MappingRow.cshtml (single table row, inline edit) │ └── _SearchResults.cshtml (command palette results) ├── wwwroot/ │ ├── css/site.css (dark theme, design tokens, animations) │ ├── lib/htmx.min.js (~14KB gzipped) │ ├── lib/Sortable.min.js (~40KB) │ ├── lib/chart.min.js (~65KB gzipped) │ └── js/app.js (command palette, keyboard shortcuts, DnD wiring) └── Program.cs (adds AddRazorPages + MapRazorPages) ``` ### Program.cs Changes ```csharp builder.Services.AddRazorPages(); // ... existing setup ... app.MapRazorPages(); // Add alongside MapControllers() app.MapControllers(); // Keep for API consumers ``` ## Client-Side Libraries (all vendored, no npm) | Library | Size (gzipped) | Purpose | |---------|---------------|---------| | htmx | ~14KB | Partial page updates via AJAX | | SortableJS | ~40KB | Drag-and-drop Kanban columns | | Chart.js | ~65KB | Timeline bar chart, category donut | | app.js | ~100 lines | Command palette, keyboard shortcuts, DnD wiring | ## Styling Plain CSS (no Tailwind, no build step). Same dark design system ported from the React app: - CSS custom properties for all design tokens (`--color-page`, `--color-surface`, `--color-accent`, etc.) - Same animations: `animate-pulse-glow`, `animate-live-dot`, `card-glow` - Custom scrollbars, selection highlight, grain texture ## Page-by-Page Design ### Board Page - 4 Kanban columns rendered server-side as partials - **Drag-and-drop:** SortableJS on each column; `onEnd` callback reads task ID + target column status, fires htmx request: - Pending → Active: `PUT /board/tasks/{id}/start` - Active → Paused: `PUT /board/tasks/{id}/pause` - Paused → Active: `PUT /board/tasks/{id}/resume` - Any → Completed: `PUT /board/tasks/{id}/complete` - Server returns updated column partials - **Task detail panel:** `hx-get="/board/tasks/{id}/detail"` loads partial into right-side container; CSS transition handles slide-in - **Inline editing:** Click-to-edit fields use `hx-put` on blur/Enter - **Subtasks/notes:** Rendered as partials inside detail panel; htmx handles add/complete - **Filter bar:** htmx re-requests board with query params (`?category=Development&hasSubtasks=true`) - **Create task:** Inline form in Pending column, htmx POST ### Analytics Page - Stat cards rendered server-side (open tasks count, active time, top category) - **Charts:** Chart.js renders from JSON data serialized into `