@burdenoff/microfe-planmagnet 2026.925.6 → 2026.925.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/wspace-types.d.ts +17 -0
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/planmagnet/board/ProjectBoard.d.ts +5 -0
- package/dist/planmagnet/board/ProjectBoard.d.ts.map +1 -0
- package/dist/planmagnet/board/ProjectBoard.js +492 -0
- package/dist/planmagnet/board/ProjectBoard.js.map +1 -0
- package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
- package/dist/planmagnet/create/CreateDialogs.js +118 -117
- package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +322 -280
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +147 -126
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +129 -93
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.js +202 -201
- package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
- package/dist/planmagnet/utils/mutationResult.d.ts +16 -0
- package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -0
- package/dist/planmagnet/utils/mutationResult.js +10 -0
- package/dist/planmagnet/utils/mutationResult.js.map +1 -0
- package/package.json +3 -2
|
@@ -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 {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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
|
|
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
|
-
`,
|
|
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
|
-
`,
|
|
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
|
-
],
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
|
89
|
-
let { workspaceId:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
},
|
|
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:
|
|
95
|
-
filter:
|
|
96
|
-
pagination: { first:
|
|
107
|
+
workspaceId: s,
|
|
108
|
+
filter: P,
|
|
109
|
+
pagination: { first: g }
|
|
97
110
|
},
|
|
98
|
-
skip: !
|
|
99
|
-
}), { data:
|
|
111
|
+
skip: !s || !!M
|
|
112
|
+
}), { data: V } = c(h, {
|
|
100
113
|
variables: {
|
|
101
|
-
workspaceId:
|
|
102
|
-
filter:
|
|
114
|
+
workspaceId: s,
|
|
115
|
+
filter: P
|
|
103
116
|
},
|
|
104
|
-
skip: !
|
|
105
|
-
}),
|
|
106
|
-
let e =
|
|
117
|
+
skip: !s
|
|
118
|
+
}), H = async () => {
|
|
119
|
+
let e = L?.planmagnetWorkItems?.pageInfo?.endCursor;
|
|
107
120
|
if (e) {
|
|
108
|
-
|
|
121
|
+
I(!0);
|
|
109
122
|
try {
|
|
110
|
-
await
|
|
123
|
+
await B({
|
|
111
124
|
variables: { pagination: {
|
|
112
|
-
first:
|
|
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
|
-
|
|
134
|
+
I(!1);
|
|
122
135
|
}
|
|
123
136
|
}
|
|
124
137
|
};
|
|
125
|
-
if (
|
|
138
|
+
if (R && !L) return /* @__PURE__ */ u("div", {
|
|
126
139
|
className: "flex items-center justify-center p-12",
|
|
127
|
-
children: /* @__PURE__ */
|
|
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 (
|
|
142
|
+
if (z) return /* @__PURE__ */ d("div", {
|
|
130
143
|
className: "p-6 text-red-500",
|
|
131
|
-
children: ["Error: ",
|
|
144
|
+
children: ["Error: ", z.message]
|
|
132
145
|
});
|
|
133
|
-
let
|
|
134
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
}),
|
|
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
|
-
|
|
178
|
+
G,
|
|
147
179
|
" tasks across all projects",
|
|
148
|
-
|
|
180
|
+
U.length < G && ` · showing ${U.length}`
|
|
149
181
|
]
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
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__ */
|
|
156
|
-
/* @__PURE__ */
|
|
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__ */
|
|
165
|
-
onClick: () =>
|
|
166
|
-
className: `px-2 py-1 text-xs rounded capitalize ${
|
|
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__ */
|
|
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__ */
|
|
179
|
-
onClick: () =>
|
|
180
|
-
className: `px-2 py-1 text-xs rounded capitalize ${
|
|
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__ */
|
|
222
|
+
/* @__PURE__ */ u("div", {
|
|
187
223
|
className: "grid grid-cols-7 gap-3 overflow-x-auto",
|
|
188
|
-
children:
|
|
189
|
-
let t =
|
|
190
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
193
|
-
className: `border-t-2 ${
|
|
194
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
232
|
+
children: [/* @__PURE__ */ u("span", {
|
|
197
233
|
className: "text-sm font-medium",
|
|
198
|
-
children:
|
|
199
|
-
}), /* @__PURE__ */
|
|
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:
|
|
238
|
+
children: K(e)
|
|
203
239
|
})]
|
|
204
240
|
})
|
|
205
|
-
}), /* @__PURE__ */
|
|
241
|
+
}), /* @__PURE__ */ u("div", {
|
|
206
242
|
className: "space-y-2 mt-2",
|
|
207
|
-
children: t.map((e) => /* @__PURE__ */
|
|
208
|
-
...r(() =>
|
|
209
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
248
|
+
/* @__PURE__ */ u("p", {
|
|
213
249
|
className: "text-sm font-medium leading-tight",
|
|
214
250
|
children: e.title
|
|
215
251
|
}),
|
|
216
|
-
/* @__PURE__ */
|
|
252
|
+
/* @__PURE__ */ u("p", {
|
|
217
253
|
className: "text-xs text-muted-foreground",
|
|
218
254
|
children: e.key
|
|
219
255
|
}),
|
|
220
|
-
/* @__PURE__ */
|
|
256
|
+
/* @__PURE__ */ d("div", {
|
|
221
257
|
className: "flex gap-1 flex-wrap",
|
|
222
|
-
children: [/* @__PURE__ */
|
|
223
|
-
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${
|
|
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__ */
|
|
226
|
-
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${
|
|
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__ */
|
|
266
|
+
/* @__PURE__ */ d("div", {
|
|
231
267
|
className: "flex items-center justify-between",
|
|
232
|
-
children: [/* @__PURE__ */
|
|
268
|
+
children: [/* @__PURE__ */ u("div", {
|
|
233
269
|
className: "flex gap-1",
|
|
234
|
-
children: (e.labels ?? []).slice(0, 2).map((e) => /* @__PURE__ */
|
|
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__ */
|
|
274
|
+
}), /* @__PURE__ */ u("div", {
|
|
239
275
|
className: "flex items-center gap-1",
|
|
240
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
282
|
+
/* @__PURE__ */ d("div", {
|
|
247
283
|
className: "flex justify-between text-[10px] text-muted-foreground",
|
|
248
|
-
children: [/* @__PURE__ */
|
|
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
|
-
|
|
293
|
+
q && /* @__PURE__ */ u("div", {
|
|
258
294
|
className: "flex justify-center",
|
|
259
|
-
children: /* @__PURE__ */
|
|
295
|
+
children: /* @__PURE__ */ u("button", {
|
|
260
296
|
type: "button",
|
|
261
|
-
onClick:
|
|
262
|
-
disabled:
|
|
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:
|
|
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 {
|
|
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":";;;;;;;;;;AAWA,IAAM,IAAiB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCpB,IAAuB,CAAG;;;;;;;;;;GAY1B,IAAY,KAoBZ,IAAU;CACd;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAmC;CACvC,SAAS;CACT,MAAM;CACN,aAAa;CACb,WAAW;CACX,SAAS;CACT,MAAM;CACN,WAAW;CACZ,EAEK,IAAmC;CACvC,SAAS;CACT,MAAM;CACN,aAAa;CACb,WAAW;CACX,SAAS;CACT,MAAM;CACN,WAAW;CACZ,EAEK,IAAwC;CAC5C,KAAK;CACL,QAAQ;CACR,MAAM;CACN,UAAU;CACX,EAEK,IAAoC;CACxC,OAAO;CACP,KAAK;CACL,MAAM;CACN,MAAM;CACN,SAAS;CACV;AAED,SAAgB,IAAqB;CACnC,IAAM,EAAE,mBAAgB,GAAe,EACjC,IAAW,GAAuB,EAClC,CAAC,GAAgB,KAAqB,EAAiB,MAAM,EAC7D,CAAC,GAAY,KAAiB,EAAiB,MAAM,EAIrD,IAAS;EACb,GAAI,MAAmB,SAAS,EAAE,UAAU,EAAe,aAAa,EAAE;EAC1E,GAAI,MAAe,SAAS,EAAE,MAAM,EAAW,aAAa,EAAE;EAC/D,EACK,CAAC,GAAa,KAAkB,EAAS,GAAM,EAE/C,EAAE,SAAM,YAAS,UAAO,iBAAc,EAMzC,GAAgB;EACjB,WAAW;GAAE;GAAa;GAAQ,YAAY,EAAE,OAAO,GAAW;GAAE;EACpE,MAAM,CAAC;EACR,CAAC,EACI,EAAE,MAAM,MAAe,EAE1B,GAAsB;EAAE,WAAW;GAAE;GAAa;GAAQ;EAAE,MAAM,CAAC;EAAa,CAAC,EAE9E,IAAW,YAAY;EAC3B,IAAM,IAAQ,GAAM,qBAAqB,UAAU;AAC9C,SACL;KAAe,GAAK;AACpB,OAAI;AACF,UAAM,EAAU;KACd,WAAW,EAAE,YAAY;MAAE,OAAO;MAAW;MAAO,EAAE;KACtD,cAAc,GAAM,EAAE,0BAAuB,EAC3C,qBAAqB;MACnB,GAAG,EAAgB;MACnB,OAAO,CACL,GAAG,EAAK,oBAAoB,OAC5B,GAAG,EAAgB,oBAAoB,MACxC;MACF,EACF;KACF,CAAC;aACM;AACR,MAAe,GAAM;;;;AAKzB,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA;EAC9F,CAAA;AAIV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAkC,WAAQ,EAAM,QAAc;;CAGvE,IAAM,IAAQ,GAAM,qBAAqB,SAAS,EAAE,EAE9C,IAAS,GAAY,0BACrB,IAAQ,GAAQ,SAAS,GAAM,qBAAqB,cAAc,EAAM,QACxE,KAAY,MAChB,GAAQ,SAAS,MAAM,MAAM,EAAE,WAAW,EAAO,EAAE,SACnD,EAAM,QAAQ,MAAM,EAAE,WAAW,EAAO,CAAC,QACrC,IAAU,GAAM,qBAAqB,UAAU,eAAe;AAEpE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAyB;KAAe,CAAA,EACtD,kBAAC,KAAD;KAAG,WAAU;KAAgC,eAAY;eAAzD;MACG;MAAM;MACN,EAAM,SAAS,KAAS,cAAc,EAAM;MAC3C;OACA,EAAA,CAAA;IACF,CAAA;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAQ,WAAU,iCAAkC,CAAA;KACpD,kBAAC,OAAD;MAAK,WAAU;gBACZ;OAAC;OAAO;OAAO;OAAU;OAAQ;OAAW,CAAC,KAAK,MACjD,kBAAC,UAAD;OAEE,eAAe,EAAkB,EAAE;OACnC,WAAW,wCAAwC,MAAmB,IAAI,uCAAuC;iBAEhH;OACM,EALF,EAKE,CACT;MACE,CAAA;KACN,kBAAC,OAAD;MAAK,WAAU;gBACZ;OAAC;OAAO;OAAS;OAAO;OAAQ;OAAO,CAAC,KAAK,MAC5C,kBAAC,UAAD;OAEE,eAAe,EAAc,EAAE;OAC/B,WAAW,wCAAwC,MAAe,IAAI,uCAAuC;iBAE5G;OACM,EALF,EAKE,CACT;MACE,CAAA;KACF;;GACN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MAAQ;KACpB,IAAM,IAAW,EAAM,QAAQ,MAAM,EAAE,WAAW,EAAI;AACtD,YACE,kBAAC,OAAD;MAAe,WAAU;gBAAzB,CACE,kBAAC,OAAD;OAAK,WAAW,cAAc,EAAS,GAAK;iBAC1C,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAuB,EAAS;SAAY,CAAA,EAC5D,kBAAC,QAAD;SACE,WAAU;SACV,eAAa,kBAAkB;mBAE9B,EAAS,EAAI;SACT,CAAA,CACH;;OACF,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAS,KAAK,MACb,kBAAC,GAAD;QAEE,GAAI,QAEA,EAAS,kCAAkC,mBAAmB,EAAK,GAAG,GAAG,EAC3E,EAAK,MACN;kBAED,kBAAC,GAAD;SAAa,WAAU;mBAAvB;UACE,kBAAC,KAAD;WAAG,WAAU;qBAAqC,EAAK;WAAU,CAAA;UACjE,kBAAC,KAAD;WAAG,WAAU;qBAAiC,EAAK;WAAQ,CAAA;UAC3D,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,QAAD;YACE,WAAW,qDAAqD,EAAc,EAAK,aAAa;sBAE/F,EAAK,SAAS,aAAa;YACvB,CAAA,EACP,kBAAC,QAAD;YACE,WAAW,qDAAqD,EAAU,EAAK,SAAS;sBAEvF,EAAK,KAAK,aAAa;YACnB,CAAA,CACH;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,OAAD;YAAK,WAAU;uBACX,EAAK,UAAU,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,MACpC,kBAAC,QAAD;aAAc,WAAU;uBACrB;aACI,EAFI,EAEJ,CACP;YACE,CAAA,EACN,kBAAC,OAAD;YAAK,WAAU;sBACb,kBAAC,QAAD;aAAM,WAAU;uBACb,EAAK,eAAe;aAChB,CAAA;YACH,CAAA,CACF;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,QAAD,EAAA,UACG,EAAK,aAAa,kBAAC,GAAD,EAAY,IAAI,EAAK,YAAc,CAAA,GAAG,cACpD,CAAA,EACP,kBAAC,QAAD,EAAA,UACG,EAAK,UAAU,IAAI,KAAK,EAAK,QAAQ,CAAC,oBAAoB,GAAG,IACzD,CAAA,CACH;;UACM;;QACT,EA7CA,EAAK,GA6CL,CACP;OACE,CAAA,CACF;QA/DI,EA+DJ;MAER;IACE,CAAA;GACL,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAU;KACV,eAAY;eAEX,IAAc,aAAa,cAAc,EAAM,OAAO,MAAM,EAAM;KAC5D,CAAA;IACL,CAAA;GAEJ"}
|
|
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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SprintControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/sprints/SprintControls.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SprintControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/sprints/SprintControls.tsx"],"names":[],"mappings":"AA6BA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AACxF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAQ3D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAI/D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD;AAsCD,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,UAAU,EACV,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB,kDAyDA;AAsFD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAQD,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,EACT,MAAM,EACN,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB,2CA2EA;AAqKD,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAuKvF"}
|