@burdenoff/microfe-planmagnet 2026.925.6 → 2026.925.7

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,14 +1,15 @@
1
1
  import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as n } from "../people/People.js";
3
3
  import { rowLinkProps as r } from "../utils/rowNavigation.js";
4
- import { useState as i } from "react";
5
- import { Filter as a } from "lucide-react";
6
- import { gql as o } from "@apollo/client";
7
- import { useQuery as s } from "@apollo/client/react";
8
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
9
- import { Card as u, CardContent as d } from "@burdenoff/fe-libs/ui";
4
+ import { ProjectBoard as i } from "../board/ProjectBoard.js";
5
+ import { useState as a } from "react";
6
+ import { Filter as o } from "lucide-react";
7
+ import { gql as s } from "@apollo/client";
8
+ import { useQuery as c } from "@apollo/client/react";
9
+ import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
10
+ import { Card as f, CardContent as p } from "@burdenoff/fe-libs/ui";
10
11
  //#region src/planmagnet/pages/TasksTasksPagePage.tsx
11
- var f = o`
12
+ var m = s`
12
13
  query GetWorkItems(
13
14
  $workspaceId: ID!
14
15
  $filter: PlanmagnetWorkItemFilter
@@ -39,7 +40,7 @@ var f = o`
39
40
  totalCount
40
41
  }
41
42
  }
42
- `, p = o`
43
+ `, h = s`
43
44
  query GetWorkItemCounts($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {
44
45
  planmagnetWorkItemCounts(workspaceId: $workspaceId, filter: $filter) {
45
46
  total
@@ -49,7 +50,16 @@ var f = o`
49
50
  }
50
51
  }
51
52
  }
