@burdenoff/microfe-planmagnet 2026.928.9 → 2026.928.10
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/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +217 -209
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +198 -99
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js +260 -170
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.js +283 -0
- package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +166 -246
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +147 -117
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
- package/dist/planmagnet/product/ProductControls.d.ts +52 -0
- package/dist/planmagnet/product/ProductControls.d.ts.map +1 -0
- package/dist/planmagnet/product/ProductControls.js +780 -0
- package/dist/planmagnet/product/ProductControls.js.map +1 -0
- package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsCreateProductPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsCreateProductPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Input,\n Badge,\n} from '@burdenoff/fe-libs/ui';\nimport { Save, X, Package, Eye, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\nimport {\n PlanningFocusBadge,\n PlanningFocusSelect,\n ROADMAP_DISPLAY,\n type PlanningFocus,\n} from '../roadmaps/planningFocus';\n\nconst CREATE_ROADMAP = gql`\n mutation CreateRoadmap($input: PlanmagnetCreateRoadmapInput!) {\n planmagnetCreateRoadmap(input: $input) {\n id\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n }\n }\n`;\n\nexport function ProductsCreateProductPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n\n const [form, setForm] = useState({\n name: '',\n description: '',\n planningFocus: null as PlanningFocus | null,\n isPublic: false,\n });\n\n const [saveError, setSaveError] = useState<string | null>(null);\n const [createRoadmap, { loading: saving }] = useMutation(CREATE_ROADMAP, {\n refetchQueries: ['ListRoadmaps'],\n });\n\n // Navigate only after the service ACCEPTED it (see ProductsCreateRoadmapPage).\n const handleCreate = async () => {\n if (!form.name.trim()) return;\n setSaveError(null);\n try {\n await throwOnError(\n createRoadmap({\n variables: {\n input: {\n name: form.name,\n description: form.description || undefined,\n type: ROADMAP_DISPLAY,\n ...(form.planningFocus ? { planningFocus: form.planningFocus } : {}),\n isPublic: form.isPublic,\n ownerId: currentUser?.id ?? '',\n },\n },\n })\n );\n navigate('/products/products-page');\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The product could not be created.');\n }\n };\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\">Create Product</h1>\n <p className=\"text-sm text-muted-foreground\">\n Define a new product with its vision and strategy.\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/products/products-page')}>\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleCreate} disabled={saving || !form.name.trim()}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Create Product\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to create product: {saveError}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Basic Information</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Product Name</label>\n <Input\n placeholder=\"e.g. PlanMagnet Analytics\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n placeholder=\"Describe what this product does...\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <PlanningFocusSelect\n value={form.planningFocus}\n onChange={(planningFocus) => setForm({ ...form, planningFocus })}\n />\n <div>\n <label className=\"text-sm font-medium\">Visibility</label>\n <div className=\"mt-1 flex gap-2\">\n {[false, true].map((pub) => (\n <button\n key={String(pub)}\n onClick={() => setForm({ ...form, isPublic: pub })}\n className={`rounded-md px-3 py-1.5 text-sm ${form.isPublic === pub ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {pub ? 'Public' : 'Private'}\n </button>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Eye className=\"h-4 w-4\" />\n Preview\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Package className=\"h-5 w-5 text-primary\" />\n <span className=\"font-medium\">{form.name || 'Product Name'}</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {form.description || 'No description yet'}\n </p>\n <Badge variant={form.isPublic ? 'default' : 'secondary'} className=\"capitalize\">\n {form.isPublic ? 'Public' : 'Private'}\n </Badge>\n <div className=\"pt-2 border-t\">\n <p className=\"text-xs font-medium text-muted-foreground\">PLANNING FOCUS</p>\n <div className=\"mt-1\">\n <PlanningFocusBadge focus={form.planningFocus} />\n </div>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Quick Tips</CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm text-muted-foreground space-y-2\">\n <p>A strong product vision guides all feature decisions.</p>\n <p>Keep the strategy focused on 2-3 key differentiators.</p>\n <p>\n Products start in "planning" and move to "active" when launched.\n </p>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductsCreateProductPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsCreateProductPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Input,\n Badge,\n} from '@burdenoff/fe-libs/ui';\nimport { Save, X, Package, Eye, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { throwOnError } from '../utils/mutationResult';\nimport { enumLabel } from '../controls/WorkItemControls';\n\n/**\n * Create a Product (BOFF-8045). A Product is a real strategy entity — name, vision,\n * lifecycle stage, owner and an optional North Star metric — distinct from a Roadmap.\n * It links roadmaps/releases/feature requests/OKRs after creation, from the product\n * detail page's Links panel; nothing is attached at creation time.\n */\n\nconst PRODUCT_LIFECYCLE_STAGES = [\n 'CONCEPT',\n 'DEVELOPMENT',\n 'BETA',\n 'LAUNCHED',\n 'MATURE',\n 'DEPRECATED',\n] as const;\ntype ProductLifecycleStage = (typeof PRODUCT_LIFECYCLE_STAGES)[number];\n\nconst CREATE_PRODUCT = gql`\n mutation CreateProduct($input: PlanmagnetCreateProductInput!) {\n planmagnetCreateProduct(input: $input) {\n id\n name\n description\n vision\n lifecycleStage\n ownerId\n createdAt\n }\n }\n`;\n\ninterface NorthStarForm {\n name: string;\n unit: string;\n target: string;\n current: string;\n window: string;\n}\n\nconst EMPTY_NORTH_STAR: NorthStarForm = { name: '', unit: '', target: '', current: '', window: '' };\n\nexport function ProductsCreateProductPagePage() {\n const { currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n\n const [form, setForm] = useState({\n name: '',\n description: '',\n vision: '',\n lifecycleStage: 'CONCEPT' as ProductLifecycleStage,\n });\n const [northStar, setNorthStar] = useState<NorthStarForm>(EMPTY_NORTH_STAR);\n\n const [saveError, setSaveError] = useState<string | null>(null);\n const [createProduct, { loading: saving }] = useMutation<{\n planmagnetCreateProduct: { id: string };\n }>(CREATE_PRODUCT, {\n refetchQueries: ['GetProducts'],\n });\n\n const northStarStarted = Object.values(northStar).some((v) => v.trim() !== '');\n const northStarComplete =\n northStar.name.trim() &&\n northStar.unit.trim() &&\n northStar.window.trim() &&\n northStar.target.trim() !== '' &&\n !Number.isNaN(Number(northStar.target)) &&\n northStar.current.trim() !== '' &&\n !Number.isNaN(Number(northStar.current));\n\n const canSave =\n form.name.trim() && form.vision.trim() && (!northStarStarted || northStarComplete);\n\n // Navigate only after the service ACCEPTED it, straight to the new product (not the list).\n const handleCreate = async () => {\n if (!canSave) return;\n setSaveError(null);\n try {\n const result = await throwOnError(\n createProduct({\n variables: {\n input: {\n name: form.name,\n description: form.description || undefined,\n vision: form.vision,\n lifecycleStage: form.lifecycleStage,\n ownerId: currentUser?.id ?? '',\n ...(northStarComplete\n ? {\n northStar: {\n name: northStar.name,\n unit: northStar.unit,\n target: Number(northStar.target),\n current: Number(northStar.current),\n window: northStar.window,\n },\n }\n : {}),\n },\n },\n })\n );\n const newId = result.data?.planmagnetCreateProduct?.id;\n navigate(\n newId ? detailPath('/products/product-detail-page', newId) : '/products/products-page'\n );\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The product could not be created.');\n }\n };\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\">Create Product</h1>\n <p className=\"text-sm text-muted-foreground\">\n Define a new product with its vision and strategy.\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/products/products-page')}>\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button size=\"sm\" onClick={() => void handleCreate()} disabled={saving || !canSave}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Create Product\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to create product: {saveError}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Basic Information</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Product Name</label>\n <Input\n placeholder=\"e.g. PlanMagnet Analytics\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[60px]\"\n placeholder=\"Describe what this product does...\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Vision</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n placeholder=\"Where this product is headed and why it matters...\"\n value={form.vision}\n onChange={(e) => setForm({ ...form, vision: e.target.value })}\n data-testid=\"product-vision\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Lifecycle Stage</label>\n <select\n aria-label=\"Lifecycle stage\"\n data-testid=\"product-lifecycle-stage\"\n className=\"mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm\"\n value={form.lifecycleStage}\n onChange={(e) =>\n setForm({ ...form, lifecycleStage: e.target.value as ProductLifecycleStage })\n }\n >\n {PRODUCT_LIFECYCLE_STAGES.map((stage) => (\n <option key={stage} value={stage}>\n {enumLabel(stage)}\n </option>\n ))}\n </select>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>North Star metric (optional)</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-xs text-muted-foreground\">\n Leave every field blank to skip — it can be added later from the product's edit\n page.\n </p>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Metric name</label>\n <Input\n placeholder=\"e.g. Weekly active users\"\n value={northStar.name}\n onChange={(e) => setNorthStar({ ...northStar, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Unit</label>\n <Input\n placeholder=\"e.g. users\"\n value={northStar.unit}\n onChange={(e) => setNorthStar({ ...northStar, unit: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Target</label>\n <Input\n type=\"number\"\n value={northStar.target}\n onChange={(e) => setNorthStar({ ...northStar, target: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Current</label>\n <Input\n type=\"number\"\n value={northStar.current}\n onChange={(e) => setNorthStar({ ...northStar, current: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div className=\"col-span-2\">\n <label className=\"text-sm font-medium\">Window</label>\n <Input\n placeholder=\"e.g. Q1 2027, weekly, ongoing\"\n value={northStar.window}\n onChange={(e) => setNorthStar({ ...northStar, window: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n {northStarStarted && !northStarComplete && (\n <p className=\"text-xs text-destructive\">\n Fill in every North Star field (or clear them all) — name, unit, target, current\n and window are all required together.\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Eye className=\"h-4 w-4\" />\n Preview\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Package className=\"h-5 w-5 text-primary\" />\n <span className=\"font-medium\">{form.name || 'Product Name'}</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">{form.vision || 'No vision set yet'}</p>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {enumLabel(form.lifecycleStage)}\n </Badge>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Quick Tips</CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm text-muted-foreground space-y-2\">\n <p>A strong product vision guides all feature decisions.</p>\n <p>\n Link roadmaps, releases, feature requests and OKRs from the product's own page\n once it is created.\n </p>\n <p>\n Products start in "Concept" and move through their lifecycle over time.\n </p>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsEditProductPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProductsEditProductPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsEditProductPagePage.tsx"],"names":[],"mappings":"AAmHA,wBAAgB,2BAA2B,4CAqT1C"}
|
|
@@ -1,278 +1,368 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { PersonName as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
1
|
+
import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { PersonName as t } from "../people/People.js";
|
|
3
|
+
import { throwOnError as n } from "../utils/mutationResult.js";
|
|
4
|
+
import { useEntityId as r } from "../hooks/useEntityId.js";
|
|
5
|
+
import { enumLabel as i } from "../controls/WorkItemControls.js";
|
|
6
|
+
import { detailPath as a } from "../utils/rowNavigation.js";
|
|
7
|
+
import { ProductHeaderActions as o } from "../product/ProductControls.js";
|
|
8
|
+
import { useMemo as s, useState as c } from "react";
|
|
9
|
+
import { ArrowLeft as l, Loader2 as u, Package as d, Save as f } from "lucide-react";
|
|
10
|
+
import { gql as p } from "@apollo/client";
|
|
11
|
+
import { useMutation as m, useQuery as h } from "@apollo/client/react";
|
|
12
|
+
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
13
|
+
import { Button as v, Card as y, CardContent as b, CardHeader as x, CardTitle as S, Input as C } from "@burdenoff/fe-libs/ui";
|
|
13
14
|
//#region src/planmagnet/pages/ProductsEditProductPagePage.tsx
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
var w = [
|
|
16
|
+
"CONCEPT",
|
|
17
|
+
"DEVELOPMENT",
|
|
18
|
+
"BETA",
|
|
19
|
+
"LAUNCHED",
|
|
20
|
+
"MATURE",
|
|
21
|
+
"DEPRECATED"
|
|
22
|
+
], T = p`
|
|
23
|
+
query GetProductForEdit($id: ID!) {
|
|
24
|
+
planmagnetProduct(id: $id) {
|
|
17
25
|
id
|
|
18
26
|
workspaceId
|
|
19
27
|
name
|
|
20
28
|
description
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
isPublic
|
|
29
|
+
vision
|
|
30
|
+
lifecycleStage
|
|
24
31
|
ownerId
|
|
32
|
+
archivedAt
|
|
33
|
+
northStar {
|
|
34
|
+
name
|
|
35
|
+
unit
|
|
36
|
+
target
|
|
37
|
+
current
|
|
38
|
+
window
|
|
39
|
+
}
|
|
25
40
|
createdAt
|
|
26
41
|
updatedAt
|
|
27
|
-
items {
|
|
28
|
-
id
|
|
29
|
-
status
|
|
30
|
-
}
|
|
31
42
|
}
|
|
32
43
|
}
|
|
33
|
-
`,
|
|
34
|
-
mutation
|
|
35
|
-
|
|
44
|
+
`, E = p`
|
|
45
|
+
mutation UpdateProduct($id: ID!, $input: PlanmagnetUpdateProductInput!) {
|
|
46
|
+
planmagnetUpdateProduct(id: $id, input: $input) {
|
|
36
47
|
id
|
|
37
48
|
name
|
|
38
49
|
description
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
isPublic
|
|
50
|
+
vision
|
|
51
|
+
lifecycleStage
|
|
42
52
|
ownerId
|
|
53
|
+
northStar {
|
|
54
|
+
name
|
|
55
|
+
unit
|
|
56
|
+
target
|
|
57
|
+
current
|
|
58
|
+
window
|
|
59
|
+
}
|
|
43
60
|
updatedAt
|
|
44
61
|
}
|
|
45
62
|
}
|
|
46
|
-
`,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
`, D = {
|
|
64
|
+
name: "",
|
|
65
|
+
unit: "",
|
|
66
|
+
target: "",
|
|
67
|
+
current: "",
|
|
68
|
+
window: ""
|
|
69
|
+
};
|
|
70
|
+
function O() {
|
|
71
|
+
let p = e(), O = r("productId") ?? "", { data: k, loading: A, error: j } = h(T, {
|
|
72
|
+
variables: { id: O },
|
|
73
|
+
skip: !O
|
|
74
|
+
}), [M, { loading: N }] = m(E, {
|
|
56
75
|
refetchQueries: [
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"GetRoadmapDetail",
|
|
61
|
-
"GetRoadmapDetailPage"
|
|
76
|
+
"GetProducts",
|
|
77
|
+
"GetProductForEdit",
|
|
78
|
+
"GetProductDetail"
|
|
62
79
|
],
|
|
63
80
|
awaitRefetchQueries: !0
|
|
64
|
-
}), [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
}), [P, F] = c(null), [I, L] = c(!1), R = k?.planmagnetProduct, z = s(() => ({
|
|
82
|
+
name: R?.name ?? "",
|
|
83
|
+
description: R?.description ?? "",
|
|
84
|
+
vision: R?.vision ?? "",
|
|
85
|
+
lifecycleStage: R?.lifecycleStage ?? "CONCEPT"
|
|
86
|
+
}), [R]), B = s(() => R?.northStar ? {
|
|
87
|
+
name: R.northStar.name,
|
|
88
|
+
unit: R.northStar.unit,
|
|
89
|
+
target: String(R.northStar.target),
|
|
90
|
+
current: String(R.northStar.current),
|
|
91
|
+
window: R.northStar.window
|
|
92
|
+
} : D, [R]), [V, H] = c({}), U = {
|
|
93
|
+
...z,
|
|
94
|
+
...V
|
|
95
|
+
}, W = (e) => H(e), [G, K] = c({}), q = {
|
|
96
|
+
...B,
|
|
74
97
|
...G
|
|
75
|
-
}, J = (e) => K(e), Y = async () => {
|
|
76
|
-
|
|
98
|
+
}, J = (e) => K(e), Y = Object.values(q).some((e) => e.trim() !== ""), X = q.name.trim() && q.unit.trim() && q.window.trim() && q.target.trim() !== "" && !Number.isNaN(Number(q.target)) && q.current.trim() !== "" && !Number.isNaN(Number(q.current)), Z = async () => {
|
|
99
|
+
F(null), L(!1);
|
|
77
100
|
try {
|
|
78
|
-
await
|
|
79
|
-
id:
|
|
101
|
+
await n(M({ variables: {
|
|
102
|
+
id: O,
|
|
80
103
|
input: {
|
|
81
|
-
name:
|
|
82
|
-
description:
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
name: U.name,
|
|
105
|
+
description: U.description.trim() || null,
|
|
106
|
+
vision: U.vision,
|
|
107
|
+
lifecycleStage: U.lifecycleStage,
|
|
108
|
+
...X ? { northStar: {
|
|
109
|
+
name: q.name,
|
|
110
|
+
unit: q.unit,
|
|
111
|
+
target: Number(q.target),
|
|
112
|
+
current: Number(q.current),
|
|
113
|
+
window: q.window
|
|
114
|
+
} } : {}
|
|
85
115
|
}
|
|
86
|
-
} })),
|
|
116
|
+
} })), L(!0);
|
|
87
117
|
} catch (e) {
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
}, X = async () => {
|
|
91
|
-
B(null);
|
|
92
|
-
try {
|
|
93
|
-
await i(L({ variables: { id: j } })), A("/products/products-page");
|
|
94
|
-
} catch (e) {
|
|
95
|
-
B(e instanceof Error ? e.message : "The product could not be deleted.");
|
|
118
|
+
F(e instanceof Error ? e.message : "The product could not be saved.");
|
|
96
119
|
}
|
|
97
120
|
};
|
|
98
|
-
return
|
|
121
|
+
return A && !k ? /* @__PURE__ */ g("div", {
|
|
99
122
|
className: "flex items-center justify-center p-12",
|
|
100
|
-
children: /* @__PURE__ */
|
|
101
|
-
}) :
|
|
123
|
+
children: /* @__PURE__ */ g(u, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
124
|
+
}) : j ? /* @__PURE__ */ _("div", {
|
|
102
125
|
className: "p-6 text-destructive",
|
|
103
|
-
children: ["Failed to load product: ",
|
|
104
|
-
}) :
|
|
126
|
+
children: ["Failed to load product: ", j.message]
|
|
127
|
+
}) : R ? /* @__PURE__ */ _("div", {
|
|
105
128
|
className: "space-y-6 p-6",
|
|
106
129
|
children: [
|
|
107
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ _("div", {
|
|
108
131
|
className: "flex items-center justify-between",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
132
|
+
children: [/* @__PURE__ */ _("div", {
|
|
110
133
|
className: "flex items-center gap-3",
|
|
111
|
-
children: [/* @__PURE__ */
|
|
134
|
+
children: [/* @__PURE__ */ g(v, {
|
|
112
135
|
variant: "ghost",
|
|
113
136
|
size: "sm",
|
|
114
|
-
onClick: () =>
|
|
115
|
-
children: /* @__PURE__ */
|
|
116
|
-
}), /* @__PURE__ */
|
|
137
|
+
onClick: () => p(a("/products/product-detail-page", R.id)),
|
|
138
|
+
children: /* @__PURE__ */ g(l, { className: "h-4 w-4" })
|
|
139
|
+
}), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
|
|
117
140
|
className: "text-2xl font-semibold",
|
|
118
141
|
children: "Edit Product"
|
|
119
|
-
}), /* @__PURE__ */
|
|
142
|
+
}), /* @__PURE__ */ _("p", {
|
|
120
143
|
className: "text-sm text-muted-foreground",
|
|
121
|
-
children: ["Editing ",
|
|
144
|
+
children: ["Editing ", R.name]
|
|
122
145
|
})] })]
|
|
123
|
-
}), /* @__PURE__ */
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
146
|
+
}), /* @__PURE__ */ _("div", {
|
|
147
|
+
className: "flex items-center gap-2",
|
|
148
|
+
children: [/* @__PURE__ */ _(v, {
|
|
149
|
+
size: "sm",
|
|
150
|
+
onClick: () => void Z(),
|
|
151
|
+
disabled: N || Y && !X,
|
|
152
|
+
children: [N ? /* @__PURE__ */ g(u, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(f, { className: "mr-1 h-4 w-4" }), "Save Changes"]
|
|
153
|
+
}), /* @__PURE__ */ g(o, {
|
|
154
|
+
productId: R.id,
|
|
155
|
+
name: R.name,
|
|
156
|
+
archivedAt: R.archivedAt
|
|
157
|
+
})]
|
|
128
158
|
})]
|
|
129
159
|
}),
|
|
130
|
-
|
|
160
|
+
I && /* @__PURE__ */ g("div", {
|
|
131
161
|
role: "status",
|
|
132
162
|
className: "p-3 bg-muted text-status-success-text rounded-md text-sm",
|
|
133
163
|
children: "Changes saved."
|
|
134
164
|
}),
|
|
135
|
-
|
|
165
|
+
P && /* @__PURE__ */ g("div", {
|
|
136
166
|
role: "alert",
|
|
137
167
|
className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
|
|
138
|
-
children:
|
|
168
|
+
children: P
|
|
139
169
|
}),
|
|
140
|
-
/* @__PURE__ */
|
|
170
|
+
/* @__PURE__ */ _("div", {
|
|
141
171
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
142
|
-
children: [/* @__PURE__ */
|
|
172
|
+
children: [/* @__PURE__ */ _("div", {
|
|
143
173
|
className: "lg:col-span-2 space-y-6",
|
|
144
|
-
children: /* @__PURE__ */
|
|
174
|
+
children: [/* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Basic Information" }) }), /* @__PURE__ */ _(b, {
|
|
145
175
|
className: "space-y-4",
|
|
146
176
|
children: [
|
|
147
|
-
/* @__PURE__ */
|
|
177
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
148
178
|
className: "text-sm font-medium",
|
|
149
179
|
children: "Product Name"
|
|
150
|
-
}), /* @__PURE__ */
|
|
151
|
-
value:
|
|
152
|
-
onChange: (e) =>
|
|
153
|
-
...
|
|
180
|
+
}), /* @__PURE__ */ g(C, {
|
|
181
|
+
value: U.name,
|
|
182
|
+
onChange: (e) => W({
|
|
183
|
+
...U,
|
|
154
184
|
name: e.target.value
|
|
155
185
|
}),
|
|
156
186
|
className: "mt-1"
|
|
157
187
|
})] }),
|
|
158
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
159
189
|
className: "text-sm font-medium",
|
|
160
190
|
children: "Description"
|
|
161
|
-
}), /* @__PURE__ */
|
|
162
|
-
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[
|
|
163
|
-
value:
|
|
164
|
-
onChange: (e) =>
|
|
165
|
-
...
|
|
191
|
+
}), /* @__PURE__ */ g("textarea", {
|
|
192
|
+
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[60px]",
|
|
193
|
+
value: U.description,
|
|
194
|
+
onChange: (e) => W({
|
|
195
|
+
...U,
|
|
166
196
|
description: e.target.value
|
|
167
197
|
})
|
|
168
198
|
})] }),
|
|
169
|
-
/* @__PURE__ */
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
199
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
200
|
+
className: "text-sm font-medium",
|
|
201
|
+
children: "Vision"
|
|
202
|
+
}), /* @__PURE__ */ g("textarea", {
|
|
203
|
+
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]",
|
|
204
|
+
value: U.vision,
|
|
205
|
+
onChange: (e) => W({
|
|
206
|
+
...U,
|
|
207
|
+
vision: e.target.value
|
|
174
208
|
})
|
|
175
|
-
}),
|
|
176
|
-
/* @__PURE__ */
|
|
209
|
+
})] }),
|
|
210
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
177
211
|
className: "text-sm font-medium",
|
|
178
|
-
children: "
|
|
179
|
-
}), /* @__PURE__ */
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
212
|
+
children: "Lifecycle Stage"
|
|
213
|
+
}), /* @__PURE__ */ g("select", {
|
|
214
|
+
"aria-label": "Lifecycle stage",
|
|
215
|
+
"data-testid": "product-lifecycle-stage",
|
|
216
|
+
className: "mt-1 w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm",
|
|
217
|
+
value: U.lifecycleStage,
|
|
218
|
+
onChange: (e) => W({
|
|
219
|
+
...U,
|
|
220
|
+
lifecycleStage: e.target.value
|
|
221
|
+
}),
|
|
222
|
+
children: w.map((e) => /* @__PURE__ */ g("option", {
|
|
223
|
+
value: e,
|
|
224
|
+
children: i(e)
|
|
225
|
+
}, e))
|
|
189
226
|
})] })
|
|
190
227
|
]
|
|
191
|
-
})] })
|
|
192
|
-
|
|
228
|
+
})] }), /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "North Star metric" }) }), /* @__PURE__ */ _(b, {
|
|
229
|
+
className: "space-y-4",
|
|
230
|
+
children: [/* @__PURE__ */ _("div", {
|
|
231
|
+
className: "grid grid-cols-2 gap-4",
|
|
232
|
+
children: [
|
|
233
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
234
|
+
className: "text-sm font-medium",
|
|
235
|
+
children: "Metric name"
|
|
236
|
+
}), /* @__PURE__ */ g(C, {
|
|
237
|
+
value: q.name,
|
|
238
|
+
onChange: (e) => J({
|
|
239
|
+
...q,
|
|
240
|
+
name: e.target.value
|
|
241
|
+
}),
|
|
242
|
+
className: "mt-1"
|
|
243
|
+
})] }),
|
|
244
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
245
|
+
className: "text-sm font-medium",
|
|
246
|
+
children: "Unit"
|
|
247
|
+
}), /* @__PURE__ */ g(C, {
|
|
248
|
+
value: q.unit,
|
|
249
|
+
onChange: (e) => J({
|
|
250
|
+
...q,
|
|
251
|
+
unit: e.target.value
|
|
252
|
+
}),
|
|
253
|
+
className: "mt-1"
|
|
254
|
+
})] }),
|
|
255
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
256
|
+
className: "text-sm font-medium",
|
|
257
|
+
children: "Target"
|
|
258
|
+
}), /* @__PURE__ */ g(C, {
|
|
259
|
+
type: "number",
|
|
260
|
+
value: q.target,
|
|
261
|
+
onChange: (e) => J({
|
|
262
|
+
...q,
|
|
263
|
+
target: e.target.value
|
|
264
|
+
}),
|
|
265
|
+
className: "mt-1"
|
|
266
|
+
})] }),
|
|
267
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
268
|
+
className: "text-sm font-medium",
|
|
269
|
+
children: "Current"
|
|
270
|
+
}), /* @__PURE__ */ g(C, {
|
|
271
|
+
type: "number",
|
|
272
|
+
value: q.current,
|
|
273
|
+
onChange: (e) => J({
|
|
274
|
+
...q,
|
|
275
|
+
current: e.target.value
|
|
276
|
+
}),
|
|
277
|
+
className: "mt-1"
|
|
278
|
+
})] }),
|
|
279
|
+
/* @__PURE__ */ _("div", {
|
|
280
|
+
className: "col-span-2",
|
|
281
|
+
children: [/* @__PURE__ */ g("label", {
|
|
282
|
+
className: "text-sm font-medium",
|
|
283
|
+
children: "Window"
|
|
284
|
+
}), /* @__PURE__ */ g(C, {
|
|
285
|
+
value: q.window,
|
|
286
|
+
onChange: (e) => J({
|
|
287
|
+
...q,
|
|
288
|
+
window: e.target.value
|
|
289
|
+
}),
|
|
290
|
+
className: "mt-1"
|
|
291
|
+
})]
|
|
292
|
+
})
|
|
293
|
+
]
|
|
294
|
+
}), Y && !X && /* @__PURE__ */ g("p", {
|
|
295
|
+
className: "text-xs text-destructive",
|
|
296
|
+
children: "Fill in every North Star field (or clear them all) — name, unit, target, current and window are all required together."
|
|
297
|
+
})]
|
|
298
|
+
})] })]
|
|
299
|
+
}), /* @__PURE__ */ g("div", {
|
|
193
300
|
className: "space-y-6",
|
|
194
|
-
children:
|
|
301
|
+
children: /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Product Info" }) }), /* @__PURE__ */ _(b, {
|
|
195
302
|
className: "space-y-3 text-sm",
|
|
196
303
|
children: [
|
|
197
|
-
/* @__PURE__ */
|
|
304
|
+
/* @__PURE__ */ _("div", {
|
|
198
305
|
className: "flex items-center gap-2",
|
|
199
|
-
children: [/* @__PURE__ */
|
|
306
|
+
children: [/* @__PURE__ */ g(d, { className: "h-4 w-4 text-primary" }), /* @__PURE__ */ g("span", {
|
|
200
307
|
className: "font-medium",
|
|
201
|
-
children:
|
|
308
|
+
children: R.name
|
|
202
309
|
})]
|
|
203
310
|
}),
|
|
204
|
-
/* @__PURE__ */
|
|
311
|
+
/* @__PURE__ */ _("div", {
|
|
205
312
|
className: "flex justify-between",
|
|
206
|
-
children: [/* @__PURE__ */
|
|
313
|
+
children: [/* @__PURE__ */ g("span", {
|
|
207
314
|
className: "text-muted-foreground",
|
|
208
|
-
children: "
|
|
209
|
-
}), /* @__PURE__ */
|
|
210
|
-
className: "font-medium",
|
|
211
|
-
children:
|
|
315
|
+
children: "Lifecycle stage"
|
|
316
|
+
}), /* @__PURE__ */ g("span", {
|
|
317
|
+
className: "font-medium capitalize",
|
|
318
|
+
children: i(R.lifecycleStage)
|
|
212
319
|
})]
|
|
213
320
|
}),
|
|
214
|
-
/* @__PURE__ */
|
|
321
|
+
/* @__PURE__ */ _("div", {
|
|
215
322
|
className: "flex justify-between",
|
|
216
|
-
children: [/* @__PURE__ */
|
|
323
|
+
children: [/* @__PURE__ */ g("span", {
|
|
217
324
|
className: "text-muted-foreground",
|
|
218
|
-
children: "
|
|
219
|
-
}), /* @__PURE__ */
|
|
220
|
-
}),
|
|
221
|
-
/* @__PURE__ */ b("div", {
|
|
222
|
-
className: "flex justify-between",
|
|
223
|
-
children: [/* @__PURE__ */ y("span", {
|
|
224
|
-
className: "text-muted-foreground",
|
|
225
|
-
children: "Visibility"
|
|
226
|
-
}), /* @__PURE__ */ y("span", {
|
|
325
|
+
children: "Status"
|
|
326
|
+
}), /* @__PURE__ */ g("span", {
|
|
227
327
|
className: "font-medium",
|
|
228
|
-
children:
|
|
328
|
+
children: R.archivedAt ? "Archived" : "Active"
|
|
229
329
|
})]
|
|
230
330
|
}),
|
|
231
|
-
/* @__PURE__ */
|
|
331
|
+
/* @__PURE__ */ _("div", {
|
|
232
332
|
className: "flex justify-between",
|
|
233
|
-
children: [/* @__PURE__ */
|
|
333
|
+
children: [/* @__PURE__ */ g("span", {
|
|
234
334
|
className: "text-muted-foreground",
|
|
235
335
|
children: "Owner"
|
|
236
|
-
}), /* @__PURE__ */
|
|
336
|
+
}), /* @__PURE__ */ g("span", {
|
|
237
337
|
className: "font-medium",
|
|
238
|
-
children: /* @__PURE__ */
|
|
338
|
+
children: /* @__PURE__ */ g(t, { id: R.ownerId })
|
|
239
339
|
})]
|
|
240
340
|
}),
|
|
241
|
-
/* @__PURE__ */
|
|
341
|
+
/* @__PURE__ */ _("div", {
|
|
242
342
|
className: "flex justify-between",
|
|
243
|
-
children: [/* @__PURE__ */
|
|
343
|
+
children: [/* @__PURE__ */ g("span", {
|
|
244
344
|
className: "text-muted-foreground",
|
|
245
345
|
children: "Created"
|
|
246
|
-
}), /* @__PURE__ */
|
|
346
|
+
}), /* @__PURE__ */ g("span", { children: new Date(R.createdAt).toLocaleDateString() })]
|
|
247
347
|
}),
|
|
248
|
-
/* @__PURE__ */
|
|
348
|
+
/* @__PURE__ */ _("div", {
|
|
249
349
|
className: "flex justify-between",
|
|
250
|
-
children: [/* @__PURE__ */
|
|
350
|
+
children: [/* @__PURE__ */ g("span", {
|
|
251
351
|
className: "text-muted-foreground",
|
|
252
352
|
children: "Updated"
|
|
253
|
-
}), /* @__PURE__ */
|
|
353
|
+
}), /* @__PURE__ */ g("span", { children: new Date(R.updatedAt).toLocaleDateString() })]
|
|
254
354
|
})
|
|
255
355
|
]
|
|
256
|
-
})] })
|
|
257
|
-
label: R ? "Deleting…" : "Delete product",
|
|
258
|
-
variant: "outline",
|
|
259
|
-
icon: /* @__PURE__ */ y(m, { className: "mr-1 h-4 w-4" }),
|
|
260
|
-
title: `Delete ${U.name}?`,
|
|
261
|
-
description: "The product's roadmap and its items are deleted. This cannot be undone here.",
|
|
262
|
-
confirmLabel: "Delete product",
|
|
263
|
-
destructive: !0,
|
|
264
|
-
disabled: R,
|
|
265
|
-
onConfirm: () => void X()
|
|
266
|
-
}) })] })]
|
|
356
|
+
})] })
|
|
267
357
|
})]
|
|
268
358
|
})
|
|
269
359
|
]
|
|
270
|
-
}) : /* @__PURE__ */
|
|
360
|
+
}) : /* @__PURE__ */ g("div", {
|
|
271
361
|
className: "p-6 text-muted-foreground",
|
|
272
362
|
children: "No product found."
|
|
273
363
|
});
|
|
274
364
|
}
|
|
275
365
|
//#endregion
|
|
276
|
-
export {
|
|
366
|
+
export { O as ProductsEditProductPagePage };
|
|
277
367
|
|
|
278
368
|
//# sourceMappingURL=ProductsEditProductPagePage.js.map
|