@burdenoff/microfe-planmagnet 2026.927.3 → 2026.927.4
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/controls/FeatureControls.d.ts +26 -0
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeatureControls.js +318 -0
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -0
- package/dist/planmagnet/controls/MilestoneControls.d.ts +17 -0
- package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/MilestoneControls.js +200 -0
- package/dist/planmagnet/controls/MilestoneControls.js.map +1 -0
- package/dist/planmagnet/controls/ObjectiveControls.d.ts +31 -0
- package/dist/planmagnet/controls/ObjectiveControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/ObjectiveControls.js +296 -0
- package/dist/planmagnet/controls/ObjectiveControls.js.map +1 -0
- package/dist/planmagnet/controls/Pickers.d.ts +26 -0
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -0
- package/dist/planmagnet/controls/Pickers.js +171 -0
- package/dist/planmagnet/controls/Pickers.js.map +1 -0
- package/dist/planmagnet/controls/ProjectControls.js +7 -7
- package/dist/planmagnet/controls/ReleaseControls.d.ts +14 -0
- package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/ReleaseControls.js +108 -0
- package/dist/planmagnet/controls/ReleaseControls.js.map +1 -0
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +186 -152
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js +63 -56
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +229 -214
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js +142 -114
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +134 -105
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +102 -108
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +119 -94
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +107 -106
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js +2 -0
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +6 -6
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +55 -63
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +4 -4
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +123 -128
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +139 -107
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +173 -126
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +1 -0
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js +23 -23
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Objective controls (BOFF-7956/7963). Objectives were created ACTIVE and could never be completed, cancelled or
|
|
3
|
+
* edited, so the Draft/Completed/Cancelled tabs could only be empty; and key results could not be linked to work
|
|
4
|
+
* items, so the linked-work-item rollup never appeared.
|
|
5
|
+
*/
|
|
6
|
+
export declare const OBJECTIVE_STATUSES: readonly ["DRAFT", "ACTIVE", "COMPLETED", "CANCELLED"];
|
|
7
|
+
export interface ObjectiveValues {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string | null;
|
|
11
|
+
periodLabel: string;
|
|
12
|
+
startDate: string | null;
|
|
13
|
+
endDate: string | null;
|
|
14
|
+
status: string;
|
|
15
|
+
}
|
|
16
|
+
/** Status select and Edit for the objective header. */
|
|
17
|
+
export declare function ObjectiveHeaderControls({ objective }: {
|
|
18
|
+
objective: ObjectiveValues;
|
|
19
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
/** The work items a key result is linked to, with unlink, and a picker to link another. */
|
|
21
|
+
export declare function KeyResultWorkItems({ keyResultId, keyResultTitle, linked, }: {
|
|
22
|
+
keyResultId: string;
|
|
23
|
+
keyResultTitle: string;
|
|
24
|
+
linked: {
|
|
25
|
+
id: string;
|
|
26
|
+
key: string;
|
|
27
|
+
title: string;
|
|
28
|
+
status: string;
|
|
29
|
+
}[];
|
|
30
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
//# sourceMappingURL=ObjectiveControls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObjectiveControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/ObjectiveControls.tsx"],"names":[],"mappings":"AAqBA;;;;GAIG;AAEH,eAAO,MAAM,kBAAkB,wDAAyD,CAAC;AAgCzF,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uDAAuD;AACvD,wBAAgB,uBAAuB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,eAAe,CAAA;CAAE,2CAsCpF;AAgJD,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,EACjC,WAAW,EACX,cAAc,EACd,MAAM,GACP,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtE,2CA6EA"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { throwOnError as e } from "../utils/mutationResult.js";
|
|
2
|
+
import { WorkItemSearchSelect as t } from "./Pickers.js";
|
|
3
|
+
import { ErrorLine as n } from "./ConfirmButton.js";
|
|
4
|
+
import { endBeforeStart as r } from "../utils/formValidation.js";
|
|
5
|
+
import { enumLabel as i } from "./WorkItemControls.js";
|
|
6
|
+
import { useState as a } from "react";
|
|
7
|
+
import { Link2 as o, Pencil as s, X as c } from "lucide-react";
|
|
8
|
+
import { gql as l } from "@apollo/client";
|
|
9
|
+
import { useMutation as u } from "@apollo/client/react";
|
|
10
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
11
|
+
import { Button as p, Input as m, ResponsiveDialog as h, ResponsiveDialogContent as g, ResponsiveDialogDescription as _, ResponsiveDialogFooter as v, ResponsiveDialogHeader as y, ResponsiveDialogTitle as b, ResponsiveDialogTrigger as x } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/planmagnet/controls/ObjectiveControls.tsx
|
|
13
|
+
var S = [
|
|
14
|
+
"DRAFT",
|
|
15
|
+
"ACTIVE",
|
|
16
|
+
"COMPLETED",
|
|
17
|
+
"CANCELLED"
|
|
18
|
+
], C = ["GetObjectiveDetail", "GetObjectives"], w = l`
|
|
19
|
+
mutation ObjectiveControlsUpdate($id: ID!, $input: PlanmagnetUpdateObjectiveInput!) {
|
|
20
|
+
planmagnetUpdateObjective(id: $id, input: $input) {
|
|
21
|
+
id
|
|
22
|
+
title
|
|
23
|
+
description
|
|
24
|
+
periodLabel
|
|
25
|
+
startDate
|
|
26
|
+
endDate
|
|
27
|
+
status
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`, T = l`
|
|
31
|
+
mutation ObjectiveControlsLinkWorkItem($keyResultId: ID!, $workItemId: ID!) {
|
|
32
|
+
planmagnetLinkKeyResultToWorkItem(keyResultId: $keyResultId, workItemId: $workItemId) {
|
|
33
|
+
id
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`, E = l`
|
|
37
|
+
mutation ObjectiveControlsUnlinkWorkItem($keyResultId: ID!, $workItemId: ID!) {
|
|
38
|
+
planmagnetUnlinkKeyResultFromWorkItem(keyResultId: $keyResultId, workItemId: $workItemId)
|
|
39
|
+
}
|
|
40
|
+
`, D = {
|
|
41
|
+
refetchQueries: C,
|
|
42
|
+
awaitRefetchQueries: !0
|
|
43
|
+
};
|
|
44
|
+
function O({ objective: t }) {
|
|
45
|
+
let [r, o] = u(w, D), [s, c] = a(null), l = S.includes(t.status) ? S : [t.status, ...S], p = async (n) => {
|
|
46
|
+
c(null);
|
|
47
|
+
try {
|
|
48
|
+
await e(r({ variables: {
|
|
49
|
+
id: t.id,
|
|
50
|
+
input: { status: n }
|
|
51
|
+
} }));
|
|
52
|
+
} catch (e) {
|
|
53
|
+
c(e instanceof Error ? e.message : "The status could not be changed.");
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
return /* @__PURE__ */ f("div", {
|
|
57
|
+
className: "flex flex-col items-end gap-1",
|
|
58
|
+
children: [/* @__PURE__ */ f("div", {
|
|
59
|
+
className: "flex items-center gap-2",
|
|
60
|
+
children: [/* @__PURE__ */ d("select", {
|
|
61
|
+
"aria-label": "Objective status",
|
|
62
|
+
"data-testid": "objective-status",
|
|
63
|
+
className: "rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60",
|
|
64
|
+
value: t.status,
|
|
65
|
+
disabled: o.loading,
|
|
66
|
+
onChange: (e) => {
|
|
67
|
+
e.target.value !== t.status && p(e.target.value);
|
|
68
|
+
},
|
|
69
|
+
children: l.map((e) => /* @__PURE__ */ d("option", {
|
|
70
|
+
value: e,
|
|
71
|
+
children: i(e)
|
|
72
|
+
}, e))
|
|
73
|
+
}), /* @__PURE__ */ d(A, { objective: t })]
|
|
74
|
+
}), s && /* @__PURE__ */ d(n, { message: s })]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
var k = (e) => e ? e.slice(0, 10) : "";
|
|
78
|
+
function A({ objective: t }) {
|
|
79
|
+
let i = {
|
|
80
|
+
title: t.title,
|
|
81
|
+
description: t.description ?? "",
|
|
82
|
+
periodLabel: t.periodLabel,
|
|
83
|
+
startDate: k(t.startDate),
|
|
84
|
+
endDate: k(t.endDate)
|
|
85
|
+
}, [o, c] = a(!1), [l, S] = a(i), [C, T] = a(null), [E, O] = u(w, D), A = r(l.startDate, l.endDate), j = l.title.trim() !== "" && l.periodLabel.trim() !== "" && !A && !O.loading, M = async () => {
|
|
86
|
+
T(null);
|
|
87
|
+
let n = {};
|
|
88
|
+
if (l.title.trim() !== i.title && (n.title = l.title.trim()), l.periodLabel.trim() !== i.periodLabel && (n.periodLabel = l.periodLabel.trim()), l.description.trim() !== i.description.trim() && (n.description = l.description.trim() || null), l.startDate !== i.startDate && (n.startDate = l.startDate || null), l.endDate !== i.endDate && (n.endDate = l.endDate || null), Object.keys(n).length === 0) {
|
|
89
|
+
c(!1);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
await e(E({ variables: {
|
|
94
|
+
id: t.id,
|
|
95
|
+
input: n
|
|
96
|
+
} })), c(!1);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
T(e instanceof Error ? e.message : "The objective could not be saved.");
|
|
99
|
+
}
|
|
100
|
+
}, N = "text-sm font-medium";
|
|
101
|
+
return /* @__PURE__ */ f(h, {
|
|
102
|
+
open: o,
|
|
103
|
+
onOpenChange: (e) => {
|
|
104
|
+
c(e), e ? S(i) : T(null);
|
|
105
|
+
},
|
|
106
|
+
children: [/* @__PURE__ */ d(x, {
|
|
107
|
+
asChild: !0,
|
|
108
|
+
children: /* @__PURE__ */ f(p, {
|
|
109
|
+
size: "sm",
|
|
110
|
+
variant: "outline",
|
|
111
|
+
"data-testid": "objective-edit",
|
|
112
|
+
children: [/* @__PURE__ */ d(s, { className: "mr-1 h-4 w-4" }), "Edit"]
|
|
113
|
+
})
|
|
114
|
+
}), /* @__PURE__ */ f(g, {
|
|
115
|
+
className: "max-w-lg",
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ f(y, { children: [/* @__PURE__ */ d(b, { children: "Edit objective" }), /* @__PURE__ */ d(_, { children: "Change the title, period or description." })] }),
|
|
118
|
+
/* @__PURE__ */ f("div", {
|
|
119
|
+
className: "space-y-3",
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
122
|
+
htmlFor: "objective-edit-title",
|
|
123
|
+
className: N,
|
|
124
|
+
children: "Title"
|
|
125
|
+
}), /* @__PURE__ */ d(m, {
|
|
126
|
+
id: "objective-edit-title",
|
|
127
|
+
className: "mt-1",
|
|
128
|
+
value: l.title,
|
|
129
|
+
onChange: (e) => S({
|
|
130
|
+
...l,
|
|
131
|
+
title: e.target.value
|
|
132
|
+
})
|
|
133
|
+
})] }),
|
|
134
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
135
|
+
htmlFor: "objective-edit-period",
|
|
136
|
+
className: N,
|
|
137
|
+
children: "Period"
|
|
138
|
+
}), /* @__PURE__ */ d(m, {
|
|
139
|
+
id: "objective-edit-period",
|
|
140
|
+
className: "mt-1",
|
|
141
|
+
placeholder: "e.g. Q4 2026",
|
|
142
|
+
value: l.periodLabel,
|
|
143
|
+
onChange: (e) => S({
|
|
144
|
+
...l,
|
|
145
|
+
periodLabel: e.target.value
|
|
146
|
+
})
|
|
147
|
+
})] }),
|
|
148
|
+
/* @__PURE__ */ f("div", {
|
|
149
|
+
className: "grid grid-cols-2 gap-3",
|
|
150
|
+
children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
151
|
+
htmlFor: "objective-edit-start",
|
|
152
|
+
className: N,
|
|
153
|
+
children: "Start date"
|
|
154
|
+
}), /* @__PURE__ */ d(m, {
|
|
155
|
+
id: "objective-edit-start",
|
|
156
|
+
type: "date",
|
|
157
|
+
className: "mt-1",
|
|
158
|
+
value: l.startDate,
|
|
159
|
+
max: l.endDate || void 0,
|
|
160
|
+
onChange: (e) => S({
|
|
161
|
+
...l,
|
|
162
|
+
startDate: e.target.value
|
|
163
|
+
})
|
|
164
|
+
})] }), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
165
|
+
htmlFor: "objective-edit-end",
|
|
166
|
+
className: N,
|
|
167
|
+
children: "End date"
|
|
168
|
+
}), /* @__PURE__ */ d(m, {
|
|
169
|
+
id: "objective-edit-end",
|
|
170
|
+
type: "date",
|
|
171
|
+
className: "mt-1",
|
|
172
|
+
value: l.endDate,
|
|
173
|
+
min: l.startDate || void 0,
|
|
174
|
+
onChange: (e) => S({
|
|
175
|
+
...l,
|
|
176
|
+
endDate: e.target.value
|
|
177
|
+
})
|
|
178
|
+
})] })]
|
|
179
|
+
}),
|
|
180
|
+
A && /* @__PURE__ */ d(n, { message: "The end date is before the start date." }),
|
|
181
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
182
|
+
htmlFor: "objective-edit-description",
|
|
183
|
+
className: N,
|
|
184
|
+
children: "Description"
|
|
185
|
+
}), /* @__PURE__ */ d("textarea", {
|
|
186
|
+
id: "objective-edit-description",
|
|
187
|
+
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]",
|
|
188
|
+
value: l.description,
|
|
189
|
+
onChange: (e) => S({
|
|
190
|
+
...l,
|
|
191
|
+
description: e.target.value
|
|
192
|
+
})
|
|
193
|
+
})] }),
|
|
194
|
+
C && /* @__PURE__ */ d(n, { message: C })
|
|
195
|
+
]
|
|
196
|
+
}),
|
|
197
|
+
/* @__PURE__ */ f(v, { children: [/* @__PURE__ */ d(p, {
|
|
198
|
+
variant: "outline",
|
|
199
|
+
onClick: () => c(!1),
|
|
200
|
+
children: "Cancel"
|
|
201
|
+
}), /* @__PURE__ */ d(p, {
|
|
202
|
+
onClick: () => void M(),
|
|
203
|
+
disabled: !j,
|
|
204
|
+
children: O.loading ? "Saving…" : "Save"
|
|
205
|
+
})] })
|
|
206
|
+
]
|
|
207
|
+
})]
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function j({ keyResultId: r, keyResultTitle: s, linked: l }) {
|
|
211
|
+
let [m, h] = u(T, D), [g, _] = u(E, D), [v, y] = a(!1), [b, x] = a(""), [S, C] = a(null), w = h.loading || _.loading, O = async (t, n, i) => {
|
|
212
|
+
C(null);
|
|
213
|
+
try {
|
|
214
|
+
await e(t({ variables: {
|
|
215
|
+
keyResultId: r,
|
|
216
|
+
workItemId: n
|
|
217
|
+
} })), i?.();
|
|
218
|
+
} catch (e) {
|
|
219
|
+
C(e instanceof Error ? e.message : "The link could not be changed.");
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
return /* @__PURE__ */ f("div", {
|
|
223
|
+
className: "space-y-2",
|
|
224
|
+
"data-testid": "kr-work-items",
|
|
225
|
+
children: [
|
|
226
|
+
l.length > 0 && /* @__PURE__ */ d("ul", {
|
|
227
|
+
className: "space-y-1",
|
|
228
|
+
children: l.map((e) => /* @__PURE__ */ f("li", {
|
|
229
|
+
className: "flex items-center justify-between gap-2 text-xs",
|
|
230
|
+
children: [/* @__PURE__ */ f("span", {
|
|
231
|
+
className: "truncate",
|
|
232
|
+
children: [
|
|
233
|
+
/* @__PURE__ */ d("span", {
|
|
234
|
+
className: "font-medium",
|
|
235
|
+
children: e.key
|
|
236
|
+
}),
|
|
237
|
+
" ",
|
|
238
|
+
e.title,
|
|
239
|
+
" ",
|
|
240
|
+
/* @__PURE__ */ f("span", {
|
|
241
|
+
className: "text-muted-foreground",
|
|
242
|
+
children: [
|
|
243
|
+
"(",
|
|
244
|
+
i(e.status),
|
|
245
|
+
")"
|
|
246
|
+
]
|
|
247
|
+
})
|
|
248
|
+
]
|
|
249
|
+
}), /* @__PURE__ */ d(p, {
|
|
250
|
+
size: "sm",
|
|
251
|
+
variant: "ghost",
|
|
252
|
+
className: "h-6 px-2",
|
|
253
|
+
"aria-label": `Unlink ${e.key} from ${s}`,
|
|
254
|
+
disabled: w,
|
|
255
|
+
onClick: () => void O(g, e.id),
|
|
256
|
+
children: /* @__PURE__ */ d(c, { className: "h-3 w-3" })
|
|
257
|
+
})]
|
|
258
|
+
}, e.id))
|
|
259
|
+
}),
|
|
260
|
+
v ? /* @__PURE__ */ f("div", {
|
|
261
|
+
className: "space-y-2 rounded-md border border-input p-2",
|
|
262
|
+
children: [/* @__PURE__ */ d(t, {
|
|
263
|
+
id: `kr-link-${r}`,
|
|
264
|
+
value: b,
|
|
265
|
+
onChange: x,
|
|
266
|
+
exclude: l.map((e) => e.id)
|
|
267
|
+
}), /* @__PURE__ */ f("div", {
|
|
268
|
+
className: "flex gap-2",
|
|
269
|
+
children: [/* @__PURE__ */ d(p, {
|
|
270
|
+
size: "sm",
|
|
271
|
+
disabled: !b || w,
|
|
272
|
+
onClick: () => void O(m, b, () => {
|
|
273
|
+
x(""), y(!1);
|
|
274
|
+
}),
|
|
275
|
+
children: "Link"
|
|
276
|
+
}), /* @__PURE__ */ d(p, {
|
|
277
|
+
size: "sm",
|
|
278
|
+
variant: "outline",
|
|
279
|
+
onClick: () => y(!1),
|
|
280
|
+
children: "Cancel"
|
|
281
|
+
})]
|
|
282
|
+
})]
|
|
283
|
+
}) : /* @__PURE__ */ f(p, {
|
|
284
|
+
size: "sm",
|
|
285
|
+
variant: "outline",
|
|
286
|
+
onClick: () => y(!0),
|
|
287
|
+
children: [/* @__PURE__ */ d(o, { className: "mr-1 h-4 w-4" }), "Link work item"]
|
|
288
|
+
}),
|
|
289
|
+
S && /* @__PURE__ */ d(n, { message: S })
|
|
290
|
+
]
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
export { j as KeyResultWorkItems, O as ObjectiveHeaderControls };
|
|
295
|
+
|
|
296
|
+
//# sourceMappingURL=ObjectiveControls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObjectiveControls.js","names":[],"sources":["../../../src/planmagnet/controls/ObjectiveControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { Link2, Pencil, X } from 'lucide-react';\nimport { throwOnError } from '../utils/mutationResult';\nimport { endBeforeStart } from '../utils/formValidation';\nimport { ErrorLine } from './ConfirmButton';\nimport { WorkItemSearchSelect } from './Pickers';\nimport { enumLabel } from './WorkItemControls';\n\n/**\n * Objective controls (BOFF-7956/7963). Objectives were created ACTIVE and could never be completed, cancelled or\n * edited, so the Draft/Completed/Cancelled tabs could only be empty; and key results could not be linked to work\n * items, so the linked-work-item rollup never appeared.\n */\n\nexport const OBJECTIVE_STATUSES = ['DRAFT', 'ACTIVE', 'COMPLETED', 'CANCELLED'] as const;\n\nconst OBJECTIVE_QUERIES = ['GetObjectiveDetail', 'GetObjectives'];\n\nconst UPDATE = gql`\n mutation ObjectiveControlsUpdate($id: ID!, $input: PlanmagnetUpdateObjectiveInput!) {\n planmagnetUpdateObjective(id: $id, input: $input) {\n id\n title\n description\n periodLabel\n startDate\n endDate\n status\n }\n }\n`;\nconst LINK = gql`\n mutation ObjectiveControlsLinkWorkItem($keyResultId: ID!, $workItemId: ID!) {\n planmagnetLinkKeyResultToWorkItem(keyResultId: $keyResultId, workItemId: $workItemId) {\n id\n }\n }\n`;\nconst UNLINK = gql`\n mutation ObjectiveControlsUnlinkWorkItem($keyResultId: ID!, $workItemId: ID!) {\n planmagnetUnlinkKeyResultFromWorkItem(keyResultId: $keyResultId, workItemId: $workItemId)\n }\n`;\n\nconst refetch = { refetchQueries: OBJECTIVE_QUERIES, awaitRefetchQueries: true };\n\nexport interface ObjectiveValues {\n id: string;\n title: string;\n description: string | null;\n periodLabel: string;\n startDate: string | null;\n endDate: string | null;\n status: string;\n}\n\n/** Status select and Edit for the objective header. */\nexport function ObjectiveHeaderControls({ objective }: { objective: ObjectiveValues }) {\n const [update, state] = useMutation(UPDATE, refetch);\n const [error, setError] = useState<string | null>(null);\n const statuses = (OBJECTIVE_STATUSES as readonly string[]).includes(objective.status)\n ? OBJECTIVE_STATUSES\n : [objective.status, ...OBJECTIVE_STATUSES];\n const onStatus = async (status: string) => {\n setError(null);\n try {\n await throwOnError(update({ variables: { id: objective.id, input: { status } } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The status could not be changed.');\n }\n };\n return (\n <div className=\"flex flex-col items-end gap-1\">\n <div className=\"flex items-center gap-2\">\n <select\n aria-label=\"Objective status\"\n data-testid=\"objective-status\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60\"\n value={objective.status}\n disabled={state.loading}\n onChange={(e) => {\n if (e.target.value !== objective.status) void onStatus(e.target.value);\n }}\n >\n {statuses.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n <ObjectiveEditDialog objective={objective} />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\nconst day = (iso: string | null) => (iso ? iso.slice(0, 10) : '');\n\nfunction ObjectiveEditDialog({ objective }: { objective: ObjectiveValues }) {\n const initial = {\n title: objective.title,\n description: objective.description ?? '',\n periodLabel: objective.periodLabel,\n startDate: day(objective.startDate),\n endDate: day(objective.endDate),\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n const [update, state] = useMutation(UPDATE, refetch);\n const invalidDates = endBeforeStart(form.startDate, form.endDate);\n const canSave =\n form.title.trim() !== '' && form.periodLabel.trim() !== '' && !invalidDates && !state.loading;\n\n const save = async () => {\n setError(null);\n const input: Record<string, unknown> = {};\n if (form.title.trim() !== initial.title) input.title = form.title.trim();\n if (form.periodLabel.trim() !== initial.periodLabel)\n input.periodLabel = form.periodLabel.trim();\n if (form.description.trim() !== initial.description.trim())\n input.description = form.description.trim() || null;\n if (form.startDate !== initial.startDate) input.startDate = form.startDate || null;\n if (form.endDate !== initial.endDate) input.endDate = form.endDate || null;\n if (Object.keys(input).length === 0) {\n setOpen(false);\n return;\n }\n try {\n await throwOnError(update({ variables: { id: objective.id, input } }));\n setOpen(false);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The objective could not be saved.');\n }\n };\n\n const label = 'text-sm font-medium';\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 <Button size=\"sm\" variant=\"outline\" data-testid=\"objective-edit\">\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit objective</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Change the title, period or description.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor=\"objective-edit-title\" className={label}>\n Title\n </label>\n <Input\n id=\"objective-edit-title\"\n className=\"mt-1\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"objective-edit-period\" className={label}>\n Period\n </label>\n <Input\n id=\"objective-edit-period\"\n className=\"mt-1\"\n placeholder=\"e.g. Q4 2026\"\n value={form.periodLabel}\n onChange={(e) => setForm({ ...form, periodLabel: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"objective-edit-start\" className={label}>\n Start date\n </label>\n <Input\n id=\"objective-edit-start\"\n type=\"date\"\n className=\"mt-1\"\n value={form.startDate}\n max={form.endDate || undefined}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"objective-edit-end\" className={label}>\n End date\n </label>\n <Input\n id=\"objective-edit-end\"\n type=\"date\"\n className=\"mt-1\"\n value={form.endDate}\n min={form.startDate || undefined}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n />\n </div>\n </div>\n {invalidDates && <ErrorLine message=\"The end date is before the start date.\" />}\n <div>\n <label htmlFor=\"objective-edit-description\" className={label}>\n Description\n </label>\n <textarea\n id=\"objective-edit-description\"\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button onClick={() => void save()} disabled={!canSave}>\n {state.loading ? 'Saving…' : 'Save'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n/** The work items a key result is linked to, with unlink, and a picker to link another. */\nexport function KeyResultWorkItems({\n keyResultId,\n keyResultTitle,\n linked,\n}: {\n keyResultId: string;\n keyResultTitle: string;\n linked: { id: string; key: string; title: string; status: string }[];\n}) {\n const [link, linkState] = useMutation(LINK, refetch);\n const [unlink, unlinkState] = useMutation(UNLINK, refetch);\n const [adding, setAdding] = useState(false);\n const [choice, setChoice] = useState('');\n const [error, setError] = useState<string | null>(null);\n const busy = linkState.loading || unlinkState.loading;\n\n const run = async (mutate: typeof link, workItemId: string, after?: () => void) => {\n setError(null);\n try {\n await throwOnError(mutate({ variables: { keyResultId, workItemId } }));\n after?.();\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The link could not be changed.');\n }\n };\n\n return (\n <div className=\"space-y-2\" data-testid=\"kr-work-items\">\n {linked.length > 0 && (\n <ul className=\"space-y-1\">\n {linked.map((w) => (\n <li key={w.id} className=\"flex items-center justify-between gap-2 text-xs\">\n <span className=\"truncate\">\n <span className=\"font-medium\">{w.key}</span> {w.title}{' '}\n <span className=\"text-muted-foreground\">({enumLabel(w.status)})</span>\n </span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 px-2\"\n aria-label={`Unlink ${w.key} from ${keyResultTitle}`}\n disabled={busy}\n onClick={() => void run(unlink, w.id)}\n >\n <X className=\"h-3 w-3\" />\n </Button>\n </li>\n ))}\n </ul>\n )}\n {adding ? (\n <div className=\"space-y-2 rounded-md border border-input p-2\">\n <WorkItemSearchSelect\n id={`kr-link-${keyResultId}`}\n value={choice}\n onChange={setChoice}\n exclude={linked.map((w) => w.id)}\n />\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n disabled={!choice || busy}\n onClick={() =>\n void run(link, choice, () => {\n setChoice('');\n setAdding(false);\n })\n }\n >\n Link\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAdding(false)}>\n Cancel\n </Button>\n </div>\n </div>\n ) : (\n <Button size=\"sm\" variant=\"outline\" onClick={() => setAdding(true)}>\n <Link2 className=\"mr-1 h-4 w-4\" />\n Link work item\n </Button>\n )}\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** A project select. '' means "no project" when `noneLabel` is given. */
|
|
2
|
+
export declare function ProjectSelect({ id, value, onChange, noneLabel, }: {
|
|
3
|
+
id?: string;
|
|
4
|
+
value: string;
|
|
5
|
+
onChange: (projectId: string) => void;
|
|
6
|
+
noneLabel?: string;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* Search, then pick one work item. The server matches the key, title and description. `exclude` hides items that are
|
|
10
|
+
* already linked (the result says how many more exist, so an empty list never claims "nothing left" falsely).
|
|
11
|
+
*/
|
|
12
|
+
export declare function WorkItemSearchSelect({ id, projectId, value, onChange, exclude, }: {
|
|
13
|
+
id: string;
|
|
14
|
+
projectId?: string;
|
|
15
|
+
value: string;
|
|
16
|
+
onChange: (workItemId: string) => void;
|
|
17
|
+
exclude?: string[];
|
|
18
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
/** Search, then pick one doc. */
|
|
20
|
+
export declare function DocSearchSelect({ id, value, onChange, exclude, }: {
|
|
21
|
+
id: string;
|
|
22
|
+
value: string;
|
|
23
|
+
onChange: (docId: string) => void;
|
|
24
|
+
exclude?: string[];
|
|
25
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
//# sourceMappingURL=Pickers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pickers.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/Pickers.tsx"],"names":[],"mappings":"AA2BA,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE;IACD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CA8BA;AAeD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,EAAE,EACF,SAAS,EACT,KAAK,EACL,QAAQ,EACR,OAAY,GACb,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,2CAqDA;AAcD,iCAAiC;AACjC,wBAAgB,eAAe,CAAC,EAC9B,EAAE,EACF,KAAK,EACL,QAAQ,EACR,OAAY,GACb,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,2CA8CA"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { useDeferredValue as t, useState as n } from "react";
|
|
3
|
+
import { gql as r } from "@apollo/client";
|
|
4
|
+
import { useQuery as i } from "@apollo/client/react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
import { Input as s } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
//#region src/planmagnet/controls/Pickers.tsx
|
|
8
|
+
var c = "w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60", l = r`
|
|
9
|
+
query PickerProjects($workspaceId: ID!) {
|
|
10
|
+
planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {
|
|
11
|
+
items {
|
|
12
|
+
id
|
|
13
|
+
name
|
|
14
|
+
}
|
|
15
|
+
totalCount
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
function u({ id: t, value: n, onChange: r, noneLabel: s }) {
|
|
20
|
+
let { workspaceId: u } = e(), { data: d, loading: f, error: p } = i(l, {
|
|
21
|
+
variables: { workspaceId: u ?? "" },
|
|
22
|
+
skip: !u
|
|
23
|
+
}), m = d?.planmagnetProjects.items ?? [];
|
|
24
|
+
return /* @__PURE__ */ o("select", {
|
|
25
|
+
id: t,
|
|
26
|
+
className: c,
|
|
27
|
+
value: n,
|
|
28
|
+
onChange: (e) => r(e.target.value),
|
|
29
|
+
children: [
|
|
30
|
+
s !== void 0 && /* @__PURE__ */ a("option", {
|
|
31
|
+
value: "",
|
|
32
|
+
children: s
|
|
33
|
+
}),
|
|
34
|
+
m.length === 0 && s === void 0 && /* @__PURE__ */ a("option", {
|
|
35
|
+
value: "",
|
|
36
|
+
children: f && !d ? "Loading projects…" : p ? "Projects could not be loaded" : "No projects yet"
|
|
37
|
+
}),
|
|
38
|
+
m.map((e) => /* @__PURE__ */ a("option", {
|
|
39
|
+
value: e.id,
|
|
40
|
+
children: e.name
|
|
41
|
+
}, e.id))
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
var d = r`
|
|
46
|
+
query PickerWorkItems($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {
|
|
47
|
+
planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: { first: 20 }) {
|
|
48
|
+
items {
|
|
49
|
+
id
|
|
50
|
+
key
|
|
51
|
+
title
|
|
52
|
+
}
|
|
53
|
+
totalCount
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
function f({ id: r, projectId: l, value: u, onChange: f, exclude: p = [] }) {
|
|
58
|
+
let { workspaceId: m } = e(), [h, g] = n(""), _ = t(h.trim()), { data: v, loading: y } = i(d, {
|
|
59
|
+
variables: {
|
|
60
|
+
workspaceId: m ?? "",
|
|
61
|
+
filter: {
|
|
62
|
+
..._ && { search: _ },
|
|
63
|
+
...l && { projectId: l }
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
skip: !m
|
|
67
|
+
}), b = v?.planmagnetWorkItems.items ?? [], x = b.filter((e) => !p.includes(e.id)), S = v?.planmagnetWorkItems.totalCount ?? 0;
|
|
68
|
+
return /* @__PURE__ */ o("div", {
|
|
69
|
+
className: "space-y-1",
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ a(s, {
|
|
72
|
+
"aria-label": "Search work items",
|
|
73
|
+
placeholder: "Search by key or title",
|
|
74
|
+
value: h,
|
|
75
|
+
onChange: (e) => g(e.target.value)
|
|
76
|
+
}),
|
|
77
|
+
/* @__PURE__ */ o("select", {
|
|
78
|
+
id: r,
|
|
79
|
+
className: c,
|
|
80
|
+
value: u,
|
|
81
|
+
onChange: (e) => f(e.target.value),
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ a("option", {
|
|
84
|
+
value: "",
|
|
85
|
+
children: y && !v ? "Searching…" : x.length ? "Choose a work item" : "No match"
|
|
86
|
+
}),
|
|
87
|
+
u && !x.some((e) => e.id === u) && /* @__PURE__ */ a("option", {
|
|
88
|
+
value: u,
|
|
89
|
+
children: "Currently linked work item"
|
|
90
|
+
}),
|
|
91
|
+
x.map((e) => /* @__PURE__ */ o("option", {
|
|
92
|
+
value: e.id,
|
|
93
|
+
children: [
|
|
94
|
+
e.key,
|
|
95
|
+
" — ",
|
|
96
|
+
e.title
|
|
97
|
+
]
|
|
98
|
+
}, e.id))
|
|
99
|
+
]
|
|
100
|
+
}),
|
|
101
|
+
S > b.length && /* @__PURE__ */ o("p", {
|
|
102
|
+
className: "text-xs text-muted-foreground",
|
|
103
|
+
children: [
|
|
104
|
+
"Showing ",
|
|
105
|
+
b.length,
|
|
106
|
+
" of ",
|
|
107
|
+
S,
|
|
108
|
+
" — type to narrow the list."
|
|
109
|
+
]
|
|
110
|
+
})
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
var p = r`
|
|
115
|
+
query PickerDocs($workspaceId: String, $filter: PlanmagnetDocFilter) {
|
|
116
|
+
planmagnetDocs(workspaceId: $workspaceId, filter: $filter, pagination: { first: 20 }) {
|
|
117
|
+
items {
|
|
118
|
+
id
|
|
119
|
+
title
|
|
120
|
+
}
|
|
121
|
+
totalCount
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
function m({ id: r, value: l, onChange: u, exclude: d = [] }) {
|
|
126
|
+
let { workspaceId: f } = e(), [m, h] = n(""), g = t(m.trim()), { data: _, loading: v } = i(p, {
|
|
127
|
+
variables: {
|
|
128
|
+
workspaceId: f ?? "",
|
|
129
|
+
filter: g ? { search: g } : void 0
|
|
130
|
+
},
|
|
131
|
+
skip: !f
|
|
132
|
+
}), y = _?.planmagnetDocs.items ?? [], b = y.filter((e) => !d.includes(e.id)), x = _?.planmagnetDocs.totalCount ?? 0;
|
|
133
|
+
return /* @__PURE__ */ o("div", {
|
|
134
|
+
className: "space-y-1",
|
|
135
|
+
children: [
|
|
136
|
+
/* @__PURE__ */ a(s, {
|
|
137
|
+
"aria-label": "Search docs",
|
|
138
|
+
placeholder: "Search docs by title",
|
|
139
|
+
value: m,
|
|
140
|
+
onChange: (e) => h(e.target.value)
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ o("select", {
|
|
143
|
+
id: r,
|
|
144
|
+
className: c,
|
|
145
|
+
value: l,
|
|
146
|
+
onChange: (e) => u(e.target.value),
|
|
147
|
+
children: [/* @__PURE__ */ a("option", {
|
|
148
|
+
value: "",
|
|
149
|
+
children: v && !_ ? "Searching…" : b.length ? "Choose a doc" : "No match"
|
|
150
|
+
}), b.map((e) => /* @__PURE__ */ a("option", {
|
|
151
|
+
value: e.id,
|
|
152
|
+
children: e.title
|
|
153
|
+
}, e.id))]
|
|
154
|
+
}),
|
|
155
|
+
x > y.length && /* @__PURE__ */ o("p", {
|
|
156
|
+
className: "text-xs text-muted-foreground",
|
|
157
|
+
children: [
|
|
158
|
+
"Showing ",
|
|
159
|
+
y.length,
|
|
160
|
+
" of ",
|
|
161
|
+
x,
|
|
162
|
+
" — type to narrow the list."
|
|
163
|
+
]
|
|
164
|
+
})
|
|
165
|
+
]
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
export { m as DocSearchSelect, u as ProjectSelect, f as WorkItemSearchSelect };
|
|
170
|
+
|
|
171
|
+
//# sourceMappingURL=Pickers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pickers.js","names":[],"sources":["../../../src/planmagnet/controls/Pickers.tsx"],"sourcesContent":["import { useDeferredValue, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\n/**\n * Record pickers shared by forms and dialogs: a project select, and search-then-pick selects for work items and\n * docs. Values are record ids. Search runs on the server (never \"the first page, filtered in the browser\"), so a\n * record past the first page can still be found.\n */\n\nconst selectClass =\n 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60';\n\nconst PICKER_PROJECTS = gql`\n query PickerProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n totalCount\n }\n }\n`;\n\n/** A project select. '' means \"no project\" when `noneLabel` is given. */\nexport function ProjectSelect({\n id,\n value,\n onChange,\n noneLabel,\n}: {\n id?: string;\n value: string;\n onChange: (projectId: string) => void;\n noneLabel?: string;\n}) {\n const { workspaceId } = usePlanMagnet();\n const { data, loading, error } = useQuery<{\n planmagnetProjects: { items: { id: string; name: string }[]; totalCount: number };\n }>(PICKER_PROJECTS, { variables: { workspaceId: workspaceId ?? '' }, skip: !workspaceId });\n const projects = data?.planmagnetProjects.items ?? [];\n return (\n <select\n id={id}\n className={selectClass}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n >\n {noneLabel !== undefined && <option value=\"\">{noneLabel}</option>}\n {projects.length === 0 && noneLabel === undefined && (\n <option value=\"\">\n {loading && !data\n ? 'Loading projects…'\n : error\n ? 'Projects could not be loaded'\n : 'No projects yet'}\n </option>\n )}\n {projects.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n </select>\n );\n}\n\nconst PICKER_WORK_ITEMS = gql`\n query PickerWorkItems($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {\n planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: { first: 20 }) {\n items {\n id\n key\n title\n }\n totalCount\n }\n }\n`;\n\n/**\n * Search, then pick one work item. The server matches the key, title and description. `exclude` hides items that are\n * already linked (the result says how many more exist, so an empty list never claims \"nothing left\" falsely).\n */\nexport function WorkItemSearchSelect({\n id,\n projectId,\n value,\n onChange,\n exclude = [],\n}: {\n id: string;\n projectId?: string;\n value: string;\n onChange: (workItemId: string) => void;\n exclude?: string[];\n}) {\n const { workspaceId } = usePlanMagnet();\n const [search, setSearch] = useState('');\n const deferred = useDeferredValue(search.trim());\n const { data, loading } = useQuery<{\n planmagnetWorkItems: {\n items: { id: string; key: string; title: string }[];\n totalCount: number;\n };\n }>(PICKER_WORK_ITEMS, {\n variables: {\n workspaceId: workspaceId ?? '',\n filter: { ...(deferred && { search: deferred }), ...(projectId && { projectId }) },\n },\n skip: !workspaceId,\n });\n const found = data?.planmagnetWorkItems.items ?? [];\n const items = found.filter((w) => !exclude.includes(w.id));\n const total = data?.planmagnetWorkItems.totalCount ?? 0;\n return (\n <div className=\"space-y-1\">\n <Input\n aria-label=\"Search work items\"\n placeholder=\"Search by key or title\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n <select\n id={id}\n className={selectClass}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n >\n <option value=\"\">\n {loading && !data ? 'Searching…' : items.length ? 'Choose a work item' : 'No match'}\n </option>\n {/* The current value stays selectable (and visible) when it is not in this page of results. */}\n {value && !items.some((w) => w.id === value) && (\n <option value={value}>Currently linked work item</option>\n )}\n {items.map((w) => (\n <option key={w.id} value={w.id}>\n {w.key} — {w.title}\n </option>\n ))}\n </select>\n {total > found.length && (\n <p className=\"text-xs text-muted-foreground\">\n Showing {found.length} of {total} — type to narrow the list.\n </p>\n )}\n </div>\n );\n}\n\nconst PICKER_DOCS = gql`\n query PickerDocs($workspaceId: String, $filter: PlanmagnetDocFilter) {\n planmagnetDocs(workspaceId: $workspaceId, filter: $filter, pagination: { first: 20 }) {\n items {\n id\n title\n }\n totalCount\n }\n }\n`;\n\n/** Search, then pick one doc. */\nexport function DocSearchSelect({\n id,\n value,\n onChange,\n exclude = [],\n}: {\n id: string;\n value: string;\n onChange: (docId: string) => void;\n exclude?: string[];\n}) {\n const { workspaceId } = usePlanMagnet();\n const [search, setSearch] = useState('');\n const deferred = useDeferredValue(search.trim());\n const { data, loading } = useQuery<{\n planmagnetDocs: { items: { id: string; title: string }[]; totalCount: number };\n }>(PICKER_DOCS, {\n variables: {\n workspaceId: workspaceId ?? '',\n filter: deferred ? { search: deferred } : undefined,\n },\n skip: !workspaceId,\n });\n const found = data?.planmagnetDocs.items ?? [];\n const docs = found.filter((d) => !exclude.includes(d.id));\n const total = data?.planmagnetDocs.totalCount ?? 0;\n return (\n <div className=\"space-y-1\">\n <Input\n aria-label=\"Search docs\"\n placeholder=\"Search docs by title\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n <select\n id={id}\n className={selectClass}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n >\n <option value=\"\">\n {loading && !data ? 'Searching…' : docs.length ? 'Choose a doc' : 'No match'}\n </option>\n {docs.map((d) => (\n <option key={d.id} value={d.id}>\n {d.title}\n </option>\n ))}\n </select>\n {total > found.length && (\n <p className=\"text-xs text-muted-foreground\">\n Showing {found.length} of {total} — type to narrow the list.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
|
|
2
2
|
import { throwOnError as t } from "../utils/mutationResult.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { ConfirmButton as n, ErrorLine as r } from "./ConfirmButton.js";
|
|
4
|
+
import { enumLabel as i } from "./WorkItemControls.js";
|
|
5
|
+
import { detailPath as a } from "../utils/rowNavigation.js";
|
|
6
6
|
import { useState as o } from "react";
|
|
7
7
|
import { Pencil as s, Trash2 as c } from "lucide-react";
|
|
8
8
|
import { gql as l } from "@apollo/client";
|
|
@@ -73,17 +73,17 @@ function v({ projectId: l, name: v, status: y }) {
|
|
|
73
73
|
},
|
|
74
74
|
children: O.map((e) => /* @__PURE__ */ d("option", {
|
|
75
75
|
value: e,
|
|
76
|
-
children:
|
|
76
|
+
children: i(e)
|
|
77
77
|
}, e))
|
|
78
78
|
}),
|
|
79
79
|
/* @__PURE__ */ f(p, {
|
|
80
80
|
size: "sm",
|
|
81
81
|
variant: "outline",
|
|
82
82
|
"data-testid": "project-edit",
|
|
83
|
-
onClick: () => b(
|
|
83
|
+
onClick: () => b(a("/projects/edit-project-page", l)),
|
|
84
84
|
children: [/* @__PURE__ */ d(s, { className: "mr-1 h-4 w-4" }), "Edit"]
|
|
85
85
|
}),
|
|
86
|
-
/* @__PURE__ */ d(
|
|
86
|
+
/* @__PURE__ */ d(n, {
|
|
87
87
|
label: "Delete",
|
|
88
88
|
variant: "outline",
|
|
89
89
|
icon: /* @__PURE__ */ d(c, { className: "mr-1 h-4 w-4" }),
|
|
@@ -95,7 +95,7 @@ function v({ projectId: l, name: v, status: y }) {
|
|
|
95
95
|
onConfirm: () => void A()
|
|
96
96
|
})
|
|
97
97
|
]
|
|
98
|
-
}), T && /* @__PURE__ */ d(
|
|
98
|
+
}), T && /* @__PURE__ */ d(r, { message: T })]
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
101
|
//#endregion
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release controls (BOFF-7952). A release started in Planning and could never be released: status showed only as
|
|
3
|
+
* badges, the Edit Release page was linked from nowhere, and nothing deleted a release. The Quality Gates card even
|
|
4
|
+
* said the release "cannot be released until the gates pass" — with nothing to click once they had. The service
|
|
5
|
+
* refuses RELEASED while a gate is open; that reason is shown here.
|
|
6
|
+
*/
|
|
7
|
+
/** Every ReleaseStatus value (the Edit page's list omitted STAGING — BOFF-7985). */
|
|
8
|
+
export declare const RELEASE_STATUSES: readonly ["DRAFT", "PLANNING", "IN_PROGRESS", "READY", "STAGING", "RELEASED", "ROLLED_BACK", "CANCELLED"];
|
|
9
|
+
export declare function ReleaseHeaderActions({ releaseId, version, status, }: {
|
|
10
|
+
releaseId: string;
|
|
11
|
+
version: string;
|
|
12
|
+
status: string;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=ReleaseControls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReleaseControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/ReleaseControls.tsx"],"names":[],"mappings":"AAWA;;;;;GAKG;AAEH,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,2GASnB,CAAC;AA0BX,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,OAAO,EACP,MAAM,GACP,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,2CA4EA"}
|