@burdenoff/microfe-planmagnet 2026.927.2 → 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.
Files changed (106) hide show
  1. package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +128 -124
  3. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
  4. package/dist/planmagnet/controls/ConfirmButton.d.ts +25 -0
  5. package/dist/planmagnet/controls/ConfirmButton.d.ts.map +1 -0
  6. package/dist/planmagnet/controls/ConfirmButton.js +30 -0
  7. package/dist/planmagnet/controls/ConfirmButton.js.map +1 -0
  8. package/dist/planmagnet/controls/FeatureControls.d.ts +26 -0
  9. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -0
  10. package/dist/planmagnet/controls/FeatureControls.js +318 -0
  11. package/dist/planmagnet/controls/FeatureControls.js.map +1 -0
  12. package/dist/planmagnet/controls/MilestoneControls.d.ts +17 -0
  13. package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -0
  14. package/dist/planmagnet/controls/MilestoneControls.js +200 -0
  15. package/dist/planmagnet/controls/MilestoneControls.js.map +1 -0
  16. package/dist/planmagnet/controls/ObjectiveControls.d.ts +31 -0
  17. package/dist/planmagnet/controls/ObjectiveControls.d.ts.map +1 -0
  18. package/dist/planmagnet/controls/ObjectiveControls.js +296 -0
  19. package/dist/planmagnet/controls/ObjectiveControls.js.map +1 -0
  20. package/dist/planmagnet/controls/Pickers.d.ts +26 -0
  21. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -0
  22. package/dist/planmagnet/controls/Pickers.js +171 -0
  23. package/dist/planmagnet/controls/Pickers.js.map +1 -0
  24. package/dist/planmagnet/controls/ProjectControls.d.ts +13 -0
  25. package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -0
  26. package/dist/planmagnet/controls/ProjectControls.js +104 -0
  27. package/dist/planmagnet/controls/ProjectControls.js.map +1 -0
  28. package/dist/planmagnet/controls/ReleaseControls.d.ts +14 -0
  29. package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -0
  30. package/dist/planmagnet/controls/ReleaseControls.js +108 -0
  31. package/dist/planmagnet/controls/ReleaseControls.js.map +1 -0
  32. package/dist/planmagnet/controls/WorkItemControls.d.ts +52 -0
  33. package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -0
  34. package/dist/planmagnet/controls/WorkItemControls.js +198 -0
  35. package/dist/planmagnet/controls/WorkItemControls.js.map +1 -0
  36. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  37. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +188 -154
  38. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  39. package/dist/planmagnet/pages/DocsDocsPagePage.js +4 -4
  40. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
  41. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js +63 -56
  42. package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js.map +1 -1
  43. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
  44. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +229 -214
  45. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
  46. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  47. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +142 -114
  48. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  49. package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts.map +1 -1
  50. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +134 -105
  51. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js.map +1 -1
  52. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  53. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +102 -108
  54. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  55. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  56. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +119 -94
  57. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  58. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +97 -90
  60. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  62. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +107 -106
  63. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  64. package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
  65. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +2 -0
  66. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  67. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
  68. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +150 -111
  69. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
  70. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
  71. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +116 -93
  72. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
  73. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
  74. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +55 -63
  75. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
  76. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +4 -4
  77. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  78. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +134 -132
  79. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  80. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  81. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +6 -5
  82. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  83. package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
  84. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +167 -116
  85. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  86. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  87. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +139 -107
  88. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  89. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  90. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +205 -158
  91. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  92. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  93. package/dist/planmagnet/pages/TasksTasksPagePage.js +1 -0
  94. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  95. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  96. package/dist/planmagnet/pages/TeamTeamPagePage.js +1 -1
  97. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  98. package/dist/planmagnet/people/People.js +1 -1
  99. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts +34 -0
  100. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -0
  101. package/dist/planmagnet/roadmaps/RoadmapControls.js +301 -0
  102. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -0
  103. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  104. package/dist/planmagnet/sprints/SprintControls.js +194 -215
  105. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  106. package/package.json +1 -1
