@burdenoff/microfe-planmagnet 2026.1001.5 → 2026.1001.7

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 (126) hide show
  1. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoutes.js +10 -6
  3. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  4. package/dist/planmagnet/automation/automationRuleTypes.d.ts +31 -19
  5. package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -1
  6. package/dist/planmagnet/automation/automationRuleTypes.js +4 -8
  7. package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -1
  8. package/dist/planmagnet/controls/CompetitorControls.d.ts +71 -0
  9. package/dist/planmagnet/controls/CompetitorControls.d.ts.map +1 -1
  10. package/dist/planmagnet/controls/CompetitorControls.js +346 -12
  11. package/dist/planmagnet/controls/CompetitorControls.js.map +1 -1
  12. package/dist/planmagnet/controls/FeatureControls.d.ts +23 -1
  13. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
  14. package/dist/planmagnet/controls/FeatureControls.js +431 -229
  15. package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
  16. package/dist/planmagnet/controls/FeedbackControls.d.ts +70 -0
  17. package/dist/planmagnet/controls/FeedbackControls.d.ts.map +1 -0
  18. package/dist/planmagnet/controls/FeedbackControls.js +153 -0
  19. package/dist/planmagnet/controls/FeedbackControls.js.map +1 -0
  20. package/dist/planmagnet/controls/Pickers.d.ts +15 -0
  21. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
  22. package/dist/planmagnet/controls/Pickers.js +61 -1
  23. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  24. package/dist/planmagnet/controls/PrioritizationControls.d.ts +36 -0
  25. package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -1
  26. package/dist/planmagnet/controls/PrioritizationControls.js +118 -1
  27. package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -1
  28. package/dist/planmagnet/controls/SyncControls.d.ts.map +1 -1
  29. package/dist/planmagnet/controls/SyncControls.js +1 -0
  30. package/dist/planmagnet/controls/SyncControls.js.map +1 -1
  31. package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts +47 -0
  32. package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts.map +1 -0
  33. package/dist/planmagnet/controls/WebhookSubscriptionControls.js +542 -0
  34. package/dist/planmagnet/controls/WebhookSubscriptionControls.js.map +1 -0
  35. package/dist/planmagnet/dashboards/DashboardUI.js +8 -8
  36. package/dist/planmagnet/dashboards/cards/RiskSeverityCard.js +4 -4
  37. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.d.ts.map +1 -1
  38. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js +67 -59
  39. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js.map +1 -1
  40. package/dist/planmagnet/forecast/DeliveryForecastCard.js +8 -8
  41. package/dist/planmagnet/forecast/ProjectOutlookCard.js +10 -10
  42. package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js +9 -9
  43. package/dist/planmagnet/forecast/ResourceForecastCard.js +6 -6
  44. package/dist/planmagnet/integrations/syncTypes.d.ts +2 -1
  45. package/dist/planmagnet/integrations/syncTypes.d.ts.map +1 -1
  46. package/dist/planmagnet/integrations/syncTypes.js +19 -9
  47. package/dist/planmagnet/integrations/syncTypes.js.map +1 -1
  48. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  49. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +290 -283
  50. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  51. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.d.ts.map +1 -1
  52. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js +126 -63
  53. package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js.map +1 -1
  54. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.d.ts.map +1 -1
  55. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js +14 -0
  56. package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js.map +1 -1
  57. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.d.ts.map +1 -1
  58. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js +93 -57
  59. package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js.map +1 -1
  60. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.d.ts.map +1 -1
  61. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js +125 -83
  62. package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js.map +1 -1
  63. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +23 -23
  64. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  65. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +340 -248
  66. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  67. package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.d.ts.map +1 -1
  68. package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js +172 -167
  69. package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js.map +1 -1
  70. package/dist/planmagnet/pages/IntegrationsSyncsPagePage.d.ts.map +1 -1
  71. package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js +92 -83
  72. package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js.map +1 -1
  73. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts +2 -0
  74. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts.map +1 -0
  75. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js +175 -0
  76. package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js.map +1 -0
  77. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.d.ts.map +1 -1
  78. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +226 -126
  79. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js.map +1 -1
  80. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  81. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +151 -126
  82. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  83. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  84. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +169 -122
  85. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  86. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  87. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +165 -109
  88. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  89. package/dist/planmagnet/pages/ProductsProductsPagePage.js +13 -13
  90. package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js +9 -9
  91. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +8 -8
  92. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  93. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +15 -3
  94. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  95. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +12 -12
  96. package/dist/planmagnet/pages/ReportsReportsPagePage.js +23 -23
  97. package/dist/planmagnet/pages/TasksMyDashboardPagePage.js +18 -18
  98. package/dist/planmagnet/pages/TasksMyWorkPagePage.js +10 -10
  99. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +5 -5
  100. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  101. package/dist/planmagnet/pages/TeamTeamPagePage.js +59 -53
  102. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  103. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -8
  104. package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +6 -6
  105. package/dist/planmagnet/pages/analytics/MetricsCard.d.ts +8 -0
  106. package/dist/planmagnet/pages/analytics/MetricsCard.d.ts.map +1 -0
  107. package/dist/planmagnet/pages/analytics/MetricsCard.js +164 -0
  108. package/dist/planmagnet/pages/analytics/MetricsCard.js.map +1 -0
  109. package/dist/planmagnet/pages/analytics/QualityMetricsCard.js +6 -6
  110. package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js +8 -8
  111. package/dist/planmagnet/pages/analytics/StatusFlowCard.js +7 -7
  112. package/dist/planmagnet/pages/analytics/ThroughputCard.js +7 -7
  113. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +7 -7
  114. package/dist/planmagnet/people/People.d.ts +14 -0
  115. package/dist/planmagnet/people/People.d.ts.map +1 -1
  116. package/dist/planmagnet/people/People.js +36 -22
  117. package/dist/planmagnet/people/People.js.map +1 -1
  118. package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts +2 -0
  119. package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts.map +1 -0
  120. package/dist/planmagnet/team/UnmatchedCrewIdentities.js +155 -0
  121. package/dist/planmagnet/team/UnmatchedCrewIdentities.js.map +1 -0
  122. package/dist/planmagnet/webhooks/webhookTypes.d.ts +54 -0
  123. package/dist/planmagnet/webhooks/webhookTypes.d.ts.map +1 -0
  124. package/dist/planmagnet/webhooks/webhookTypes.js +17 -0
  125. package/dist/planmagnet/webhooks/webhookTypes.js.map +1 -0
  126. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PortfoliosPortfolioDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/PortfoliosPortfolioDetailPagePage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Briefcase, Target, AlertTriangle, Clock, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { PortfolioHeaderActions } from '../controls/PortfolioControls';\n\nconst GET_PORTFOLIO = gql`\n query GetPortfolio($id: ID!) {\n planmagnetPortfolio(id: $id) {\n id\n workspaceId\n name\n description\n status\n alignment\n alignmentLabel\n ownerId\n budgetTotal\n currency\n fiscalPeriod\n tags\n createdAt\n updatedAt\n programs {\n id\n name\n status\n }\n projects {\n id\n name\n status\n }\n }\n }\n`;\n\nconst GET_PORTFOLIO_OVERVIEW = gql`\n query GetPortfolioOverview($portfolioId: ID!) {\n planmagnetPortfolioOverview(portfolioId: $portfolioId) {\n portfolioId\n totalPrograms\n totalProjects\n projectsByStatus\n totalBudget\n totalSpent\n budgetUtilization\n healthScore\n atRiskCount\n overdueMilestones\n }\n }\n`;\n\ninterface RelatedNode {\n id: string;\n name: string;\n status: string;\n}\n\ninterface OverviewNode {\n totalPrograms: number;\n totalProjects: number;\n projectsByStatus: Record<string, number>;\n totalBudget: number | null;\n totalSpent: number | null;\n budgetUtilization: number | null;\n healthScore: number | null;\n atRiskCount: number;\n overdueMilestones: number;\n}\n\nexport function PortfoliosPortfolioDetailPagePage() {\n const navigate = usePlanMagnetNavigate();\n const params = useParams<{ id: string }>();\n const portfolioId = params.id ?? '';\n\n const { data, loading, error } = useQuery<{\n planmagnetPortfolio: {\n id: string;\n name: string;\n description: string | null;\n status: string;\n alignment: string | null;\n alignmentLabel: string | null;\n ownerId: string;\n budgetTotal: number | null;\n currency: string | null;\n fiscalPeriod: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n programs: RelatedNode[];\n projects: RelatedNode[];\n } | null;\n }>(GET_PORTFOLIO, { variables: { id: portfolioId }, skip: !portfolioId });\n\n const { data: overviewData } = useQuery<{ planmagnetPortfolioOverview: OverviewNode }>(\n GET_PORTFOLIO_OVERVIEW,\n { variables: { portfolioId }, skip: !portfolioId }\n );\n\n // Only before the first load: a refetch must not blank the page (it unmounts every component on it and\n // loses their state).\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load portfolio: {error.message}</div>;\n }\n\n const portfolio = data?.planmagnetPortfolio;\n if (!portfolio) return <div className=\"p-6 text-muted-foreground\">Portfolio not found.</div>;\n\n const overview = overviewData?.planmagnetPortfolioOverview ?? null;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Briefcase className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{portfolio.name}</h1>\n <Badge variant=\"outline\" className=\"text-xs\">\n {enumLabel(portfolio.status)}\n </Badge>\n </div>\n {portfolio.alignment && (\n <p className=\"text-sm text-muted-foreground mt-1\">\n {portfolio.alignment === 'CUSTOM'\n ? (portfolio.alignmentLabel ?? 'Custom alignment')\n : enumLabel(portfolio.alignment)}\n </p>\n )}\n </div>\n <PortfolioHeaderActions\n portfolioId={portfolio.id}\n name={portfolio.name}\n status={portfolio.status}\n />\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Briefcase className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">\n {overview?.totalPrograms ?? portfolio.programs.length}\n </p>\n <p className=\"text-xs text-muted-foreground\">Programs</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">\n {overview?.totalProjects ?? portfolio.projects.length}\n </p>\n <p className=\"text-xs text-muted-foreground\">Projects</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <AlertTriangle className=\"h-5 w-5 mx-auto text-destructive\" />\n <p className=\"text-2xl font-bold mt-1\">{overview?.atRiskCount ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">At Risk</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Clock className=\"h-5 w-5 mx-auto text-status-warning-text\" />\n <p className=\"text-2xl font-bold mt-1\">{overview?.overdueMilestones ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Overdue Milestones</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Overview</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm\">{portfolio.description ?? 'No description provided.'}</p>\n {overview ? (\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Budget Utilization</p>\n <p className=\"text-lg font-semibold mt-1\">\n {overview.budgetUtilization != null\n ? `${overview.budgetUtilization.toFixed(0)}%`\n : 'No budget data'}\n </p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Health Score</p>\n <p className=\"text-lg font-semibold mt-1\">\n {overview.healthScore != null\n ? overview.healthScore.toFixed(0)\n : 'No projects yet'}\n </p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Total Spent</p>\n <p className=\"text-lg font-semibold mt-1\">\n {overview.totalSpent != null ? overview.totalSpent.toLocaleString() : 'N/A'}\n </p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Projects by Status</p>\n <p className=\"text-sm mt-1\">\n {Object.keys(overview.projectsByStatus).length > 0\n ? Object.entries(overview.projectsByStatus)\n .map(([status, count]) => `${enumLabel(status)}: ${count}`)\n .join(', ')\n : 'No projects yet'}\n </p>\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">Loading overview…</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Programs ({portfolio.programs.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {portfolio.programs.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n No programs are linked to this portfolio yet.\n </p>\n )}\n {portfolio.programs.map((program) => (\n <button\n key={program.id}\n type=\"button\"\n className=\"flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted\"\n onClick={() => navigate(detailPath('/products/program-detail-page', program.id))}\n >\n <span className=\"text-sm font-medium\">{program.name}</span>\n <Badge variant=\"outline\" className=\"text-xs\">\n {enumLabel(program.status)}\n </Badge>\n </button>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Projects ({portfolio.projects.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {portfolio.projects.length === 0 && (\n <div className=\"space-y-1\">\n <p className=\"text-sm text-muted-foreground\">\n No projects are linked to this portfolio yet.\n </p>\n <p className=\"text-sm text-muted-foreground\">\n Open a project and set its Portfolio field to add it here.{' '}\n <Button\n variant=\"link\"\n className=\"h-auto p-0 text-sm\"\n onClick={() => navigate('/projects/projects-page')}\n >\n Go to Projects\n </Button>\n </p>\n </div>\n )}\n {portfolio.projects.map((project) => (\n <button\n key={project.id}\n type=\"button\"\n className=\"flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted\"\n onClick={() => navigate(detailPath('/projects/project-detail-page', project.id))}\n >\n <span className=\"text-sm font-medium\">{project.name}</span>\n <Badge variant=\"outline\" className=\"text-xs\">\n {enumLabel(project.status)}\n </Badge>\n </button>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <PersonName id={portfolio.ownerId} />\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Budget</span>\n <span className=\"font-medium\">\n {portfolio.budgetTotal != null\n ? `${portfolio.currency ?? ''} ${portfolio.budgetTotal.toLocaleString()}`.trim()\n : 'Not set'}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Fiscal Period</span>\n <span>{portfolio.fiscalPeriod ?? 'Not set'}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(portfolio.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(portfolio.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {portfolio.tags.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {portfolio.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PortfoliosPortfolioDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/PortfoliosPortfolioDetailPagePage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Briefcase, Target, AlertTriangle, Clock, Loader2, BarChart3 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { PortfolioHeaderActions } from '../controls/PortfolioControls';\nimport { StatTile } from './analytics/DeliveryHealthCards';\n\nconst GET_PORTFOLIO = gql`\n query GetPortfolio($id: ID!) {\n planmagnetPortfolio(id: $id) {\n id\n workspaceId\n name\n description\n status\n alignment\n alignmentLabel\n ownerId\n budgetTotal\n currency\n fiscalPeriod\n tags\n createdAt\n updatedAt\n programs {\n id\n name\n status\n }\n projects {\n id\n name\n status\n }\n }\n }\n`;\n\nconst GET_PORTFOLIO_OVERVIEW = gql`\n query GetPortfolioOverview($portfolioId: ID!) {\n planmagnetPortfolioOverview(portfolioId: $portfolioId) {\n portfolioId\n totalPrograms\n totalProjects\n projectsByStatus\n totalBudget\n totalSpent\n budgetUtilization\n healthScore\n atRiskCount\n overdueMilestones\n }\n }\n`;\n\n// ANR-12 MVP: a second, server-computed view alongside planmagnetPortfolioOverview above — not a replacement\n// for it (see PlanmagnetPortfolioRollup's own doc comment in graphql/wspace.graphql for exactly where the two\n// agree and where they deliberately don't, e.g. budgetUtilization at a $0 budget: Overview says 0%, this says\n// null). Recomputed on every call, never a stored snapshot.\nconst GET_PORTFOLIO_ROLLUP = gql`\n query GetPortfolioRollup($portfolioId: ID!) {\n planmagnetPortfolioRollup(portfolioId: $portfolioId) {\n portfolioId\n totalProjects\n projectsByStatus\n totalBudget\n totalSpent\n budgetUtilization\n averageSprintOnTimeRate\n averageReleaseOnTimeRate\n averageMilestoneOnTimeRate\n calculatedAt\n }\n }\n`;\n\ninterface RollupNode {\n totalProjects: number;\n projectsByStatus: Record<string, number>;\n totalBudget: number | null;\n totalSpent: number | null;\n budgetUtilization: number | null;\n averageSprintOnTimeRate: number | null;\n averageReleaseOnTimeRate: number | null;\n averageMilestoneOnTimeRate: number | null;\n calculatedAt: string;\n}\n\n/** A rollup rate that is null for a real, documented reason — never a fabricated 0%. */\nfunction RollupRate({\n label,\n rate,\n emptyReason,\n}: {\n label: string;\n rate: number | null;\n emptyReason: string;\n}) {\n return (\n <StatTile\n label={label}\n value={rate == null ? '—' : `${Math.round(rate * 100)}%`}\n hint={rate == null ? emptyReason : undefined}\n />\n );\n}\n\ninterface RelatedNode {\n id: string;\n name: string;\n status: string;\n}\n\ninterface OverviewNode {\n totalPrograms: number;\n totalProjects: number;\n projectsByStatus: Record<string, number>;\n totalBudget: number | null;\n totalSpent: number | null;\n budgetUtilization: number | null;\n healthScore: number | null;\n atRiskCount: number;\n overdueMilestones: number;\n}\n\nexport function PortfoliosPortfolioDetailPagePage() {\n const navigate = usePlanMagnetNavigate();\n const params = useParams<{ id: string }>();\n const portfolioId = params.id ?? '';\n\n const { data, loading, error } = useQuery<{\n planmagnetPortfolio: {\n id: string;\n name: string;\n description: string | null;\n status: string;\n alignment: string | null;\n alignmentLabel: string | null;\n ownerId: string;\n budgetTotal: number | null;\n currency: string | null;\n fiscalPeriod: string | null;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n programs: RelatedNode[];\n projects: RelatedNode[];\n } | null;\n }>(GET_PORTFOLIO, { variables: { id: portfolioId }, skip: !portfolioId });\n\n const { data: overviewData } = useQuery<{ planmagnetPortfolioOverview: OverviewNode }>(\n GET_PORTFOLIO_OVERVIEW,\n { variables: { portfolioId }, skip: !portfolioId }\n );\n\n const {\n data: rollupData,\n loading: rollupLoading,\n error: rollupError,\n } = useQuery<{\n planmagnetPortfolioRollup: RollupNode;\n }>(GET_PORTFOLIO_ROLLUP, { variables: { portfolioId }, skip: !portfolioId });\n\n // Only before the first load: a refetch must not blank the page (it unmounts every component on it and\n // loses their state).\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load portfolio: {error.message}</div>;\n }\n\n const portfolio = data?.planmagnetPortfolio;\n if (!portfolio) return <div className=\"p-6 text-muted-foreground\">Portfolio not found.</div>;\n\n const overview = overviewData?.planmagnetPortfolioOverview ?? null;\n const rollup = rollupData?.planmagnetPortfolioRollup ?? null;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Briefcase className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{portfolio.name}</h1>\n <Badge variant=\"outline\" className=\"text-xs\">\n {enumLabel(portfolio.status)}\n </Badge>\n </div>\n {portfolio.alignment && (\n <p className=\"text-sm text-muted-foreground mt-1\">\n {portfolio.alignment === 'CUSTOM'\n ? (portfolio.alignmentLabel ?? 'Custom alignment')\n : enumLabel(portfolio.alignment)}\n </p>\n )}\n </div>\n <PortfolioHeaderActions\n portfolioId={portfolio.id}\n name={portfolio.name}\n status={portfolio.status}\n />\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Briefcase className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">\n {overview?.totalPrograms ?? portfolio.programs.length}\n </p>\n <p className=\"text-xs text-muted-foreground\">Programs</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">\n {overview?.totalProjects ?? portfolio.projects.length}\n </p>\n <p className=\"text-xs text-muted-foreground\">Projects</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <AlertTriangle className=\"h-5 w-5 mx-auto text-destructive\" />\n <p className=\"text-2xl font-bold mt-1\">{overview?.atRiskCount ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">At Risk</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Clock className=\"h-5 w-5 mx-auto text-status-warning-text\" />\n <p className=\"text-2xl font-bold mt-1\">{overview?.overdueMilestones ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Overdue Milestones</p>\n </CardContent>\n </Card>\n </div>\n\n {/* ANR-12 MVP: server-computed rollup across this portfolio's live projects — total, status breakdown,\n planned vs. spent with utilisation %, and average on-time rates. A different, additive view from the\n Overview stats above (see GET_PORTFOLIO_ROLLUP's own doc comment for exactly where the two differ). */}\n <Card data-testid=\"portfolio-rollup-card\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n Portfolio Rollup\n </CardTitle>\n <p className=\"text-xs text-muted-foreground\">\n Recomputed on every load across every live project in this portfolio.\n </p>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {rollupLoading && !rollup ? (\n <p className=\"text-sm text-muted-foreground\">Loading…</p>\n ) : rollupError ? (\n <p className=\"text-sm text-status-error-text\">\n Rollup could not be loaded: {rollupError.message}\n </p>\n ) : !rollup ? null : (\n <>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <StatTile label=\"Total projects\" value={String(rollup.totalProjects)} />\n <StatTile\n label=\"Status breakdown\"\n value={\n Object.keys(rollup.projectsByStatus).length === 0\n ? '—'\n : Object.entries(rollup.projectsByStatus)\n .map(([status, count]) => `${enumLabel(status)}: ${count}`)\n .join(', ')\n }\n hint={\n rollup.totalProjects === 0 ? 'No live projects in this portfolio' : undefined\n }\n />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-3\">\n <StatTile\n label=\"Planned\"\n value={rollup.totalBudget == null ? '—' : rollup.totalBudget.toLocaleString()}\n hint={rollup.totalBudget == null ? 'No budgets set on these projects' : undefined}\n />\n <StatTile\n label=\"Spent\"\n value={rollup.totalSpent == null ? '—' : rollup.totalSpent.toLocaleString()}\n hint={rollup.totalSpent == null ? 'No budgets set on these projects' : undefined}\n />\n <StatTile\n label=\"Utilisation\"\n value={\n rollup.budgetUtilization == null\n ? '—'\n : `${rollup.budgetUtilization.toFixed(0)}%`\n }\n hint={\n rollup.budgetUtilization == null\n ? 'No budget to compare spend against'\n : undefined\n }\n />\n </div>\n <div>\n <h3 className=\"text-sm font-medium mb-1\">\n Average on-time rate (trailing 30 days)\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-3\">\n <RollupRate\n label=\"Sprints\"\n rate={rollup.averageSprintOnTimeRate}\n emptyReason=\"No completed sprint in this window across these projects\"\n />\n <RollupRate\n label=\"Releases\"\n rate={rollup.averageReleaseOnTimeRate}\n emptyReason=\"No completed release in this window across these projects\"\n />\n <RollupRate\n label=\"Milestones\"\n rate={rollup.averageMilestoneOnTimeRate}\n emptyReason=\"No completed milestone in this window across these projects\"\n />\n </div>\n </div>\n </>\n )}\n </CardContent>\n </Card>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Overview</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm\">{portfolio.description ?? 'No description provided.'}</p>\n {overview ? (\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Budget Utilization</p>\n <p className=\"text-lg font-semibold mt-1\">\n {overview.budgetUtilization != null\n ? `${overview.budgetUtilization.toFixed(0)}%`\n : 'No budget data'}\n </p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Health Score</p>\n <p className=\"text-lg font-semibold mt-1\">\n {overview.healthScore != null\n ? overview.healthScore.toFixed(0)\n : 'No projects yet'}\n </p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Total Spent</p>\n <p className=\"text-lg font-semibold mt-1\">\n {overview.totalSpent != null ? overview.totalSpent.toLocaleString() : 'N/A'}\n </p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-xs text-muted-foreground\">Projects by Status</p>\n <p className=\"text-sm mt-1\">\n {Object.keys(overview.projectsByStatus).length > 0\n ? Object.entries(overview.projectsByStatus)\n .map(([status, count]) => `${enumLabel(status)}: ${count}`)\n .join(', ')\n : 'No projects yet'}\n </p>\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">Loading overview…</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Programs ({portfolio.programs.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {portfolio.programs.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">\n No programs are linked to this portfolio yet.\n </p>\n )}\n {portfolio.programs.map((program) => (\n <button\n key={program.id}\n type=\"button\"\n className=\"flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted\"\n onClick={() => navigate(detailPath('/products/program-detail-page', program.id))}\n >\n <span className=\"text-sm font-medium\">{program.name}</span>\n <Badge variant=\"outline\" className=\"text-xs\">\n {enumLabel(program.status)}\n </Badge>\n </button>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Projects ({portfolio.projects.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {portfolio.projects.length === 0 && (\n <div className=\"space-y-1\">\n <p className=\"text-sm text-muted-foreground\">\n No projects are linked to this portfolio yet.\n </p>\n <p className=\"text-sm text-muted-foreground\">\n Open a project and set its Portfolio field to add it here.{' '}\n <Button\n variant=\"link\"\n className=\"h-auto p-0 text-sm\"\n onClick={() => navigate('/projects/projects-page')}\n >\n Go to Projects\n </Button>\n </p>\n </div>\n )}\n {portfolio.projects.map((project) => (\n <button\n key={project.id}\n type=\"button\"\n className=\"flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted\"\n onClick={() => navigate(detailPath('/projects/project-detail-page', project.id))}\n >\n <span className=\"text-sm font-medium\">{project.name}</span>\n <Badge variant=\"outline\" className=\"text-xs\">\n {enumLabel(project.status)}\n </Badge>\n </button>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <PersonName id={portfolio.ownerId} />\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Budget</span>\n <span className=\"font-medium\">\n {portfolio.budgetTotal != null\n ? `${portfolio.currency ?? ''} ${portfolio.budgetTotal.toLocaleString()}`.trim()\n : 'Not set'}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Fiscal Period</span>\n <span>{portfolio.fiscalPeriod ?? 'Not set'}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(portfolio.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(portfolio.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {portfolio.tags.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {portfolio.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,IAAM,IAAgB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BnB,IAAyB,CAAG;;;;;;;;;;;;;;;GAqB5B,IAAuB,CAAG;;;;;;;;;;;;;;;;AA8BhC,SAAS,EAAW,EAClB,UACA,SACA,kBAKC;AACD,QACE,kBAAC,GAAD;EACS;EACP,OAAO,KAAQ,OAAO,MAAM,GAAG,KAAK,MAAM,IAAO,IAAI,CAAC;EACtD,MAAM,KAAQ,OAAO,IAAc,KAAA;EACnC,CAAA;;AAsBN,SAAgB,IAAoC;CAClD,IAAM,IAAW,GAAuB,EAElC,IADS,GAA2B,CACf,MAAM,IAE3B,EAAE,SAAM,YAAS,aAAU,EAkB9B,GAAe;EAAE,WAAW,EAAE,IAAI,GAAa;EAAE,MAAM,CAAC;EAAa,CAAC,EAEnE,EAAE,MAAM,MAAiB,EAC7B,GACA;EAAE,WAAW,EAAE,gBAAa;EAAE,MAAM,CAAC;EAAa,CACnD,EAEK,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAED,GAAsB;EAAE,WAAW,EAAE,gBAAa;EAAE,MAAM,CAAC;EAAa,CAAC;AAI5E,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;EAC9D,CAAA;AAIV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAsC,8BAA2B,EAAM,QAAc;;CAG9F,IAAM,IAAY,GAAM;AACxB,KAAI,CAAC,EAAW,QAAO,kBAAC,OAAD;EAAK,WAAU;YAA4B;EAA0B,CAAA;CAE5F,IAAM,IAAW,GAAc,+BAA+B,MACxD,IAAS,GAAY,6BAA6B;AAExD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAW,WAAU,wBAAyB,CAAA;MAC9C,kBAAC,MAAD;OAAI,WAAU;iBAA0B,EAAU;OAAU,CAAA;MAC5D,kBAAC,GAAD;OAAO,SAAQ;OAAU,WAAU;iBAChC,EAAU,EAAU,OAAO;OACtB,CAAA;MACJ;QACL,EAAU,aACT,kBAAC,KAAD;KAAG,WAAU;eACV,EAAU,cAAc,WACpB,EAAU,kBAAkB,qBAC7B,EAAU,EAAU,UAAU;KAChC,CAAA,CAEF,EAAA,CAAA,EACN,kBAAC,GAAD;KACE,aAAa,EAAU;KACvB,MAAM,EAAU;KAChB,QAAQ,EAAU;KAClB,CAAA,CACE;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,GAAD,EAAW,WAAU,gCAAiC,CAAA;OACtD,kBAAC,KAAD;QAAG,WAAU;kBACV,GAAU,iBAAiB,EAAU,SAAS;QAC7C,CAAA;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAgC;QAAY,CAAA;OAC7C;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,GAAD,EAAQ,WAAU,gCAAiC,CAAA;OACnD,kBAAC,KAAD;QAAG,WAAU;kBACV,GAAU,iBAAiB,EAAU,SAAS;QAC7C,CAAA;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAgC;QAAY,CAAA;OAC7C;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,GAAD,EAAe,WAAU,oCAAqC,CAAA;OAC9D,kBAAC,KAAD;QAAG,WAAU;kBAA2B,GAAU,eAAe;QAAM,CAAA;OACvE,kBAAC,KAAD;QAAG,WAAU;kBAAgC;QAAW,CAAA;OAC5C;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,GAAD,EAAO,WAAU,4CAA6C,CAAA;OAC9D,kBAAC,KAAD;QAAG,WAAU;kBAA2B,GAAU,qBAAqB;QAAM,CAAA;OAC7E,kBAAC,KAAD;QAAG,WAAU;kBAAgC;QAAsB,CAAA;OACvD;SACT,CAAA;KACH;;GAKN,kBAAC,GAAD;IAAM,eAAY;cAAlB,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAW,WAAU;eAArB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,mBAEvB;QACZ,kBAAC,KAAD;KAAG,WAAU;eAAgC;KAEzC,CAAA,CACO,EAAA,CAAA,EACb,kBAAC,GAAD;KAAa,WAAU;eACpB,KAAiB,CAAC,IACjB,kBAAC,KAAD;MAAG,WAAU;gBAAgC;MAAY,CAAA,GACvD,IACF,kBAAC,KAAD;MAAG,WAAU;gBAAb,CAA8C,gCACf,EAAY,QACvC;UACD,IACH,kBAAA,GAAA,EAAA,UAAA;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAU,OAAM;QAAiB,OAAO,OAAO,EAAO,cAAc;QAAI,CAAA,EACxE,kBAAC,GAAD;QACE,OAAM;QACN,OACE,OAAO,KAAK,EAAO,iBAAiB,CAAC,WAAW,IAC5C,MACA,OAAO,QAAQ,EAAO,iBAAiB,CACpC,KAAK,CAAC,GAAQ,OAAW,GAAG,EAAU,EAAO,CAAC,IAAI,IAAQ,CAC1D,KAAK,KAAK;QAEnB,MACE,EAAO,kBAAkB,IAAI,uCAAuC,KAAA;QAEtE,CAAA,CACE;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SACE,OAAM;SACN,OAAO,EAAO,eAAe,OAAO,MAAM,EAAO,YAAY,gBAAgB;SAC7E,MAAM,EAAO,eAAe,OAAO,qCAAqC,KAAA;SACxE,CAAA;QACF,kBAAC,GAAD;SACE,OAAM;SACN,OAAO,EAAO,cAAc,OAAO,MAAM,EAAO,WAAW,gBAAgB;SAC3E,MAAM,EAAO,cAAc,OAAO,qCAAqC,KAAA;SACvE,CAAA;QACF,kBAAC,GAAD;SACE,OAAM;SACN,OACE,EAAO,qBAAqB,OACxB,MACA,GAAG,EAAO,kBAAkB,QAAQ,EAAE,CAAC;SAE7C,MACE,EAAO,qBAAqB,OACxB,uCACA,KAAA;SAEN,CAAA;QACE;;MACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA2B;OAEpC,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SACE,OAAM;SACN,MAAM,EAAO;SACb,aAAY;SACZ,CAAA;QACF,kBAAC,GAAD;SACE,OAAM;SACN,MAAM,EAAO;SACb,aAAY;SACZ,CAAA;QACF,kBAAC,GAAD;SACE,OAAM;SACN,MAAM,EAAO;SACb,aAAY;SACZ,CAAA;QACE;SACF,EAAA,CAAA;MACL,EAAA,CAAA,GAjES;KAmEF,CAAA,CACT;;GAEP,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA,EACpB,CAAA,EACb,kBAAC,GAAD;OAAa,WAAU;iBAAvB,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAW,EAAU,eAAe;QAA+B,CAAA,EAC/E,IACC,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAgC;WAAsB,CAAA,EACnE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAS,qBAAqB,OAE3B,mBADA,GAAG,EAAS,kBAAkB,QAAQ,EAAE,CAAC;WAE3C,CAAA,CACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAgC;WAAgB,CAAA,EAC7D,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAS,eAAe,OAErB,oBADA,EAAS,YAAY,QAAQ,EAAE;WAEjC,CAAA,CACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAgC;WAAe,CAAA,EAC5D,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAS,cAAc,OAA8C,QAAvC,EAAS,WAAW,gBAAgB;WACjE,CAAA,CACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAgC;WAAsB,CAAA,EACnE,kBAAC,KAAD;WAAG,WAAU;qBACV,OAAO,KAAK,EAAS,iBAAiB,CAAC,SAAS,IAC7C,OAAO,QAAQ,EAAS,iBAAiB,CACtC,KAAK,CAAC,GAAQ,OAAW,GAAG,EAAU,EAAO,CAAC,IAAI,IAAQ,CAC1D,KAAK,KAAK,GACb;WACF,CAAA,CACA;;SACF;YAEN,kBAAC,KAAD;QAAG,WAAU;kBAAgC;QAAqB,CAAA,CAExD;SACT,EAAA,CAAA;MAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;OAAW;OAAW,EAAU,SAAS;OAAO;OAAa,EAAA,CAAA,EAClD,CAAA,EACb,kBAAC,GAAD;OAAa,WAAU;iBAAvB,CACG,EAAU,SAAS,WAAW,KAC7B,kBAAC,KAAD;QAAG,WAAU;kBAAgC;QAEzC,CAAA,EAEL,EAAU,SAAS,KAAK,MACvB,kBAAC,UAAD;QAEE,MAAK;QACL,WAAU;QACV,eAAe,EAAS,EAAW,iCAAiC,EAAQ,GAAG,CAAC;kBAJlF,CAME,kBAAC,QAAD;SAAM,WAAU;mBAAuB,EAAQ;SAAY,CAAA,EAC3D,kBAAC,GAAD;SAAO,SAAQ;SAAU,WAAU;mBAChC,EAAU,EAAQ,OAAO;SACpB,CAAA,CACD;UATF,EAAQ,GASN,CACT,CACU;SACT,EAAA,CAAA;MAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;OAAW;OAAW,EAAU,SAAS;OAAO;OAAa,EAAA,CAAA,EAClD,CAAA,EACb,kBAAC,GAAD;OAAa,WAAU;iBAAvB,CACG,EAAU,SAAS,WAAW,KAC7B,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAEzC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb;UAA6C;UACgB;UAC3D,kBAAC,GAAD;WACE,SAAQ;WACR,WAAU;WACV,eAAe,EAAS,0BAA0B;qBACnD;WAEQ,CAAA;UACP;WACA;WAEP,EAAU,SAAS,KAAK,MACvB,kBAAC,UAAD;QAEE,MAAK;QACL,WAAU;QACV,eAAe,EAAS,EAAW,iCAAiC,EAAQ,GAAG,CAAC;kBAJlF,CAME,kBAAC,QAAD;SAAM,WAAU;mBAAuB,EAAQ;SAAY,CAAA,EAC3D,kBAAC,GAAD;SAAO,SAAQ;SAAU,WAAU;mBAChC,EAAU,EAAQ,OAAO;SACpB,CAAA,CACD;UATF,EAAQ,GASN,CACT,CACU;SACT,EAAA,CAAA;MACH;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA,EACnB,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAY,CAAA,EACpD,kBAAC,GAAD,EAAY,IAAI,EAAU,SAAW,CAAA,CACjC;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAa,CAAA,EACrD,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAU,eAAe,OAEtB,YADA,GAAG,EAAU,YAAY,GAAG,GAAG,EAAU,YAAY,gBAAgB,GAAG,MAAM;SAE7E,CAAA,CACH;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAoB,CAAA,EAC5D,kBAAC,QAAD,EAAA,UAAO,EAAU,gBAAgB,WAAiB,CAAA,CAC9C;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAc,CAAA,EACtD,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAU,UAAU,CAAC,oBAAoB,EAAQ,CAAA,CAC7D;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAc,CAAA,EACtD,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAU,UAAU,CAAC,oBAAoB,EAAQ,CAAA,CAC7D;;OACM;QACT,EAAA,CAAA,EAEN,EAAU,KAAK,SAAS,KACvB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,QAAgB,CAAA,EAChB,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBACpB,EAAU,KAAK,KAAK,MACnB,kBAAC,GAAD;OAAiB,SAAQ;OAAU,WAAU;iBAC1C;OACK,EAFI,EAEJ,CACR;MACU,CAAA,CACT,EAAA,CAAA,CAEL;OACF;;GACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsFeatureDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"names":[],"mappings":"AA6FA,wBAAgB,6BAA6B,4CA4Q5C"}
1
+ {"version":3,"file":"ProductsFeatureDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"names":[],"mappings":"AAqGA,wBAAgB,6BAA6B,4CA0R5C"}
@@ -3,17 +3,18 @@ import { PersonName as n } from "../people/People.js";
3
3
  import { detailPath as r } from "../utils/rowNavigation.js";
4
4
  import { CompetitorLinkCard as i } from "../controls/CompetitorControls.js";
5
5
  import { FeatureTriageActions as a, isOpenRequest as o, isSnoozed as s } from "../controls/FeatureTriage.js";
6
- import { FeatureHeaderActions as c, FeaturePrioritizationCard as l, FeatureVoteButton as u } from "../controls/FeatureControls.js";
7
- import { FeatureInitiativeCard as d } from "../roadmaps/FeatureInitiativeCard.js";
8
- import { useState as f } from "react";
9
- import { Loader2 as p, MessageSquare as m, Star as h, Target as g, ThumbsUp as _ } from "lucide-react";
10
- import { useParams as v } from "react-router";
11
- import { gql as y } from "@apollo/client";
12
- import { useQuery as b } from "@apollo/client/react";
13
- import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
14
- import { Badge as w, Card as T, CardContent as E, CardHeader as D, CardTitle as O } from "@burdenoff/fe-libs/ui";
6
+ import { CategoryBadge as c, RatingDisplay as l } from "../controls/FeedbackControls.js";
7
+ import { FeatureHeaderActions as u, FeaturePrioritizationCard as d, FeatureVoteButton as f } from "../controls/FeatureControls.js";
8
+ import { FeatureInitiativeCard as p } from "../roadmaps/FeatureInitiativeCard.js";
9
+ import { useState as m } from "react";
10
+ import { Loader2 as h, MessageSquare as g, Star as _, Target as v, ThumbsUp as y } from "lucide-react";
11
+ import { useParams as b } from "react-router";
12
+ import { gql as x } from "@apollo/client";
13
+ import { useQuery as S } from "@apollo/client/react";
14
+ import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
15
+ import { Badge as E, Card as D, CardContent as O, CardHeader as k, CardTitle as A } from "@burdenoff/fe-libs/ui";
15
16
  //#region src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx
16
- var k = y`
17
+ var j = x`
17
18
  query GetFeatureRequest($id: ID!) {
18
19
  planmagnetFeatureRequest(id: $id) {
19
20
  id
@@ -30,11 +31,18 @@ var k = y`
30
31
  workItemId
31
32
  tags
32
33
  snoozedUntil
34
+ rating
35
+ category
36
+ isAnonymous
33
37
  moscow
34
38
  kano
35
39
  valueScore
36
40
  effortScore
37
41
  valueEffortQuadrant
42
+ reach
43
+ impact
44
+ confidence
45
+ riceScore
38
46
  declineReason
39
47
  duplicateOfId
40
48
  duplicateOf {
@@ -70,88 +78,88 @@ var k = y`
70
78
  }
71
79
  }
72
80
  `;
73
- function A() {
74
- let { workspaceId: y, currentUser: A } = e(), j = t(), [M] = f(() => Date.now()), N = v().id ?? "", { data: P, loading: F, error: I } = b(k, {
75
- variables: { id: N },
76
- skip: !N
81
+ function M() {
82
+ let { workspaceId: x, currentUser: M } = e(), N = t(), [P] = m(() => Date.now()), F = b().id ?? "", { data: I, loading: L, error: R } = S(j, {
83
+ variables: { id: F },
84
+ skip: !F
77
85
  });
78
- if (F && !P) return /* @__PURE__ */ S("div", {
86
+ if (L && !I) return /* @__PURE__ */ w("div", {
79
87
  className: "flex items-center justify-center p-12",
80
- children: /* @__PURE__ */ S(p, { className: "h-8 w-8 animate-spin text-muted-foreground" })
88
+ children: /* @__PURE__ */ w(h, { className: "h-8 w-8 animate-spin text-muted-foreground" })
81
89
  });
82
- if (I) return /* @__PURE__ */ C("div", {
90
+ if (R) return /* @__PURE__ */ T("div", {
83
91
  className: "p-6 text-destructive",
84
- children: ["Failed to load feature: ", I.message]
92
+ children: ["Failed to load feature: ", R.message]
85
93
  });
86
- let L = P?.planmagnetFeatureRequest;
87
- if (!L) return /* @__PURE__ */ S("div", {
94
+ let z = I?.planmagnetFeatureRequest;
95
+ if (!z) return /* @__PURE__ */ w("div", {
88
96
  className: "p-6 text-muted-foreground",
89
97
  children: "Feature not found."
90
98
  });
91
- let R = L.votes ?? [], z = L.competitors ?? [];
92
- return /* @__PURE__ */ C("div", {
99
+ let B = z.votes ?? [], V = z.competitors ?? [];
100
+ return /* @__PURE__ */ T("div", {
93
101
  className: "space-y-6 p-6",
94
102
  children: [
95
- /* @__PURE__ */ C("div", {
103
+ /* @__PURE__ */ T("div", {
96
104
  className: "flex items-center justify-between",
97
- children: [/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("h1", {
105
+ children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("h1", {
98
106
  className: "text-2xl font-semibold",
99
- children: L.title
100
- }), /* @__PURE__ */ C("p", {
107
+ children: z.title
108
+ }), /* @__PURE__ */ T("p", {
101
109
  className: "text-sm text-muted-foreground",
102
110
  children: [
103
111
  "Submitted by: ",
104
- /* @__PURE__ */ S(n, { id: L.submittedBy }),
112
+ /* @__PURE__ */ w(n, { id: z.submittedBy }),
105
113
  " · Effort:",
106
114
  " ",
107
- L.effortEstimate ?? "N/A"
115
+ z.effortEstimate ?? "N/A"
108
116
  ]
109
- })] }), /* @__PURE__ */ C("div", {
117
+ })] }), /* @__PURE__ */ T("div", {
110
118
  className: "flex flex-col items-end gap-2",
111
- children: [/* @__PURE__ */ S(u, {
112
- featureId: L.id,
113
- voted: R.some((e) => e.voterId === A?.id)
114
- }), /* @__PURE__ */ S(c, { feature: L })]
119
+ children: [/* @__PURE__ */ w(f, {
120
+ featureId: z.id,
121
+ voted: B.some((e) => e.voterId === M?.id)
122
+ }, z.id), /* @__PURE__ */ w(u, { feature: z })]
115
123
  })]
116
124
  }),
117
- o(L.status) && /* @__PURE__ */ C(T, {
125
+ o(z.status) && /* @__PURE__ */ T(D, {
118
126
  "data-testid": "triage-card",
119
- children: [/* @__PURE__ */ S(D, { children: /* @__PURE__ */ S(O, { children: "Triage" }) }), /* @__PURE__ */ C(E, {
127
+ children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Triage" }) }), /* @__PURE__ */ T(O, {
120
128
  className: "space-y-2",
121
- children: [/* @__PURE__ */ S("p", {
129
+ children: [/* @__PURE__ */ w("p", {
122
130
  className: "text-sm text-muted-foreground",
123
- children: s(L.snoozedUntil, M) ? `Snoozed until ${new Date(L.snoozedUntil).toLocaleString()} — it returns to the triage queue then.` : L.status === "SUBMITTED" ? "Waiting for a decision." : "Under review: accept it as planned, decline it, or merge it into its original."
124
- }), /* @__PURE__ */ S(a, {
125
- feature: L,
126
- now: M
131
+ children: s(z.snoozedUntil, P) ? `Snoozed until ${new Date(z.snoozedUntil).toLocaleString()} — it returns to the triage queue then.` : z.status === "SUBMITTED" ? "Waiting for a decision." : "Under review: accept it as planned, decline it, or merge it into its original."
132
+ }), /* @__PURE__ */ w(a, {
133
+ feature: z,
134
+ now: P
127
135
  })]
128
136
  })]
129
137
  }),
130
- L.status === "DECLINED" && L.declineReason && /* @__PURE__ */ S(T, {
138
+ z.status === "DECLINED" && z.declineReason && /* @__PURE__ */ w(D, {
131
139
  "data-testid": "decline-reason",
132
- children: /* @__PURE__ */ C(E, {
140
+ children: /* @__PURE__ */ T(O, {
133
141
  className: "pt-4 text-sm",
134
- children: [/* @__PURE__ */ S("span", {
142
+ children: [/* @__PURE__ */ w("span", {
135
143
  className: "font-medium",
136
144
  children: "Declined: "
137
- }), L.declineReason]
145
+ }), z.declineReason]
138
146
  })
139
147
  }),
140
- L.status === "DUPLICATE" && L.duplicateOfId && /* @__PURE__ */ S(T, {
148
+ z.status === "DUPLICATE" && z.duplicateOfId && /* @__PURE__ */ w(D, {
141
149
  "data-testid": "duplicate-of",
142
- children: /* @__PURE__ */ C(E, {
150
+ children: /* @__PURE__ */ T(O, {
143
151
  className: "pt-4 text-sm",
144
152
  children: [
145
- /* @__PURE__ */ S("span", {
153
+ /* @__PURE__ */ w("span", {
146
154
  className: "font-medium",
147
155
  children: "Duplicate of "
148
156
  }),
149
- L.duplicateOf ? /* @__PURE__ */ S("button", {
157
+ z.duplicateOf ? /* @__PURE__ */ w("button", {
150
158
  type: "button",
151
159
  className: "text-primary underline-offset-2 hover:underline",
152
- onClick: () => j(r("/products/feature-detail-page", L.duplicateOf.id)),
153
- children: L.duplicateOf.title
154
- }) : /* @__PURE__ */ S("span", {
160
+ onClick: () => N(r("/products/feature-detail-page", z.duplicateOf.id)),
161
+ children: z.duplicateOf.title
162
+ }) : /* @__PURE__ */ w("span", {
155
163
  className: "text-muted-foreground",
156
164
  children: "a request that has since been deleted"
157
165
  }),
@@ -160,60 +168,60 @@ function A() {
160
168
  ]
161
169
  })
162
170
  }),
163
- /* @__PURE__ */ C("div", {
171
+ /* @__PURE__ */ T("div", {
164
172
  className: "grid grid-cols-2 md:grid-cols-4 gap-4",
165
173
  children: [
166
- /* @__PURE__ */ S(T, { children: /* @__PURE__ */ C(E, {
174
+ /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
167
175
  className: "pt-4 text-center",
168
176
  children: [
169
- /* @__PURE__ */ S(g, { className: "h-5 w-5 mx-auto text-primary" }),
170
- /* @__PURE__ */ S("p", {
177
+ /* @__PURE__ */ w(v, { className: "h-5 w-5 mx-auto text-primary" }),
178
+ /* @__PURE__ */ w("p", {
171
179
  className: "text-2xl font-bold mt-1",
172
- children: L.priorityScore ?? 0
180
+ children: z.priorityScore ?? 0
173
181
  }),
174
- /* @__PURE__ */ S("p", {
182
+ /* @__PURE__ */ w("p", {
175
183
  className: "text-xs text-muted-foreground",
176
184
  children: "Priority Score"
177
185
  })
178
186
  ]
179
187
  }) }),
180
- /* @__PURE__ */ S(T, { children: /* @__PURE__ */ C(E, {
188
+ /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
181
189
  className: "pt-4 text-center",
182
190
  children: [
183
- /* @__PURE__ */ S(_, { className: "h-5 w-5 mx-auto text-green-500" }),
184
- /* @__PURE__ */ S("p", {
191
+ /* @__PURE__ */ w(y, { className: "h-5 w-5 mx-auto text-green-500" }),
192
+ /* @__PURE__ */ w("p", {
185
193
  className: "text-2xl font-bold mt-1",
186
- children: L.voteCount
194
+ children: z.voteCount
187
195
  }),
188
- /* @__PURE__ */ S("p", {
196
+ /* @__PURE__ */ w("p", {
189
197
  className: "text-xs text-muted-foreground",
190
198
  children: "Votes"
191
199
  })
192
200
  ]
193
201
  }) }),
194
- /* @__PURE__ */ S(T, { children: /* @__PURE__ */ C(E, {
202
+ /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
195
203
  className: "pt-4 text-center",
196
204
  children: [
197
- /* @__PURE__ */ S(h, { className: "h-5 w-5 mx-auto text-yellow-500" }),
198
- /* @__PURE__ */ S("p", {
205
+ /* @__PURE__ */ w(_, { className: "h-5 w-5 mx-auto text-yellow-500" }),
206
+ /* @__PURE__ */ w("p", {
199
207
  className: "text-2xl font-bold mt-1",
200
- children: L.businessValue ?? 0
208
+ children: z.businessValue ?? 0
201
209
  }),
202
- /* @__PURE__ */ S("p", {
210
+ /* @__PURE__ */ w("p", {
203
211
  className: "text-xs text-muted-foreground",
204
212
  children: "Business Value"
205
213
  })
206
214
  ]
207
215
  }) }),
208
- /* @__PURE__ */ S(T, { children: /* @__PURE__ */ C(E, {
216
+ /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
209
217
  className: "pt-4 text-center",
210
218
  children: [
211
- /* @__PURE__ */ S(m, { className: "h-5 w-5 mx-auto text-blue-500" }),
212
- /* @__PURE__ */ S("p", {
219
+ /* @__PURE__ */ w(g, { className: "h-5 w-5 mx-auto text-blue-500" }),
220
+ /* @__PURE__ */ w("p", {
213
221
  className: "text-2xl font-bold mt-1",
214
- children: z.length
222
+ children: V.length
215
223
  }),
216
- /* @__PURE__ */ S("p", {
224
+ /* @__PURE__ */ w("p", {
217
225
  className: "text-xs text-muted-foreground",
218
226
  children: "Competitors"
219
227
  })
@@ -221,130 +229,147 @@ function A() {
221
229
  }) })
222
230
  ]
223
231
  }),
224
- /* @__PURE__ */ C("div", {
232
+ /* @__PURE__ */ T("div", {
225
233
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
226
- children: [/* @__PURE__ */ C("div", {
234
+ children: [/* @__PURE__ */ T("div", {
227
235
  className: "lg:col-span-2 space-y-6",
228
236
  children: [
229
- /* @__PURE__ */ C(T, { children: [/* @__PURE__ */ S(D, { children: /* @__PURE__ */ S(O, { children: "Description" }) }), /* @__PURE__ */ S(E, { children: /* @__PURE__ */ S("p", {
237
+ /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Description" }) }), /* @__PURE__ */ w(O, { children: /* @__PURE__ */ w("p", {
230
238
  className: "text-sm",
231
- children: L.description ?? "No description provided."
239
+ children: z.description ?? "No description provided."
232
240
  }) })] }),
233
- /* @__PURE__ */ C(T, { children: [/* @__PURE__ */ S(D, { children: /* @__PURE__ */ C(O, { children: [
241
+ /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ T(A, { children: [
234
242
  "Votes (",
235
- R.length,
243
+ B.length,
236
244
  ")"
237
- ] }) }), /* @__PURE__ */ C(E, {
245
+ ] }) }), /* @__PURE__ */ T(O, {
238
246
  className: "space-y-3",
239
- children: [R.length === 0 && /* @__PURE__ */ S("p", {
247
+ children: [B.length === 0 && /* @__PURE__ */ w("p", {
240
248
  className: "text-sm text-muted-foreground",
241
249
  children: "No votes yet."
242
- }), R.map((e) => /* @__PURE__ */ C("div", {
250
+ }), B.map((e) => /* @__PURE__ */ T("div", {
243
251
  className: "flex items-start justify-between border rounded-md p-3",
244
- children: [/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ C("p", {
252
+ children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ T("p", {
245
253
  className: "text-sm font-medium",
246
- children: ["Voter: ", /* @__PURE__ */ S(n, { id: e.voterId })]
247
- }), e.comment && /* @__PURE__ */ S("p", {
254
+ children: ["Voter: ", /* @__PURE__ */ w(n, { id: e.voterId })]
255
+ }), e.comment && /* @__PURE__ */ w("p", {
248
256
  className: "text-xs text-muted-foreground mt-1",
249
257
  children: e.comment
250
- })] }), /* @__PURE__ */ C("div", {
258
+ })] }), /* @__PURE__ */ T("div", {
251
259
  className: "flex items-center gap-1",
252
- children: [/* @__PURE__ */ S(_, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ S("span", {
260
+ children: [/* @__PURE__ */ w(y, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ w("span", {
253
261
  className: "text-sm font-bold",
254
262
  children: e.weight
255
263
  })]
256
264
  })]
257
265
  }, e.id))]
258
266
  })] }),
259
- /* @__PURE__ */ C(T, { children: [/* @__PURE__ */ S(D, { children: /* @__PURE__ */ C(O, { children: [
267
+ /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ T(A, { children: [
260
268
  "Competitors (",
261
- z.length,
269
+ V.length,
262
270
  ")"
263
- ] }) }), /* @__PURE__ */ S(E, { children: /* @__PURE__ */ S(i, {
264
- featureRequestId: L.id,
265
- competitors: z
271
+ ] }) }), /* @__PURE__ */ w(O, { children: /* @__PURE__ */ w(i, {
272
+ featureRequestId: z.id,
273
+ competitors: V
266
274
  }) })] })
267
275
  ]
268
- }), /* @__PURE__ */ C("div", {
276
+ }), /* @__PURE__ */ T("div", {
269
277
  className: "space-y-6",
270
278
  children: [
271
- /* @__PURE__ */ S(d, {
272
- featureId: L.id,
273
- productId: L.productId ?? null,
274
- initiative: L.roadmapItem ?? null
279
+ /* @__PURE__ */ w(p, {
280
+ featureId: z.id,
281
+ productId: z.productId ?? null,
282
+ initiative: z.roadmapItem ?? null
275
283
  }),
276
- /* @__PURE__ */ S(l, { feature: L }),
277
- /* @__PURE__ */ C(T, { children: [/* @__PURE__ */ S(D, { children: /* @__PURE__ */ S(O, { children: "Details" }) }), /* @__PURE__ */ C(E, {
284
+ /* @__PURE__ */ w(d, { feature: z }),
285
+ /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Details" }) }), /* @__PURE__ */ T(O, {
278
286
  className: "space-y-3 text-sm",
279
287
  children: [
280
- /* @__PURE__ */ C("div", {
288
+ /* @__PURE__ */ T("div", {
281
289
  className: "flex justify-between",
282
- children: [/* @__PURE__ */ S("span", {
290
+ children: [/* @__PURE__ */ w("span", {
283
291
  className: "text-muted-foreground",
284
292
  children: "Status"
285
- }), /* @__PURE__ */ S(w, {
293
+ }), /* @__PURE__ */ w(E, {
286
294
  variant: "secondary",
287
295
  className: "capitalize",
288
- children: L.status.toLowerCase().replace("_", " ")
296
+ children: z.status.toLowerCase().replace("_", " ")
289
297
  })]
290
298
  }),
291
- /* @__PURE__ */ C("div", {
299
+ /* @__PURE__ */ T("div", {
292
300
  className: "flex justify-between",
293
- children: [/* @__PURE__ */ S("span", {
301
+ children: [/* @__PURE__ */ w("span", {
294
302
  className: "text-muted-foreground",
295
303
  children: "Priority Score"
296
- }), /* @__PURE__ */ S(w, {
304
+ }), /* @__PURE__ */ w(E, {
297
305
  variant: "outline",
298
- children: L.priorityScore ?? "N/A"
306
+ children: z.priorityScore ?? "N/A"
299
307
  })]
300
308
  }),
301
- /* @__PURE__ */ C("div", {
309
+ /* @__PURE__ */ T("div", {
302
310
  className: "flex justify-between",
303
- children: [/* @__PURE__ */ S("span", {
311
+ children: [/* @__PURE__ */ w("span", {
304
312
  className: "text-muted-foreground",
305
313
  children: "Effort"
306
- }), /* @__PURE__ */ S(w, {
314
+ }), /* @__PURE__ */ w(E, {
307
315
  variant: "outline",
308
316
  className: "capitalize",
309
- children: L.effortEstimate ?? "N/A"
317
+ children: z.effortEstimate ?? "N/A"
310
318
  })]
311
319
  }),
312
- /* @__PURE__ */ C("div", {
320
+ /* @__PURE__ */ T("div", {
313
321
  className: "flex justify-between",
314
- children: [/* @__PURE__ */ S("span", {
322
+ children: [/* @__PURE__ */ w("span", {
315
323
  className: "text-muted-foreground",
316
324
  children: "Business Value"
317
- }), /* @__PURE__ */ S("span", {
325
+ }), /* @__PURE__ */ w("span", {
318
326
  className: "font-medium",
319
- children: L.businessValue ?? "N/A"
327
+ children: z.businessValue ?? "N/A"
320
328
  })]
321
329
  }),
322
- /* @__PURE__ */ C("div", {
330
+ /* @__PURE__ */ T("div", {
331
+ className: "flex justify-between items-center",
332
+ children: [/* @__PURE__ */ w("span", {
333
+ className: "text-muted-foreground",
334
+ children: "Rating"
335
+ }), /* @__PURE__ */ w(l, { rating: z.rating ?? null })]
336
+ }),
337
+ /* @__PURE__ */ T("div", {
338
+ className: "flex justify-between items-center",
339
+ children: [/* @__PURE__ */ w("span", {
340
+ className: "text-muted-foreground",
341
+ children: "Category"
342
+ }), z.category ? /* @__PURE__ */ w(c, { category: z.category }) : /* @__PURE__ */ w("span", {
343
+ className: "text-sm text-muted-foreground",
344
+ children: "Not set"
345
+ })]
346
+ }),
347
+ /* @__PURE__ */ T("div", {
323
348
  className: "flex justify-between",
324
- children: [/* @__PURE__ */ S("span", {
349
+ children: [/* @__PURE__ */ w("span", {
325
350
  className: "text-muted-foreground",
326
351
  children: "Submitted By"
327
- }), /* @__PURE__ */ S(n, { id: L.submittedBy })]
352
+ }), /* @__PURE__ */ w(n, { id: z.submittedBy })]
328
353
  }),
329
- L.triagedAt && /* @__PURE__ */ C("div", {
354
+ z.triagedAt && /* @__PURE__ */ T("div", {
330
355
  className: "flex justify-between",
331
- children: [/* @__PURE__ */ S("span", {
356
+ children: [/* @__PURE__ */ w("span", {
332
357
  className: "text-muted-foreground",
333
358
  children: "Decided"
334
- }), /* @__PURE__ */ C("span", { children: [new Date(L.triagedAt).toLocaleDateString(), L.triagedBy && /* @__PURE__ */ C(x, { children: [" by ", /* @__PURE__ */ S(n, { id: L.triagedBy })] })] })]
359
+ }), /* @__PURE__ */ T("span", { children: [new Date(z.triagedAt).toLocaleDateString(), z.triagedBy && /* @__PURE__ */ T(C, { children: [" by ", /* @__PURE__ */ w(n, { id: z.triagedBy })] })] })]
335
360
  }),
336
- /* @__PURE__ */ C("div", {
361
+ /* @__PURE__ */ T("div", {
337
362
  className: "flex justify-between",
338
- children: [/* @__PURE__ */ S("span", {
363
+ children: [/* @__PURE__ */ w("span", {
339
364
  className: "text-muted-foreground",
340
365
  children: "Updated"
341
- }), /* @__PURE__ */ S("span", { children: new Date(L.updatedAt).toLocaleDateString() })]
366
+ }), /* @__PURE__ */ w("span", { children: new Date(z.updatedAt).toLocaleDateString() })]
342
367
  })
343
368
  ]
344
369
  })] }),
345
- L.tags?.length > 0 && /* @__PURE__ */ C(T, { children: [/* @__PURE__ */ S(D, { children: /* @__PURE__ */ S(O, { children: "Tags" }) }), /* @__PURE__ */ S(E, {
370
+ z.tags?.length > 0 && /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Tags" }) }), /* @__PURE__ */ w(O, {
346
371
  className: "flex flex-wrap gap-1",
347
- children: L.tags.map((e) => /* @__PURE__ */ S(w, {
372
+ children: z.tags.map((e) => /* @__PURE__ */ w(E, {
348
373
  variant: "outline",
349
374
  className: "text-xs",
350
375
  children: e
@@ -357,6 +382,6 @@ function A() {
357
382
  });
358
383
  }
359
384
  //#endregion
360
- export { A as ProductsFeatureDetailPagePage };
385
+ export { M as ProductsFeatureDetailPagePage };
361
386
 
362
387
  //# sourceMappingURL=ProductsFeatureDetailPagePage.js.map