Design for moving the TaskTracker web UI from React/npm to Razor Pages + htmx, eliminating the Node toolchain and unifying on a single .NET stack with one-binary deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.8 KiB
6.8 KiB
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
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;
onEndcallback 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
- Pending → Active:
- 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-puton 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
<script>tags by the server- Timeline: bar chart with events bucketed by hour, colored by category
- Category breakdown: donut chart with legend
- Activity feed: Initial batch server-rendered; "Load more" button uses htmx to append items
Mappings Page
- Table rows rendered server-side
- Inline edit: Click "Edit" → htmx swaps row with editable form row partial
- Add new: htmx inserts empty form row at top
- Delete:
hx-deletewithhx-confirmfor confirmation
Search Modal (Ctrl+K)
app.jshandles keyboard shortcut and modal open/close- On keystroke: htmx fetches
/board/search?q={query}, swaps results list - Arrow key navigation + Enter-to-select in JS (~80 lines)
- When empty: shows recent Active/Paused/Pending tasks
htmx Endpoint Design
Razor Pages handler methods return HTML partials (not JSON). Examples:
| Endpoint | Method | Returns |
|---|---|---|
/board |
GET | Full board page |
/board?handler=Column&status=Active |
GET | Single column partial |
/board?handler=TaskDetail&id=5 |
GET | Detail panel partial |
/board?handler=Search&q=auth |
GET | Search results partial |
/board?handler=Start&id=5 |
PUT | Updated board columns |
/board?handler=Pause&id=5 |
PUT | Updated board columns |
/board?handler=Resume&id=5 |
PUT | Updated board columns |
/board?handler=Complete&id=5 |
PUT | Updated board columns |
/board?handler=CreateTask |
POST | Updated Pending column |
/board?handler=UpdateTask&id=5 |
PUT | Updated task card |
/board?handler=AddSubtask&id=5 |
POST | Updated subtask list |
/board?handler=AddNote&id=5 |
POST | Updated notes list |
/analytics |
GET | Full analytics page |
/analytics?handler=ActivityFeed&offset=20 |
GET | More activity items |
/mappings |
GET | Full mappings page |
/mappings?handler=EditRow&id=3 |
GET | Editable row partial |
/mappings?handler=Save |
POST/PUT | Updated row partial |
/mappings?handler=Delete&id=3 |
DELETE | Empty (row removed) |
What Gets Removed
TaskTracker.Web/directory (React app: node_modules, package.json, src/, dist/, etc.)UseDefaultFiles()in Program.cs (Razor Pages handle routing)- CORS configuration becomes optional (UI is same-origin)
What Stays Unchanged
- All API controllers (
/api/*) — used by MCP, WindowWatcher, Chrome extension TaskTracker.Core— entities, DTOs, interfacesTaskTracker.Infrastructure— EF Core, repositories, migrationsTaskTracker.MCP— MCP serverWindowWatcher— context watcher service