From c9e667017df6e2e5bf5993d4d07d3e1665795a7d Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 21:08:18 +0000 Subject: [PATCH] Generated by Spark: Ok implement new features from ROADMAP --- PRD.md | 7 + src/components/Roadmap.tsx | 577 +++++++++++++++++++++++++------------ 2 files changed, 406 insertions(+), 178 deletions(-) diff --git a/PRD.md b/PRD.md index 89768c2..4bf4467 100644 --- a/PRD.md +++ b/PRD.md @@ -40,6 +40,13 @@ This is an enterprise-grade platform requiring multiple interconnected views (St - **Progression**: View enterprise scorecard → Select portfolio or initiative → Drill into specific KPI → View trend & targets → Navigate to related initiatives - **Success criteria**: No ambiguity about performance; financial and operational metrics connected; accessible to all stakeholders +### Strategic Roadmap with Hoshin Kanri +- **Functionality**: Comprehensive project planning with objectives, metrics, bowling charts, X-Matrix, and timeline visualization +- **Purpose**: Enable end-to-end strategic planning and execution tracking using proven Hoshin Kanri methodology +- **Trigger**: User navigates to Roadmap tab and accesses sub-views (Projects, Dashboard, Timeline, Objectives, Metrics, Bowling Chart, X-Matrix) +- **Progression**: Create project → Add objectives (breakthrough/annual/improvement) → Define metrics with baseline/current/target → Track progress → View bowling chart → Analyze X-Matrix alignment → Monitor timeline +- **Success criteria**: Complete visibility of strategic execution; objectives linked to measurable metrics; monthly bowling chart shows status at a glance; X-Matrix demonstrates strategic alignment; timeline shows project scheduling and dependencies + ### Strategy-to-Execution Traceability - **Functionality**: Visual relationship mapping showing how initiatives link to strategic goals and outcomes - **Purpose**: Ensure every activity directly supports strategic intent; identify gaps diff --git a/src/components/Roadmap.tsx b/src/components/Roadmap.tsx index 42c768a..897fc99 100644 --- a/src/components/Roadmap.tsx +++ b/src/components/Roadmap.tsx @@ -24,7 +24,8 @@ import { Plus, FolderOpen, Trash, - PencilSimple + PencilSimple, + CalendarBlank } from '@phosphor-icons/react' import { Progress } from '@/components/ui/progress' import { toast } from 'sonner' @@ -659,60 +660,6 @@ function ProjectsView({ projects, setProjects }: { projects: RoadmapProject[], s ) } -const mockBowlingChart: BowlingChartData[] = [ - { - objective: 'Revenue Growth 25%', - months: [ - { month: 'Jan', status: 'green', actual: 102, target: 102 }, - { month: 'Feb', status: 'green', actual: 105, target: 104 }, - { month: 'Mar', status: 'yellow', actual: 107, target: 108 }, - { month: 'Apr', status: 'green', actual: 111, target: 110 }, - { month: 'May', status: 'green', actual: 115, target: 113 }, - { month: 'Jun', status: 'not-started', actual: 0, target: 116 }, - { month: 'Jul', status: 'not-started', actual: 0, target: 118 }, - { month: 'Aug', status: 'not-started', actual: 0, target: 120 }, - { month: 'Sep', status: 'not-started', actual: 0, target: 122 }, - { month: 'Oct', status: 'not-started', actual: 0, target: 123 }, - { month: 'Nov', status: 'not-started', actual: 0, target: 124 }, - { month: 'Dec', status: 'not-started', actual: 0, target: 125 } - ] - }, - { - objective: 'Cost Reduction 15%', - months: [ - { month: 'Jan', status: 'green', actual: 98, target: 98 }, - { month: 'Feb', status: 'yellow', actual: 96, target: 95 }, - { month: 'Mar', status: 'yellow', actual: 94, target: 92 }, - { month: 'Apr', status: 'red', actual: 93, target: 90 }, - { month: 'May', status: 'yellow', actual: 92, target: 88 }, - { month: 'Jun', status: 'not-started', actual: 0, target: 87 }, - { month: 'Jul', status: 'not-started', actual: 0, target: 86 }, - { month: 'Aug', status: 'not-started', actual: 0, target: 86 }, - { month: 'Sep', status: 'not-started', actual: 0, target: 85 }, - { month: 'Oct', status: 'not-started', actual: 0, target: 85 }, - { month: 'Nov', status: 'not-started', actual: 0, target: 85 }, - { month: 'Dec', status: 'not-started', actual: 0, target: 85 } - ] - }, - { - objective: 'Customer Satisfaction 95%', - months: [ - { month: 'Jan', status: 'green', actual: 75, target: 75 }, - { month: 'Feb', status: 'green', actual: 78, target: 77 }, - { month: 'Mar', status: 'green', actual: 81, target: 80 }, - { month: 'Apr', status: 'green', actual: 84, target: 83 }, - { month: 'May', status: 'green', actual: 87, target: 86 }, - { month: 'Jun', status: 'not-started', actual: 0, target: 89 }, - { month: 'Jul', status: 'not-started', actual: 0, target: 91 }, - { month: 'Aug', status: 'not-started', actual: 0, target: 92 }, - { month: 'Sep', status: 'not-started', actual: 0, target: 93 }, - { month: 'Oct', status: 'not-started', actual: 0, target: 94 }, - { month: 'Nov', status: 'not-started', actual: 0, target: 94 }, - { month: 'Dec', status: 'not-started', actual: 0, target: 95 } - ] - } -] - function ObjectivesView({ projects, setProjects }: { projects: RoadmapProject[], setProjects: (updater: (prev: RoadmapProject[]) => RoadmapProject[]) => void }) { const [isAddingMetricToObjective, setIsAddingMetricToObjective] = useState(false) const [selectedObjectiveId, setSelectedObjectiveId] = useState('') @@ -980,11 +927,35 @@ function ObjectivesView({ projects, setProjects }: { projects: RoadmapProject[], ) } -function MetricsView({ projects }: { projects: RoadmapProject[] }) { +function MetricsView({ projects, setProjects }: { projects: RoadmapProject[], setProjects: (updater: (prev: RoadmapProject[]) => RoadmapProject[]) => void }) { + const [editingMetric, setEditingMetric] = useState(null) + const [isUpdatingMetric, setIsUpdatingMetric] = useState(false) + const [newValue, setNewValue] = useState(0) + const allMetrics = projects.flatMap(project => - project.metrics.map(m => ({ ...m, projectName: project.name })) + project.metrics.map(m => ({ ...m, projectName: project.name, projectId: project.id })) ) + const handleUpdateMetric = () => { + if (!editingMetric) return + + setProjects((prev) => prev.map(p => { + if (p.id !== editingMetric.projectId) return p + return { + ...p, + metrics: p.metrics.map(m => + m.id === editingMetric.id + ? { ...m, current: newValue, lastUpdated: new Date().toISOString() } + : m + ) + } + })) + + setIsUpdatingMetric(false) + setEditingMetric(null) + toast.success('Metric updated successfully') + } + const trendIcons = { improving: , stable: , @@ -1003,68 +974,124 @@ function MetricsView({ projects }: { projects: RoadmapProject[] }) { } return ( -
- - - Metrics Dashboard - Track all key performance indicators - - -
- {allMetrics.map((metric) => { - const progressPercent = ((metric.current - metric.baseline) / (metric.target - metric.baseline)) * 100 - const variance = metric.current - metric.target - const variancePercent = (variance / metric.target) * 100 - - return ( -
-
-
-
-

{metric.name}

- {trendIcons[metric.trend]} + <> +
+ + + Metrics Dashboard + Track all key performance indicators + + +
+ {allMetrics.map((metric) => { + const progressPercent = ((metric.current - metric.baseline) / (metric.target - metric.baseline)) * 100 + const variance = metric.current - metric.target + const variancePercent = (variance / metric.target) * 100 + + return ( +
+
+
+
+

{metric.name}

+ {trendIcons[metric.trend]} +
+

{metric.projectName}

+
+
+ + {metric.frequency} + +
-

{metric.projectName}

- - {metric.frequency} - -
- -
-
-
Baseline
-
{metric.baseline}{metric.unit}
+ +
+
+
Baseline
+
{metric.baseline}{metric.unit}
+
+
+
Current
+
{metric.current}{metric.unit}
+
+
+
Target
+
{metric.target}{metric.unit}
+
-
-
Current
-
{metric.current}{metric.unit}
-
-
-
Target
-
{metric.target}{metric.unit}
+ + + +
+ = 0 ? 'text-success' : 'text-destructive'}> + Variance: {variance > 0 ? '+' : ''}{variance.toFixed(1)}{metric.unit} ({variancePercent > 0 ? '+' : ''}{variancePercent.toFixed(1)}%) + + Last updated: {new Date(metric.lastUpdated).toLocaleDateString()}
- - - -
- = 0 ? 'text-success' : 'text-destructive'}> - Variance: {variance > 0 ? '+' : ''}{variance.toFixed(1)}{metric.unit} ({variancePercent > 0 ? '+' : ''}{variancePercent.toFixed(1)}%) - - Last updated: {new Date(metric.lastUpdated).toLocaleDateString()} -
+ ) + })} +
+ + +
+ + + + + Update Metric Value + Enter the new current value for {editingMetric?.name} + +
+
+ + setNewValue(parseFloat(e.target.value))} + placeholder="Enter new value" + /> +
+ {editingMetric && ( +
+
+
Baseline
+
{editingMetric.baseline}{editingMetric.unit}
- ) - })} +
+
New Value
+
{newValue}{editingMetric.unit}
+
+
+
Target
+
{editingMetric.target}{editingMetric.unit}
+
+
+ )}
- - -
+ + + + + + + ) } -function BowlingChartView() { +function BowlingChartView({ projects }: { projects: RoadmapProject[] }) { const statusColors = { green: 'bg-success', yellow: 'bg-warning', @@ -1079,6 +1106,56 @@ function BowlingChartView() { 'not-started': 'Not Started' } + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + + const bowlingData: BowlingChartData[] = projects.flatMap(project => + project.objectives.map(obj => { + const monthlyData = months.map((month, idx) => { + const avgProgress = obj.metrics.length > 0 + ? obj.metrics.reduce((sum, m) => { + const progress = ((m.current - m.baseline) / (m.target - m.baseline)) * 100 + return sum + progress + }, 0) / obj.metrics.length + : 0 + + const currentMonth = new Date().getMonth() + const isNotStarted = idx > currentMonth + const targetProgress = ((idx + 1) / 12) * 100 + + let status: 'green' | 'yellow' | 'red' | 'not-started' = 'not-started' + if (!isNotStarted) { + const diff = avgProgress - targetProgress + if (diff >= -5) status = 'green' + else if (diff >= -15) status = 'yellow' + else status = 'red' + } + + return { + month, + status, + actual: isNotStarted ? 0 : Math.round(avgProgress), + target: Math.round(targetProgress) + } + }) + + return { + objective: obj.description, + months: monthlyData + } + }) + ) + + if (bowlingData.length === 0) { + return ( + + + +

No objectives to track. Add projects with objectives to see the bowling chart.

+
+
+ ) + } + return (
@@ -1088,7 +1165,7 @@ function BowlingChartView() {
- {mockBowlingChart.map((item, idx) => ( + {bowlingData.map((item, idx) => (
{idx > 0 && }

{item.objective}

@@ -1097,7 +1174,7 @@ function BowlingChartView() {
{month.status !== 'not-started' && month.actual}
@@ -1125,7 +1202,28 @@ function BowlingChartView() { ) } -function XMatrixView() { +function XMatrixView({ projects }: { projects: RoadmapProject[] }) { + const allObjectives = projects.flatMap(p => p.objectives) + const annualObjectives = allObjectives.filter(o => o.category === 'annual') + const breakthroughObjectives = allObjectives.filter(o => o.category === 'breakthrough') + const improvementObjectives = allObjectives.filter(o => o.category === 'improvement') + + const allMetrics = projects.flatMap(p => p.metrics) + const topMetrics = allMetrics.slice(0, 4) + + const hasData = projects.length > 0 && (annualObjectives.length > 0 || breakthroughObjectives.length > 0) + + if (!hasData) { + return ( + + + +

No data for X-Matrix. Add projects with objectives to see strategic alignment.

+
+
+ ) + } + return (
@@ -1139,54 +1237,47 @@ function XMatrixView() {

Annual Objectives

-
- 25% Revenue Growth -
-
- 15% Cost Reduction -
-
- 95% Customer Satisfaction -
-
- Launch 3 New Products -
+ {annualObjectives.length > 0 ? ( + annualObjectives.slice(0, 4).map((obj) => ( +
+ {obj.description} +
+ )) + ) : ( + breakthroughObjectives.slice(0, 4).map((obj) => ( +
+ {obj.description} +
+ )) + )}
-

Strategic Initiatives

+

Strategic Projects

-
- Market Expansion Program -
-
- Process Automation -
-
- Customer Experience Transformation -
-
- Innovation Pipeline -
+ {projects.slice(0, 4).map((project) => ( +
+ {project.name} +
+ ))}

Key Metrics (KPIs)

-
- Monthly Revenue Growth Rate -
-
- Operating Cost Ratio -
-
- Net Promoter Score -
-
- Time to Market -
+ {topMetrics.length > 0 ? ( + topMetrics.map((metric) => ( +
+ {metric.name} +
+ )) + ) : ( +
+ No metrics defined yet +
+ )}
@@ -1196,36 +1287,28 @@ function XMatrixView() {

Improvement Tactics

-
- Digital Marketing Campaign (Q1-Q2) -
-
- Partnership Development (Q1-Q3) -
-
- RPA Implementation (Q2-Q3) -
-
- Lean Process Redesign (Q1-Q4) -
-
- CX Training Program (Q1-Q2) -
-
- Feedback System Redesign (Q2-Q3) -
-
- R&D Investment Plan (Q1-Q4) -
-
- Agile Product Development (Q2-Q4) -
+ {improvementObjectives.length > 0 ? ( + improvementObjectives.map((obj) => ( +
+ {obj.description} +
+ )) + ) : ( + <> +
+ Add improvement objectives to projects +
+
+ Tactics will appear here +
+ + )}

- Note: The X-Matrix creates strategic alignment by connecting Annual Objectives → Strategic Initiatives → Improvement Tactics → Key Metrics. + Note: The X-Matrix creates strategic alignment by connecting Annual Objectives → Strategic Projects → Improvement Tactics → Key Metrics. Each connection represents a cause-and-effect relationship ensuring all activities drive toward strategic goals.

@@ -1236,6 +1319,136 @@ function XMatrixView() { ) } +function TimelineView({ projects }: { projects: RoadmapProject[] }) { + if (projects.length === 0) { + return ( + + + +

No projects to display. Add projects to see the timeline.

+
+
+ ) + } + + const allDates = projects.flatMap(p => [new Date(p.startDate), new Date(p.endDate)]) + const minDate = new Date(Math.min(...allDates.map(d => d.getTime()))) + const maxDate = new Date(Math.max(...allDates.map(d => d.getTime()))) + + const totalDays = Math.ceil((maxDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24)) + const monthsInRange = Math.ceil(totalDays / 30) + + const getProjectPosition = (startDate: string, endDate: string) => { + const start = new Date(startDate) + const end = new Date(endDate) + const startOffset = Math.ceil((start.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24)) + const duration = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)) + + return { + left: `${(startOffset / totalDays) * 100}%`, + width: `${(duration / totalDays) * 100}%` + } + } + + const statusColorsBg = { + 'not-started': 'bg-muted', + 'on-track': 'bg-success', + 'at-risk': 'bg-warning', + 'blocked': 'bg-destructive', + 'completed': 'bg-primary' + } + + const priorityBorders = { + 'critical': 'border-destructive', + 'high': 'border-warning', + 'medium': 'border-primary', + 'low': 'border-muted-foreground' + } + + return ( +
+ + + Project Timeline + Gantt-style view of all strategic projects + + +
+
+ + Timeline: {minDate.toLocaleDateString()} - {maxDate.toLocaleDateString()} + + + {monthsInRange} months + +
+ +
+ {projects.map((project) => { + const position = getProjectPosition(project.startDate, project.endDate) + return ( +
+
+
+

{project.name}

+ + {project.status.replace('-', ' ')} + + + {project.priority} + +
+ + {new Date(project.startDate).toLocaleDateString()} → {new Date(project.endDate).toLocaleDateString()} + +
+ +
+
+ {project.progress}% +
+
+
+ ) + })} +
+ + + +
+
+
Status Legend
+
+ {Object.entries(statusColorsBg).map(([status, color]) => ( +
+
+ {status.replace('-', ' ')} +
+ ))} +
+
+
+
Priority Legend
+
+ {Object.entries(priorityBorders).map(([priority, border]) => ( +
+
+ {priority} +
+ ))} +
+
+
+
+ + +
+ ) +} + function DashboardView({ projects }: { projects: RoadmapProject[] }) { const allObjectives = projects.flatMap(p => p.objectives) @@ -1597,7 +1810,7 @@ export default function Roadmap() { )} - + Projects @@ -1606,6 +1819,10 @@ export default function Roadmap() { Dashboard + + + Timeline + Objectives @@ -1632,20 +1849,24 @@ export default function Roadmap() { + + + + - + - + - +