@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":"ProductsProductsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Package, Plus, Archive, Loader2, Map, GitBranch, Star } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { enumLabel } from '../controls/WorkItemControls';\n\n/**\n * The real \"Products\" list (BOFF-8045). Before this shipped, this page listed Roadmaps\n * relabelled as products (CAPABILITY_REGISTER: \"not-built\") — it now queries the Product\n * entity directly, which links roadmaps/releases/feature requests/OKR objectives rather\n * than being one.\n */\n\nconst LIST_PRODUCTS = gql`\n query GetProducts(\n $workspaceId: ID!\n $filter: PlanmagnetProductFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetProducts(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n description\n vision\n lifecycleStage\n ownerId\n archivedAt\n northStar {\n name\n unit\n target\n current\n progressRatio\n }\n roadmaps {\n id\n }\n releases {\n id\n }\n featureRequests {\n id\n }\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface ProductNorthStar {\n name: string;\n unit: string;\n target: number;\n current: number;\n progressRatio: number | null;\n}\n\ninterface ProductListNode {\n id: string;\n name: string;\n description: string | null;\n vision: string;\n lifecycleStage: string;\n ownerId: string;\n archivedAt: string | null;\n northStar: ProductNorthStar | null;\n roadmaps: { id: string }[];\n releases: { id: string }[];\n featureRequests: { id: string }[];\n updatedAt: string;\n}\n\nexport function ProductsProductsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const { t } = useI18n();\n const [showArchived, setShowArchived] = useState(false);\n\n const { data, loading, error } = useQuery<{\n planmagnetProducts: { items: ProductListNode[]; totalCount: number };\n }>(LIST_PRODUCTS, {\n variables: {\n workspaceId,\n filter: showArchived ? undefined : { isActive: true },\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n // Only before the first load: a refetch must not blank the page.\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 (\n <div className=\"p-6 text-destructive\">\n {t('planmagnet.products.errorLoading', 'Failed to load products')}: {error.message}\n </div>\n );\n }\n\n const products = data?.planmagnetProducts?.items ?? [];\n const totalProducts = data?.planmagnetProducts?.totalCount ?? 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold flex items-center gap-2\">\n <Package className=\"h-6 w-6\" /> {t('planmagnet.products.title', 'Products')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\n 'planmagnet.products.subtitle',\n 'Strategy entities that link roadmaps, releases, feature requests and OKRs'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"sm\"\n variant={showArchived ? 'default' : 'outline'}\n onClick={() => setShowArchived((v) => !v)}\n data-testid=\"products-toggle-archived\"\n >\n <Archive className=\"h-4 w-4 mr-1\" />\n {showArchived\n ? t('planmagnet.products.actions.hideArchived', 'Hide archived')\n : t('planmagnet.products.actions.showArchived', 'Show archived')}\n </Button>\n <Button size=\"sm\" onClick={() => navigate('/products/create-product-page')}>\n <Plus className=\"h-4 w-4 mr-1\" />\n {t('planmagnet.products.actions.newProduct', 'New Product')}\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n {[\n {\n label: t('planmagnet.products.metrics.totalProducts', 'Total Products'),\n val: totalProducts,\n icon: Package,\n color: 'text-blue-500',\n },\n {\n label: t('planmagnet.products.metrics.roadmaps', 'Linked roadmaps'),\n val: products.reduce((sum, p) => sum + p.roadmaps.length, 0),\n icon: Map,\n color: 'text-green-500',\n },\n {\n label: t('planmagnet.products.metrics.releases', 'Linked releases'),\n val: products.reduce((sum, p) => sum + p.releases.length, 0),\n icon: GitBranch,\n color: 'text-purple-500',\n },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-muted-foreground\">{s.label}</p>\n <p className=\"text-2xl font-bold\">{s.val}</p>\n </div>\n <s.icon className={`h-8 w-8 ${s.color} opacity-60`} />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {products.length === 0 ? (\n <Card>\n <CardContent className=\"py-12 text-center text-sm text-muted-foreground\">\n {t('planmagnet.products.empty', 'No products yet. Create one to get started.')}\n </CardContent>\n </Card>\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-2\">\n {products.map((product) => (\n <Card\n key={product.id}\n {...rowLinkProps(\n () => navigate(detailPath('/products/product-detail-page', product.id)),\n product.name\n )}\n >\n <CardHeader>\n <div className=\"flex items-center justify-between gap-2\">\n <CardTitle className=\"text-base\">{product.name}</CardTitle>\n <div className=\"flex items-center gap-1 shrink-0\">\n {product.archivedAt && (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('planmagnet.products.shared.archived', 'Archived')}\n </Badge>\n )}\n <Badge variant=\"secondary\" className=\"text-xs capitalize\">\n {enumLabel(product.lifecycleStage)}\n </Badge>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground line-clamp-2\">\n {product.vision || t('planmagnet.products.shared.noVision', 'No vision set')}\n </p>\n <div className=\"grid grid-cols-3 gap-2 text-center\">\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{product.roadmaps.length}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.roadmaps', 'Roadmaps')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{product.releases.length}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.releases', 'Releases')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{product.featureRequests.length}</p>\n <p className=\"text-xs text-muted-foreground\">\n <Star className=\"inline h-3 w-3 mr-0.5\" />\n {t('planmagnet.products.shared.features', 'Features')}\n </p>\n </div>\n </div>\n {product.northStar && (\n <div className=\"rounded border p-2 text-xs\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-muted-foreground\">{product.northStar.name}</span>\n <span className=\"font-medium\">\n {product.northStar.current} / {product.northStar.target}{' '}\n {product.northStar.unit}\n {product.northStar.progressRatio != null\n ? ` (${Math.round(product.northStar.progressRatio * 100)}%)`\n : ''}\n </span>\n </div>\n </div>\n )}\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>\n {t('planmagnet.products.shared.owner', 'Owner')}:{' '}\n <PersonName id={product.ownerId} />\n </span>\n <span>\n {t('planmagnet.products.shared.updated', 'Updated')}{' '}\n {new Date(product.updatedAt).toLocaleDateString()}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAM,IAAgB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DzB,SAAgB,IAA2B;CACzC,IAAM,EAAE,mBAAgB,GAAe,EACjC,IAAW,GAAuB,EAClC,EAAE,SAAM,GAAS,EACjB,CAAC,GAAc,KAAmB,EAAS,GAAM,EAEjD,EAAE,SAAM,YAAS,aAAU,EAE9B,GAAe;EAChB,WAAW;GACT;GACA,QAAQ,IAAe,KAAA,IAAY,EAAE,UAAU,IAAM;GACrD,YAAY,EAAE,OAAO,IAAI;GAC1B;EACD,MAAM,CAAC;EACR,CAAC;AAGF,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;EAC9D,CAAA;AAIV,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACG,EAAE,oCAAoC,0BAA0B;GAAC;GAAG,EAAM;GACvE;;CAIV,IAAM,IAAW,GAAM,oBAAoB,SAAS,EAAE,EAChD,IAAgB,GAAM,oBAAoB,cAAc;AAE9D,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAd;MACE,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;;MAAE,EAAE,6BAA6B,WAAW;MACxE;QACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,gCACA,4EACD;KACC,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,MAAK;MACL,SAAS,IAAe,YAAY;MACpC,eAAe,GAAiB,MAAM,CAAC,EAAE;MACzC,eAAY;gBAJd,CAME,kBAAC,GAAD,EAAS,WAAU,gBAAiB,CAAA,EACnC,IACG,EAAE,4CAA4C,gBAAgB,GAC9D,EAAE,4CAA4C,gBAAgB,CAC3D;SACT,kBAAC,GAAD;MAAQ,MAAK;MAAK,eAAe,EAAS,gCAAgC;gBAA1E,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAChC,EAAE,0CAA0C,cAAc,CACpD;QACL;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ;KACC;MACE,OAAO,EAAE,6CAA6C,iBAAiB;MACvE,KAAK;MACL,MAAM;MACN,OAAO;MACR;KACD;MACE,OAAO,EAAE,wCAAwC,kBAAkB;MACnE,KAAK,EAAS,QAAQ,GAAK,MAAM,IAAM,EAAE,SAAS,QAAQ,EAAE;MAC5D,MAAM;MACN,OAAO;MACR;KACD;MACE,OAAO,EAAE,wCAAwC,kBAAkB;MACnE,KAAK,EAAS,QAAQ,GAAK,MAAM,IAAM,EAAE,SAAS,QAAQ,EAAE;MAC5D,MAAM;MACN,OAAO;MACR;KACF,CAAC,KAAK,MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,WAAU;eACrB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAiC,EAAE;OAAU,CAAA,EAC1D,kBAAC,KAAD;OAAG,WAAU;iBAAsB,EAAE;OAAQ,CAAA,CACzC,EAAA,CAAA,EACN,kBAAC,EAAE,MAAH,EAAQ,WAAW,WAAW,EAAE,MAAM,cAAgB,CAAA,CAClD;;KACM,CAAA,EACT,EAVI,EAAE,MAUN,CACP;IACE,CAAA;GAEL,EAAS,WAAW,IACnB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IAAa,WAAU;cACpB,EAAE,6BAA6B,8CAA8C;IAClE,CAAA,EACT,CAAA,GAEP,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KAAK,MACb,kBAAC,GAAD;KAEE,GAAI,QACI,EAAS,EAAW,iCAAiC,EAAQ,GAAG,CAAC,EACvE,EAAQ,KACT;eALH,CAOE,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAW,WAAU;iBAAa,EAAQ;OAAiB,CAAA,EAC3D,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAQ,cACP,kBAAC,GAAD;QAAO,SAAQ;QAAU,WAAU;kBAChC,EAAE,uCAAuC,WAAW;QAC/C,CAAA,EAEV,kBAAC,GAAD;QAAO,SAAQ;QAAY,WAAU;kBAClC,EAAU,EAAQ,eAAe;QAC5B,CAAA,CACJ;SACF;SACK,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAQ,UAAU,EAAE,uCAAuC,gBAAgB;QAC1E,CAAA;OACJ,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAqB,EAAQ,SAAS;WAAW,CAAA,EAC9D,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAE,uCAAuC,WAAW;WACnD,CAAA,CACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAqB,EAAQ,SAAS;WAAW,CAAA,EAC9D,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAE,uCAAuC,WAAW;WACnD,CAAA,CACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAqB,EAAQ,gBAAgB;WAAW,CAAA,EACrE,kBAAC,KAAD;WAAG,WAAU;qBAAb,CACE,kBAAC,GAAD,EAAM,WAAU,yBAA0B,CAAA,EACzC,EAAE,uCAAuC,WAAW,CACnD;aACA;;SACF;;OACL,EAAQ,aACP,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAyB,EAAQ,UAAU;UAAY,CAAA,EACvE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACG,EAAQ,UAAU;WAAQ;WAAI,EAAQ,UAAU;WAAQ;WACxD,EAAQ,UAAU;WAClB,EAAQ,UAAU,iBAAiB,OAEhC,KADA,KAAK,KAAK,MAAM,EAAQ,UAAU,gBAAgB,IAAI,CAAC;WAEtD;YACH;;QACF,CAAA;OAER,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD,EAAA,UAAA;SACG,EAAE,oCAAoC,QAAQ;SAAC;SAAE;SAClD,kBAAC,GAAD,EAAY,IAAI,EAAQ,SAAW,CAAA;SAC9B,EAAA,CAAA,EACP,kBAAC,QAAD,EAAA,UAAA;SACG,EAAE,sCAAsC,UAAU;SAAE;SACpD,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB;SAC5C,EAAA,CAAA,CACH;;OACM;QACT;OAvEA,EAAQ,GAuER,CACP;IACE,CAAA;GAEJ"}
|
|
1
|
+
{"version":3,"file":"ProductsProductsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Package, Plus, Archive, Loader2, Map, GitBranch, Star } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { enumLabel } from '../controls/WorkItemControls';\n\n/**\n * The real \"Products\" list (BOFF-8045). Before this shipped, this page listed Roadmaps\n * relabelled as products (CAPABILITY_REGISTER: \"not-built\") — it now queries the Product\n * entity directly, which links roadmaps/releases/feature requests/OKR objectives rather\n * than being one.\n */\n\nconst LIST_PRODUCTS = gql`\n query GetProducts(\n $workspaceId: ID!\n $filter: PlanmagnetProductFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetProducts(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n description\n vision\n lifecycleStage\n ownerId\n archivedAt\n northStar {\n name\n unit\n target\n current\n progressRatio\n }\n roadmaps {\n id\n }\n releases {\n id\n }\n featureRequests {\n id\n }\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface ProductNorthStar {\n name: string;\n unit: string;\n target: number;\n current: number;\n progressRatio: number | null;\n}\n\ninterface ProductListNode {\n id: string;\n name: string;\n description: string | null;\n vision: string;\n lifecycleStage: string;\n ownerId: string;\n archivedAt: string | null;\n northStar: ProductNorthStar | null;\n roadmaps: { id: string }[];\n releases: { id: string }[];\n featureRequests: { id: string }[];\n updatedAt: string;\n}\n\nexport function ProductsProductsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const { t } = useI18n();\n const [showArchived, setShowArchived] = useState(false);\n\n const { data, loading, error } = useQuery<{\n planmagnetProducts: { items: ProductListNode[]; totalCount: number };\n }>(LIST_PRODUCTS, {\n variables: {\n workspaceId,\n filter: showArchived ? undefined : { isActive: true },\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n // Only before the first load: a refetch must not blank the page.\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 (\n <div className=\"p-6 text-destructive\">\n {t('planmagnet.products.errorLoading', 'Failed to load products')}: {error.message}\n </div>\n );\n }\n\n const products = data?.planmagnetProducts?.items ?? [];\n const totalProducts = data?.planmagnetProducts?.totalCount ?? 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold flex items-center gap-2\">\n <Package className=\"h-6 w-6\" /> {t('planmagnet.products.title', 'Products')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\n 'planmagnet.products.subtitle',\n 'Strategy entities that link roadmaps, releases, feature requests and OKRs'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"sm\"\n variant={showArchived ? 'default' : 'outline'}\n onClick={() => setShowArchived((v) => !v)}\n data-testid=\"products-toggle-archived\"\n >\n <Archive className=\"h-4 w-4 mr-1\" />\n {showArchived\n ? t('planmagnet.products.actions.hideArchived', 'Hide archived')\n : t('planmagnet.products.actions.showArchived', 'Show archived')}\n </Button>\n <Button size=\"sm\" onClick={() => navigate('/products/create-product-page')}>\n <Plus className=\"h-4 w-4 mr-1\" />\n {t('planmagnet.products.actions.newProduct', 'New Product')}\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n {[\n {\n label: t('planmagnet.products.metrics.totalProducts', 'Total Products'),\n val: totalProducts,\n icon: Package,\n color: 'text-blue-500',\n },\n {\n label: t('planmagnet.products.metrics.roadmaps', 'Linked roadmaps'),\n val: products.reduce((sum, p) => sum + p.roadmaps.length, 0),\n icon: Map,\n color: 'text-green-500',\n },\n {\n label: t('planmagnet.products.metrics.releases', 'Linked releases'),\n val: products.reduce((sum, p) => sum + p.releases.length, 0),\n icon: GitBranch,\n color: 'text-purple-500',\n },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-muted-foreground\">{s.label}</p>\n <p className=\"text-2xl font-bold\">{s.val}</p>\n </div>\n <s.icon className={`h-8 w-8 ${s.color} opacity-60`} />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {products.length === 0 ? (\n <Card>\n <CardContent className=\"py-12 text-center text-sm text-muted-foreground\">\n {t('planmagnet.products.empty', 'No products yet. Create one to get started.')}\n </CardContent>\n </Card>\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-2\">\n {products.map((product) => (\n <Card\n key={product.id}\n {...rowLinkProps(\n () => navigate(detailPath('/products/product-detail-page', product.id)),\n product.name\n )}\n >\n <CardHeader>\n <div className=\"flex items-center justify-between gap-2\">\n <CardTitle className=\"text-base\">{product.name}</CardTitle>\n <div className=\"flex items-center gap-1 shrink-0\">\n {product.archivedAt && (\n <Badge variant=\"outline\" className=\"text-xs\">\n {t('planmagnet.products.shared.archived', 'Archived')}\n </Badge>\n )}\n <Badge variant=\"secondary\" className=\"text-xs capitalize\">\n {enumLabel(product.lifecycleStage)}\n </Badge>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground line-clamp-2\">\n {product.vision || t('planmagnet.products.shared.noVision', 'No vision set')}\n </p>\n <div className=\"grid grid-cols-3 gap-2 text-center\">\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{product.roadmaps.length}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.roadmaps', 'Roadmaps')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{product.releases.length}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.releases', 'Releases')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{product.featureRequests.length}</p>\n <p className=\"text-xs text-muted-foreground\">\n <Star className=\"inline h-3 w-3 mr-0.5\" />\n {/* PRM-01: these are feature REQUESTS (customer feedback), not product features.\n A new key, so a locale that translated the old one as \"Features\" cannot keep\n the old wording. */}\n {t('planmagnet.products.shared.featureRequests', 'Feature requests')}\n </p>\n </div>\n </div>\n {product.northStar && (\n <div className=\"rounded border p-2 text-xs\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-muted-foreground\">{product.northStar.name}</span>\n <span className=\"font-medium\">\n {product.northStar.current} / {product.northStar.target}{' '}\n {product.northStar.unit}\n {product.northStar.progressRatio != null\n ? ` (${Math.round(product.northStar.progressRatio * 100)}%)`\n : ''}\n </span>\n </div>\n </div>\n )}\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>\n {t('planmagnet.products.shared.owner', 'Owner')}:{' '}\n <PersonName id={product.ownerId} />\n </span>\n <span>\n {t('planmagnet.products.shared.updated', 'Updated')}{' '}\n {new Date(product.updatedAt).toLocaleDateString()}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TasksTaskDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksTaskDetailPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TasksTaskDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/TasksTaskDetailPagePage.tsx"],"names":[],"mappings":"AAgXA,wBAAgB,uBAAuB,4CAsLtC"}
|