diff --git a/src/components/Roadmap.tsx b/src/components/Roadmap.tsx index ebfb739..42c768a 100644 --- a/src/components/Roadmap.tsx +++ b/src/components/Roadmap.tsx @@ -40,6 +40,9 @@ interface XMatrixItem { function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], setProjects: (updater: (prev: RoadmapProject[]) => RoadmapProject[]) => void }) { const [isAddingProject, setIsAddingProject] = useState(false) const [editingProject, setEditingProject] = useState(null) + const [isAddingObjective, setIsAddingObjective] = useState(false) + const [isAddingMetric, setIsAddingMetric] = useState(false) + const [selectedProjectId, setSelectedProjectId] = useState('') const [newProject, setNewProject] = useState>({ name: '', description: '', @@ -52,6 +55,23 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s objectives: [], metrics: [] }) + const [newObjective, setNewObjective] = useState>({ + category: 'annual', + description: '', + owner: '', + targetDate: '', + status: 'not-started', + metrics: [] + }) + const [newMetric, setNewMetric] = useState>({ + name: '', + baseline: 0, + current: 0, + target: 0, + unit: '', + frequency: 'monthly', + trend: 'stable' + }) const handleAddProject = () => { if (!newProject.name || !newProject.owner || !newProject.startDate || !newProject.endDate) { @@ -95,6 +115,94 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s toast.success('Project deleted') } + const handleAddObjective = () => { + if (!newObjective.description || !newObjective.owner || !newObjective.targetDate) { + toast.error('Please fill in all required fields') + return + } + + const objective: RoadmapObjective = { + id: `obj-${Date.now()}`, + projectId: selectedProjectId, + category: newObjective.category as 'breakthrough' | 'annual' | 'improvement', + description: newObjective.description, + owner: newObjective.owner, + targetDate: newObjective.targetDate, + status: newObjective.status as StatusType, + metrics: [] + } + + setProjects((prev) => prev.map(p => + p.id === selectedProjectId + ? { ...p, objectives: [...p.objectives, objective] } + : p + )) + + setIsAddingObjective(false) + setNewObjective({ + category: 'annual', + description: '', + owner: '', + targetDate: '', + status: 'not-started', + metrics: [] + }) + toast.success('Objective added to project') + } + + const handleAddMetric = () => { + if (!newMetric.name || !newMetric.unit) { + toast.error('Please fill in all required fields') + return + } + + const metric: RoadmapMetric = { + id: `metric-${Date.now()}`, + name: newMetric.name || '', + baseline: newMetric.baseline || 0, + current: newMetric.current || 0, + target: newMetric.target || 0, + unit: newMetric.unit || '', + frequency: newMetric.frequency as 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'annual', + lastUpdated: new Date().toISOString(), + trend: newMetric.trend as 'improving' | 'stable' | 'declining' + } + + setProjects((prev) => prev.map(p => + p.id === selectedProjectId + ? { ...p, metrics: [...p.metrics, metric] } + : p + )) + + setIsAddingMetric(false) + setNewMetric({ + name: '', + baseline: 0, + current: 0, + target: 0, + unit: '', + frequency: 'monthly', + trend: 'stable' + }) + toast.success('Metric added to project') + } + + const handleUpdateProjectProgress = (projectId: string) => { + setProjects((prev) => prev.map(p => { + if (p.id !== projectId) return p + + const totalMetrics = p.metrics.length + if (totalMetrics === 0) return p + + const totalProgress = p.metrics.reduce((sum, metric) => { + const progress = ((metric.current - metric.baseline) / (metric.target - metric.baseline)) * 100 + return sum + Math.max(0, Math.min(100, progress)) + }, 0) + + return { ...p, progress: Math.round(totalProgress / totalMetrics) } + })) + } + const statusColors = { 'not-started': 'bg-muted text-muted-foreground', 'on-track': 'bg-success/10 text-success border-success/30', @@ -276,22 +384,277 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s -
+
- Objectives: - {project.objectives.length} +
+ Objectives + {project.objectives.length} +
+
- Metrics: - {project.metrics.length} +
+ Metrics + {project.metrics.length} +
+
+ + {project.objectives.length > 0 && ( + <> + +
+
Project Objectives
+ {project.objectives.map((obj) => ( +
+
+

{obj.description}

+ + {obj.category} + +
+

+ Owner: {obj.owner} • Target: {new Date(obj.targetDate).toLocaleDateString()} +

+
+ ))} +
+ + )} + + {project.metrics.length > 0 && ( + <> + +
+
Project Metrics
+ {project.metrics.map((metric) => { + const progressPercent = ((metric.current - metric.baseline) / (metric.target - metric.baseline)) * 100 + return ( +
+
+ {metric.name} + + {metric.current}{metric.unit} / {metric.target}{metric.unit} + +
+ +
+ ) + })} +
+ + )}
))} )} + + + + + Add Objective to Project + Create a strategic objective with measurable outcomes + +
+
+ +