@burdenoff/microfe-planmagnet 2026.1001.8 → 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/global-types.d.ts +5 -0
- package/dist/generated/global-types.d.ts.map +1 -1
- 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/comments/CommentAttachmentUpload.d.ts +16 -0
- package/dist/planmagnet/comments/CommentAttachmentUpload.d.ts.map +1 -0
- package/dist/planmagnet/comments/CommentAttachmentUpload.js +218 -0
- package/dist/planmagnet/comments/CommentAttachmentUpload.js.map +1 -0
- package/dist/planmagnet/comments/CommentAttachments.d.ts +15 -0
- package/dist/planmagnet/comments/CommentAttachments.d.ts.map +1 -0
- package/dist/planmagnet/comments/CommentAttachments.js +74 -0
- package/dist/planmagnet/comments/CommentAttachments.js.map +1 -0
- package/dist/planmagnet/comments/CommentBody.d.ts +11 -0
- package/dist/planmagnet/comments/CommentBody.d.ts.map +1 -0
- package/dist/planmagnet/comments/CommentBody.js +130 -0
- package/dist/planmagnet/comments/CommentBody.js.map +1 -0
- package/dist/planmagnet/comments/CommentThread.d.ts +7 -2
- package/dist/planmagnet/comments/CommentThread.d.ts.map +1 -1
- package/dist/planmagnet/comments/CommentThread.js +288 -233
- package/dist/planmagnet/comments/CommentThread.js.map +1 -1
- package/dist/planmagnet/comments/MentionComposer.d.ts +0 -7
- package/dist/planmagnet/comments/MentionComposer.d.ts.map +1 -1
- package/dist/planmagnet/comments/MentionComposer.js +225 -86
- package/dist/planmagnet/comments/MentionComposer.js.map +1 -1
- package/dist/planmagnet/comments/TargetComments.d.ts.map +1 -1
- package/dist/planmagnet/comments/TargetComments.js +44 -6
- package/dist/planmagnet/comments/TargetComments.js.map +1 -1
- package/dist/planmagnet/comments/WorkItemComments.d.ts.map +1 -1
- package/dist/planmagnet/comments/WorkItemComments.js +22 -3
- package/dist/planmagnet/comments/WorkItemComments.js.map +1 -1
- package/dist/planmagnet/comments/commentAttachmentUploadClient.d.ts +102 -0
- package/dist/planmagnet/comments/commentAttachmentUploadClient.d.ts.map +1 -0
- package/dist/planmagnet/comments/commentAttachmentUploadClient.js +298 -0
- package/dist/planmagnet/comments/commentAttachmentUploadClient.js.map +1 -0
- package/dist/planmagnet/comments/commentImageTokens.d.ts +5 -0
- package/dist/planmagnet/comments/commentImageTokens.d.ts.map +1 -0
- package/dist/planmagnet/comments/commentImageTokens.js +14 -0
- package/dist/planmagnet/comments/commentImageTokens.js.map +1 -0
- package/dist/planmagnet/comments/commentTokens.d.ts +31 -0
- package/dist/planmagnet/comments/commentTokens.d.ts.map +1 -0
- package/dist/planmagnet/comments/commentTokens.js +142 -0
- package/dist/planmagnet/comments/commentTokens.js.map +1 -0
- package/dist/planmagnet/comments/fileDisplay.d.ts +22 -0
- package/dist/planmagnet/comments/fileDisplay.d.ts.map +1 -0
- package/dist/planmagnet/comments/fileDisplay.js +48 -0
- package/dist/planmagnet/comments/fileDisplay.js.map +1 -0
- package/dist/planmagnet/comments/useCommentFileUrl.d.ts +22 -0
- package/dist/planmagnet/comments/useCommentFileUrl.d.ts.map +1 -0
- package/dist/planmagnet/comments/useCommentFileUrl.js +54 -0
- package/dist/planmagnet/comments/useCommentFileUrl.js.map +1 -0
- 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 +5 -4
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product line controls (PRM-01). A line groups products — one line, many products, a product in at most one line.
|
|
3
|
+
* The line never owns its products: archiving keeps them in it (an archived line just accepts no new ones), and
|
|
4
|
+
* deleting it only unassigns them. Nothing here ever deletes or changes a product beyond which line it is in.
|
|
5
|
+
*/
|
|
6
|
+
export interface ProductLineValues {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description: string | null;
|
|
10
|
+
ownerId: string | null;
|
|
11
|
+
archivedAt: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface ProductLineProductRef {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
archivedAt: string | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create a line (no `line`) or edit one. An edit sends only the fields that changed, and an emptied description or
|
|
20
|
+
* a cleared owner is sent as null, which clears it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function ProductLineFormDialog({ line }: {
|
|
23
|
+
line?: ProductLineValues;
|
|
24
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
/**
|
|
26
|
+
* Edit, archive/unarchive and delete for one line. `onArchived` / `onDeleted` run after the service accepted the
|
|
27
|
+
* write — the list may have dropped this card by then (archived lines are hidden by default), so the page, not
|
|
28
|
+
* the card, says what happened.
|
|
29
|
+
*/
|
|
30
|
+
export declare function ProductLineActions({ line, productCount, onArchived, onDeleted, }: {
|
|
31
|
+
line: ProductLineValues;
|
|
32
|
+
productCount: number;
|
|
33
|
+
onArchived?: (name: string) => void;
|
|
34
|
+
onDeleted?: (name: string) => void;
|
|
35
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
/**
|
|
37
|
+
* The products in one line, each linking to its product page with a "take out of this line" control, and an "Add
|
|
38
|
+
* products" dialog. Taking a product out only unassigns it; the product itself is untouched.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ProductLineProducts({ line, products, }: {
|
|
41
|
+
line: ProductLineValues;
|
|
42
|
+
products: ProductLineProductRef[];
|
|
43
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
/**
|
|
45
|
+
* The product page's product-line select: assigns on change, "No product line" unassigns. Only active lines are
|
|
46
|
+
* offered (an archived line accepts no new products); the product's current line stays listed — marked archived
|
|
47
|
+
* when it is — so the select never claims the product is somewhere it is not.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ProductLinePicker({ productId }: {
|
|
50
|
+
productId: string;
|
|
51
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
//# sourceMappingURL=ProductLineControls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductLineControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/product/ProductLineControls.tsx"],"names":[],"mappings":"AAqCA;;;;GAIG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAmBD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,iBAAiB,CAAA;CAAE,2CAgK3E;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,SAAS,GACV,EAAE;IACD,IAAI,EAAE,iBAAiB,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,2CA8FA;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC,2CA0FA;AAiMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,2CA6FrE"}
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { useTr as n } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { throwOnError as r } from "../utils/mutationResult.js";
|
|
4
|
+
import { ConfirmButton as i, ErrorLine as a } from "../controls/ConfirmButton.js";
|
|
5
|
+
import { AssigneeSelect as o } from "../controls/WorkItemControls.js";
|
|
6
|
+
import { detailPath as s } from "../utils/rowNavigation.js";
|
|
7
|
+
import { ARCHIVE_PRODUCT_LINE as c, ASSIGN_PRODUCT_LINE as l, CREATE_PRODUCT_LINE as u, DELETE_PRODUCT_LINE as d, PRODUCT_LINE_ASSIGNMENT as f, PRODUCT_LINE_CANDIDATE_PRODUCTS as p, PRODUCT_LINE_OPTIONS as m, PRODUCT_LINE_QUERIES as h, UNARCHIVE_PRODUCT_LINE as g, UPDATE_PRODUCT_LINE as _ } from "./hierarchyOperations.js";
|
|
8
|
+
import { changedFields as v, normalizeText as y } from "./hierarchy.js";
|
|
9
|
+
import { refusalMessage as b } from "./refusal.js";
|
|
10
|
+
import { useState as x } from "react";
|
|
11
|
+
import { Archive as S, ArchiveRestore as C, Pencil as w, Plus as T, Trash2 as E, X as D } from "lucide-react";
|
|
12
|
+
import { useMutation as O, useQuery as k } from "@apollo/client/react";
|
|
13
|
+
import { jsx as A, jsxs as j } from "react/jsx-runtime";
|
|
14
|
+
import { Badge as M, Button as N, Checkbox as P, Input as F, ResponsiveDialog as I, ResponsiveDialogContent as L, ResponsiveDialogDescription as R, ResponsiveDialogFooter as z, ResponsiveDialogHeader as B, ResponsiveDialogTitle as V, ResponsiveDialogTrigger as H } from "@burdenoff/fe-libs/ui";
|
|
15
|
+
//#region src/planmagnet/product/ProductLineControls.tsx
|
|
16
|
+
var U = "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]", W = "w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60", G = {
|
|
17
|
+
refetchQueries: h,
|
|
18
|
+
awaitRefetchQueries: !0
|
|
19
|
+
};
|
|
20
|
+
function K() {
|
|
21
|
+
return n()("planmagnet.productLines.errors.forbidden", "Your role cannot change product lines in this workspace.");
|
|
22
|
+
}
|
|
23
|
+
function q({ line: t }) {
|
|
24
|
+
let i = n(), s = K(), { currentUser: c } = e(), l = {
|
|
25
|
+
name: t?.name ?? "",
|
|
26
|
+
description: t?.description ?? "",
|
|
27
|
+
ownerId: t ? t.ownerId ?? "" : c?.id ?? ""
|
|
28
|
+
}, [d, f] = x(!1), [p, m] = x(l), [h, g] = x(null), [S, C] = O(u, G), [E, D] = O(_, G), k = C.loading || D.loading, M = p.name.trim() !== "" && !k, P = t ? `product-line-edit-${t.id}` : "product-line-new", W = async () => {
|
|
29
|
+
g(null);
|
|
30
|
+
let e = {
|
|
31
|
+
name: p.name.trim(),
|
|
32
|
+
description: y(p.description),
|
|
33
|
+
ownerId: p.ownerId || null
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
if (t) {
|
|
37
|
+
let n = v({
|
|
38
|
+
name: t.name.trim(),
|
|
39
|
+
description: y(t.description ?? ""),
|
|
40
|
+
ownerId: t.ownerId
|
|
41
|
+
}, e);
|
|
42
|
+
Object.keys(n).length > 0 && await r(E({ variables: {
|
|
43
|
+
id: t.id,
|
|
44
|
+
input: n
|
|
45
|
+
} }));
|
|
46
|
+
} else await r(S({ variables: { input: {
|
|
47
|
+
name: e.name,
|
|
48
|
+
...e.description ? { description: e.description } : {},
|
|
49
|
+
...e.ownerId ? { ownerId: e.ownerId } : {}
|
|
50
|
+
} } }));
|
|
51
|
+
f(!1);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
g(b(e, t ? i("planmagnet.productLines.errors.save", "The product line could not be saved.") : i("planmagnet.productLines.errors.create", "The product line could not be created."), s));
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
return /* @__PURE__ */ j(I, {
|
|
57
|
+
open: d,
|
|
58
|
+
onOpenChange: (e) => {
|
|
59
|
+
f(e), e ? m(l) : g(null);
|
|
60
|
+
},
|
|
61
|
+
children: [/* @__PURE__ */ A(H, {
|
|
62
|
+
asChild: !0,
|
|
63
|
+
children: t ? /* @__PURE__ */ j(N, {
|
|
64
|
+
size: "sm",
|
|
65
|
+
variant: "outline",
|
|
66
|
+
"data-testid": "product-line-edit",
|
|
67
|
+
"aria-label": i("planmagnet.productLines.actions.editNamed", "Edit {{name}}", { name: t.name }),
|
|
68
|
+
children: [/* @__PURE__ */ A(w, { className: "mr-1 h-4 w-4" }), i("planmagnet.productLines.actions.edit", "Edit")]
|
|
69
|
+
}) : /* @__PURE__ */ j(N, {
|
|
70
|
+
size: "sm",
|
|
71
|
+
"data-testid": "product-line-new",
|
|
72
|
+
children: [/* @__PURE__ */ A(T, { className: "mr-1 h-4 w-4" }), i("planmagnet.productLines.actions.new", "New product line")]
|
|
73
|
+
})
|
|
74
|
+
}), /* @__PURE__ */ j(L, {
|
|
75
|
+
className: "max-w-lg",
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ j(B, { children: [/* @__PURE__ */ A(V, { children: t ? i("planmagnet.productLines.dialog.editTitle", "Edit product line") : i("planmagnet.productLines.dialog.newTitle", "New product line") }), /* @__PURE__ */ A(R, { children: i("planmagnet.productLines.dialog.description", "A product line groups related products. A product belongs to at most one line.") })] }),
|
|
78
|
+
/* @__PURE__ */ j("div", {
|
|
79
|
+
className: "space-y-4",
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ j("div", { children: [/* @__PURE__ */ j("label", {
|
|
82
|
+
htmlFor: `${P}-name`,
|
|
83
|
+
className: "text-sm font-medium",
|
|
84
|
+
children: [i("planmagnet.productLines.fields.name", "Name"), " *"]
|
|
85
|
+
}), /* @__PURE__ */ A(F, {
|
|
86
|
+
id: `${P}-name`,
|
|
87
|
+
className: "mt-1",
|
|
88
|
+
value: p.name,
|
|
89
|
+
placeholder: i("planmagnet.productLines.fields.namePlaceholder", "e.g. Mobile apps"),
|
|
90
|
+
onChange: (e) => m({
|
|
91
|
+
...p,
|
|
92
|
+
name: e.target.value
|
|
93
|
+
}),
|
|
94
|
+
"data-testid": "product-line-name"
|
|
95
|
+
})] }),
|
|
96
|
+
/* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("label", {
|
|
97
|
+
htmlFor: `${P}-description`,
|
|
98
|
+
className: "text-sm font-medium",
|
|
99
|
+
children: i("planmagnet.productLines.fields.description", "Description")
|
|
100
|
+
}), /* @__PURE__ */ A("textarea", {
|
|
101
|
+
id: `${P}-description`,
|
|
102
|
+
className: U,
|
|
103
|
+
value: p.description,
|
|
104
|
+
onChange: (e) => m({
|
|
105
|
+
...p,
|
|
106
|
+
description: e.target.value
|
|
107
|
+
}),
|
|
108
|
+
"data-testid": "product-line-description"
|
|
109
|
+
})] }),
|
|
110
|
+
/* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("label", {
|
|
111
|
+
htmlFor: `${P}-owner`,
|
|
112
|
+
className: "text-sm font-medium",
|
|
113
|
+
children: i("planmagnet.productLines.fields.owner", "Owner")
|
|
114
|
+
}), /* @__PURE__ */ A("div", {
|
|
115
|
+
className: "mt-1",
|
|
116
|
+
children: /* @__PURE__ */ A(o, {
|
|
117
|
+
id: `${P}-owner`,
|
|
118
|
+
value: p.ownerId,
|
|
119
|
+
noneLabel: i("planmagnet.productLines.fields.noOwner", "No owner"),
|
|
120
|
+
onChange: (e) => m({
|
|
121
|
+
...p,
|
|
122
|
+
ownerId: e
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
})] })
|
|
126
|
+
]
|
|
127
|
+
}),
|
|
128
|
+
h && /* @__PURE__ */ A(a, { message: h }),
|
|
129
|
+
/* @__PURE__ */ A(z, { children: /* @__PURE__ */ A(N, {
|
|
130
|
+
onClick: () => void W(),
|
|
131
|
+
disabled: !M,
|
|
132
|
+
"data-testid": "product-line-save",
|
|
133
|
+
children: k ? i("planmagnet.productLines.actions.saving", "Saving…") : t ? i("planmagnet.productLines.actions.save", "Save") : i("planmagnet.productLines.actions.create", "Create product line")
|
|
134
|
+
}) })
|
|
135
|
+
]
|
|
136
|
+
})]
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function J({ line: e, productCount: t, onArchived: o, onDeleted: s }) {
|
|
140
|
+
let l = n(), u = K(), [f, p] = O(c, G), [m, h] = O(g, G), [_, v] = O(d, G), [y, w] = x(null), T = p.loading || h.loading || v.loading, D = async () => {
|
|
141
|
+
w(null);
|
|
142
|
+
try {
|
|
143
|
+
e.archivedAt ? await r(m({ variables: { id: e.id } })) : (await r(f({ variables: { id: e.id } })), o?.(e.name));
|
|
144
|
+
} catch (e) {
|
|
145
|
+
w(b(e, l("planmagnet.productLines.errors.update", "The product line could not be updated."), u));
|
|
146
|
+
}
|
|
147
|
+
}, k = async () => {
|
|
148
|
+
w(null);
|
|
149
|
+
try {
|
|
150
|
+
await r(_({ variables: { id: e.id } })), s?.(e.name);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
w(b(e, l("planmagnet.productLines.errors.delete", "The product line could not be deleted."), u));
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
return /* @__PURE__ */ j("div", {
|
|
156
|
+
className: "flex flex-col items-end gap-2",
|
|
157
|
+
children: [/* @__PURE__ */ j("div", {
|
|
158
|
+
className: "flex flex-wrap justify-end gap-2",
|
|
159
|
+
children: [
|
|
160
|
+
/* @__PURE__ */ A(q, { line: e }),
|
|
161
|
+
/* @__PURE__ */ j(N, {
|
|
162
|
+
size: "sm",
|
|
163
|
+
variant: "outline",
|
|
164
|
+
disabled: T,
|
|
165
|
+
onClick: () => void D(),
|
|
166
|
+
"data-testid": "product-line-toggle-archive",
|
|
167
|
+
children: [e.archivedAt ? /* @__PURE__ */ A(C, { className: "mr-1 h-4 w-4" }) : /* @__PURE__ */ A(S, { className: "mr-1 h-4 w-4" }), e.archivedAt ? l("planmagnet.productLines.actions.unarchive", "Unarchive") : l("planmagnet.productLines.actions.archive", "Archive")]
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ A(i, {
|
|
170
|
+
label: l("planmagnet.productLines.actions.delete", "Delete"),
|
|
171
|
+
variant: "outline",
|
|
172
|
+
icon: /* @__PURE__ */ A(E, { className: "mr-1 h-4 w-4" }),
|
|
173
|
+
testId: "product-line-delete",
|
|
174
|
+
title: l("planmagnet.productLines.confirmDelete.title", "Delete {{name}}?", { name: e.name }),
|
|
175
|
+
description: t === 0 ? l("planmagnet.productLines.confirmDelete.empty", "The line has no products. Nothing else changes. This cannot be undone here.") : l("planmagnet.productLines.confirmDelete.withProducts", "Its {{count}} product(s) are kept — they are only taken out of this line and show under \"No product line\". Nothing else about them changes. This cannot be undone here.", { count: t }),
|
|
176
|
+
confirmLabel: l("planmagnet.productLines.confirmDelete.confirm", "Delete product line"),
|
|
177
|
+
destructive: !0,
|
|
178
|
+
disabled: T,
|
|
179
|
+
onConfirm: () => void k()
|
|
180
|
+
})
|
|
181
|
+
]
|
|
182
|
+
}), y && /* @__PURE__ */ A(a, { message: y })]
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function Y({ line: e, products: i }) {
|
|
186
|
+
let o = n(), c = K(), u = t(), [d, f] = O(l, G), [p, m] = x(null), [h, g] = x(null), _ = async (e) => {
|
|
187
|
+
g(null), m(e);
|
|
188
|
+
try {
|
|
189
|
+
await r(d({ variables: {
|
|
190
|
+
productId: e,
|
|
191
|
+
productLineId: null
|
|
192
|
+
} }));
|
|
193
|
+
} catch (e) {
|
|
194
|
+
g(b(e, o("planmagnet.productLines.errors.unassign", "The product could not be taken out of the line."), c));
|
|
195
|
+
} finally {
|
|
196
|
+
m(null);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
return /* @__PURE__ */ j("div", {
|
|
200
|
+
className: "space-y-2",
|
|
201
|
+
"data-testid": "product-line-products",
|
|
202
|
+
children: [
|
|
203
|
+
/* @__PURE__ */ j("div", {
|
|
204
|
+
className: "flex items-center justify-between gap-2",
|
|
205
|
+
children: [/* @__PURE__ */ A("p", {
|
|
206
|
+
className: "text-xs font-medium text-muted-foreground",
|
|
207
|
+
children: i.length === 1 ? o("planmagnet.productLines.products.one", "1 product") : o("planmagnet.productLines.products.count", "{{count}} products", { count: i.length })
|
|
208
|
+
}), /* @__PURE__ */ A(X, {
|
|
209
|
+
line: e,
|
|
210
|
+
existingIds: i.map((e) => e.id)
|
|
211
|
+
})]
|
|
212
|
+
}),
|
|
213
|
+
i.length === 0 ? /* @__PURE__ */ A("p", {
|
|
214
|
+
className: "text-sm text-muted-foreground",
|
|
215
|
+
children: o("planmagnet.productLines.products.empty", "No products in this line yet. Add some here, or pick this line on a product’s page.")
|
|
216
|
+
}) : /* @__PURE__ */ A("ul", {
|
|
217
|
+
className: "space-y-1",
|
|
218
|
+
children: i.map((e) => /* @__PURE__ */ j("li", {
|
|
219
|
+
className: "flex flex-wrap items-center justify-between gap-2 rounded-md border p-2 text-sm",
|
|
220
|
+
children: [/* @__PURE__ */ A("button", {
|
|
221
|
+
type: "button",
|
|
222
|
+
className: "min-w-0 truncate text-left hover:underline",
|
|
223
|
+
onClick: () => u(s("/products/product-detail-page", e.id)),
|
|
224
|
+
children: e.name
|
|
225
|
+
}), /* @__PURE__ */ j("div", {
|
|
226
|
+
className: "flex shrink-0 items-center gap-2",
|
|
227
|
+
children: [e.archivedAt && /* @__PURE__ */ A(M, {
|
|
228
|
+
variant: "outline",
|
|
229
|
+
className: "text-xs",
|
|
230
|
+
children: o("planmagnet.productLines.archived", "Archived")
|
|
231
|
+
}), /* @__PURE__ */ A(N, {
|
|
232
|
+
size: "sm",
|
|
233
|
+
variant: "ghost",
|
|
234
|
+
"aria-label": o("planmagnet.productLines.products.removeNamed", "Take {{name}} out of this line", { name: e.name }),
|
|
235
|
+
disabled: f.loading,
|
|
236
|
+
onClick: () => void _(e.id),
|
|
237
|
+
"data-testid": "product-line-product-remove",
|
|
238
|
+
children: p === e.id ? "…" : /* @__PURE__ */ A(D, { className: "h-4 w-4" })
|
|
239
|
+
})]
|
|
240
|
+
})]
|
|
241
|
+
}, e.id))
|
|
242
|
+
}),
|
|
243
|
+
h && /* @__PURE__ */ A(a, { message: h })
|
|
244
|
+
]
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function X({ line: t, existingIds: i }) {
|
|
248
|
+
let o = n(), s = K(), { workspaceId: c } = e(), [u, d] = x(!1), [f, m] = x(/* @__PURE__ */ new Set()), [h, g] = x(null), { data: _, loading: v, error: y } = k(p, {
|
|
249
|
+
variables: { workspaceId: c ?? "" },
|
|
250
|
+
skip: !u || !c
|
|
251
|
+
}), [S, C] = O(l, G), w = _?.planmagnetProducts.items ?? [], E = w.filter((e) => !i.includes(e.id)), D = _?.planmagnetProducts.totalCount ?? 0, F = (e, t) => m((n) => {
|
|
252
|
+
let r = new Set(n);
|
|
253
|
+
return t ? r.add(e) : r.delete(e), r;
|
|
254
|
+
}), U = async () => {
|
|
255
|
+
g(null);
|
|
256
|
+
try {
|
|
257
|
+
await Promise.all([...f].map((e) => r(S({ variables: {
|
|
258
|
+
productId: e,
|
|
259
|
+
productLineId: t.id
|
|
260
|
+
} })))), m(/* @__PURE__ */ new Set()), d(!1);
|
|
261
|
+
} catch (e) {
|
|
262
|
+
g(b(e, o("planmagnet.productLines.errors.assign", "The products could not be added."), s));
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
return t.archivedAt ? /* @__PURE__ */ A("span", {
|
|
266
|
+
className: "text-xs text-muted-foreground",
|
|
267
|
+
children: o("planmagnet.productLines.products.archivedNoAdd", "Unarchive to add products")
|
|
268
|
+
}) : /* @__PURE__ */ j(I, {
|
|
269
|
+
open: u,
|
|
270
|
+
onOpenChange: (e) => {
|
|
271
|
+
d(e), e || (m(/* @__PURE__ */ new Set()), g(null));
|
|
272
|
+
},
|
|
273
|
+
children: [/* @__PURE__ */ A(H, {
|
|
274
|
+
asChild: !0,
|
|
275
|
+
children: /* @__PURE__ */ j(N, {
|
|
276
|
+
size: "sm",
|
|
277
|
+
variant: "outline",
|
|
278
|
+
"data-testid": "product-line-add-products",
|
|
279
|
+
children: [/* @__PURE__ */ A(T, { className: "mr-1 h-4 w-4" }), o("planmagnet.productLines.products.add", "Add products")]
|
|
280
|
+
})
|
|
281
|
+
}), /* @__PURE__ */ j(L, {
|
|
282
|
+
className: "max-w-lg",
|
|
283
|
+
children: [
|
|
284
|
+
/* @__PURE__ */ j(B, { children: [/* @__PURE__ */ A(V, { children: o("planmagnet.productLines.products.addTitle", "Add products to {{name}}", { name: t.name }) }), /* @__PURE__ */ A(R, { children: o("planmagnet.productLines.products.addDescription", "A product is in one line at most: picking one that is in another line moves it here. Nothing else about the product changes.") })] }),
|
|
285
|
+
/* @__PURE__ */ j("div", {
|
|
286
|
+
className: "max-h-80 space-y-1 overflow-y-auto",
|
|
287
|
+
"data-testid": "product-line-add-candidates",
|
|
288
|
+
children: [
|
|
289
|
+
v && !_ && /* @__PURE__ */ A("p", {
|
|
290
|
+
className: "py-4 text-center text-sm text-muted-foreground",
|
|
291
|
+
children: o("planmagnet.productLines.loading", "Loading…")
|
|
292
|
+
}),
|
|
293
|
+
y && /* @__PURE__ */ A(a, { message: y.message }),
|
|
294
|
+
_ && E.length === 0 && /* @__PURE__ */ A("p", {
|
|
295
|
+
className: "py-4 text-center text-sm text-muted-foreground",
|
|
296
|
+
children: w.length === 0 ? o("planmagnet.productLines.products.noProducts", "No products yet.") : o("planmagnet.productLines.products.noCandidates", "Every product is already in this line.")
|
|
297
|
+
}),
|
|
298
|
+
E.map((e) => {
|
|
299
|
+
let n = `product-line-add-${t.id}-${e.id}`;
|
|
300
|
+
return /* @__PURE__ */ j("label", {
|
|
301
|
+
htmlFor: n,
|
|
302
|
+
className: "flex cursor-pointer items-center gap-3 rounded-md border p-2 text-sm hover:bg-muted",
|
|
303
|
+
children: [
|
|
304
|
+
/* @__PURE__ */ A(P, {
|
|
305
|
+
id: n,
|
|
306
|
+
checked: f.has(e.id),
|
|
307
|
+
onCheckedChange: (t) => F(e.id, t === !0)
|
|
308
|
+
}),
|
|
309
|
+
/* @__PURE__ */ A("span", {
|
|
310
|
+
className: "min-w-0 flex-1 truncate",
|
|
311
|
+
children: e.name
|
|
312
|
+
}),
|
|
313
|
+
e.archivedAt && /* @__PURE__ */ A(M, {
|
|
314
|
+
variant: "outline",
|
|
315
|
+
className: "shrink-0 text-xs",
|
|
316
|
+
children: o("planmagnet.productLines.archived", "Archived")
|
|
317
|
+
}),
|
|
318
|
+
e.productLine && /* @__PURE__ */ A("span", {
|
|
319
|
+
className: "shrink-0 text-xs text-muted-foreground",
|
|
320
|
+
children: o("planmagnet.productLines.products.movesFrom", "moves from {{name}}", { name: e.productLine.name })
|
|
321
|
+
})
|
|
322
|
+
]
|
|
323
|
+
}, e.id);
|
|
324
|
+
}),
|
|
325
|
+
D > w.length && /* @__PURE__ */ A("p", {
|
|
326
|
+
className: "text-xs text-muted-foreground",
|
|
327
|
+
children: o("planmagnet.productLines.products.truncated", "Showing the first {{shown}} of {{total}} products — pick this line on a product’s own page for the rest.", {
|
|
328
|
+
shown: w.length,
|
|
329
|
+
total: D
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
]
|
|
333
|
+
}),
|
|
334
|
+
h && /* @__PURE__ */ A(a, { message: h }),
|
|
335
|
+
/* @__PURE__ */ A(z, { children: /* @__PURE__ */ A(N, {
|
|
336
|
+
onClick: () => void U(),
|
|
337
|
+
disabled: f.size === 0 || C.loading,
|
|
338
|
+
"data-testid": "product-line-add-confirm",
|
|
339
|
+
children: C.loading ? o("planmagnet.productLines.actions.adding", "Adding…") : o("planmagnet.productLines.products.addCount", "Add {{count}} product(s)", { count: f.size })
|
|
340
|
+
}) })
|
|
341
|
+
]
|
|
342
|
+
})]
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
function Z({ productId: i }) {
|
|
346
|
+
let o = n(), s = K(), { workspaceId: c } = e(), u = t(), d = k(f, {
|
|
347
|
+
variables: { productId: i },
|
|
348
|
+
skip: !i
|
|
349
|
+
}), p = k(m, {
|
|
350
|
+
variables: { workspaceId: c ?? "" },
|
|
351
|
+
skip: !c
|
|
352
|
+
}), [h, g] = O(l, G), [_, v] = x(null), y = d.data?.planmagnetProduct?.productLine ?? null, S = p.data?.planmagnetProductLines.items ?? [], C = !!y && S.some((e) => e.id === y.id), w = async (e) => {
|
|
353
|
+
if (e !== (y?.id ?? "")) {
|
|
354
|
+
v(null);
|
|
355
|
+
try {
|
|
356
|
+
await r(h({ variables: {
|
|
357
|
+
productId: i,
|
|
358
|
+
productLineId: e || null
|
|
359
|
+
} }));
|
|
360
|
+
} catch (e) {
|
|
361
|
+
v(b(e, o("planmagnet.productLines.errors.assignOne", "The product line could not be changed."), s));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
return d.error ? /* @__PURE__ */ A(a, { message: o("planmagnet.productLines.errors.loadAssignment", "The product line could not be loaded: {{message}}", { message: d.error.message }) }) : /* @__PURE__ */ j("div", {
|
|
366
|
+
className: "space-y-2",
|
|
367
|
+
"data-testid": "product-line-picker",
|
|
368
|
+
children: [
|
|
369
|
+
/* @__PURE__ */ A("label", {
|
|
370
|
+
htmlFor: "product-line-select",
|
|
371
|
+
className: "sr-only",
|
|
372
|
+
children: o("planmagnet.productLines.picker.label", "Product line")
|
|
373
|
+
}),
|
|
374
|
+
/* @__PURE__ */ j("select", {
|
|
375
|
+
id: "product-line-select",
|
|
376
|
+
className: W,
|
|
377
|
+
value: y?.id ?? "",
|
|
378
|
+
disabled: g.loading || d.loading && !d.data,
|
|
379
|
+
onChange: (e) => void w(e.target.value),
|
|
380
|
+
children: [
|
|
381
|
+
/* @__PURE__ */ A("option", {
|
|
382
|
+
value: "",
|
|
383
|
+
children: o("planmagnet.productLines.picker.none", "No product line")
|
|
384
|
+
}),
|
|
385
|
+
y && !C && /* @__PURE__ */ A("option", {
|
|
386
|
+
value: y.id,
|
|
387
|
+
disabled: !0,
|
|
388
|
+
children: y.archivedAt ? o("planmagnet.productLines.picker.archivedOption", "{{name}} (archived)", { name: y.name }) : y.name
|
|
389
|
+
}),
|
|
390
|
+
S.map((e) => /* @__PURE__ */ A("option", {
|
|
391
|
+
value: e.id,
|
|
392
|
+
children: e.name
|
|
393
|
+
}, e.id))
|
|
394
|
+
]
|
|
395
|
+
}),
|
|
396
|
+
p.error && /* @__PURE__ */ A(a, { message: p.error.message }),
|
|
397
|
+
_ && /* @__PURE__ */ A(a, { message: _ }),
|
|
398
|
+
/* @__PURE__ */ j("p", {
|
|
399
|
+
className: "text-xs text-muted-foreground",
|
|
400
|
+
children: [
|
|
401
|
+
o("planmagnet.productLines.picker.help", "Only active lines are offered — an archived line takes no new products."),
|
|
402
|
+
" ",
|
|
403
|
+
/* @__PURE__ */ A("button", {
|
|
404
|
+
type: "button",
|
|
405
|
+
className: "text-primary hover:underline",
|
|
406
|
+
onClick: () => u("/products/product-lines-page"),
|
|
407
|
+
children: o("planmagnet.productLines.picker.manage", "Manage product lines")
|
|
408
|
+
})
|
|
409
|
+
]
|
|
410
|
+
})
|
|
411
|
+
]
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
415
|
+
export { J as ProductLineActions, q as ProductLineFormDialog, Z as ProductLinePicker, Y as ProductLineProducts };
|
|
416
|
+
|
|
417
|
+
//# sourceMappingURL=ProductLineControls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductLineControls.js","names":[],"sources":["../../../src/planmagnet/product/ProductLineControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n Checkbox,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { Archive, ArchiveRestore, Pencil, Plus, Trash2, X } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { ConfirmButton, ErrorLine } from '../controls/ConfirmButton';\nimport { AssigneeSelect } from '../controls/WorkItemControls';\nimport { throwOnError } from '../utils/mutationResult';\nimport { detailPath } from '../utils/rowNavigation';\nimport { changedFields, normalizeText } from './hierarchy';\nimport {\n ARCHIVE_PRODUCT_LINE,\n ASSIGN_PRODUCT_LINE,\n CREATE_PRODUCT_LINE,\n DELETE_PRODUCT_LINE,\n PRODUCT_LINE_ASSIGNMENT,\n PRODUCT_LINE_CANDIDATE_PRODUCTS,\n PRODUCT_LINE_OPTIONS,\n PRODUCT_LINE_QUERIES,\n UNARCHIVE_PRODUCT_LINE,\n UPDATE_PRODUCT_LINE,\n} from './hierarchyOperations';\nimport { refusalMessage } from './refusal';\n\n/**\n * Product line controls (PRM-01). A line groups products — one line, many products, a product in at most one line.\n * The line never owns its products: archiving keeps them in it (an archived line just accepts no new ones), and\n * deleting it only unassigns them. Nothing here ever deletes or changes a product beyond which line it is in.\n */\n\nexport interface ProductLineValues {\n id: string;\n name: string;\n description: string | null;\n ownerId: string | null;\n archivedAt: string | null;\n}\n\nexport interface ProductLineProductRef {\n id: string;\n name: string;\n archivedAt: string | null;\n}\n\nconst textareaClass =\n 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]';\nconst selectClass =\n 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60';\n\nconst writeOptions = { refetchQueries: PRODUCT_LINE_QUERIES, awaitRefetchQueries: true };\n\nfunction useForbiddenText() {\n const tr = useTr();\n return tr(\n 'planmagnet.productLines.errors.forbidden',\n 'Your role cannot change product lines in this workspace.'\n );\n}\n\n// ─── Create / edit ──────────────────────────────────────────────────────────\n\n/**\n * Create a line (no `line`) or edit one. An edit sends only the fields that changed, and an emptied description or\n * a cleared owner is sent as null, which clears it.\n */\nexport function ProductLineFormDialog({ line }: { line?: ProductLineValues }) {\n const tr = useTr();\n const forbidden = useForbiddenText();\n const { currentUser } = usePlanMagnet();\n const initial = {\n name: line?.name ?? '',\n description: line?.description ?? '',\n // A new line is owned by whoever creates it unless they pick someone else (or nobody).\n ownerId: line ? (line.ownerId ?? '') : (currentUser?.id ?? ''),\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n const [create, createState] = useMutation(CREATE_PRODUCT_LINE, writeOptions);\n const [update, updateState] = useMutation(UPDATE_PRODUCT_LINE, writeOptions);\n const saving = createState.loading || updateState.loading;\n const canSave = form.name.trim() !== '' && !saving;\n const idPrefix = line ? `product-line-edit-${line.id}` : 'product-line-new';\n\n const save = async () => {\n setError(null);\n const next = {\n name: form.name.trim(),\n description: normalizeText(form.description),\n ownerId: form.ownerId || null,\n };\n try {\n if (line) {\n const before = {\n name: line.name.trim(),\n description: normalizeText(line.description ?? ''),\n ownerId: line.ownerId,\n };\n const input = changedFields(before, next);\n if (Object.keys(input).length > 0) {\n await throwOnError(update({ variables: { id: line.id, input } }));\n }\n } else {\n await throwOnError(\n create({\n variables: {\n input: {\n name: next.name,\n ...(next.description ? { description: next.description } : {}),\n ...(next.ownerId ? { ownerId: next.ownerId } : {}),\n },\n },\n })\n );\n }\n setOpen(false);\n } catch (e) {\n setError(\n refusalMessage(\n e,\n line\n ? tr('planmagnet.productLines.errors.save', 'The product line could not be saved.')\n : tr('planmagnet.productLines.errors.create', 'The product line could not be created.'),\n forbidden\n )\n );\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setForm(initial);\n else setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n {line ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"product-line-edit\"\n aria-label={tr('planmagnet.productLines.actions.editNamed', 'Edit {{name}}', {\n name: line.name,\n })}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n {tr('planmagnet.productLines.actions.edit', 'Edit')}\n </Button>\n ) : (\n <Button size=\"sm\" data-testid=\"product-line-new\">\n <Plus className=\"mr-1 h-4 w-4\" />\n {tr('planmagnet.productLines.actions.new', 'New product line')}\n </Button>\n )}\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {line\n ? tr('planmagnet.productLines.dialog.editTitle', 'Edit product line')\n : tr('planmagnet.productLines.dialog.newTitle', 'New product line')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'planmagnet.productLines.dialog.description',\n 'A product line groups related products. A product belongs to at most one line.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div>\n <label htmlFor={`${idPrefix}-name`} className=\"text-sm font-medium\">\n {tr('planmagnet.productLines.fields.name', 'Name')} *\n </label>\n <Input\n id={`${idPrefix}-name`}\n className=\"mt-1\"\n value={form.name}\n placeholder={tr('planmagnet.productLines.fields.namePlaceholder', 'e.g. Mobile apps')}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n data-testid=\"product-line-name\"\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-description`} className=\"text-sm font-medium\">\n {tr('planmagnet.productLines.fields.description', 'Description')}\n </label>\n <textarea\n id={`${idPrefix}-description`}\n className={textareaClass}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n data-testid=\"product-line-description\"\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-owner`} className=\"text-sm font-medium\">\n {tr('planmagnet.productLines.fields.owner', 'Owner')}\n </label>\n <div className=\"mt-1\">\n <AssigneeSelect\n id={`${idPrefix}-owner`}\n value={form.ownerId}\n noneLabel={tr('planmagnet.productLines.fields.noOwner', 'No owner')}\n onChange={(ownerId) => setForm({ ...form, ownerId })}\n />\n </div>\n </div>\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button onClick={() => void save()} disabled={!canSave} data-testid=\"product-line-save\">\n {saving\n ? tr('planmagnet.productLines.actions.saving', 'Saving…')\n : line\n ? tr('planmagnet.productLines.actions.save', 'Save')\n : tr('planmagnet.productLines.actions.create', 'Create product line')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── Archive / unarchive / delete ───────────────────────────────────────────\n\n/**\n * Edit, archive/unarchive and delete for one line. `onArchived` / `onDeleted` run after the service accepted the\n * write — the list may have dropped this card by then (archived lines are hidden by default), so the page, not\n * the card, says what happened.\n */\nexport function ProductLineActions({\n line,\n productCount,\n onArchived,\n onDeleted,\n}: {\n line: ProductLineValues;\n productCount: number;\n onArchived?: (name: string) => void;\n onDeleted?: (name: string) => void;\n}) {\n const tr = useTr();\n const forbidden = useForbiddenText();\n const [archive, archiveState] = useMutation(ARCHIVE_PRODUCT_LINE, writeOptions);\n const [unarchive, unarchiveState] = useMutation(UNARCHIVE_PRODUCT_LINE, writeOptions);\n const [remove, removeState] = useMutation(DELETE_PRODUCT_LINE, writeOptions);\n const [error, setError] = useState<string | null>(null);\n const busy = archiveState.loading || unarchiveState.loading || removeState.loading;\n\n const onToggleArchive = async () => {\n setError(null);\n try {\n if (line.archivedAt) {\n await throwOnError(unarchive({ variables: { id: line.id } }));\n } else {\n await throwOnError(archive({ variables: { id: line.id } }));\n onArchived?.(line.name);\n }\n } catch (e) {\n setError(\n refusalMessage(\n e,\n tr('planmagnet.productLines.errors.update', 'The product line could not be updated.'),\n forbidden\n )\n );\n }\n };\n\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: line.id } }));\n onDeleted?.(line.name);\n } catch (e) {\n setError(\n refusalMessage(\n e,\n tr('planmagnet.productLines.errors.delete', 'The product line could not be deleted.'),\n forbidden\n )\n );\n }\n };\n\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex flex-wrap justify-end gap-2\">\n <ProductLineFormDialog line={line} />\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={busy}\n onClick={() => void onToggleArchive()}\n data-testid=\"product-line-toggle-archive\"\n >\n {line.archivedAt ? (\n <ArchiveRestore className=\"mr-1 h-4 w-4\" />\n ) : (\n <Archive className=\"mr-1 h-4 w-4\" />\n )}\n {line.archivedAt\n ? tr('planmagnet.productLines.actions.unarchive', 'Unarchive')\n : tr('planmagnet.productLines.actions.archive', 'Archive')}\n </Button>\n <ConfirmButton\n label={tr('planmagnet.productLines.actions.delete', 'Delete')}\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n testId=\"product-line-delete\"\n title={tr('planmagnet.productLines.confirmDelete.title', 'Delete {{name}}?', {\n name: line.name,\n })}\n description={\n productCount === 0\n ? tr(\n 'planmagnet.productLines.confirmDelete.empty',\n 'The line has no products. Nothing else changes. This cannot be undone here.'\n )\n : tr(\n 'planmagnet.productLines.confirmDelete.withProducts',\n 'Its {{count}} product(s) are kept — they are only taken out of this line and show under \"No product line\". Nothing else about them changes. This cannot be undone here.',\n { count: productCount }\n )\n }\n confirmLabel={tr('planmagnet.productLines.confirmDelete.confirm', 'Delete product line')}\n destructive\n disabled={busy}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\n// ─── The products in a line ─────────────────────────────────────────────────\n\n/**\n * The products in one line, each linking to its product page with a \"take out of this line\" control, and an \"Add\n * products\" dialog. Taking a product out only unassigns it; the product itself is untouched.\n */\nexport function ProductLineProducts({\n line,\n products,\n}: {\n line: ProductLineValues;\n products: ProductLineProductRef[];\n}) {\n const tr = useTr();\n const forbidden = useForbiddenText();\n const navigate = usePlanMagnetNavigate();\n const [assign, assignState] = useMutation(ASSIGN_PRODUCT_LINE, writeOptions);\n const [removingId, setRemovingId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const onRemove = async (productId: string) => {\n setError(null);\n setRemovingId(productId);\n try {\n await throwOnError(assign({ variables: { productId, productLineId: null } }));\n } catch (e) {\n setError(\n refusalMessage(\n e,\n tr(\n 'planmagnet.productLines.errors.unassign',\n 'The product could not be taken out of the line.'\n ),\n forbidden\n )\n );\n } finally {\n setRemovingId(null);\n }\n };\n\n return (\n <div className=\"space-y-2\" data-testid=\"product-line-products\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {products.length === 1\n ? tr('planmagnet.productLines.products.one', '1 product')\n : tr('planmagnet.productLines.products.count', '{{count}} products', {\n count: products.length,\n })}\n </p>\n <AddProductsToLineDialog line={line} existingIds={products.map((p) => p.id)} />\n </div>\n {products.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.productLines.products.empty',\n 'No products in this line yet. Add some here, or pick this line on a product’s page.'\n )}\n </p>\n ) : (\n <ul className=\"space-y-1\">\n {products.map((product) => (\n <li\n key={product.id}\n className=\"flex flex-wrap items-center justify-between gap-2 rounded-md border p-2 text-sm\"\n >\n <button\n type=\"button\"\n className=\"min-w-0 truncate text-left hover:underline\"\n onClick={() => navigate(detailPath('/products/product-detail-page', product.id))}\n >\n {product.name}\n </button>\n <div className=\"flex shrink-0 items-center gap-2\">\n {product.archivedAt && (\n <Badge variant=\"outline\" className=\"text-xs\">\n {tr('planmagnet.productLines.archived', 'Archived')}\n </Badge>\n )}\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={tr(\n 'planmagnet.productLines.products.removeNamed',\n 'Take {{name}} out of this line',\n { name: product.name }\n )}\n disabled={assignState.loading}\n onClick={() => void onRemove(product.id)}\n data-testid=\"product-line-product-remove\"\n >\n {removingId === product.id ? '…' : <X className=\"h-4 w-4\" />}\n </Button>\n </div>\n </li>\n ))}\n </ul>\n )}\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\ninterface CandidateProduct {\n id: string;\n name: string;\n archivedAt: string | null;\n productLine: { id: string; name: string } | null;\n}\n\n/** Pick products to put in this line. A product already in another line moves here (a product is in one line). */\nfunction AddProductsToLineDialog({\n line,\n existingIds,\n}: {\n line: ProductLineValues;\n existingIds: string[];\n}) {\n const tr = useTr();\n const forbidden = useForbiddenText();\n const { workspaceId } = usePlanMagnet();\n const [open, setOpen] = useState(false);\n const [selected, setSelected] = useState<Set<string>>(new Set());\n const [error, setError] = useState<string | null>(null);\n const {\n data,\n loading,\n error: loadError,\n } = useQuery<{\n planmagnetProducts: { items: CandidateProduct[]; totalCount: number };\n }>(PRODUCT_LINE_CANDIDATE_PRODUCTS, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !open || !workspaceId,\n });\n const [assign, assignState] = useMutation(ASSIGN_PRODUCT_LINE, writeOptions);\n const loaded = data?.planmagnetProducts.items ?? [];\n const candidates = loaded.filter((p) => !existingIds.includes(p.id));\n const total = data?.planmagnetProducts.totalCount ?? 0;\n\n const toggle = (id: string, checked: boolean) =>\n setSelected((prev) => {\n const next = new Set(prev);\n if (checked) next.add(id);\n else next.delete(id);\n return next;\n });\n\n const onAdd = async () => {\n setError(null);\n try {\n await Promise.all(\n [...selected].map((productId) =>\n throwOnError(assign({ variables: { productId, productLineId: line.id } }))\n )\n );\n setSelected(new Set());\n setOpen(false);\n } catch (e) {\n setError(\n refusalMessage(\n e,\n tr('planmagnet.productLines.errors.assign', 'The products could not be added.'),\n forbidden\n )\n );\n }\n };\n\n // An archived line accepts no new products (the service refuses it), so it offers none.\n if (line.archivedAt) {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {tr('planmagnet.productLines.products.archivedNoAdd', 'Unarchive to add products')}\n </span>\n );\n }\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) {\n setSelected(new Set());\n setError(null);\n }\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\" variant=\"outline\" data-testid=\"product-line-add-products\">\n <Plus className=\"mr-1 h-4 w-4\" />\n {tr('planmagnet.productLines.products.add', 'Add products')}\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('planmagnet.productLines.products.addTitle', 'Add products to {{name}}', {\n name: line.name,\n })}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'planmagnet.productLines.products.addDescription',\n 'A product is in one line at most: picking one that is in another line moves it here. Nothing else about the product changes.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div\n className=\"max-h-80 space-y-1 overflow-y-auto\"\n data-testid=\"product-line-add-candidates\"\n >\n {loading && !data && (\n <p className=\"py-4 text-center text-sm text-muted-foreground\">\n {tr('planmagnet.productLines.loading', 'Loading…')}\n </p>\n )}\n {loadError && <ErrorLine message={loadError.message} />}\n {data && candidates.length === 0 && (\n <p className=\"py-4 text-center text-sm text-muted-foreground\">\n {loaded.length === 0\n ? tr('planmagnet.productLines.products.noProducts', 'No products yet.')\n : tr(\n 'planmagnet.productLines.products.noCandidates',\n 'Every product is already in this line.'\n )}\n </p>\n )}\n {candidates.map((product) => {\n const inputId = `product-line-add-${line.id}-${product.id}`;\n return (\n <label\n key={product.id}\n htmlFor={inputId}\n className=\"flex cursor-pointer items-center gap-3 rounded-md border p-2 text-sm hover:bg-muted\"\n >\n <Checkbox\n id={inputId}\n checked={selected.has(product.id)}\n onCheckedChange={(checked) => toggle(product.id, checked === true)}\n />\n <span className=\"min-w-0 flex-1 truncate\">{product.name}</span>\n {product.archivedAt && (\n <Badge variant=\"outline\" className=\"shrink-0 text-xs\">\n {tr('planmagnet.productLines.archived', 'Archived')}\n </Badge>\n )}\n {product.productLine && (\n <span className=\"shrink-0 text-xs text-muted-foreground\">\n {tr('planmagnet.productLines.products.movesFrom', 'moves from {{name}}', {\n name: product.productLine.name,\n })}\n </span>\n )}\n </label>\n );\n })}\n {total > loaded.length && (\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.productLines.products.truncated',\n 'Showing the first {{shown}} of {{total}} products — pick this line on a product’s own page for the rest.',\n { shown: loaded.length, total }\n )}\n </p>\n )}\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button\n onClick={() => void onAdd()}\n disabled={selected.size === 0 || assignState.loading}\n data-testid=\"product-line-add-confirm\"\n >\n {assignState.loading\n ? tr('planmagnet.productLines.actions.adding', 'Adding…')\n : tr('planmagnet.productLines.products.addCount', 'Add {{count}} product(s)', {\n count: selected.size,\n })}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── A product's line (product detail) ──────────────────────────────────────\n\ninterface LineAssignment {\n id: string;\n productLineId: string | null;\n productLine: { id: string; name: string; archivedAt: string | null } | null;\n}\n\n/**\n * The product page's product-line select: assigns on change, \"No product line\" unassigns. Only active lines are\n * offered (an archived line accepts no new products); the product's current line stays listed — marked archived\n * when it is — so the select never claims the product is somewhere it is not.\n */\nexport function ProductLinePicker({ productId }: { productId: string }) {\n const tr = useTr();\n const forbidden = useForbiddenText();\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const assignment = useQuery<{ planmagnetProduct: LineAssignment | null }>(\n PRODUCT_LINE_ASSIGNMENT,\n { variables: { productId }, skip: !productId }\n );\n const options = useQuery<{\n planmagnetProductLines: { items: { id: string; name: string }[]; totalCount: number };\n }>(PRODUCT_LINE_OPTIONS, { variables: { workspaceId: workspaceId ?? '' }, skip: !workspaceId });\n const [assign, assignState] = useMutation(ASSIGN_PRODUCT_LINE, writeOptions);\n const [error, setError] = useState<string | null>(null);\n\n const current = assignment.data?.planmagnetProduct?.productLine ?? null;\n const lines = options.data?.planmagnetProductLines.items ?? [];\n const currentListed = !!current && lines.some((l) => l.id === current.id);\n\n const onSelect = async (next: string) => {\n if (next === (current?.id ?? '')) return;\n setError(null);\n try {\n await throwOnError(assign({ variables: { productId, productLineId: next || null } }));\n } catch (e) {\n setError(\n refusalMessage(\n e,\n tr('planmagnet.productLines.errors.assignOne', 'The product line could not be changed.'),\n forbidden\n )\n );\n }\n };\n\n if (assignment.error) {\n return (\n <ErrorLine\n message={tr(\n 'planmagnet.productLines.errors.loadAssignment',\n 'The product line could not be loaded: {{message}}',\n { message: assignment.error.message }\n )}\n />\n );\n }\n\n return (\n <div className=\"space-y-2\" data-testid=\"product-line-picker\">\n {/* The card around the picker already titles it; the label stays for screen readers. */}\n <label htmlFor=\"product-line-select\" className=\"sr-only\">\n {tr('planmagnet.productLines.picker.label', 'Product line')}\n </label>\n <select\n id=\"product-line-select\"\n className={selectClass}\n value={current?.id ?? ''}\n disabled={assignState.loading || (assignment.loading && !assignment.data)}\n onChange={(e) => void onSelect(e.target.value)}\n >\n <option value=\"\">{tr('planmagnet.productLines.picker.none', 'No product line')}</option>\n {current && !currentListed && (\n <option value={current.id} disabled>\n {current.archivedAt\n ? tr('planmagnet.productLines.picker.archivedOption', '{{name}} (archived)', {\n name: current.name,\n })\n : current.name}\n </option>\n )}\n {lines.map((l) => (\n <option key={l.id} value={l.id}>\n {l.name}\n </option>\n ))}\n </select>\n {options.error && <ErrorLine message={options.error.message} />}\n {error && <ErrorLine message={error} />}\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.productLines.picker.help',\n 'Only active lines are offered — an archived line takes no new products.'\n )}{' '}\n <button\n type=\"button\"\n className=\"text-primary hover:underline\"\n onClick={() => navigate('/products/product-lines-page')}\n >\n {tr('planmagnet.productLines.picker.manage', 'Manage product lines')}\n </button>\n </p>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductTeamsPanel.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/product/ProductTeamsPanel.tsx"],"names":[],"mappings":"AA2BA,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,2CAkKrE"}
|