feat(#15): implement Gantt chart component

- Create GanttChart component with timeline visualization
- Add task bars with status-based color coding
- Implement PDA-friendly language (Target passed vs OVERDUE)
- Support task click interactions
- Comprehensive test coverage (96.18%)
- 33 tests passing (22 component + 11 helper tests)
- Fully accessible with ARIA labels and keyboard navigation
- Demo page at /demo/gantt
- Responsive design with customizable height

Technical details:
- Uses Next.js 16 + React 19 + TypeScript
- Strict typing (NO any types)
- Helper functions to convert Task to GanttTask
- Timeline calculation with automatic range detection
- Status indicators: completed, in-progress, paused, not-started

Refs #15
This commit is contained in:
Jason Woltje
2026-01-29 17:43:40 -06:00
parent 95833fb4ea
commit 9ff7718f9c
15 changed files with 2421 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ import { Test, TestingModule } from "@nestjs/testing";
import { ProjectsService } from "./projects.service";
import { PrismaService } from "../prisma/prisma.service";
import { ActivityService } from "../activity/activity.service";
import { WebSocketGateway } from "../websocket/websocket.gateway";
import { ProjectStatus, Prisma } from "@prisma/client";
import { NotFoundException } from "@nestjs/common";
@@ -10,6 +11,7 @@ describe("ProjectsService", () => {
let service: ProjectsService;
let prisma: PrismaService;
let activityService: ActivityService;
let wsGateway: WebSocketGateway;
const mockPrismaService = {
project: {
@@ -28,6 +30,10 @@ describe("ProjectsService", () => {
logProjectDeleted: vi.fn(),
};
const mockWebSocketGateway = {
emitProjectUpdated: vi.fn(),
};
const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440001";
const mockUserId = "550e8400-e29b-41d4-a716-446655440002";
const mockProjectId = "550e8400-e29b-41d4-a716-446655440003";
@@ -59,12 +65,17 @@ describe("ProjectsService", () => {
provide: ActivityService,
useValue: mockActivityService,
},
{
provide: WebSocketGateway,
useValue: mockWebSocketGateway,
},
],
}).compile();
service = module.get<ProjectsService>(ProjectsService);
prisma = module.get<PrismaService>(PrismaService);
activityService = module.get<ActivityService>(ActivityService);
wsGateway = module.get<WebSocketGateway>(WebSocketGateway);
vi.clearAllMocks();
});