@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":"ProductsFeatureDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Star, ThumbsUp, MessageSquare, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { FeatureTriageActions, isOpenRequest, isSnoozed } from '../controls/FeatureTriage';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n FeatureHeaderActions,\n FeaturePrioritizationCard,\n FeatureVoteButton,\n} from '../controls/FeatureControls';\nimport { CompetitorLinkCard } from '../controls/CompetitorControls';\nimport { FeatureInitiativeCard } from '../roadmaps/FeatureInitiativeCard';\n\nconst GET_FEATURE_REQUEST = gql`\n query GetFeatureRequest($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n workspaceId\n projectId\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n workItemId\n tags\n snoozedUntil\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n declineReason\n duplicateOfId\n duplicateOf {\n id\n title\n }\n triagedAt\n triagedBy\n createdAt\n updatedAt\n productId\n roadmapItem {\n id\n title\n roadmap {\n id\n name\n }\n }\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n description\n website\n }\n }\n }\n`;\n\ninterface FeatureVoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\ninterface CompetitorNode {\n id: string;\n name: string;\n description: string | null;\n website: string | null;\n}\n\nexport function ProductsFeatureDetailPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n // Snooze state is read against the moment the page opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST, {\n variables: { id: featureId },\n skip: !featureId,\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 <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 feature: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature not found.</div>;\n\n const votes: FeatureVoteNode[] = feature.votes ?? [];\n const competitors: CompetitorNode[] = feature.competitors ?? [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{feature.title}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Submitted by: <PersonName id={feature.submittedBy} /> &middot; Effort:{' '}\n {feature.effortEstimate ?? 'N/A'}\n </p>\n </div>\n {/* Vote, status, Edit and Delete (BOFF-7951): the request was read-only. */}\n <div className=\"flex flex-col items-end gap-2\">\n <FeatureVoteButton\n featureId={feature.id}\n voted={votes.some((v) => v.voterId === currentUser?.id)}\n />\n <FeatureHeaderActions feature={feature} />\n </div>\n </div>\n\n {/* Triage (P1.3): an undecided request is accepted, declined, snoozed or merged from here. */}\n {isOpenRequest(feature.status) && (\n <Card data-testid=\"triage-card\">\n <CardHeader>\n <CardTitle>Triage</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">\n {isSnoozed(feature.snoozedUntil, now)\n ? `Snoozed until ${new Date(feature.snoozedUntil).toLocaleString()} — it returns to the triage queue then.`\n : feature.status === 'SUBMITTED'\n ? 'Waiting for a decision.'\n : 'Under review: accept it as planned, decline it, or merge it into its original.'}\n </p>\n <FeatureTriageActions feature={feature} now={now} />\n </CardContent>\n </Card>\n )}\n {feature.status === 'DECLINED' && feature.declineReason && (\n <Card data-testid=\"decline-reason\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Declined: </span>\n {feature.declineReason}\n </CardContent>\n </Card>\n )}\n {feature.status === 'DUPLICATE' && feature.duplicateOfId && (\n <Card data-testid=\"duplicate-of\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Duplicate of </span>\n {feature.duplicateOf ? (\n <button\n type=\"button\"\n className=\"text-primary underline-offset-2 hover:underline\"\n onClick={() =>\n navigate(detailPath('/products/feature-detail-page', feature.duplicateOf.id))\n }\n >\n {feature.duplicateOf.title}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">a request that has since been deleted</span>\n )}{' '}\n — its votes were moved there.\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\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\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Priority Score</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <ThumbsUp className=\"h-5 w-5 mx-auto text-green-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-muted-foreground\">Votes</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-yellow-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Business Value</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <MessageSquare className=\"h-5 w-5 mx-auto text-blue-500\" />\n <p className=\"text-2xl font-bold mt-1\">{competitors.length}</p>\n <p className=\"text-xs text-muted-foreground\">Competitors</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>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm\">{feature.description ?? 'No description provided.'}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Votes ({votes.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {votes.length === 0 && <p className=\"text-sm text-muted-foreground\">No votes yet.</p>}\n {votes.map((vote) => (\n <div\n key={vote.id}\n className=\"flex items-start justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">\n Voter: <PersonName id={vote.voterId} />\n </p>\n {vote.comment && (\n <p className=\"text-xs text-muted-foreground mt-1\">{vote.comment}</p>\n )}\n </div>\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{vote.weight}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Competitor tracking (BOFF-8047): add, remove and browse to the competitors linked here. */}\n <Card>\n <CardHeader>\n <CardTitle>Competitors ({competitors.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <CompetitorLinkCard featureRequestId={feature.id} competitors={competitors} />\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n {/* PRM-24: the roadmap initiative that addresses this request. */}\n <FeatureInitiativeCard\n featureId={feature.id}\n productId={feature.productId ?? null}\n initiative={feature.roadmapItem ?? null}\n />\n {/* PRM-27: MoSCoW/Kano/Value-Effort scoring, edited in place with the computed quadrant read back\n from the save's own response. */}\n <FeaturePrioritizationCard feature={feature} />\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {feature.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Priority Score</span>\n <Badge variant=\"outline\">{feature.priorityScore ?? 'N/A'}</Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Effort</span>\n <Badge variant=\"outline\" className=\"capitalize\">\n {feature.effortEstimate ?? 'N/A'}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Business Value</span>\n <span className=\"font-medium\">{feature.businessValue ?? 'N/A'}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Submitted By</span>\n <PersonName id={feature.submittedBy} />\n </div>\n {feature.triagedAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Decided</span>\n <span>\n {new Date(feature.triagedAt).toLocaleDateString()}\n {feature.triagedBy && (\n <>\n {' by '}\n <PersonName id={feature.triagedBy} />\n </>\n )}\n </span>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {feature.tags?.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {feature.tags.map((tag: string) => (\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":"ProductsFeatureDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Star, ThumbsUp, MessageSquare, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { FeatureTriageActions, isOpenRequest, isSnoozed } from '../controls/FeatureTriage';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n FeatureHeaderActions,\n FeaturePrioritizationCard,\n FeatureVoteButton,\n} from '../controls/FeatureControls';\nimport { CompetitorLinkCard } from '../controls/CompetitorControls';\nimport { FeatureInitiativeCard } from '../roadmaps/FeatureInitiativeCard';\nimport { CategoryBadge, RatingDisplay } from '../controls/FeedbackControls';\n\nconst GET_FEATURE_REQUEST = gql`\n query GetFeatureRequest($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n workspaceId\n projectId\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n workItemId\n tags\n snoozedUntil\n rating\n category\n isAnonymous\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n reach\n impact\n confidence\n riceScore\n declineReason\n duplicateOfId\n duplicateOf {\n id\n title\n }\n triagedAt\n triagedBy\n createdAt\n updatedAt\n productId\n roadmapItem {\n id\n title\n roadmap {\n id\n name\n }\n }\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n description\n website\n }\n }\n }\n`;\n\ninterface FeatureVoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\ninterface CompetitorNode {\n id: string;\n name: string;\n description: string | null;\n website: string | null;\n}\n\nexport function ProductsFeatureDetailPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n // Snooze state is read against the moment the page opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST, {\n variables: { id: featureId },\n skip: !featureId,\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 <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 feature: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature not found.</div>;\n\n const votes: FeatureVoteNode[] = feature.votes ?? [];\n const competitors: CompetitorNode[] = feature.competitors ?? [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{feature.title}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Submitted by: <PersonName id={feature.submittedBy} /> &middot; Effort:{' '}\n {feature.effortEstimate ?? 'N/A'}\n </p>\n </div>\n {/* Vote, status, Edit and Delete (BOFF-7951): the request was read-only. */}\n <div className=\"flex flex-col items-end gap-2\">\n <FeatureVoteButton\n key={feature.id}\n featureId={feature.id}\n voted={votes.some((v) => v.voterId === currentUser?.id)}\n />\n <FeatureHeaderActions feature={feature} />\n </div>\n </div>\n\n {/* Triage (P1.3): an undecided request is accepted, declined, snoozed or merged from here. */}\n {isOpenRequest(feature.status) && (\n <Card data-testid=\"triage-card\">\n <CardHeader>\n <CardTitle>Triage</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">\n {isSnoozed(feature.snoozedUntil, now)\n ? `Snoozed until ${new Date(feature.snoozedUntil).toLocaleString()} — it returns to the triage queue then.`\n : feature.status === 'SUBMITTED'\n ? 'Waiting for a decision.'\n : 'Under review: accept it as planned, decline it, or merge it into its original.'}\n </p>\n <FeatureTriageActions feature={feature} now={now} />\n </CardContent>\n </Card>\n )}\n {feature.status === 'DECLINED' && feature.declineReason && (\n <Card data-testid=\"decline-reason\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Declined: </span>\n {feature.declineReason}\n </CardContent>\n </Card>\n )}\n {feature.status === 'DUPLICATE' && feature.duplicateOfId && (\n <Card data-testid=\"duplicate-of\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Duplicate of </span>\n {feature.duplicateOf ? (\n <button\n type=\"button\"\n className=\"text-primary underline-offset-2 hover:underline\"\n onClick={() =>\n navigate(detailPath('/products/feature-detail-page', feature.duplicateOf.id))\n }\n >\n {feature.duplicateOf.title}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">a request that has since been deleted</span>\n )}{' '}\n — its votes were moved there.\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\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\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Priority Score</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <ThumbsUp className=\"h-5 w-5 mx-auto text-green-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-muted-foreground\">Votes</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-yellow-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Business Value</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <MessageSquare className=\"h-5 w-5 mx-auto text-blue-500\" />\n <p className=\"text-2xl font-bold mt-1\">{competitors.length}</p>\n <p className=\"text-xs text-muted-foreground\">Competitors</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>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm\">{feature.description ?? 'No description provided.'}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Votes ({votes.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {votes.length === 0 && <p className=\"text-sm text-muted-foreground\">No votes yet.</p>}\n {votes.map((vote) => (\n <div\n key={vote.id}\n className=\"flex items-start justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">\n Voter: <PersonName id={vote.voterId} />\n </p>\n {vote.comment && (\n <p className=\"text-xs text-muted-foreground mt-1\">{vote.comment}</p>\n )}\n </div>\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{vote.weight}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Competitor tracking (BOFF-8047): add, remove and browse to the competitors linked here. */}\n <Card>\n <CardHeader>\n <CardTitle>Competitors ({competitors.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <CompetitorLinkCard featureRequestId={feature.id} competitors={competitors} />\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n {/* PRM-24: the roadmap initiative that addresses this request. */}\n <FeatureInitiativeCard\n featureId={feature.id}\n productId={feature.productId ?? null}\n initiative={feature.roadmapItem ?? null}\n />\n {/* PRM-27: MoSCoW/Kano/Value-Effort scoring, edited in place with the computed quadrant read back\n from the save's own response. */}\n <FeaturePrioritizationCard feature={feature} />\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {feature.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Priority Score</span>\n <Badge variant=\"outline\">{feature.priorityScore ?? 'N/A'}</Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Effort</span>\n <Badge variant=\"outline\" className=\"capitalize\">\n {feature.effortEstimate ?? 'N/A'}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Business Value</span>\n <span className=\"font-medium\">{feature.businessValue ?? 'N/A'}</span>\n </div>\n {/* CWF-15 */}\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Rating</span>\n <RatingDisplay rating={feature.rating ?? null} />\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Category</span>\n {feature.category ? (\n <CategoryBadge category={feature.category} />\n ) : (\n <span className=\"text-sm text-muted-foreground\">Not set</span>\n )}\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Submitted By</span>\n <PersonName id={feature.submittedBy} />\n </div>\n {feature.triagedAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Decided</span>\n <span>\n {new Date(feature.triagedAt).toLocaleDateString()}\n {feature.triagedBy && (\n <>\n {' by '}\n <PersonName id={feature.triagedBy} />\n </>\n )}\n </span>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {feature.tags?.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {feature.tags.map((tag: string) => (\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 +1 @@
1
- {"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"AA6FA,wBAAgB,wBAAwB,4CAyTvC"}
1
+ {"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"AA8GA,wBAAgB,wBAAwB,4CAkXvC"}
@@ -3,15 +3,16 @@ import { PersonName as n } from "../people/People.js";
3
3
  import { countLabel as r, useFeatureCounts as i } from "../hooks/useCounts.js";
4
4
  import { detailPath as a, rowLinkProps as o } from "../utils/rowNavigation.js";
5
5
  import { FeatureTriageActions as s, isSnoozed as c } from "../controls/FeatureTriage.js";
6
- import { KANO_META as l, KANO_VALUES as u, MOSCOW_META as d, MOSCOW_VALUES as f, QUADRANT_META as p, QUADRANT_VALUES as m, QuadrantBadge as h } from "../controls/PrioritizationControls.js";
7
- import { useState as g } from "react";
8
- import { ArrowUpDown as _, Filter as v, Loader2 as y, Plus as b, Star as x, Target as S, ThumbsUp as ee } from "lucide-react";
9
- import { gql as C } from "@apollo/client";
10
- import { useQuery as w } from "@apollo/client/react";
11
- import { jsx as T, jsxs as E } from "react/jsx-runtime";
12
- import { Badge as D, Button as O, Card as k, CardContent as A } from "@burdenoff/fe-libs/ui";
6
+ import { KANO_META as l, KANO_VALUES as u, MOSCOW_META as d, MOSCOW_VALUES as ee, QUADRANT_META as f, QUADRANT_VALUES as p, QuadrantBadge as m, riceScoreMissingHint as h } from "../controls/PrioritizationControls.js";
7
+ import { CategoryBadge as te, RatingDisplay as g } from "../controls/FeedbackControls.js";
8
+ import { useState as _ } from "react";
9
+ import { ArrowUpDown as v, Filter as y, Gauge as b, Loader2 as ne, Plus as re, Star as ie, Target as ae, ThumbsUp as x } from "lucide-react";
10
+ import { gql as S } from "@apollo/client";
11
+ import { useQuery as C } from "@apollo/client/react";
12
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
13
+ import { Badge as E, Button as D, Card as O, CardContent as k } from "@burdenoff/fe-libs/ui";
13
14
  //#region src/planmagnet/pages/ProductsFeaturesPagePage.tsx
14
- var j = C`
15
+ var A = S`
15
16
  query ListFeatureRequests(
16
17
  $workspaceId: ID!
17
18
  $filter: PlanmagnetFeatureRequestFilter
@@ -40,13 +41,20 @@ var j = C`
40
41
  moscow
41
42
  kano
42
43
  valueEffortQuadrant
44
+ reach
45
+ impact
46
+ confidence
47
+ effortScore
48
+ riceScore
49
+ rating
50
+ category
43
51
  createdAt
44
52
  updatedAt
45
53
  }
46
54
  totalCount
47
55
  }
48
56
  }
49
- `, M = {
57
+ `, j = {
50
58
  SUBMITTED: "bg-gray-100 text-gray-800",
51
59
  UNDER_REVIEW: "bg-yellow-100 text-yellow-800",
52
60
  PLANNED: "bg-blue-100 text-blue-800",
@@ -55,30 +63,30 @@ var j = C`
55
63
  DECLINED: "bg-red-100 text-red-800",
56
64
  DUPLICATE: "bg-orange-100 text-orange-800"
57
65
  };
58
- function N() {
59
- let C = t(), { workspaceId: N } = e(), [P, F] = g("all"), [I] = g(() => Date.now()), [L, te] = g("NEWEST"), [R, z] = g(""), [B, V] = g(""), [H, U] = g(""), W = i(), G = {
60
- ...P === "TRIAGE" ? { triage: !0 } : P === "SNOOZED" ? { snoozed: !0 } : P === "all" ? {} : { status: P },
61
- ...R ? { moscow: R } : {},
62
- ...B ? { kano: B } : {},
63
- ...H ? { quadrant: H } : {}
64
- }, { data: K, loading: q, error: J } = w(j, {
66
+ function M() {
67
+ let S = t(), { workspaceId: M } = e(), [N, P] = _("all"), [F] = _(() => Date.now()), [I, L] = _("NEWEST"), [R, z] = _(!1), [B, V] = _(""), [H, U] = _(""), [W, G] = _(""), K = i(), q = {
68
+ ...N === "TRIAGE" ? { triage: !0 } : N === "SNOOZED" ? { snoozed: !0 } : N === "all" ? {} : { status: N },
69
+ ...B ? { moscow: B } : {},
70
+ ...H ? { kano: H } : {},
71
+ ...W ? { quadrant: W } : {}
72
+ }, { data: J, loading: oe, error: Y } = C(A, {
65
73
  variables: {
66
- workspaceId: N,
67
- filter: Object.keys(G).length > 0 ? G : void 0,
74
+ workspaceId: M,
75
+ filter: Object.keys(q).length > 0 ? q : void 0,
68
76
  pagination: { first: 50 },
69
- orderBy: L
77
+ orderBy: I
70
78
  },
71
- skip: !N
79
+ skip: !M
72
80
  });
73
- if (q && !K) return /* @__PURE__ */ T("div", {
81
+ if (oe && !J) return /* @__PURE__ */ w("div", {
74
82
  className: "flex items-center justify-center p-12",
75
- children: /* @__PURE__ */ T(y, { className: "h-8 w-8 animate-spin text-muted-foreground" })
83
+ children: /* @__PURE__ */ w(ne, { className: "h-8 w-8 animate-spin text-muted-foreground" })
76
84
  });
77
- if (J) return /* @__PURE__ */ E("div", {
85
+ if (Y) return /* @__PURE__ */ T("div", {
78
86
  className: "p-6 text-destructive",
79
- children: ["Failed to load features: ", J.message]
87
+ children: ["Failed to load features: ", Y.message]
80
88
  });
81
- let Y = K?.planmagnetFeatureRequests?.items ?? [], X = K?.planmagnetFeatureRequests?.totalCount ?? 0, Z = Y, Q = [
89
+ let X = J?.planmagnetFeatureRequests?.items ?? [], Z = J?.planmagnetFeatureRequests?.totalCount ?? 0, Q = R ? [...X].sort((e, t) => e.riceScore == null && t.riceScore == null ? 0 : e.riceScore == null ? 1 : t.riceScore == null ? -1 : t.riceScore - e.riceScore) : X, se = [
82
90
  "all",
83
91
  "TRIAGE",
84
92
  "SNOOZED",
@@ -90,257 +98,296 @@ function N() {
90
98
  "DECLINED",
91
99
  "DUPLICATE"
92
100
  ], $ = {
93
- total: r(W?.all),
94
- open: r(W?.open),
95
- inProgress: r(W?.byStatus.IN_PROGRESS),
96
- completed: r(W?.byStatus.COMPLETED)
101
+ total: r(K?.all),
102
+ open: r(K?.open),
103
+ inProgress: r(K?.byStatus.IN_PROGRESS),
104
+ completed: r(K?.byStatus.COMPLETED)
97
105
  };
98
- return /* @__PURE__ */ E("div", {
106
+ return /* @__PURE__ */ T("div", {
99
107
  className: "space-y-6 p-6",
100
108
  children: [
101
- /* @__PURE__ */ E("div", {
109
+ /* @__PURE__ */ T("div", {
102
110
  className: "flex items-center justify-between",
103
- children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("h1", {
111
+ children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("h1", {
104
112
  className: "text-2xl font-semibold",
105
113
  children: "Features"
106
- }), /* @__PURE__ */ T("p", {
114
+ }), /* @__PURE__ */ w("p", {
107
115
  className: "text-sm text-muted-foreground",
108
- children: P === "all" ? `${$.total} feature requests` : P === "TRIAGE" ? `${X} waiting for triage` : `${X} ${P.toLowerCase().replace("_", " ")} match`
109
- })] }), /* @__PURE__ */ E(O, {
116
+ children: N === "all" ? `${$.total} feature requests` : N === "TRIAGE" ? `${Z} waiting for triage` : `${Z} ${N.toLowerCase().replace("_", " ")} match`
117
+ })] }), /* @__PURE__ */ T(D, {
110
118
  size: "sm",
111
- onClick: () => C("/products/new-feature-page"),
112
- children: [/* @__PURE__ */ T(b, { className: "mr-1 h-4 w-4" }), "New Feature"]
119
+ onClick: () => S("/products/new-feature-page"),
120
+ children: [/* @__PURE__ */ w(re, { className: "mr-1 h-4 w-4" }), "New Feature"]
113
121
  })]
114
122
  }),
115
- /* @__PURE__ */ E("div", {
123
+ /* @__PURE__ */ T("div", {
116
124
  className: "grid grid-cols-2 md:grid-cols-4 gap-4",
117
125
  children: [
118
- /* @__PURE__ */ T(k, { children: /* @__PURE__ */ E(A, {
126
+ /* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
119
127
  className: "pt-4",
120
- children: [/* @__PURE__ */ T("p", {
128
+ children: [/* @__PURE__ */ w("p", {
121
129
  className: "text-xs text-muted-foreground",
122
130
  children: "Total Features"
123
- }), /* @__PURE__ */ T("p", {
131
+ }), /* @__PURE__ */ w("p", {
124
132
  className: "text-xl font-bold",
125
133
  children: $.total
126
134
  })]
127
135
  }) }),
128
- /* @__PURE__ */ T(k, { children: /* @__PURE__ */ E(A, {
136
+ /* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
129
137
  className: "pt-4",
130
- children: [/* @__PURE__ */ T("p", {
138
+ children: [/* @__PURE__ */ w("p", {
131
139
  className: "text-xs text-muted-foreground",
132
140
  children: "In Progress"
133
- }), /* @__PURE__ */ T("p", {
141
+ }), /* @__PURE__ */ w("p", {
134
142
  className: "text-xl font-bold",
135
143
  children: $.inProgress
136
144
  })]
137
145
  }) }),
138
- /* @__PURE__ */ T(k, { children: /* @__PURE__ */ E(A, {
146
+ /* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
139
147
  className: "pt-4",
140
- children: [/* @__PURE__ */ T("p", {
148
+ children: [/* @__PURE__ */ w("p", {
141
149
  className: "text-xs text-muted-foreground",
142
150
  children: "Completed"
143
- }), /* @__PURE__ */ T("p", {
151
+ }), /* @__PURE__ */ w("p", {
144
152
  className: "text-xl font-bold",
145
153
  children: $.completed
146
154
  })]
147
155
  }) }),
148
- /* @__PURE__ */ T(k, { children: /* @__PURE__ */ E(A, {
156
+ /* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
149
157
  className: "pt-4",
150
- children: [/* @__PURE__ */ T("p", {
158
+ children: [/* @__PURE__ */ w("p", {
151
159
  className: "text-xs text-muted-foreground",
152
160
  children: "Open"
153
- }), /* @__PURE__ */ T("p", {
161
+ }), /* @__PURE__ */ w("p", {
154
162
  className: "text-xl font-bold",
155
163
  children: $.open
156
164
  })]
157
165
  }) })
158
166
  ]
159
167
  }),
160
- /* @__PURE__ */ E("div", {
168
+ /* @__PURE__ */ T("div", {
161
169
  className: "flex items-center justify-between flex-wrap gap-2",
162
- children: [/* @__PURE__ */ E("div", {
170
+ children: [/* @__PURE__ */ T("div", {
163
171
  className: "flex items-center gap-2",
164
- children: [/* @__PURE__ */ T(v, { className: "h-4 w-4 text-muted-foreground" }), Q.map((e) => /* @__PURE__ */ T("button", {
165
- onClick: () => F(e),
166
- className: `px-2 py-1 text-xs rounded-md capitalize ${P === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
167
- children: e === "all" ? "All" : e === "TRIAGE" ? `Triage (${r(W?.triage)})` : e === "SNOOZED" ? `Snoozed (${r(W?.snoozed)})` : e.toLowerCase().replace("_", " ")
172
+ children: [/* @__PURE__ */ w(y, { className: "h-4 w-4 text-muted-foreground" }), se.map((e) => /* @__PURE__ */ w("button", {
173
+ onClick: () => P(e),
174
+ className: `px-2 py-1 text-xs rounded-md capitalize ${N === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
175
+ children: e === "all" ? "All" : e === "TRIAGE" ? `Triage (${r(K?.triage)})` : e === "SNOOZED" ? `Snoozed (${r(K?.snoozed)})` : e.toLowerCase().replace("_", " ")
168
176
  }, e))]
169
- }), /* @__PURE__ */ E("div", {
177
+ }), /* @__PURE__ */ T("div", {
170
178
  className: "flex items-center gap-2",
171
- children: [/* @__PURE__ */ T(_, { className: "h-4 w-4 text-muted-foreground" }), ["NEWEST", "MOST_VOTED"].map((e) => /* @__PURE__ */ T("button", {
172
- onClick: () => te(e),
173
- className: `px-2 py-1 text-xs rounded-md ${L === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
174
- children: e === "NEWEST" ? "Newest" : "Most voted"
175
- }, e))]
179
+ children: [
180
+ /* @__PURE__ */ w(v, { className: "h-4 w-4 text-muted-foreground" }),
181
+ ["NEWEST", "MOST_VOTED"].map((e) => /* @__PURE__ */ w("button", {
182
+ onClick: () => L(e),
183
+ className: `px-2 py-1 text-xs rounded-md ${!R && I === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
184
+ children: e === "NEWEST" ? "Newest" : "Most voted"
185
+ }, e)),
186
+ /* @__PURE__ */ w("button", {
187
+ type: "button",
188
+ "aria-pressed": R,
189
+ "data-testid": "feature-sort-rice",
190
+ onClick: () => z((e) => !e),
191
+ className: `px-2 py-1 text-xs rounded-md ${R ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
192
+ children: "RICE score"
193
+ })
194
+ ]
176
195
  })]
177
196
  }),
178
- /* @__PURE__ */ E("div", {
197
+ R && /* @__PURE__ */ T("p", {
198
+ className: "text-xs text-muted-foreground",
199
+ "data-testid": "feature-sort-rice-note",
200
+ children: [
201
+ "Sorted by RICE score within the ",
202
+ Q.length,
203
+ " loaded results above — not a server-wide sort."
204
+ ]
205
+ }),
206
+ /* @__PURE__ */ T("div", {
179
207
  className: "flex flex-wrap items-center gap-3",
180
208
  children: [
181
- /* @__PURE__ */ E("div", {
209
+ /* @__PURE__ */ T("div", {
182
210
  className: "flex items-center gap-1",
183
- children: [/* @__PURE__ */ T("label", {
211
+ children: [/* @__PURE__ */ w("label", {
184
212
  htmlFor: "feature-filter-moscow",
185
213
  className: "text-xs text-muted-foreground",
186
214
  children: "MoSCoW"
187
- }), /* @__PURE__ */ E("select", {
215
+ }), /* @__PURE__ */ T("select", {
188
216
  id: "feature-filter-moscow",
189
217
  "data-testid": "feature-filter-moscow",
190
218
  className: "rounded-md border border-input bg-background px-2 py-1 text-xs",
191
- value: R,
192
- onChange: (e) => z(e.target.value),
193
- children: [/* @__PURE__ */ T("option", {
219
+ value: B,
220
+ onChange: (e) => V(e.target.value),
221
+ children: [/* @__PURE__ */ w("option", {
194
222
  value: "",
195
223
  children: "All"
196
- }), f.map((e) => /* @__PURE__ */ T("option", {
224
+ }), ee.map((e) => /* @__PURE__ */ w("option", {
197
225
  value: e,
198
226
  children: d[e].label
199
227
  }, e))]
200
228
  })]
201
229
  }),
202
- /* @__PURE__ */ E("div", {
230
+ /* @__PURE__ */ T("div", {
203
231
  className: "flex items-center gap-1",
204
- children: [/* @__PURE__ */ T("label", {
232
+ children: [/* @__PURE__ */ w("label", {
205
233
  htmlFor: "feature-filter-kano",
206
234
  className: "text-xs text-muted-foreground",
207
235
  children: "Kano"
208
- }), /* @__PURE__ */ E("select", {
236
+ }), /* @__PURE__ */ T("select", {
209
237
  id: "feature-filter-kano",
210
238
  "data-testid": "feature-filter-kano",
211
239
  className: "rounded-md border border-input bg-background px-2 py-1 text-xs",
212
- value: B,
213
- onChange: (e) => V(e.target.value),
214
- children: [/* @__PURE__ */ T("option", {
240
+ value: H,
241
+ onChange: (e) => U(e.target.value),
242
+ children: [/* @__PURE__ */ w("option", {
215
243
  value: "",
216
244
  children: "All"
217
- }), u.map((e) => /* @__PURE__ */ T("option", {
245
+ }), u.map((e) => /* @__PURE__ */ w("option", {
218
246
  value: e,
219
247
  children: l[e].label
220
248
  }, e))]
221
249
  })]
222
250
  }),
223
- /* @__PURE__ */ E("div", {
251
+ /* @__PURE__ */ T("div", {
224
252
  className: "flex items-center gap-1",
225
- children: [/* @__PURE__ */ T("label", {
253
+ children: [/* @__PURE__ */ w("label", {
226
254
  htmlFor: "feature-filter-quadrant",
227
255
  className: "text-xs text-muted-foreground",
228
256
  children: "Quadrant"
229
- }), /* @__PURE__ */ E("select", {
257
+ }), /* @__PURE__ */ T("select", {
230
258
  id: "feature-filter-quadrant",
231
259
  "data-testid": "feature-filter-quadrant",
232
260
  className: "rounded-md border border-input bg-background px-2 py-1 text-xs",
233
- value: H,
234
- onChange: (e) => U(e.target.value),
235
- children: [/* @__PURE__ */ T("option", {
261
+ value: W,
262
+ onChange: (e) => G(e.target.value),
263
+ children: [/* @__PURE__ */ w("option", {
236
264
  value: "",
237
265
  children: "All"
238
- }), m.map((e) => /* @__PURE__ */ T("option", {
266
+ }), p.map((e) => /* @__PURE__ */ w("option", {
239
267
  value: e,
240
- children: p[e].label
268
+ children: f[e].label
241
269
  }, e))]
242
270
  })]
243
271
  })
244
272
  ]
245
273
  }),
246
- /* @__PURE__ */ T("div", {
274
+ /* @__PURE__ */ w("div", {
247
275
  className: "space-y-3",
248
- children: Z.map((e) => /* @__PURE__ */ T(k, {
249
- ...o(() => C(a("/products/feature-detail-page", e.id)), e.title),
250
- children: /* @__PURE__ */ T(A, {
276
+ children: Q.map((e) => /* @__PURE__ */ w(O, {
277
+ ...o(() => S(a("/products/feature-detail-page", e.id)), e.title),
278
+ children: /* @__PURE__ */ w(k, {
251
279
  className: "py-4",
252
- children: /* @__PURE__ */ E("div", {
280
+ children: /* @__PURE__ */ T("div", {
253
281
  className: "flex items-start justify-between gap-4",
254
- children: [/* @__PURE__ */ E("div", {
282
+ children: [/* @__PURE__ */ T("div", {
255
283
  className: "flex-1 space-y-2",
256
284
  children: [
257
- /* @__PURE__ */ E("div", {
285
+ /* @__PURE__ */ T("div", {
258
286
  className: "flex items-center gap-2",
259
287
  children: [
260
- /* @__PURE__ */ T("span", {
288
+ /* @__PURE__ */ w("span", {
261
289
  className: "text-sm font-medium",
262
290
  children: e.title
263
291
  }),
264
- /* @__PURE__ */ T("span", {
265
- className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${M[e.status] ?? "bg-gray-100"}`,
292
+ /* @__PURE__ */ w("span", {
293
+ className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${j[e.status] ?? "bg-gray-100"}`,
266
294
  children: e.status.toLowerCase().replace("_", " ")
267
295
  }),
268
- /* @__PURE__ */ T(h, {
296
+ /* @__PURE__ */ w(m, {
269
297
  quadrant: e.valueEffortQuadrant,
270
298
  hideWhenUnscored: !0
271
299
  })
272
300
  ]
273
301
  }),
274
- /* @__PURE__ */ T("p", {
302
+ /* @__PURE__ */ w("p", {
275
303
  className: "text-xs text-muted-foreground",
276
304
  children: e.description
277
305
  }),
278
- /* @__PURE__ */ E("div", {
306
+ /* @__PURE__ */ T("div", {
279
307
  className: "flex items-center gap-4 text-xs text-muted-foreground",
280
308
  children: [
281
- /* @__PURE__ */ E("span", { children: ["Effort: ", e.effortEstimate ?? "N/A"] }),
282
- /* @__PURE__ */ E("span", { children: ["Submitted by: ", /* @__PURE__ */ T(n, { id: e.submittedBy })] }),
283
- /* @__PURE__ */ T("span", { children: new Date(e.updatedAt).toLocaleDateString() })
309
+ /* @__PURE__ */ T("span", { children: ["Effort: ", e.effortEstimate ?? "N/A"] }),
310
+ /* @__PURE__ */ T("span", { children: ["Submitted by: ", /* @__PURE__ */ w(n, { id: e.submittedBy })] }),
311
+ /* @__PURE__ */ w("span", { children: new Date(e.updatedAt).toLocaleDateString() })
284
312
  ]
285
313
  }),
286
- c(e.snoozedUntil, I) && /* @__PURE__ */ E("p", {
314
+ (e.rating != null || e.category) && /* @__PURE__ */ T("div", {
315
+ className: "flex items-center gap-2",
316
+ children: [e.rating != null && /* @__PURE__ */ w(g, { rating: e.rating }), /* @__PURE__ */ w(te, { category: e.category })]
317
+ }),
318
+ c(e.snoozedUntil, F) && /* @__PURE__ */ T("p", {
287
319
  className: "text-xs text-muted-foreground",
288
320
  children: ["Snoozed until ", new Date(e.snoozedUntil).toLocaleDateString()]
289
321
  }),
290
- /* @__PURE__ */ T(s, {
322
+ /* @__PURE__ */ w(s, {
291
323
  feature: e,
292
- now: I
324
+ now: F
293
325
  }),
294
- e.tags.length > 0 && /* @__PURE__ */ T("div", {
326
+ e.tags.length > 0 && /* @__PURE__ */ w("div", {
295
327
  className: "flex gap-1",
296
- children: e.tags.map((e) => /* @__PURE__ */ T(D, {
328
+ children: e.tags.map((e) => /* @__PURE__ */ w(E, {
297
329
  variant: "outline",
298
330
  className: "text-[10px]",
299
331
  children: e
300
332
  }, e))
301
333
  })
302
334
  ]
303
- }), /* @__PURE__ */ E("div", {
335
+ }), /* @__PURE__ */ T("div", {
304
336
  className: "flex items-center gap-4 shrink-0",
305
337
  children: [
306
- /* @__PURE__ */ E("div", {
338
+ /* @__PURE__ */ T("div", {
307
339
  className: "text-center",
308
- children: [/* @__PURE__ */ E("div", {
340
+ children: [/* @__PURE__ */ T("div", {
309
341
  className: "flex items-center gap-1",
310
- children: [/* @__PURE__ */ T(S, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ T("span", {
342
+ children: [/* @__PURE__ */ w(ae, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ w("span", {
311
343
  className: "text-sm font-bold",
312
344
  children: e.priorityScore ?? 0
313
345
  })]
314
- }), /* @__PURE__ */ T("p", {
346
+ }), /* @__PURE__ */ w("p", {
315
347
  className: "text-[10px] text-muted-foreground",
316
348
  children: "Priority"
317
349
  })]
318
350
  }),
319
- /* @__PURE__ */ E("div", {
351
+ /* @__PURE__ */ T("div", {
320
352
  className: "text-center",
321
- children: [/* @__PURE__ */ E("div", {
353
+ children: [/* @__PURE__ */ T("div", {
322
354
  className: "flex items-center gap-1",
323
- children: [/* @__PURE__ */ T(ee, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ T("span", {
355
+ children: [/* @__PURE__ */ w(x, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ w("span", {
324
356
  className: "text-sm font-bold",
325
357
  children: e.voteCount
326
358
  })]
327
- }), /* @__PURE__ */ T("p", {
359
+ }), /* @__PURE__ */ w("p", {
328
360
  className: "text-[10px] text-muted-foreground",
329
361
  children: "Votes"
330
362
  })]
331
363
  }),
332
- /* @__PURE__ */ E("div", {
364
+ /* @__PURE__ */ T("div", {
333
365
  className: "text-center",
334
- children: [/* @__PURE__ */ E("div", {
366
+ children: [/* @__PURE__ */ T("div", {
335
367
  className: "flex items-center gap-1",
336
- children: [/* @__PURE__ */ T(x, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ T("span", {
368
+ children: [/* @__PURE__ */ w(ie, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ w("span", {
337
369
  className: "text-sm font-bold",
338
370
  children: e.businessValue ?? 0
339
371
  })]
340
- }), /* @__PURE__ */ T("p", {
372
+ }), /* @__PURE__ */ w("p", {
341
373
  className: "text-[10px] text-muted-foreground",
342
374
  children: "Value"
343
375
  })]
376
+ }),
377
+ /* @__PURE__ */ T("div", {
378
+ className: "text-center",
379
+ title: e.riceScore == null ? h(e) ?? void 0 : void 0,
380
+ children: [/* @__PURE__ */ T("div", {
381
+ className: "flex items-center gap-1",
382
+ children: [/* @__PURE__ */ w(b, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ w("span", {
383
+ className: "text-sm font-bold",
384
+ "data-testid": "feature-rice-score-cell",
385
+ children: e.riceScore == null ? "—" : e.riceScore.toFixed(1)
386
+ })]
387
+ }), /* @__PURE__ */ w("p", {
388
+ className: "text-[10px] text-muted-foreground",
389
+ children: "RICE"
390
+ })]
344
391
  })
345
392
  ]
346
393
  })]
@@ -348,14 +395,14 @@ function N() {
348
395
  })
349
396
  }, e.id))
350
397
  }),
351
- Z.length === 0 && /* @__PURE__ */ T("p", {
398
+ Q.length === 0 && /* @__PURE__ */ w("p", {
352
399
  className: "text-center text-muted-foreground py-8",
353
- children: P === "TRIAGE" ? "Nothing waits for triage." : "No features match the selected filters."
400
+ children: N === "TRIAGE" ? "Nothing waits for triage." : "No features match the selected filters."
354
401
  })
355
402
  ]
356
403
  });
357
404
  }
358
405
  //#endregion
359
- export { N as ProductsFeaturesPagePage };
406
+ export { M as ProductsFeaturesPagePage };
360
407
 
361
408
  //# sourceMappingURL=ProductsFeaturesPagePage.js.map