52
- `, m = 100, h = [
53
+ `, g = 100, _ = s`
54
+ query GetBoardProjects($workspaceId: ID!) {
55
+ planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {
56
+ items {
57
+ id
58
+ name
59
+ }
60
+ }
61
+ }
62
+ `, v = [
53
63
  "BACKLOG",
54
64
  "TODO",
55
65
  "IN_PROGRESS",
@@ -57,7 +67,7 @@ var f = o`
57
67
  "TESTING",
58
68
  "DONE",
59
69
  "CANCELLED"
60
- ], g = {
70
+ ], y = {
61
71
  BACKLOG: "Backlog",
62
72
  TODO: "To Do",
63
73
  IN_PROGRESS: "In Progress",
@@ -65,7 +75,7 @@ var f = o`
65
75
  TESTING: "Testing",
66
76
  DONE: "Done",
67
77
  CANCELLED: "Cancelled"
68
- }, _ = {
78
+ }, b = {
69
79
  BACKLOG: "border-t-gray-400",
70
80
  TODO: "border-t-blue-400",
71
81
  IN_PROGRESS: "border-t-yellow-400",
@@ -73,43 +83,46 @@ var f = o`
73
83
  TESTING: "border-t-teal-400",
74
84
  DONE: "border-t-green-400",
75
85
  CANCELLED: "border-t-muted-foreground"
76
- }, v = {
86
+ }, x = {
77
87
  LOW: "bg-gray-100 text-gray-700",
78
88
  MEDIUM: "bg-blue-100 text-blue-700",
79
89
  HIGH: "bg-orange-100 text-orange-700",
80
90
  CRITICAL: "bg-red-100 text-red-700"
81
- }, y = {
91
+ }, S = {
82
92
  STORY: "bg-green-100 text-green-700",
83
93
  BUG: "bg-red-100 text-red-700",
84
94
  TASK: "bg-blue-100 text-blue-700",
85
95
  EPIC: "bg-purple-100 text-purple-700",
86
96
  SUBTASK: "bg-gray-100 text-gray-700"
87
97
  };
88
- function b() {
89
- let { workspaceId: o } = e(), b = t(), [x, S] = i("all"), [C, w] = i("all"), T = {
90
- ...x !== "all" && { priority: x.toUpperCase() },
91
- ...C !== "all" && { type: C.toUpperCase() }
92
- }, [E, D] = i(!1), { data: O, loading: k, error: A, fetchMore: j } = s(f, {
98
+ function C() {
99
+ let { workspaceId: s } = e(), C = t(), [w, T] = a("all"), [E, D] = a("all"), [O, k] = a(null), { data: A } = c(_, {
100
+ variables: { workspaceId: s },
101
+ skip: !s
102
+ }), j = A?.planmagnetProjects?.items ?? [], M = O ?? j[0]?.id ?? "", N = j.find((e) => e.id === M)?.name ?? "", P = {
103
+ ...w !== "all" && { priority: w.toUpperCase() },
104
+ ...E !== "all" && { type: E.toUpperCase() }
105
+ }, [F, I] = a(!1), { data: L, loading: R, error: z, fetchMore: B } = c(m, {
93
106
  variables: {
94
- workspaceId: o,
95
- filter: T,
96
- pagination: { first: m }
107
+ workspaceId: s,
108
+ filter: P,
109
+ pagination: { first: g }
97
110
  },
98
- skip: !o
99
- }), { data: M } = s(p, {
111
+ skip: !s || !!M
112
+ }), { data: V } = c(h, {
100
113
  variables: {
101
- workspaceId: o,
102
- filter: T
114
+ workspaceId: s,
115
+ filter: P
103
116
  },
104
- skip: !o
105
- }), N = async () => {
106
- let e = O?.planmagnetWorkItems?.pageInfo?.endCursor;
117
+ skip: !s
118
+ }), H = async () => {
119
+ let e = L?.planmagnetWorkItems?.pageInfo?.endCursor;
107
120
  if (e) {
108
- D(!0);
121
+ I(!0);
109
122
  try {
110
- await j({
123
+ await B({
111
124
  variables: { pagination: {
112
- first: m,
125
+ first: g,
113
126
  after: e
114
127
  } },
115
128
  updateQuery: (e, { fetchMoreResult: t }) => ({ planmagnetWorkItems: {
@@ -118,42 +131,65 @@ function b() {
118
131
  } })
119
132
  });
120
133
  } finally {
121
- D(!1);
134
+ I(!1);
122
135
  }
123
136
  }
124
137
  };
125
- if (k && !O) return /* @__PURE__ */ c("div", {
138
+ if (R && !L) return /* @__PURE__ */ u("div", {
126
139
  className: "flex items-center justify-center p-12",
127
- children: /* @__PURE__ */ c("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
140
+ children: /* @__PURE__ */ u("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
128
141
  });
129
- if (A) return /* @__PURE__ */ l("div", {
142
+ if (z) return /* @__PURE__ */ d("div", {
130
143
  className: "p-6 text-red-500",
131
- children: ["Error: ", A.message]
144
+ children: ["Error: ", z.message]
132
145
  });
133
- let P = O?.planmagnetWorkItems?.items ?? [], F = M?.planmagnetWorkItemCounts, I = F?.total ?? O?.planmagnetWorkItems?.totalCount ?? P.length, L = (e) => F?.byStatus.find((t) => t.status === e)?.count ?? P.filter((t) => t.status === e).length, R = O?.planmagnetWorkItems?.pageInfo?.hasNextPage ?? !1;
134
- return /* @__PURE__ */ l("div", {
146
+ let U = L?.planmagnetWorkItems?.items ?? [], W = V?.planmagnetWorkItemCounts, G = W?.total ?? L?.planmagnetWorkItems?.totalCount ?? U.length, K = (e) => W?.byStatus.find((t) => t.status === e)?.count ?? U.filter((t) => t.status === e).length, q = L?.planmagnetWorkItems?.pageInfo?.hasNextPage ?? !1;
147
+ return /* @__PURE__ */ d("div", {
135
148
  className: "space-y-4 p-6",
136
- children: [
137
- /* @__PURE__ */ c("div", {
138
- className: "flex items-center justify-between",
139
- children: /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("h1", {
149
+ children: [/* @__PURE__ */ u("div", {
150
+ className: "flex items-center justify-between",
151
+ children: /* @__PURE__ */ d("div", { children: [
152
+ /* @__PURE__ */ u("h1", {
140
153
  className: "text-2xl font-semibold",
141
154
  children: "Task Board"
142
- }), /* @__PURE__ */ l("p", {
155
+ }),
156
+ /* @__PURE__ */ u("label", {
157
+ className: "sr-only",
158
+ htmlFor: "task-board-project",
159
+ children: "Project"
160
+ }),
161
+ /* @__PURE__ */ d("select", {
162
+ id: "task-board-project",
163
+ className: "mt-2 rounded-md border border-input bg-background px-2 py-1 text-sm",
164
+ value: M,
165
+ onChange: (e) => k(e.target.value),
166
+ children: [j.map((e) => /* @__PURE__ */ u("option", {
167
+ value: e.id,
168
+ children: e.name
169
+ }, e.id)), /* @__PURE__ */ u("option", {
170
+ value: "",
171
+ children: "All projects — status overview"
172
+ })]
173
+ }),
174
+ /* @__PURE__ */ d("p", {
143
175
  className: "text-sm text-muted-foreground",
144
176
  "data-testid": "task-board-total",
145
177
  children: [
146
- I,
178
+ G,
147
179
  " tasks across all projects",
148
- P.length < I && ` · showing ${P.length}`
180
+ U.length < G && ` · showing ${U.length}`
149
181
  ]
150
- })] })
151
- }),
152
- /* @__PURE__ */ l("div", {
182
+ })
183
+ ] })
184
+ }), M ? /* @__PURE__ */ u(i, {
185
+ projectId: M,
186
+ projectName: N
187
+ }) : /* @__PURE__ */ d(l, { children: [
188
+ /* @__PURE__ */ d("div", {
153
189
  className: "flex gap-4 items-center flex-wrap",
154
190
  children: [
155
- /* @__PURE__ */ c(a, { className: "h-4 w-4 text-muted-foreground" }),
156
- /* @__PURE__ */ c("div", {
191
+ /* @__PURE__ */ u(o, { className: "h-4 w-4 text-muted-foreground" }),
192
+ /* @__PURE__ */ u("div", {
157
193
  className: "flex gap-1",
158
194
  children: [
159
195
  "all",
@@ -161,13 +197,13 @@ function b() {
161
197
  "medium",
162
198
  "high",
163
199
  "critical"
164
- ].map((e) => /* @__PURE__ */ c("button", {
165
- onClick: () => S(e),
166
- className: `px-2 py-1 text-xs rounded capitalize ${x === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
200
+ ].map((e) => /* @__PURE__ */ u("button", {
201
+ onClick: () => T(e),
202
+ className: `px-2 py-1 text-xs rounded capitalize ${w === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
167
203
  children: e
168
204
  }, e))
169
205
  }),
170
- /* @__PURE__ */ c("div", {
206
+ /* @__PURE__ */ u("div", {
171
207
  className: "flex gap-1",
172
208
  children: [
173
209
  "all",
@@ -175,77 +211,77 @@ function b() {
175
211
  "bug",
176
212
  "task",
177
213
  "epic"
178
- ].map((e) => /* @__PURE__ */ c("button", {
179
- onClick: () => w(e),
180
- className: `px-2 py-1 text-xs rounded capitalize ${C === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
214
+ ].map((e) => /* @__PURE__ */ u("button", {
215
+ onClick: () => D(e),
216
+ className: `px-2 py-1 text-xs rounded capitalize ${E === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
181
217
  children: e
182
218
  }, e))
183
219
  })
184
220
  ]
185
221
  }),
186
- /* @__PURE__ */ c("div", {
222
+ /* @__PURE__ */ u("div", {
187
223
  className: "grid grid-cols-7 gap-3 overflow-x-auto",
188
- children: h.map((e) => {
189
- let t = P.filter((t) => t.status === e);
190
- return /* @__PURE__ */ l("div", {
224
+ children: v.map((e) => {
225
+ let t = U.filter((t) => t.status === e);
226
+ return /* @__PURE__ */ d("div", {
191
227
  className: "min-w-[200px]",
192
- children: [/* @__PURE__ */ c("div", {
193
- className: `border-t-2 ${_[e]} rounded-t-md p-2 bg-muted/50`,
194
- children: /* @__PURE__ */ l("div", {
228
+ children: [/* @__PURE__ */ u("div", {
229
+ className: `border-t-2 ${b[e]} rounded-t-md p-2 bg-muted/50`,
230
+ children: /* @__PURE__ */ d("div", {
195
231
  className: "flex justify-between items-center",
196
- children: [/* @__PURE__ */ c("span", {
232
+ children: [/* @__PURE__ */ u("span", {
197
233
  className: "text-sm font-medium",
198
- children: g[e]
199
- }), /* @__PURE__ */ c("span", {
234
+ children: y[e]
235
+ }), /* @__PURE__ */ u("span", {
200
236
  className: "text-xs bg-muted rounded-full px-1.5",
201
237
  "data-testid": `task-col-count-${e}`,
202
- children: L(e)
238
+ children: K(e)
203
239
  })]
204
240
  })
205
- }), /* @__PURE__ */ c("div", {
241
+ }), /* @__PURE__ */ u("div", {
206
242
  className: "space-y-2 mt-2",
207
- children: t.map((e) => /* @__PURE__ */ c(u, {
208
- ...r(() => b(`/tasks/task-detail-page?taskId=${encodeURIComponent(e.id)}`), e.title),
209
- children: /* @__PURE__ */ l(d, {
243
+ children: t.map((e) => /* @__PURE__ */ u(f, {
244
+ ...r(() => C(`/tasks/task-detail-page?taskId=${encodeURIComponent(e.id)}`), e.title),
245
+ children: /* @__PURE__ */ d(p, {
210
246
  className: "p-3 space-y-2",
211
247
  children: [
212
- /* @__PURE__ */ c("p", {
248
+ /* @__PURE__ */ u("p", {
213
249
  className: "text-sm font-medium leading-tight",
214
250
  children: e.title
215
251
  }),
216
- /* @__PURE__ */ c("p", {
252
+ /* @__PURE__ */ u("p", {
217
253
  className: "text-xs text-muted-foreground",
218
254
  children: e.key
219
255
  }),
220
- /* @__PURE__ */ l("div", {
256
+ /* @__PURE__ */ d("div", {
221
257
  className: "flex gap-1 flex-wrap",
222
- children: [/* @__PURE__ */ c("span", {
223
- className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${v[e.priority] ?? ""}`,
258
+ children: [/* @__PURE__ */ u("span", {
259
+ className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${x[e.priority] ?? ""}`,
224
260
  children: e.priority.toLowerCase()
225
- }), /* @__PURE__ */ c("span", {
226
- className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${y[e.type] ?? ""}`,
261
+ }), /* @__PURE__ */ u("span", {
262
+ className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${S[e.type] ?? ""}`,
227
263
  children: e.type.toLowerCase()
228
264
  })]
229
265
  }),
230
- /* @__PURE__ */ l("div", {
266
+ /* @__PURE__ */ d("div", {
231
267
  className: "flex items-center justify-between",
232
- children: [/* @__PURE__ */ c("div", {
268
+ children: [/* @__PURE__ */ u("div", {
233
269
  className: "flex gap-1",
234
- children: (e.labels ?? []).slice(0, 2).map((e) => /* @__PURE__ */ c("span", {
270
+ children: (e.labels ?? []).slice(0, 2).map((e) => /* @__PURE__ */ u("span", {
235
271
  className: "text-[9px] px-1 py-0.5 rounded bg-muted",
236
272
  children: e
237
273
  }, e))
238
- }), /* @__PURE__ */ c("div", {
274
+ }), /* @__PURE__ */ u("div", {
239
275
  className: "flex items-center gap-1",
240
- children: /* @__PURE__ */ c("span", {
276
+ children: /* @__PURE__ */ u("span", {
241
277
  className: "text-[10px] font-medium bg-primary/10 text-primary rounded-full h-5 w-5 flex items-center justify-center",
242
278
  children: e.storyPoints ?? 0
243
279
  })
244
280
  })]
245
281
  }),
246
- /* @__PURE__ */ l("div", {
282
+ /* @__PURE__ */ d("div", {
247
283
  className: "flex justify-between text-[10px] text-muted-foreground",
248
- children: [/* @__PURE__ */ c("span", { children: e.assigneeId ? /* @__PURE__ */ c(n, { id: e.assigneeId }) : "Unassigned" }), /* @__PURE__ */ c("span", { children: e.dueDate ? new Date(e.dueDate).toLocaleDateString() : "" })]
284
+ children: [/* @__PURE__ */ u("span", { children: e.assigneeId ? /* @__PURE__ */ u(n, { id: e.assigneeId }) : "Unassigned" }), /* @__PURE__ */ u("span", { children: e.dueDate ? new Date(e.dueDate).toLocaleDateString() : "" })]
249
285
  })
250
286
  ]
251
287
  })
@@ -254,21 +290,21 @@ function b() {
254
290
  }, e);
255
291
  })
256
292
  }),
257
- R && /* @__PURE__ */ c("div", {
293
+ q && /* @__PURE__ */ u("div", {
258
294
  className: "flex justify-center",
259
- children: /* @__PURE__ */ c("button", {
295
+ children: /* @__PURE__ */ u("button", {
260
296
  type: "button",
261
- onClick: N,
262
- disabled: E,
297
+ onClick: H,
298
+ disabled: F,
263
299
  className: "px-3 py-1.5 text-sm rounded-md bg-muted text-foreground hover:bg-muted/80 disabled:opacity-50",
264
300
  "data-testid": "task-board-load-more",
265
- children: E ? "Loading…" : `Load more (${P.length} of ${I})`
301
+ children: F ? "Loading…" : `Load more (${U.length} of ${G})`
266
302
  })
267
303
  })
268
- ]
304
+ ] })]
269
305
  });
270
306
  }
271
307
  //#endregion
272
- export { b as TasksTasksPagePage };
308
+ export { C as TasksTasksPagePage };
273
309
 
274
310
  //# sourceMappingURL=TasksTasksPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TasksTasksPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksTasksPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Filter } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\n\nconst GET_WORK_ITEMS = gql`\n query GetWorkItems(\n $workspaceId: ID!\n $filter: PlanmagnetWorkItemFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n type\n status\n priority\n key\n assigneeId\n reporterId\n sprintId\n storyPoints\n estimateMinutes\n dueDate\n labels\n tags\n createdAt\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n }\n`;\n\n// Server-side counts (QA H2): the list returns at most 100 rows per page, so\n// counting the loaded page undercounted every workspace past 100 items.\nconst GET_WORK_ITEM_COUNTS = gql`\n query GetWorkItemCounts($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {\n planmagnetWorkItemCounts(workspaceId: $workspaceId, filter: $filter) {\n total\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nconst PAGE_SIZE = 100;\n\ninterface WorkItemNode {\n id: string;\n title: string;\n type: string;\n status: string;\n priority: string;\n key: string;\n assigneeId: string | null;\n reporterId: string | null;\n sprintId: string | null;\n storyPoints: number | null;\n estimateMinutes: number | null;\n dueDate: string | null;\n labels: string[];\n tags: string[];\n createdAt: string;\n}\n\nconst columns = [\n 'BACKLOG',\n 'TODO',\n 'IN_PROGRESS',\n 'IN_REVIEW',\n 'TESTING',\n 'DONE',\n 'CANCELLED',\n] as const;\n\nconst colLabel: Record<string, string> = {\n BACKLOG: 'Backlog',\n TODO: 'To Do',\n IN_PROGRESS: 'In Progress',\n IN_REVIEW: 'In Review',\n TESTING: 'Testing',\n DONE: 'Done',\n CANCELLED: 'Cancelled',\n};\n\nconst colColor: Record<string, string> = {\n BACKLOG: 'border-t-gray-400',\n TODO: 'border-t-blue-400',\n IN_PROGRESS: 'border-t-yellow-400',\n IN_REVIEW: 'border-t-purple-400',\n TESTING: 'border-t-teal-400',\n DONE: 'border-t-green-400',\n CANCELLED: 'border-t-muted-foreground',\n};\n\nconst priorityColor: Record<string, string> = {\n LOW: 'bg-gray-100 text-gray-700',\n MEDIUM: 'bg-blue-100 text-blue-700',\n HIGH: 'bg-orange-100 text-orange-700',\n CRITICAL: 'bg-red-100 text-red-700',\n};\n\nconst typeColor: Record<string, string> = {\n STORY: 'bg-green-100 text-green-700',\n BUG: 'bg-red-100 text-red-700',\n TASK: 'bg-blue-100 text-blue-700',\n EPIC: 'bg-purple-100 text-purple-700',\n SUBTASK: 'bg-gray-100 text-gray-700',\n};\n\nexport function TasksTasksPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [priorityFilter, setPriorityFilter] = useState<string>('all');\n const [typeFilter, setTypeFilter] = useState<string>('all');\n\n // Filters go to the server so the loaded items AND the counts both reflect\n // every matching item, not just the first page.\n const filter = {\n ...(priorityFilter !== 'all' && { priority: priorityFilter.toUpperCase() }),\n ...(typeFilter !== 'all' && { type: typeFilter.toUpperCase() }),\n };\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetWorkItems: {\n items: WorkItemNode[];\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n totalCount: number;\n };\n }>(GET_WORK_ITEMS, {\n variables: { workspaceId, filter, pagination: { first: PAGE_SIZE } },\n skip: !workspaceId,\n });\n const { data: countsData } = useQuery<{\n planmagnetWorkItemCounts: { total: number; byStatus: { status: string; count: number }[] };\n }>(GET_WORK_ITEM_COUNTS, { variables: { workspaceId, filter }, skip: !workspaceId });\n\n const loadMore = async () => {\n const after = data?.planmagnetWorkItems?.pageInfo?.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE_SIZE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetWorkItems: {\n ...fetchMoreResult.planmagnetWorkItems,\n items: [\n ...prev.planmagnetWorkItems.items,\n ...fetchMoreResult.planmagnetWorkItems.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n // Only before the first load: a refetch or a filter change must not blank the board.\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n const tasks = data?.planmagnetWorkItems?.items ?? [];\n\n const counts = countsData?.planmagnetWorkItemCounts;\n const total = counts?.total ?? data?.planmagnetWorkItems?.totalCount ?? tasks.length;\n const countFor = (status: string) =>\n counts?.byStatus.find((c) => c.status === status)?.count ??\n tasks.filter((t) => t.status === status).length;\n const hasMore = data?.planmagnetWorkItems?.pageInfo?.hasNextPage ?? false;\n\n return (\n <div className=\"space-y-4 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Task Board</h1>\n <p className=\"text-sm text-muted-foreground\" data-testid=\"task-board-total\">\n {total} tasks across all projects\n {tasks.length < total && ` · showing ${tasks.length}`}\n </p>\n </div>\n </div>\n <div className=\"flex gap-4 items-center flex-wrap\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n <div className=\"flex gap-1\">\n {['all', 'low', 'medium', 'high', 'critical'].map((p) => (\n <button\n key={p}\n onClick={() => setPriorityFilter(p)}\n className={`px-2 py-1 text-xs rounded capitalize ${priorityFilter === p ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {p}\n </button>\n ))}\n </div>\n <div className=\"flex gap-1\">\n {['all', 'story', 'bug', 'task', 'epic'].map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-2 py-1 text-xs rounded capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {t}\n </button>\n ))}\n </div>\n </div>\n <div className=\"grid grid-cols-7 gap-3 overflow-x-auto\">\n {columns.map((col) => {\n const colTasks = tasks.filter((t) => t.status === col);\n return (\n <div key={col} className=\"min-w-[200px]\">\n <div className={`border-t-2 ${colColor[col]} rounded-t-md p-2 bg-muted/50`}>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm font-medium\">{colLabel[col]}</span>\n <span\n className=\"text-xs bg-muted rounded-full px-1.5\"\n data-testid={`task-col-count-${col}`}\n >\n {countFor(col)}\n </span>\n </div>\n </div>\n <div className=\"space-y-2 mt-2\">\n {colTasks.map((task) => (\n <Card\n key={task.id}\n {...rowLinkProps(\n () =>\n navigate(`/tasks/task-detail-page?taskId=${encodeURIComponent(task.id)}`),\n task.title\n )}\n >\n <CardContent className=\"p-3 space-y-2\">\n <p className=\"text-sm font-medium leading-tight\">{task.title}</p>\n <p className=\"text-xs text-muted-foreground\">{task.key}</p>\n <div className=\"flex gap-1 flex-wrap\">\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${priorityColor[task.priority] ?? ''}`}\n >\n {task.priority.toLowerCase()}\n </span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${typeColor[task.type] ?? ''}`}\n >\n {task.type.toLowerCase()}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <div className=\"flex gap-1\">\n {(task.labels ?? []).slice(0, 2).map((l) => (\n <span key={l} className=\"text-[9px] px-1 py-0.5 rounded bg-muted\">\n {l}\n </span>\n ))}\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] font-medium bg-primary/10 text-primary rounded-full h-5 w-5 flex items-center justify-center\">\n {task.storyPoints ?? 0}\n </span>\n </div>\n </div>\n <div className=\"flex justify-between text-[10px] text-muted-foreground\">\n <span>\n {task.assigneeId ? <PersonName id={task.assigneeId} /> : 'Unassigned'}\n </span>\n <span>\n {task.dueDate ? new Date(task.dueDate).toLocaleDateString() : ''}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n {hasMore && (\n <div className=\"flex justify-center\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loadingMore}\n className=\"px-3 py-1.5 text-sm rounded-md bg-muted text-foreground hover:bg-muted/80 disabled:opacity-50\"\n data-testid=\"task-board-load-more\"\n >\n {loadingMore ? 'Loading…' : `Load more (${tasks.length} of ${total})`}\n </button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TasksTasksPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/TasksTasksPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Filter } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { ProjectBoard } from '../board/ProjectBoard';\n\nconst GET_WORK_ITEMS = gql`\n query GetWorkItems(\n $workspaceId: ID!\n $filter: PlanmagnetWorkItemFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetWorkItems(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n type\n status\n priority\n key\n assigneeId\n reporterId\n sprintId\n storyPoints\n estimateMinutes\n dueDate\n labels\n tags\n createdAt\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n }\n`;\n\n// Server-side counts (QA H2): the list returns at most 100 rows per page, so\n// counting the loaded page undercounted every workspace past 100 items.\nconst GET_WORK_ITEM_COUNTS = gql`\n query GetWorkItemCounts($workspaceId: ID!, $filter: PlanmagnetWorkItemFilter) {\n planmagnetWorkItemCounts(workspaceId: $workspaceId, filter: $filter) {\n total\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nconst PAGE_SIZE = 100;\n\nconst GET_BOARD_PROJECTS = gql`\n query GetBoardProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\ninterface WorkItemNode {\n id: string;\n title: string;\n type: string;\n status: string;\n priority: string;\n key: string;\n assigneeId: string | null;\n reporterId: string | null;\n sprintId: string | null;\n storyPoints: number | null;\n estimateMinutes: number | null;\n dueDate: string | null;\n labels: string[];\n tags: string[];\n createdAt: string;\n}\n\nconst columns = [\n 'BACKLOG',\n 'TODO',\n 'IN_PROGRESS',\n 'IN_REVIEW',\n 'TESTING',\n 'DONE',\n 'CANCELLED',\n] as const;\n\nconst colLabel: Record<string, string> = {\n BACKLOG: 'Backlog',\n TODO: 'To Do',\n IN_PROGRESS: 'In Progress',\n IN_REVIEW: 'In Review',\n TESTING: 'Testing',\n DONE: 'Done',\n CANCELLED: 'Cancelled',\n};\n\nconst colColor: Record<string, string> = {\n BACKLOG: 'border-t-gray-400',\n TODO: 'border-t-blue-400',\n IN_PROGRESS: 'border-t-yellow-400',\n IN_REVIEW: 'border-t-purple-400',\n TESTING: 'border-t-teal-400',\n DONE: 'border-t-green-400',\n CANCELLED: 'border-t-muted-foreground',\n};\n\nconst priorityColor: Record<string, string> = {\n LOW: 'bg-gray-100 text-gray-700',\n MEDIUM: 'bg-blue-100 text-blue-700',\n HIGH: 'bg-orange-100 text-orange-700',\n CRITICAL: 'bg-red-100 text-red-700',\n};\n\nconst typeColor: Record<string, string> = {\n STORY: 'bg-green-100 text-green-700',\n BUG: 'bg-red-100 text-red-700',\n TASK: 'bg-blue-100 text-blue-700',\n EPIC: 'bg-purple-100 text-purple-700',\n SUBTASK: 'bg-gray-100 text-gray-700',\n};\n\nexport function TasksTasksPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [priorityFilter, setPriorityFilter] = useState<string>('all');\n const [typeFilter, setTypeFilter] = useState<string>('all');\n // P1.2: the Task Board is a project's real board (server-side placement,\n // drag-and-drop, WIP limits). null = not chosen yet -> the first project;\n // '' = the cross-project status overview, which is NOT a board.\n const [boardProject, setBoardProject] = useState<string | null>(null);\n const { data: projectsData } = useQuery(GET_BOARD_PROJECTS, {\n variables: { workspaceId },\n skip: !workspaceId,\n });\n const boardProjects = projectsData?.planmagnetProjects?.items ?? [];\n const selectedProject = boardProject ?? boardProjects[0]?.id ?? '';\n const selectedProjectName = boardProjects.find((p) => p.id === selectedProject)?.name ?? '';\n\n // Filters go to the server so the loaded items AND the counts both reflect\n // every matching item, not just the first page.\n const filter = {\n ...(priorityFilter !== 'all' && { priority: priorityFilter.toUpperCase() }),\n ...(typeFilter !== 'all' && { type: typeFilter.toUpperCase() }),\n };\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, fetchMore } = useQuery<{\n planmagnetWorkItems: {\n items: WorkItemNode[];\n pageInfo: { hasNextPage: boolean; endCursor: string | null };\n totalCount: number;\n };\n }>(GET_WORK_ITEMS, {\n variables: { workspaceId, filter, pagination: { first: PAGE_SIZE } },\n // The list only feeds the cross-project overview; a board loads its own cards.\n skip: !workspaceId || !!selectedProject,\n });\n const { data: countsData } = useQuery<{\n planmagnetWorkItemCounts: { total: number; byStatus: { status: string; count: number }[] };\n }>(GET_WORK_ITEM_COUNTS, { variables: { workspaceId, filter }, skip: !workspaceId });\n\n const loadMore = async () => {\n const after = data?.planmagnetWorkItems?.pageInfo?.endCursor;\n if (!after) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { first: PAGE_SIZE, after } },\n updateQuery: (prev, { fetchMoreResult }) => ({\n planmagnetWorkItems: {\n ...fetchMoreResult.planmagnetWorkItems,\n items: [\n ...prev.planmagnetWorkItems.items,\n ...fetchMoreResult.planmagnetWorkItems.items,\n ],\n },\n }),\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n // Only before the first load: a refetch or a filter change must not blank the board.\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n const tasks = data?.planmagnetWorkItems?.items ?? [];\n\n const counts = countsData?.planmagnetWorkItemCounts;\n const total = counts?.total ?? data?.planmagnetWorkItems?.totalCount ?? tasks.length;\n const countFor = (status: string) =>\n counts?.byStatus.find((c) => c.status === status)?.count ??\n tasks.filter((t) => t.status === status).length;\n const hasMore = data?.planmagnetWorkItems?.pageInfo?.hasNextPage ?? false;\n\n return (\n <div className=\"space-y-4 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Task Board</h1>\n <label className=\"sr-only\" htmlFor=\"task-board-project\">\n Project\n </label>\n <select\n id=\"task-board-project\"\n className=\"mt-2 rounded-md border border-input bg-background px-2 py-1 text-sm\"\n value={selectedProject}\n onChange={(e) => setBoardProject(e.target.value)}\n >\n {boardProjects.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n <option value=\"\">All projects — status overview</option>\n </select>\n <p className=\"text-sm text-muted-foreground\" data-testid=\"task-board-total\">\n {total} tasks across all projects\n {tasks.length < total && ` · showing ${tasks.length}`}\n </p>\n </div>\n </div>\n {selectedProject ? (\n <ProjectBoard projectId={selectedProject} projectName={selectedProjectName} />\n ) : (\n <>\n <div className=\"flex gap-4 items-center flex-wrap\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n <div className=\"flex gap-1\">\n {['all', 'low', 'medium', 'high', 'critical'].map((p) => (\n <button\n key={p}\n onClick={() => setPriorityFilter(p)}\n className={`px-2 py-1 text-xs rounded capitalize ${priorityFilter === p ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {p}\n </button>\n ))}\n </div>\n <div className=\"flex gap-1\">\n {['all', 'story', 'bug', 'task', 'epic'].map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-2 py-1 text-xs rounded capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {t}\n </button>\n ))}\n </div>\n </div>\n <div className=\"grid grid-cols-7 gap-3 overflow-x-auto\">\n {columns.map((col) => {\n const colTasks = tasks.filter((t) => t.status === col);\n return (\n <div key={col} className=\"min-w-[200px]\">\n <div className={`border-t-2 ${colColor[col]} rounded-t-md p-2 bg-muted/50`}>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm font-medium\">{colLabel[col]}</span>\n <span\n className=\"text-xs bg-muted rounded-full px-1.5\"\n data-testid={`task-col-count-${col}`}\n >\n {countFor(col)}\n </span>\n </div>\n </div>\n <div className=\"space-y-2 mt-2\">\n {colTasks.map((task) => (\n <Card\n key={task.id}\n {...rowLinkProps(\n () =>\n navigate(\n `/tasks/task-detail-page?taskId=${encodeURIComponent(task.id)}`\n ),\n task.title\n )}\n >\n <CardContent className=\"p-3 space-y-2\">\n <p className=\"text-sm font-medium leading-tight\">{task.title}</p>\n <p className=\"text-xs text-muted-foreground\">{task.key}</p>\n <div className=\"flex gap-1 flex-wrap\">\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${priorityColor[task.priority] ?? ''}`}\n >\n {task.priority.toLowerCase()}\n </span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${typeColor[task.type] ?? ''}`}\n >\n {task.type.toLowerCase()}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <div className=\"flex gap-1\">\n {(task.labels ?? []).slice(0, 2).map((l) => (\n <span key={l} className=\"text-[9px] px-1 py-0.5 rounded bg-muted\">\n {l}\n </span>\n ))}\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] font-medium bg-primary/10 text-primary rounded-full h-5 w-5 flex items-center justify-center\">\n {task.storyPoints ?? 0}\n </span>\n </div>\n </div>\n <div className=\"flex justify-between text-[10px] text-muted-foreground\">\n <span>\n {task.assigneeId ? <PersonName id={task.assigneeId} /> : 'Unassigned'}\n </span>\n <span>\n {task.dueDate ? new Date(task.dueDate).toLocaleDateString() : ''}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n {hasMore && (\n <div className=\"flex justify-center\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loadingMore}\n className=\"px-3 py-1.5 text-sm rounded-md bg-muted text-foreground hover:bg-muted/80 disabled:opacity-50\"\n data-testid=\"task-board-load-more\"\n >\n {loadingMore ? 'Loading…' : `Load more (${tasks.length} of ${total})`}\n </button>\n </div>\n )}\n </>\n )}\n </div>\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.925.6",
3
+ "version": "2026.925.7",
4
4
  "description": "Burdenoff PlanMagnet Microfrontend",
5
5
  "type": "module",
6
6
  "files": [