@burdenoff/microfe-planmagnet 2026.927.4 → 2026.927.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/generated/global-types.d.ts +9 -1
  2. package/dist/generated/global-types.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.d.ts +0 -12
  4. package/dist/generated/wspace-operations.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.js +14 -10
  7. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  8. package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
  9. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +149 -127
  10. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
  11. package/dist/planmagnet/board/ProjectBoard.d.ts +2 -0
  12. package/dist/planmagnet/board/ProjectBoard.d.ts.map +1 -1
  13. package/dist/planmagnet/board/ProjectBoard.js +492 -225
  14. package/dist/planmagnet/board/ProjectBoard.js.map +1 -1
  15. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
  16. package/dist/planmagnet/controls/FeatureControls.js +142 -139
  17. package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
  18. package/dist/planmagnet/controls/FeatureTriage.d.ts +21 -0
  19. package/dist/planmagnet/controls/FeatureTriage.d.ts.map +1 -0
  20. package/dist/planmagnet/controls/FeatureTriage.js +398 -0
  21. package/dist/planmagnet/controls/FeatureTriage.js.map +1 -0
  22. package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -1
  23. package/dist/planmagnet/controls/MilestoneControls.js +76 -73
  24. package/dist/planmagnet/controls/MilestoneControls.js.map +1 -1
  25. package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
  26. package/dist/planmagnet/controls/ProjectControls.js +47 -44
  27. package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
  28. package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -1
  29. package/dist/planmagnet/controls/ReleaseControls.js +47 -44
  30. package/dist/planmagnet/controls/ReleaseControls.js.map +1 -1
  31. package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
  32. package/dist/planmagnet/controls/WorkItemControls.js +68 -65
  33. package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
  34. package/dist/planmagnet/hooks/useCounts.d.ts +39 -0
  35. package/dist/planmagnet/hooks/useCounts.d.ts.map +1 -0
  36. package/dist/planmagnet/hooks/useCounts.js +209 -0
  37. package/dist/planmagnet/hooks/useCounts.js.map +1 -0
  38. package/dist/planmagnet/import/exportColumns.d.ts +52 -0
  39. package/dist/planmagnet/import/exportColumns.d.ts.map +1 -0
  40. package/dist/planmagnet/import/exportColumns.js +81 -0
  41. package/dist/planmagnet/import/exportColumns.js.map +1 -0
  42. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  43. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +255 -242
  44. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  45. package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  46. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +199 -160
  47. package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
  48. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  49. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +147 -144
  50. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  51. package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
  52. package/dist/planmagnet/pages/GoalsGoalsPagePage.js +2 -7
  53. package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
  54. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
  55. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +173 -173
  56. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
  57. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  58. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +120 -117
  59. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  60. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts.map +1 -1
  61. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +97 -117
  62. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
  63. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  64. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +168 -103
  65. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  66. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  67. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +117 -103
  68. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  69. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  70. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +4 -8
  71. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  72. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  73. package/dist/planmagnet/pages/ProductsProductsPagePage.js +85 -90
  74. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  75. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  76. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +12 -2
  77. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  78. package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
  79. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +2 -2
  80. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  81. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
  82. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +48 -71
  83. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  84. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  85. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +17 -13
  86. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  87. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
  88. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +1 -1
  89. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
  90. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +6 -6
  91. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  92. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +11 -8
  93. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  94. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +4 -4
  95. package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
  96. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +1 -1
  97. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
  98. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  99. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +152 -139
  100. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  101. package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  102. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +75 -74
  103. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
  104. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +7 -0
  105. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
  106. package/dist/planmagnet/pages/ReportsReportsPagePage.js +39 -201
  107. package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
  108. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  109. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +118 -99
  110. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  111. package/dist/planmagnet/pages/TasksImportPagePage.d.ts +2 -0
  112. package/dist/planmagnet/pages/TasksImportPagePage.d.ts.map +1 -0
  113. package/dist/planmagnet/pages/TasksImportPagePage.js +309 -0
  114. package/dist/planmagnet/pages/TasksImportPagePage.js.map +1 -0
  115. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  116. package/dist/planmagnet/pages/TasksTasksPagePage.js +110 -104
  117. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  118. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  119. package/dist/planmagnet/pages/TeamTeamPagePage.js +99 -177
  120. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  121. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
  122. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -4
  123. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
  124. package/dist/planmagnet/releases/ReleaseWorkItemsCard.d.ts.map +1 -1
  125. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js +10 -4
  126. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js.map +1 -1
  127. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
  128. package/dist/planmagnet/roadmaps/RoadmapControls.js +151 -138
  129. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
  130. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  131. package/dist/planmagnet/sprints/SprintControls.js +32 -30
  132. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  133. package/dist/planmagnet/utils/mutationResult.d.ts +10 -0
  134. package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -1
  135. package/dist/planmagnet/utils/mutationResult.js +2 -1
  136. package/dist/planmagnet/utils/mutationResult.js.map +1 -1
  137. package/dist/planmagnet/utils/rowNavigation.d.ts +2 -0
  138. package/dist/planmagnet/utils/rowNavigation.d.ts.map +1 -1
  139. package/dist/planmagnet/utils/rowNavigation.js +9 -8
  140. package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
  141. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/AnalyticsAnalyticsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { TrendingUp, Zap, FolderKanban, BarChart3, Target } from 'lucide-react';\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Legend,\n Line,\n LineChart,\n ReferenceLine,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { BudgetPerformanceCard, CycleTimeCard } from './analytics/DeliveryHealthCards';\n\n// Server-side totals (QA H2). The work-item list returns at most 100 rows per\n// page, so every figure computed from the loaded page undercounted past 100\n// items — and \"Project Health\" did not filter by project at all.\nconst GET_ANALYTICS_COUNTS = gql`\n query GetAnalyticsWorkItemCounts($workspaceId: ID!) {\n planmagnetWorkItemCounts(workspaceId: $workspaceId) {\n total\n points\n byStatus {\n status\n count\n points\n }\n byProject {\n projectId\n status\n count\n }\n }\n }\n`;\n\ninterface WorkItemCounts {\n total: number;\n points: number;\n byStatus: Array<{ status: string; count: number; points: number }>;\n byProject: Array<{ projectId: string; status: string; count: number }>;\n}\n\nconst GET_ANALYTICS_DATA = gql`\n query GetAnalyticsData(\n $workspaceId: ID!\n $projectPagination: PlanmagnetPaginationInput\n $workItemPagination: PlanmagnetPaginationInput\n $sprintPagination: PlanmagnetPaginationInput\n $milestonePagination: PlanmagnetPaginationInput\n ) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: $projectPagination) {\n items {\n id\n name\n key\n status\n startDate\n endDate\n tags\n }\n totalCount\n }\n planmagnetWorkItems(workspaceId: $workspaceId, pagination: $workItemPagination) {\n items {\n id\n status\n priority\n storyPoints\n assigneeId\n }\n totalCount\n }\n planmagnetSprints(workspaceId: $workspaceId, pagination: $sprintPagination) {\n items {\n id\n name\n status\n velocity\n capacity\n projectId\n startDate\n endDate\n }\n totalCount\n }\n planmagnetMilestones(workspaceId: $workspaceId, pagination: $milestonePagination) {\n items {\n id\n name\n status\n dueDate\n }\n totalCount\n }\n }\n`;\n\n// BOFF-4447: real server-computed sprint delivery analytics — burndown,\n// velocity, and cumulative flow are all aggregated in wspace-planmagnet-svc\n// (src/modules/analytics) from the Sprint/WorkItem data model plus the\n// WorkItemStatusTransition audit trail, not derived client-side from raw\n// lists like the summary cards above.\nconst GET_SPRINT_BURNDOWN = gql`\n query GetSprintBurndown($workspaceId: ID!, $sprintId: ID!) {\n planmagnetSprintBurndown(workspaceId: $workspaceId, sprintId: $sprintId) {\n sprintId\n startDate\n endDate\n totalPoints\n totalCount\n points {\n date\n remainingPoints\n remainingCount\n completedPoints\n completedCount\n }\n }\n }\n`;\n\nconst GET_VELOCITY = gql`\n query GetVelocity($workspaceId: ID!, $projectId: ID!, $trailingSprintCount: Int) {\n planmagnetVelocity(\n workspaceId: $workspaceId\n projectId: $projectId\n trailingSprintCount: $trailingSprintCount\n ) {\n projectId\n averageVelocity\n forecastNextSprintPoints\n sprints {\n sprintId\n sprintName\n sequenceNumber\n completedPoints\n completedCount\n plannedPoints\n }\n }\n }\n`;\n\nconst GET_CUMULATIVE_FLOW = gql`\n query GetCumulativeFlow($workspaceId: ID!, $projectId: ID!) {\n planmagnetCumulativeFlow(workspaceId: $workspaceId, projectId: $projectId) {\n projectId\n startDate\n endDate\n points {\n date\n statuses {\n status\n count\n }\n }\n }\n }\n`;\n\ninterface ProjectNode {\n id: string;\n name: string;\n key: string;\n status: string;\n startDate: string | null;\n endDate: string | null;\n tags: string[];\n}\n\ninterface WorkItemNode {\n id: string;\n status: string;\n priority: string;\n storyPoints: number | null;\n assigneeId: string | null;\n}\n\ninterface SprintNode {\n id: string;\n name: string;\n status: string;\n velocity: number | null;\n capacity: number | null;\n projectId: string;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface MilestoneNode {\n id: string;\n name: string;\n status: string;\n dueDate: string | null;\n}\n\ninterface AnalyticsData {\n planmagnetProjects: { items: ProjectNode[]; totalCount: number };\n planmagnetWorkItems: { items: WorkItemNode[]; totalCount: number };\n planmagnetSprints: { items: SprintNode[]; totalCount: number };\n planmagnetMilestones: { items: MilestoneNode[]; totalCount: number };\n}\n\ninterface BurndownData {\n planmagnetSprintBurndown: {\n sprintId: string;\n startDate: string | null;\n endDate: string | null;\n totalPoints: number;\n totalCount: number;\n points: {\n date: string;\n remainingPoints: number;\n remainingCount: number;\n completedPoints: number;\n completedCount: number;\n }[];\n } | null;\n}\n\ninterface VelocityData {\n planmagnetVelocity: {\n projectId: string;\n averageVelocity: number;\n forecastNextSprintPoints: number;\n sprints: {\n sprintId: string;\n sprintName: string;\n sequenceNumber: number;\n completedPoints: number;\n completedCount: number;\n plannedPoints: number | null;\n }[];\n };\n}\n\ninterface CumulativeFlowData {\n planmagnetCumulativeFlow: {\n projectId: string;\n startDate: string;\n endDate: string;\n points: {\n date: string;\n statuses: { status: string; count: number }[];\n }[];\n };\n}\n\n// recharts' `stroke`/`fill` props render as literal SVG paint attributes, not\n// Tailwind classNames — they cannot consume the semantic `bg-*`/`text-*`\n// utility tokens the lint rule expects everywhere else on this page. This is\n// the one place on the page raw color values are load-bearing rather than\n// stylistic, matching the same constraint every other MFE chart primitive in\n// the fleet (e.g. microfe-assethandler's `ChartCard`/`DonutChart`) hits.\nconst CFD_STATUS_COLORS: Record<string, string> = {\n BACKLOG: '#9ca3af',\n TODO: '#60a5fa',\n IN_PROGRESS: '#fbbf24',\n IN_REVIEW: '#a78bfa',\n TESTING: '#f472b6',\n DONE: '#4ade80',\n CANCELLED: '#f87171',\n};\n\nfunction formatDay(iso: string): string {\n const d = new Date(iso);\n return `${d.getUTCMonth() + 1}/${d.getUTCDate()}`;\n}\n\nexport function AnalyticsAnalyticsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const { t } = useI18n();\n const [selectedProjectId, setSelectedProjectId] = useState<string>('');\n const [selectedSprintId, setSelectedSprintId] = useState<string>('');\n\n const { data, loading, error } = useQuery<AnalyticsData>(GET_ANALYTICS_DATA, {\n variables: {\n workspaceId,\n projectPagination: { first: 100 },\n workItemPagination: { first: 500 },\n sprintPagination: { first: 100 },\n milestonePagination: { first: 100 },\n },\n skip: !workspaceId,\n });\n\n const { data: countsData } = useQuery<{ planmagnetWorkItemCounts: WorkItemCounts }>(\n GET_ANALYTICS_COUNTS,\n { variables: { workspaceId }, skip: !workspaceId }\n );\n const counts = countsData?.planmagnetWorkItemCounts;\n\n const projects = data?.planmagnetProjects?.items ?? [];\n const tasks = data?.planmagnetWorkItems?.items ?? [];\n const sprints = data?.planmagnetSprints?.items ?? [];\n const milestones = data?.planmagnetMilestones?.items ?? [];\n\n // Default the project/sprint selectors to the first available option once\n // the summary data loads, so the delivery-analytics charts render without\n // requiring a manual pick every time the page opens. Filtering a list of\n // at most a few hundred sprints is cheap enough to redo on every render —\n // memoizing it would only need its own dependency-array workaround since\n // the `?? []` fallback above hands back a fresh array identity per render\n // whenever the query hasn't resolved yet.\n const effectiveProjectId = selectedProjectId || projects[0]?.id || '';\n const sprintsForProject = sprints.filter((s) => s.projectId === effectiveProjectId);\n const effectiveSprintId = selectedSprintId || sprintsForProject[0]?.id || '';\n\n const { data: burndownData, loading: burndownLoading } = useQuery<BurndownData>(\n GET_SPRINT_BURNDOWN,\n {\n variables: { workspaceId, sprintId: effectiveSprintId },\n skip: !workspaceId || !effectiveSprintId,\n }\n );\n\n const { data: velocityData, loading: velocityLoading } = useQuery<VelocityData>(GET_VELOCITY, {\n variables: { workspaceId, projectId: effectiveProjectId, trailingSprintCount: 6 },\n skip: !workspaceId || !effectiveProjectId,\n });\n\n const { data: cfdData, loading: cfdLoading } = useQuery<CumulativeFlowData>(GET_CUMULATIVE_FLOW, {\n variables: { workspaceId, projectId: effectiveProjectId },\n skip: !workspaceId || !effectiveProjectId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"p-6 text-red-500\">\n {t('planmagnet.analytics.errorLoading', 'Error loading analytics')}: {error.message}\n </div>\n );\n }\n\n const statusCount = (status: string) => counts?.byStatus.find((c) => c.status === status);\n const totalTasks = counts?.total ?? tasks.length;\n const doneTasks = statusCount('DONE')?.count ?? tasks.filter((t) => t.status === 'DONE').length;\n // BLOCKED exists in storage but not in the API enum, so only the server-side\n // counts can see it; the loaded page never contains one.\n const blockedTasks = statusCount('BLOCKED')?.count ?? 0;\n const totalPoints = counts?.points ?? tasks.reduce((s, t) => s + (t.storyPoints ?? 0), 0);\n const completedPoints =\n statusCount('DONE')?.points ??\n tasks.filter((t) => t.status === 'DONE').reduce((s, t) => s + (t.storyPoints ?? 0), 0);\n const projectProgress = (projectId: string) => {\n const rows = counts?.byProject.filter((r) => r.projectId === projectId) ?? [];\n const total = rows.reduce((sum, r) => sum + r.count, 0);\n const done = rows.filter((r) => r.status === 'DONE').reduce((sum, r) => sum + r.count, 0);\n return total > 0 ? Math.round((done / total) * 100) : 0;\n };\n\n const activeSprints = sprints.filter((s) => s.status === 'ACTIVE').length;\n // MilestoneStatus has no AT_RISK/OVERDUE (the old check could never match):\n // at risk = MISSED, or still open past its due date.\n const now = new Date();\n const atRiskMs = milestones.filter(\n (m) =>\n m.status === 'MISSED' ||\n ((m.status === 'PENDING' || m.status === 'IN_PROGRESS') &&\n m.dueDate != null &&\n new Date(m.dueDate) < now)\n ).length;\n const activeProjects = projects.filter((p) => p.status === 'ACTIVE').length;\n const completionRate = totalTasks > 0 ? Math.round((doneTasks / totalTasks) * 100) : 0;\n\n const burndown = burndownData?.planmagnetSprintBurndown;\n const burndownChartData =\n burndown?.points.map((p) => ({\n day: formatDay(p.date),\n remaining: p.remainingPoints,\n completed: p.completedPoints,\n })) ?? [];\n\n const velocity = velocityData?.planmagnetVelocity;\n const velocityChartData =\n velocity?.sprints.map((s) => ({\n name: s.sprintName,\n completed: s.completedPoints,\n planned: s.plannedPoints ?? undefined,\n })) ?? [];\n const avgVelocity = velocity ? Math.round(velocity.averageVelocity) : 0;\n const forecastNextSprint = velocity ? Math.round(velocity.forecastNextSprintPoints) : 0;\n\n const cfd = cfdData?.planmagnetCumulativeFlow;\n const cfdStatusKeys = Array.from(\n new Set(cfd?.points.flatMap((p) => p.statuses.map((s) => s.status)) ?? [])\n );\n const cfdChartData =\n cfd?.points.map((p) => {\n const row: Record<string, string | number> = { day: formatDay(p.date) };\n for (const s of p.statuses) row[s.status] = s.count;\n return row;\n }) ?? [];\n\n const metrics = [\n {\n label: t('planmagnet.analytics.metrics.totalWorkItems.label', 'Total Work Items'),\n value: String(totalTasks),\n trend: `${completionRate}% ${t('planmagnet.analytics.metrics.totalWorkItems.trend', 'completed')}`,\n },\n {\n label: t('planmagnet.analytics.metrics.storyPoints.label', 'Story Points'),\n value: String(totalPoints),\n trend: `${completedPoints} ${t('planmagnet.analytics.metrics.storyPoints.trend', 'completed')}`,\n },\n {\n label: t('planmagnet.analytics.metrics.activeProjects.label', 'Active Projects'),\n value: String(activeProjects),\n trend: `${data?.planmagnetProjects?.totalCount ?? projects.length} ${t('planmagnet.analytics.metrics.activeProjects.trend', 'total')}`,\n },\n {\n label: t('planmagnet.analytics.metrics.blockedItems.label', 'Blocked Items'),\n value: String(blockedTasks),\n trend:\n blockedTasks > 0\n ? t('planmagnet.analytics.metrics.blockedItems.attention', 'Needs attention')\n : t('planmagnet.analytics.metrics.blockedItems.clear', 'All clear'),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n {t('planmagnet.analytics.title', 'Analytics Dashboard')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('planmagnet.analytics.subtitle', 'Overview of project health and team performance')}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {metrics.map((m) => (\n <Card key={m.label}>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">{m.label}</p>\n <p className=\"text-xl font-bold mt-1\">{m.value}</p>\n <p className=\"text-xs text-green-600 mt-1\">{m.trend}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <FolderKanban className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.activeProjects', 'Active Projects')}\n </div>\n <p className=\"text-xl font-bold mt-1\">{activeProjects}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Zap className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.activeSprints', 'Active Sprints')}\n </div>\n <p className=\"text-xl font-bold mt-1\">{activeSprints}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <TrendingUp className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.avgVelocity', 'Avg Velocity')}\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {avgVelocity} {t('planmagnet.analytics.summary.pointsShort', 'pts')}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Target className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.atRisk', 'At Risk')}\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {atRiskMs} {t('planmagnet.analytics.summary.milestones', 'milestones')}\n </p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex flex-col sm:flex-row gap-3 items-start sm:items-center\">\n <h2 className=\"text-lg font-semibold\">\n {t('planmagnet.analytics.delivery.title', 'Sprint Delivery Analytics')}\n </h2>\n <div className=\"flex gap-2 sm:ml-auto\">\n <Select\n value={effectiveProjectId}\n onValueChange={(v) => {\n setSelectedProjectId(v);\n setSelectedSprintId('');\n }}\n >\n <SelectTrigger className=\"h-8 w-48\">\n <SelectValue\n placeholder={t(\n 'planmagnet.analytics.delivery.projectPlaceholder',\n 'Select project'\n )}\n />\n </SelectTrigger>\n <SelectContent>\n {projects.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={effectiveSprintId} onValueChange={setSelectedSprintId}>\n <SelectTrigger className=\"h-8 w-48\">\n <SelectValue\n placeholder={t('planmagnet.analytics.delivery.sprintPlaceholder', 'Select sprint')}\n />\n </SelectTrigger>\n <SelectContent>\n {sprintsForProject.map((s) => (\n <SelectItem key={s.id} value={s.id}>\n {s.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n {t('planmagnet.analytics.burndown.title', 'Sprint Burndown')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {burndownLoading ? (\n <div className=\"flex justify-center py-12\">\n <div className=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n ) : burndownChartData.length > 0 ? (\n <ResponsiveContainer width=\"100%\" height={220}>\n <LineChart data={burndownChartData}>\n <CartesianGrid strokeDasharray=\"3 3\" opacity={0.2} />\n <XAxis dataKey=\"day\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} allowDecimals={false} />\n <Tooltip />\n <Legend wrapperStyle={{ fontSize: 12 }} />\n <Line\n type=\"monotone\"\n dataKey=\"remaining\"\n name={t('planmagnet.analytics.burndown.remaining', 'Remaining points')}\n stroke=\"#f59e0b\"\n strokeWidth={2}\n dot={{ r: 2 }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"completed\"\n name={t('planmagnet.analytics.burndown.completed', 'Completed points')}\n stroke=\"#22c55e\"\n strokeWidth={2}\n dot={{ r: 2 }}\n />\n </LineChart>\n </ResponsiveContainer>\n ) : (\n <p className=\"text-sm text-muted-foreground py-8 text-center\">\n {t(\n 'planmagnet.analytics.burndown.empty',\n 'No sprint selected, or the sprint has no work items yet'\n )}\n </p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <TrendingUp className=\"h-4 w-4\" />\n {t('planmagnet.analytics.velocity.title', 'Sprint Velocity')} (\n {velocityChartData.length} {t('planmagnet.analytics.velocity.sprints', 'Sprints')})\n </CardTitle>\n </CardHeader>\n <CardContent>\n {velocityLoading ? (\n <div className=\"flex justify-center py-12\">\n <div className=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n ) : velocityChartData.length > 0 ? (\n <>\n <ResponsiveContainer width=\"100%\" height={190}>\n <BarChart data={velocityChartData}>\n <CartesianGrid strokeDasharray=\"3 3\" opacity={0.2} />\n <XAxis dataKey=\"name\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} allowDecimals={false} />\n <Tooltip />\n <Legend wrapperStyle={{ fontSize: 12 }} />\n <Bar\n dataKey=\"planned\"\n name={t('planmagnet.analytics.velocity.planned', 'Planned')}\n fill=\"#cbd5e1\"\n />\n <Bar\n dataKey=\"completed\"\n name={t('planmagnet.analytics.velocity.completed', 'Completed')}\n fill=\"#6366f1\"\n />\n <ReferenceLine\n y={avgVelocity}\n stroke=\"#22c55e\"\n strokeDasharray=\"4 4\"\n label={{\n value: t('planmagnet.analytics.velocity.avgLabel', 'avg'),\n fontSize: 10,\n position: 'right',\n }}\n />\n </BarChart>\n </ResponsiveContainer>\n <p className=\"text-xs text-muted-foreground text-center mt-2\">\n {t(\n 'planmagnet.analytics.velocity.forecast',\n 'Forecast for next sprint: {{points}} pts (trailing average)',\n { points: forecastNextSprint }\n )}\n </p>\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground py-8 text-center\">\n {t(\n 'planmagnet.analytics.velocity.empty',\n 'No completed sprints yet for this project'\n )}\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n {t('planmagnet.analytics.cfd.title', 'Cumulative Flow Diagram')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {cfdLoading ? (\n <div className=\"flex justify-center py-12\">\n <div className=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n ) : cfdChartData.length > 0 ? (\n <ResponsiveContainer width=\"100%\" height={260}>\n <AreaChart data={cfdChartData}>\n <CartesianGrid strokeDasharray=\"3 3\" opacity={0.2} />\n <XAxis dataKey=\"day\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} allowDecimals={false} />\n <Tooltip />\n <Legend wrapperStyle={{ fontSize: 12 }} />\n {cfdStatusKeys.map((status) => (\n <Area\n key={status}\n type=\"monotone\"\n dataKey={status}\n name={status.toLowerCase().replace('_', ' ')}\n stackId=\"cfd\"\n stroke={CFD_STATUS_COLORS[status] ?? '#a3a3a3'}\n fill={CFD_STATUS_COLORS[status] ?? '#a3a3a3'}\n fillOpacity={0.7}\n />\n ))}\n </AreaChart>\n </ResponsiveContainer>\n ) : (\n <p className=\"text-sm text-muted-foreground py-8 text-center\">\n {t('planmagnet.analytics.cfd.empty', 'No work items found for this project')}\n </p>\n )}\n </CardContent>\n </Card>\n {/* Cycle time follows the delivery selector above; budgets are workspace-wide. */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <CycleTimeCard workspaceId={workspaceId} projectId={effectiveProjectId} />\n <BudgetPerformanceCard workspaceId={workspaceId} />\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{t('planmagnet.analytics.projectHealth.title', 'Project Health')}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {projects\n .filter((p) => p.status === 'ACTIVE')\n .map((p) => {\n // Per project, from the server. This used every ASSIGNED item in\n // the workspace for every project, so all bars were identical.\n const progress = projectProgress(p.id);\n const hasOverdue = p.endDate && new Date(p.endDate) < new Date() && progress < 100;\n const health = hasOverdue ? 'at_risk' : progress >= 75 ? 'on_track' : 'in_progress';\n return (\n <div key={p.id} className=\"flex items-center gap-4\">\n <span className=\"text-sm font-medium w-40 truncate\">{p.name}</span>\n <div className=\"flex-1\">\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${health === 'at_risk' ? 'bg-orange-400' : health === 'on_track' ? 'bg-green-400' : 'bg-blue-400'}`}\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n <span className=\"text-sm w-12 text-right\">{progress}%</span>\n <Badge variant=\"outline\" className=\"capitalize text-xs w-20 justify-center\">\n {health.replace('_', ' ')}\n </Badge>\n </div>\n );\n })}\n {projects.filter((p) => p.status === 'ACTIVE').length === 0 && (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n {t('planmagnet.analytics.projectHealth.empty', 'No active projects found')}\n </p>\n )}\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/AnalyticsAnalyticsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Badge,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { TrendingUp, Zap, FolderKanban, BarChart3, Target } from 'lucide-react';\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Legend,\n Line,\n LineChart,\n ReferenceLine,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport { countLabel, useProjectCounts, useSprintCounts } from '../hooks/useCounts';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { BudgetPerformanceCard, CycleTimeCard } from './analytics/DeliveryHealthCards';\n\n// Server-side totals (QA H2). The work-item list returns at most 100 rows per\n// page, so every figure computed from the loaded page undercounted past 100\n// items — and \"Project Health\" did not filter by project at all.\n// The chosen project's sprints, queried by project (BOFF-7964: the picker was built from the 100 newest sprints\n// of the whole workspace, so an older project's list could be empty).\nconst GET_PROJECT_SPRINTS = gql`\n query AnalyticsProjectSprints($workspaceId: ID!, $projectId: String) {\n planmagnetSprints(\n workspaceId: $workspaceId\n filter: { projectId: $projectId }\n pagination: { first: 100 }\n ) {\n items {\n id\n name\n status\n completedAt\n endDate\n }\n }\n }\n`;\n\n// At risk = missed, or still open and past due — counted on the server over every milestone.\nconst GET_MILESTONE_RISK = gql`\n query AnalyticsMilestoneRisk($workspaceId: ID!, $yesterday: DateTime) {\n missed: planmagnetMilestones(\n workspaceId: $workspaceId\n filter: { status: MISSED }\n pagination: { first: 1 }\n ) {\n totalCount\n }\n overdue: planmagnetMilestones(\n workspaceId: $workspaceId\n filter: { open: true, dueTo: $yesterday }\n pagination: { first: 1 }\n ) {\n totalCount\n }\n }\n`;\n\nconst GET_ANALYTICS_COUNTS = gql`\n query GetAnalyticsWorkItemCounts($workspaceId: ID!) {\n planmagnetWorkItemCounts(workspaceId: $workspaceId) {\n total\n points\n blocked\n byStatus {\n status\n count\n points\n }\n byProject {\n projectId\n status\n count\n }\n }\n }\n`;\n\ninterface WorkItemCounts {\n total: number;\n points: number;\n blocked: number;\n byStatus: Array<{ status: string; count: number; points: number }>;\n byProject: Array<{ projectId: string; status: string; count: number }>;\n}\n\n// Only the project list (for the pickers and per-project health). Counts come from the server; the 500 work items,\n// 100 sprints and 100 milestones this used to load only fed page-derived counts (BOFF-7997).\nconst GET_ANALYTICS_DATA = gql`\n query GetAnalyticsData($workspaceId: ID!, $projectPagination: PlanmagnetPaginationInput) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: $projectPagination) {\n items {\n id\n name\n key\n status\n startDate\n endDate\n tags\n }\n totalCount\n }\n }\n`;\n\n// BOFF-4447: real server-computed sprint delivery analytics — burndown,\n// velocity, and cumulative flow are all aggregated in wspace-planmagnet-svc\n// (src/modules/analytics) from the Sprint/WorkItem data model plus the\n// WorkItemStatusTransition audit trail, not derived client-side from raw\n// lists like the summary cards above.\nconst GET_SPRINT_BURNDOWN = gql`\n query GetSprintBurndown($workspaceId: ID!, $sprintId: ID!) {\n planmagnetSprintBurndown(workspaceId: $workspaceId, sprintId: $sprintId) {\n sprintId\n startDate\n endDate\n totalPoints\n totalCount\n points {\n date\n remainingPoints\n remainingCount\n completedPoints\n completedCount\n }\n }\n }\n`;\n\nconst GET_VELOCITY = gql`\n query GetVelocity($workspaceId: ID!, $projectId: ID!, $trailingSprintCount: Int) {\n planmagnetVelocity(\n workspaceId: $workspaceId\n projectId: $projectId\n trailingSprintCount: $trailingSprintCount\n ) {\n projectId\n averageVelocity\n forecastNextSprintPoints\n sprints {\n sprintId\n sprintName\n sequenceNumber\n completedPoints\n completedCount\n plannedPoints\n }\n }\n }\n`;\n\nconst GET_CUMULATIVE_FLOW = gql`\n query GetCumulativeFlow($workspaceId: ID!, $projectId: ID!) {\n planmagnetCumulativeFlow(workspaceId: $workspaceId, projectId: $projectId) {\n projectId\n startDate\n endDate\n points {\n date\n statuses {\n status\n count\n }\n }\n }\n }\n`;\n\ninterface ProjectNode {\n id: string;\n name: string;\n key: string;\n status: string;\n startDate: string | null;\n endDate: string | null;\n tags: string[];\n}\n\ninterface AnalyticsData {\n planmagnetProjects: { items: ProjectNode[]; totalCount: number };\n}\n\ninterface BurndownData {\n planmagnetSprintBurndown: {\n sprintId: string;\n startDate: string | null;\n endDate: string | null;\n totalPoints: number;\n totalCount: number;\n points: {\n date: string;\n remainingPoints: number;\n remainingCount: number;\n completedPoints: number;\n completedCount: number;\n }[];\n } | null;\n}\n\ninterface VelocityData {\n planmagnetVelocity: {\n projectId: string;\n averageVelocity: number;\n forecastNextSprintPoints: number;\n sprints: {\n sprintId: string;\n sprintName: string;\n sequenceNumber: number;\n completedPoints: number;\n completedCount: number;\n plannedPoints: number | null;\n }[];\n };\n}\n\ninterface CumulativeFlowData {\n planmagnetCumulativeFlow: {\n projectId: string;\n startDate: string;\n endDate: string;\n points: {\n date: string;\n statuses: { status: string; count: number }[];\n }[];\n };\n}\n\n// recharts' `stroke`/`fill` props render as literal SVG paint attributes, not\n// Tailwind classNames — they cannot consume the semantic `bg-*`/`text-*`\n// utility tokens the lint rule expects everywhere else on this page. This is\n// the one place on the page raw color values are load-bearing rather than\n// stylistic, matching the same constraint every other MFE chart primitive in\n// the fleet (e.g. microfe-assethandler's `ChartCard`/`DonutChart`) hits.\nconst CFD_STATUS_COLORS: Record<string, string> = {\n BACKLOG: '#9ca3af',\n TODO: '#60a5fa',\n IN_PROGRESS: '#fbbf24',\n IN_REVIEW: '#a78bfa',\n TESTING: '#f472b6',\n DONE: '#4ade80',\n CANCELLED: '#f87171',\n};\n\nfunction formatDay(iso: string): string {\n const d = new Date(iso);\n return `${d.getUTCMonth() + 1}/${d.getUTCDate()}`;\n}\n\nexport function AnalyticsAnalyticsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const { t } = useI18n();\n const [selectedProjectId, setSelectedProjectId] = useState<string>('');\n const [selectedSprintId, setSelectedSprintId] = useState<string>('');\n\n const { data, loading, error } = useQuery<AnalyticsData>(GET_ANALYTICS_DATA, {\n variables: { workspaceId, projectPagination: { first: 100 } },\n skip: !workspaceId,\n });\n\n const { data: countsData } = useQuery<{ planmagnetWorkItemCounts: WorkItemCounts }>(\n GET_ANALYTICS_COUNTS,\n { variables: { workspaceId }, skip: !workspaceId }\n );\n const counts = countsData?.planmagnetWorkItemCounts;\n const projectCounts = useProjectCounts();\n const sprintCounts = useSprintCounts();\n // Fixed for the life of the page (render must stay pure).\n const [yesterday] = useState(() => new Date(Date.now() - 86_400_000).toISOString().slice(0, 10));\n const { data: riskData } = useQuery<{\n missed: { totalCount: number };\n overdue: { totalCount: number };\n }>(GET_MILESTONE_RISK, { variables: { workspaceId, yesterday }, skip: !workspaceId });\n\n const projects = data?.planmagnetProjects?.items ?? [];\n\n // Default the project/sprint selectors to the first available option once\n // the summary data loads, so the delivery-analytics charts render without\n // requiring a manual pick every time the page opens. Filtering a list of\n // at most a few hundred sprints is cheap enough to redo on every render —\n // memoizing it would only need its own dependency-array workaround since\n // the `?? []` fallback above hands back a fresh array identity per render\n // whenever the query hasn't resolved yet.\n const effectiveProjectId = selectedProjectId || projects[0]?.id || '';\n const { data: projectSprintsData } = useQuery<{\n planmagnetSprints: {\n items: {\n id: string;\n name: string;\n status: string;\n completedAt: string | null;\n endDate: string | null;\n }[];\n };\n }>(GET_PROJECT_SPRINTS, {\n variables: { workspaceId, projectId: effectiveProjectId },\n skip: !workspaceId || !effectiveProjectId,\n });\n const sprintsForProject = projectSprintsData?.planmagnetSprints.items ?? [];\n // Default to the running sprint (a PLANNING one has a future start and an empty burndown), else the latest\n // completed one, else whatever there is.\n const lastCompleted = sprintsForProject\n .filter((s) => s.status === 'COMPLETED')\n .sort((a, b) =>\n (b.completedAt ?? b.endDate ?? '').localeCompare(a.completedAt ?? a.endDate ?? '')\n )[0];\n const defaultSprint =\n sprintsForProject.find((s) => s.status === 'ACTIVE' || s.status === 'REVIEW') ??\n lastCompleted ??\n sprintsForProject[0];\n const effectiveSprintId = selectedSprintId || defaultSprint?.id || '';\n const effectiveProjectName = projects.find((p) => p.id === effectiveProjectId)?.name ?? '';\n\n const { data: burndownData, loading: burndownLoading } = useQuery<BurndownData>(\n GET_SPRINT_BURNDOWN,\n {\n variables: { workspaceId, sprintId: effectiveSprintId },\n skip: !workspaceId || !effectiveSprintId,\n }\n );\n\n const { data: velocityData, loading: velocityLoading } = useQuery<VelocityData>(GET_VELOCITY, {\n variables: { workspaceId, projectId: effectiveProjectId, trailingSprintCount: 6 },\n skip: !workspaceId || !effectiveProjectId,\n });\n\n const { data: cfdData, loading: cfdLoading } = useQuery<CumulativeFlowData>(GET_CUMULATIVE_FLOW, {\n variables: { workspaceId, projectId: effectiveProjectId },\n skip: !workspaceId || !effectiveProjectId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"p-6 text-red-500\">\n {t('planmagnet.analytics.errorLoading', 'Error loading analytics')}: {error.message}\n </div>\n );\n }\n\n // Every figure comes from the server counts; while they load (or if they fail) the tiles say \"—\" instead of\n // quietly falling back to the loaded page (BOFF-7997).\n const statusCount = (status: string) => counts?.byStatus.find((c) => c.status === status);\n const cancelled = statusCount('CANCELLED')?.count ?? 0;\n const doneTasks = counts ? (statusCount('DONE')?.count ?? 0) : null;\n const totalTasks = counts ? counts.total : null;\n // Blocked = held by a dependency (the release readiness rule), not a status nobody can set (BOFF-7973).\n const blockedTasks = counts ? counts.blocked : null;\n const totalPoints = counts ? counts.points : null;\n const completedPoints = counts ? (statusCount('DONE')?.points ?? 0) : null;\n const projectProgress = (projectId: string) => {\n const rows = counts?.byProject.filter((r) => r.projectId === projectId) ?? [];\n // Cancelled work no longer needs doing (BOFF-7989).\n const live = rows.filter((r) => r.status !== 'CANCELLED').reduce((sum, r) => sum + r.count, 0);\n const done = rows.filter((r) => r.status === 'DONE').reduce((sum, r) => sum + r.count, 0);\n return live > 0 ? Math.round((done / live) * 100) : 0;\n };\n\n const activeSprints = sprintCounts?.running ?? null;\n const atRiskMs = riskData ? riskData.missed.totalCount + riskData.overdue.totalCount : null;\n const activeProjects = projectCounts?.active ?? null;\n const completionRate =\n totalTasks != null && doneTasks != null && totalTasks - cancelled > 0\n ? Math.round((doneTasks / (totalTasks - cancelled)) * 100)\n : null;\n\n const burndown = burndownData?.planmagnetSprintBurndown;\n const burndownChartData =\n burndown?.points.map((p) => ({\n day: formatDay(p.date),\n remaining: p.remainingPoints,\n completed: p.completedPoints,\n })) ?? [];\n\n const velocity = velocityData?.planmagnetVelocity;\n const velocityChartData =\n velocity?.sprints.map((s) => ({\n name: s.sprintName,\n completed: s.completedPoints,\n planned: s.plannedPoints ?? undefined,\n })) ?? [];\n const avgVelocity = velocity ? Math.round(velocity.averageVelocity) : 0;\n const forecastNextSprint = velocity ? Math.round(velocity.forecastNextSprintPoints) : 0;\n\n const cfd = cfdData?.planmagnetCumulativeFlow;\n const cfdStatusKeys = Array.from(\n new Set(cfd?.points.flatMap((p) => p.statuses.map((s) => s.status)) ?? [])\n );\n const cfdChartData =\n cfd?.points.map((p) => {\n const row: Record<string, string | number> = { day: formatDay(p.date) };\n for (const s of p.statuses) row[s.status] = s.count;\n return row;\n }) ?? [];\n\n const metrics = [\n {\n label: t('planmagnet.analytics.metrics.totalWorkItems.label', 'Total Work Items'),\n value: countLabel(totalTasks),\n trend: `${countLabel(completionRate)}% ${t('planmagnet.analytics.metrics.totalWorkItems.trend', 'completed')}`,\n },\n {\n label: t('planmagnet.analytics.metrics.storyPoints.label', 'Story Points'),\n value: countLabel(totalPoints),\n trend: `${countLabel(completedPoints)} ${t('planmagnet.analytics.metrics.storyPoints.trend', 'completed')}`,\n },\n {\n label: t('planmagnet.analytics.metrics.activeProjects.label', 'Active Projects'),\n value: countLabel(activeProjects),\n trend: `${countLabel(projectCounts?.all)} ${t('planmagnet.analytics.metrics.activeProjects.trend', 'total')}`,\n },\n {\n label: t('planmagnet.analytics.metrics.blockedItems.label', 'Blocked Items'),\n value: countLabel(blockedTasks),\n trend:\n blockedTasks == null\n ? ''\n : blockedTasks > 0\n ? t('planmagnet.analytics.metrics.blockedItems.attention', 'Needs attention')\n : t('planmagnet.analytics.metrics.blockedItems.clear', 'All clear'),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n {t('planmagnet.analytics.title', 'Analytics Dashboard')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('planmagnet.analytics.subtitle', 'Overview of project health and team performance')}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {metrics.map((m) => (\n <Card key={m.label}>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">{m.label}</p>\n <p className=\"text-xl font-bold mt-1\">{m.value}</p>\n <p className=\"text-xs text-green-600 mt-1\">{m.trend}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <FolderKanban className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.activeProjects', 'Active Projects')}\n </div>\n <p className=\"text-xl font-bold mt-1\">{countLabel(activeProjects)}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Zap className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.activeSprints', 'Active Sprints')}\n </div>\n <p className=\"text-xl font-bold mt-1\">{countLabel(activeSprints)}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <TrendingUp className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.avgVelocity', 'Avg Velocity')}\n </div>\n {/* This one is per project — name it (BOFF-7987: it silently followed the picker below). */}\n <p className=\"text-xs text-muted-foreground truncate\">{effectiveProjectName || '—'}</p>\n <p className=\"text-xl font-bold mt-1\">\n {avgVelocity} {t('planmagnet.analytics.summary.pointsShort', 'pts')}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Target className=\"h-4 w-4\" />\n {t('planmagnet.analytics.summary.atRisk', 'At Risk')}\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {countLabel(atRiskMs)} {t('planmagnet.analytics.summary.milestones', 'milestones')}\n </p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex flex-col sm:flex-row gap-3 items-start sm:items-center\">\n <h2 className=\"text-lg font-semibold\">\n {t('planmagnet.analytics.delivery.title', 'Sprint Delivery Analytics')}\n </h2>\n <div className=\"flex gap-2 sm:ml-auto\">\n <Select\n value={effectiveProjectId}\n onValueChange={(v) => {\n setSelectedProjectId(v);\n setSelectedSprintId('');\n }}\n >\n <SelectTrigger className=\"h-8 w-48\">\n <SelectValue\n placeholder={t(\n 'planmagnet.analytics.delivery.projectPlaceholder',\n 'Select project'\n )}\n />\n </SelectTrigger>\n <SelectContent>\n {projects.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={effectiveSprintId} onValueChange={setSelectedSprintId}>\n <SelectTrigger className=\"h-8 w-48\">\n <SelectValue\n placeholder={t('planmagnet.analytics.delivery.sprintPlaceholder', 'Select sprint')}\n />\n </SelectTrigger>\n <SelectContent>\n {sprintsForProject.map((s) => (\n <SelectItem key={s.id} value={s.id}>\n {s.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n {t('planmagnet.analytics.burndown.title', 'Sprint Burndown')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {burndownLoading ? (\n <div className=\"flex justify-center py-12\">\n <div className=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n ) : burndownChartData.length > 0 ? (\n <ResponsiveContainer width=\"100%\" height={220}>\n <LineChart data={burndownChartData}>\n <CartesianGrid strokeDasharray=\"3 3\" opacity={0.2} />\n <XAxis dataKey=\"day\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} allowDecimals={false} />\n <Tooltip />\n <Legend wrapperStyle={{ fontSize: 12 }} />\n <Line\n type=\"monotone\"\n dataKey=\"remaining\"\n name={t('planmagnet.analytics.burndown.remaining', 'Remaining points')}\n stroke=\"#f59e0b\"\n strokeWidth={2}\n dot={{ r: 2 }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"completed\"\n name={t('planmagnet.analytics.burndown.completed', 'Completed points')}\n stroke=\"#22c55e\"\n strokeWidth={2}\n dot={{ r: 2 }}\n />\n </LineChart>\n </ResponsiveContainer>\n ) : (\n <p className=\"text-sm text-muted-foreground py-8 text-center\">\n {t(\n 'planmagnet.analytics.burndown.empty',\n 'No sprint selected, or the sprint has no work items yet'\n )}\n </p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <TrendingUp className=\"h-4 w-4\" />\n {t('planmagnet.analytics.velocity.title', 'Sprint Velocity')} (\n {velocityChartData.length} {t('planmagnet.analytics.velocity.sprints', 'Sprints')})\n </CardTitle>\n </CardHeader>\n <CardContent>\n {velocityLoading ? (\n <div className=\"flex justify-center py-12\">\n <div className=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n ) : velocityChartData.length > 0 ? (\n <>\n <ResponsiveContainer width=\"100%\" height={190}>\n <BarChart data={velocityChartData}>\n <CartesianGrid strokeDasharray=\"3 3\" opacity={0.2} />\n <XAxis dataKey=\"name\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} allowDecimals={false} />\n <Tooltip />\n <Legend wrapperStyle={{ fontSize: 12 }} />\n <Bar\n dataKey=\"planned\"\n name={t('planmagnet.analytics.velocity.planned', 'Planned')}\n fill=\"#cbd5e1\"\n />\n <Bar\n dataKey=\"completed\"\n name={t('planmagnet.analytics.velocity.completed', 'Completed')}\n fill=\"#6366f1\"\n />\n <ReferenceLine\n y={avgVelocity}\n stroke=\"#22c55e\"\n strokeDasharray=\"4 4\"\n label={{\n value: t('planmagnet.analytics.velocity.avgLabel', 'avg'),\n fontSize: 10,\n position: 'right',\n }}\n />\n </BarChart>\n </ResponsiveContainer>\n <p className=\"text-xs text-muted-foreground text-center mt-2\">\n {t(\n 'planmagnet.analytics.velocity.forecast',\n 'Forecast for next sprint: {{points}} pts (trailing average)',\n { points: forecastNextSprint }\n )}\n </p>\n </>\n ) : (\n <p className=\"text-sm text-muted-foreground py-8 text-center\">\n {t(\n 'planmagnet.analytics.velocity.empty',\n 'No completed sprints yet for this project'\n )}\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n {t('planmagnet.analytics.cfd.title', 'Cumulative Flow Diagram')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {cfdLoading ? (\n <div className=\"flex justify-center py-12\">\n <div className=\"animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n ) : cfdChartData.length > 0 ? (\n <ResponsiveContainer width=\"100%\" height={260}>\n <AreaChart data={cfdChartData}>\n <CartesianGrid strokeDasharray=\"3 3\" opacity={0.2} />\n <XAxis dataKey=\"day\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} allowDecimals={false} />\n <Tooltip />\n <Legend wrapperStyle={{ fontSize: 12 }} />\n {cfdStatusKeys.map((status) => (\n <Area\n key={status}\n type=\"monotone\"\n dataKey={status}\n name={status.toLowerCase().replace('_', ' ')}\n stackId=\"cfd\"\n stroke={CFD_STATUS_COLORS[status] ?? '#a3a3a3'}\n fill={CFD_STATUS_COLORS[status] ?? '#a3a3a3'}\n fillOpacity={0.7}\n />\n ))}\n </AreaChart>\n </ResponsiveContainer>\n ) : (\n <p className=\"text-sm text-muted-foreground py-8 text-center\">\n {t('planmagnet.analytics.cfd.empty', 'No work items found for this project')}\n </p>\n )}\n </CardContent>\n </Card>\n {/* Cycle time follows the delivery selector above; budgets are workspace-wide. */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <CycleTimeCard workspaceId={workspaceId} projectId={effectiveProjectId} />\n <BudgetPerformanceCard workspaceId={workspaceId} />\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{t('planmagnet.analytics.projectHealth.title', 'Project Health')}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {projects\n .filter((p) => p.status === 'ACTIVE')\n .map((p) => {\n // Per project, from the server. This used every ASSIGNED item in\n // the workspace for every project, so all bars were identical.\n const progress = projectProgress(p.id);\n const hasOverdue = p.endDate && new Date(p.endDate) < new Date() && progress < 100;\n const health = hasOverdue ? 'at_risk' : progress >= 75 ? 'on_track' : 'in_progress';\n return (\n <div key={p.id} className=\"flex items-center gap-4\">\n <span className=\"text-sm font-medium w-40 truncate\">{p.name}</span>\n <div className=\"flex-1\">\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${health === 'at_risk' ? 'bg-orange-400' : health === 'on_track' ? 'bg-green-400' : 'bg-blue-400'}`}\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n <span className=\"text-sm w-12 text-right\">{progress}%</span>\n <Badge variant=\"outline\" className=\"capitalize text-xs w-20 justify-center\">\n {health.replace('_', ' ')}\n </Badge>\n </div>\n );\n })}\n {projects.filter((p) => p.status === 'ACTIVE').length === 0 && (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n {t('planmagnet.analytics.projectHealth.empty', 'No active projects found')}\n </p>\n )}\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardDashboardPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/DashboardDashboardPagePage.tsx"],"names":[],"mappings":"AAgGA,wBAAgB,0BAA0B,4CAqWzC"}
1
+ {"version":3,"file":"DashboardDashboardPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/DashboardDashboardPagePage.tsx"],"names":[],"mappings":"AA8HA,wBAAgB,0BAA0B,4CAqXzC"}