@burdenoff/microfe-planmagnet 2026.925.18 → 2026.926.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/generated/wspace-types.d.ts +61 -2
  2. package/dist/generated/wspace-types.d.ts.map +1 -1
  3. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoot.js +90 -79
  5. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  7. package/dist/planmagnet/PlanMagnetRoutes.js +124 -127
  8. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  9. package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
  10. package/dist/planmagnet/create/CreateDialogs.js +149 -124
  11. package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
  12. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  13. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +1 -1
  14. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  15. package/dist/planmagnet/pages/NotFoundPage.d.ts +6 -0
  16. package/dist/planmagnet/pages/NotFoundPage.d.ts.map +1 -0
  17. package/dist/planmagnet/pages/NotFoundPage.js +18 -0
  18. package/dist/planmagnet/pages/NotFoundPage.js.map +1 -0
  19. package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts.map +1 -1
  20. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +93 -98
  21. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -1
  22. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.d.ts.map +1 -1
  23. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js +72 -79
  24. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js.map +1 -1
  25. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  26. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +112 -123
  27. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  28. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  29. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +103 -87
  30. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  31. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  32. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +92 -98
  33. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  34. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  35. package/dist/planmagnet/pages/ProductsProductsPagePage.js +88 -96
  36. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  37. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
  38. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +116 -114
  39. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  40. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
  41. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +82 -88
  42. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
  43. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  44. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +79 -79
  45. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  46. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.d.ts.map +1 -1
  47. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js +111 -91
  48. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js.map +1 -1
  49. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
  50. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +117 -97
  51. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
  52. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  53. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +150 -154
  54. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  55. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts.map +1 -1
  56. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +154 -117
  57. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
  58. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +75 -31
  60. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
  62. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +161 -115
  63. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  64. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  65. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +117 -102
  66. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  67. package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts.map +1 -1
  68. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js +147 -147
  69. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js.map +1 -1
  70. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  71. package/dist/planmagnet/pages/TeamTeamPagePage.js +77 -67
  72. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  73. package/dist/planmagnet/roadmaps/planningFocus.d.ts +23 -0
  74. package/dist/planmagnet/roadmaps/planningFocus.d.ts.map +1 -0
  75. package/dist/planmagnet/roadmaps/planningFocus.js +76 -0
  76. package/dist/planmagnet/roadmaps/planningFocus.js.map +1 -0
  77. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  78. package/dist/planmagnet/sprints/SprintControls.js +48 -29
  79. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  80. package/dist/planmagnet/sprints/SprintDates.d.ts +37 -0
  81. package/dist/planmagnet/sprints/SprintDates.d.ts.map +1 -0
  82. package/dist/planmagnet/sprints/SprintDates.js +114 -0
  83. package/dist/planmagnet/sprints/SprintDates.js.map +1 -0
  84. package/dist/planmagnet/utils/email.d.ts +9 -0
  85. package/dist/planmagnet/utils/email.d.ts.map +1 -0
  86. package/dist/planmagnet/utils/email.js +15 -0
  87. package/dist/planmagnet/utils/email.js.map +1 -0
  88. package/dist/planmagnet/utils/formValidation.d.ts +18 -0
  89. package/dist/planmagnet/utils/formValidation.d.ts.map +1 -0
  90. package/dist/planmagnet/utils/formValidation.js +17 -0
  91. package/dist/planmagnet/utils/formValidation.js.map +1 -0
  92. package/package.json +1 -1
@@ -1,13 +1,13 @@
1
- import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
2
- import { throwOnError as t } from "../utils/mutationResult.js";
3
- import { useState as n } from "react";
4
- import { Plus as r } from "lucide-react";
5
- import { gql as i } from "@apollo/client";
6
- import { useMutation as a, useQuery as o } from "@apollo/client/react";
7
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
8
- import { Button as l, Input as u, ResponsiveDialog as d, ResponsiveDialogContent as f, ResponsiveDialogDescription as p, ResponsiveDialogFooter as m, ResponsiveDialogHeader as h, ResponsiveDialogTitle as g, ResponsiveDialogTrigger as _ } from "@burdenoff/fe-libs/ui";
1
+ import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
+ import { throwOnError as n } from "../utils/mutationResult.js";
3
+ import { useState as r } from "react";
4
+ import { Plus as i } from "lucide-react";
5
+ import { gql as a } from "@apollo/client";
6
+ import { useMutation as o, useQuery as s } from "@apollo/client/react";
7
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
+ import { Button as u, Input as d, ResponsiveDialog as f, ResponsiveDialogContent as p, ResponsiveDialogDescription as m, ResponsiveDialogFooter as h, ResponsiveDialogHeader as g, ResponsiveDialogTitle as _, ResponsiveDialogTrigger as v } from "@burdenoff/fe-libs/ui";
9
9
  //#region src/planmagnet/create/CreateDialogs.tsx
