import React, { useState, useMemo } from 'react'; import { Gantt, Task, ViewMode } from 'gantt-task-react'; import { Button, Space, Badge } from 'antd'; import { CalendarOutlined, TeamOutlined, CheckCircleOutlined } from '@ant-design/icons'; import { ProjectPhase, ProjectRoadmap, GanttViewOptions, PhaseModalData } from '../../types/project-roadmap.types'; import PhaseModal from './PhaseModal'; import { useAppSelector } from '../../hooks/useAppSelector'; import { themeWiseColor } from '../../utils/themeWiseColor'; import 'gantt-task-react/dist/index.css'; import './gantt-theme.css'; interface ProjectRoadmapGanttProps { roadmap: ProjectRoadmap; viewOptions?: Partial; onPhaseUpdate?: (phaseId: string, updates: Partial) => void; onTaskUpdate?: (phaseId: string, taskId: string, updates: any) => void; } const ProjectRoadmapGantt: React.FC = ({ roadmap, viewOptions = {}, onPhaseUpdate, onTaskUpdate, }) => { const [selectedPhase, setSelectedPhase] = useState(null); const [isModalVisible, setIsModalVisible] = useState(false); const [viewMode, setViewMode] = useState(ViewMode.Month); // Theme support const themeMode = useAppSelector(state => state.themeReducer.mode); const isDarkMode = themeMode === 'dark'; const defaultViewOptions: GanttViewOptions = { viewMode: 'month', showTasks: true, showMilestones: true, groupByPhase: true, ...viewOptions, }; // Theme-aware colors const ganttColors = useMemo(() => { return { background: themeWiseColor('#ffffff', '#1f2937', themeMode), surface: themeWiseColor('#f8f9fa', '#374151', themeMode), border: themeWiseColor('#e5e7eb', '#4b5563', themeMode), taskBar: themeWiseColor('#3b82f6', '#60a5fa', themeMode), taskBarHover: themeWiseColor('#2563eb', '#93c5fd', themeMode), progressBar: themeWiseColor('#10b981', '#34d399', themeMode), milestone: themeWiseColor('#f59e0b', '#fbbf24', themeMode), criticalPath: themeWiseColor('#ef4444', '#f87171', themeMode), text: { primary: themeWiseColor('#111827', '#f9fafb', themeMode), secondary: themeWiseColor('#6b7280', '#d1d5db', themeMode), }, grid: themeWiseColor('#f3f4f6', '#4b5563', themeMode), today: themeWiseColor('rgba(59, 130, 246, 0.1)', 'rgba(96, 165, 250, 0.2)', themeMode), }; }, [themeMode]); // Convert phases to Gantt tasks const ganttTasks = useMemo(() => { const tasks: Task[] = []; roadmap.phases.forEach((phase, phaseIndex) => { // Add phase as main task with theme-aware colors const phaseTask: Task = { id: phase.id, name: phase.name, start: phase.startDate, end: phase.endDate, progress: phase.progress, type: 'project', styles: { progressColor: themeWiseColor(phase.color, phase.color, themeMode), progressSelectedColor: themeWiseColor(phase.color, phase.color, themeMode), backgroundColor: themeWiseColor(`${phase.color}20`, `${phase.color}30`, themeMode), }, }; tasks.push(phaseTask); // Add phase tasks if enabled if (defaultViewOptions.showTasks) { phase.tasks.forEach((task) => { const ganttTask: Task = { id: task.id, name: task.name, start: task.startDate, end: task.endDate, progress: task.progress, type: 'task', project: phase.id, dependencies: task.dependencies, styles: { progressColor: ganttColors.taskBar, progressSelectedColor: ganttColors.taskBarHover, backgroundColor: themeWiseColor('rgba(59, 130, 246, 0.1)', 'rgba(96, 165, 250, 0.2)', themeMode), }, }; tasks.push(ganttTask); }); } // Add milestones if enabled if (defaultViewOptions.showMilestones) { phase.milestones.forEach((milestone) => { const milestoneTask: Task = { id: milestone.id, name: milestone.name, start: milestone.dueDate, end: milestone.dueDate, progress: milestone.isCompleted ? 100 : 0, type: 'milestone', project: phase.id, styles: { progressColor: milestone.criticalPath ? ganttColors.criticalPath : ganttColors.progressBar, progressSelectedColor: milestone.criticalPath ? ganttColors.criticalPath : ganttColors.progressBar, backgroundColor: milestone.criticalPath ? themeWiseColor('rgba(239, 68, 68, 0.1)', 'rgba(248, 113, 113, 0.2)', themeMode) : themeWiseColor('rgba(16, 185, 129, 0.1)', 'rgba(52, 211, 153, 0.2)', themeMode), }, }; tasks.push(milestoneTask); }); } }); return tasks; }, [roadmap.phases, defaultViewOptions, ganttColors, themeMode]); const handlePhaseClick = (phase: ProjectPhase) => { const taskCount = phase.tasks.length; const completedTaskCount = phase.tasks.filter(task => task.status === 'done').length; const milestoneCount = phase.milestones.length; const completedMilestoneCount = phase.milestones.filter(m => m.isCompleted).length; const teamMembers = [...new Set(phase.tasks.map(task => task.assigneeName).filter(Boolean))]; const phaseModalData: PhaseModalData = { ...phase, taskCount, completedTaskCount, milestoneCount, completedMilestoneCount, teamMembers, }; setSelectedPhase(phaseModalData); setIsModalVisible(true); }; const handleTaskClick = (task: Task) => { // Find the phase this task belongs to const phase = roadmap.phases.find(p => p.tasks.some(t => t.id === task.id) || p.milestones.some(m => m.id === task.id) ); if (phase) { handlePhaseClick(phase); } }; const handleDateChange = (task: Task) => { const phase = roadmap.phases.find(p => p.id === task.id); if (phase && onPhaseUpdate) { onPhaseUpdate(phase.id, { startDate: task.start, endDate: task.end, }); } else if (onTaskUpdate) { const parentPhase = roadmap.phases.find(p => p.tasks.some(t => t.id === task.id) ); if (parentPhase) { onTaskUpdate(parentPhase.id, task.id, { startDate: task.start, endDate: task.end, }); } } }; const handleProgressChange = (task: Task) => { const phase = roadmap.phases.find(p => p.id === task.id); if (phase && onPhaseUpdate) { onPhaseUpdate(phase.id, { progress: task.progress }); } else if (onTaskUpdate) { const parentPhase = roadmap.phases.find(p => p.tasks.some(t => t.id === task.id) ); if (parentPhase) { onTaskUpdate(parentPhase.id, task.id, { progress: task.progress }); } } }; const getStatusColor = (status: string) => { switch (status) { case 'completed': return '#52c41a'; case 'in-progress': return '#1890ff'; case 'on-hold': return '#faad14'; default: return '#d9d9d9'; } }; const columnWidth = viewMode === ViewMode.Year ? 350 : viewMode === ViewMode.Month ? 300 : viewMode === ViewMode.Week ? 250 : 60; return (
{/* Header */}

{roadmap.name}

{roadmap.description && (

{roadmap.description}

)}
{/* Phase Overview */}
{roadmap.phases.map((phase) => (
handlePhaseClick(phase)} >
{phase.name} } />
{phase.startDate.toLocaleDateString()} - {phase.endDate.toLocaleDateString()}
{phase.tasks.length} tasks
{phase.progress}% complete
))}
{/* Gantt Chart */}
{/* Phase Modal */} setIsModalVisible(false)} onUpdate={(updates) => { if (selectedPhase && onPhaseUpdate) { onPhaseUpdate(selectedPhase.id, updates); } }} />
); }; export default ProjectRoadmapGantt;