From e04e9573ea2b4ee8bf0b37043e56100ee1a8f260 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 1 Mar 2026 21:47:08 -0500 Subject: [PATCH] docs: add Razor Pages migration design 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 --- ...2026-03-01-razor-pages-migration-design.md | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 docs/plans/2026-03-01-razor-pages-migration-design.md diff --git a/docs/plans/2026-03-01-razor-pages-migration-design.md b/docs/plans/2026-03-01-razor-pages-migration-design.md new file mode 100644 index 0000000..f540aae --- /dev/null +++ b/docs/plans/2026-03-01-razor-pages-migration-design.md @@ -0,0 +1,160 @@ +# 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 `