10
- var v = i`
10
+ var y = a`
11
11
  query CreateDialogProjects($workspaceId: ID!) {
12
12
  planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {
13
13
  items {
@@ -16,49 +16,76 @@ var v = i`
16
16
  }
17
17
  }
18
18
  }
19
- `, y = "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm";
20
- function b({ message: e }) {
21
- return /* @__PURE__ */ s("p", {
19
+ `, b = "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm";
20
+ function x({ message: e }) {
21
+ return /* @__PURE__ */ c("p", {
22
22
  role: "alert",
23
23
  className: "text-sm text-destructive",
24
24
  children: e
25
25
  });
26
26
  }
27
- function x(t) {
28
- let { workspaceId: n } = e(), { data: r, loading: i } = o(v, {
27
+ function S(t) {
28
+ let { workspaceId: n } = e(), { data: r, loading: i, error: a, refetch: o } = s(y, {
29
29
  variables: { workspaceId: n ?? "" },
30
30
  skip: !t || !n
31
31
  });
32
32
  return {
33
33
  projects: r?.planmagnetProjects.items ?? [],
34
- loading: i
34
+ loading: i && !r,
35
+ error: a ? a.message : null,
36
+ retry: () => void o()
35
37
  };
36
38
  }
37
- function S({ id: e, value: t, onChange: n, projects: r, loading: i }) {
38
- return /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("label", {
39
- htmlFor: e,
40
- className: "text-sm font-medium",
41
- children: "Project"
42
- }), /* @__PURE__ */ c("select", {
43
- id: e,
44
- className: y,
45
- value: t,
46
- onChange: (e) => n(e.target.value),
47
- children: [r.length === 0 && /* @__PURE__ */ s("option", {
48
- value: "",
49
- children: i ? "Loading…" : "No projects"
50
- }), r.map((e) => /* @__PURE__ */ s("option", {
51
- value: e.id,
52
- children: e.name
53
- }, e.id))]
54
- })] });
39
+ function C({ id: e, value: n, onChange: r, state: i }) {
40
+ let a = t(), { projects: o, loading: s, error: d, retry: f } = i, p = !s && !d && o.length === 0;
41
+ return /* @__PURE__ */ l("div", { children: [
42
+ /* @__PURE__ */ c("label", {
43
+ htmlFor: e,
44
+ className: "text-sm font-medium",
45
+ children: "Project"
46
+ }),
47
+ /* @__PURE__ */ l("select", {
48
+ id: e,
49
+ className: b,
50
+ value: n,
51
+ onChange: (e) => r(e.target.value),
52
+ children: [o.length === 0 && /* @__PURE__ */ c("option", {
53
+ value: "",
54
+ children: s ? "Loading projects…" : d ? "Projects could not be loaded" : "No projects yet"
55
+ }), o.map((e) => /* @__PURE__ */ c("option", {
56
+ value: e.id,
57
+ children: e.name
58
+ }, e.id))]
59
+ }),
60
+ d && /* @__PURE__ */ l("p", {
61
+ role: "alert",
62
+ className: "mt-1 flex items-center gap-2 text-xs text-destructive",
63
+ children: [d, /* @__PURE__ */ c(u, {
64
+ type: "button",
65
+ size: "sm",
66
+ variant: "ghost",
67
+ className: "h-6 px-2 text-xs",
68
+ onClick: f,
69
+ children: "Retry"
70
+ })]
71
+ }),
72
+ p && /* @__PURE__ */ c("p", {
73
+ className: "mt-1 text-xs text-muted-foreground",
74
+ children: /* @__PURE__ */ c("button", {
75
+ type: "button",
76
+ className: "underline",
77
+ onClick: () => a("/projects/create-project-page"),
78
+ children: "Create a project first"
79
+ })
80
+ })
81
+ ] });
55
82
  }
56
- function C({ id: e, label: t, value: n, onChange: r, type: i = "text", placeholder: a }) {
57
- return /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("label", {
83
+ function w({ id: e, label: t, value: n, onChange: r, type: i = "text", placeholder: a }) {
84
+ return /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("label", {
58
85
  htmlFor: e,
59
86
  className: "text-sm font-medium",
60
87
  children: t
61
- }), /* @__PURE__ */ s(u, {
88
+ }), /* @__PURE__ */ c(d, {
62
89
  id: e,
63
90
  type: i,
64
91
  className: "mt-1",
@@ -67,107 +94,106 @@ function C({ id: e, label: t, value: n, onChange: r, type: i = "text", placehold
67
94
  onChange: (e) => r(e.target.value)
68
95
  })] });
69
96
  }
70
- var w = (e, t) => e instanceof Error ? e.message : t, T = i`
97
+ var T = (e, t) => e instanceof Error ? e.message : t, E = a`
71
98
  mutation CreateDialogRelease($input: PlanmagnetCreateReleaseInput!) {
72
99
  planmagnetCreateRelease(input: $input) {
73
100
  id
74
101
  }
75
102
  }
76
- `, E = {
103
+ `, D = {
77
104
  name: "",
78
105
  version: "",
79
106
  projectId: "",
80
107
  plannedDate: "",
81
108
  description: ""
82
109
  };
83
- function D({ onCreated: e }) {
84
- let [i, o] = n(!1), [u, v] = n(E), [y, D] = n(null), { projects: O, loading: k } = x(i), A = u.projectId || O[0]?.id || "", [j, { loading: M }] = a(T, {
110
+ function O({ onCreated: e }) {
111
+ let [t, a] = r(!1), [s, d] = r(D), [y, b] = r(null), O = S(t), { projects: k } = O, A = s.projectId || k[0]?.id || "", [j, { loading: M }] = o(E, {
85
112
  refetchQueries: ["ListReleases"],
86
113
  awaitRefetchQueries: !0
87
- }), N = u.name.trim() !== "" && u.version.trim() !== "" && A !== "" && !M;
88
- return /* @__PURE__ */ c(d, {
89
- open: i,
114
+ }), N = s.name.trim() !== "" && s.version.trim() !== "" && A !== "" && !M;
115
+ return /* @__PURE__ */ l(f, {
116
+ open: t,
90
117
  onOpenChange: (e) => {
91
- o(e), e || D(null);
118
+ a(e), e || b(null);
92
119
  },
93
- children: [/* @__PURE__ */ s(_, {
120
+ children: [/* @__PURE__ */ c(v, {
94
121
  asChild: !0,
95
- children: /* @__PURE__ */ c(l, {
122
+ children: /* @__PURE__ */ l(u, {
96
123
  size: "sm",
97
- children: [/* @__PURE__ */ s(r, { className: "mr-1 h-4 w-4" }), "New Release"]
124
+ children: [/* @__PURE__ */ c(i, { className: "mr-1 h-4 w-4" }), "New Release"]
98
125
  })
99
- }), /* @__PURE__ */ c(f, {
126
+ }), /* @__PURE__ */ l(p, {
100
127
  className: "max-w-lg",
101
128
  children: [
102
- /* @__PURE__ */ c(h, { children: [/* @__PURE__ */ s(g, { children: "New release" }), /* @__PURE__ */ s(p, { children: "The release starts in Planning." })] }),
103
- /* @__PURE__ */ c("div", {
129
+ /* @__PURE__ */ l(g, { children: [/* @__PURE__ */ c(_, { children: "New release" }), /* @__PURE__ */ c(m, { children: "The release starts in Planning." })] }),
130
+ /* @__PURE__ */ l("div", {
104
131
  className: "space-y-3",
105
132
  children: [
106
- /* @__PURE__ */ s(C, {
133
+ /* @__PURE__ */ c(w, {
107
134
  id: "new-release-name",
108
135
  label: "Name",
109
- value: u.name,
110
- onChange: (e) => v({
111
- ...u,
136
+ value: s.name,
137
+ onChange: (e) => d({
138
+ ...s,
112
139
  name: e
113
140
  })
114
141
  }),
115
- /* @__PURE__ */ s(C, {
142
+ /* @__PURE__ */ c(w, {
116
143
  id: "new-release-version",
117
144
  label: "Version",
118
145
  placeholder: "e.g. 1.4.0",
119
- value: u.version,
120
- onChange: (e) => v({
121
- ...u,
146
+ value: s.version,
147
+ onChange: (e) => d({
148
+ ...s,
122
149
  version: e
123
150
  })
124
151
  }),
125
- /* @__PURE__ */ s(S, {
152
+ /* @__PURE__ */ c(C, {
126
153
  id: "new-release-project",
127
154
  value: A,
128
- onChange: (e) => v({
129
- ...u,
155
+ onChange: (e) => d({
156
+ ...s,
130
157
  projectId: e
131
158
  }),
132
- projects: O,
133
- loading: k
159
+ state: O
134
160
  }),
135
- /* @__PURE__ */ s(C, {
161
+ /* @__PURE__ */ c(w, {
136
162
  id: "new-release-date",
137
163
  label: "Planned date",
138
164
  type: "date",
139
- value: u.plannedDate,
140
- onChange: (e) => v({
141
- ...u,
165
+ value: s.plannedDate,
166
+ onChange: (e) => d({
167
+ ...s,
142
168
  plannedDate: e
143
169
  })
144
170
  }),
145
- /* @__PURE__ */ s(C, {
171
+ /* @__PURE__ */ c(w, {
146
172
  id: "new-release-description",
147
173
  label: "Description",
148
- value: u.description,
149
- onChange: (e) => v({
150
- ...u,
174
+ value: s.description,
175
+ onChange: (e) => d({
176
+ ...s,
151
177
  description: e
152
178
  })
153
179
  })
154
180
  ]
155
181
  }),
156
- y && /* @__PURE__ */ s(b, { message: y }),
157
- /* @__PURE__ */ s(m, { children: /* @__PURE__ */ s(l, {
182
+ y && /* @__PURE__ */ c(x, { message: y }),
183
+ /* @__PURE__ */ c(h, { children: /* @__PURE__ */ c(u, {
158
184
  onClick: async () => {
159
- D(null);
185
+ b(null);
160
186
  try {
161
- let n = (await t(j({ variables: { input: {
162
- name: u.name.trim(),
163
- version: u.version.trim(),
187
+ let t = (await n(j({ variables: { input: {
188
+ name: s.name.trim(),
189
+ version: s.version.trim(),
164
190
  projectId: A,
165
- plannedDate: u.plannedDate || void 0,
166
- description: u.description.trim() || void 0
191
+ plannedDate: s.plannedDate || void 0,
192
+ description: s.description.trim() || void 0
167
193
  } } }))).data?.planmagnetCreateRelease.id;
168
- v(E), o(!1), n && e(n);
194
+ d(D), a(!1), t && e(t);
169
195
  } catch (e) {
170
- D(w(e, "The release could not be created."));
196
+ b(T(e, "The release could not be created."));
171
197
  }
172
198
  },
173
199
  disabled: !N,
@@ -178,95 +204,94 @@ function D({ onCreated: e }) {
178
204
  })]
179
205
  });
180
206
  }
181
- var O = i`
207
+ var k = a`
182
208
  mutation CreateDialogMilestone($input: PlanmagnetCreateMilestoneInput!) {
183
209
  planmagnetCreateMilestone(input: $input) {
184
210
  id
185
211
  }
186
212
  }
187
- `, k = {
213
+ `, A = {
188
214
  name: "",
189
215
  projectId: "",
190
216
  dueDate: "",
191
217
  description: ""
192
218
  };
193
- function A({ onCreated: e }) {
194
- let [i, o] = n(!1), [u, v] = n(k), [y, T] = n(null), { projects: E, loading: D } = x(i), A = u.projectId || E[0]?.id || "", [j, { loading: M }] = a(O, {
219
+ function j({ onCreated: e }) {
220
+ let [t, a] = r(!1), [s, d] = r(A), [y, b] = r(null), E = S(t), { projects: D } = E, O = s.projectId || D[0]?.id || "", [j, { loading: M }] = o(k, {
195
221
  refetchQueries: ["GetMilestones"],
196
222
  awaitRefetchQueries: !0
197
- }), N = u.name.trim() !== "" && A !== "" && !M;
198
- return /* @__PURE__ */ c(d, {
199
- open: i,
223
+ }), N = s.name.trim() !== "" && O !== "" && !M;
224
+ return /* @__PURE__ */ l(f, {
225
+ open: t,
200
226
  onOpenChange: (e) => {
201
- o(e), e || T(null);
227
+ a(e), e || b(null);
202
228
  },
203
- children: [/* @__PURE__ */ s(_, {
229
+ children: [/* @__PURE__ */ c(v, {
204
230
  asChild: !0,
205
- children: /* @__PURE__ */ c(l, {
231
+ children: /* @__PURE__ */ l(u, {
206
232
  size: "sm",
207
- children: [/* @__PURE__ */ s(r, { className: "mr-1 h-4 w-4" }), "New Milestone"]
233
+ children: [/* @__PURE__ */ c(i, { className: "mr-1 h-4 w-4" }), "New Milestone"]
208
234
  })
209
- }), /* @__PURE__ */ c(f, {
235
+ }), /* @__PURE__ */ l(p, {
210
236
  className: "max-w-lg",
211
237
  children: [
212
- /* @__PURE__ */ c(h, { children: [/* @__PURE__ */ s(g, { children: "New milestone" }), /* @__PURE__ */ s(p, { children: "The milestone starts as Pending." })] }),
213
- /* @__PURE__ */ c("div", {
238
+ /* @__PURE__ */ l(g, { children: [/* @__PURE__ */ c(_, { children: "New milestone" }), /* @__PURE__ */ c(m, { children: "The milestone starts as Pending." })] }),
239
+ /* @__PURE__ */ l("div", {
214
240
  className: "space-y-3",
215
241
  children: [
216
- /* @__PURE__ */ s(C, {
242
+ /* @__PURE__ */ c(w, {
217
243
  id: "new-milestone-name",
218
244
  label: "Name",
219
- value: u.name,
220
- onChange: (e) => v({
221
- ...u,
245
+ value: s.name,
246
+ onChange: (e) => d({
247
+ ...s,
222
248
  name: e
223
249
  })
224
250
  }),
225
- /* @__PURE__ */ s(S, {
251
+ /* @__PURE__ */ c(C, {
226
252
  id: "new-milestone-project",
227
- value: A,
228
- onChange: (e) => v({
229
- ...u,
253
+ value: O,
254
+ onChange: (e) => d({
255
+ ...s,
230
256
  projectId: e
231
257
  }),
232
- projects: E,
233
- loading: D
258
+ state: E
234
259
  }),
235
- /* @__PURE__ */ s(C, {
260
+ /* @__PURE__ */ c(w, {
236
261
  id: "new-milestone-due",
237
262
  label: "Due date",
238
263
  type: "date",
239
- value: u.dueDate,
240
- onChange: (e) => v({
241
- ...u,
264
+ value: s.dueDate,
265
+ onChange: (e) => d({
266
+ ...s,
242
267
  dueDate: e
243
268
  })
244
269
  }),
245
- /* @__PURE__ */ s(C, {
270
+ /* @__PURE__ */ c(w, {
246
271
  id: "new-milestone-description",
247
272
  label: "Description",
248
- value: u.description,
249
- onChange: (e) => v({
250
- ...u,
273
+ value: s.description,
274
+ onChange: (e) => d({
275
+ ...s,
251
276
  description: e
252
277
  })
253
278
  })
254
279
  ]
255
280
  }),
256
- y && /* @__PURE__ */ s(b, { message: y }),
257
- /* @__PURE__ */ s(m, { children: /* @__PURE__ */ s(l, {
281
+ y && /* @__PURE__ */ c(x, { message: y }),
282
+ /* @__PURE__ */ c(h, { children: /* @__PURE__ */ c(u, {
258
283
  onClick: async () => {
259
- T(null);
284
+ b(null);
260
285
  try {
261
- let n = (await t(j({ variables: { input: {
262
- name: u.name.trim(),
263
- projectId: A,
264
- dueDate: u.dueDate || void 0,
265
- description: u.description.trim() || void 0
286
+ let t = (await n(j({ variables: { input: {
287
+ name: s.name.trim(),
288
+ projectId: O,
289
+ dueDate: s.dueDate || void 0,
290
+ description: s.description.trim() || void 0
266
291
  } } }))).data?.planmagnetCreateMilestone.id;
267
- v(k), o(!1), n && e(n);
292
+ d(A), a(!1), t && e(t);
268
293
  } catch (e) {
269
- T(w(e, "The milestone could not be created."));
294
+ b(T(e, "The milestone could not be created."));
270
295
  }
271
296
  },
272
297
  disabled: !N,
@@ -278,6 +303,6 @@ function A({ onCreated: e }) {
278
303
  });
279
304
  }
280
305
  //#endregion
281
- export { A as NewMilestoneDialog, D as NewReleaseDialog };
306
+ export { j as NewMilestoneDialog, O as NewReleaseDialog };
282
307
 
283
308
  //# sourceMappingURL=CreateDialogs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreateDialogs.js","names":[],"sources":["../../../src/planmagnet/create/CreateDialogs.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } 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 { Plus } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\n\n/**\n * Create dialogs for records whose list pages had a dead \"New …\" button, or\n * none at all (QA report 2026-09-23: C4 New Release had no onClick; H7 there\n * was no way to create a milestone). Both mutations were already served and\n * verified in prod; only the UI trigger was missing.\n */\n\nconst DIALOG_PROJECTS = gql`\n query CreateDialogProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst FIELD = 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm';\n\nfunction ErrorLine({ message }: { message: string }) {\n return (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {message}\n </p>\n );\n}\n\nfunction useDialogProjects(open: boolean) {\n const { workspaceId } = usePlanMagnet();\n const { data, loading } = useQuery<{\n planmagnetProjects: { items: Array<{ id: string; name: string }> };\n }>(DIALOG_PROJECTS, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !open || !workspaceId,\n });\n return { projects: data?.planmagnetProjects.items ?? [], loading };\n}\n\nfunction ProjectField({\n id,\n value,\n onChange,\n projects,\n loading,\n}: {\n id: string;\n value: string;\n onChange: (id: string) => void;\n projects: Array<{ id: string; name: string }>;\n loading: boolean;\n}) {\n return (\n <div>\n <label htmlFor={id} className=\"text-sm font-medium\">\n Project\n </label>\n <select id={id} className={FIELD} value={value} onChange={(e) => onChange(e.target.value)}>\n {projects.length === 0 && <option value=\"\">{loading ? 'Loading…' : 'No projects'}</option>}\n {projects.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n </select>\n </div>\n );\n}\n\nfunction TextField({\n id,\n label,\n value,\n onChange,\n type = 'text',\n placeholder,\n}: {\n id: string;\n label: string;\n value: string;\n onChange: (value: string) => void;\n type?: string;\n placeholder?: string;\n}) {\n return (\n <div>\n <label htmlFor={id} className=\"text-sm font-medium\">\n {label}\n </label>\n <Input\n id={id}\n type={type}\n className=\"mt-1\"\n value={value}\n placeholder={placeholder}\n onChange={(e) => onChange(e.target.value)}\n />\n </div>\n );\n}\n\nconst messageOf = (e: unknown, fallback: string) => (e instanceof Error ? e.message : fallback);\n\n// ─── New release (C4) ────────────────────────────────────────────────────────\n\nconst CREATE_RELEASE = gql`\n mutation CreateDialogRelease($input: PlanmagnetCreateReleaseInput!) {\n planmagnetCreateRelease(input: $input) {\n id\n }\n }\n`;\n\nconst EMPTY_RELEASE = { name: '', version: '', projectId: '', plannedDate: '', description: '' };\n\nexport function NewReleaseDialog({ onCreated }: { onCreated: (releaseId: string) => void }) {\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(EMPTY_RELEASE);\n const [error, setError] = useState<string | null>(null);\n const { projects, loading: projectsLoading } = useDialogProjects(open);\n const projectId = form.projectId || projects[0]?.id || '';\n const [create, { loading }] = useMutation<{ planmagnetCreateRelease: { id: string } }>(\n CREATE_RELEASE,\n { refetchQueries: ['ListReleases'], awaitRefetchQueries: true }\n );\n\n const canSubmit =\n form.name.trim() !== '' && form.version.trim() !== '' && projectId !== '' && !loading;\n\n const handleCreate = async () => {\n setError(null);\n try {\n const result = await throwOnError(\n create({\n variables: {\n input: {\n name: form.name.trim(),\n version: form.version.trim(),\n projectId,\n plannedDate: form.plannedDate || undefined,\n description: form.description.trim() || undefined,\n },\n },\n })\n );\n const id = result.data?.planmagnetCreateRelease.id;\n setForm(EMPTY_RELEASE);\n setOpen(false);\n if (id) onCreated(id);\n } catch (e) {\n setError(messageOf(e, 'The release could not be created.'));\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Release\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New release</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>The release starts in Planning.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <TextField\n id=\"new-release-name\"\n label=\"Name\"\n value={form.name}\n onChange={(name) => setForm({ ...form, name })}\n />\n <TextField\n id=\"new-release-version\"\n label=\"Version\"\n placeholder=\"e.g. 1.4.0\"\n value={form.version}\n onChange={(version) => setForm({ ...form, version })}\n />\n <ProjectField\n id=\"new-release-project\"\n value={projectId}\n onChange={(id) => setForm({ ...form, projectId: id })}\n projects={projects}\n loading={projectsLoading}\n />\n <TextField\n id=\"new-release-date\"\n label=\"Planned date\"\n type=\"date\"\n value={form.plannedDate}\n onChange={(plannedDate) => setForm({ ...form, plannedDate })}\n />\n <TextField\n id=\"new-release-description\"\n label=\"Description\"\n value={form.description}\n onChange={(description) => setForm({ ...form, description })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button onClick={handleCreate} disabled={!canSubmit} data-testid=\"new-release-submit\">\n {loading ? 'Creating…' : 'Create release'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New milestone (H7) ──────────────────────────────────────────────────────\n\nconst CREATE_MILESTONE = gql`\n mutation CreateDialogMilestone($input: PlanmagnetCreateMilestoneInput!) {\n planmagnetCreateMilestone(input: $input) {\n id\n }\n }\n`;\n\nconst EMPTY_MILESTONE = { name: '', projectId: '', dueDate: '', description: '' };\n\nexport function NewMilestoneDialog({ onCreated }: { onCreated: (milestoneId: string) => void }) {\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(EMPTY_MILESTONE);\n const [error, setError] = useState<string | null>(null);\n const { projects, loading: projectsLoading } = useDialogProjects(open);\n const projectId = form.projectId || projects[0]?.id || '';\n const [create, { loading }] = useMutation<{ planmagnetCreateMilestone: { id: string } }>(\n CREATE_MILESTONE,\n { refetchQueries: ['GetMilestones'], awaitRefetchQueries: true }\n );\n\n const canSubmit = form.name.trim() !== '' && projectId !== '' && !loading;\n\n const handleCreate = async () => {\n setError(null);\n try {\n const result = await throwOnError(\n create({\n variables: {\n input: {\n name: form.name.trim(),\n projectId,\n dueDate: form.dueDate || undefined,\n description: form.description.trim() || undefined,\n },\n },\n })\n );\n const id = result.data?.planmagnetCreateMilestone.id;\n setForm(EMPTY_MILESTONE);\n setOpen(false);\n if (id) onCreated(id);\n } catch (e) {\n setError(messageOf(e, 'The milestone could not be created.'));\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Milestone\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New milestone</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n The milestone starts as Pending.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <TextField\n id=\"new-milestone-name\"\n label=\"Name\"\n value={form.name}\n onChange={(name) => setForm({ ...form, name })}\n />\n <ProjectField\n id=\"new-milestone-project\"\n value={projectId}\n onChange={(id) => setForm({ ...form, projectId: id })}\n projects={projects}\n loading={projectsLoading}\n />\n <TextField\n id=\"new-milestone-due\"\n label=\"Due date\"\n type=\"date\"\n value={form.dueDate}\n onChange={(dueDate) => setForm({ ...form, dueDate })}\n />\n <TextField\n id=\"new-milestone-description\"\n label=\"Description\"\n value={form.description}\n onChange={(description) => setForm({ ...form, description })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button onClick={handleCreate} disabled={!canSubmit} data-testid=\"new-milestone-submit\">\n {loading ? 'Creating…' : 'Create milestone'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreateDialogs.js","names":[],"sources":["../../../src/planmagnet/create/CreateDialogs.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } 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 { Plus } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\n\n/**\n * Create dialogs for records whose list pages had a dead \"New …\" button, or\n * none at all (QA report 2026-09-23: C4 New Release had no onClick; H7 there\n * was no way to create a milestone). Both mutations were already served and\n * verified in prod; only the UI trigger was missing.\n */\n\nconst DIALOG_PROJECTS = gql`\n query CreateDialogProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst FIELD = 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm';\n\nfunction ErrorLine({ message }: { message: string }) {\n return (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {message}\n </p>\n );\n}\n\n/**\n * C2 (BOFF-7833): the picker used to show one \"No projects\" option for three different situations —\n * still loading, the query failed, and genuinely no projects. It now says which, offers a retry on\n * failure, and a way to create a project when there are none.\n */\ninterface ProjectsState {\n projects: Array<{ id: string; name: string }>;\n loading: boolean;\n error: string | null;\n retry: () => void;\n}\n\nfunction useDialogProjects(open: boolean): ProjectsState {\n const { workspaceId } = usePlanMagnet();\n const { data, loading, error, refetch } = useQuery<{\n planmagnetProjects: { items: Array<{ id: string; name: string }> };\n }>(DIALOG_PROJECTS, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !open || !workspaceId,\n });\n return {\n projects: data?.planmagnetProjects.items ?? [],\n loading: loading && !data,\n error: error ? error.message : null,\n retry: () => void refetch(),\n };\n}\n\nfunction ProjectField({\n id,\n value,\n onChange,\n state,\n}: {\n id: string;\n value: string;\n onChange: (id: string) => void;\n state: ProjectsState;\n}) {\n const navigate = usePlanMagnetNavigate();\n const { projects, loading, error, retry } = state;\n const empty = !loading && !error && projects.length === 0;\n return (\n <div>\n <label htmlFor={id} className=\"text-sm font-medium\">\n Project\n </label>\n <select id={id} className={FIELD} value={value} onChange={(e) => onChange(e.target.value)}>\n {projects.length === 0 && (\n <option value=\"\">\n {loading\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 {error && (\n <p role=\"alert\" className=\"mt-1 flex items-center gap-2 text-xs text-destructive\">\n {error}\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 px-2 text-xs\"\n onClick={retry}\n >\n Retry\n </Button>\n </p>\n )}\n {empty && (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n <button\n type=\"button\"\n className=\"underline\"\n onClick={() => navigate('/projects/create-project-page')}\n >\n Create a project first\n </button>\n </p>\n )}\n </div>\n );\n}\n\nfunction TextField({\n id,\n label,\n value,\n onChange,\n type = 'text',\n placeholder,\n}: {\n id: string;\n label: string;\n value: string;\n onChange: (value: string) => void;\n type?: string;\n placeholder?: string;\n}) {\n return (\n <div>\n <label htmlFor={id} className=\"text-sm font-medium\">\n {label}\n </label>\n <Input\n id={id}\n type={type}\n className=\"mt-1\"\n value={value}\n placeholder={placeholder}\n onChange={(e) => onChange(e.target.value)}\n />\n </div>\n );\n}\n\nconst messageOf = (e: unknown, fallback: string) => (e instanceof Error ? e.message : fallback);\n\n// ─── New release (C4) ────────────────────────────────────────────────────────\n\nconst CREATE_RELEASE = gql`\n mutation CreateDialogRelease($input: PlanmagnetCreateReleaseInput!) {\n planmagnetCreateRelease(input: $input) {\n id\n }\n }\n`;\n\nconst EMPTY_RELEASE = { name: '', version: '', projectId: '', plannedDate: '', description: '' };\n\nexport function NewReleaseDialog({ onCreated }: { onCreated: (releaseId: string) => void }) {\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(EMPTY_RELEASE);\n const [error, setError] = useState<string | null>(null);\n const projectsState = useDialogProjects(open);\n const { projects } = projectsState;\n const projectId = form.projectId || projects[0]?.id || '';\n const [create, { loading }] = useMutation<{ planmagnetCreateRelease: { id: string } }>(\n CREATE_RELEASE,\n { refetchQueries: ['ListReleases'], awaitRefetchQueries: true }\n );\n\n const canSubmit =\n form.name.trim() !== '' && form.version.trim() !== '' && projectId !== '' && !loading;\n\n const handleCreate = async () => {\n setError(null);\n try {\n const result = await throwOnError(\n create({\n variables: {\n input: {\n name: form.name.trim(),\n version: form.version.trim(),\n projectId,\n plannedDate: form.plannedDate || undefined,\n description: form.description.trim() || undefined,\n },\n },\n })\n );\n const id = result.data?.planmagnetCreateRelease.id;\n setForm(EMPTY_RELEASE);\n setOpen(false);\n if (id) onCreated(id);\n } catch (e) {\n setError(messageOf(e, 'The release could not be created.'));\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Release\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New release</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>The release starts in Planning.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <TextField\n id=\"new-release-name\"\n label=\"Name\"\n value={form.name}\n onChange={(name) => setForm({ ...form, name })}\n />\n <TextField\n id=\"new-release-version\"\n label=\"Version\"\n placeholder=\"e.g. 1.4.0\"\n value={form.version}\n onChange={(version) => setForm({ ...form, version })}\n />\n <ProjectField\n id=\"new-release-project\"\n value={projectId}\n onChange={(id) => setForm({ ...form, projectId: id })}\n state={projectsState}\n />\n <TextField\n id=\"new-release-date\"\n label=\"Planned date\"\n type=\"date\"\n value={form.plannedDate}\n onChange={(plannedDate) => setForm({ ...form, plannedDate })}\n />\n <TextField\n id=\"new-release-description\"\n label=\"Description\"\n value={form.description}\n onChange={(description) => setForm({ ...form, description })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button onClick={handleCreate} disabled={!canSubmit} data-testid=\"new-release-submit\">\n {loading ? 'Creating…' : 'Create release'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New milestone (H7) ──────────────────────────────────────────────────────\n\nconst CREATE_MILESTONE = gql`\n mutation CreateDialogMilestone($input: PlanmagnetCreateMilestoneInput!) {\n planmagnetCreateMilestone(input: $input) {\n id\n }\n }\n`;\n\nconst EMPTY_MILESTONE = { name: '', projectId: '', dueDate: '', description: '' };\n\nexport function NewMilestoneDialog({ onCreated }: { onCreated: (milestoneId: string) => void }) {\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(EMPTY_MILESTONE);\n const [error, setError] = useState<string | null>(null);\n const projectsState = useDialogProjects(open);\n const { projects } = projectsState;\n const projectId = form.projectId || projects[0]?.id || '';\n const [create, { loading }] = useMutation<{ planmagnetCreateMilestone: { id: string } }>(\n CREATE_MILESTONE,\n { refetchQueries: ['GetMilestones'], awaitRefetchQueries: true }\n );\n\n const canSubmit = form.name.trim() !== '' && projectId !== '' && !loading;\n\n const handleCreate = async () => {\n setError(null);\n try {\n const result = await throwOnError(\n create({\n variables: {\n input: {\n name: form.name.trim(),\n projectId,\n dueDate: form.dueDate || undefined,\n description: form.description.trim() || undefined,\n },\n },\n })\n );\n const id = result.data?.planmagnetCreateMilestone.id;\n setForm(EMPTY_MILESTONE);\n setOpen(false);\n if (id) onCreated(id);\n } catch (e) {\n setError(messageOf(e, 'The milestone could not be created.'));\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Milestone\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New milestone</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n The milestone starts as Pending.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <TextField\n id=\"new-milestone-name\"\n label=\"Name\"\n value={form.name}\n onChange={(name) => setForm({ ...form, name })}\n />\n <ProjectField\n id=\"new-milestone-project\"\n value={projectId}\n onChange={(id) => setForm({ ...form, projectId: id })}\n state={projectsState}\n />\n <TextField\n id=\"new-milestone-due\"\n label=\"Due date\"\n type=\"date\"\n value={form.dueDate}\n onChange={(dueDate) => setForm({ ...form, dueDate })}\n />\n <TextField\n id=\"new-milestone-description\"\n label=\"Description\"\n value={form.description}\n onChange={(description) => setForm({ ...form, description })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button onClick={handleCreate} disabled={!canSubmit} data-testid=\"new-milestone-submit\">\n {loading ? 'Creating…' : 'Create milestone'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;;;AAyBA,IAAM,IAAkB,CAAG;;;;;;;;;GAWrB,IAAQ;AAEd,SAAS,EAAU,EAAE,cAAgC;AACnD,QACE,kBAAC,KAAD;EAAG,MAAK;EAAQ,WAAU;YACvB;EACC,CAAA;;AAgBR,SAAS,EAAkB,GAA8B;CACvD,IAAM,EAAE,mBAAgB,GAAe,EACjC,EAAE,SAAM,YAAS,UAAO,eAAY,EAEvC,GAAiB;EAClB,WAAW,EAAE,aAAa,KAAe,IAAI;EAC7C,MAAM,CAAC,KAAQ,CAAC;EACjB,CAAC;AACF,QAAO;EACL,UAAU,GAAM,mBAAmB,SAAS,EAAE;EAC9C,SAAS,KAAW,CAAC;EACrB,OAAO,IAAQ,EAAM,UAAU;EAC/B,aAAa,KAAK,GAAS;EAC5B;;AAGH,SAAS,EAAa,EACpB,OACA,UACA,aACA,YAMC;CACD,IAAM,IAAW,GAAuB,EAClC,EAAE,aAAU,YAAS,UAAO,aAAU,GACtC,IAAQ,CAAC,KAAW,CAAC,KAAS,EAAS,WAAW;AACxD,QACE,kBAAC,OAAD,EAAA,UAAA;EACE,kBAAC,SAAD;GAAO,SAAS;GAAI,WAAU;aAAsB;GAE5C,CAAA;EACR,kBAAC,UAAD;GAAY;GAAI,WAAW;GAAc;GAAO,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;aAAzF,CACG,EAAS,WAAW,KACnB,kBAAC,UAAD;IAAQ,OAAM;cACX,IACG,sBACA,IACE,iCACA;IACC,CAAA,EAEV,EAAS,KAAK,MACb,kBAAC,UAAD;IAAmB,OAAO,EAAE;cACzB,EAAE;IACI,EAFI,EAAE,GAEN,CACT,CACK;;EACR,KACC,kBAAC,KAAD;GAAG,MAAK;GAAQ,WAAU;aAA1B,CACG,GACD,kBAAC,GAAD;IACE,MAAK;IACL,MAAK;IACL,SAAQ;IACR,WAAU;IACV,SAAS;cACV;IAEQ,CAAA,CACP;;EAEL,KACC,kBAAC,KAAD;GAAG,WAAU;aACX,kBAAC,UAAD;IACE,MAAK;IACL,WAAU;IACV,eAAe,EAAS,gCAAgC;cACzD;IAEQ,CAAA;GACP,CAAA;EAEF,EAAA,CAAA;;AAIV,SAAS,EAAU,EACjB,OACA,UACA,UACA,aACA,UAAO,QACP,kBAQC;AACD,QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;EAAO,SAAS;EAAI,WAAU;YAC3B;EACK,CAAA,EACR,kBAAC,GAAD;EACM;EACE;EACN,WAAU;EACH;EACM;EACb,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;EACzC,CAAA,CACE,EAAA,CAAA;;AAIV,IAAM,KAAa,GAAY,MAAsB,aAAa,QAAQ,EAAE,UAAU,GAIhF,IAAiB,CAAG;;;;;;GAQpB,IAAgB;CAAE,MAAM;CAAI,SAAS;CAAI,WAAW;CAAI,aAAa;CAAI,aAAa;CAAI;AAEhG,SAAgB,EAAiB,EAAE,gBAAyD;CAC1F,IAAM,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAM,KAAW,EAAS,EAAc,EACzC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAgB,EAAkB,EAAK,EACvC,EAAE,gBAAa,GACf,IAAY,EAAK,aAAa,EAAS,IAAI,MAAM,IACjD,CAAC,GAAQ,EAAE,gBAAa,EAC5B,GACA;EAAE,gBAAgB,CAAC,eAAe;EAAE,qBAAqB;EAAM,CAChE,EAEK,IACJ,EAAK,KAAK,MAAM,KAAK,MAAM,EAAK,QAAQ,MAAM,KAAK,MAAM,MAAc,MAAM,CAAC;AA2BhF,QACE,kBAAC,GAAD;EACQ;EACN,eAAe,MAAS;AAEtB,GADA,EAAQ,EAAK,EACR,KAAM,EAAS,KAAK;;YAJ7B,CAOE,kBAAC,GAAD;GAAyB,SAAA;aACvB,kBAAC,GAAD;IAAQ,MAAK;cAAb,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,cAE1B;;GACe,CAAA,EAC1B,kBAAC,GAAD;GAAyB,WAAU;aAAnC;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,eAAmC,CAAA,EAC1D,kBAAC,GAAD,EAAA,UAA6B,mCAA6D,CAAA,CACnE,EAAA,CAAA;IACzB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO,EAAK;OACZ,WAAW,MAAS,EAAQ;QAAE,GAAG;QAAM;QAAM,CAAC;OAC9C,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,aAAY;OACZ,OAAO,EAAK;OACZ,WAAW,MAAY,EAAQ;QAAE,GAAG;QAAM;QAAS,CAAC;OACpD,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAO;OACP,WAAW,MAAO,EAAQ;QAAE,GAAG;QAAM,WAAW;QAAI,CAAC;OACrD,OAAO;OACP,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,MAAK;OACL,OAAO,EAAK;OACZ,WAAW,MAAgB,EAAQ;QAAE,GAAG;QAAM;QAAa,CAAC;OAC5D,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO,EAAK;OACZ,WAAW,MAAgB,EAAQ;QAAE,GAAG;QAAM;QAAa,CAAC;OAC5D,CAAA;MACE;;IACL,KAAS,kBAAC,GAAD,EAAW,SAAS,GAAS,CAAA;IACvC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAQ,SAhFK,YAAY;AAC/B,QAAS,KAAK;AACd,UAAI;OAcF,IAAM,KAbS,MAAM,EACnB,EAAO,EACL,WAAW,EACT,OAAO;QACL,MAAM,EAAK,KAAK,MAAM;QACtB,SAAS,EAAK,QAAQ,MAAM;QAC5B;QACA,aAAa,EAAK,eAAe,KAAA;QACjC,aAAa,EAAK,YAAY,MAAM,IAAI,KAAA;QACzC,EACF,EACF,CAAC,CACH,EACiB,MAAM,wBAAwB;AAGhD,OAFA,EAAQ,EAAc,EACtB,EAAQ,GAAM,EACV,KAAI,EAAU,EAAG;eACd,GAAG;AACV,SAAS,EAAU,GAAG,oCAAoC,CAAC;;;KA2DxB,UAAU,CAAC;KAAW,eAAY;eAC9D,IAAU,cAAc;KAClB,CAAA,EACc,CAAA;IACD;KACT;;;AAMvB,IAAM,IAAmB,CAAG;;;;;;GAQtB,IAAkB;CAAE,MAAM;CAAI,WAAW;CAAI,SAAS;CAAI,aAAa;CAAI;AAEjF,SAAgB,EAAmB,EAAE,gBAA2D;CAC9F,IAAM,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAM,KAAW,EAAS,EAAgB,EAC3C,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAgB,EAAkB,EAAK,EACvC,EAAE,gBAAa,GACf,IAAY,EAAK,aAAa,EAAS,IAAI,MAAM,IACjD,CAAC,GAAQ,EAAE,gBAAa,EAC5B,GACA;EAAE,gBAAgB,CAAC,gBAAgB;EAAE,qBAAqB;EAAM,CACjE,EAEK,IAAY,EAAK,KAAK,MAAM,KAAK,MAAM,MAAc,MAAM,CAAC;AA0BlE,QACE,kBAAC,GAAD;EACQ;EACN,eAAe,MAAS;AAEtB,GADA,EAAQ,EAAK,EACR,KAAM,EAAS,KAAK;;YAJ7B,CAOE,kBAAC,GAAD;GAAyB,SAAA;aACvB,kBAAC,GAAD;IAAQ,MAAK;cAAb,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,gBAE1B;;GACe,CAAA,EAC1B,kBAAC,GAAD;GAAyB,WAAU;aAAnC;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,iBAAqC,CAAA,EAC5D,kBAAC,GAAD,EAAA,UAA6B,oCAEC,CAAA,CACP,EAAA,CAAA;IACzB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO,EAAK;OACZ,WAAW,MAAS,EAAQ;QAAE,GAAG;QAAM;QAAM,CAAC;OAC9C,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAO;OACP,WAAW,MAAO,EAAQ;QAAE,GAAG;QAAM,WAAW;QAAI,CAAC;OACrD,OAAO;OACP,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,MAAK;OACL,OAAO,EAAK;OACZ,WAAW,MAAY,EAAQ;QAAE,GAAG;QAAM;QAAS,CAAC;OACpD,CAAA;MACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO,EAAK;OACZ,WAAW,MAAgB,EAAQ;QAAE,GAAG;QAAM;QAAa,CAAC;OAC5D,CAAA;MACE;;IACL,KAAS,kBAAC,GAAD,EAAW,SAAS,GAAS,CAAA;IACvC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAQ,SA1EK,YAAY;AAC/B,QAAS,KAAK;AACd,UAAI;OAaF,IAAM,KAZS,MAAM,EACnB,EAAO,EACL,WAAW,EACT,OAAO;QACL,MAAM,EAAK,KAAK,MAAM;QACtB;QACA,SAAS,EAAK,WAAW,KAAA;QACzB,aAAa,EAAK,YAAY,MAAM,IAAI,KAAA;QACzC,EACF,EACF,CAAC,CACH,EACiB,MAAM,0BAA0B;AAGlD,OAFA,EAAQ,EAAgB,EACxB,EAAQ,GAAM,EACV,KAAI,EAAU,EAAG;eACd,GAAG;AACV,SAAS,EAAU,GAAG,sCAAsC,CAAC;;;KAsD1B,UAAU,CAAC;KAAW,eAAY;eAC9D,IAAU,cAAc;KAClB,CAAA,EACc,CAAA;IACD;KACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"DocsDocDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/DocsDocDetailPagePage.tsx"],"names":[],"mappings":"AA4HA,wBAAgB,qBAAqB,4CAwZpC"}
1
+ {"version":3,"file":"DocsDocDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/DocsDocDetailPagePage.tsx"],"names":[],"mappings":"AA4HA,wBAAgB,qBAAqB,4CAyZpC"}
@@ -376,7 +376,7 @@ function A() {
376
376
  className: "space-y-2",
377
377
  children: [Y.linkedWorkItems.length === 0 && /* @__PURE__ */ o("p", {
378
378
  className: "text-sm text-muted-foreground",
379
- children: "Not linked to any work item. Link docs from a task's “Linked Docs” panel."
379
+ children: "Not linked to any work item. A doc created from a task's “Linked Docs” panel is linked to that task; linking an existing doc is not available yet."
380
380
  }), Y.linkedWorkItems.map((e) => /* @__PURE__ */ s("div", {
381
381
  className: "flex items-center justify-between gap-2 rounded-md border p-2",
382
382
  children: [/* @__PURE__ */ s("button", {