@burdenoff/microfe-timecampus 2026.626.2 → 2026.703.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/generated/wspace-operations.js +996 -816
  2. package/dist/generated/wspace-operations.js.map +1 -1
  3. package/dist/generated/wspace-types.js +28 -0
  4. package/dist/generated/wspace-types.js.map +1 -0
  5. package/dist/index.js +4 -4
  6. package/dist/timecampus/TimeCampusRoutes.js +16 -12
  7. package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
  8. package/dist/timecampus/components/Modal.js +65 -0
  9. package/dist/timecampus/components/Modal.js.map +1 -0
  10. package/dist/timecampus/components/StatusBadge.js +28 -0
  11. package/dist/timecampus/components/StatusBadge.js.map +1 -0
  12. package/dist/timecampus/components/Toast.js +87 -0
  13. package/dist/timecampus/components/Toast.js.map +1 -0
  14. package/dist/timecampus/index.js +1 -1
  15. package/dist/timecampus/pages/HomePage.js +143 -135
  16. package/dist/timecampus/pages/HomePage.js.map +1 -1
  17. package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
  18. package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
  19. package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
  20. package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
  21. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
  22. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  23. package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
  24. package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
  25. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +374 -92
  26. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  27. package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
  28. package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
  29. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
  30. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  31. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +461 -87
  32. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  33. package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
  34. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  35. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
  36. package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
  37. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
  38. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  39. package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
  40. package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
  41. package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
  42. package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
  43. package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
  44. package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
  45. package/dist/timecampus/pages/meetings/MeetingFormModal.js +156 -0
  46. package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
  47. package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
  48. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  49. package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
  50. package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
  51. package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
  52. package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
  53. package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
  54. package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
  55. package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
  56. package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
  57. package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
  58. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  59. package/dist/timecampus/pages/projects/TasksPage.js +367 -65
  60. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  61. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
  62. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  63. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
  64. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  65. package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
  66. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  67. package/dist/timecampus/pages/team/TeamPage.js +4 -3
  68. package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
  69. package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
  70. package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
  71. package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
  72. package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
  73. package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
  74. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  75. package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
  76. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  77. package/dist/timecampus/pages/time/TimerPage.js +229 -97
  78. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  79. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +315 -110
  80. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  81. package/dist/timecampus/pages/timeline/TimelinePage.js +250 -0
  82. package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -0
  83. package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
  84. package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
  85. package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
  86. package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
  87. package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
  88. package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
  89. package/dist/timecampus/store/timecampusStore.js +13 -1
  90. package/dist/timecampus/store/timecampusStore.js.map +1 -1
  91. package/dist/timecampus/utils/helpers.js +20 -10
  92. package/dist/timecampus/utils/helpers.js.map +1 -1
  93. package/package.json +1 -1
@@ -1,83 +1,385 @@
1
- import { getPriorityColor as e, getStatusColor as t } from "../../utils/helpers.js";
2
- import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
3
- import { useListTasksQuery as r } from "../../../generated/wspace-operations.js";
4
- import { PageLayout as i } from "../../components/PageLayout.js";
5
- import { EmptyState as a } from "../../components/EmptyState.js";
6
- import { CheckSquare as o, Plus as s } from "lucide-react";
7
- import { useParams as c } from "react-router-dom";
8
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
9
- import { GlassCard as d } from "@burdenoff/fe-libs/ui";
1
+ import { getPriorityColor as e } from "../../utils/helpers.js";
2
+ import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
+ import { useCreateTaskMutation as n, useDeleteTaskMutation as r, useListTasksQuery as i, useReorderTasksMutation as a, useUpdateTaskMutation as o, useUpdateTaskStatusMutation as s } from "../../../generated/wspace-operations.js";
4
+ import { PageLayout as c } from "../../components/PageLayout.js";
5
+ import { EmptyState as l } from "../../components/EmptyState.js";
6
+ import { Modal as u } from "../../components/Modal.js";
7
+ import { useState as d } from "react";
8
+ import { ArrowDown as f, ArrowUp as ee, CheckSquare as te, Pencil as p, Plus as m, Trash2 as h } from "lucide-react";
9
+ import { useParams as g } from "react-router-dom";
10
+ import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
11
+ import { GlassCard as b } from "@burdenoff/fe-libs/ui";
10
12
  //#region src/timecampus/pages/projects/TasksPage.tsx
