diff --git a/src/components/Roadmap.tsx b/src/components/Roadmap.tsx index 897fc99..b3deba5 100644 --- a/src/components/Roadmap.tsx +++ b/src/components/Roadmap.tsx @@ -25,11 +25,17 @@ import { FolderOpen, Trash, PencilSimple, - CalendarBlank + CalendarBlank, + CurrencyDollar, + Users, + Lightning, + ArrowsDownUp, + Link as LinkIcon, + WarningOctagon } from '@phosphor-icons/react' import { Progress } from '@/components/ui/progress' import { toast } from 'sonner' -import type { RoadmapProject, RoadmapObjective, RoadmapMetric, BowlingChartData, StatusType, PriorityType } from '@/types' +import type { RoadmapProject, RoadmapObjective, RoadmapMetric, BowlingChartData, StatusType, PriorityType, Countermeasure } from '@/types' interface XMatrixItem { id: string @@ -43,6 +49,7 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s const [editingProject, setEditingProject] = useState(null) const [isAddingObjective, setIsAddingObjective] = useState(false) const [isAddingMetric, setIsAddingMetric] = useState(false) + const [isAddingCountermeasure, setIsAddingCountermeasure] = useState(false) const [selectedProjectId, setSelectedProjectId] = useState('') const [newProject, setNewProject] = useState>({ name: '', @@ -54,7 +61,18 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s endDate: '', progress: 0, objectives: [], - metrics: [] + metrics: [], + budget: 0, + actualSpend: 0, + dependencies: [], + countermeasures: [] + }) + const [newCountermeasure, setNewCountermeasure] = useState>({ + issue: '', + action: '', + owner: '', + dueDate: '', + status: 'open' }) const [newObjective, setNewObjective] = useState>({ category: 'annual', @@ -91,7 +109,11 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s endDate: newProject.endDate, progress: 0, objectives: [], - metrics: [] + metrics: [], + budget: newProject.budget || 0, + actualSpend: 0, + dependencies: [], + countermeasures: [] } setProjects((prev) => [...prev, project]) @@ -106,7 +128,11 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s endDate: '', progress: 0, objectives: [], - metrics: [] + metrics: [], + budget: 0, + actualSpend: 0, + dependencies: [], + countermeasures: [] }) toast.success('Project created successfully') } @@ -188,6 +214,39 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s toast.success('Metric added to project') } + const handleAddCountermeasure = () => { + if (!newCountermeasure.issue || !newCountermeasure.action || !newCountermeasure.owner || !newCountermeasure.dueDate) { + toast.error('Please fill in all required fields') + return + } + + const countermeasure: Countermeasure = { + id: `cm-${Date.now()}`, + issue: newCountermeasure.issue, + action: newCountermeasure.action, + owner: newCountermeasure.owner, + dueDate: newCountermeasure.dueDate, + status: newCountermeasure.status as 'open' | 'in-progress' | 'completed', + createdAt: new Date().toISOString() + } + + setProjects((prev) => prev.map(p => + p.id === selectedProjectId + ? { ...p, countermeasures: [...(p.countermeasures || []), countermeasure] } + : p + )) + + setIsAddingCountermeasure(false) + setNewCountermeasure({ + issue: '', + action: '', + owner: '', + dueDate: '', + status: 'open' + }) + toast.success('Countermeasure added to project') + } + const handleUpdateProjectProgress = (projectId: string) => { setProjects((prev) => prev.map(p => { if (p.id !== projectId) return p @@ -324,6 +383,16 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s +
+ + setNewProject({ ...newProject, budget: parseFloat(e.target.value) })} + placeholder="0" + /> +
@@ -383,9 +452,27 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s + {project.budget && project.budget > 0 && ( +
+
+ + + Budget Utilization + + + ${(project.actualSpend || 0).toLocaleString()} / ${project.budget.toLocaleString()} + +
+ +
+ )} + -
+
Objectives @@ -422,6 +509,24 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s Add Metric
+
+
+ Countermeasures + {project.countermeasures?.length || 0} +
+ +
{project.objectives.length > 0 && ( @@ -468,6 +573,38 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s
)} + + {project.countermeasures && project.countermeasures.length > 0 && ( + <> + +
+
+ + Active Countermeasures +
+ {project.countermeasures.map((cm) => ( +
+
+
+

{cm.issue}

+

Action: {cm.action}

+
+ + {cm.status.replace('-', ' ')} + +
+

+ Owner: {cm.owner} • Due: {new Date(cm.dueDate).toLocaleDateString()} +

+
+ ))} +
+ + )}
@@ -656,6 +793,77 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s
+ + + + + Add Countermeasure + Define an action to address an issue or risk (Hoshin Kanri PDCA) + +
+
+ +