chore: initial commit of TaskTracker project

Existing ASP.NET API with vanilla JS SPA, WindowWatcher, Chrome extension, and MCP server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 22:08:45 -05:00
commit e12f78c479
66 changed files with 5170 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
# TaskTracker Web UI Redesign
**Date**: 2026-02-26
**Status**: Approved
## Overview
Ground-up redesign of the TaskTracker web UI. Replace the current vanilla JS SPA in `wwwroot/` with a React-based Kanban board application in a separate `TaskTracker.Web` project.
## Tech Stack
- **Framework**: React 19, TypeScript
- **Build**: Vite
- **Styling**: Tailwind CSS
- **Drag-and-drop**: @dnd-kit/core + @dnd-kit/sortable
- **Data fetching**: @tanstack/react-query
- **Routing**: react-router-dom
- **Charts**: Recharts
- **HTTP**: Axios
- **Icons**: lucide-react
- **Font**: Inter
Dev server on port 5173, proxies `/api` to `localhost:5200`.
## Project Structure
```
TaskTracker.Web/
├── index.html
├── package.json
├── vite.config.ts
├── tailwind.config.ts
├── tsconfig.json
├── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── api/
│ │ ├── client.ts # Axios wrapper → localhost:5200
│ │ ├── tasks.ts # useTasksQuery, useStartTask, etc.
│ │ ├── context.ts # useContextSummary, useRecentEvents
│ │ └── mappings.ts # useMappings, useCreateMapping
│ ├── components/
│ │ ├── Layout.tsx # Sidebar + top bar + content area
│ │ ├── KanbanBoard.tsx
│ │ ├── KanbanColumn.tsx
│ │ ├── TaskCard.tsx
│ │ ├── TaskModal.tsx
│ │ ├── SearchBar.tsx
│ │ └── ...
│ ├── pages/
│ │ ├── Board.tsx # Kanban board (main view)
│ │ ├── Analytics.tsx # Context tracking visualizations
│ │ ├── Mappings.tsx # App-to-category mapping management
│ │ └── Settings.tsx # (future)
│ ├── types/ # TypeScript types matching API DTOs
│ └── lib/ # Utilities, constants, theme tokens
└── public/
```
## Pages & Features
### Kanban Board (main page)
Four columns: Pending, Active, Paused, Completed.
**Drag-and-drop behavior**:
- Pending → Active: calls `PUT /tasks/{id}/start` (auto-pauses any active task)
- Active → Paused: calls `PUT /tasks/{id}/pause`
- Paused → Active: calls `PUT /tasks/{id}/resume`
- Any → Completed: calls `PUT /tasks/{id}/complete`
- Dragging to Pending from other columns is blocked
- Optimistic updates via TanStack Query; rolls back on API error
**Task card displays**:
- Title
- Category badge (color-coded)
- Elapsed time (live-updating for active task)
- Progress bar (actual vs estimated time, if estimate set)
- Subtask count indicator (e.g., "2/4 subtasks done")
- Active task gets a pulsing cyan accent border
**"+ Add Task"** button in Pending column opens quick-create form.
**Card click** opens the Task Detail Panel.
**Filter bar** below board header:
- Category filter chips
- Date range picker
- "Has subtasks" toggle
- Filters are additive (AND), shown as dismissible chips
### Task Detail Panel
Slide-over panel from the right (~400px), board visible behind (dimmed).
**Sections**:
- **Header**: Title (inline editable), status, category dropdown
- **Description**: Inline editable text area
- **Time**: Elapsed display, estimate input field, progress bar (actual/estimated)
- **Subtasks**: Checklist of child tasks. Check to complete, "+" to add new subtask (creates child task via API)
- **Notes**: Chronological list with type badges (Pause, Resume, General). "+" for inline add
- **Actions**: Context-aware buttons (Start/Pause/Resume/Complete/Abandon)
Close with Escape or click outside.
### Analytics Page
Three visualization sections, filterable by time range and task.
**Filters** (top bar):
- Time range: Today, Last 7 days, Last 30 days, Custom
- Task filter: All Tasks or specific task
**Timeline**:
- Horizontal swim lane showing app usage blocks, color-coded by category
- Task lifecycle markers (started/paused/resumed/completed) as annotations
- Hover shows app name, window title, duration
**Category Breakdown**:
- Donut chart + horizontal bar list with time and percentage per category
- Colors match category badges
**Activity Feed**:
- Reverse-chronological log of context events
- Each row: colored dot, timestamp, app name, window title/URL
- Task lifecycle events interleaved
- Paginated with "Load more"
### Mappings Page
CRUD table for app-to-category rules.
- Columns: Pattern, Match Type, Category (color badge), Friendly Name, Edit/Delete actions
- "+ Add Rule" opens inline form row at top
- Inline editing on existing rows
### Search
Global search bar in top bar, always visible.
- Client-side search over task titles and descriptions
- Results as dropdown below search bar
- Keyboard navigable (arrows, Enter, Escape)
## New Features (require API changes)
- **Time estimates**: New field on WorkTask entity for estimated duration. Progress bar = elapsed / estimated.
- **Subtask progress rollup**: Parent task cards show child completion count (may be client-side calculation from existing data).
## Visual Design
**Palette**:
- Background: `#0f1117` (deep charcoal)
- Surface/cards: `#1a1d27`
- Accent primary: `#6366f1` (electric indigo)
- Accent secondary: `#06b6d4` (vivid cyan)
- Success: `#10b981` (emerald)
- Warning: `#f59e0b` (amber)
- Danger: `#f43f5e` (rose)
- Per-category saturated colors: dev=indigo, research=cyan, comms=violet, devops=orange
**Typography**:
- Font: Inter
- Body: 13px / Medium (500)
- Labels: 11px
- Headings: 18px / Semibold (600)
**Visual details**:
- Subtle gradient on sidebar
- Colored left border on task cards (category color)
- Active task: soft glowing cyan border animation
- Column headers: subtle colored underline per status
- Smooth transitions on drag, hover, panel open/close
- Colored-tint shadows for depth
**Layout**:
- Sidebar: ~60px collapsed, ~200px expanded, icon + label nav
- Top bar: search, minimal
- Board: full remaining width, columns flex evenly
- Detail panel: ~400px, slides from right, overlays board
## API Endpoints Used
| Endpoint | Used By |
|----------|---------|
| `GET /api/tasks` | Board, Search |
| `GET /api/tasks/active` | Board (active indicator) |
| `GET /api/tasks/{id}` | Detail panel |
| `POST /api/tasks` | New task, new subtask |
| `PUT /api/tasks/{id}/start` | Drag to Active |
| `PUT /api/tasks/{id}/pause` | Drag to Paused |
| `PUT /api/tasks/{id}/resume` | Drag to Active from Paused |
| `PUT /api/tasks/{id}/complete` | Drag to Completed, subtask checkbox |
| `DELETE /api/tasks/{id}` | Abandon button |
| `GET /api/tasks/{taskId}/notes` | Detail panel notes |
| `POST /api/tasks/{taskId}/notes` | Add note |
| `GET /api/context/summary` | Analytics category breakdown |
| `GET /api/context/recent` | Analytics timeline + feed |
| `GET /api/mappings` | Mappings page, category colors |
| `POST /api/mappings` | Add mapping |
| `PUT /api/mappings/{id}` | Edit mapping |
| `DELETE /api/mappings/{id}` | Delete mapping |

