@burdenoff/microfe-planmagnet 2026.1002.1 → 2026.1002.2
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.
- package/dist/generated/wspace-operations.d.ts +1814 -401
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-types.d.ts +5167 -54
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +172 -160
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +95 -83
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/controls/Pickers.d.ts +7 -2
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
- package/dist/planmagnet/controls/Pickers.js +10 -9
- package/dist/planmagnet/controls/Pickers.js.map +1 -1
- package/dist/planmagnet/index.d.ts +1 -1
- package/dist/planmagnet/index.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +4 -4
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +133 -131
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +5 -5
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +6 -6
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +105 -82
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.js +174 -0
- package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.js +350 -0
- package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductLinesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsProductLinesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsProductLinesPagePage.js +168 -0
- package/dist/planmagnet/pages/ProductsProductLinesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +156 -154
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/product/FeatureLinkPanels.d.ts +29 -0
- package/dist/planmagnet/product/FeatureLinkPanels.d.ts.map +1 -0
- package/dist/planmagnet/product/FeatureLinkPanels.js +373 -0
- package/dist/planmagnet/product/FeatureLinkPanels.js.map +1 -0
- package/dist/planmagnet/product/ProductControls.js +1 -1
- package/dist/planmagnet/product/ProductControls.js.map +1 -1
- package/dist/planmagnet/product/ProductFeatureControls.d.ts +39 -0
- package/dist/planmagnet/product/ProductFeatureControls.d.ts.map +1 -0
- package/dist/planmagnet/product/ProductFeatureControls.js +315 -0
- package/dist/planmagnet/product/ProductFeatureControls.js.map +1 -0
- package/dist/planmagnet/product/ProductFeatureLinkCard.d.ts +4 -0
- package/dist/planmagnet/product/ProductFeatureLinkCard.d.ts.map +1 -0
- package/dist/planmagnet/product/ProductFeatureLinkCard.js +192 -0
- package/dist/planmagnet/product/ProductFeatureLinkCard.js.map +1 -0
- package/dist/planmagnet/product/ProductLineControls.d.ts +52 -0
- package/dist/planmagnet/product/ProductLineControls.d.ts.map +1 -0
- package/dist/planmagnet/product/ProductLineControls.js +417 -0
- package/dist/planmagnet/product/ProductLineControls.js.map +1 -0
- package/dist/planmagnet/product/ProductTeamsPanel.d.ts +4 -0
- package/dist/planmagnet/product/ProductTeamsPanel.d.ts.map +1 -0
- package/dist/planmagnet/product/ProductTeamsPanel.js +129 -0
- package/dist/planmagnet/product/ProductTeamsPanel.js.map +1 -0
- package/dist/planmagnet/product/WorkItemProductFeatureChip.d.ts +4 -0
- package/dist/planmagnet/product/WorkItemProductFeatureChip.d.ts.map +1 -0
- package/dist/planmagnet/product/WorkItemProductFeatureChip.js +41 -0
- package/dist/planmagnet/product/WorkItemProductFeatureChip.js.map +1 -0
- package/dist/planmagnet/product/hierarchy.d.ts +127 -0
- package/dist/planmagnet/product/hierarchy.d.ts.map +1 -0
- package/dist/planmagnet/product/hierarchy.js +173 -0
- package/dist/planmagnet/product/hierarchy.js.map +1 -0
- package/dist/planmagnet/product/hierarchyOperations.d.ts +35 -0
- package/dist/planmagnet/product/hierarchyOperations.d.ts.map +1 -0
- package/dist/planmagnet/product/hierarchyOperations.js +440 -0
- package/dist/planmagnet/product/hierarchyOperations.js.map +1 -0
- package/dist/planmagnet/product/refusal.d.ts +14 -0
- package/dist/planmagnet/product/refusal.d.ts.map +1 -0
- package/dist/planmagnet/product/refusal.js +10 -0
- package/dist/planmagnet/product/refusal.js.map +1 -0
- package/dist/planmagnet/product/routes.d.ts +4 -0
- package/dist/planmagnet/product/routes.d.ts.map +1 -1
- package/dist/planmagnet/product/routes.js +2 -2
- package/dist/planmagnet/product/routes.js.map +1 -1
- package/dist/shared/utils/i18n.d.ts +3 -0
- package/dist/shared/utils/i18n.d.ts.map +1 -1
- package/dist/shared/utils/i18n.js +1 -1
- package/dist/shared/utils/i18n.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsFeatureAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureAnalyticsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { TrendingUp, ThumbsUp, Users, BarChart3, Target, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { countLabel, useFeatureCounts } from '../hooks/useCounts';\n\nconst GET_FEATURE_REQUEST_ANALYTICS = gql`\n query GetFeatureRequestAnalytics($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n voteRank\n submittedBy\n tags\n createdAt\n updatedAt\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n }\n }\n }\n`;\n\ninterface VoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\nexport function ProductsFeatureAnalyticsPagePage() {\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_ANALYTICS, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n // Rank and distribution cover EVERY request, from the server (BOFF-7995: both were computed over the 100\n // newest in the browser, so a workspace with more read \"of 100\", and an older request had no rank).\n const featureCounts = useFeatureCounts();\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 analytics: {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: VoteNode[] = feature.votes ?? [];\n const competitorCount = feature.competitors?.length ?? 0;\n\n // Vote distribution by weight\n const votesByWeight: Record<number, number> = {};\n for (const v of votes) {\n votesByWeight[v.weight] = (votesByWeight[v.weight] || 0) + 1;\n }\n const maxVoteWeight = Math.max(...Object.values(votesByWeight), 1);\n\n // Vote timeline - group by creation date\n const voteTimeline: { date: string; count: number }[] = [];\n const voteDateMap: Record<string, number> = {};\n for (const v of votes) {\n const date = new Date(v.createdAt).toLocaleDateString();\n voteDateMap[date] = (voteDateMap[date] || 0) + 1;\n }\n for (const [date, count] of Object.entries(voteDateMap)) {\n voteTimeline.push({ date, count });\n }\n voteTimeline.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());\n const maxTimelineCount = Math.max(...voteTimeline.map((d) => d.count), 1);\n\n const statusDistribution: Record<string, number> = featureCounts\n ? Object.fromEntries(Object.entries(featureCounts.byStatus).filter(([, n]) => n > 0))\n : {};\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Feature Analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n {feature.title} - {feature.status.toLowerCase().replace('_', ' ')}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Target className=\"h-4 w-4\" />\n Priority Score\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <ThumbsUp className=\"h-4 w-4\" />\n Total Votes\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-green-600 mt-1\">\n Rank #{feature.voteRank} of {countLabel(featureCounts?.all)}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Users className=\"h-4 w-4\" />\n Competitors\n </div>\n <p className=\"text-2xl font-bold mt-1\">{competitorCount}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <TrendingUp className=\"h-4 w-4\" />\n Business Value\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n Vote Timeline\n </CardTitle>\n </CardHeader>\n <CardContent>\n {voteTimeline.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">\n No vote data available\n </p>\n ) : (\n <div className=\"flex items-end gap-1 h-48\">\n {voteTimeline.map((d) => (\n <div key={d.date} className=\"flex-1 flex flex-col items-center gap-1\">\n <div className=\"w-full flex justify-center\">\n <div\n className=\"w-5 bg-primary rounded-t transition-all\"\n style={{ height: `${(d.count / maxTimelineCount) * 160}px` }}\n />\n </div>\n <span className=\"text-[10px] text-muted-foreground truncate max-w-[40px]\">\n {d.date}\n </span>\n </div>\n ))}\n </div>\n )}\n <p className=\"text-xs text-muted-foreground text-center mt-2\">Votes over time</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Vote Weight Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {Object.entries(votesByWeight).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">No votes recorded</p>\n ) : (\n Object.entries(votesByWeight)\n .sort(([a], [b]) => Number(a) - Number(b))\n .map(([weight, count]) => (\n <div key={weight}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span>Weight {weight}</span>\n <span className=\"font-semibold\">{count} votes</span>\n </div>\n <div className=\"w-full h-3 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-primary\"\n style={{ width: `${(count / maxVoteWeight) * 100}%` }}\n />\n </div>\n </div>\n ))\n )}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>All Features - Status Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusDistribution).map(([status, count]) => {\n const total = featureCounts?.all ?? 0;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n SUBMITTED: 'bg-gray-400',\n UNDER_REVIEW: 'bg-yellow-400',\n PLANNED: 'bg-blue-400',\n IN_PROGRESS: 'bg-purple-400',\n COMPLETED: 'bg-green-400',\n DECLINED: 'bg-red-400',\n DUPLICATE: 'bg-orange-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Feature Impact</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"text-center p-4 bg-muted rounded-md\">\n <p className=\"text-3xl font-bold text-primary\">{feature.voteCount}</p>\n <p className=\"text-sm text-muted-foreground\">total votes received</p>\n </div>\n <div className=\"grid grid-cols-2 gap-2 text-center\">\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">business value</p>\n </div>\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">priority score</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductsFeatureAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureAnalyticsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { TrendingUp, ThumbsUp, Users, BarChart3, Target, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { countLabel, useFeatureCounts } from '../hooks/useCounts';\n\nconst GET_FEATURE_REQUEST_ANALYTICS = gql`\n query GetFeatureRequestAnalytics($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n voteRank\n submittedBy\n tags\n createdAt\n updatedAt\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n }\n }\n }\n`;\n\ninterface VoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\nexport function ProductsFeatureAnalyticsPagePage() {\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_ANALYTICS, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n // Rank and distribution cover EVERY request, from the server (BOFF-7995: both were computed over the 100\n // newest in the browser, so a workspace with more read \"of 100\", and an older request had no rank).\n const featureCounts = useFeatureCounts();\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 analytics: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature request not found.</div>;\n\n const votes: VoteNode[] = feature.votes ?? [];\n const competitorCount = feature.competitors?.length ?? 0;\n\n // Vote distribution by weight\n const votesByWeight: Record<number, number> = {};\n for (const v of votes) {\n votesByWeight[v.weight] = (votesByWeight[v.weight] || 0) + 1;\n }\n const maxVoteWeight = Math.max(...Object.values(votesByWeight), 1);\n\n // Vote timeline - group by creation date\n const voteTimeline: { date: string; count: number }[] = [];\n const voteDateMap: Record<string, number> = {};\n for (const v of votes) {\n const date = new Date(v.createdAt).toLocaleDateString();\n voteDateMap[date] = (voteDateMap[date] || 0) + 1;\n }\n for (const [date, count] of Object.entries(voteDateMap)) {\n voteTimeline.push({ date, count });\n }\n voteTimeline.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());\n const maxTimelineCount = Math.max(...voteTimeline.map((d) => d.count), 1);\n\n const statusDistribution: Record<string, number> = featureCounts\n ? Object.fromEntries(Object.entries(featureCounts.byStatus).filter(([, n]) => n > 0))\n : {};\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Feature request analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n {feature.title} - {feature.status.toLowerCase().replace('_', ' ')}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Target className=\"h-4 w-4\" />\n Priority Score\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <ThumbsUp className=\"h-4 w-4\" />\n Total Votes\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-green-600 mt-1\">\n Rank #{feature.voteRank} of {countLabel(featureCounts?.all)}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Users className=\"h-4 w-4\" />\n Competitors\n </div>\n <p className=\"text-2xl font-bold mt-1\">{competitorCount}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <TrendingUp className=\"h-4 w-4\" />\n Business Value\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n Vote Timeline\n </CardTitle>\n </CardHeader>\n <CardContent>\n {voteTimeline.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">\n No vote data available\n </p>\n ) : (\n <div className=\"flex items-end gap-1 h-48\">\n {voteTimeline.map((d) => (\n <div key={d.date} className=\"flex-1 flex flex-col items-center gap-1\">\n <div className=\"w-full flex justify-center\">\n <div\n className=\"w-5 bg-primary rounded-t transition-all\"\n style={{ height: `${(d.count / maxTimelineCount) * 160}px` }}\n />\n </div>\n <span className=\"text-[10px] text-muted-foreground truncate max-w-[40px]\">\n {d.date}\n </span>\n </div>\n ))}\n </div>\n )}\n <p className=\"text-xs text-muted-foreground text-center mt-2\">Votes over time</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Vote Weight Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {Object.entries(votesByWeight).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">No votes recorded</p>\n ) : (\n Object.entries(votesByWeight)\n .sort(([a], [b]) => Number(a) - Number(b))\n .map(([weight, count]) => (\n <div key={weight}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span>Weight {weight}</span>\n <span className=\"font-semibold\">{count} votes</span>\n </div>\n <div className=\"w-full h-3 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-primary\"\n style={{ width: `${(count / maxVoteWeight) * 100}%` }}\n />\n </div>\n </div>\n ))\n )}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>All feature requests - status distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusDistribution).map(([status, count]) => {\n const total = featureCounts?.all ?? 0;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n SUBMITTED: 'bg-gray-400',\n UNDER_REVIEW: 'bg-yellow-400',\n PLANNED: 'bg-blue-400',\n IN_PROGRESS: 'bg-purple-400',\n COMPLETED: 'bg-green-400',\n DECLINED: 'bg-red-400',\n DUPLICATE: 'bg-orange-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Request impact</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"text-center p-4 bg-muted rounded-md\">\n <p className=\"text-3xl font-bold text-primary\">{feature.voteCount}</p>\n <p className=\"text-sm text-muted-foreground\">total votes received</p>\n </div>\n <div className=\"grid grid-cols-2 gap-2 text-center\">\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">business value</p>\n </div>\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">priority score</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsFeatureDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProductsFeatureDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"names":[],"mappings":"AAsGA,wBAAgB,6BAA6B,4CA8R5C"}
|
|
@@ -6,15 +6,16 @@ import { FeatureTriageActions as a, isOpenRequest as o, isSnoozed as s } from ".
|
|
|
6
6
|
import { CategoryBadge as c, RatingDisplay as l } from "../controls/FeedbackControls.js";
|
|
7
7
|
import { FeatureHeaderActions as u, FeaturePrioritizationCard as d, FeatureVoteButton as f } from "../controls/FeatureControls.js";
|
|
8
8
|
import { FeatureInitiativeCard as p } from "../roadmaps/FeatureInitiativeCard.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
9
|
+
import { ProductFeatureLinkCard as m } from "../product/ProductFeatureLinkCard.js";
|
|
10
|
+
import { useState as h } from "react";
|
|
11
|
+
import { Loader2 as g, MessageSquare as _, Star as v, Target as y, ThumbsUp as b } from "lucide-react";
|
|
12
|
+
import { useParams as x } from "react-router";
|
|
13
|
+
import { gql as S } from "@apollo/client";
|
|
14
|
+
import { useQuery as C } from "@apollo/client/react";
|
|
15
|
+
import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
16
|
+
import { Badge as D, Card as O, CardContent as k, CardHeader as A, CardTitle as j } from "@burdenoff/fe-libs/ui";
|
|
16
17
|
//#region src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx
|
|
17
|
-
var
|
|
18
|
+
var M = S`
|
|
18
19
|
query GetFeatureRequest($id: ID!) {
|
|
19
20
|
planmagnetFeatureRequest(id: $id) {
|
|
20
21
|
id
|
|
@@ -78,88 +79,88 @@ var j = x`
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
`;
|
|
81
|
-
function
|
|
82
|
-
let { workspaceId:
|
|
83
|
-
variables: { id:
|
|
84
|
-
skip: !
|
|
82
|
+
function N() {
|
|
83
|
+
let { workspaceId: S, currentUser: N } = e(), P = t(), [F] = h(() => Date.now()), I = x().id ?? "", { data: L, loading: R, error: z } = C(M, {
|
|
84
|
+
variables: { id: I },
|
|
85
|
+
skip: !I
|
|
85
86
|
});
|
|
86
|
-
if (
|
|
87
|
+
if (R && !L) return /* @__PURE__ */ T("div", {
|
|
87
88
|
className: "flex items-center justify-center p-12",
|
|
88
|
-
children: /* @__PURE__ */
|
|
89
|
+
children: /* @__PURE__ */ T(g, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
89
90
|
});
|
|
90
|
-
if (
|
|
91
|
+
if (z) return /* @__PURE__ */ E("div", {
|
|
91
92
|
className: "p-6 text-destructive",
|
|
92
|
-
children: ["Failed to load feature: ",
|
|
93
|
+
children: ["Failed to load feature request: ", z.message]
|
|
93
94
|
});
|
|
94
|
-
let
|
|
95
|
-
if (!
|
|
95
|
+
let B = L?.planmagnetFeatureRequest;
|
|
96
|
+
if (!B) return /* @__PURE__ */ T("div", {
|
|
96
97
|
className: "p-6 text-muted-foreground",
|
|
97
|
-
children: "Feature not found."
|
|
98
|
+
children: "Feature request not found."
|
|
98
99
|
});
|
|
99
|
-
let
|
|
100
|
-
return /* @__PURE__ */
|
|
100
|
+
let V = B.votes ?? [], H = B.competitors ?? [];
|
|
101
|
+
return /* @__PURE__ */ E("div", {
|
|
101
102
|
className: "space-y-6 p-6",
|
|
102
103
|
children: [
|
|
103
|
-
/* @__PURE__ */
|
|
104
|
+
/* @__PURE__ */ E("div", {
|
|
104
105
|
className: "flex items-center justify-between",
|
|
105
|
-
children: [/* @__PURE__ */
|
|
106
|
+
children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("h1", {
|
|
106
107
|
className: "text-2xl font-semibold",
|
|
107
|
-
children:
|
|
108
|
-
}), /* @__PURE__ */
|
|
108
|
+
children: B.title
|
|
109
|
+
}), /* @__PURE__ */ E("p", {
|
|
109
110
|
className: "text-sm text-muted-foreground",
|
|
110
111
|
children: [
|
|
111
112
|
"Submitted by: ",
|
|
112
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ T(n, { id: B.submittedBy }),
|
|
113
114
|
" · Effort:",
|
|
114
115
|
" ",
|
|
115
|
-
|
|
116
|
+
B.effortEstimate ?? "N/A"
|
|
116
117
|
]
|
|
117
|
-
})] }), /* @__PURE__ */
|
|
118
|
+
})] }), /* @__PURE__ */ E("div", {
|
|
118
119
|
className: "flex flex-col items-end gap-2",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
120
|
-
featureId:
|
|
121
|
-
voted:
|
|
122
|
-
},
|
|
120
|
+
children: [/* @__PURE__ */ T(f, {
|
|
121
|
+
featureId: B.id,
|
|
122
|
+
voted: V.some((e) => e.voterId === N?.id)
|
|
123
|
+
}, B.id), /* @__PURE__ */ T(u, { feature: B })]
|
|
123
124
|
})]
|
|
124
125
|
}),
|
|
125
|
-
o(
|
|
126
|
+
o(B.status) && /* @__PURE__ */ E(O, {
|
|
126
127
|
"data-testid": "triage-card",
|
|
127
|
-
children: [/* @__PURE__ */
|
|
128
|
+
children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Triage" }) }), /* @__PURE__ */ E(k, {
|
|
128
129
|
className: "space-y-2",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ T("p", {
|
|
130
131
|
className: "text-sm text-muted-foreground",
|
|
131
|
-
children: s(
|
|
132
|
-
}), /* @__PURE__ */
|
|
133
|
-
feature:
|
|
134
|
-
now:
|
|
132
|
+
children: s(B.snoozedUntil, F) ? `Snoozed until ${new Date(B.snoozedUntil).toLocaleString()} — it returns to the triage queue then.` : B.status === "SUBMITTED" ? "Waiting for a decision." : "Under review: accept it as planned, decline it, or merge it into its original."
|
|
133
|
+
}), /* @__PURE__ */ T(a, {
|
|
134
|
+
feature: B,
|
|
135
|
+
now: F
|
|
135
136
|
})]
|
|
136
137
|
})]
|
|
137
138
|
}),
|
|
138
|
-
|
|
139
|
+
B.status === "DECLINED" && B.declineReason && /* @__PURE__ */ T(O, {
|
|
139
140
|
"data-testid": "decline-reason",
|
|
140
|
-
children: /* @__PURE__ */
|
|
141
|
+
children: /* @__PURE__ */ E(k, {
|
|
141
142
|
className: "pt-4 text-sm",
|
|
142
|
-
children: [/* @__PURE__ */
|
|
143
|
+
children: [/* @__PURE__ */ T("span", {
|
|
143
144
|
className: "font-medium",
|
|
144
145
|
children: "Declined: "
|
|
145
|
-
}),
|
|
146
|
+
}), B.declineReason]
|
|
146
147
|
})
|
|
147
148
|
}),
|
|
148
|
-
|
|
149
|
+
B.status === "DUPLICATE" && B.duplicateOfId && /* @__PURE__ */ T(O, {
|
|
149
150
|
"data-testid": "duplicate-of",
|
|
150
|
-
children: /* @__PURE__ */
|
|
151
|
+
children: /* @__PURE__ */ E(k, {
|
|
151
152
|
className: "pt-4 text-sm",
|
|
152
153
|
children: [
|
|
153
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ T("span", {
|
|
154
155
|
className: "font-medium",
|
|
155
156
|
children: "Duplicate of "
|
|
156
157
|
}),
|
|
157
|
-
|
|
158
|
+
B.duplicateOf ? /* @__PURE__ */ T("button", {
|
|
158
159
|
type: "button",
|
|
159
160
|
className: "text-primary underline-offset-2 hover:underline",
|
|
160
|
-
onClick: () =>
|
|
161
|
-
children:
|
|
162
|
-
}) : /* @__PURE__ */
|
|
161
|
+
onClick: () => P(r("/products/feature-detail-page", B.duplicateOf.id)),
|
|
162
|
+
children: B.duplicateOf.title
|
|
163
|
+
}) : /* @__PURE__ */ T("span", {
|
|
163
164
|
className: "text-muted-foreground",
|
|
164
165
|
children: "a request that has since been deleted"
|
|
165
166
|
}),
|
|
@@ -168,60 +169,60 @@ function M() {
|
|
|
168
169
|
]
|
|
169
170
|
})
|
|
170
171
|
}),
|
|
171
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ E("div", {
|
|
172
173
|
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
173
174
|
children: [
|
|
174
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
|
|
175
176
|
className: "pt-4 text-center",
|
|
176
177
|
children: [
|
|
177
|
-
/* @__PURE__ */
|
|
178
|
-
/* @__PURE__ */
|
|
178
|
+
/* @__PURE__ */ T(y, { className: "h-5 w-5 mx-auto text-primary" }),
|
|
179
|
+
/* @__PURE__ */ T("p", {
|
|
179
180
|
className: "text-2xl font-bold mt-1",
|
|
180
|
-
children:
|
|
181
|
+
children: B.priorityScore ?? 0
|
|
181
182
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ T("p", {
|
|
183
184
|
className: "text-xs text-muted-foreground",
|
|
184
185
|
children: "Priority Score"
|
|
185
186
|
})
|
|
186
187
|
]
|
|
187
188
|
}) }),
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
|
|
189
190
|
className: "pt-4 text-center",
|
|
190
191
|
children: [
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ T(b, { className: "h-5 w-5 mx-auto text-green-500" }),
|
|
193
|
+
/* @__PURE__ */ T("p", {
|
|
193
194
|
className: "text-2xl font-bold mt-1",
|
|
194
|
-
children:
|
|
195
|
+
children: B.voteCount
|
|
195
196
|
}),
|
|
196
|
-
/* @__PURE__ */
|
|
197
|
+
/* @__PURE__ */ T("p", {
|
|
197
198
|
className: "text-xs text-muted-foreground",
|
|
198
199
|
children: "Votes"
|
|
199
200
|
})
|
|
200
201
|
]
|
|
201
202
|
}) }),
|
|
202
|
-
/* @__PURE__ */
|
|
203
|
+
/* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
|
|
203
204
|
className: "pt-4 text-center",
|
|
204
205
|
children: [
|
|
205
|
-
/* @__PURE__ */
|
|
206
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ T(v, { className: "h-5 w-5 mx-auto text-yellow-500" }),
|
|
207
|
+
/* @__PURE__ */ T("p", {
|
|
207
208
|
className: "text-2xl font-bold mt-1",
|
|
208
|
-
children:
|
|
209
|
+
children: B.businessValue ?? 0
|
|
209
210
|
}),
|
|
210
|
-
/* @__PURE__ */
|
|
211
|
+
/* @__PURE__ */ T("p", {
|
|
211
212
|
className: "text-xs text-muted-foreground",
|
|
212
213
|
children: "Business Value"
|
|
213
214
|
})
|
|
214
215
|
]
|
|
215
216
|
}) }),
|
|
216
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
|
|
217
218
|
className: "pt-4 text-center",
|
|
218
219
|
children: [
|
|
219
|
-
/* @__PURE__ */
|
|
220
|
-
/* @__PURE__ */
|
|
220
|
+
/* @__PURE__ */ T(_, { className: "h-5 w-5 mx-auto text-blue-500" }),
|
|
221
|
+
/* @__PURE__ */ T("p", {
|
|
221
222
|
className: "text-2xl font-bold mt-1",
|
|
222
|
-
children:
|
|
223
|
+
children: H.length
|
|
223
224
|
}),
|
|
224
|
-
/* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ T("p", {
|
|
225
226
|
className: "text-xs text-muted-foreground",
|
|
226
227
|
children: "Competitors"
|
|
227
228
|
})
|
|
@@ -229,147 +230,148 @@ function M() {
|
|
|
229
230
|
}) })
|
|
230
231
|
]
|
|
231
232
|
}),
|
|
232
|
-
/* @__PURE__ */
|
|
233
|
+
/* @__PURE__ */ E("div", {
|
|
233
234
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
234
|
-
children: [/* @__PURE__ */
|
|
235
|
+
children: [/* @__PURE__ */ E("div", {
|
|
235
236
|
className: "lg:col-span-2 space-y-6",
|
|
236
237
|
children: [
|
|
237
|
-
/* @__PURE__ */
|
|
238
|
+
/* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Description" }) }), /* @__PURE__ */ T(k, { children: /* @__PURE__ */ T("p", {
|
|
238
239
|
className: "text-sm",
|
|
239
|
-
children:
|
|
240
|
+
children: B.description ?? "No description provided."
|
|
240
241
|
}) })] }),
|
|
241
|
-
/* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ E(j, { children: [
|
|
242
243
|
"Votes (",
|
|
243
|
-
|
|
244
|
+
V.length,
|
|
244
245
|
")"
|
|
245
|
-
] }) }), /* @__PURE__ */
|
|
246
|
+
] }) }), /* @__PURE__ */ E(k, {
|
|
246
247
|
className: "space-y-3",
|
|
247
|
-
children: [
|
|
248
|
+
children: [V.length === 0 && /* @__PURE__ */ T("p", {
|
|
248
249
|
className: "text-sm text-muted-foreground",
|
|
249
250
|
children: "No votes yet."
|
|
250
|
-
}),
|
|
251
|
+
}), V.map((e) => /* @__PURE__ */ E("div", {
|
|
251
252
|
className: "flex items-start justify-between border rounded-md p-3",
|
|
252
|
-
children: [/* @__PURE__ */
|
|
253
|
+
children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ E("p", {
|
|
253
254
|
className: "text-sm font-medium",
|
|
254
|
-
children: ["Voter: ", /* @__PURE__ */
|
|
255
|
-
}), e.comment && /* @__PURE__ */
|
|
255
|
+
children: ["Voter: ", /* @__PURE__ */ T(n, { id: e.voterId })]
|
|
256
|
+
}), e.comment && /* @__PURE__ */ T("p", {
|
|
256
257
|
className: "text-xs text-muted-foreground mt-1",
|
|
257
258
|
children: e.comment
|
|
258
|
-
})] }), /* @__PURE__ */
|
|
259
|
+
})] }), /* @__PURE__ */ E("div", {
|
|
259
260
|
className: "flex items-center gap-1",
|
|
260
|
-
children: [/* @__PURE__ */
|
|
261
|
+
children: [/* @__PURE__ */ T(b, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ T("span", {
|
|
261
262
|
className: "text-sm font-bold",
|
|
262
263
|
children: e.weight
|
|
263
264
|
})]
|
|
264
265
|
})]
|
|
265
266
|
}, e.id))]
|
|
266
267
|
})] }),
|
|
267
|
-
/* @__PURE__ */
|
|
268
|
+
/* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ E(j, { children: [
|
|
268
269
|
"Competitors (",
|
|
269
|
-
|
|
270
|
+
H.length,
|
|
270
271
|
")"
|
|
271
|
-
] }) }), /* @__PURE__ */
|
|
272
|
-
featureRequestId:
|
|
273
|
-
competitors:
|
|
272
|
+
] }) }), /* @__PURE__ */ T(k, { children: /* @__PURE__ */ T(i, {
|
|
273
|
+
featureRequestId: B.id,
|
|
274
|
+
competitors: H
|
|
274
275
|
}) })] })
|
|
275
276
|
]
|
|
276
|
-
}), /* @__PURE__ */
|
|
277
|
+
}), /* @__PURE__ */ E("div", {
|
|
277
278
|
className: "space-y-6",
|
|
278
279
|
children: [
|
|
279
|
-
/* @__PURE__ */
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
/* @__PURE__ */ T(m, { featureRequestId: B.id }),
|
|
281
|
+
/* @__PURE__ */ T(p, {
|
|
282
|
+
featureId: B.id,
|
|
283
|
+
productId: B.productId ?? null,
|
|
284
|
+
initiative: B.roadmapItem ?? null
|
|
283
285
|
}),
|
|
284
|
-
/* @__PURE__ */
|
|
285
|
-
/* @__PURE__ */
|
|
286
|
+
/* @__PURE__ */ T(d, { feature: B }),
|
|
287
|
+
/* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Details" }) }), /* @__PURE__ */ E(k, {
|
|
286
288
|
className: "space-y-3 text-sm",
|
|
287
289
|
children: [
|
|
288
|
-
/* @__PURE__ */
|
|
290
|
+
/* @__PURE__ */ E("div", {
|
|
289
291
|
className: "flex justify-between",
|
|
290
|
-
children: [/* @__PURE__ */
|
|
292
|
+
children: [/* @__PURE__ */ T("span", {
|
|
291
293
|
className: "text-muted-foreground",
|
|
292
294
|
children: "Status"
|
|
293
|
-
}), /* @__PURE__ */
|
|
295
|
+
}), /* @__PURE__ */ T(D, {
|
|
294
296
|
variant: "secondary",
|
|
295
297
|
className: "capitalize",
|
|
296
|
-
children:
|
|
298
|
+
children: B.status.toLowerCase().replace("_", " ")
|
|
297
299
|
})]
|
|
298
300
|
}),
|
|
299
|
-
/* @__PURE__ */
|
|
301
|
+
/* @__PURE__ */ E("div", {
|
|
300
302
|
className: "flex justify-between",
|
|
301
|
-
children: [/* @__PURE__ */
|
|
303
|
+
children: [/* @__PURE__ */ T("span", {
|
|
302
304
|
className: "text-muted-foreground",
|
|
303
305
|
children: "Priority Score"
|
|
304
|
-
}), /* @__PURE__ */
|
|
306
|
+
}), /* @__PURE__ */ T(D, {
|
|
305
307
|
variant: "outline",
|
|
306
|
-
children:
|
|
308
|
+
children: B.priorityScore ?? "N/A"
|
|
307
309
|
})]
|
|
308
310
|
}),
|
|
309
|
-
/* @__PURE__ */
|
|
311
|
+
/* @__PURE__ */ E("div", {
|
|
310
312
|
className: "flex justify-between",
|
|
311
|
-
children: [/* @__PURE__ */
|
|
313
|
+
children: [/* @__PURE__ */ T("span", {
|
|
312
314
|
className: "text-muted-foreground",
|
|
313
315
|
children: "Effort"
|
|
314
|
-
}), /* @__PURE__ */
|
|
316
|
+
}), /* @__PURE__ */ T(D, {
|
|
315
317
|
variant: "outline",
|
|
316
318
|
className: "capitalize",
|
|
317
|
-
children:
|
|
319
|
+
children: B.effortEstimate ?? "N/A"
|
|
318
320
|
})]
|
|
319
321
|
}),
|
|
320
|
-
/* @__PURE__ */
|
|
322
|
+
/* @__PURE__ */ E("div", {
|
|
321
323
|
className: "flex justify-between",
|
|
322
|
-
children: [/* @__PURE__ */
|
|
324
|
+
children: [/* @__PURE__ */ T("span", {
|
|
323
325
|
className: "text-muted-foreground",
|
|
324
326
|
children: "Business Value"
|
|
325
|
-
}), /* @__PURE__ */
|
|
327
|
+
}), /* @__PURE__ */ T("span", {
|
|
326
328
|
className: "font-medium",
|
|
327
|
-
children:
|
|
329
|
+
children: B.businessValue ?? "N/A"
|
|
328
330
|
})]
|
|
329
331
|
}),
|
|
330
|
-
/* @__PURE__ */
|
|
332
|
+
/* @__PURE__ */ E("div", {
|
|
331
333
|
className: "flex justify-between items-center",
|
|
332
|
-
children: [/* @__PURE__ */
|
|
334
|
+
children: [/* @__PURE__ */ T("span", {
|
|
333
335
|
className: "text-muted-foreground",
|
|
334
336
|
children: "Rating"
|
|
335
|
-
}), /* @__PURE__ */
|
|
337
|
+
}), /* @__PURE__ */ T(l, { rating: B.rating ?? null })]
|
|
336
338
|
}),
|
|
337
|
-
/* @__PURE__ */
|
|
339
|
+
/* @__PURE__ */ E("div", {
|
|
338
340
|
className: "flex justify-between items-center",
|
|
339
|
-
children: [/* @__PURE__ */
|
|
341
|
+
children: [/* @__PURE__ */ T("span", {
|
|
340
342
|
className: "text-muted-foreground",
|
|
341
343
|
children: "Category"
|
|
342
|
-
}),
|
|
344
|
+
}), B.category ? /* @__PURE__ */ T(c, { category: B.category }) : /* @__PURE__ */ T("span", {
|
|
343
345
|
className: "text-sm text-muted-foreground",
|
|
344
346
|
children: "Not set"
|
|
345
347
|
})]
|
|
346
348
|
}),
|
|
347
|
-
/* @__PURE__ */
|
|
349
|
+
/* @__PURE__ */ E("div", {
|
|
348
350
|
className: "flex justify-between",
|
|
349
|
-
children: [/* @__PURE__ */
|
|
351
|
+
children: [/* @__PURE__ */ T("span", {
|
|
350
352
|
className: "text-muted-foreground",
|
|
351
353
|
children: "Submitted By"
|
|
352
|
-
}), /* @__PURE__ */
|
|
354
|
+
}), /* @__PURE__ */ T(n, { id: B.submittedBy })]
|
|
353
355
|
}),
|
|
354
|
-
|
|
356
|
+
B.triagedAt && /* @__PURE__ */ E("div", {
|
|
355
357
|
className: "flex justify-between",
|
|
356
|
-
children: [/* @__PURE__ */
|
|
358
|
+
children: [/* @__PURE__ */ T("span", {
|
|
357
359
|
className: "text-muted-foreground",
|
|
358
360
|
children: "Decided"
|
|
359
|
-
}), /* @__PURE__ */
|
|
361
|
+
}), /* @__PURE__ */ E("span", { children: [new Date(B.triagedAt).toLocaleDateString(), B.triagedBy && /* @__PURE__ */ E(w, { children: [" by ", /* @__PURE__ */ T(n, { id: B.triagedBy })] })] })]
|
|
360
362
|
}),
|
|
361
|
-
/* @__PURE__ */
|
|
363
|
+
/* @__PURE__ */ E("div", {
|
|
362
364
|
className: "flex justify-between",
|
|
363
|
-
children: [/* @__PURE__ */
|
|
365
|
+
children: [/* @__PURE__ */ T("span", {
|
|
364
366
|
className: "text-muted-foreground",
|
|
365
367
|
children: "Updated"
|
|
366
|
-
}), /* @__PURE__ */
|
|
368
|
+
}), /* @__PURE__ */ T("span", { children: new Date(B.updatedAt).toLocaleDateString() })]
|
|
367
369
|
})
|
|
368
370
|
]
|
|
369
371
|
})] }),
|
|
370
|
-
|
|
372
|
+
B.tags?.length > 0 && /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Tags" }) }), /* @__PURE__ */ T(k, {
|
|
371
373
|
className: "flex flex-wrap gap-1",
|
|
372
|
-
children:
|
|
374
|
+
children: B.tags.map((e) => /* @__PURE__ */ T(D, {
|
|
373
375
|
variant: "outline",
|
|
374
376
|
className: "text-xs",
|
|
375
377
|
children: e
|
|
@@ -382,6 +384,6 @@ function M() {
|
|
|
382
384
|
});
|
|
383
385
|
}
|
|
384
386
|
//#endregion
|
|
385
|
-
export {
|
|
387
|
+
export { N as ProductsFeatureDetailPagePage };
|
|
386
388
|
|
|
387
389
|
//# sourceMappingURL=ProductsFeatureDetailPagePage.js.map
|