11
- function f() {
12
- let { projectId: f } = c(), { data: p, loading: m, error: h } = r({
13
- variables: { projectId: f },
14
- skip: !f
15
- }), g = p?.tasks?.items ?? [];
16
- return h ? /* @__PURE__ */ l(i, {
13
+ var x = 25, S = [
14
+ "todo",
15
+ "in-progress",
16
+ "review",
17
+ "done"
18
+ ], ne = [
19
+ "low",
20
+ "medium",
21
+ "high",
22
+ "urgent"
23
+ ], C = {
24
+ title: "",
25
+ description: "",
26
+ status: "todo",
27
+ priority: "medium",
28
+ assigneeId: "",
29
+ estimatedHours: "",
30
+ dueDate: ""
31
+ }, w = "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary", T = "block text-sm font-medium text-text-primary";
32
+ function E() {
33
+ let { projectId: E } = g(), [D, re] = d(x), { data: O, loading: k, error: ie, refetch: A } = i({
34
+ variables: {
35
+ projectId: E,
36
+ limit: D,
37
+ offset: 0
38
+ },
39
+ skip: !E,
40
+ notifyOnNetworkStatusChange: !0
41
+ }), j = O?.tasks?.items ?? [], M = O?.tasks?.hasMore ?? !1, [N, P] = d(!1), [F, I] = d(null), [L, R] = d(null), [z, B] = d(C), [V, { loading: H, error: U }] = n(), [W, { loading: G, error: K }] = o(), [q] = s(), [J, { loading: Y, error: ae }] = r(), [oe, { loading: X }] = a(), Z = () => {
42
+ B(C), P(!0);
43
+ }, se = (e) => {
44
+ B({
45
+ title: e.title,
46
+ description: e.description ?? "",
47
+ status: e.status,
48
+ priority: e.priority,
49
+ assigneeId: e.assigneeId ?? "",
50
+ estimatedHours: e.estimatedHours == null ? "" : String(e.estimatedHours),
51
+ dueDate: e.dueDate ? e.dueDate.slice(0, 10) : ""
52
+ }), I(e);
53
+ }, ce = async () => {
54
+ !E || !z.title.trim() || (await V({ variables: { input: {
55
+ projectId: E,
56
+ title: z.title.trim(),
57
+ description: z.description.trim() || void 0,
58
+ status: z.status,
59
+ priority: z.priority,
60
+ assigneeId: z.assigneeId.trim() || void 0,
61
+ estimatedHours: z.estimatedHours ? Number(z.estimatedHours) : void 0,
62
+ dueDate: z.dueDate ? new Date(z.dueDate).toISOString() : void 0
63
+ } } }), P(!1), await A());
64
+ }, le = async () => {
65
+ !F || !z.title.trim() || (await W({ variables: {
66
+ id: F.id,
67
+ input: {
68
+ title: z.title.trim(),
69
+ description: z.description.trim() || void 0,
70
+ status: z.status,
71
+ priority: z.priority,
72
+ assigneeId: z.assigneeId.trim() || void 0,
73
+ estimatedHours: z.estimatedHours ? Number(z.estimatedHours) : void 0,
74
+ dueDate: z.dueDate ? new Date(z.dueDate).toISOString() : void 0
75
+ }
76
+ } }), I(null), await A());
77
+ }, ue = async (e, t) => {
78
+ await q({ variables: {
79
+ id: e.id,
80
+ status: t
81
+ } }), await A();
82
+ }, de = async () => {
83
+ L && (await J({ variables: { id: L.id } }), R(null), await A());
84
+ }, Q = async (e, t) => {
85
+ if (!E) return;
86
+ let n = e + t;
87
+ if (n < 0 || n >= j.length) return;
88
+ let r = j.map((e) => e.id), [i] = r.splice(e, 1);
89
+ r.splice(n, 0, i), await oe({ variables: {
90
+ projectId: E,
91
+ taskIds: r
92
+ } }), await A();
93
+ }, $ = /* @__PURE__ */ y("div", {
94
+ className: "space-y-4",
95
+ children: [
96
+ /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
97
+ className: T,
98
+ children: "Title"
99
+ }), /* @__PURE__ */ v("input", {
100
+ type: "text",
101
+ placeholder: "Task title",
102
+ value: z.title,
103
+ onChange: (e) => B({
104
+ ...z,
105
+ title: e.target.value
106
+ }),
107
+ className: w
108
+ })] }),
109
+ /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
110
+ className: T,
111
+ children: "Description"
112
+ }), /* @__PURE__ */ v("textarea", {
113
+ placeholder: "Task description",
114
+ rows: 3,
115
+ value: z.description,
116
+ onChange: (e) => B({
117
+ ...z,
118
+ description: e.target.value
119
+ }),
120
+ className: w
121
+ })] }),
122
+ /* @__PURE__ */ y("div", {
123
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
124
+ children: [/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
125
+ className: T,
126
+ children: "Status"
127
+ }), /* @__PURE__ */ v("select", {
128
+ value: z.status,
129
+ onChange: (e) => B({
130
+ ...z,
131
+ status: e.target.value
132
+ }),
133
+ className: w,
134
+ children: S.map((e) => /* @__PURE__ */ v("option", {
135
+ value: e,
136
+ children: e
137
+ }, e))
138
+ })] }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
139
+ className: T,
140
+ children: "Priority"
141
+ }), /* @__PURE__ */ v("select", {
142
+ value: z.priority,
143
+ onChange: (e) => B({
144
+ ...z,
145
+ priority: e.target.value
146
+ }),
147
+ className: w,
148
+ children: ne.map((e) => /* @__PURE__ */ v("option", {
149
+ value: e,
150
+ children: e
151
+ }, e))
152
+ })] })]
153
+ }),
154
+ /* @__PURE__ */ y("div", {
155
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
156
+ children: [
157
+ /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
158
+ className: T,
159
+ children: "Assignee ID"
160
+ }), /* @__PURE__ */ v("input", {
161
+ type: "text",
162
+ placeholder: "User ID",
163
+ value: z.assigneeId,
164
+ onChange: (e) => B({
165
+ ...z,
166
+ assigneeId: e.target.value
167
+ }),
168
+ className: w
169
+ })] }),
170
+ /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
171
+ className: T,
172
+ children: "Estimated Hours"
173
+ }), /* @__PURE__ */ v("input", {
174
+ type: "number",
175
+ placeholder: "0",
176
+ value: z.estimatedHours,
177
+ onChange: (e) => B({
178
+ ...z,
179
+ estimatedHours: e.target.value
180
+ }),
181
+ className: w
182
+ })] }),
183
+ /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
184
+ className: T,
185
+ children: "Due Date"
186
+ }), /* @__PURE__ */ v("input", {
187
+ type: "date",
188
+ value: z.dueDate,
189
+ onChange: (e) => B({
190
+ ...z,
191
+ dueDate: e.target.value
192
+ }),
193
+ className: w
194
+ })] })
195
+ ]
196
+ })
197
+ ]
198
+ });
199
+ return ie ? /* @__PURE__ */ v(c, {
17
200
  title: "Tasks",
18
- description: `Tasks for project ${f}`,
19
- children: /* @__PURE__ */ l("div", {
201
+ description: `Tasks for project ${E}`,
202
+ children: /* @__PURE__ */ v("div", {
20
203
  className: "rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text",
21
204
  children: "Failed to load tasks. Please try again later."
22
205
  })
23
- }) : /* @__PURE__ */ l(i, {
206
+ }) : /* @__PURE__ */ y(c, {
24
207
  title: "Tasks",
25
- description: `Tasks for project ${f}`,
208
+ description: `Tasks for project ${E}`,
26
209
  purpose: "Break a project down into the concrete pieces of work that need doing, then track hours against each one — so you can see what's done, what's blocking, and where effort is actually being spent.",
27
- actions: /* @__PURE__ */ u("button", {
210
+ actions: /* @__PURE__ */ y("button", {
211
+ onClick: Z,
28
212
  className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
29
- children: [/* @__PURE__ */ l(s, { className: "h-4 w-4" }), "Add Task"]
213
+ children: [/* @__PURE__ */ v(m, { className: "h-4 w-4" }), "Add Task"]
30
214
  }),
31
- children: m ? /* @__PURE__ */ l(n, {
32
- variant: "list",
33
- count: 5
34
- }) : g.length === 0 ? /* @__PURE__ */ l(a, {
35
- illustration: "empty-generic",
36
- title: "No tasks yet",
37
- description: "Add a task to break this project into trackable work.",
38
- action: /* @__PURE__ */ u("button", {
39
- className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
40
- children: [/* @__PURE__ */ l(s, { className: "h-4 w-4" }), "Add Task"]
41
- })
42
- }) : /* @__PURE__ */ l(d, {
43
- treatment: "glass",
44
- glow: !0,
45
- className: "overflow-hidden",
46
- children: /* @__PURE__ */ l("div", {
47
- className: "divide-y divide-border",
48
- children: g.map((n) => /* @__PURE__ */ u("div", {
49
- className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
50
- children: [/* @__PURE__ */ u("div", {
51
- className: "flex items-center gap-3",
52
- children: [/* @__PURE__ */ l(o, { className: `h-5 w-5 ${n.status === "done" ? "text-status-success-text" : "text-text-secondary"}` }), /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
53
- className: `text-sm font-medium ${n.status === "done" ? "text-text-secondary line-through" : "text-text-primary"}`,
54
- children: n.title
55
- }), /* @__PURE__ */ u("p", {
56
- className: "text-xs text-text-secondary",
215
+ children: [
216
+ k && j.length === 0 ? /* @__PURE__ */ v(t, {
217
+ variant: "list",
218
+ count: 5
219
+ }) : j.length === 0 ? /* @__PURE__ */ v(l, {
220
+ illustration: "empty-generic",
221
+ title: "No tasks yet",
222
+ description: "Add a task to break this project into trackable work.",
223
+ action: /* @__PURE__ */ y("button", {
224
+ onClick: Z,
225
+ className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
226
+ children: [/* @__PURE__ */ v(m, { className: "h-4 w-4" }), "Add Task"]
227
+ })
228
+ }) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v(b, {
229
+ treatment: "glass",
230
+ glow: !0,
231
+ className: "overflow-hidden",
232
+ children: /* @__PURE__ */ v("div", {
233
+ className: "divide-y divide-border",
234
+ children: j.map((t, n) => /* @__PURE__ */ y("div", {
235
+ className: "flex flex-col gap-3 p-4 transition-colors hover:bg-bg-sunken/50 sm:flex-row sm:items-center sm:justify-between",
236
+ children: [/* @__PURE__ */ y("div", {
237
+ className: "flex items-center gap-3",
238
+ children: [/* @__PURE__ */ v(te, { className: `h-5 w-5 shrink-0 ${t.status === "done" ? "text-status-success-text" : "text-text-secondary"}` }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
239
+ className: `text-sm font-medium ${t.status === "done" ? "text-text-secondary line-through" : "text-text-primary"}`,
240
+ children: t.title
241
+ }), /* @__PURE__ */ y("p", {
242
+ className: "text-xs text-text-secondary",
243
+ children: [
244
+ t.assigneeId ?? "Unassigned",
245
+ " ·",
246
+ " ",
247
+ t.trackedHours,
248
+ "h tracked"
249
+ ]
250
+ })] })]
251
+ }), /* @__PURE__ */ y("div", {
252
+ className: "flex flex-wrap items-center gap-2",
57
253
  children: [
58
- n.assigneeId ?? "Unassigned",
59
- " ·",
60
- " ",
61
- n.trackedHours,
62
- "h tracked"
254
+ /* @__PURE__ */ v("span", {
255
+ className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${e(t.priority)}`,
256
+ children: t.priority
257
+ }),
258
+ /* @__PURE__ */ v("select", {
259
+ value: t.status,
260
+ onChange: (e) => void ue(t, e.target.value),
261
+ className: "rounded-md border border-border-subtle bg-bg-surface px-2 py-1 text-xs font-medium text-text-primary",
262
+ "aria-label": "Task status",
263
+ children: S.map((e) => /* @__PURE__ */ v("option", {
264
+ value: e,
265
+ children: e
266
+ }, e))
267
+ }),
268
+ /* @__PURE__ */ v("button", {
269
+ onClick: () => void Q(n, -1),
270
+ disabled: n === 0 || X,
271
+ className: "rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40",
272
+ "aria-label": "Move up",
273
+ children: /* @__PURE__ */ v(ee, { className: "h-4 w-4" })
274
+ }),
275
+ /* @__PURE__ */ v("button", {
276
+ onClick: () => void Q(n, 1),
277
+ disabled: n === j.length - 1 || X,
278
+ className: "rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40",
279
+ "aria-label": "Move down",
280
+ children: /* @__PURE__ */ v(f, { className: "h-4 w-4" })
281
+ }),
282
+ /* @__PURE__ */ v("button", {
283
+ onClick: () => se(t),
284
+ className: "rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary",
285
+ "aria-label": "Edit task",
286
+ children: /* @__PURE__ */ v(p, { className: "h-4 w-4" })
287
+ }),
288
+ /* @__PURE__ */ v("button", {
289
+ onClick: () => R(t),
290
+ className: "rounded-md p-1 text-status-error-text hover:bg-status-error-bg",
291
+ "aria-label": "Delete task",
292
+ children: /* @__PURE__ */ v(h, { className: "h-4 w-4" })
293
+ })
63
294
  ]
64
- })] })]
65
- }), /* @__PURE__ */ u("div", {
66
- className: "flex items-center gap-2",
67
- children: [/* @__PURE__ */ l("span", {
68
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${e(n.priority)}`,
69
- children: n.priority
70
- }), /* @__PURE__ */ l("span", {
71
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${t(n.status)}`,
72
- children: n.status
73
295
  })]
74
- })]
75
- }, n.id))
296
+ }, t.id))
297
+ })
298
+ }), M && /* @__PURE__ */ v("div", {
299
+ className: "mt-6 flex justify-center",
300
+ children: /* @__PURE__ */ v("button", {
301
+ onClick: () => re((e) => e + x),
302
+ disabled: k,
303
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50",
304
+ children: k ? "Loading…" : "Load more"
305
+ })
306
+ })] }),
307
+ /* @__PURE__ */ y(u, {
308
+ open: N,
309
+ onClose: () => P(!1),
310
+ title: "Add Task",
311
+ description: "Break this project into a trackable piece of work.",
312
+ size: "xl",
313
+ footer: /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("button", {
314
+ onClick: () => P(!1),
315
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
316
+ children: "Cancel"
317
+ }), /* @__PURE__ */ v("button", {
318
+ onClick: ce,
319
+ disabled: H || !z.title.trim(),
320
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
321
+ children: H ? "Creating…" : "Create Task"
322
+ })] }),
323
+ children: [U && /* @__PURE__ */ v("div", {
324
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
325
+ children: "Failed to create task. Please try again."
326
+ }), $]
327
+ }),
328
+ /* @__PURE__ */ y(u, {
329
+ open: F !== null,
330
+ onClose: () => I(null),
331
+ title: "Edit Task",
332
+ size: "xl",
333
+ footer: /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("button", {
334
+ onClick: () => I(null),
335
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
336
+ children: "Cancel"
337
+ }), /* @__PURE__ */ v("button", {
338
+ onClick: le,
339
+ disabled: G || !z.title.trim(),
340
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
341
+ children: G ? "Saving…" : "Save Changes"
342
+ })] }),
343
+ children: [K && /* @__PURE__ */ v("div", {
344
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
345
+ children: "Failed to update task. Please try again."
346
+ }), $]
347
+ }),
348
+ /* @__PURE__ */ y(u, {
349
+ open: L !== null,
350
+ onClose: () => R(null),
351
+ title: "Delete Task",
352
+ size: "sm",
353
+ footer: /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("button", {
354
+ onClick: () => R(null),
355
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
356
+ children: "Cancel"
357
+ }), /* @__PURE__ */ v("button", {
358
+ onClick: de,
359
+ disabled: Y,
360
+ className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/90 disabled:opacity-50",
361
+ children: Y ? "Deleting…" : "Delete"
362
+ })] }),
363
+ children: [ae && /* @__PURE__ */ v("div", {
364
+ className: "mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text",
365
+ children: "Failed to delete task. Please try again."
366
+ }), /* @__PURE__ */ y("p", {
367
+ className: "text-sm text-text-secondary",
368
+ children: [
369
+ "Are you sure you want to delete",
370
+ " ",
371
+ /* @__PURE__ */ v("span", {
372
+ className: "font-semibold text-text-primary",
373
+ children: L?.title
374
+ }),
375
+ "? This action cannot be undone."
376
+ ]
377
+ })]
76
378
  })
77
- })
379
+ ]
78
380
  });
79
381
  }
80
382
  //#endregion
81
- export { f as TasksPage };
383
+ export { E as TasksPage };
82
384
 
83
385
  //# sourceMappingURL=TasksPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TasksPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/TasksPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { Plus, CheckSquare } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, getPriorityColor } from \"../../utils/helpers\";\nimport {\n useListTasksQuery,\n type TaskFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TasksPage() {\n const { projectId } = useParams<{ projectId: string }>();\n\n const { data, loading, error } = useListTasksQuery({\n variables: { projectId: projectId! },\n skip: !projectId,\n });\n\n const tasks = data?.tasks?.items ?? [];\n\n if (error) {\n return (\n <PageLayout title=\"Tasks\" description={`Tasks for project ${projectId}`}>\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load tasks. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Tasks\"\n description={`Tasks for project ${projectId}`}\n purpose=\"Break a project down into the concrete pieces of work that need doing, then track hours against each one — so you can see what's done, what's blocking, and where effort is actually being spent.\"\n actions={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Task\n </button>\n }\n >\n {loading ? (\n <LoadingSkeleton variant=\"list\" count={5} />\n ) : tasks.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No tasks yet\"\n description=\"Add a task to break this project into trackable work.\"\n action={\n <button className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\">\n <Plus className=\"h-4 w-4\" />\n Add Task\n </button>\n }\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {tasks.map((task: TaskFieldsFragment) => (\n <div\n key={task.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <CheckSquare\n className={`h-5 w-5 ${task.status === \"done\" ? \"text-status-success-text\" : \"text-text-secondary\"}`}\n />\n <div>\n <p\n className={`text-sm font-medium ${task.status === \"done\" ? \"text-text-secondary line-through\" : \"text-text-primary\"}`}\n >\n {task.title}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {task.assigneeId ?? \"Unassigned\"} &middot;{\" \"}\n {task.trackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(task.priority)}`}\n >\n {task.priority}\n </span>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(task.status)}`}\n >\n {task.status}\n </span>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,IAAY;CAC1B,IAAM,EAAE,iBAAc,GAAkC,EAElD,EAAE,SAAM,YAAS,aAAU,EAAkB;EACjD,WAAW,EAAa,cAAY;EACpC,MAAM,CAAC;EACR,CAAC,EAEI,IAAQ,GAAM,OAAO,SAAS,EAAE;AAYtC,QAVI,IAEA,kBAAC,GAAD;EAAY,OAAM;EAAQ,aAAa,qBAAqB;YAC1D,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAa,qBAAqB;EAClC,SAAQ;EACR,SACE,kBAAC,UAAD;GAAQ,WAAU;aAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,WAErB;;YAGV,IACC,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA,GAC1C,EAAM,WAAW,IACnB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,WAErB;;GAEX,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAC1C,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,MACV,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EACE,WAAW,WAAW,EAAK,WAAW,SAAS,6BAA6B,yBAC5E,CAAA,EACF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OACE,WAAW,uBAAuB,EAAK,WAAW,SAAS,qCAAqC;iBAE/F,EAAK;OACJ,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAK,cAAc;QAAa;QAAU;QAC1C,EAAK;QAAa;QACjB;SACA,EAAA,CAAA,CACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAiB,EAAK,SAAS;iBAErG,EAAK;OACD,CAAA,EACP,kBAAC,QAAD;OACE,WAAW,4DAA4D,EAAe,EAAK,OAAO;iBAEjG,EAAK;OACD,CAAA,CACH;QACF;OA/BC,EAAK,GA+BN,CACN;IACE,CAAA;GACI,CAAA;EAEH,CAAA"}
1
+ {"version":3,"file":"TasksPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/TasksPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Plus,\n CheckSquare,\n Pencil,\n Trash2,\n ArrowUp,\n ArrowDown,\n} from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { getPriorityColor } from \"../../utils/helpers\";\nimport {\n useListTasksQuery,\n useCreateTaskMutation,\n useUpdateTaskMutation,\n useUpdateTaskStatusMutation,\n useDeleteTaskMutation,\n useReorderTasksMutation,\n type TaskFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst PAGE_SIZE = 25;\n\nconst TASK_STATUSES = [\"todo\", \"in-progress\", \"review\", \"done\"] as const;\nconst TASK_PRIORITIES = [\"low\", \"medium\", \"high\", \"urgent\"] as const;\n\ninterface TaskFormState {\n title: string;\n description: string;\n status: string;\n priority: string;\n assigneeId: string;\n estimatedHours: string;\n dueDate: string;\n}\n\nconst emptyForm: TaskFormState = {\n title: \"\",\n description: \"\",\n status: \"todo\",\n priority: \"medium\",\n assigneeId: \"\",\n estimatedHours: \"\",\n dueDate: \"\",\n};\n\nconst inputClass =\n \"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\";\nconst labelClass = \"block text-sm font-medium text-text-primary\";\n\nexport function TasksPage() {\n const { projectId } = useParams<{ projectId: string }>();\n\n const [limit, setLimit] = useState(PAGE_SIZE);\n const { data, loading, error, refetch } = useListTasksQuery({\n variables: { projectId: projectId!, limit, offset: 0 },\n skip: !projectId,\n notifyOnNetworkStatusChange: true,\n });\n\n const tasks = data?.tasks?.items ?? [];\n const hasMore = data?.tasks?.hasMore ?? false;\n\n const [createOpen, setCreateOpen] = useState(false);\n const [editTarget, setEditTarget] = useState<TaskFieldsFragment | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<TaskFieldsFragment | null>(\n null,\n );\n const [form, setForm] = useState<TaskFormState>(emptyForm);\n\n const [createTask, { loading: creating, error: createError }] =\n useCreateTaskMutation();\n const [updateTask, { loading: updating, error: updateError }] =\n useUpdateTaskMutation();\n const [updateTaskStatus] = useUpdateTaskStatusMutation();\n const [deleteTask, { loading: deleting, error: deleteError }] =\n useDeleteTaskMutation();\n const [reorderTasks, { loading: reordering }] = useReorderTasksMutation();\n\n const openCreate = () => {\n setForm(emptyForm);\n setCreateOpen(true);\n };\n\n const openEdit = (task: TaskFieldsFragment) => {\n setForm({\n title: task.title,\n description: task.description ?? \"\",\n status: task.status,\n priority: task.priority,\n assigneeId: task.assigneeId ?? \"\",\n estimatedHours:\n task.estimatedHours != null ? String(task.estimatedHours) : \"\",\n dueDate: task.dueDate ? task.dueDate.slice(0, 10) : \"\",\n });\n setEditTarget(task);\n };\n\n const handleCreate = async () => {\n if (!projectId || !form.title.trim()) return;\n await createTask({\n variables: {\n input: {\n projectId,\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n status: form.status,\n priority: form.priority,\n assigneeId: form.assigneeId.trim() || undefined,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n dueDate: form.dueDate\n ? new Date(form.dueDate).toISOString()\n : undefined,\n },\n },\n });\n setCreateOpen(false);\n await refetch();\n };\n\n const handleUpdate = async () => {\n if (!editTarget || !form.title.trim()) return;\n await updateTask({\n variables: {\n id: editTarget.id,\n input: {\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n status: form.status,\n priority: form.priority,\n assigneeId: form.assigneeId.trim() || undefined,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n dueDate: form.dueDate\n ? new Date(form.dueDate).toISOString()\n : undefined,\n },\n },\n });\n setEditTarget(null);\n await refetch();\n };\n\n const handleStatusChange = async (\n task: TaskFieldsFragment,\n status: string,\n ) => {\n await updateTaskStatus({ variables: { id: task.id, status } });\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!deleteTarget) return;\n await deleteTask({ variables: { id: deleteTarget.id } });\n setDeleteTarget(null);\n await refetch();\n };\n\n const handleMove = async (index: number, direction: -1 | 1) => {\n if (!projectId) return;\n const target = index + direction;\n if (target < 0 || target >= tasks.length) return;\n const ordered = tasks.map((task: TaskFieldsFragment) => task.id);\n const [moved] = ordered.splice(index, 1);\n ordered.splice(target, 0, moved);\n await reorderTasks({ variables: { projectId, taskIds: ordered } });\n await refetch();\n };\n\n const formFields = (\n <div className=\"space-y-4\">\n <div>\n <label className={labelClass}>Title</label>\n <input\n type=\"text\"\n placeholder=\"Task title\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Description</label>\n <textarea\n placeholder=\"Task description\"\n rows={3}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>Status</label>\n <select\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n className={inputClass}\n >\n {TASK_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className={labelClass}>Priority</label>\n <select\n value={form.priority}\n onChange={(e) => setForm({ ...form, priority: e.target.value })}\n className={inputClass}\n >\n {TASK_PRIORITIES.map((p) => (\n <option key={p} value={p}>\n {p}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label className={labelClass}>Assignee ID</label>\n <input\n type=\"text\"\n placeholder=\"User ID\"\n value={form.assigneeId}\n onChange={(e) => setForm({ ...form, assigneeId: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Estimated Hours</label>\n <input\n type=\"number\"\n placeholder=\"0\"\n value={form.estimatedHours}\n onChange={(e) =>\n setForm({ ...form, estimatedHours: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Due Date</label>\n <input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n );\n\n if (error) {\n return (\n <PageLayout title=\"Tasks\" description={`Tasks for project ${projectId}`}>\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load tasks. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Tasks\"\n description={`Tasks for project ${projectId}`}\n purpose=\"Break a project down into the concrete pieces of work that need doing, then track hours against each one — so you can see what's done, what's blocking, and where effort is actually being spent.\"\n actions={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Task\n </button>\n }\n >\n {loading && tasks.length === 0 ? (\n <LoadingSkeleton variant=\"list\" count={5} />\n ) : tasks.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No tasks yet\"\n description=\"Add a task to break this project into trackable work.\"\n action={\n <button\n onClick={openCreate}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Task\n </button>\n }\n />\n ) : (\n <>\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {tasks.map((task: TaskFieldsFragment, index: number) => (\n <div\n key={task.id}\n className=\"flex flex-col gap-3 p-4 transition-colors hover:bg-bg-sunken/50 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-center gap-3\">\n <CheckSquare\n className={`h-5 w-5 shrink-0 ${task.status === \"done\" ? \"text-status-success-text\" : \"text-text-secondary\"}`}\n />\n <div>\n <p\n className={`text-sm font-medium ${task.status === \"done\" ? \"text-text-secondary line-through\" : \"text-text-primary\"}`}\n >\n {task.title}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {task.assigneeId ?? \"Unassigned\"} &middot;{\" \"}\n {task.trackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(task.priority)}`}\n >\n {task.priority}\n </span>\n <select\n value={task.status}\n onChange={(e) =>\n void handleStatusChange(task, e.target.value)\n }\n className=\"rounded-md border border-border-subtle bg-bg-surface px-2 py-1 text-xs font-medium text-text-primary\"\n aria-label=\"Task status\"\n >\n {TASK_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n <button\n onClick={() => void handleMove(index, -1)}\n disabled={index === 0 || reordering}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40\"\n aria-label=\"Move up\"\n >\n <ArrowUp className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => void handleMove(index, 1)}\n disabled={index === tasks.length - 1 || reordering}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40\"\n aria-label=\"Move down\"\n >\n <ArrowDown className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => openEdit(task)}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Edit task\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeleteTarget(task)}\n className=\"rounded-md p-1 text-status-error-text hover:bg-status-error-bg\"\n aria-label=\"Delete task\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n {hasMore && (\n <div className=\"mt-6 flex justify-center\">\n <button\n onClick={() => setLimit((prev) => prev + PAGE_SIZE)}\n disabled={loading}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n {loading ? \"Loading…\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n {/* Create modal */}\n <Modal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n title=\"Add Task\"\n description=\"Break this project into a trackable piece of work.\"\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setCreateOpen(false)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleCreate}\n disabled={creating || !form.title.trim()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {creating ? \"Creating…\" : \"Create Task\"}\n </button>\n </>\n }\n >\n {createError && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to create task. Please try again.\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Edit modal */}\n <Modal\n open={editTarget !== null}\n onClose={() => setEditTarget(null)}\n title=\"Edit Task\"\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setEditTarget(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleUpdate}\n disabled={updating || !form.title.trim()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {updating ? \"Saving…\" : \"Save Changes\"}\n </button>\n </>\n }\n >\n {updateError && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to update task. Please try again.\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={deleteTarget !== null}\n onClose={() => setDeleteTarget(null)}\n title=\"Delete Task\"\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteTarget(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/90 disabled:opacity-50\"\n >\n {deleting ? \"Deleting…\" : \"Delete\"}\n </button>\n </>\n }\n >\n {deleteError && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to delete task. Please try again.\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete{\" \"}\n <span className=\"font-semibold text-text-primary\">\n {deleteTarget?.title}\n </span>\n ? This action cannot be undone.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}