@burdenoff/microfe-planmagnet 2026.927.5 → 2026.927.6

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.
@@ -1,3 +1,5 @@
1
+ /** Whole days in the current status, or null when unknown. */
2
+ export declare function daysInStatus(statusSince: string | null, now: number): number | null;
1
3
  export declare function ProjectBoard({ projectId, projectName, }: {
2
4
  projectId: string;
3
5
  projectName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectBoard.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/board/ProjectBoard.tsx"],"names":[],"mappings":"AAwLA,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,2CAiKA"}
1
+ {"version":3,"file":"ProjectBoard.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/board/ProjectBoard.tsx"],"names":[],"mappings":"AAiNA,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGnF;AAgCD,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,2CAoPA"}
@@ -4,11 +4,11 @@ import { throwOnError as r } from "../utils/mutationResult.js";
4
4
  import { useState as i } from "react";
5
5
  import { ChevronLeft as a, ChevronRight as o, LayoutGrid as s, Pencil as c, Plus as l } from "lucide-react";
6
6
  import { gql as u } from "@apollo/client";
7
- import { useMutation as d, useQuery as f } from "@apollo/client/react";
8
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
9
- import { AlertDialog as h, AlertDialogAction as g, AlertDialogCancel as _, AlertDialogContent as v, AlertDialogDescription as y, AlertDialogFooter as b, AlertDialogHeader as x, AlertDialogTitle as S, AlertDialogTrigger as C, Button as w, Card as T, CardContent as E, Input as D, ResponsiveDialog as O, ResponsiveDialogContent as k, ResponsiveDialogDescription as A, ResponsiveDialogFooter as j, ResponsiveDialogHeader as M, ResponsiveDialogTitle as N, ResponsiveDialogTrigger as ee } from "@burdenoff/fe-libs/ui";
7
+ import { useApolloClient as d, useMutation as f, useQuery as p } from "@apollo/client/react";
8
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
9
+ import { AlertDialog as g, AlertDialogAction as _, AlertDialogCancel as v, AlertDialogContent as y, AlertDialogDescription as b, AlertDialogFooter as x, AlertDialogHeader as ee, AlertDialogTitle as S, AlertDialogTrigger as C, Button as w, Card as T, CardContent as E, Input as D, ResponsiveDialog as te, ResponsiveDialogContent as O, ResponsiveDialogDescription as k, ResponsiveDialogFooter as ne, ResponsiveDialogHeader as re, ResponsiveDialogTitle as A, ResponsiveDialogTrigger as j } from "@burdenoff/fe-libs/ui";
10
10
  //#region src/planmagnet/board/ProjectBoard.tsx
11
- var te = [
11
+ var M = [
12
12
  "BACKLOG",
13
13
  "TODO",
14
14
  "IN_PROGRESS",
@@ -16,7 +16,7 @@ var te = [
16
16
  "TESTING",
17
17
  "DONE",
18
18
  "CANCELLED"
19
- ], P = (e) => e.charAt(0) + e.slice(1).toLowerCase().replace(/_/g, " "), F = 100, I = u`
19
+ ], N = (e) => e.charAt(0) + e.slice(1).toLowerCase().replace(/_/g, " "), P = 100, F = u`
20
20
  query ProjectBoards($workspaceId: ID!, $projectId: String) {
21
21
  planmagnetBoards(
22
22
  workspaceId: $workspaceId
@@ -30,7 +30,30 @@ var te = [
30
30
  }
31
31
  }
32
32
  }
33
- `, L = u`
33
+ `, I = u`
34
+ query ProjectBoardColumnPage($boardId: ID!, $columnId: ID!, $after: String) {
35
+ planmagnetBoardColumnCards(
36
+ boardId: $boardId
37
+ columnId: $columnId
38
+ pagination: { first: 100, after: $after }
39
+ ) {
40
+ pageInfo {
41
+ hasNextPage
42
+ endCursor
43
+ }
44
+ items {
45
+ id
46
+ key
47
+ title
48
+ status
49
+ priority
50
+ storyPoints
51
+ assigneeId
52
+ statusSince
53
+ }
54
+ }
55
+ }
56
+ `, ie = u`
34
57
  query ProjectBoard($id: ID!) {
35
58
  planmagnetBoard(id: $id) {
36
59
  id
@@ -44,6 +67,10 @@ var te = [
44
67
  sortOrder
45
68
  cards(pagination: { first: 100 }) {
46
69
  totalCount
70
+ pageInfo {
71
+ hasNextPage
72
+ endCursor
73
+ }
47
74
  items {
48
75
  id
49
76
  key
@@ -52,434 +79,674 @@ var te = [
52
79
  priority
53
80
  storyPoints
54
81
  assigneeId
82
+ statusSince
55
83
  }
56
84
  }
57
85
  }
58
86
  }
59
87
  }
60
- `, R = u`
88
+ `, L = u`
61
89
  mutation ProjectBoardMoveCard($boardId: ID!, $input: PlanmagnetMoveCardInput!) {
62
90
  planmagnetMoveCard(boardId: $boardId, input: $input) {
63
91
  id
64
92
  }
65
93
  }
66
- `, z = u`
94
+ `, R = u`
67
95
  mutation ProjectBoardCreate($input: PlanmagnetCreateBoardInput!) {
68
96
  planmagnetCreateBoard(input: $input) {
69
97
  id
70
98
  }
71
99
  }
72
- `, B = u`
100
+ `, z = u`
73
101
  mutation ProjectBoardCreateColumn($input: PlanmagnetCreateBoardColumnInput!) {
74
102
  planmagnetCreateBoardColumn(input: $input) {
75
103
  id
76
104
  }
77
105
  }
78
- `, V = u`
106
+ `, B = u`
79
107
  mutation ProjectBoardUpdateColumn($id: ID!, $input: PlanmagnetUpdateBoardColumnInput!) {
80
108
  planmagnetUpdateBoardColumn(id: $id, input: $input) {
81
109
  id
82
110
  }
83
111
  }
84
- `, H = u`
112
+ `, V = u`
85
113
  mutation ProjectBoardDeleteColumn($id: ID!) {
86
114
  planmagnetDeleteBoardColumn(id: $id)
87
115
  }
88
- `, U = u`
116
+ `, H = u`
89
117
  mutation ProjectBoardReorder($boardId: ID!, $input: PlanmagnetReorderColumnsInput!) {
90
118
  planmagnetReorderColumns(boardId: $boardId, input: $input) {
91
119
  id
92
120
  }
93
121
  }
94
- `, W = [
122
+ `, U = [
95
123
  "ProjectBoard",
96
124
  "ProjectBoards",
97
125
  "GetWorkItems",
98
126
  "GetWorkItemCounts",
99
127
  "GetDashboardData"
100
- ], G = (e, t) => e instanceof Error ? e.message : t;
101
- function K({ message: e }) {
102
- return /* @__PURE__ */ p("p", {
128
+ ], W = [
129
+ "CRITICAL",
130
+ "HIGH",
131
+ "MEDIUM",
132
+ "LOW",
133
+ "NONE"
134
+ ], G = 864e5;
135
+ function K(e, t) {
136
+ return e ? Math.max(0, Math.floor((t - new Date(e).getTime()) / G)) : null;
137
+ }
138
+ function q(e) {
139
+ return e >= 14 ? "text-destructive font-medium" : e >= 7 ? "text-status-warning-text" : "text-muted-foreground";
140
+ }
141
+ function J(e, t) {
142
+ return t === "assignee" ? e.assigneeId ?? "" : t === "priority" ? e.priority || "NONE" : "";
143
+ }
144
+ var Y = (e, t) => e instanceof Error ? e.message : t;
145
+ function X({ message: e }) {
146
+ return /* @__PURE__ */ m("p", {
103
147
  role: "alert",
104
148
  className: "text-sm text-destructive",
105
149
  children: e
106
150
  });
107
151
  }
108
- function q({ projectId: t, projectName: n }) {
109
- let { workspaceId: a } = e(), [o, c] = i(null), [u, h] = i(null), { data: g, loading: _ } = f(I, {
152
+ function ae({ projectId: t, projectName: n }) {
153
+ let { workspaceId: a } = e(), o = d(), [c, u] = i(null), [g, _] = i(null), [v] = i(() => Date.now()), [y, b] = i("none"), [x, ee] = i({
154
+ key: null,
155
+ byColumn: {}
156
+ }), [S, C] = i(null), { data: D, loading: te } = p(F, {
110
157
  variables: {
111
158
  workspaceId: a,
112
159
  projectId: t
113
160
  },
114
161
  skip: !a
115
- }), v = g?.planmagnetBoards.items ?? [], y = (o && v.some((e) => e.id === o) ? o : null) ?? v.find((e) => e.isDefault)?.id ?? v[0]?.id ?? null, { data: b, loading: x } = f(L, {
116
- variables: { id: y },
117
- skip: !y
118
- }), S = b?.planmagnetBoard ?? null, [C, D] = d(z, {
119
- refetchQueries: W,
162
+ }), O = D?.planmagnetBoards.items ?? [], k = (c && O.some((e) => e.id === c) ? c : null) ?? O.find((e) => e.isDefault)?.id ?? O[0]?.id ?? null, { data: ne, loading: re } = p(ie, {
163
+ variables: { id: k },
164
+ skip: !k
165
+ }), A = ne?.planmagnetBoard ?? null, j = x.key === A ? x.byColumn : {}, M = (e) => {
166
+ let t = j[e.id];
167
+ return {
168
+ items: t ? [...e.cards.items, ...t.items] : e.cards.items,
169
+ pageInfo: t ? t.pageInfo : e.cards.pageInfo,
170
+ totalCount: e.cards.totalCount
171
+ };
172
+ }, N = async (e) => {
173
+ let t = M(e).pageInfo.endCursor;
174
+ if (!(!A || !t)) {
175
+ _(null), C(e.id);
176
+ try {
177
+ let n = await o.query({
178
+ query: I,
179
+ variables: {
180
+ boardId: A.id,
181
+ columnId: e.id,
182
+ after: t
183
+ },
184
+ fetchPolicy: "network-only"
185
+ }), r = n.data?.planmagnetBoardColumnCards;
186
+ if (!r) throw Error(n.error?.message ?? "The next cards could not be loaded.");
187
+ let i = j[e.id]?.items ?? [];
188
+ ee({
189
+ key: A,
190
+ byColumn: {
191
+ ...j,
192
+ [e.id]: {
193
+ items: [...i, ...r.items],
194
+ pageInfo: r.pageInfo
195
+ }
196
+ }
197
+ });
198
+ } catch (e) {
199
+ _(Y(e, "The next cards could not be loaded."));
200
+ } finally {
201
+ C(null);
202
+ }
203
+ }
204
+ }, [P, z] = f(R, {
205
+ refetchQueries: U,
120
206
  awaitRefetchQueries: !0
121
- }), [O, k] = d(R, {
122
- refetchQueries: W,
207
+ }), [B, V] = f(L, {
208
+ refetchQueries: U,
123
209
  awaitRefetchQueries: !0
124
- }), [A] = d(U, {
125
- refetchQueries: W,
210
+ }), [W] = f(H, {
211
+ refetchQueries: U,
126
212
  awaitRefetchQueries: !0
127
- }), j = async (e, t) => {
128
- h(null);
213
+ }), G = async (e, t) => {
214
+ _(null);
129
215
  try {
130
216
  await r(e());
131
217
  } catch (e) {
132
- h(G(e, t));
218
+ _(Y(e, t));
133
219
  }
134
- }, M = (e, t) => j(() => O({ variables: {
135
- boardId: y,
220
+ }, K = (e, t) => G(() => B({ variables: {
221
+ boardId: k,
136
222
  input: {
137
223
  workItemId: e,
138
224
  targetColumnId: t
139
225
  }
140
- } }), "The card could not be moved."), N = (e, t) => {
141
- if (!S) return;
142
- let n = S.columns.map((e) => e.id), r = e + t;
143
- if (!(r < 0 || r >= n.length)) return [n[e], n[r]] = [n[r], n[e]], j(() => A({ variables: {
144
- boardId: y,
226
+ } }), "The card could not be moved."), q = (e, t) => {
227
+ if (!A) return;
228
+ let n = A.columns.map((e) => e.id), r = e + t;
229
+ if (!(r < 0 || r >= n.length)) return [n[e], n[r]] = [n[r], n[e]], G(() => W({ variables: {
230
+ boardId: k,
145
231
  input: { columnIds: n }
146
232
  } }), "The columns could not be reordered.");
147
233
  };
148
- return _ && !g ? /* @__PURE__ */ p("p", {
234
+ return te && !D ? /* @__PURE__ */ m("p", {
149
235
  className: "text-sm text-muted-foreground p-4",
150
236
  children: "Loading board…"
151
- }) : y ? /* @__PURE__ */ m("div", {
237
+ }) : k ? /* @__PURE__ */ h("div", {
152
238
  className: "space-y-3",
153
239
  "data-testid": "project-board",
154
240
  children: [
155
- /* @__PURE__ */ m("div", {
241
+ /* @__PURE__ */ h("div", {
156
242
  className: "flex flex-wrap items-center gap-3",
157
243
  children: [
158
- v.length > 1 && /* @__PURE__ */ p("select", {
244
+ O.length > 1 && /* @__PURE__ */ m("select", {
159
245
  "aria-label": "Board",
160
246
  className: "rounded-md border border-input bg-background px-2 py-1 text-sm",
161
- value: y,
162
- onChange: (e) => c(e.target.value),
163
- children: v.map((e) => /* @__PURE__ */ p("option", {
247
+ value: k,
248
+ onChange: (e) => u(e.target.value),
249
+ children: O.map((e) => /* @__PURE__ */ m("option", {
164
250
  value: e.id,
165
251
  children: e.name
166
252
  }, e.id))
167
253
  }),
168
- S && S.unplacedCount > 0 && /* @__PURE__ */ m("p", {
254
+ A && /* @__PURE__ */ h("label", {
255
+ className: "flex items-center gap-1 text-sm",
256
+ children: [/* @__PURE__ */ m("span", {
257
+ className: "text-muted-foreground",
258
+ children: "Swimlanes"
259
+ }), /* @__PURE__ */ h("select", {
260
+ "aria-label": "Swimlanes",
261
+ "data-testid": "board-lanes",
262
+ className: "rounded-md border border-input bg-background px-2 py-1 text-sm",
263
+ value: y,
264
+ onChange: (e) => b(e.target.value),
265
+ children: [
266
+ /* @__PURE__ */ m("option", {
267
+ value: "none",
268
+ children: "None"
269
+ }),
270
+ /* @__PURE__ */ m("option", {
271
+ value: "assignee",
272
+ children: "By assignee"
273
+ }),
274
+ /* @__PURE__ */ m("option", {
275
+ value: "priority",
276
+ children: "By priority"
277
+ })
278
+ ]
279
+ })]
280
+ }),
281
+ A && A.unplacedCount > 0 && /* @__PURE__ */ h("p", {
169
282
  className: "text-xs text-muted-foreground",
170
283
  "data-testid": "board-unplaced",
171
284
  children: [
172
- S.unplacedCount,
285
+ A.unplacedCount,
173
286
  " work item",
174
- S.unplacedCount === 1 ? " is" : "s are",
287
+ A.unplacedCount === 1 ? " is" : "s are",
175
288
  " not on this board: ",
176
- S.unplacedCount === 1 ? "its status has" : "their status has",
289
+ A.unplacedCount === 1 ? "its status has" : "their status has",
177
290
  " no column."
178
291
  ]
179
292
  }),
180
- S && /* @__PURE__ */ p("div", {
293
+ A && /* @__PURE__ */ m("div", {
181
294
  className: "ml-auto",
182
- children: /* @__PURE__ */ p(Y, { boardId: S.id })
295
+ children: /* @__PURE__ */ m(ce, { boardId: A.id })
183
296
  })
184
297
  ]
185
298
  }),
186
- u && /* @__PURE__ */ p(K, { message: u }),
187
- x && !S ? /* @__PURE__ */ p("p", {
299
+ g && /* @__PURE__ */ m(X, { message: g }),
300
+ re && !A ? /* @__PURE__ */ m("p", {
188
301
  className: "text-sm text-muted-foreground p-4",
189
302
  children: "Loading board…"
190
- }) : /* @__PURE__ */ m("div", {
303
+ }) : A && y !== "none" ? /* @__PURE__ */ m(oe, {
304
+ board: A,
305
+ laneBy: y,
306
+ cardsOf: M,
307
+ now: v,
308
+ busy: V.loading,
309
+ loadingMore: S,
310
+ onLoadMore: (e) => void N(e),
311
+ onMove: K
312
+ }) : /* @__PURE__ */ h("div", {
191
313
  className: "flex gap-3 overflow-x-auto pb-2",
192
- children: [S?.columns.map((e, t) => /* @__PURE__ */ p(J, {
193
- boardId: S.id,
314
+ children: [A?.columns.map((e, t) => /* @__PURE__ */ m(se, {
315
+ boardId: A.id,
194
316
  column: e,
195
- columns: S.columns,
317
+ columns: A.columns,
318
+ cards: M(e),
196
319
  isFirst: t === 0,
197
- isLast: t === S.columns.length - 1,
198
- busy: k.loading,
199
- onMove: M,
200
- onShift: (e) => N(t, e)
201
- }, e.id)), S && S.columns.length === 0 && /* @__PURE__ */ p("p", {
320
+ isLast: t === A.columns.length - 1,
321
+ busy: V.loading,
322
+ now: v,
323
+ loadingMore: S === e.id,
324
+ onLoadMore: () => void N(e),
325
+ onMove: K,
326
+ onShift: (e) => q(t, e)
327
+ }, e.id)), A && A.columns.length === 0 && /* @__PURE__ */ m("p", {
202
328
  className: "text-sm text-muted-foreground p-4",
203
329
  children: "This board has no columns. Add one to start placing work."
204
330
  })]
205
331
  })
206
332
  ]
207
- }) : /* @__PURE__ */ p(T, { children: /* @__PURE__ */ m(E, {
333
+ }) : /* @__PURE__ */ m(T, { children: /* @__PURE__ */ h(E, {
208
334
  className: "p-8 text-center space-y-3",
209
335
  "data-testid": "board-empty",
210
336
  children: [
211
- /* @__PURE__ */ p(s, { className: "h-8 w-8 mx-auto text-muted-foreground" }),
212
- /* @__PURE__ */ m("p", {
337
+ /* @__PURE__ */ m(s, { className: "h-8 w-8 mx-auto text-muted-foreground" }),
338
+ /* @__PURE__ */ h("p", {
213
339
  className: "text-sm text-muted-foreground",
214
340
  children: [n, " has no board yet."]
215
341
  }),
216
- /* @__PURE__ */ m(w, {
342
+ /* @__PURE__ */ h(w, {
217
343
  size: "sm",
218
- disabled: D.loading,
219
- onClick: () => j(() => C({ variables: { input: {
344
+ disabled: z.loading,
345
+ onClick: () => G(() => P({ variables: { input: {
220
346
  projectId: t,
221
347
  name: `${n} board`,
222
348
  type: "KANBAN",
223
349
  isDefault: !0,
224
350
  withDefaultColumns: !0
225
351
  } } }), "The board could not be created."),
226
- children: [/* @__PURE__ */ p(l, { className: "mr-1 h-4 w-4" }), D.loading ? "Creating…" : "Create board"]
352
+ children: [/* @__PURE__ */ m(l, { className: "mr-1 h-4 w-4" }), z.loading ? "Creating…" : "Create board"]
227
353
  }),
228
- /* @__PURE__ */ p("p", {
354
+ /* @__PURE__ */ m("p", {
229
355
  className: "text-xs text-muted-foreground",
230
356
  children: "Starts with one column per status: Backlog, To Do, In Progress, In Review, Testing, Done."
231
357
  }),
232
- u && /* @__PURE__ */ p(K, { message: u })
358
+ g && /* @__PURE__ */ m(X, { message: g })
233
359
  ]
234
360
  }) });
235
361
  }
236
- function J({ boardId: e, column: r, columns: s, isFirst: c, isLast: l, busy: u, onMove: d, onShift: f }) {
237
- let h = t(), [g, _] = i(!1), v = r.cards.totalCount, y = r.wipLimit != null && r.wipLimit > 0 && v >= r.wipLimit, b = v - r.cards.items.length;
362
+ function Z({ card: e, column: r, columns: i, busy: a, now: o, onMove: s }) {
363
+ let c = t(), l = K(e.statusSince, o);
364
+ return /* @__PURE__ */ h("li", {
365
+ draggable: !0,
366
+ onDragStart: (t) => {
367
+ t.dataTransfer.setData("text/plain", e.id), t.dataTransfer.effectAllowed = "move";
368
+ },
369
+ className: "rounded-md border bg-background p-2 space-y-1 cursor-grab",
370
+ "data-testid": "board-card",
371
+ children: [
372
+ /* @__PURE__ */ m("button", {
373
+ type: "button",
374
+ className: "text-left text-sm font-medium leading-tight hover:underline",
375
+ onClick: () => c(`/tasks/task-detail-page?taskId=${encodeURIComponent(e.id)}`),
376
+ children: e.title
377
+ }),
378
+ /* @__PURE__ */ h("div", {
379
+ className: "flex items-center justify-between text-[11px] text-muted-foreground",
380
+ children: [
381
+ /* @__PURE__ */ m("span", { children: e.key }),
382
+ l !== null && /* @__PURE__ */ m("span", {
383
+ "data-testid": "card-age",
384
+ className: q(l),
385
+ title: `In ${N(e.status)} since ${new Date(e.statusSince ?? "").toLocaleDateString()}`,
386
+ children: l === 0 ? "today" : `${l}d in status`
387
+ }),
388
+ /* @__PURE__ */ h("span", { children: [e.storyPoints ?? 0, " pts"] })
389
+ ]
390
+ }),
391
+ /* @__PURE__ */ h("div", {
392
+ className: "flex items-center justify-between gap-1 text-[11px] text-muted-foreground",
393
+ children: [/* @__PURE__ */ m("span", {
394
+ className: "truncate",
395
+ children: e.assigneeId ? /* @__PURE__ */ m(n, { id: e.assigneeId }) : "Unassigned"
396
+ }), /* @__PURE__ */ h("select", {
397
+ "aria-label": `Move ${e.key} to column`,
398
+ className: "rounded border border-input bg-background px-1 py-0.5 text-[11px]",
399
+ value: "",
400
+ disabled: a,
401
+ onChange: (t) => t.target.value && s(e.id, t.target.value),
402
+ children: [/* @__PURE__ */ m("option", {
403
+ value: "",
404
+ children: "Move…"
405
+ }), i.filter((e) => e.id !== r.id).map((e) => /* @__PURE__ */ m("option", {
406
+ value: e.id,
407
+ children: e.name
408
+ }, e.id))]
409
+ })]
410
+ })
411
+ ]
412
+ });
413
+ }
414
+ function Q({ column: e, onMove: t, className: n, children: r, label: a }) {
415
+ let [o, s] = i(!1);
238
416
  return /* @__PURE__ */ m("section", {
239
- "aria-label": `${r.name} column`,
240
- "data-testid": `board-column-${r.name}`,
241
- className: `min-w-[240px] w-[240px] shrink-0 rounded-md border bg-muted/30 ${g ? "ring-2 ring-primary" : ""}`,
417
+ "aria-label": a,
418
+ className: `${n} ${o ? "ring-2 ring-primary" : ""}`,
242
419
  onDragOver: (e) => {
243
- e.preventDefault(), _(!0);
420
+ e.preventDefault(), s(!0);
244
421
  },
245
- onDragLeave: () => _(!1),
246
- onDrop: (e) => {
247
- e.preventDefault(), _(!1);
248
- let t = e.dataTransfer.getData("text/plain");
249
- t && d(t, r.id);
422
+ onDragLeave: () => s(!1),
423
+ onDrop: (n) => {
424
+ n.preventDefault(), s(!1);
425
+ let r = n.dataTransfer.getData("text/plain");
426
+ r && t(r, e.id);
250
427
  },
428
+ children: r
429
+ });
430
+ }
431
+ function $({ cards: e, loading: t, onLoadMore: n }) {
432
+ let r = e.totalCount - e.items.length;
433
+ return r <= 0 || !e.pageInfo.hasNextPage ? null : /* @__PURE__ */ h("div", {
434
+ className: "px-2 pb-2 space-y-1",
435
+ children: [/* @__PURE__ */ h("p", {
436
+ className: "text-xs text-muted-foreground",
437
+ children: [
438
+ "Showing ",
439
+ e.items.length,
440
+ " of ",
441
+ e.totalCount
442
+ ]
443
+ }), /* @__PURE__ */ m(w, {
444
+ size: "sm",
445
+ variant: "outline",
446
+ className: "w-full",
447
+ "data-testid": "board-column-more",
448
+ disabled: t,
449
+ onClick: n,
450
+ children: t ? "Loading…" : `Show ${Math.min(r, P)} more`
451
+ })]
452
+ });
453
+ }
454
+ function oe({ board: e, laneBy: t, cardsOf: r, now: i, busy: a, loadingMore: o, onLoadMore: s, onMove: c }) {
455
+ let l = e.columns.map((e) => ({
456
+ column: e,
457
+ cards: r(e)
458
+ })), u = [...new Set(l.flatMap(({ cards: e }) => e.items.map((e) => J(e, t))))].sort((e, n) => t === "priority" ? W.indexOf(e) - W.indexOf(n) : e === "" ? 1 : n === "" ? -1 : e.localeCompare(n)), d = (e) => t === "priority" ? N(e) : e ? /* @__PURE__ */ m(n, { id: e }) : "Unassigned";
459
+ return /* @__PURE__ */ h("div", {
460
+ className: "overflow-x-auto pb-2 space-y-3",
461
+ "data-testid": "board-swimlanes",
251
462
  children: [
252
- /* @__PURE__ */ m("header", {
253
- className: "p-2 border-b space-y-1",
254
- children: [/* @__PURE__ */ m("div", {
255
- className: "flex items-center justify-between gap-1",
256
- children: [/* @__PURE__ */ p("h3", {
463
+ /* @__PURE__ */ h("p", {
464
+ className: "text-xs text-muted-foreground",
465
+ children: [
466
+ "Dragging moves a card between columns. Its lane follows its",
467
+ " ",
468
+ t === "priority" ? "priority" : "assignee",
469
+ "."
470
+ ]
471
+ }),
472
+ /* @__PURE__ */ m("div", {
473
+ className: "flex gap-3",
474
+ children: l.map(({ column: e, cards: t }) => /* @__PURE__ */ m("div", {
475
+ className: "min-w-[240px] w-[240px] shrink-0 px-2",
476
+ children: /* @__PURE__ */ h("h3", {
257
477
  className: "text-sm font-medium truncate",
258
- children: r.name
259
- }), /* @__PURE__ */ p("span", {
260
- "data-testid": `board-column-count-${r.name}`,
261
- className: `text-xs rounded-full px-1.5 ${y ? "bg-status-warning-subtle text-status-warning" : "bg-muted"}`,
262
- title: r.wipLimit ? `WIP limit ${r.wipLimit}` : void 0,
263
- children: r.wipLimit ? `${v} / ${r.wipLimit}` : v
264
- })]
265
- }), /* @__PURE__ */ m("div", {
266
- className: "flex items-center justify-between gap-1",
267
- children: [/* @__PURE__ */ p("span", {
268
- className: "text-[11px] text-muted-foreground",
269
- children: r.mappedStatus ? `Sets status: ${P(r.mappedStatus)}` : "No status"
270
- }), /* @__PURE__ */ m("span", {
271
- className: "flex",
272
478
  children: [
273
- /* @__PURE__ */ p(w, {
274
- size: "icon",
275
- variant: "ghost",
276
- className: "h-6 w-6",
277
- "aria-label": `Move ${r.name} column left`,
278
- disabled: c,
279
- onClick: () => f(-1),
280
- children: /* @__PURE__ */ p(a, { className: "h-3.5 w-3.5" })
281
- }),
282
- /* @__PURE__ */ p(w, {
283
- size: "icon",
284
- variant: "ghost",
285
- className: "h-6 w-6",
286
- "aria-label": `Move ${r.name} column right`,
287
- disabled: l,
288
- onClick: () => f(1),
289
- children: /* @__PURE__ */ p(o, { className: "h-3.5 w-3.5" })
290
- }),
291
- /* @__PURE__ */ p(Y, {
292
- boardId: e,
293
- column: r
479
+ e.name,
480
+ " ",
481
+ /* @__PURE__ */ m("span", {
482
+ className: "text-xs text-muted-foreground",
483
+ children: e.wipLimit ? `${t.totalCount} / ${e.wipLimit}` : t.totalCount
294
484
  })
295
485
  ]
296
- })]
297
- })]
486
+ })
487
+ }, e.id))
298
488
  }),
299
- /* @__PURE__ */ m("ul", {
300
- className: "p-2 space-y-2 min-h-[80px]",
301
- children: [r.cards.items.map((e) => /* @__PURE__ */ m("li", {
302
- draggable: !0,
303
- onDragStart: (t) => {
304
- t.dataTransfer.setData("text/plain", e.id), t.dataTransfer.effectAllowed = "move";
305
- },
306
- className: "rounded-md border bg-background p-2 space-y-1 cursor-grab",
307
- "data-testid": "board-card",
489
+ u.length === 0 && /* @__PURE__ */ m("p", {
490
+ className: "text-sm text-muted-foreground p-4",
491
+ children: "No cards on this board yet."
492
+ }),
493
+ u.map((n) => /* @__PURE__ */ h("div", {
494
+ "data-testid": "board-lane",
495
+ className: "space-y-1",
496
+ children: [/* @__PURE__ */ h("p", {
497
+ className: "text-xs font-medium",
308
498
  children: [
309
- /* @__PURE__ */ p("button", {
310
- type: "button",
311
- className: "text-left text-sm font-medium leading-tight hover:underline",
312
- onClick: () => h(`/tasks/task-detail-page?taskId=${encodeURIComponent(e.id)}`),
313
- children: e.title
314
- }),
315
- /* @__PURE__ */ m("div", {
316
- className: "flex items-center justify-between text-[11px] text-muted-foreground",
317
- children: [/* @__PURE__ */ p("span", { children: e.key }), /* @__PURE__ */ m("span", { children: [e.storyPoints ?? 0, " pts"] })]
318
- }),
319
- /* @__PURE__ */ m("div", {
320
- className: "flex items-center justify-between gap-1 text-[11px] text-muted-foreground",
321
- children: [/* @__PURE__ */ p("span", {
322
- className: "truncate",
323
- children: e.assigneeId ? /* @__PURE__ */ p(n, { id: e.assigneeId }) : "Unassigned"
324
- }), /* @__PURE__ */ m("select", {
325
- "aria-label": `Move ${e.key} to column`,
326
- className: "rounded border border-input bg-background px-1 py-0.5 text-[11px]",
327
- value: "",
328
- disabled: u,
329
- onChange: (t) => t.target.value && d(e.id, t.target.value),
330
- children: [/* @__PURE__ */ p("option", {
331
- value: "",
332
- children: "Move…"
333
- }), s.filter((e) => e.id !== r.id).map((e) => /* @__PURE__ */ p("option", {
334
- value: e.id,
335
- children: e.name
336
- }, e.id))]
337
- })]
499
+ d(n),
500
+ " ",
501
+ /* @__PURE__ */ h("span", {
502
+ className: "text-muted-foreground",
503
+ children: [
504
+ "(",
505
+ l.reduce((e, { cards: r }) => e + r.items.filter((e) => J(e, t) === n).length, 0),
506
+ ")"
507
+ ]
338
508
  })
339
509
  ]
340
- }, e.id)), v === 0 && /* @__PURE__ */ p("li", {
341
- className: "text-xs text-muted-foreground text-center py-4",
342
- children: "Empty"
510
+ }), /* @__PURE__ */ m("div", {
511
+ className: "flex gap-3",
512
+ children: l.map(({ column: r, cards: o }) => /* @__PURE__ */ m(Q, {
513
+ column: r,
514
+ onMove: c,
515
+ label: `${r.name} column`,
516
+ className: "min-w-[240px] w-[240px] shrink-0 rounded-md border bg-muted/30",
517
+ children: /* @__PURE__ */ m("ul", {
518
+ className: "p-2 space-y-2 min-h-[48px]",
519
+ children: o.items.filter((e) => J(e, t) === n).map((t) => /* @__PURE__ */ m(Z, {
520
+ card: t,
521
+ column: r,
522
+ columns: e.columns,
523
+ busy: a,
524
+ now: i,
525
+ onMove: c
526
+ }, t.id))
527
+ })
528
+ }, r.id))
343
529
  })]
344
- }),
345
- b > 0 && /* @__PURE__ */ m("p", {
346
- className: "px-2 pb-2 text-xs text-muted-foreground",
347
- children: [
348
- "+",
349
- b,
350
- " more (showing the first ",
351
- F,
352
- ")"
353
- ]
530
+ }, n || "none")),
531
+ /* @__PURE__ */ m("div", {
532
+ className: "flex gap-3",
533
+ children: l.map(({ column: e, cards: t }) => /* @__PURE__ */ m("div", {
534
+ className: "min-w-[240px] w-[240px] shrink-0",
535
+ children: /* @__PURE__ */ m($, {
536
+ cards: t,
537
+ loading: o === e.id,
538
+ onLoadMore: () => s(e)
539
+ })
540
+ }, e.id))
354
541
  })
355
542
  ]
356
543
  });
357
544
  }
358
- function Y({ boardId: e, column: t }) {
359
- let n = t !== void 0, [a, o] = i(!1), [s, u] = i(t?.name ?? ""), [f, T] = i(t?.mappedStatus ?? ""), [E, F] = i(t?.wipLimit ? String(t.wipLimit) : ""), [I, L] = i(null), R = {
360
- refetchQueries: W,
545
+ function se({ boardId: e, column: t, columns: n, cards: r, isFirst: i, isLast: s, busy: c, now: l, loadingMore: u, onLoadMore: d, onMove: f, onShift: p }) {
546
+ let g = r.totalCount, _ = t.wipLimit != null && t.wipLimit > 0 && g >= t.wipLimit;
547
+ return /* @__PURE__ */ m(Q, {
548
+ column: t,
549
+ onMove: f,
550
+ label: `${t.name} column`,
551
+ className: "min-w-[240px] w-[240px] shrink-0 rounded-md border bg-muted/30",
552
+ children: /* @__PURE__ */ h("div", {
553
+ "data-testid": `board-column-${t.name}`,
554
+ children: [
555
+ /* @__PURE__ */ h("header", {
556
+ className: "p-2 border-b space-y-1",
557
+ children: [/* @__PURE__ */ h("div", {
558
+ className: "flex items-center justify-between gap-1",
559
+ children: [/* @__PURE__ */ m("h3", {
560
+ className: "text-sm font-medium truncate",
561
+ children: t.name
562
+ }), /* @__PURE__ */ m("span", {
563
+ "data-testid": `board-column-count-${t.name}`,
564
+ className: `text-xs rounded-full px-1.5 ${_ ? "bg-status-warning-subtle text-status-warning" : "bg-muted"}`,
565
+ title: t.wipLimit ? `WIP limit ${t.wipLimit}` : void 0,
566
+ children: t.wipLimit ? `${g} / ${t.wipLimit}` : g
567
+ })]
568
+ }), /* @__PURE__ */ h("div", {
569
+ className: "flex items-center justify-between gap-1",
570
+ children: [/* @__PURE__ */ m("span", {
571
+ className: "text-[11px] text-muted-foreground",
572
+ children: t.mappedStatus ? `Sets status: ${N(t.mappedStatus)}` : "No status"
573
+ }), /* @__PURE__ */ h("span", {
574
+ className: "flex",
575
+ children: [
576
+ /* @__PURE__ */ m(w, {
577
+ size: "icon",
578
+ variant: "ghost",
579
+ className: "h-6 w-6",
580
+ "aria-label": `Move ${t.name} column left`,
581
+ disabled: i,
582
+ onClick: () => p(-1),
583
+ children: /* @__PURE__ */ m(a, { className: "h-3.5 w-3.5" })
584
+ }),
585
+ /* @__PURE__ */ m(w, {
586
+ size: "icon",
587
+ variant: "ghost",
588
+ className: "h-6 w-6",
589
+ "aria-label": `Move ${t.name} column right`,
590
+ disabled: s,
591
+ onClick: () => p(1),
592
+ children: /* @__PURE__ */ m(o, { className: "h-3.5 w-3.5" })
593
+ }),
594
+ /* @__PURE__ */ m(ce, {
595
+ boardId: e,
596
+ column: t
597
+ })
598
+ ]
599
+ })]
600
+ })]
601
+ }),
602
+ /* @__PURE__ */ h("ul", {
603
+ className: "p-2 space-y-2 min-h-[80px]",
604
+ children: [r.items.map((e) => /* @__PURE__ */ m(Z, {
605
+ card: e,
606
+ column: t,
607
+ columns: n,
608
+ busy: c,
609
+ now: l,
610
+ onMove: f
611
+ }, e.id)), g === 0 && /* @__PURE__ */ m("li", {
612
+ className: "text-xs text-muted-foreground text-center py-4",
613
+ children: "Empty"
614
+ })]
615
+ }),
616
+ /* @__PURE__ */ m($, {
617
+ cards: r,
618
+ loading: u,
619
+ onLoadMore: d
620
+ })
621
+ ]
622
+ })
623
+ });
624
+ }
625
+ function ce({ boardId: e, column: t }) {
626
+ let n = t !== void 0, [a, o] = i(!1), [s, u] = i(t?.name ?? ""), [d, p] = i(t?.mappedStatus ?? ""), [T, E] = i(t?.wipLimit ? String(t.wipLimit) : ""), [P, F] = i(null), I = {
627
+ refetchQueries: U,
361
628
  awaitRefetchQueries: !0
362
- }, [z, U] = d(B, R), [q, J] = d(V, R), [Y, ne] = d(H, R), X = U.loading || J.loading || ne.loading, Z = E.trim() === "" ? null : Number(E), Q = Z !== null && (!Number.isInteger(Z) || Z < 1), re = s.trim() !== "" && !Q && !X, ie = () => {
363
- u(t?.name ?? ""), T(t?.mappedStatus ?? ""), F(t?.wipLimit ? String(t.wipLimit) : ""), L(null);
364
- }, ae = async () => {
365
- L(null);
629
+ }, [ie, L] = f(z, I), [R, H] = f(B, I), [W, G] = f(V, I), K = L.loading || H.loading || G.loading, q = T.trim() === "" ? null : Number(T), J = q !== null && (!Number.isInteger(q) || q < 1), ae = s.trim() !== "" && !J && !K, Z = () => {
630
+ u(t?.name ?? ""), p(t?.mappedStatus ?? ""), E(t?.wipLimit ? String(t.wipLimit) : ""), F(null);
631
+ }, Q = async () => {
632
+ F(null);
366
633
  let i = {
367
634
  name: s.trim(),
368
- mappedStatus: f || null,
369
- wipLimit: Z
635
+ mappedStatus: d || null,
636
+ wipLimit: q
370
637
  };
371
638
  try {
372
- n ? await r(q({ variables: {
639
+ n ? await r(R({ variables: {
373
640
  id: t.id,
374
641
  input: i
375
- } })) : await r(z({ variables: { input: {
642
+ } })) : await r(ie({ variables: { input: {
376
643
  boardId: e,
377
644
  type: "CUSTOM",
378
645
  ...i
379
646
  } } })), o(!1);
380
647
  } catch (e) {
381
- L(G(e, "The column could not be saved."));
648
+ F(Y(e, "The column could not be saved."));
382
649
  }
383
650
  }, $ = n ? `column-${t.id}` : "new-column";
384
- return /* @__PURE__ */ m(O, {
651
+ return /* @__PURE__ */ h(te, {
385
652
  open: a,
386
653
  onOpenChange: (e) => {
387
- o(e), e && ie();
654
+ o(e), e && Z();
388
655
  },
389
- children: [/* @__PURE__ */ p(ee, {
656
+ children: [/* @__PURE__ */ m(j, {
390
657
  asChild: !0,
391
- children: n ? /* @__PURE__ */ p(w, {
658
+ children: n ? /* @__PURE__ */ m(w, {
392
659
  size: "icon",
393
660
  variant: "ghost",
394
661
  className: "h-6 w-6",
395
662
  "aria-label": `Edit ${t.name} column`,
396
- children: /* @__PURE__ */ p(c, { className: "h-3.5 w-3.5" })
397
- }) : /* @__PURE__ */ m(w, {
663
+ children: /* @__PURE__ */ m(c, { className: "h-3.5 w-3.5" })
664
+ }) : /* @__PURE__ */ h(w, {
398
665
  size: "sm",
399
666
  variant: "outline",
400
- children: [/* @__PURE__ */ p(l, { className: "mr-1 h-4 w-4" }), "Add column"]
667
+ children: [/* @__PURE__ */ m(l, { className: "mr-1 h-4 w-4" }), "Add column"]
401
668
  })
402
- }), /* @__PURE__ */ m(k, {
669
+ }), /* @__PURE__ */ h(O, {
403
670
  className: "max-w-md",
404
671
  children: [
405
- /* @__PURE__ */ m(M, { children: [/* @__PURE__ */ p(N, { children: n ? `Edit ${t.name}` : "Add column" }), /* @__PURE__ */ p(A, { children: "Moving a card into a column that sets a status changes the work item's status." })] }),
406
- /* @__PURE__ */ m("div", {
672
+ /* @__PURE__ */ h(re, { children: [/* @__PURE__ */ m(A, { children: n ? `Edit ${t.name}` : "Add column" }), /* @__PURE__ */ m(k, { children: "Moving a card into a column that sets a status changes the work item's status." })] }),
673
+ /* @__PURE__ */ h("div", {
407
674
  className: "space-y-3",
408
675
  children: [
409
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
676
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
410
677
  htmlFor: `${$}-name`,
411
678
  className: "text-sm font-medium",
412
679
  children: "Name"
413
- }), /* @__PURE__ */ p(D, {
680
+ }), /* @__PURE__ */ m(D, {
414
681
  id: `${$}-name`,
415
682
  className: "mt-1",
416
683
  value: s,
417
684
  onChange: (e) => u(e.target.value)
418
685
  })] }),
419
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
686
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
420
687
  htmlFor: `${$}-status`,
421
688
  className: "text-sm font-medium",
422
689
  children: "Sets status"
423
- }), /* @__PURE__ */ m("select", {
690
+ }), /* @__PURE__ */ h("select", {
424
691
  id: `${$}-status`,
425
692
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm",
426
- value: f,
427
- onChange: (e) => T(e.target.value),
428
- children: [/* @__PURE__ */ p("option", {
693
+ value: d,
694
+ onChange: (e) => p(e.target.value),
695
+ children: [/* @__PURE__ */ m("option", {
429
696
  value: "",
430
697
  children: "No status (cards keep theirs)"
431
- }), te.map((e) => /* @__PURE__ */ p("option", {
698
+ }), M.map((e) => /* @__PURE__ */ m("option", {
432
699
  value: e,
433
- children: P(e)
700
+ children: N(e)
434
701
  }, e))]
435
702
  })] }),
436
- /* @__PURE__ */ m("div", { children: [
437
- /* @__PURE__ */ p("label", {
703
+ /* @__PURE__ */ h("div", { children: [
704
+ /* @__PURE__ */ m("label", {
438
705
  htmlFor: `${$}-wip`,
439
706
  className: "text-sm font-medium",
440
707
  children: "WIP limit (blank for none)"
441
708
  }),
442
- /* @__PURE__ */ p(D, {
709
+ /* @__PURE__ */ m(D, {
443
710
  id: `${$}-wip`,
444
711
  type: "number",
445
712
  min: 1,
446
713
  className: "mt-1",
447
- value: E,
448
- onChange: (e) => F(e.target.value)
714
+ value: T,
715
+ onChange: (e) => E(e.target.value)
449
716
  }),
450
- Q && /* @__PURE__ */ p(K, { message: "The WIP limit must be a whole number, 1 or more." })
717
+ J && /* @__PURE__ */ m(X, { message: "The WIP limit must be a whole number, 1 or more." })
451
718
  ] })
452
719
  ]
453
720
  }),
454
- I && /* @__PURE__ */ p(K, { message: I }),
455
- /* @__PURE__ */ m(j, {
721
+ P && /* @__PURE__ */ m(X, { message: P }),
722
+ /* @__PURE__ */ h(ne, {
456
723
  className: "gap-2",
457
- children: [n && /* @__PURE__ */ m(h, { children: [/* @__PURE__ */ p(C, {
724
+ children: [n && /* @__PURE__ */ h(g, { children: [/* @__PURE__ */ m(C, {
458
725
  asChild: !0,
459
- children: /* @__PURE__ */ p(w, {
726
+ children: /* @__PURE__ */ m(w, {
460
727
  variant: "outline",
461
- disabled: X,
728
+ disabled: K,
462
729
  children: "Delete column"
463
730
  })
464
- }), /* @__PURE__ */ m(v, { children: [/* @__PURE__ */ m(x, { children: [/* @__PURE__ */ m(S, { children: [
731
+ }), /* @__PURE__ */ h(y, { children: [/* @__PURE__ */ h(ee, { children: [/* @__PURE__ */ h(S, { children: [
465
732
  "Delete ",
466
733
  t.name,
467
734
  "?"
468
- ] }), /* @__PURE__ */ p(y, { children: "Its cards are not deleted: each goes back to the column for its status, or off the board if no column sets that status." })] }), /* @__PURE__ */ m(b, { children: [/* @__PURE__ */ p(_, { children: "Keep it" }), /* @__PURE__ */ p(g, {
735
+ ] }), /* @__PURE__ */ m(b, { children: "Its cards are not deleted: each goes back to the column for its status, or off the board if no column sets that status." })] }), /* @__PURE__ */ h(x, { children: [/* @__PURE__ */ m(v, { children: "Keep it" }), /* @__PURE__ */ m(_, {
469
736
  className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
470
737
  onClick: async () => {
471
738
  try {
472
- await r(Y({ variables: { id: t.id } })), o(!1);
739
+ await r(W({ variables: { id: t.id } })), o(!1);
473
740
  } catch (e) {
474
- L(G(e, "The column could not be deleted."));
741
+ F(Y(e, "The column could not be deleted."));
475
742
  }
476
743
  },
477
744
  children: "Delete column"
478
- })] })] })] }), /* @__PURE__ */ p(w, {
479
- onClick: ae,
480
- disabled: !re,
745
+ })] })] })] }), /* @__PURE__ */ m(w, {
746
+ onClick: Q,
747
+ disabled: !ae,
481
748
  "data-testid": `${$}-save`,
482
- children: X ? "Saving…" : n ? "Save" : "Add column"
749
+ children: K ? "Saving…" : n ? "Save" : "Add column"
483
750
  })]
484
751
  })
485
752
  ]
@@ -487,6 +754,6 @@ function Y({ boardId: e, column: t }) {
487
754
  });
488
755
  }
489
756
  //#endregion
490
- export { q as ProjectBoard };
757
+ export { ae as ProjectBoard };
491
758
 
492
759
  //# sourceMappingURL=ProjectBoard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectBoard.js","names":[],"sources":["../../../src/planmagnet/board/ProjectBoard.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Button,\n Card,\n CardContent,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { ChevronLeft, ChevronRight, LayoutGrid, Pencil, Plus } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { PersonName } from '../people/People';\nimport { throwOnError } from '../utils/mutationResult';\n\n/**\n * A project's board (P1.2). The server owns placement: each column's `cards`\n * follow the rule in wspace-planmagnet-svc board.service.ts `columnCardsWhere`\n * (placed column, else the first column mapped to the item's status), and\n * `unplacedCount` says how many items are on no column. Moving a card into a\n * column mapped to a status changes that status on the server, and a column's\n * WIP limit is enforced there too. This component only draws and asks.\n */\n\nconst STATUSES = [\n 'BACKLOG',\n 'TODO',\n 'IN_PROGRESS',\n 'IN_REVIEW',\n 'TESTING',\n 'DONE',\n 'CANCELLED',\n] as const;\nconst statusLabel = (status: string) =>\n status.charAt(0) + status.slice(1).toLowerCase().replace(/_/g, ' ');\n\nconst CARDS_PER_COLUMN = 100;\n\nconst BOARDS = gql`\n query ProjectBoards($workspaceId: ID!, $projectId: String) {\n planmagnetBoards(\n workspaceId: $workspaceId\n filter: { projectId: $projectId }\n pagination: { first: 20 }\n ) {\n items {\n id\n name\n isDefault\n }\n }\n }\n`;\n\nconst BOARD = gql`\n query ProjectBoard($id: ID!) {\n planmagnetBoard(id: $id) {\n id\n name\n unplacedCount\n columns {\n id\n name\n mappedStatus\n wipLimit\n sortOrder\n cards(pagination: { first: 100 }) {\n totalCount\n items {\n id\n key\n title\n status\n priority\n storyPoints\n assigneeId\n }\n }\n }\n }\n }\n`;\n\nconst MOVE_CARD = gql`\n mutation ProjectBoardMoveCard($boardId: ID!, $input: PlanmagnetMoveCardInput!) {\n planmagnetMoveCard(boardId: $boardId, input: $input) {\n id\n }\n }\n`;\nconst CREATE_BOARD = gql`\n mutation ProjectBoardCreate($input: PlanmagnetCreateBoardInput!) {\n planmagnetCreateBoard(input: $input) {\n id\n }\n }\n`;\nconst CREATE_COLUMN = gql`\n mutation ProjectBoardCreateColumn($input: PlanmagnetCreateBoardColumnInput!) {\n planmagnetCreateBoardColumn(input: $input) {\n id\n }\n }\n`;\nconst UPDATE_COLUMN = gql`\n mutation ProjectBoardUpdateColumn($id: ID!, $input: PlanmagnetUpdateBoardColumnInput!) {\n planmagnetUpdateBoardColumn(id: $id, input: $input) {\n id\n }\n }\n`;\nconst DELETE_COLUMN = gql`\n mutation ProjectBoardDeleteColumn($id: ID!) {\n planmagnetDeleteBoardColumn(id: $id)\n }\n`;\nconst REORDER_COLUMNS = gql`\n mutation ProjectBoardReorder($boardId: ID!, $input: PlanmagnetReorderColumnsInput!) {\n planmagnetReorderColumns(boardId: $boardId, input: $input) {\n id\n }\n }\n`;\n\n/** Every query whose numbers a board change can move. */\nconst REFETCH = [\n 'ProjectBoard',\n 'ProjectBoards',\n 'GetWorkItems',\n 'GetWorkItemCounts',\n 'GetDashboardData',\n];\n\ninterface BoardCard {\n id: string;\n key: string;\n title: string;\n status: string;\n priority: string;\n storyPoints: number | null;\n assigneeId: string | null;\n}\n\ninterface BoardColumn {\n id: string;\n name: string;\n mappedStatus: string | null;\n wipLimit: number | null;\n sortOrder: number;\n cards: { totalCount: number; items: BoardCard[] };\n}\n\ninterface BoardData {\n id: string;\n name: string;\n unplacedCount: number;\n columns: BoardColumn[];\n}\n\nconst messageOf = (e: unknown, fallback: string) => (e instanceof Error ? e.message : fallback);\n\nfunction ErrorLine({ message }: { message: string }) {\n return (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {message}\n </p>\n );\n}\n\nexport function ProjectBoard({\n projectId,\n projectName,\n}: {\n projectId: string;\n projectName: string;\n}) {\n const { workspaceId } = usePlanMagnet();\n const [chosenBoardId, setChosenBoardId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const { data: boardsData, loading: boardsLoading } = useQuery<{\n planmagnetBoards: { items: Array<{ id: string; name: string; isDefault: boolean }> };\n }>(BOARDS, { variables: { workspaceId, projectId }, skip: !workspaceId });\n const boards = boardsData?.planmagnetBoards.items ?? [];\n const boardId =\n (chosenBoardId && boards.some((b) => b.id === chosenBoardId) ? chosenBoardId : null) ??\n boards.find((b) => b.isDefault)?.id ??\n boards[0]?.id ??\n null;\n\n const { data, loading } = useQuery<{ planmagnetBoard: BoardData | null }>(BOARD, {\n variables: { id: boardId },\n skip: !boardId,\n });\n const board = data?.planmagnetBoard ?? null;\n\n const [createBoard, createBoardState] = useMutation<{ planmagnetCreateBoard: { id: string } }>(\n CREATE_BOARD,\n { refetchQueries: REFETCH, awaitRefetchQueries: true }\n );\n const [moveCard, moveState] = useMutation(MOVE_CARD, {\n refetchQueries: REFETCH,\n awaitRefetchQueries: true,\n });\n const [reorder] = useMutation(REORDER_COLUMNS, {\n refetchQueries: REFETCH,\n awaitRefetchQueries: true,\n });\n\n const run = async (action: () => Promise<{ error?: unknown }>, fallback: string) => {\n setError(null);\n try {\n await throwOnError(action());\n } catch (e) {\n setError(messageOf(e, fallback));\n }\n };\n\n const move = (workItemId: string, targetColumnId: string) =>\n run(\n () => moveCard({ variables: { boardId, input: { workItemId, targetColumnId } } }),\n 'The card could not be moved.'\n );\n\n const shiftColumn = (index: number, delta: -1 | 1) => {\n if (!board) return;\n const order = board.columns.map((c) => c.id);\n const target = index + delta;\n if (target < 0 || target >= order.length) return;\n [order[index], order[target]] = [order[target], order[index]];\n return run(\n () => reorder({ variables: { boardId, input: { columnIds: order } } }),\n 'The columns could not be reordered.'\n );\n };\n\n if (boardsLoading && !boardsData) {\n return <p className=\"text-sm text-muted-foreground p-4\">Loading board…</p>;\n }\n\n if (!boardId) {\n return (\n <Card>\n <CardContent className=\"p-8 text-center space-y-3\" data-testid=\"board-empty\">\n <LayoutGrid className=\"h-8 w-8 mx-auto text-muted-foreground\" />\n <p className=\"text-sm text-muted-foreground\">{projectName} has no board yet.</p>\n <Button\n size=\"sm\"\n disabled={createBoardState.loading}\n onClick={() =>\n run(\n () =>\n createBoard({\n variables: {\n input: {\n projectId,\n name: `${projectName} board`,\n type: 'KANBAN',\n isDefault: true,\n withDefaultColumns: true,\n },\n },\n }),\n 'The board could not be created.'\n )\n }\n >\n <Plus className=\"mr-1 h-4 w-4\" />\n {createBoardState.loading ? 'Creating…' : 'Create board'}\n </Button>\n <p className=\"text-xs text-muted-foreground\">\n Starts with one column per status: Backlog, To Do, In Progress, In Review, Testing,\n Done.\n </p>\n {error && <ErrorLine message={error} />}\n </CardContent>\n </Card>\n );\n }\n\n return (\n <div className=\"space-y-3\" data-testid=\"project-board\">\n <div className=\"flex flex-wrap items-center gap-3\">\n {boards.length > 1 && (\n <select\n aria-label=\"Board\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm\"\n value={boardId}\n onChange={(e) => setChosenBoardId(e.target.value)}\n >\n {boards.map((b) => (\n <option key={b.id} value={b.id}>\n {b.name}\n </option>\n ))}\n </select>\n )}\n {board && board.unplacedCount > 0 && (\n <p className=\"text-xs text-muted-foreground\" data-testid=\"board-unplaced\">\n {board.unplacedCount} work item{board.unplacedCount === 1 ? ' is' : 's are'} not on this\n board: {board.unplacedCount === 1 ? 'its status has' : 'their status has'} no column.\n </p>\n )}\n {board && (\n <div className=\"ml-auto\">\n <ColumnDialog boardId={board.id} />\n </div>\n )}\n </div>\n {error && <ErrorLine message={error} />}\n {loading && !board ? (\n <p className=\"text-sm text-muted-foreground p-4\">Loading board…</p>\n ) : (\n <div className=\"flex gap-3 overflow-x-auto pb-2\">\n {board?.columns.map((column, index) => (\n <BoardColumnView\n key={column.id}\n boardId={board.id}\n column={column}\n columns={board.columns}\n isFirst={index === 0}\n isLast={index === board.columns.length - 1}\n busy={moveState.loading}\n onMove={move}\n onShift={(delta) => shiftColumn(index, delta)}\n />\n ))}\n {board && board.columns.length === 0 && (\n <p className=\"text-sm text-muted-foreground p-4\">\n This board has no columns. Add one to start placing work.\n </p>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction BoardColumnView({\n boardId,\n column,\n columns,\n isFirst,\n isLast,\n busy,\n onMove,\n onShift,\n}: {\n boardId: string;\n column: BoardColumn;\n columns: BoardColumn[];\n isFirst: boolean;\n isLast: boolean;\n busy: boolean;\n onMove: (workItemId: string, targetColumnId: string) => void;\n onShift: (delta: -1 | 1) => void;\n}) {\n const navigate = usePlanMagnetNavigate();\n const [dragOver, setDragOver] = useState(false);\n const count = column.cards.totalCount;\n const atLimit = column.wipLimit != null && column.wipLimit > 0 && count >= column.wipLimit;\n const hidden = count - column.cards.items.length;\n\n return (\n <section\n aria-label={`${column.name} column`}\n data-testid={`board-column-${column.name}`}\n className={`min-w-[240px] w-[240px] shrink-0 rounded-md border bg-muted/30 ${dragOver ? 'ring-2 ring-primary' : ''}`}\n onDragOver={(e) => {\n e.preventDefault();\n setDragOver(true);\n }}\n onDragLeave={() => setDragOver(false)}\n onDrop={(e) => {\n e.preventDefault();\n setDragOver(false);\n const workItemId = e.dataTransfer.getData('text/plain');\n if (workItemId) onMove(workItemId, column.id);\n }}\n >\n <header className=\"p-2 border-b space-y-1\">\n <div className=\"flex items-center justify-between gap-1\">\n <h3 className=\"text-sm font-medium truncate\">{column.name}</h3>\n <span\n data-testid={`board-column-count-${column.name}`}\n className={`text-xs rounded-full px-1.5 ${atLimit ? 'bg-status-warning-subtle text-status-warning' : 'bg-muted'}`}\n title={column.wipLimit ? `WIP limit ${column.wipLimit}` : undefined}\n >\n {column.wipLimit ? `${count} / ${column.wipLimit}` : count}\n </span>\n </div>\n <div className=\"flex items-center justify-between gap-1\">\n <span className=\"text-[11px] text-muted-foreground\">\n {column.mappedStatus ? `Sets status: ${statusLabel(column.mappedStatus)}` : 'No status'}\n </span>\n <span className=\"flex\">\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-6 w-6\"\n aria-label={`Move ${column.name} column left`}\n disabled={isFirst}\n onClick={() => onShift(-1)}\n >\n <ChevronLeft className=\"h-3.5 w-3.5\" />\n </Button>\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-6 w-6\"\n aria-label={`Move ${column.name} column right`}\n disabled={isLast}\n onClick={() => onShift(1)}\n >\n <ChevronRight className=\"h-3.5 w-3.5\" />\n </Button>\n <ColumnDialog boardId={boardId} column={column} />\n </span>\n </div>\n </header>\n <ul className=\"p-2 space-y-2 min-h-[80px]\">\n {column.cards.items.map((card) => (\n <li\n key={card.id}\n draggable\n onDragStart={(e) => {\n e.dataTransfer.setData('text/plain', card.id);\n e.dataTransfer.effectAllowed = 'move';\n }}\n className=\"rounded-md border bg-background p-2 space-y-1 cursor-grab\"\n data-testid=\"board-card\"\n >\n <button\n type=\"button\"\n className=\"text-left text-sm font-medium leading-tight hover:underline\"\n onClick={() =>\n navigate(`/tasks/task-detail-page?taskId=${encodeURIComponent(card.id)}`)\n }\n >\n {card.title}\n </button>\n <div className=\"flex items-center justify-between text-[11px] text-muted-foreground\">\n <span>{card.key}</span>\n <span>{card.storyPoints ?? 0} pts</span>\n </div>\n <div className=\"flex items-center justify-between gap-1 text-[11px] text-muted-foreground\">\n <span className=\"truncate\">\n {card.assigneeId ? <PersonName id={card.assigneeId} /> : 'Unassigned'}\n </span>\n {/* The same move as drag-and-drop, for keyboard and assistive tech. */}\n <select\n aria-label={`Move ${card.key} to column`}\n className=\"rounded border border-input bg-background px-1 py-0.5 text-[11px]\"\n value=\"\"\n disabled={busy}\n onChange={(e) => e.target.value && onMove(card.id, e.target.value)}\n >\n <option value=\"\">Move…</option>\n {columns\n .filter((c) => c.id !== column.id)\n .map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </select>\n </div>\n </li>\n ))}\n {count === 0 && <li className=\"text-xs text-muted-foreground text-center py-4\">Empty</li>}\n </ul>\n {hidden > 0 && (\n <p className=\"px-2 pb-2 text-xs text-muted-foreground\">\n +{hidden} more (showing the first {CARDS_PER_COLUMN})\n </p>\n )}\n </section>\n );\n}\n\n/** Add a column (no `column`) or edit one: name, the status it sets, WIP limit; delete. */\nfunction ColumnDialog({ boardId, column }: { boardId: string; column?: BoardColumn }) {\n const editing = column !== undefined;\n const [open, setOpen] = useState(false);\n const [name, setName] = useState(column?.name ?? '');\n const [mappedStatus, setMappedStatus] = useState(column?.mappedStatus ?? '');\n const [wip, setWip] = useState(column?.wipLimit ? String(column.wipLimit) : '');\n const [error, setError] = useState<string | null>(null);\n const options = { refetchQueries: REFETCH, awaitRefetchQueries: true };\n const [create, createState] = useMutation(CREATE_COLUMN, options);\n const [update, updateState] = useMutation(UPDATE_COLUMN, options);\n const [remove, removeState] = useMutation(DELETE_COLUMN, options);\n const busy = createState.loading || updateState.loading || removeState.loading;\n\n const wipValue = wip.trim() === '' ? null : Number(wip);\n const invalidWip = wipValue !== null && (!Number.isInteger(wipValue) || wipValue < 1);\n const canSave = name.trim() !== '' && !invalidWip && !busy;\n\n const reset = () => {\n setName(column?.name ?? '');\n setMappedStatus(column?.mappedStatus ?? '');\n setWip(column?.wipLimit ? String(column.wipLimit) : '');\n setError(null);\n };\n\n const save = async () => {\n setError(null);\n const fields = {\n name: name.trim(),\n mappedStatus: mappedStatus || null,\n wipLimit: wipValue,\n };\n try {\n if (editing) {\n await throwOnError(update({ variables: { id: column.id, input: fields } }));\n } else {\n await throwOnError(\n create({ variables: { input: { boardId, type: 'CUSTOM', ...fields } } })\n );\n }\n setOpen(false);\n } catch (e) {\n setError(messageOf(e, 'The column could not be saved.'));\n }\n };\n\n const idPrefix = editing ? `column-${column.id}` : 'new-column';\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) reset();\n }}\n >\n <ResponsiveDialogTrigger asChild>\n {editing ? (\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-6 w-6\"\n aria-label={`Edit ${column.name} column`}\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"outline\">\n <Plus className=\"mr-1 h-4 w-4\" />\n Add column\n </Button>\n )}\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {editing ? `Edit ${column.name}` : 'Add column'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Moving a card into a column that sets a status changes the work item&apos;s status.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor={`${idPrefix}-name`} className=\"text-sm font-medium\">\n Name\n </label>\n <Input\n id={`${idPrefix}-name`}\n className=\"mt-1\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-status`} className=\"text-sm font-medium\">\n Sets status\n </label>\n <select\n id={`${idPrefix}-status`}\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={mappedStatus}\n onChange={(e) => setMappedStatus(e.target.value)}\n >\n <option value=\"\">No status (cards keep theirs)</option>\n {STATUSES.map((s) => (\n <option key={s} value={s}>\n {statusLabel(s)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-wip`} className=\"text-sm font-medium\">\n WIP limit (blank for none)\n </label>\n <Input\n id={`${idPrefix}-wip`}\n type=\"number\"\n min={1}\n className=\"mt-1\"\n value={wip}\n onChange={(e) => setWip(e.target.value)}\n />\n {invalidWip && <ErrorLine message=\"The WIP limit must be a whole number, 1 or more.\" />}\n </div>\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter className=\"gap-2\">\n {editing && (\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"outline\" disabled={busy}>\n Delete column\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete {column.name}?</AlertDialogTitle>\n <AlertDialogDescription>\n Its cards are not deleted: each goes back to the column for its status, or off\n the board if no column sets that status.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Keep it</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n onClick={async () => {\n try {\n await throwOnError(remove({ variables: { id: column.id } }));\n setOpen(false);\n } catch (e) {\n setError(messageOf(e, 'The column could not be deleted.'));\n }\n }}\n >\n Delete column\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n )}\n <Button onClick={save} disabled={!canSave} data-testid={`${idPrefix}-save`}>\n {busy ? 'Saving…' : editing ? 'Save' : 'Add column'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectBoard.js","names":[],"sources":["../../../src/planmagnet/board/ProjectBoard.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react';\nimport { gql } from '@apollo/client';\nimport { useApolloClient, useMutation, useQuery } from '@apollo/client/react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Button,\n Card,\n CardContent,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { ChevronLeft, ChevronRight, LayoutGrid, Pencil, Plus } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { PersonName } from '../people/People';\nimport { throwOnError } from '../utils/mutationResult';\n\n/**\n * A project's board (P1.2). The server owns placement: each column's `cards`\n * follow the rule in wspace-planmagnet-svc board.service.ts `columnCardsWhere`\n * (placed column, else the first column mapped to the item's status), and\n * `unplacedCount` says how many items are on no column. Moving a card into a\n * column mapped to a status changes that status on the server, and a column's\n * WIP limit is enforced there too. This component only draws and asks.\n */\n\nconst STATUSES = [\n 'BACKLOG',\n 'TODO',\n 'IN_PROGRESS',\n 'IN_REVIEW',\n 'TESTING',\n 'DONE',\n 'CANCELLED',\n] as const;\nconst statusLabel = (status: string) =>\n status.charAt(0) + status.slice(1).toLowerCase().replace(/_/g, ' ');\n\nconst CARDS_PER_COLUMN = 100;\n\nconst BOARDS = gql`\n query ProjectBoards($workspaceId: ID!, $projectId: String) {\n planmagnetBoards(\n workspaceId: $workspaceId\n filter: { projectId: $projectId }\n pagination: { first: 20 }\n ) {\n items {\n id\n name\n isDefault\n }\n }\n }\n`;\n\n/** The next page of one column (P1.2: a column shows 100 cards and used to say only \"+N more\"). */\nconst COLUMN_PAGE = gql`\n query ProjectBoardColumnPage($boardId: ID!, $columnId: ID!, $after: String) {\n planmagnetBoardColumnCards(\n boardId: $boardId\n columnId: $columnId\n pagination: { first: 100, after: $after }\n ) {\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n key\n title\n status\n priority\n storyPoints\n assigneeId\n statusSince\n }\n }\n }\n`;\n\nconst BOARD = gql`\n query ProjectBoard($id: ID!) {\n planmagnetBoard(id: $id) {\n id\n name\n unplacedCount\n columns {\n id\n name\n mappedStatus\n wipLimit\n sortOrder\n cards(pagination: { first: 100 }) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n items {\n id\n key\n title\n status\n priority\n storyPoints\n assigneeId\n statusSince\n }\n }\n }\n }\n }\n`;\n\nconst MOVE_CARD = gql`\n mutation ProjectBoardMoveCard($boardId: ID!, $input: PlanmagnetMoveCardInput!) {\n planmagnetMoveCard(boardId: $boardId, input: $input) {\n id\n }\n }\n`;\nconst CREATE_BOARD = gql`\n mutation ProjectBoardCreate($input: PlanmagnetCreateBoardInput!) {\n planmagnetCreateBoard(input: $input) {\n id\n }\n }\n`;\nconst CREATE_COLUMN = gql`\n mutation ProjectBoardCreateColumn($input: PlanmagnetCreateBoardColumnInput!) {\n planmagnetCreateBoardColumn(input: $input) {\n id\n }\n }\n`;\nconst UPDATE_COLUMN = gql`\n mutation ProjectBoardUpdateColumn($id: ID!, $input: PlanmagnetUpdateBoardColumnInput!) {\n planmagnetUpdateBoardColumn(id: $id, input: $input) {\n id\n }\n }\n`;\nconst DELETE_COLUMN = gql`\n mutation ProjectBoardDeleteColumn($id: ID!) {\n planmagnetDeleteBoardColumn(id: $id)\n }\n`;\nconst REORDER_COLUMNS = gql`\n mutation ProjectBoardReorder($boardId: ID!, $input: PlanmagnetReorderColumnsInput!) {\n planmagnetReorderColumns(boardId: $boardId, input: $input) {\n id\n }\n }\n`;\n\n/** Every query whose numbers a board change can move. */\nconst REFETCH = [\n 'ProjectBoard',\n 'ProjectBoards',\n 'GetWorkItems',\n 'GetWorkItemCounts',\n 'GetDashboardData',\n];\n\ninterface BoardCard {\n id: string;\n key: string;\n title: string;\n status: string;\n priority: string;\n storyPoints: number | null;\n assigneeId: string | null;\n /** When it entered its current status; null when unknown (changed by a path that records no history). */\n statusSince: string | null;\n}\n\ninterface PageInfo {\n hasNextPage: boolean;\n endCursor: string | null;\n}\n\ninterface BoardColumn {\n id: string;\n name: string;\n mappedStatus: string | null;\n wipLimit: number | null;\n sortOrder: number;\n cards: { totalCount: number; pageInfo: PageInfo; items: BoardCard[] };\n}\n\ntype LaneBy = 'none' | 'assignee' | 'priority';\nconst PRIORITY_ORDER = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'NONE'];\nconst DAY_MS = 86_400_000;\n\n/** Whole days in the current status, or null when unknown. */\nexport function daysInStatus(statusSince: string | null, now: number): number | null {\n if (!statusSince) return null;\n return Math.max(0, Math.floor((now - new Date(statusSince).getTime()) / DAY_MS));\n}\n\n/** Card aging: quiet under a week, a warning from 7 days, stronger from 14. Semantic tokens only. */\nfunction ageClass(days: number): string {\n if (days >= 14) return 'text-destructive font-medium';\n if (days >= 7) return 'text-status-warning-text';\n return 'text-muted-foreground';\n}\n\nfunction laneOf(card: BoardCard, by: LaneBy): string {\n if (by === 'assignee') return card.assigneeId ?? '';\n if (by === 'priority') return card.priority || 'NONE';\n return '';\n}\n\ninterface BoardData {\n id: string;\n name: string;\n unplacedCount: number;\n columns: BoardColumn[];\n}\n\nconst messageOf = (e: unknown, fallback: string) => (e instanceof Error ? e.message : fallback);\n\nfunction ErrorLine({ message }: { message: string }) {\n return (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {message}\n </p>\n );\n}\n\nexport function ProjectBoard({\n projectId,\n projectName,\n}: {\n projectId: string;\n projectName: string;\n}) {\n const { workspaceId } = usePlanMagnet();\n const client = useApolloClient();\n const [chosenBoardId, setChosenBoardId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n // Card age is read against the moment the board opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n const [laneBy, setLaneBy] = useState<LaneBy>('none');\n // Pages loaded past each column's first 100, keyed by the board data they extend: a refetch starts over.\n const [more, setMore] = useState<{\n key: unknown;\n byColumn: Record<string, { items: BoardCard[]; pageInfo: PageInfo }>;\n }>({\n key: null,\n byColumn: {},\n });\n const [loadingMore, setLoadingMore] = useState<string | null>(null);\n\n const { data: boardsData, loading: boardsLoading } = useQuery<{\n planmagnetBoards: { items: Array<{ id: string; name: string; isDefault: boolean }> };\n }>(BOARDS, { variables: { workspaceId, projectId }, skip: !workspaceId });\n const boards = boardsData?.planmagnetBoards.items ?? [];\n const boardId =\n (chosenBoardId && boards.some((b) => b.id === chosenBoardId) ? chosenBoardId : null) ??\n boards.find((b) => b.isDefault)?.id ??\n boards[0]?.id ??\n null;\n\n const { data, loading } = useQuery<{ planmagnetBoard: BoardData | null }>(BOARD, {\n variables: { id: boardId },\n skip: !boardId,\n });\n const board = data?.planmagnetBoard ?? null;\n const extra = more.key === board ? more.byColumn : {};\n /** A column with every page loaded so far. */\n const cardsOf = (column: BoardColumn) => {\n const loaded = extra[column.id];\n return {\n items: loaded ? [...column.cards.items, ...loaded.items] : column.cards.items,\n pageInfo: loaded ? loaded.pageInfo : column.cards.pageInfo,\n totalCount: column.cards.totalCount,\n };\n };\n const loadMore = async (column: BoardColumn) => {\n const cursor = cardsOf(column).pageInfo.endCursor;\n if (!board || !cursor) return;\n setError(null);\n setLoadingMore(column.id);\n try {\n const res = await client.query<{\n planmagnetBoardColumnCards: { pageInfo: PageInfo; items: BoardCard[] };\n }>({\n query: COLUMN_PAGE,\n variables: { boardId: board.id, columnId: column.id, after: cursor },\n fetchPolicy: 'network-only',\n });\n const page = res.data?.planmagnetBoardColumnCards;\n if (!page) throw new Error(res.error?.message ?? 'The next cards could not be loaded.');\n const prior = extra[column.id]?.items ?? [];\n setMore({\n key: board,\n byColumn: {\n ...extra,\n [column.id]: { items: [...prior, ...page.items], pageInfo: page.pageInfo },\n },\n });\n } catch (e) {\n setError(messageOf(e, 'The next cards could not be loaded.'));\n } finally {\n setLoadingMore(null);\n }\n };\n\n const [createBoard, createBoardState] = useMutation<{ planmagnetCreateBoard: { id: string } }>(\n CREATE_BOARD,\n { refetchQueries: REFETCH, awaitRefetchQueries: true }\n );\n const [moveCard, moveState] = useMutation(MOVE_CARD, {\n refetchQueries: REFETCH,\n awaitRefetchQueries: true,\n });\n const [reorder] = useMutation(REORDER_COLUMNS, {\n refetchQueries: REFETCH,\n awaitRefetchQueries: true,\n });\n\n const run = async (action: () => Promise<{ error?: unknown }>, fallback: string) => {\n setError(null);\n try {\n await throwOnError(action());\n } catch (e) {\n setError(messageOf(e, fallback));\n }\n };\n\n const move = (workItemId: string, targetColumnId: string) =>\n run(\n () => moveCard({ variables: { boardId, input: { workItemId, targetColumnId } } }),\n 'The card could not be moved.'\n );\n\n const shiftColumn = (index: number, delta: -1 | 1) => {\n if (!board) return;\n const order = board.columns.map((c) => c.id);\n const target = index + delta;\n if (target < 0 || target >= order.length) return;\n [order[index], order[target]] = [order[target], order[index]];\n return run(\n () => reorder({ variables: { boardId, input: { columnIds: order } } }),\n 'The columns could not be reordered.'\n );\n };\n\n if (boardsLoading && !boardsData) {\n return <p className=\"text-sm text-muted-foreground p-4\">Loading board…</p>;\n }\n\n if (!boardId) {\n return (\n <Card>\n <CardContent className=\"p-8 text-center space-y-3\" data-testid=\"board-empty\">\n <LayoutGrid className=\"h-8 w-8 mx-auto text-muted-foreground\" />\n <p className=\"text-sm text-muted-foreground\">{projectName} has no board yet.</p>\n <Button\n size=\"sm\"\n disabled={createBoardState.loading}\n onClick={() =>\n run(\n () =>\n createBoard({\n variables: {\n input: {\n projectId,\n name: `${projectName} board`,\n type: 'KANBAN',\n isDefault: true,\n withDefaultColumns: true,\n },\n },\n }),\n 'The board could not be created.'\n )\n }\n >\n <Plus className=\"mr-1 h-4 w-4\" />\n {createBoardState.loading ? 'Creating…' : 'Create board'}\n </Button>\n <p className=\"text-xs text-muted-foreground\">\n Starts with one column per status: Backlog, To Do, In Progress, In Review, Testing,\n Done.\n </p>\n {error && <ErrorLine message={error} />}\n </CardContent>\n </Card>\n );\n }\n\n return (\n <div className=\"space-y-3\" data-testid=\"project-board\">\n <div className=\"flex flex-wrap items-center gap-3\">\n {boards.length > 1 && (\n <select\n aria-label=\"Board\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm\"\n value={boardId}\n onChange={(e) => setChosenBoardId(e.target.value)}\n >\n {boards.map((b) => (\n <option key={b.id} value={b.id}>\n {b.name}\n </option>\n ))}\n </select>\n )}\n {board && (\n <label className=\"flex items-center gap-1 text-sm\">\n <span className=\"text-muted-foreground\">Swimlanes</span>\n <select\n aria-label=\"Swimlanes\"\n data-testid=\"board-lanes\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm\"\n value={laneBy}\n onChange={(e) => setLaneBy(e.target.value as LaneBy)}\n >\n <option value=\"none\">None</option>\n <option value=\"assignee\">By assignee</option>\n <option value=\"priority\">By priority</option>\n </select>\n </label>\n )}\n {board && board.unplacedCount > 0 && (\n <p className=\"text-xs text-muted-foreground\" data-testid=\"board-unplaced\">\n {board.unplacedCount} work item{board.unplacedCount === 1 ? ' is' : 's are'} not on this\n board: {board.unplacedCount === 1 ? 'its status has' : 'their status has'} no column.\n </p>\n )}\n {board && (\n <div className=\"ml-auto\">\n <ColumnDialog boardId={board.id} />\n </div>\n )}\n </div>\n {error && <ErrorLine message={error} />}\n {loading && !board ? (\n <p className=\"text-sm text-muted-foreground p-4\">Loading board…</p>\n ) : board && laneBy !== 'none' ? (\n <SwimlaneBoard\n board={board}\n laneBy={laneBy}\n cardsOf={cardsOf}\n now={now}\n busy={moveState.loading}\n loadingMore={loadingMore}\n onLoadMore={(column) => void loadMore(column)}\n onMove={move}\n />\n ) : (\n <div className=\"flex gap-3 overflow-x-auto pb-2\">\n {board?.columns.map((column, index) => (\n <BoardColumnView\n key={column.id}\n boardId={board.id}\n column={column}\n columns={board.columns}\n cards={cardsOf(column)}\n isFirst={index === 0}\n isLast={index === board.columns.length - 1}\n busy={moveState.loading}\n now={now}\n loadingMore={loadingMore === column.id}\n onLoadMore={() => void loadMore(column)}\n onMove={move}\n onShift={(delta) => shiftColumn(index, delta)}\n />\n ))}\n {board && board.columns.length === 0 && (\n <p className=\"text-sm text-muted-foreground p-4\">\n This board has no columns. Add one to start placing work.\n </p>\n )}\n </div>\n )}\n </div>\n );\n}\n\ntype ColumnCards = { items: BoardCard[]; pageInfo: PageInfo; totalCount: number };\n\n/** One card: title, key, points, assignee, age in status, and a keyboard \"Move…\" equal to drag-and-drop. */\nfunction CardView({\n card,\n column,\n columns,\n busy,\n now,\n onMove,\n}: {\n card: BoardCard;\n column: BoardColumn;\n columns: BoardColumn[];\n busy: boolean;\n now: number;\n onMove: (workItemId: string, targetColumnId: string) => void;\n}) {\n const navigate = usePlanMagnetNavigate();\n const days = daysInStatus(card.statusSince, now);\n return (\n <li\n draggable\n onDragStart={(e) => {\n e.dataTransfer.setData('text/plain', card.id);\n e.dataTransfer.effectAllowed = 'move';\n }}\n className=\"rounded-md border bg-background p-2 space-y-1 cursor-grab\"\n data-testid=\"board-card\"\n >\n <button\n type=\"button\"\n className=\"text-left text-sm font-medium leading-tight hover:underline\"\n onClick={() => navigate(`/tasks/task-detail-page?taskId=${encodeURIComponent(card.id)}`)}\n >\n {card.title}\n </button>\n <div className=\"flex items-center justify-between text-[11px] text-muted-foreground\">\n <span>{card.key}</span>\n {/* Card aging (P1.2): how long it has had this status. Unknown when its status changed off the record. */}\n {days !== null && (\n <span\n data-testid=\"card-age\"\n className={ageClass(days)}\n title={`In ${statusLabel(card.status)} since ${new Date(card.statusSince ?? '').toLocaleDateString()}`}\n >\n {days === 0 ? 'today' : `${days}d in status`}\n </span>\n )}\n <span>{card.storyPoints ?? 0} pts</span>\n </div>\n <div className=\"flex items-center justify-between gap-1 text-[11px] text-muted-foreground\">\n <span className=\"truncate\">\n {card.assigneeId ? <PersonName id={card.assigneeId} /> : 'Unassigned'}\n </span>\n {/* The same move as drag-and-drop, for keyboard and assistive tech. */}\n <select\n aria-label={`Move ${card.key} to column`}\n className=\"rounded border border-input bg-background px-1 py-0.5 text-[11px]\"\n value=\"\"\n disabled={busy}\n onChange={(e) => e.target.value && onMove(card.id, e.target.value)}\n >\n <option value=\"\">Move…</option>\n {columns\n .filter((c) => c.id !== column.id)\n .map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </select>\n </div>\n </li>\n );\n}\n\n/** A drop target: dropping a card here moves it into `column`. */\nfunction DropZone({\n column,\n onMove,\n className,\n children,\n label,\n}: {\n column: BoardColumn;\n onMove: (workItemId: string, targetColumnId: string) => void;\n className: string;\n children: ReactNode;\n label: string;\n}) {\n const [dragOver, setDragOver] = useState(false);\n return (\n <section\n aria-label={label}\n className={`${className} ${dragOver ? 'ring-2 ring-primary' : ''}`}\n onDragOver={(e) => {\n e.preventDefault();\n setDragOver(true);\n }}\n onDragLeave={() => setDragOver(false)}\n onDrop={(e) => {\n e.preventDefault();\n setDragOver(false);\n const workItemId = e.dataTransfer.getData('text/plain');\n if (workItemId) onMove(workItemId, column.id);\n }}\n >\n {children}\n </section>\n );\n}\n\n/** \"Showing N of M — Show more\" for a column with cards past the ones loaded. */\nfunction MoreCards({\n cards,\n loading,\n onLoadMore,\n}: {\n cards: ColumnCards;\n loading: boolean;\n onLoadMore: () => void;\n}) {\n const hidden = cards.totalCount - cards.items.length;\n if (hidden <= 0 || !cards.pageInfo.hasNextPage) return null;\n return (\n <div className=\"px-2 pb-2 space-y-1\">\n <p className=\"text-xs text-muted-foreground\">\n Showing {cards.items.length} of {cards.totalCount}\n </p>\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"w-full\"\n data-testid=\"board-column-more\"\n disabled={loading}\n onClick={onLoadMore}\n >\n {loading ? 'Loading…' : `Show ${Math.min(hidden, CARDS_PER_COLUMN)} more`}\n </Button>\n </div>\n );\n}\n\n/**\n * Swimlanes (P1.2): rows by assignee or priority across the same columns. Moving a card changes its column (and the\n * status that column sets); its lane follows its assignee or priority, which are changed on the card itself.\n */\nfunction SwimlaneBoard({\n board,\n laneBy,\n cardsOf,\n now,\n busy,\n loadingMore,\n onLoadMore,\n onMove,\n}: {\n board: BoardData;\n laneBy: LaneBy;\n cardsOf: (column: BoardColumn) => ColumnCards;\n now: number;\n busy: boolean;\n loadingMore: string | null;\n onLoadMore: (column: BoardColumn) => void;\n onMove: (workItemId: string, targetColumnId: string) => void;\n}) {\n const perColumn = board.columns.map((column) => ({ column, cards: cardsOf(column) }));\n const lanes = [\n ...new Set(perColumn.flatMap(({ cards }) => cards.items.map((c) => laneOf(c, laneBy)))),\n ].sort((a, b) =>\n laneBy === 'priority'\n ? PRIORITY_ORDER.indexOf(a) - PRIORITY_ORDER.indexOf(b)\n : a === ''\n ? 1\n : b === ''\n ? -1\n : a.localeCompare(b)\n );\n const laneName = (lane: string) =>\n laneBy === 'priority' ? statusLabel(lane) : lane ? <PersonName id={lane} /> : 'Unassigned';\n return (\n <div className=\"overflow-x-auto pb-2 space-y-3\" data-testid=\"board-swimlanes\">\n <p className=\"text-xs text-muted-foreground\">\n Dragging moves a card between columns. Its lane follows its{' '}\n {laneBy === 'priority' ? 'priority' : 'assignee'}.\n </p>\n <div className=\"flex gap-3\">\n {perColumn.map(({ column, cards }) => (\n <div key={column.id} className=\"min-w-[240px] w-[240px] shrink-0 px-2\">\n <h3 className=\"text-sm font-medium truncate\">\n {column.name}{' '}\n <span className=\"text-xs text-muted-foreground\">\n {column.wipLimit ? `${cards.totalCount} / ${column.wipLimit}` : cards.totalCount}\n </span>\n </h3>\n </div>\n ))}\n </div>\n {lanes.length === 0 && (\n <p className=\"text-sm text-muted-foreground p-4\">No cards on this board yet.</p>\n )}\n {lanes.map((lane) => (\n <div key={lane || 'none'} data-testid=\"board-lane\" className=\"space-y-1\">\n <p className=\"text-xs font-medium\">\n {laneName(lane)}{' '}\n <span className=\"text-muted-foreground\">\n (\n {perColumn.reduce(\n (n, { cards }) => n + cards.items.filter((c) => laneOf(c, laneBy) === lane).length,\n 0\n )}\n )\n </span>\n </p>\n <div className=\"flex gap-3\">\n {perColumn.map(({ column, cards }) => (\n <DropZone\n key={column.id}\n column={column}\n onMove={onMove}\n label={`${column.name} column`}\n className=\"min-w-[240px] w-[240px] shrink-0 rounded-md border bg-muted/30\"\n >\n <ul className=\"p-2 space-y-2 min-h-[48px]\">\n {cards.items\n .filter((c) => laneOf(c, laneBy) === lane)\n .map((card) => (\n <CardView\n key={card.id}\n card={card}\n column={column}\n columns={board.columns}\n busy={busy}\n now={now}\n onMove={onMove}\n />\n ))}\n </ul>\n </DropZone>\n ))}\n </div>\n </div>\n ))}\n <div className=\"flex gap-3\">\n {perColumn.map(({ column, cards }) => (\n <div key={column.id} className=\"min-w-[240px] w-[240px] shrink-0\">\n <MoreCards\n cards={cards}\n loading={loadingMore === column.id}\n onLoadMore={() => onLoadMore(column)}\n />\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nfunction BoardColumnView({\n boardId,\n column,\n columns,\n cards,\n isFirst,\n isLast,\n busy,\n now,\n loadingMore,\n onLoadMore,\n onMove,\n onShift,\n}: {\n boardId: string;\n column: BoardColumn;\n columns: BoardColumn[];\n cards: ColumnCards;\n isFirst: boolean;\n isLast: boolean;\n busy: boolean;\n now: number;\n loadingMore: boolean;\n onLoadMore: () => void;\n onMove: (workItemId: string, targetColumnId: string) => void;\n onShift: (delta: -1 | 1) => void;\n}) {\n const count = cards.totalCount;\n const atLimit = column.wipLimit != null && column.wipLimit > 0 && count >= column.wipLimit;\n\n return (\n <DropZone\n column={column}\n onMove={onMove}\n label={`${column.name} column`}\n className=\"min-w-[240px] w-[240px] shrink-0 rounded-md border bg-muted/30\"\n >\n <div data-testid={`board-column-${column.name}`}>\n <header className=\"p-2 border-b space-y-1\">\n <div className=\"flex items-center justify-between gap-1\">\n <h3 className=\"text-sm font-medium truncate\">{column.name}</h3>\n <span\n data-testid={`board-column-count-${column.name}`}\n className={`text-xs rounded-full px-1.5 ${atLimit ? 'bg-status-warning-subtle text-status-warning' : 'bg-muted'}`}\n title={column.wipLimit ? `WIP limit ${column.wipLimit}` : undefined}\n >\n {column.wipLimit ? `${count} / ${column.wipLimit}` : count}\n </span>\n </div>\n <div className=\"flex items-center justify-between gap-1\">\n <span className=\"text-[11px] text-muted-foreground\">\n {column.mappedStatus\n ? `Sets status: ${statusLabel(column.mappedStatus)}`\n : 'No status'}\n </span>\n <span className=\"flex\">\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-6 w-6\"\n aria-label={`Move ${column.name} column left`}\n disabled={isFirst}\n onClick={() => onShift(-1)}\n >\n <ChevronLeft className=\"h-3.5 w-3.5\" />\n </Button>\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-6 w-6\"\n aria-label={`Move ${column.name} column right`}\n disabled={isLast}\n onClick={() => onShift(1)}\n >\n <ChevronRight className=\"h-3.5 w-3.5\" />\n </Button>\n <ColumnDialog boardId={boardId} column={column} />\n </span>\n </div>\n </header>\n <ul className=\"p-2 space-y-2 min-h-[80px]\">\n {cards.items.map((card) => (\n <CardView\n key={card.id}\n card={card}\n column={column}\n columns={columns}\n busy={busy}\n now={now}\n onMove={onMove}\n />\n ))}\n {count === 0 && <li className=\"text-xs text-muted-foreground text-center py-4\">Empty</li>}\n </ul>\n <MoreCards cards={cards} loading={loadingMore} onLoadMore={onLoadMore} />\n </div>\n </DropZone>\n );\n}\n\nfunction ColumnDialog({ boardId, column }: { boardId: string; column?: BoardColumn }) {\n const editing = column !== undefined;\n const [open, setOpen] = useState(false);\n const [name, setName] = useState(column?.name ?? '');\n const [mappedStatus, setMappedStatus] = useState(column?.mappedStatus ?? '');\n const [wip, setWip] = useState(column?.wipLimit ? String(column.wipLimit) : '');\n const [error, setError] = useState<string | null>(null);\n const options = { refetchQueries: REFETCH, awaitRefetchQueries: true };\n const [create, createState] = useMutation(CREATE_COLUMN, options);\n const [update, updateState] = useMutation(UPDATE_COLUMN, options);\n const [remove, removeState] = useMutation(DELETE_COLUMN, options);\n const busy = createState.loading || updateState.loading || removeState.loading;\n\n const wipValue = wip.trim() === '' ? null : Number(wip);\n const invalidWip = wipValue !== null && (!Number.isInteger(wipValue) || wipValue < 1);\n const canSave = name.trim() !== '' && !invalidWip && !busy;\n\n const reset = () => {\n setName(column?.name ?? '');\n setMappedStatus(column?.mappedStatus ?? '');\n setWip(column?.wipLimit ? String(column.wipLimit) : '');\n setError(null);\n };\n\n const save = async () => {\n setError(null);\n const fields = {\n name: name.trim(),\n mappedStatus: mappedStatus || null,\n wipLimit: wipValue,\n };\n try {\n if (editing) {\n await throwOnError(update({ variables: { id: column.id, input: fields } }));\n } else {\n await throwOnError(\n create({ variables: { input: { boardId, type: 'CUSTOM', ...fields } } })\n );\n }\n setOpen(false);\n } catch (e) {\n setError(messageOf(e, 'The column could not be saved.'));\n }\n };\n\n const idPrefix = editing ? `column-${column.id}` : 'new-column';\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) reset();\n }}\n >\n <ResponsiveDialogTrigger asChild>\n {editing ? (\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"h-6 w-6\"\n aria-label={`Edit ${column.name} column`}\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"outline\">\n <Plus className=\"mr-1 h-4 w-4\" />\n Add column\n </Button>\n )}\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {editing ? `Edit ${column.name}` : 'Add column'}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Moving a card into a column that sets a status changes the work item&apos;s status.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor={`${idPrefix}-name`} className=\"text-sm font-medium\">\n Name\n </label>\n <Input\n id={`${idPrefix}-name`}\n className=\"mt-1\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-status`} className=\"text-sm font-medium\">\n Sets status\n </label>\n <select\n id={`${idPrefix}-status`}\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={mappedStatus}\n onChange={(e) => setMappedStatus(e.target.value)}\n >\n <option value=\"\">No status (cards keep theirs)</option>\n {STATUSES.map((s) => (\n <option key={s} value={s}>\n {statusLabel(s)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor={`${idPrefix}-wip`} className=\"text-sm font-medium\">\n WIP limit (blank for none)\n </label>\n <Input\n id={`${idPrefix}-wip`}\n type=\"number\"\n min={1}\n className=\"mt-1\"\n value={wip}\n onChange={(e) => setWip(e.target.value)}\n />\n {invalidWip && <ErrorLine message=\"The WIP limit must be a whole number, 1 or more.\" />}\n </div>\n </div>\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter className=\"gap-2\">\n {editing && (\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"outline\" disabled={busy}>\n Delete column\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete {column.name}?</AlertDialogTitle>\n <AlertDialogDescription>\n Its cards are not deleted: each goes back to the column for its status, or off\n the board if no column sets that status.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Keep it</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n onClick={async () => {\n try {\n await throwOnError(remove({ variables: { id: column.id } }));\n setOpen(false);\n } catch (e) {\n setError(messageOf(e, 'The column could not be deleted.'));\n }\n }}\n >\n Delete column\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n )}\n <Button onClick={save} disabled={!canSave} data-testid={`${idPrefix}-save`}>\n {busy ? 'Saving…' : editing ? 'Save' : 'Add column'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-planmagnet",
3
- "version": "2026.927.5",
3
+ "version": "2026.927.6",
4
4
  "description": "Burdenoff PlanMagnet Microfrontend",
5
5
  "type": "module",
6
6
  "files": [