View File

@@ -0,0 +1,965 @@
# TaskTracker Web UI Redesign — Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Replace the vanilla JS SPA with a React Kanban board app featuring rich analytics, subtask management, time estimates, and search/filters.
**Architecture:** Separate Vite+React project (`TaskTracker.Web`) calling the existing ASP.NET API at `localhost:5200`. Two small API changes needed first (time estimate field, task update endpoint). Frontend uses TanStack Query for server state, dnd-kit for drag-and-drop, Recharts for analytics.
**Tech Stack:** React 19, TypeScript, Vite, Tailwind CSS, @dnd-kit, @tanstack/react-query, react-router-dom, Recharts, Axios, lucide-react
**Design doc:** `docs/plans/2026-02-26-web-ui-redesign-design.md`
---
## Task 1: API — Add EstimatedMinutes field and Update endpoint
The React UI needs two things the API doesn't have yet: a time estimate field on tasks, and a general-purpose update endpoint for inline editing.
**Files:**
- Modify: `TaskTracker.Core/Entities/WorkTask.cs`
- Modify: `TaskTracker.Core/DTOs/CreateTaskRequest.cs`
- Create: `TaskTracker.Core/DTOs/UpdateTaskRequest.cs`
- Modify: `TaskTracker.Api/Controllers/TasksController.cs`
- Modify: `TaskTracker.Infrastructure/Data/AppDbContext.cs` (if migration needed)
**Step 1: Add EstimatedMinutes to WorkTask entity**
In `TaskTracker.Core/Entities/WorkTask.cs`, add:
```csharp
public int? EstimatedMinutes { get; set; }
```
**Step 2: Add EstimatedMinutes to CreateTaskRequest**
In `TaskTracker.Core/DTOs/CreateTaskRequest.cs`, add:
```csharp
public int? EstimatedMinutes { get; set; }
```
**Step 3: Create UpdateTaskRequest DTO**
Create `TaskTracker.Core/DTOs/UpdateTaskRequest.cs`:
```csharp
namespace TaskTracker.Core.DTOs;
public class UpdateTaskRequest
{
public string? Title { get; set; }
public string? Description { get; set; }
public string? Category { get; set; }
public int? EstimatedMinutes { get; set; }
}
```
**Step 4: Wire EstimatedMinutes in TasksController.Create**
In `TasksController.cs`, update the `Create` method's `new WorkTask` block to include:
```csharp
EstimatedMinutes = request.EstimatedMinutes,
```
**Step 5: Add PUT update endpoint to TasksController**
Add to `TasksController.cs`:
```csharp
[HttpPut("{id:int}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateTaskRequest request)
{
var task = await taskRepo.GetByIdAsync(id);
if (task is null)
return NotFound(ApiResponse.Fail("Task not found"));
if (request.Title is not null) task.Title = request.Title;
if (request.Description is not null) task.Description = request.Description;
if (request.Category is not null) task.Category = request.Category;
if (request.EstimatedMinutes.HasValue) task.EstimatedMinutes = request.EstimatedMinutes;
await taskRepo.UpdateAsync(task);
return Ok(ApiResponse<WorkTask>.Ok(task));
}
```
**Step 6: Create and apply EF migration**
Run:
```bash
cd TaskTracker.Infrastructure
dotnet ef migrations add AddEstimatedMinutes --startup-project ../TaskTracker.Api
dotnet ef database update --startup-project ../TaskTracker.Api
```
**Step 7: Verify with Swagger**
Run the API (`dotnet run --project TaskTracker.Api`) and test the new PUT endpoint at `/swagger`.
**Step 8: Commit**
```bash
git add TaskTracker.Core/Entities/WorkTask.cs TaskTracker.Core/DTOs/CreateTaskRequest.cs TaskTracker.Core/DTOs/UpdateTaskRequest.cs TaskTracker.Api/Controllers/TasksController.cs TaskTracker.Infrastructure/
git commit -m "feat: add EstimatedMinutes field and general PUT update endpoint for tasks"
```
---
## Task 2: Scaffold React project with Vite + Tailwind
**Files:**
- Create: `TaskTracker.Web/` (entire project scaffold)
**Step 1: Create Vite React TypeScript project**
```bash
cd C:/Users/AJ/Desktop/Projects/TaskTracker
npm create vite@latest TaskTracker.Web -- --template react-ts
```
**Step 2: Install dependencies**
```bash
cd TaskTracker.Web
npm install axios @tanstack/react-query react-router-dom @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities recharts lucide-react
npm install -D tailwindcss @tailwindcss/vite
```
**Step 3: Configure Tailwind**
Replace `TaskTracker.Web/src/index.css` with:
```css
@import "tailwindcss";
```
Add Tailwind plugin to `TaskTracker.Web/vite.config.ts`:
```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:5200',
changeOrigin: true,
},
},
},
})
```
**Step 4: Clean up scaffold**
- Delete `src/App.css`, `src/assets/`
- Replace `src/App.tsx` with a minimal placeholder:
```tsx
function App() {
return <div className="bg-[#0f1117] min-h-screen text-white p-8">
<h1 className="text-2xl font-semibold">TaskTracker</h1>
</div>
}
export default App
```
- Replace `src/main.tsx`:
```tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
```
**Step 5: Verify it runs**
```bash
npm run dev
```
Open `http://localhost:5173` — should see "TaskTracker" on a dark background.
**Step 6: Commit**
```bash
git add TaskTracker.Web/
git commit -m "feat: scaffold TaskTracker.Web with Vite, React, TypeScript, Tailwind"
```
---
## Task 3: TypeScript types + API client + TanStack Query provider
**Files:**
- Create: `TaskTracker.Web/src/types/index.ts`
- Create: `TaskTracker.Web/src/api/client.ts`
- Create: `TaskTracker.Web/src/api/tasks.ts`
- Create: `TaskTracker.Web/src/api/context.ts`
- Create: `TaskTracker.Web/src/api/mappings.ts`
- Modify: `TaskTracker.Web/src/main.tsx`
**Step 1: Create TypeScript types matching the API**
Create `TaskTracker.Web/src/types/index.ts`:
```typescript
export enum WorkTaskStatus {
Pending = 0,
Active = 1,
Paused = 2,
Completed = 3,
Abandoned = 4,
}
export enum NoteType {
PauseNote = 0,
ResumeNote = 1,
General = 2,
}
export interface WorkTask {
id: number
title: string
description: string | null
status: WorkTaskStatus
category: string | null
createdAt: string
startedAt: string | null
completedAt: string | null
estimatedMinutes: number | null
parentTaskId: number | null
subTasks: WorkTask[]
notes: TaskNote[]
contextEvents: ContextEvent[]
}
export interface TaskNote {
id: number
workTaskId: number
content: string
type: NoteType
createdAt: string
}
export interface ContextEvent {
id: number
workTaskId: number | null
source: string
appName: string
windowTitle: string
url: string | null
timestamp: string
}
export interface AppMapping {
id: number
pattern: string
matchType: string
category: string
friendlyName: string | null
}
export interface ContextSummaryItem {
appName: string
category: string
eventCount: number
firstSeen: string
lastSeen: string
}
export interface ApiResponse<T> {
success: boolean
data: T
error: string | null
}
```
**Step 2: Create Axios client**
Create `TaskTracker.Web/src/api/client.ts`:
```typescript
import axios from 'axios'
import type { ApiResponse } from '../types'
const api = axios.create({ baseURL: '/api' })
export async function request<T>(config: Parameters<typeof api.request>[0]): Promise<T> {
const { data } = await api.request<ApiResponse<T>>(config)
if (!data.success) throw new Error(data.error ?? 'API error')
return data.data
}
export default api
```
**Step 3: Create task API hooks**
Create `TaskTracker.Web/src/api/tasks.ts`:
```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { request } from './client'
import type { WorkTask } from '../types'
export function useTasks(includeSubTasks = true) {
return useQuery({
queryKey: ['tasks', { includeSubTasks }],
queryFn: () => request<WorkTask[]>({ url: '/tasks', params: { includeSubTasks } }),
})
}
export function useActiveTask() {
return useQuery({
queryKey: ['tasks', 'active'],
queryFn: () => request<WorkTask | null>({ url: '/tasks/active' }),
refetchInterval: 30_000,
})
}
export function useTask(id: number) {
return useQuery({
queryKey: ['tasks', id],
queryFn: () => request<WorkTask>({ url: `/tasks/${id}` }),
})
}
function useInvalidateTasks() {
const qc = useQueryClient()
return () => {
qc.invalidateQueries({ queryKey: ['tasks'] })
}
}
export function useCreateTask() {
const invalidate = useInvalidateTasks()
return useMutation({
mutationFn: (body: { title: string; description?: string; category?: string; parentTaskId?: number; estimatedMinutes?: number }) =>
request<WorkTask>({ method: 'POST', url: '/tasks', data: body }),
onSuccess: invalidate,
})
}
export function useUpdateTask() {
const invalidate = useInvalidateTasks()
return useMutation({
mutationFn: ({ id, ...body }: { id: number; title?: string; description?: string; category?: string; estimatedMinutes?: number }) =>
request<WorkTask>({ method: 'PUT', url: `/tasks/${id}`, data: body }),
onSuccess: invalidate,
})
}
export function useStartTask() {
const invalidate = useInvalidateTasks()
return useMutation({
mutationFn: (id: number) => request<WorkTask>({ method: 'PUT', url: `/tasks/${id}/start` }),
onSuccess: invalidate,
})
}
export function usePauseTask() {
const invalidate = useInvalidateTasks()
return useMutation({
mutationFn: ({ id, note }: { id: number; note?: string }) =>
request<WorkTask>({ method: 'PUT', url: `/tasks/${id}/pause`, data: { note } }),
onSuccess: invalidate,
})
}
export function useResumeTask() {
const invalidate = useInvalidateTasks()
return useMutation({
mutationFn: ({ id, note }: { id: number; note?: string }) =>
request<WorkTask>({ method: 'PUT', url: `/tasks/${id}/resume`, data: { note } }),
onSuccess: invalidate,
})
}
export function useCompleteTask() {
const invalidate = useInvalidateTasks()
return useMutation({
mutationFn: (id: number) => request<WorkTask>({ method: 'PUT', url: `/tasks/${id}/complete` }),
onSuccess: invalidate,
})
}
export function useAbandonTask() {
const invalidate = useInvalidateTasks()
return useMutation({
mutationFn: (id: number) => request<void>({ method: 'DELETE', url: `/tasks/${id}` }),
onSuccess: invalidate,
})
}
```
**Step 4: Create context API hooks**
Create `TaskTracker.Web/src/api/context.ts`:
```typescript
import { useQuery } from '@tanstack/react-query'
import { request } from './client'
import type { ContextEvent, ContextSummaryItem } from '../types'
export function useRecentContext(minutes = 30) {
return useQuery({
queryKey: ['context', 'recent', minutes],
queryFn: () => request<ContextEvent[]>({ url: '/context/recent', params: { minutes } }),
refetchInterval: 60_000,
})
}
export function useContextSummary() {
return useQuery({
queryKey: ['context', 'summary'],
queryFn: () => request<ContextSummaryItem[]>({ url: '/context/summary' }),
refetchInterval: 60_000,
})
}
```
**Step 5: Create mappings API hooks**
Create `TaskTracker.Web/src/api/mappings.ts`:
```typescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { request } from './client'
import type { AppMapping } from '../types'
export function useMappings() {
return useQuery({
queryKey: ['mappings'],
queryFn: () => request<AppMapping[]>({ url: '/mappings' }),
})
}
export function useCreateMapping() {
const qc = useQueryClient()
return useMutation({
mutationFn: (body: { pattern: string; matchType: string; category: string; friendlyName?: string }) =>
request<AppMapping>({ method: 'POST', url: '/mappings', data: body }),
onSuccess: () => qc.invalidateQueries({ queryKey: ['mappings'] }),
})
}
export function useUpdateMapping() {
const qc = useQueryClient()
return useMutation({
mutationFn: ({ id, ...body }: { id: number; pattern: string; matchType: string; category: string; friendlyName?: string }) =>
request<AppMapping>({ method: 'PUT', url: `/mappings/${id}`, data: body }),
onSuccess: () => qc.invalidateQueries({ queryKey: ['mappings'] }),
})
}
export function useDeleteMapping() {
const qc = useQueryClient()
return useMutation({
mutationFn: (id: number) => request<void>({ method: 'DELETE', url: `/mappings/${id}` }),
onSuccess: () => qc.invalidateQueries({ queryKey: ['mappings'] }),
})
}
```
**Step 6: Add QueryClientProvider to main.tsx**
```tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import './index.css'
import App from './App'
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 10_000, retry: 1 } },
})
createRoot(document.getElementById('root')!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</StrictMode>,
)
```
**Step 7: Verify compilation**
```bash
cd TaskTracker.Web && npm run build
```
Expected: clean build with no errors.
**Step 8: Commit**
```bash
git add TaskTracker.Web/src/types/ TaskTracker.Web/src/api/ TaskTracker.Web/src/main.tsx
git commit -m "feat: add TypeScript types, API client, and TanStack Query hooks"
```
---
## Task 4: Layout shell — sidebar, top bar, routing
**Files:**
- Create: `TaskTracker.Web/src/components/Layout.tsx`
- Create: `TaskTracker.Web/src/pages/Board.tsx` (placeholder)
- Create: `TaskTracker.Web/src/pages/Analytics.tsx` (placeholder)
- Create: `TaskTracker.Web/src/pages/Mappings.tsx` (placeholder)
- Modify: `TaskTracker.Web/src/App.tsx`
**Step 1: Create Layout component**
Create `TaskTracker.Web/src/components/Layout.tsx` with:
- Collapsible sidebar (60px collapsed, 200px expanded) with gradient background
- Nav items: Board (LayoutGrid icon), Analytics (BarChart3 icon), Mappings (Link icon)
- Top bar with app title and SearchBar placeholder
- `<Outlet />` for page content
- Use `lucide-react` for icons
- Use `react-router-dom` `NavLink` with active styling (indigo highlight)
**Step 2: Create placeholder pages**
Each page as a simple component with just a heading, e.g.:
```tsx
export default function Board() {
return <div><h1 className="text-xl font-semibold text-white">Board</h1></div>
}
```
**Step 3: Wire up routing in App.tsx**
```tsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import Layout from './components/Layout'
import Board from './pages/Board'
import Analytics from './pages/Analytics'
import Mappings from './pages/Mappings'
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Navigate to="/board" replace />} />
<Route path="/board" element={<Board />} />
<Route path="/analytics" element={<Analytics />} />
<Route path="/mappings" element={<Mappings />} />
</Route>
</Routes>
</BrowserRouter>
)
}
```
**Step 4: Verify navigation works**
Run `npm run dev`. Click sidebar links — URL changes and placeholder pages render.
**Step 5: Commit**
```bash
git add TaskTracker.Web/src/
git commit -m "feat: add layout shell with sidebar navigation and routing"
```
---
## Task 5: Kanban board — columns, cards, drag-and-drop
This is the core feature. Build it incrementally.
**Files:**
- Create: `TaskTracker.Web/src/lib/constants.ts` (colors, status labels)
- Create: `TaskTracker.Web/src/components/KanbanColumn.tsx`
- Create: `TaskTracker.Web/src/components/TaskCard.tsx`
- Create: `TaskTracker.Web/src/components/KanbanBoard.tsx`
- Modify: `TaskTracker.Web/src/pages/Board.tsx`
**Step 1: Create constants for status/category config**
Create `TaskTracker.Web/src/lib/constants.ts`:
```typescript
import { WorkTaskStatus } from '../types'
export const COLUMN_CONFIG = [
{ status: WorkTaskStatus.Pending, label: 'Pending', color: '#94a3b8' },
{ status: WorkTaskStatus.Active, label: 'Active', color: '#06b6d4' },
{ status: WorkTaskStatus.Paused, label: 'Paused', color: '#f59e0b' },
{ status: WorkTaskStatus.Completed, label: 'Completed', color: '#10b981' },
] as const
export const CATEGORY_COLORS: Record<string, string> = {
Development: '#6366f1',
Research: '#06b6d4',
Communication: '#8b5cf6',
DevOps: '#f97316',
Documentation: '#14b8a6',
Design: '#ec4899',
Unknown: '#64748b',
}
```
**Step 2: Build TaskCard component**
Create `TaskTracker.Web/src/components/TaskCard.tsx`:
- Renders task title, category badge (color-coded from `CATEGORY_COLORS`), elapsed time
- If `estimatedMinutes` set, show progress bar (elapsed / estimated)
- If task has subtasks, show "N/M done" indicator
- Active task gets a pulsing cyan border (`animate-pulse` or custom keyframe)
- Colored left border matching category
- Uses `useSortable` from dnd-kit for drag handle
- `onClick` prop to open detail panel
**Step 3: Build KanbanColumn component**
Create `TaskTracker.Web/src/components/KanbanColumn.tsx`:
- Column header with status label + count + colored underline
- Uses `useDroppable` from dnd-kit
- Renders list of `TaskCard` components
- "Pending" column has "+ Add Task" button at bottom
- Visual drop indicator when dragging over
**Step 4: Build KanbanBoard component**
Create `TaskTracker.Web/src/components/KanbanBoard.tsx`:
- `DndContext` + `SortableContext` from dnd-kit wrapping the columns
- `useTasks()` hook to fetch all tasks
- Groups tasks by status into columns
- `onDragEnd` handler that:
- Determines source/target column
- Maps column transitions to API calls (start/pause/resume/complete)
- Blocks invalid transitions (e.g., moving to Pending)
- Shows error toast on failure
- Filters out subtasks from board (only show top-level tasks, `parentTaskId === null`)
**Step 5: Wire Board page**
Update `TaskTracker.Web/src/pages/Board.tsx` to render `<KanbanBoard />` and pass an `onTaskClick` callback that opens the detail panel (wired in Task 6).
**Step 6: Verify board renders with real data**
Start the API (`dotnet run --project TaskTracker.Api`), then `npm run dev`. Create a few tasks via Swagger, verify they show in the correct columns. Test drag-and-drop transitions.
**Step 7: Commit**
```bash
git add TaskTracker.Web/src/
git commit -m "feat: implement Kanban board with drag-and-drop task management"
```
---
## Task 6: Task detail slide-over panel
**Files:**
- Create: `TaskTracker.Web/src/components/TaskDetailPanel.tsx`
- Create: `TaskTracker.Web/src/components/SubtaskList.tsx`
- Create: `TaskTracker.Web/src/components/NotesList.tsx`
- Modify: `TaskTracker.Web/src/pages/Board.tsx`
**Step 1: Build TaskDetailPanel**
Create `TaskTracker.Web/src/components/TaskDetailPanel.tsx`:
- Slide-over panel from right (~400px), board dimmed behind with semi-transparent overlay
- Fetches task detail via `useTask(id)`
- Sections:
- **Header**: Title (click to edit inline, blur to save via `useUpdateTask`), status badge, category dropdown
- **Description**: Click-to-edit text area, saves on blur
- **Time**: Show elapsed time. Input for estimated minutes, saves on blur. Progress bar if estimate set.
- **Subtasks**: `<SubtaskList />` component
- **Notes**: `<NotesList />` component
- **Actions**: Context-aware buttons — Start/Pause/Resume/Complete/Abandon based on current status
- Close on Escape keypress or overlay click
- Animate slide-in/out with CSS transition
**Step 2: Build SubtaskList**
Create `TaskTracker.Web/src/components/SubtaskList.tsx`:
- Renders `task.subTasks` as checkboxes
- Checking a subtask calls `useCompleteTask()` on it
- "+" button at top shows inline text input → `useCreateTask()` with `parentTaskId`
- Shows subtask status (completed = strikethrough + checkmark)
**Step 3: Build NotesList**
Create `TaskTracker.Web/src/components/NotesList.tsx`:
- Renders `task.notes` chronologically
- Each note shows: type badge (PauseNote/ResumeNote/General), content, relative timestamp
- "+" button shows inline text input → `POST /api/tasks/{id}/notes` with type General
**Step 4: Wire panel into Board page**
In `Board.tsx`:
- Add `selectedTaskId` state
- Pass `onTaskClick={(id) => setSelectedTaskId(id)}` to `KanbanBoard`
- Render `<TaskDetailPanel taskId={selectedTaskId} onClose={() => setSelectedTaskId(null)} />` when set
**Step 5: Verify full workflow**
Test: click card → panel opens → edit title → add subtask → add note → change status → close panel. All changes persist.
**Step 6: Commit**
```bash
git add TaskTracker.Web/src/
git commit -m "feat: add task detail slide-over panel with inline editing, subtasks, and notes"
```
---
## Task 7: Create task form
**Files:**
- Create: `TaskTracker.Web/src/components/CreateTaskForm.tsx`
- Modify: `TaskTracker.Web/src/components/KanbanColumn.tsx`
**Step 1: Build CreateTaskForm**
Create `TaskTracker.Web/src/components/CreateTaskForm.tsx`:
- Inline form that expands in the Pending column when "+ Add Task" is clicked
- Fields: Title (required), Description (optional textarea), Category (dropdown from known categories), Estimated Minutes (optional number)
- Submit calls `useCreateTask()`
- Escape or click away cancels
- Auto-focus title input on open
**Step 2: Wire into Pending column**
Modify `KanbanColumn.tsx`:
- When column is Pending, show "+ Add Task" button
- On click, toggle showing `<CreateTaskForm />` at bottom of column
- On submit/cancel, hide the form
**Step 3: Verify**
Create a task via the inline form. It should appear in the Pending column immediately.
**Step 4: Commit**
```bash
git add TaskTracker.Web/src/
git commit -m "feat: add inline create task form in Pending column"
```
---
## Task 8: Search bar + board filters
**Files:**
- Create: `TaskTracker.Web/src/components/SearchBar.tsx`
- Create: `TaskTracker.Web/src/components/FilterBar.tsx`
- Modify: `TaskTracker.Web/src/components/Layout.tsx`
- Modify: `TaskTracker.Web/src/pages/Board.tsx`
**Step 1: Build SearchBar**
Create `TaskTracker.Web/src/components/SearchBar.tsx`:
- Input with search icon (lucide `Search`)
- On typing, filters tasks client-side (title + description match)
- Results shown as dropdown list of matching tasks
- Click result → opens TaskDetailPanel (via callback prop)
- Keyboard navigation: arrow keys to move, Enter to select, Escape to close
- Debounce input by 200ms
**Step 2: Build FilterBar**
Create `TaskTracker.Web/src/components/FilterBar.tsx`:
- Row of filter chips rendered below the board header
- Category chips: derived from unique categories across all tasks + mappings
- "Has subtasks" toggle chip
- Active filters shown as colored chips with "x" to dismiss
- Exposes `filters` state and `filteredTasks` computation to parent
**Step 3: Wire SearchBar into Layout**
Add `<SearchBar />` to the top bar in `Layout.tsx`. It needs access to task data — use `useTasks()` inside the component.
**Step 4: Wire FilterBar into Board page**
In `Board.tsx`, add `<FilterBar />` above the `<KanbanBoard />`. Pass filtered tasks down to the board instead of all tasks. Lift task fetching up to `Board.tsx` and pass tasks as props to both `FilterBar` and `KanbanBoard`.
**Step 5: Verify**
- Type in search bar → dropdown shows matching tasks
- Click category chip → board filters to that category
- Combine filters → board shows intersection
**Step 6: Commit**
```bash
git add TaskTracker.Web/src/
git commit -m "feat: add global search bar and board filter chips"
```
---
## Task 9: Analytics page — timeline, charts, activity feed
**Files:**
- Create: `TaskTracker.Web/src/components/analytics/Timeline.tsx`
- Create: `TaskTracker.Web/src/components/analytics/CategoryBreakdown.tsx`
- Create: `TaskTracker.Web/src/components/analytics/ActivityFeed.tsx`
- Modify: `TaskTracker.Web/src/pages/Analytics.tsx`
**Step 1: Build Analytics page shell**
Update `TaskTracker.Web/src/pages/Analytics.tsx`:
- Filter bar at top: time range dropdown (Today/7d/30d), task filter dropdown
- Time range maps to `minutes` param for `/context/recent` (1440 for today, 10080 for 7d, 43200 for 30d)
- Three sections stacked vertically: Timeline, Category Breakdown, Activity Feed
**Step 2: Build Timeline component**
Create `TaskTracker.Web/src/components/analytics/Timeline.tsx`:
- Recharts `BarChart` with custom rendering
- X-axis: time of day (hourly buckets)
- Bars: colored by category (from mappings)
- Tooltip on hover: app name, window title, duration
- Uses `useRecentContext()` data, groups events into time buckets
**Step 3: Build CategoryBreakdown component**
Create `TaskTracker.Web/src/components/analytics/CategoryBreakdown.tsx`:
- Left side: Recharts `PieChart` (donut style with inner radius)
- Right side: list of categories with horizontal bar, time, percentage
- Uses `useContextSummary()` data
- Colors from `CATEGORY_COLORS` constant
**Step 4: Build ActivityFeed component**
Create `TaskTracker.Web/src/components/analytics/ActivityFeed.tsx`:
- Reverse-chronological list of context events
- Each row: colored category dot, relative timestamp, app name (bold), window title/URL
- "Load more" button at bottom (increase `minutes` param or paginate client-side)
- Uses `useRecentContext()` with the selected time range
**Step 5: Verify analytics page**
Need some context data — either run WindowWatcher/Chrome extension to generate real data, or POST a few test events via Swagger. Verify all three visualizations render.
**Step 6: Commit**
```bash
git add TaskTracker.Web/src/
git commit -m "feat: add analytics page with timeline, category breakdown, and activity feed"
```
---
## Task 10: Mappings page
**Files:**
- Modify: `TaskTracker.Web/src/pages/Mappings.tsx`
**Step 1: Build Mappings page**
Update `TaskTracker.Web/src/pages/Mappings.tsx`:
- Table with columns: Pattern, Match Type, Category (color badge), Friendly Name, Actions (edit/delete)
- Uses `useMappings()` to fetch data
- "+ Add Rule" button at top → inserts inline form row at top of table
- Form fields: Pattern (text), Match Type (dropdown: ProcessName/TitleContains/UrlContains), Category (text), Friendly Name (text)
- Submit → `useCreateMapping()`
- Edit button → row becomes editable inline → save → `useUpdateMapping()`
- Delete button → confirm dialog → `useDeleteMapping()`
**Step 2: Verify CRUD**
Create a mapping, edit it, delete it. Verify all operations work.
**Step 3: Commit**
```bash
git add TaskTracker.Web/src/
git commit -m "feat: add mappings page with inline CRUD table"
```
---
## Task 11: Visual polish and animations
**Files:**
- Modify: Various component files for animation/transition polish
- Modify: `TaskTracker.Web/index.html` (add Inter font)
**Step 1: Add Inter font**
In `TaskTracker.Web/index.html`, add to `<head>`:
```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
```
Add to `index.css` (inside `@theme`):
```css
@theme {
--font-sans: 'Inter', sans-serif;
}
```
**Step 2: Add animations**
- Active task card: glowing cyan border pulse (CSS `@keyframes` animation)
- Detail panel: slide-in from right with opacity transition
- Drag-and-drop: smooth card movement via dnd-kit's built-in transitions
- Hover effects: subtle card lift with colored shadow
- Column drop target: border highlight on drag-over
**Step 3: Polish visual details**
- Sidebar gradient background
- Colored left border on task cards
- Category badge colors consistent everywhere
- Column header colored underlines
- Colored-tint shadows on cards (not plain gray)
- Consistent spacing and typography
**Step 4: Verify visual quality**
Walk through every page and interaction. Check dark background rendering, color contrast, animation smoothness.
**Step 5: Commit**
```bash
git add TaskTracker.Web/
git commit -m "feat: add visual polish — Inter font, animations, colored shadows, hover effects"
```
---
## Task 12: Final integration testing and cleanup
**Step 1: Full workflow test**
Run both the API and the React dev server. Walk through the complete workflow:
1. Create a task from the board
2. Start it (drag or button)
3. Add a subtask, complete it
4. Add a note
5. Set time estimate, verify progress bar
6. Pause with note, resume
7. Complete the task
8. Check analytics page
9. Add/edit/delete a mapping
10. Test search and filters
11. Test sidebar collapse/expand
**Step 2: Fix any issues found**
Address bugs or visual issues found during testing.
**Step 3: Clean up**
- Remove any unused imports or dead code
- Ensure no console warnings in browser
- Verify `npm run build` produces a clean production build
**Step 4: Commit**
```bash
git add -A
git commit -m "chore: final cleanup and integration testing"
```