@burdenoff/microfe-planmagnet 2026.1002.1 → 2026.1002.3
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 +2533 -546
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-types.d.ts +5654 -71
- 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 +180 -162
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +100 -84
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/board/BulkActions.d.ts.map +1 -1
- package/dist/planmagnet/board/BulkActions.js +106 -55
- package/dist/planmagnet/board/BulkActions.js.map +1 -1
- package/dist/planmagnet/controls/Pickers.d.ts +10 -5
- 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/dashboards/DashboardUI.d.ts +0 -4
- package/dist/planmagnet/dashboards/DashboardUI.d.ts.map +1 -1
- package/dist/planmagnet/dashboards/DashboardUI.js +6 -13
- package/dist/planmagnet/dashboards/DashboardUI.js.map +1 -1
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.d.ts.map +1 -1
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js +70 -59
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js.map +1 -1
- package/dist/planmagnet/forecast/ResourceForecastCard.js +1 -1
- package/dist/planmagnet/forecast/ResourceForecastCard.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/DashboardsProjectManagerPagePage.d.ts +3 -3
- package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.js +26 -19
- package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.d.ts +2 -2
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.js +18 -12
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.js +251 -0
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.js.map +1 -0
- 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 +107 -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/ProductsReleaseDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +116 -114
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +174 -159
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +159 -145
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +161 -143
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +160 -156
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.d.ts.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +97 -99
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.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 +10 -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/planmagnet/satisfaction/DeliverySatisfactionSection.d.ts +20 -0
- package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.js +55 -0
- package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.d.ts +30 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.js +282 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.d.ts +9 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.js +390 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.d.ts +17 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.js +77 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.d.ts +18 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.js +87 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.d.ts +13 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.js +326 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.d.ts +23 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.js +258 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.d.ts +6 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.js +56 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.d.ts +16 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.js +102 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfaction.d.ts +154 -0
- package/dist/planmagnet/satisfaction/satisfaction.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfaction.js +210 -0
- package/dist/planmagnet/satisfaction/satisfaction.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.d.ts +108 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.js +211 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.d.ts +90 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.js +90 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.d.ts +116 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.js +86 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.js.map +1 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.d.ts +20 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.js +31 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.js.map +1 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.d.ts +35 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.d.ts.map +1 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.js +124 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.js.map +1 -0
- package/dist/planmagnet/skills/SkillChips.d.ts +30 -0
- package/dist/planmagnet/skills/SkillChips.d.ts.map +1 -0
- package/dist/planmagnet/skills/SkillChips.js +57 -0
- package/dist/planmagnet/skills/SkillChips.js.map +1 -0
- package/dist/planmagnet/skills/SkillCoverageCard.d.ts +24 -0
- package/dist/planmagnet/skills/SkillCoverageCard.d.ts.map +1 -0
- package/dist/planmagnet/skills/SkillCoverageCard.js +256 -0
- package/dist/planmagnet/skills/SkillCoverageCard.js.map +1 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.d.ts +10 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.d.ts.map +1 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.js +65 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.js.map +1 -0
- package/dist/planmagnet/skills/skillOperations.d.ts +52 -0
- package/dist/planmagnet/skills/skillOperations.d.ts.map +1 -0
- package/dist/planmagnet/skills/skillOperations.js +44 -0
- package/dist/planmagnet/skills/skillOperations.js.map +1 -0
- package/dist/planmagnet/skills/skillTags.d.ts +103 -0
- package/dist/planmagnet/skills/skillTags.d.ts.map +1 -0
- package/dist/planmagnet/skills/skillTags.js +115 -0
- package/dist/planmagnet/skills/skillTags.js.map +1 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.d.ts +28 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.d.ts.map +1 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.js +30 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.js.map +1 -0
- 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":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Package, Target, Users, Calendar, Loader2, Pencil } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { PlanMagnetNotFoundPage } from './NotFoundPage';\nimport {\n ProductHeaderActions,\n ProductRoadmapsPanel,\n ProductReleasesPanel,\n ProductFeatureRequestsPanel,\n ProductObjectivesPanel,\n ProductAboutPanel,\n ProductCompetitorsPanel,\n type ProductRoadmapRef,\n type ProductReleaseRef,\n type ProductFeatureRequestRef,\n type ProductObjectiveRef,\n type ProductCompetitorRef,\n} from '../product/ProductControls';\n\n/**\n * Product detail (BOFF-8045): the product's own fields, its North Star, and a Links\n * management panel for roadmaps/releases/feature requests/OKR objectives. Every relation\n * is selected directly off `planmagnetProduct(id)` rather than a separate workspace-wide\n * query — before this rewrite the page queried EVERY feature request/release in the\n * workspace regardless of product, which is the pre-existing bug this fixes (there was no\n * productId to filter on until now).\n */\n\nconst GET_PRODUCT_DETAIL = gql`\n query GetProductDetail($id: ID!) {\n planmagnetProduct(id: $id) {\n id\n workspaceId\n name\n description\n vision\n lifecycleStage\n ownerId\n archivedAt\n mission\n valueProposition\n businessModel\n targetSegments\n competitors {\n id\n name\n description\n website\n }\n northStar {\n name\n unit\n target\n current\n window\n measuredAt\n progressRatio\n }\n roadmaps {\n id\n name\n }\n releases {\n id\n name\n version\n status\n }\n featureRequests {\n id\n title\n status\n }\n objectives {\n id\n title\n status\n }\n createdAt\n updatedAt\n }\n }\n`;\n\ninterface ProductDetail {\n id: string;\n workspaceId: string;\n name: string;\n description: string | null;\n vision: string;\n lifecycleStage: string;\n ownerId: string;\n archivedAt: string | null;\n mission: string | null;\n valueProposition: string | null;\n businessModel: string | null;\n targetSegments: string[];\n competitors: ProductCompetitorRef[];\n northStar: {\n name: string;\n unit: string;\n target: number;\n current: number;\n window: string;\n measuredAt: string | null;\n progressRatio: number | null;\n } | null;\n roadmaps: ProductRoadmapRef[];\n releases: ProductReleaseRef[];\n featureRequests: ProductFeatureRequestRef[];\n objectives: ProductObjectiveRef[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport function ProductsProductDetailPagePage() {\n const navigate = usePlanMagnetNavigate();\n const productId = useEntityId('productId') ?? '';\n\n const { data, loading, error } = useQuery<{ planmagnetProduct: ProductDetail | null }>(\n GET_PRODUCT_DETAIL,\n { variables: { id: productId }, skip: !productId }\n );\n\n if (!productId) {\n return (\n <div className=\"p-6 text-muted-foreground\">No product selected. Open one from the list.</div>\n );\n }\n\n // Only before the first load: a refetch must not blank the page (it\n // unmounts every component on it and loses their state).\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load product: {error.message}</div>;\n }\n\n const product = data?.planmagnetProduct;\n // Deleted, unauthorized or an unknown id all read back the same way (no cross-tenant\n // existence leak) — the real 404 page, not an inline message.\n if (!product) return <PlanMagnetNotFoundPage />;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Package className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{product.name}</h1>\n {product.archivedAt && <Badge variant=\"outline\">Archived</Badge>}\n <Badge variant=\"secondary\" className=\"capitalize\">\n {enumLabel(product.lifecycleStage)}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">{product.vision || 'No vision set'}</p>\n </div>\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/products/edit-product-page', product.id))}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <ProductHeaderActions\n productId={product.id}\n name={product.name}\n archivedAt={product.archivedAt}\n />\n </div>\n </div>\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 North Star\n </div>\n {product.northStar ? (\n <p className=\"text-sm font-bold mt-1 truncate\">\n {product.northStar.current} / {product.northStar.target} {product.northStar.unit}\n {product.northStar.progressRatio != null\n ? ` (${Math.round(product.northStar.progressRatio * 100)}%)`\n : ''}\n </p>\n ) : (\n <p className=\"text-sm font-medium mt-1 text-muted-foreground\">Not set</p>\n )}\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 Owner\n </div>\n <p className=\"text-sm font-bold mt-1 truncate\">\n <PersonName id={product.ownerId} />\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 <Calendar className=\"h-4 w-4\" />\n Created\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {new Date(product.createdAt).toLocaleDateString()}\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 <Calendar className=\"h-4 w-4\" />\n Updated\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {new Date(product.updatedAt).toLocaleDateString()}\n </p>\n </CardContent>\n </Card>\n </div>\n\n {product.description && (\n <Card>\n <CardContent className=\"pt-4 text-sm text-muted-foreground\">\n {product.description}\n </CardContent>\n </Card>\n )}\n\n {/* PRM-02: strategy narrative + competitive landscape. */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <ProductAboutPanel product={product} />\n <Card>\n <CardHeader>\n <CardTitle>Competitive landscape</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductCompetitorsPanel productId={product.id} competitors={product.competitors} />\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>Roadmaps</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductRoadmapsPanel productId={product.id} roadmaps={product.roadmaps} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Releases</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductReleasesPanel productId={product.id} releases={product.releases} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Feature Requests</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductFeatureRequestsPanel\n productId={product.id}\n featureRequests={product.featureRequests}\n />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>OKR Objectives</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductObjectivesPanel productId={product.id} objectives={product.objectives} />\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Package, Target, Users, Calendar, Loader2, Pencil, ListTree } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { PlanMagnetNotFoundPage } from './NotFoundPage';\nimport {\n ProductHeaderActions,\n ProductRoadmapsPanel,\n ProductReleasesPanel,\n ProductFeatureRequestsPanel,\n ProductObjectivesPanel,\n ProductAboutPanel,\n ProductCompetitorsPanel,\n type ProductRoadmapRef,\n type ProductReleaseRef,\n type ProductFeatureRequestRef,\n type ProductObjectiveRef,\n type ProductCompetitorRef,\n} from '../product/ProductControls';\nimport { ProductLinePicker } from '../product/ProductLineControls';\nimport { ProductTeamsPanel } from '../product/ProductTeamsPanel';\nimport { ProductFeaturesPanel } from '../product/ProductFeatureControls';\nimport { SatisfactionSummaryCard } from '../satisfaction/SatisfactionSummaryCard';\n\n/**\n * Product detail (BOFF-8045): the product's own fields, its North Star, and a Links\n * management panel for roadmaps/releases/feature requests/OKR objectives. Every relation\n * is selected directly off `planmagnetProduct(id)` rather than a separate workspace-wide\n * query — before this rewrite the page queried EVERY feature request/release in the\n * workspace regardless of product, which is the pre-existing bug this fixes (there was no\n * productId to filter on until now).\n *\n * PRM-01 adds the product's place in the hierarchy: its product line, the teams that own it\n * (a link that grants no access), and its features — capabilities the team builds, distinct\n * from the feature requests below. Each of those sections runs its own query, so a field the\n * gateway does not serve yet fails that card, never this page's GetProductDetail.\n */\n\nconst GET_PRODUCT_DETAIL = gql`\n query GetProductDetail($id: ID!) {\n planmagnetProduct(id: $id) {\n id\n workspaceId\n name\n description\n vision\n lifecycleStage\n ownerId\n archivedAt\n mission\n valueProposition\n businessModel\n targetSegments\n competitors {\n id\n name\n description\n website\n }\n northStar {\n name\n unit\n target\n current\n window\n measuredAt\n progressRatio\n }\n roadmaps {\n id\n name\n }\n releases {\n id\n name\n version\n status\n }\n featureRequests {\n id\n title\n status\n }\n objectives {\n id\n title\n status\n }\n createdAt\n updatedAt\n }\n }\n`;\n\ninterface ProductDetail {\n id: string;\n workspaceId: string;\n name: string;\n description: string | null;\n vision: string;\n lifecycleStage: string;\n ownerId: string;\n archivedAt: string | null;\n mission: string | null;\n valueProposition: string | null;\n businessModel: string | null;\n targetSegments: string[];\n competitors: ProductCompetitorRef[];\n northStar: {\n name: string;\n unit: string;\n target: number;\n current: number;\n window: string;\n measuredAt: string | null;\n progressRatio: number | null;\n } | null;\n roadmaps: ProductRoadmapRef[];\n releases: ProductReleaseRef[];\n featureRequests: ProductFeatureRequestRef[];\n objectives: ProductObjectiveRef[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport function ProductsProductDetailPagePage() {\n const navigate = usePlanMagnetNavigate();\n const productId = useEntityId('productId') ?? '';\n\n const { data, loading, error } = useQuery<{ planmagnetProduct: ProductDetail | null }>(\n GET_PRODUCT_DETAIL,\n { variables: { id: productId }, skip: !productId }\n );\n\n if (!productId) {\n return (\n <div className=\"p-6 text-muted-foreground\">No product selected. Open one from the list.</div>\n );\n }\n\n // Only before the first load: a refetch must not blank the page (it\n // unmounts every component on it and loses their state).\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load product: {error.message}</div>;\n }\n\n const product = data?.planmagnetProduct;\n // Deleted, unauthorized or an unknown id all read back the same way (no cross-tenant\n // existence leak) — the real 404 page, not an inline message.\n if (!product) return <PlanMagnetNotFoundPage />;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Package className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{product.name}</h1>\n {product.archivedAt && <Badge variant=\"outline\">Archived</Badge>}\n <Badge variant=\"secondary\" className=\"capitalize\">\n {enumLabel(product.lifecycleStage)}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">{product.vision || 'No vision set'}</p>\n </div>\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/products/edit-product-page', product.id))}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <ProductHeaderActions\n productId={product.id}\n name={product.name}\n archivedAt={product.archivedAt}\n />\n </div>\n </div>\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 North Star\n </div>\n {product.northStar ? (\n <p className=\"text-sm font-bold mt-1 truncate\">\n {product.northStar.current} / {product.northStar.target} {product.northStar.unit}\n {product.northStar.progressRatio != null\n ? ` (${Math.round(product.northStar.progressRatio * 100)}%)`\n : ''}\n </p>\n ) : (\n <p className=\"text-sm font-medium mt-1 text-muted-foreground\">Not set</p>\n )}\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 Owner\n </div>\n <p className=\"text-sm font-bold mt-1 truncate\">\n <PersonName id={product.ownerId} />\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 <Calendar className=\"h-4 w-4\" />\n Created\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {new Date(product.createdAt).toLocaleDateString()}\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 <Calendar className=\"h-4 w-4\" />\n Updated\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {new Date(product.updatedAt).toLocaleDateString()}\n </p>\n </CardContent>\n </Card>\n </div>\n\n {product.description && (\n <Card>\n <CardContent className=\"pt-4 text-sm text-muted-foreground\">\n {product.description}\n </CardContent>\n </Card>\n )}\n\n {/* PRM-01: Product line > Product > Feature > Epic > Story. */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>Product line</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductLinePicker productId={product.id} />\n </CardContent>\n </Card>\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle>Teams</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductTeamsPanel productId={product.id} />\n </CardContent>\n </Card>\n </div>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2\">\n <CardTitle>Features</CardTitle>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => navigate('/products/product-hierarchy-page')}\n data-testid=\"product-view-hierarchy\"\n >\n <ListTree className=\"mr-1 h-4 w-4\" />\n View hierarchy\n </Button>\n </CardHeader>\n <CardContent>\n <ProductFeaturesPanel productId={product.id} productArchived={!!product.archivedAt} />\n </CardContent>\n </Card>\n\n {/* ANR-21: CSAT and NPS from responses about this product (its own query — a failure stays in the card). */}\n <SatisfactionSummaryCard scope={{ productId: product.id }} />\n\n {/* PRM-02: strategy narrative + competitive landscape. */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <ProductAboutPanel product={product} />\n <Card>\n <CardHeader>\n <CardTitle>Competitive landscape</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductCompetitorsPanel productId={product.id} competitors={product.competitors} />\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>Roadmaps</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductRoadmapsPanel productId={product.id} roadmaps={product.roadmaps} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Releases</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductReleasesPanel productId={product.id} releases={product.releases} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Feature requests</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductFeatureRequestsPanel\n productId={product.id}\n featureRequests={product.featureRequests}\n />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>OKR Objectives</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductObjectivesPanel productId={product.id} objectives={product.objectives} />\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA2CA,IAAM,IAAqB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuF9B,SAAgB,IAAgC;CAC9C,IAAM,IAAW,GAAuB,EAClC,IAAY,EAAY,YAAY,IAAI,IAExC,EAAE,SAAM,YAAS,aAAU,EAC/B,GACA;EAAE,WAAW,EAAE,IAAI,GAAW;EAAE,MAAM,CAAC;EAAW,CACnD;AAED,KAAI,CAAC,EACH,QACE,kBAAC,OAAD;EAAK,WAAU;YAA4B;EAAkD,CAAA;AAMjG,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;EAC9D,CAAA;AAIV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAsC,4BAAyB,EAAM,QAAc;;CAG5F,IAAM,IAAU,GAAM;AAKtB,QAFK,IAGH,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA;MAC5C,kBAAC,MAAD;OAAI,WAAU;iBAA0B,EAAQ;OAAU,CAAA;MACzD,EAAQ,cAAc,kBAAC,GAAD;OAAO,SAAQ;iBAAU;OAAgB,CAAA;MAChE,kBAAC,GAAD;OAAO,SAAQ;OAAY,WAAU;iBAClC,EAAU,EAAQ,eAAe;OAC5B,CAAA;MACJ;QACN,kBAAC,KAAD;KAAG,WAAU;eAAsC,EAAQ,UAAU;KAAoB,CAAA,CACrF,EAAA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,MAAK;OACL,SAAQ;OACR,eAAe,EAAS,EAAW,+BAA+B,EAAQ,GAAG,CAAC;iBAHhF,CAKE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,OAE5B;UACT,kBAAC,GAAD;OACE,WAAW,EAAQ;OACnB,MAAM,EAAQ;OACd,YAAY,EAAQ;OACpB,CAAA,CACE;;KACF,CAAA,CACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,aAE1B;UACL,EAAQ,YACP,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAQ,UAAU;QAAQ;QAAI,EAAQ,UAAU;QAAO;QAAE,EAAQ,UAAU;QAC3E,EAAQ,UAAU,iBAAiB,OAEhC,KADA,KAAK,KAAK,MAAM,EAAQ,UAAU,gBAAgB,IAAI,CAAC;QAEzD;WAEJ,kBAAC,KAAD;OAAG,WAAU;iBAAiD;OAAW,CAAA,CAE/D;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA,EAAA,QAEzB;UACN,kBAAC,KAAD;OAAG,WAAU;iBACX,kBAAC,GAAD,EAAY,IAAI,EAAQ,SAAW,CAAA;OACjC,CAAA,CACQ;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,UAE5B;UACN,kBAAC,KAAD;OAAG,WAAU;iBACV,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB;OAC/C,CAAA,CACQ;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,UAE5B;UACN,kBAAC,KAAD;OAAG,WAAU;iBACV,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB;OAC/C,CAAA,CACQ;SACT,CAAA;KACH;;GAEL,EAAQ,eACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IAAa,WAAU;cACpB,EAAQ;IACG,CAAA,EACT,CAAA;GAIT,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,gBAAwB,CAAA,EACxB,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAmB,WAAW,EAAQ,IAAM,CAAA,EAChC,CAAA,CACT,EAAA,CAAA,EACP,kBAAC,GAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA,EACjB,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAmB,WAAW,EAAQ,IAAM,CAAA,EAChC,CAAA,CACT;OACH;;GACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAY,WAAU;cAAtB,CACE,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA,EAC/B,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,eAAe,EAAS,mCAAmC;KAC3D,eAAY;eAJd,CAME,kBAAC,GAAD,EAAU,WAAU,gBAAiB,CAAA,EAAA,iBAE9B;OACE;OACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IAAsB,WAAW,EAAQ;IAAI,iBAAiB,CAAC,CAAC,EAAQ;IAAc,CAAA,EAC1E,CAAA,CACT,EAAA,CAAA;GAGP,kBAAC,GAAD,EAAyB,OAAO,EAAE,WAAW,EAAQ,IAAI,EAAI,CAAA;GAG7D,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAA4B,YAAW,CAAA,EACvC,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,yBAAiC,CAAA,EACjC,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAyB,WAAW,EAAQ;KAAI,aAAa,EAAQ;KAAe,CAAA,EACxE,CAAA,CACT,EAAA,CAAA,CACH;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA,EACpB,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAsB,WAAW,EAAQ;MAAI,UAAU,EAAQ;MAAY,CAAA,EAC/D,CAAA,CACT,EAAA,CAAA;KACP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA,EACpB,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAsB,WAAW,EAAQ;MAAI,UAAU,EAAQ;MAAY,CAAA,EAC/D,CAAA,CACT,EAAA,CAAA;KACP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,oBAA4B,CAAA,EAC5B,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MACE,WAAW,EAAQ;MACnB,iBAAiB,EAAQ;MACzB,CAAA,EACU,CAAA,CACT,EAAA,CAAA;KACP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,kBAA0B,CAAA,EAC1B,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAwB,WAAW,EAAQ;MAAI,YAAY,EAAQ;MAAc,CAAA,EACrE,CAAA,CACT,EAAA,CAAA;KACH;;GACF;MA3La,kBAAC,GAAD,EAA0B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsProductFeatureDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductFeatureDetailPagePage.tsx"],"names":[],"mappings":"AA4CA,wBAAgB,oCAAoC,4CAwOnD"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { PersonName as t } from "../people/People.js";
|
|
3
|
+
import { useTr as n } from "../../shared/hooks/useTr.js";
|
|
4
|
+
import { ErrorLine as r } from "../controls/ConfirmButton.js";
|
|
5
|
+
import { detailPath as i } from "../utils/rowNavigation.js";
|
|
6
|
+
import { useEntityId as a } from "../hooks/useEntityId.js";
|
|
7
|
+
import { PlanMagnetNotFoundPage as o } from "./NotFoundPage.js";
|
|
8
|
+
import { PRODUCT_FEATURE_DETAIL as s } from "../product/hierarchyOperations.js";
|
|
9
|
+
import { parseServerDate as c } from "../product/hierarchy.js";
|
|
10
|
+
import { ProductFeatureHeaderActions as l, ProductFeatureStatusBadge as u } from "../product/ProductFeatureControls.js";
|
|
11
|
+
import { FeatureEpicsPanel as d, FeatureRequestsPanel as f } from "../product/FeatureLinkPanels.js";
|
|
12
|
+
import { ChevronRight as p, Loader2 as m, MessageSquare as h, Milestone as g, Puzzle as _, Users as v } from "lucide-react";
|
|
13
|
+
import { useQuery as y } from "@apollo/client/react";
|
|
14
|
+
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
15
|
+
import { Badge as C, Card as w, CardContent as T, CardHeader as E, CardTitle as D } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
//#region src/planmagnet/pages/ProductsProductFeatureDetailPagePage.tsx
|
|
17
|
+
function O() {
|
|
18
|
+
let O = n(), k = e(), A = a("productFeatureId") ?? "", { data: j, loading: M, error: N } = y(s, {
|
|
19
|
+
variables: { id: A },
|
|
20
|
+
skip: !A
|
|
21
|
+
});
|
|
22
|
+
if (!A) return /* @__PURE__ */ x("div", {
|
|
23
|
+
className: "p-6 text-muted-foreground",
|
|
24
|
+
children: O("planmagnet.productFeatures.detail.noneSelected", "No feature selected. Open one from its product’s page.")
|
|
25
|
+
});
|
|
26
|
+
if (M && !j) return /* @__PURE__ */ x("div", {
|
|
27
|
+
className: "flex items-center justify-center p-12",
|
|
28
|
+
children: /* @__PURE__ */ x(m, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
29
|
+
});
|
|
30
|
+
if (N && !j) return /* @__PURE__ */ x("div", {
|
|
31
|
+
className: "p-6 text-destructive",
|
|
32
|
+
children: O("planmagnet.productFeatures.detail.loadError", "Failed to load the feature: {{message}}", { message: N.message })
|
|
33
|
+
});
|
|
34
|
+
let P = j?.planmagnetProductFeature;
|
|
35
|
+
if (!P) return /* @__PURE__ */ x(o, {});
|
|
36
|
+
let F = !!P.archivedAt, I = c(P.updatedAt), L = c(P.createdAt);
|
|
37
|
+
return /* @__PURE__ */ S("div", {
|
|
38
|
+
className: "space-y-6 p-6",
|
|
39
|
+
children: [
|
|
40
|
+
/* @__PURE__ */ S("nav", {
|
|
41
|
+
"aria-label": O("planmagnet.productFeatures.detail.breadcrumb", "Breadcrumb"),
|
|
42
|
+
className: "flex flex-wrap items-center gap-1 text-xs text-muted-foreground",
|
|
43
|
+
children: [
|
|
44
|
+
P.product?.productLine && /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x("button", {
|
|
45
|
+
type: "button",
|
|
46
|
+
className: "hover:underline",
|
|
47
|
+
onClick: () => k("/products/product-lines-page"),
|
|
48
|
+
children: P.product.productLine.name
|
|
49
|
+
}), /* @__PURE__ */ x(p, { className: "h-3 w-3" })] }),
|
|
50
|
+
P.product ? /* @__PURE__ */ x("button", {
|
|
51
|
+
type: "button",
|
|
52
|
+
className: "hover:underline",
|
|
53
|
+
onClick: () => P.product && k(i("/products/product-detail-page", P.product.id)),
|
|
54
|
+
children: P.product.name
|
|
55
|
+
}) : /* @__PURE__ */ x("span", { children: O("planmagnet.productFeatures.detail.productGone", "Deleted product") }),
|
|
56
|
+
/* @__PURE__ */ x(p, { className: "h-3 w-3" }),
|
|
57
|
+
/* @__PURE__ */ x("span", {
|
|
58
|
+
className: "text-foreground",
|
|
59
|
+
children: P.name
|
|
60
|
+
})
|
|
61
|
+
]
|
|
62
|
+
}),
|
|
63
|
+
/* @__PURE__ */ S("div", {
|
|
64
|
+
className: "flex flex-wrap items-start justify-between gap-4",
|
|
65
|
+
children: [/* @__PURE__ */ S("div", {
|
|
66
|
+
className: "min-w-0",
|
|
67
|
+
children: [/* @__PURE__ */ S("div", {
|
|
68
|
+
className: "flex flex-wrap items-center gap-2",
|
|
69
|
+
children: [
|
|
70
|
+
/* @__PURE__ */ x(_, { className: "h-5 w-5 text-primary" }),
|
|
71
|
+
/* @__PURE__ */ x("h1", {
|
|
72
|
+
className: "text-2xl font-semibold",
|
|
73
|
+
children: P.name
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ x(u, { status: P.status }),
|
|
76
|
+
F && /* @__PURE__ */ x(C, {
|
|
77
|
+
variant: "outline",
|
|
78
|
+
children: O("planmagnet.productFeatures.archived", "Archived")
|
|
79
|
+
})
|
|
80
|
+
]
|
|
81
|
+
}), /* @__PURE__ */ x("p", {
|
|
82
|
+
className: "mt-1 text-sm text-muted-foreground",
|
|
83
|
+
children: O("planmagnet.productFeatures.detail.subtitle", "A product feature — what the team is building. Feature requests are the feedback it answers.")
|
|
84
|
+
})]
|
|
85
|
+
}), /* @__PURE__ */ x(l, {
|
|
86
|
+
feature: P,
|
|
87
|
+
epicCount: P.epics.length,
|
|
88
|
+
requestCount: P.featureRequests.length
|
|
89
|
+
})]
|
|
90
|
+
}),
|
|
91
|
+
N && /* @__PURE__ */ x(r, { message: O("planmagnet.productFeatures.detail.refreshError", "The feature could not be refreshed: {{message}}", { message: N.message }) }),
|
|
92
|
+
F && /* @__PURE__ */ x("p", {
|
|
93
|
+
className: "rounded-md bg-muted p-3 text-sm text-muted-foreground",
|
|
94
|
+
children: O("planmagnet.productFeatures.detail.archivedNote", "This feature is archived. It keeps its epics and requests, but takes no new links until it is unarchived.")
|
|
95
|
+
}),
|
|
96
|
+
/* @__PURE__ */ S("div", {
|
|
97
|
+
className: "grid grid-cols-2 gap-4 md:grid-cols-4",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ x(w, { children: /* @__PURE__ */ S(T, {
|
|
100
|
+
className: "pt-4",
|
|
101
|
+
children: [/* @__PURE__ */ x("p", {
|
|
102
|
+
className: "text-xs text-muted-foreground",
|
|
103
|
+
children: O("planmagnet.productFeatures.fields.status", "Status")
|
|
104
|
+
}), /* @__PURE__ */ x("div", {
|
|
105
|
+
className: "mt-1",
|
|
106
|
+
children: /* @__PURE__ */ x(u, { status: P.status })
|
|
107
|
+
})]
|
|
108
|
+
}) }),
|
|
109
|
+
/* @__PURE__ */ x(w, { children: /* @__PURE__ */ S(T, {
|
|
110
|
+
className: "pt-4",
|
|
111
|
+
children: [/* @__PURE__ */ S("div", {
|
|
112
|
+
className: "flex items-center gap-2 text-xs text-muted-foreground",
|
|
113
|
+
children: [/* @__PURE__ */ x(v, { className: "h-4 w-4" }), O("planmagnet.productFeatures.fields.owner", "Owner")]
|
|
114
|
+
}), /* @__PURE__ */ x("p", {
|
|
115
|
+
className: "mt-1 truncate text-sm font-bold",
|
|
116
|
+
children: P.ownerId ? /* @__PURE__ */ x(t, { id: P.ownerId }) : O("planmagnet.productFeatures.fields.noOwner", "No owner")
|
|
117
|
+
})]
|
|
118
|
+
}) }),
|
|
119
|
+
/* @__PURE__ */ x(w, { children: /* @__PURE__ */ S(T, {
|
|
120
|
+
className: "pt-4",
|
|
121
|
+
children: [/* @__PURE__ */ S("div", {
|
|
122
|
+
className: "flex items-center gap-2 text-xs text-muted-foreground",
|
|
123
|
+
children: [/* @__PURE__ */ x(g, { className: "h-4 w-4" }), O("planmagnet.productFeatures.detail.epics", "Epics")]
|
|
124
|
+
}), /* @__PURE__ */ x("p", {
|
|
125
|
+
className: "mt-1 text-xl font-bold",
|
|
126
|
+
children: P.epics.length
|
|
127
|
+
})]
|
|
128
|
+
}) }),
|
|
129
|
+
/* @__PURE__ */ x(w, { children: /* @__PURE__ */ S(T, {
|
|
130
|
+
className: "pt-4",
|
|
131
|
+
children: [/* @__PURE__ */ S("div", {
|
|
132
|
+
className: "flex items-center gap-2 text-xs text-muted-foreground",
|
|
133
|
+
children: [/* @__PURE__ */ x(h, { className: "h-4 w-4" }), O("planmagnet.productFeatures.detail.requests", "Feature requests")]
|
|
134
|
+
}), /* @__PURE__ */ x("p", {
|
|
135
|
+
className: "mt-1 text-xl font-bold",
|
|
136
|
+
children: P.featureRequests.length
|
|
137
|
+
})]
|
|
138
|
+
}) })
|
|
139
|
+
]
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ S(w, { children: [/* @__PURE__ */ x(E, { children: /* @__PURE__ */ x(D, { children: O("planmagnet.productFeatures.fields.description", "Description") }) }), /* @__PURE__ */ S(T, {
|
|
142
|
+
className: "space-y-2",
|
|
143
|
+
children: [/* @__PURE__ */ x("p", {
|
|
144
|
+
className: "whitespace-pre-wrap text-sm",
|
|
145
|
+
children: P.description || O("planmagnet.productFeatures.detail.noDescription", "No description yet.")
|
|
146
|
+
}), /* @__PURE__ */ S("p", {
|
|
147
|
+
className: "text-xs text-muted-foreground",
|
|
148
|
+
children: [
|
|
149
|
+
L && O("planmagnet.productFeatures.detail.created", "Created {{date}}", { date: L.toLocaleDateString() }),
|
|
150
|
+
L && I && " · ",
|
|
151
|
+
I && O("planmagnet.productFeatures.detail.updated", "Updated {{date}}", { date: I.toLocaleDateString() })
|
|
152
|
+
]
|
|
153
|
+
})]
|
|
154
|
+
})] }),
|
|
155
|
+
/* @__PURE__ */ S("div", {
|
|
156
|
+
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
157
|
+
children: [/* @__PURE__ */ S(w, { children: [/* @__PURE__ */ x(E, { children: /* @__PURE__ */ x(D, { children: O("planmagnet.productFeatures.detail.epicsTitle", "Epics and stories") }) }), /* @__PURE__ */ x(T, { children: /* @__PURE__ */ x(d, {
|
|
158
|
+
featureId: P.id,
|
|
159
|
+
archived: F,
|
|
160
|
+
epics: P.epics
|
|
161
|
+
}) })] }), /* @__PURE__ */ S(w, { children: [/* @__PURE__ */ x(E, { children: /* @__PURE__ */ x(D, { children: O("planmagnet.productFeatures.detail.requestsTitle", "Feature requests") }) }), /* @__PURE__ */ x(T, { children: /* @__PURE__ */ x(f, {
|
|
162
|
+
featureId: P.id,
|
|
163
|
+
productId: P.productId,
|
|
164
|
+
archived: F,
|
|
165
|
+
requests: P.featureRequests
|
|
166
|
+
}) })] })]
|
|
167
|
+
})
|
|
168
|
+
]
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
export { O as ProductsProductFeatureDetailPagePage };
|
|
173
|
+
|
|
174
|
+
//# sourceMappingURL=ProductsProductFeatureDetailPagePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsProductFeatureDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductFeatureDetailPagePage.tsx"],"sourcesContent":["import { useQuery } from '@apollo/client/react';\nimport { Badge, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { ChevronRight, Loader2, MessageSquare, Milestone, Puzzle, Users } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { ErrorLine } from '../controls/ConfirmButton';\nimport { PlanMagnetNotFoundPage } from './NotFoundPage';\nimport { PRODUCT_FEATURE_DETAIL } from '../product/hierarchyOperations';\nimport { parseServerDate } from '../product/hierarchy';\nimport {\n ProductFeatureHeaderActions,\n ProductFeatureStatusBadge,\n type ProductFeatureValues,\n} from '../product/ProductFeatureControls';\nimport {\n FeatureEpicsPanel,\n FeatureRequestsPanel,\n type FeatureEpic,\n type FeatureLinkedRequest,\n} from '../product/FeatureLinkPanels';\n\n/**\n * A product feature (PRM-01): a capability the team decided to build, under one product. Edit its name,\n * description, status and owner; archive or delete it; link the EPIC work items that deliver it (each shown with\n * its stories) and the feature requests it answers. Not a feature request — those are customer feedback, and keep\n * their own page.\n */\n\ninterface FeatureDetail extends ProductFeatureValues {\n createdAt: string;\n updatedAt: string;\n product: {\n id: string;\n name: string;\n archivedAt: string | null;\n productLine: { id: string; name: string } | null;\n } | null;\n epics: FeatureEpic[];\n featureRequests: FeatureLinkedRequest[];\n}\n\nexport function ProductsProductFeatureDetailPagePage() {\n const tr = useTr();\n const navigate = usePlanMagnetNavigate();\n const featureId = useEntityId('productFeatureId') ?? '';\n\n const { data, loading, error } = useQuery<{ planmagnetProductFeature: FeatureDetail | null }>(\n PRODUCT_FEATURE_DETAIL,\n { variables: { id: featureId }, skip: !featureId }\n );\n\n if (!featureId) {\n return (\n <div className=\"p-6 text-muted-foreground\">\n {tr(\n 'planmagnet.productFeatures.detail.noneSelected',\n 'No feature selected. Open one from its product’s page.'\n )}\n </div>\n );\n }\n\n // Only before the first load: a refetch must not blank the page (it unmounts every component on it).\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 && !data) {\n return (\n <div className=\"p-6 text-destructive\">\n {tr(\n 'planmagnet.productFeatures.detail.loadError',\n 'Failed to load the feature: {{message}}',\n {\n message: error.message,\n }\n )}\n </div>\n );\n }\n\n const feature = data?.planmagnetProductFeature;\n // Deleted, another workspace's or an unknown id all read back as null — the real 404 page.\n if (!feature) return <PlanMagnetNotFoundPage />;\n\n const archived = !!feature.archivedAt;\n const updated = parseServerDate(feature.updatedAt);\n const created = parseServerDate(feature.createdAt);\n\n return (\n <div className=\"space-y-6 p-6\">\n <nav\n aria-label={tr('planmagnet.productFeatures.detail.breadcrumb', 'Breadcrumb')}\n className=\"flex flex-wrap items-center gap-1 text-xs text-muted-foreground\"\n >\n {feature.product?.productLine && (\n <>\n <button\n type=\"button\"\n className=\"hover:underline\"\n onClick={() => navigate('/products/product-lines-page')}\n >\n {feature.product.productLine.name}\n </button>\n <ChevronRight className=\"h-3 w-3\" />\n </>\n )}\n {feature.product ? (\n <button\n type=\"button\"\n className=\"hover:underline\"\n onClick={() =>\n feature.product &&\n navigate(detailPath('/products/product-detail-page', feature.product.id))\n }\n >\n {feature.product.name}\n </button>\n ) : (\n <span>{tr('planmagnet.productFeatures.detail.productGone', 'Deleted product')}</span>\n )}\n <ChevronRight className=\"h-3 w-3\" />\n <span className=\"text-foreground\">{feature.name}</span>\n </nav>\n\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Puzzle className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{feature.name}</h1>\n <ProductFeatureStatusBadge status={feature.status} />\n {archived && (\n <Badge variant=\"outline\">\n {tr('planmagnet.productFeatures.archived', 'Archived')}\n </Badge>\n )}\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.productFeatures.detail.subtitle',\n 'A product feature — what the team is building. Feature requests are the feedback it answers.'\n )}\n </p>\n </div>\n <ProductFeatureHeaderActions\n feature={feature}\n epicCount={feature.epics.length}\n requestCount={feature.featureRequests.length}\n />\n </div>\n\n {error && (\n <ErrorLine\n message={tr(\n 'planmagnet.productFeatures.detail.refreshError',\n 'The feature could not be refreshed: {{message}}',\n { message: error.message }\n )}\n />\n )}\n {archived && (\n <p className=\"rounded-md bg-muted p-3 text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.productFeatures.detail.archivedNote',\n 'This feature is archived. It keeps its epics and requests, but takes no new links until it is unarchived.'\n )}\n </p>\n )}\n\n <div className=\"grid grid-cols-2 gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">\n {tr('planmagnet.productFeatures.fields.status', 'Status')}\n </p>\n <div className=\"mt-1\">\n <ProductFeatureStatusBadge status={feature.status} />\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Users className=\"h-4 w-4\" />\n {tr('planmagnet.productFeatures.fields.owner', 'Owner')}\n </div>\n <p className=\"mt-1 truncate text-sm font-bold\">\n {feature.ownerId ? (\n <PersonName id={feature.ownerId} />\n ) : (\n tr('planmagnet.productFeatures.fields.noOwner', 'No owner')\n )}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Milestone className=\"h-4 w-4\" />\n {tr('planmagnet.productFeatures.detail.epics', 'Epics')}\n </div>\n <p className=\"mt-1 text-xl font-bold\">{feature.epics.length}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <MessageSquare className=\"h-4 w-4\" />\n {tr('planmagnet.productFeatures.detail.requests', 'Feature requests')}\n </div>\n <p className=\"mt-1 text-xl font-bold\">{feature.featureRequests.length}</p>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>\n {tr('planmagnet.productFeatures.fields.description', 'Description')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"whitespace-pre-wrap text-sm\">\n {feature.description ||\n tr('planmagnet.productFeatures.detail.noDescription', 'No description yet.')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {created &&\n tr('planmagnet.productFeatures.detail.created', 'Created {{date}}', {\n date: created.toLocaleDateString(),\n })}\n {created && updated && ' · '}\n {updated &&\n tr('planmagnet.productFeatures.detail.updated', 'Updated {{date}}', {\n date: updated.toLocaleDateString(),\n })}\n </p>\n </CardContent>\n </Card>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>\n {tr('planmagnet.productFeatures.detail.epicsTitle', 'Epics and stories')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <FeatureEpicsPanel featureId={feature.id} archived={archived} epics={feature.epics} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>\n {tr('planmagnet.productFeatures.detail.requestsTitle', 'Feature requests')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <FeatureRequestsPanel\n featureId={feature.id}\n productId={feature.productId}\n archived={archived}\n requests={feature.featureRequests}\n />\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsProductHierarchyPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductHierarchyPagePage.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,gCAAgC,4CA2N/C"}
|