@@ -0,0 +1,200 @@
1
+ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
+ import { throwOnError as t } from "../utils/mutationResult.js";
3
+ import { ConfirmButton as n, ErrorLine as r } from "./ConfirmButton.js";
4
+ import { enumLabel as i } from "./WorkItemControls.js";
5
+ import { useState as a } from "react";
6
+ import { Pencil as o, Trash2 as s } from "lucide-react";
7
+ import { gql as c } from "@apollo/client";
8
+ import { useMutation as l } from "@apollo/client/react";
9
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
10
+ import { Button as f, Input as p, ResponsiveDialog as m, ResponsiveDialogContent as h, ResponsiveDialogDescription as g, ResponsiveDialogFooter as _, ResponsiveDialogHeader as v, ResponsiveDialogTitle as y, ResponsiveDialogTrigger as b } from "@burdenoff/fe-libs/ui";
11
+ //#region src/planmagnet/controls/MilestoneControls.tsx
12
+ var x = [
13
+ "PENDING",
14
+ "IN_PROGRESS",
15
+ "COMPLETED",
16
+ "MISSED",
17
+ "CANCELLED"
18
+ ], S = [
19
+ "GetMilestonesForDetail",
20
+ "GetMilestones",
21
+ "GetDashboardData",
22
+ "GetAnalyticsData"
23
+ ], C = c`
24
+ mutation MilestoneControlsUpdate($id: ID!, $input: PlanmagnetUpdateMilestoneInput!) {
25
+ planmagnetUpdateMilestone(id: $id, input: $input) {
26
+ id
27
+ name
28
+ description
29
+ status
30
+ dueDate
31
+ completedAt
32
+ }
33
+ }
34
+ `, w = c`
35
+ mutation MilestoneControlsDelete($id: ID!) {
36
+ planmagnetDeleteMilestone(id: $id)
37
+ }
38
+ `;
39
+ function T({ milestone: o }) {
40
+ let c = e(), [f, p] = l(C, {
41
+ refetchQueries: S,
42
+ awaitRefetchQueries: !0
43
+ }), [m, h] = l(w, { refetchQueries: S.filter((e) => e !== "GetMilestonesForDetail") }), [g, _] = a(null), v = p.loading || h.loading, y = x.includes(o.status) ? x : [o.status, ...x], b = async (e) => {
44
+ _(null);
45
+ try {
46
+ await t(f({ variables: {
47
+ id: o.id,
48
+ input: { status: e }
49
+ } }));
50
+ } catch (e) {
51
+ _(e instanceof Error ? e.message : "The status could not be changed.");
52
+ }
53
+ }, T = async () => {
54
+ _(null);
55
+ try {
56
+ await t(m({ variables: { id: o.id } })), c("/projects/milestones-page");
57
+ } catch (e) {
58
+ _(e instanceof Error ? e.message : "The milestone could not be deleted.");
59
+ }
60
+ };
61
+ return /* @__PURE__ */ d("div", {
62
+ className: "flex flex-col items-end gap-2",
63
+ children: [/* @__PURE__ */ d("div", {
64
+ className: "flex flex-wrap items-center justify-end gap-2",
65
+ children: [
66
+ /* @__PURE__ */ u("select", {
67
+ "aria-label": "Status",
68
+ "data-testid": "milestone-status",
69
+ className: "rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60",
70
+ value: o.status,
71
+ disabled: v,
72
+ onChange: (e) => {
73
+ e.target.value !== o.status && b(e.target.value);
74
+ },
75
+ children: y.map((e) => /* @__PURE__ */ u("option", {
76
+ value: e,
77
+ children: i(e)
78
+ }, e))
79
+ }),
80
+ /* @__PURE__ */ u(D, { milestone: o }),
81
+ /* @__PURE__ */ u(n, {
82
+ label: "Delete",
83
+ variant: "outline",
84
+ icon: /* @__PURE__ */ u(s, { className: "mr-1 h-4 w-4" }),
85
+ title: `Delete ${o.name}?`,
86
+ description: "The milestone is deleted. This cannot be undone here.",
87
+ confirmLabel: "Delete milestone",
88
+ destructive: !0,
89
+ disabled: v,
90
+ onConfirm: () => void T()
91
+ })
92
+ ]
93
+ }), g && /* @__PURE__ */ u(r, { message: g })]
94
+ });
95
+ }
96
+ var E = (e) => e ? e.slice(0, 10) : "";
97
+ function D({ milestone: e }) {
98
+ let n = {
99
+ name: e.name,
100
+ description: e.description ?? "",
101
+ dueDate: E(e.dueDate)
102
+ }, [i, s] = a(!1), [c, x] = a(n), [w, T] = a(null), [D, O] = l(C, {
103
+ refetchQueries: S,
104
+ awaitRefetchQueries: !0
105
+ }), k = async () => {
106
+ T(null);
107
+ let r = {};
108
+ if (c.name.trim() !== n.name && (r.name = c.name.trim()), c.description.trim() !== n.description.trim() && (r.description = c.description.trim() || null), c.dueDate !== n.dueDate && (r.dueDate = c.dueDate || null), Object.keys(r).length === 0) {
109
+ s(!1);
110
+ return;
111
+ }
112
+ try {
113
+ await t(D({ variables: {
114
+ id: e.id,
115
+ input: r
116
+ } })), s(!1);
117
+ } catch (e) {
118
+ T(e instanceof Error ? e.message : "The milestone could not be saved.");
119
+ }
120
+ };
121
+ return /* @__PURE__ */ d(m, {
122
+ open: i,
123
+ onOpenChange: (e) => {
124
+ s(e), e ? x(n) : T(null);
125
+ },
126
+ children: [/* @__PURE__ */ u(b, {
127
+ asChild: !0,
128
+ children: /* @__PURE__ */ d(f, {
129
+ size: "sm",
130
+ variant: "outline",
131
+ "data-testid": "milestone-edit",
132
+ children: [/* @__PURE__ */ u(o, { className: "mr-1 h-4 w-4" }), "Edit"]
133
+ })
134
+ }), /* @__PURE__ */ d(h, {
135
+ className: "max-w-lg",
136
+ children: [
137
+ /* @__PURE__ */ d(v, { children: [/* @__PURE__ */ u(y, { children: "Edit milestone" }), /* @__PURE__ */ u(g, { children: "Change the name, description or due date." })] }),
138
+ /* @__PURE__ */ d("div", {
139
+ className: "space-y-3",
140
+ children: [
141
+ /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("label", {
142
+ htmlFor: "milestone-edit-name",
143
+ className: "text-sm font-medium",
144
+ children: "Name"
145
+ }), /* @__PURE__ */ u(p, {
146
+ id: "milestone-edit-name",
147
+ className: "mt-1",
148
+ value: c.name,
149
+ onChange: (e) => x({
150
+ ...c,
151
+ name: e.target.value
152
+ })
153
+ })] }),
154
+ /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("label", {
155
+ htmlFor: "milestone-edit-due",
156
+ className: "text-sm font-medium",
157
+ children: "Due date"
158
+ }), /* @__PURE__ */ u(p, {
159
+ id: "milestone-edit-due",
160
+ type: "date",
161
+ className: "mt-1",
162
+ value: c.dueDate,
163
+ onChange: (e) => x({
164
+ ...c,
165
+ dueDate: e.target.value
166
+ })
167
+ })] }),
168
+ /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("label", {
169
+ htmlFor: "milestone-edit-description",
170
+ className: "text-sm font-medium",
171
+ children: "Description"
172
+ }), /* @__PURE__ */ u("textarea", {
173
+ id: "milestone-edit-description",
174
+ className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]",
175
+ value: c.description,
176
+ onChange: (e) => x({
177
+ ...c,
178
+ description: e.target.value
179
+ })
180
+ })] }),
181
+ w && /* @__PURE__ */ u(r, { message: w })
182
+ ]
183
+ }),
184
+ /* @__PURE__ */ d(_, { children: [/* @__PURE__ */ u(f, {
185
+ variant: "outline",
186
+ onClick: () => s(!1),
187
+ children: "Cancel"
188
+ }), /* @__PURE__ */ u(f, {
189
+ onClick: () => void k(),
190
+ disabled: c.name.trim() === "" || O.loading,
191
+ children: O.loading ? "Saving…" : "Save"
192
+ })] })
193
+ ]
194
+ })]
195
+ });
196
+ }
197
+ //#endregion
198
+ export { T as MilestoneHeaderActions };
199
+
200
+ //# sourceMappingURL=MilestoneControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MilestoneControls.js","names":[],"sources":["../../../src/planmagnet/controls/MilestoneControls.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 { Pencil, Trash2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\nimport { ConfirmButton, ErrorLine } from './ConfirmButton';\nimport { enumLabel } from './WorkItemControls';\n\n/**\n * Milestone controls (BOFF-7953/7963). A milestone was created Pending and could never move: no status control,\n * no edit, no delete — so the In progress / Completed / Missed / Cancelled tabs could only be empty, and the\n * \"Completed\" date stayed \"—\". Completing now also records the date (service, 2026-09-27).\n */\n\nexport const MILESTONE_STATUSES = [\n 'PENDING',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'MISSED',\n 'CANCELLED',\n] as const;\n\nconst MILESTONE_QUERIES = [\n 'GetMilestonesForDetail',\n 'GetMilestones',\n 'GetDashboardData',\n 'GetAnalyticsData',\n];\n\nconst UPDATE = gql`\n mutation MilestoneControlsUpdate($id: ID!, $input: PlanmagnetUpdateMilestoneInput!) {\n planmagnetUpdateMilestone(id: $id, input: $input) {\n id\n name\n description\n status\n dueDate\n completedAt\n }\n }\n`;\nconst DELETE = gql`\n mutation MilestoneControlsDelete($id: ID!) {\n planmagnetDeleteMilestone(id: $id)\n }\n`;\n\nexport interface MilestoneValues {\n id: string;\n name: string;\n description: string | null;\n status: string;\n dueDate: string | null;\n}\n\nexport function MilestoneHeaderActions({ milestone }: { milestone: MilestoneValues }) {\n const navigate = usePlanMagnetNavigate();\n const [update, updateState] = useMutation(UPDATE, {\n refetchQueries: MILESTONE_QUERIES,\n awaitRefetchQueries: true,\n });\n const [remove, removeState] = useMutation(DELETE, {\n refetchQueries: MILESTONE_QUERIES.filter((q) => q !== 'GetMilestonesForDetail'),\n });\n const [error, setError] = useState<string | null>(null);\n const busy = updateState.loading || removeState.loading;\n const statuses = (MILESTONE_STATUSES as readonly string[]).includes(milestone.status)\n ? MILESTONE_STATUSES\n : [milestone.status, ...MILESTONE_STATUSES];\n\n const onStatus = async (status: string) => {\n setError(null);\n try {\n await throwOnError(update({ variables: { id: milestone.id, input: { status } } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The status could not be changed.');\n }\n };\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: milestone.id } }));\n navigate('/projects/milestones-page');\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The milestone could not be deleted.');\n }\n };\n\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <select\n aria-label=\"Status\"\n data-testid=\"milestone-status\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60\"\n value={milestone.status}\n disabled={busy}\n onChange={(e) => {\n if (e.target.value !== milestone.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 <MilestoneEditDialog milestone={milestone} />\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${milestone.name}?`}\n description=\"The milestone is deleted. This cannot be undone here.\"\n confirmLabel=\"Delete milestone\"\n destructive\n disabled={busy}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\nconst day = (iso: string | null) => (iso ? iso.slice(0, 10) : '');\n\n/** Name, description and due date. Only changed fields are sent; an emptied field is cleared. */\nfunction MilestoneEditDialog({ milestone }: { milestone: MilestoneValues }) {\n const initial = {\n name: milestone.name,\n description: milestone.description ?? '',\n dueDate: day(milestone.dueDate),\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, {\n refetchQueries: MILESTONE_QUERIES,\n awaitRefetchQueries: true,\n });\n\n const save = async () => {\n setError(null);\n const input: Record<string, unknown> = {};\n if (form.name.trim() !== initial.name) input.name = form.name.trim();\n if (form.description.trim() !== initial.description.trim())\n input.description = form.description.trim() || null;\n if (form.dueDate !== initial.dueDate) input.dueDate = form.dueDate || null;\n if (Object.keys(input).length === 0) {\n setOpen(false);\n return;\n }\n try {\n await throwOnError(update({ variables: { id: milestone.id, input } }));\n setOpen(false);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The milestone could not be saved.');\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 <Button size=\"sm\" variant=\"outline\" data-testid=\"milestone-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 milestone</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Change the name, description or due date.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor=\"milestone-edit-name\" className=\"text-sm font-medium\">\n Name\n </label>\n <Input\n id=\"milestone-edit-name\"\n className=\"mt-1\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"milestone-edit-due\" className=\"text-sm font-medium\">\n Due date\n </label>\n <Input\n id=\"milestone-edit-due\"\n type=\"date\"\n className=\"mt-1\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"milestone-edit-description\" className=\"text-sm font-medium\">\n Description\n </label>\n <textarea\n id=\"milestone-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={form.name.trim() === '' || state.loading}>\n {state.loading ? 'Saving…' : 'Save'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -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"}