@burdenoff/microfe-timecampus 2026.703.2 → 2026.703.4

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,58 +1,68 @@
1
1
  import { formatDate as e, formatHours as t, getInitials as n } from "../../utils/helpers.js";
2
2
  import { StatCard as r } from "../../components/StatCard.js";
3
3
  import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
4
- import { useApproveRequestMutation as a, useGetApprovalStatsQuery as o, useListApprovalRequestsQuery as ee, useRejectRequestMutation as te, useRequestRevisionMutation as ne, useWithdrawRequestMutation as s } from "../../../generated/wspace-operations.js";
5
- import { PageLayout as c } from "../../components/PageLayout.js";
6
- import { EmptyState as l } from "../../components/EmptyState.js";
7
- import { ApprovalStatus as u } from "../../../generated/wspace-types.js";
8
- import { Modal as d } from "../../components/Modal.js";
9
- import { StatusBadge as re } from "../../components/StatusBadge.js";
10
- import { ToastContainer as ie, useToast as ae } from "../../components/Toast.js";
11
- import { useState as f } from "react";
12
- import { CheckCircle as p, Clock as oe, MessageSquare as se, Undo2 as ce, XCircle as m } from "lucide-react";
4
+ import { useApproveRequestMutation as a, useGetApprovalStatsQuery as o, useListApprovalRequestsQuery as ee, useRejectRequestMutation as te, useRequestRevisionMutation as ne, useWithdrawRequestMutation as re } from "../../../generated/wspace-operations.js";
5
+ import { PageLayout as s } from "../../components/PageLayout.js";
6
+ import { EmptyState as c } from "../../components/EmptyState.js";
7
+ import { ApprovalStatus as l } from "../../../generated/wspace-types.js";
8
+ import { Modal as u } from "../../components/Modal.js";
9
+ import { StatusBadge as ie } from "../../components/StatusBadge.js";
10
+ import { ToastContainer as ae, useToast as oe } from "../../components/Toast.js";
11
+ import { useState as d } from "react";
12
+ import { CheckCircle as f, Clock as p, MessageSquare as se, MessagesSquare as ce, Undo2 as le, XCircle as m } from "lucide-react";
13
13
  import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
14
+ import { ContextConversationWidget as v } from "@burdenoff/fe-libs/shared";
15
+ import { useRegisterPageConversationContext as y } from "@burdenoff/fe-libs/shared/providers/shell";
14
16
  //#region src/timecampus/pages/time-approval/TimeApprovalPage.tsx
15
- var v = 25, y = "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";
16
- function b() {
17
- let { toasts: b, addToast: x, dismissToast: S } = ae(), { data: C, loading: w, error: T, refetch: le, fetchMore: ue } = ee({
17
+ var b = 25;
18
+ function x({ request: e }) {
19
+ return y({
20
+ entityType: "TIMECAMPUS_APPROVAL_REQUEST",
21
+ entityId: e.id,
22
+ contextTitle: `Approval · ${e.period ?? e.id}`
23
+ }), /* @__PURE__ */ g(v, { widgetKey: "approval-review" });
24
+ }
25
+ var S = "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";
26
+ function C() {
27
+ let { toasts: v, addToast: y, dismissToast: C } = oe(), { data: w, loading: T, error: ue, refetch: de, fetchMore: fe } = ee({
18
28
  variables: {
19
- limit: v,
29
+ limit: b,
20
30
  offset: 0
21
31
  },
22
32
  notifyOnNetworkStatusChange: !0
23
- }), { data: de, loading: E, error: D, refetch: O } = o(), [k] = a(), [A, { loading: j }] = te(), [M, { loading: N }] = ne(), [P] = s(), F = C?.approvalRequests?.items ?? [], I = C?.approvalRequests?.total ?? 0, L = C?.approvalRequests?.hasMore ?? !1, R = de?.approvalStats, [z, B] = f(!1), [V, H] = f(null), [U, W] = f(null), [G, K] = f(""), [q, J] = f(null), [Y, X] = f(""), [Z, Q] = f(null), $ = async () => {
24
- await Promise.all([le(), O()]);
25
- }, fe = async (e) => {
26
- H(e);
33
+ }), { data: pe, loading: me, error: E, refetch: D } = o(), [O] = a(), [k, { loading: A }] = te(), [j, { loading: M }] = ne(), [N] = re(), P = w?.approvalRequests?.items ?? [], F = w?.approvalRequests?.total ?? 0, I = w?.approvalRequests?.hasMore ?? !1, L = pe?.approvalStats, [R, z] = d(!1), [he, B] = d(null), [V, H] = d(null), [U, W] = d(""), [G, K] = d(null), [q, J] = d(""), [Y, X] = d(null), [Z, Q] = d(null), $ = async () => {
34
+ await Promise.all([de(), D()]);
35
+ }, ge = async (e) => {
36
+ B(e);
27
37
  try {
28
- await k({ variables: { id: e } }), x({
38
+ await O({ variables: { id: e } }), y({
29
39
  title: "Timesheet approved",
30
40
  variant: "success"
31
41
  }), await $();
32
42
  } catch {
33
- x({
43
+ y({
34
44
  title: "Failed to approve timesheet",
35
45
  variant: "error"
36
46
  });
37
47
  } finally {
38
- H(null);
48
+ B(null);
39
49
  }
40
50
  };
41
- return w && E && F.length === 0 ? /* @__PURE__ */ g(c, {
51
+ return T && me && P.length === 0 ? /* @__PURE__ */ g(s, {
42
52
  title: "Time Approval",
43
53
  description: "Review and approve team timesheets",
44
54
  children: /* @__PURE__ */ g(i, {
45
55
  variant: "list",
46
56
  count: 4
47
57
  })
48
- }) : T && D ? /* @__PURE__ */ g(c, {
58
+ }) : ue && E ? /* @__PURE__ */ g(s, {
49
59
  title: "Time Approval",
50
60
  description: "Review and approve team timesheets",
51
61
  children: /* @__PURE__ */ g("div", {
52
62
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
53
63
  children: "Failed to load approval data. Please try again later."
54
64
  })
55
- }) : /* @__PURE__ */ _(c, {
65
+ }) : /* @__PURE__ */ _(s, {
56
66
  title: "Time Approval",
57
67
  description: "Review and approve team timesheets",
58
68
  purpose: "The reviewer's queue for signing off team timesheets before they're billed or paid — approve, reject or request changes in one place so payroll and invoices go out accurate and on time.",
@@ -63,30 +73,30 @@ function b() {
63
73
  /* @__PURE__ */ g(r, {
64
74
  featured: !0,
65
75
  title: "Pending Approvals",
66
- value: String(R?.pendingCount ?? 0),
67
- icon: /* @__PURE__ */ g(oe, { className: "h-5 w-5" }),
76
+ value: String(L?.pendingCount ?? 0),
77
+ icon: /* @__PURE__ */ g(p, { className: "h-5 w-5" }),
68
78
  iconBgColor: "bg-status-warning-bg text-status-warning-text"
69
79
  }),
70
80
  /* @__PURE__ */ g(r, {
71
81
  title: "Approved This Week",
72
- value: String(R?.approvedCount ?? 0),
73
- icon: /* @__PURE__ */ g(p, { className: "h-5 w-5" }),
82
+ value: String(L?.approvedCount ?? 0),
83
+ icon: /* @__PURE__ */ g(f, { className: "h-5 w-5" }),
74
84
  iconBgColor: "bg-status-success-bg text-status-success-text"
75
85
  }),
76
86
  /* @__PURE__ */ g(r, {
77
87
  title: "Rejected",
78
- value: String(R?.rejectedCount ?? 0),
88
+ value: String(L?.rejectedCount ?? 0),
79
89
  icon: /* @__PURE__ */ g(m, { className: "h-5 w-5" }),
80
90
  iconBgColor: "bg-status-error-bg text-status-error-text"
81
91
  }),
82
92
  /* @__PURE__ */ g(r, {
83
93
  title: "Avg. Review Time",
84
- value: R?.averageReviewTime ? `${Math.round(R.averageReviewTime)}h` : "N/A",
94
+ value: L?.averageReviewTime ? `${Math.round(L.averageReviewTime)}h` : "N/A",
85
95
  description: "Target: 24h"
86
96
  })
87
97
  ]
88
98
  }),
89
- F.length === 0 && !w ? /* @__PURE__ */ g(l, {
99
+ P.length === 0 && !T ? /* @__PURE__ */ g(c, {
90
100
  illustration: "empty-notifications",
91
101
  title: "No approval requests found",
92
102
  description: "You're all caught up — submitted timesheets waiting on your review will appear here."
@@ -94,8 +104,8 @@ function b() {
94
104
  className: "rounded-xl border border-border-seam bg-gloss-card shadow-pop",
95
105
  children: /* @__PURE__ */ g("div", {
96
106
  className: "divide-y divide-border",
97
- children: F.map((r) => {
98
- let i = r.status === u.Pending, a = V === r.id;
107
+ children: P.map((r) => {
108
+ let i = r.status === l.Pending, a = he === r.id;
99
109
  return /* @__PURE__ */ _("div", {
100
110
  className: "flex flex-col gap-3 p-4 hover:bg-bg-sunken/50 transition-colors sm:flex-row sm:items-center sm:justify-between",
101
111
  children: [/* @__PURE__ */ _("div", {
@@ -128,23 +138,29 @@ function b() {
128
138
  children: [t(r.billableHours), " billable"]
129
139
  })]
130
140
  }),
131
- /* @__PURE__ */ g(re, {
141
+ /* @__PURE__ */ g(ie, {
132
142
  label: r.status.toLowerCase(),
133
143
  status: r.status.toLowerCase()
134
144
  }),
145
+ /* @__PURE__ */ g("button", {
146
+ onClick: () => Q(r),
147
+ title: "Review / Discussion",
148
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken",
149
+ children: /* @__PURE__ */ g(ce, { className: "h-4 w-4" })
150
+ }),
135
151
  i && /* @__PURE__ */ _("div", {
136
152
  className: "flex items-center gap-1",
137
153
  children: [
138
154
  /* @__PURE__ */ g("button", {
139
- onClick: () => fe(r.id),
155
+ onClick: () => ge(r.id),
140
156
  disabled: a,
141
157
  title: "Approve",
142
158
  className: "rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg disabled:opacity-50",
143
- children: /* @__PURE__ */ g(p, { className: "h-4 w-4" })
159
+ children: /* @__PURE__ */ g(f, { className: "h-4 w-4" })
144
160
  }),
145
161
  /* @__PURE__ */ g("button", {
146
162
  onClick: () => {
147
- J(r), X("");
163
+ K(r), J("");
148
164
  },
149
165
  disabled: a,
150
166
  title: "Request revision",
@@ -153,7 +169,7 @@ function b() {
153
169
  }),
154
170
  /* @__PURE__ */ g("button", {
155
171
  onClick: () => {
156
- W(r), K("");
172
+ H(r), W("");
157
173
  },
158
174
  disabled: a,
159
175
  title: "Reject",
@@ -161,11 +177,11 @@ function b() {
161
177
  children: /* @__PURE__ */ g(m, { className: "h-4 w-4" })
162
178
  }),
163
179
  /* @__PURE__ */ g("button", {
164
- onClick: () => Q(r),
180
+ onClick: () => X(r),
165
181
  disabled: a,
166
182
  title: "Withdraw request",
167
183
  className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken disabled:opacity-50",
168
- children: /* @__PURE__ */ g(ce, { className: "h-4 w-4" })
184
+ children: /* @__PURE__ */ g(le, { className: "h-4 w-4" })
169
185
  })
170
186
  ]
171
187
  })
@@ -180,18 +196,18 @@ function b() {
180
196
  className: "text-xs text-text-secondary",
181
197
  children: [
182
198
  "Showing ",
183
- F.length,
199
+ P.length,
184
200
  " of ",
185
- I
201
+ F
186
202
  ]
187
- }), L && /* @__PURE__ */ g("button", {
203
+ }), I && /* @__PURE__ */ g("button", {
188
204
  onClick: async () => {
189
- B(!0);
205
+ z(!0);
190
206
  try {
191
- await ue({
207
+ await fe({
192
208
  variables: {
193
- limit: v,
194
- offset: F.length
209
+ limit: b,
210
+ offset: P.length
195
211
  },
196
212
  updateQuery: (e, { fetchMoreResult: t }) => t ? {
197
213
  ...e,
@@ -202,44 +218,44 @@ function b() {
202
218
  } : e
203
219
  });
204
220
  } finally {
205
- B(!1);
221
+ z(!1);
206
222
  }
207
223
  },
208
- disabled: z,
224
+ disabled: R,
209
225
  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",
210
- children: z ? "Loading..." : "Load more"
226
+ children: R ? "Loading..." : "Load more"
211
227
  })]
212
228
  })] }),
213
- /* @__PURE__ */ _(d, {
214
- open: U !== null,
215
- onClose: () => W(null),
229
+ /* @__PURE__ */ _(u, {
230
+ open: V !== null,
231
+ onClose: () => H(null),
216
232
  title: "Reject timesheet",
217
- description: U ? `Rejecting ${U.userId}'s ${U.period} timesheet.` : void 0,
233
+ description: V ? `Rejecting ${V.userId}'s ${V.period} timesheet.` : void 0,
218
234
  size: "sm",
219
235
  footer: /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
220
- onClick: () => W(null),
236
+ onClick: () => H(null),
221
237
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
222
238
  children: "Cancel"
223
239
  }), /* @__PURE__ */ g("button", {
224
240
  onClick: async () => {
225
- if (!(!U || !G.trim())) try {
226
- await A({ variables: {
227
- id: U.id,
228
- reason: G.trim()
229
- } }), x({
241
+ if (!(!V || !U.trim())) try {
242
+ await k({ variables: {
243
+ id: V.id,
244
+ reason: U.trim()
245
+ } }), y({
230
246
  title: "Timesheet rejected",
231
247
  variant: "success"
232
- }), W(null), K(""), await $();
248
+ }), H(null), W(""), await $();
233
249
  } catch {
234
- x({
250
+ y({
235
251
  title: "Failed to reject timesheet",
236
252
  variant: "error"
237
253
  });
238
254
  }
239
255
  },
240
- disabled: j || !G.trim(),
256
+ disabled: A || !U.trim(),
241
257
  className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50",
242
- children: j ? "Rejecting..." : "Reject"
258
+ children: A ? "Rejecting..." : "Reject"
243
259
  })] }),
244
260
  children: [/* @__PURE__ */ g("label", {
245
261
  className: "block text-sm font-medium text-text-primary",
@@ -247,41 +263,41 @@ function b() {
247
263
  }), /* @__PURE__ */ g("textarea", {
248
264
  rows: 3,
249
265
  placeholder: "Explain why this timesheet is being rejected",
250
- value: G,
251
- onChange: (e) => K(e.target.value),
252
- className: y
266
+ value: U,
267
+ onChange: (e) => W(e.target.value),
268
+ className: S
253
269
  })]
254
270
  }),
255
- /* @__PURE__ */ _(d, {
256
- open: q !== null,
257
- onClose: () => J(null),
271
+ /* @__PURE__ */ _(u, {
272
+ open: G !== null,
273
+ onClose: () => K(null),
258
274
  title: "Request revision",
259
- description: q ? `Ask ${q.userId} to revise their ${q.period} timesheet.` : void 0,
275
+ description: G ? `Ask ${G.userId} to revise their ${G.period} timesheet.` : void 0,
260
276
  size: "sm",
261
277
  footer: /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
262
- onClick: () => J(null),
278
+ onClick: () => K(null),
263
279
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
264
280
  children: "Cancel"
265
281
  }), /* @__PURE__ */ g("button", {
266
282
  onClick: async () => {
267
- if (!(!q || !Y.trim())) try {
268
- await M({ variables: {
269
- id: q.id,
270
- notes: Y.trim()
271
- } }), x({
283
+ if (!(!G || !q.trim())) try {
284
+ await j({ variables: {
285
+ id: G.id,
286
+ notes: q.trim()
287
+ } }), y({
272
288
  title: "Revision requested",
273
289
  variant: "success"
274
- }), J(null), X(""), await $();
290
+ }), K(null), J(""), await $();
275
291
  } catch {
276
- x({
292
+ y({
277
293
  title: "Failed to request revision",
278
294
  variant: "error"
279
295
  });
280
296
  }
281
297
  },
282
- disabled: N || !Y.trim(),
298
+ disabled: M || !q.trim(),
283
299
  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",
284
- children: N ? "Sending..." : "Request Revision"
300
+ children: M ? "Sending..." : "Request Revision"
285
301
  })] }),
286
302
  children: [/* @__PURE__ */ g("label", {
287
303
  className: "block text-sm font-medium text-text-primary",
@@ -289,30 +305,30 @@ function b() {
289
305
  }), /* @__PURE__ */ g("textarea", {
290
306
  rows: 3,
291
307
  placeholder: "Describe the changes needed before this can be approved",
292
- value: Y,
293
- onChange: (e) => X(e.target.value),
294
- className: y
308
+ value: q,
309
+ onChange: (e) => J(e.target.value),
310
+ className: S
295
311
  })]
296
312
  }),
297
- /* @__PURE__ */ g(d, {
298
- open: Z !== null,
299
- onClose: () => Q(null),
313
+ /* @__PURE__ */ g(u, {
314
+ open: Y !== null,
315
+ onClose: () => X(null),
300
316
  title: "Withdraw request?",
301
- description: Z ? `${Z.userId}'s ${Z.period} timesheet will be pulled from the review queue.` : void 0,
317
+ description: Y ? `${Y.userId}'s ${Y.period} timesheet will be pulled from the review queue.` : void 0,
302
318
  size: "sm",
303
319
  footer: /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("button", {
304
- onClick: () => Q(null),
320
+ onClick: () => X(null),
305
321
  className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
306
322
  children: "Cancel"
307
323
  }), /* @__PURE__ */ g("button", {
308
324
  onClick: async () => {
309
- if (Z) try {
310
- await P({ variables: { id: Z.id } }), x({
325
+ if (Y) try {
326
+ await N({ variables: { id: Y.id } }), y({
311
327
  title: "Request withdrawn",
312
328
  variant: "success"
313
- }), Q(null), await $();
329
+ }), X(null), await $();
314
330
  } catch {
315
- x({
331
+ y({
316
332
  title: "Failed to withdraw request",
317
333
  variant: "error"
318
334
  });
@@ -326,14 +342,22 @@ function b() {
326
342
  children: "Withdrawing removes the request from the approval queue. It can be resubmitted later."
327
343
  })
328
344
  }),
329
- /* @__PURE__ */ g(ie, {
330
- toasts: b,
331
- onDismiss: S
345
+ /* @__PURE__ */ g(u, {
346
+ open: Z !== null,
347
+ onClose: () => Q(null),
348
+ title: "Review / Discussion",
349
+ description: Z ? `Discuss ${Z.userId}'s ${Z.period} timesheet with the team.` : void 0,
350
+ size: "lg",
351
+ children: Z && /* @__PURE__ */ g(x, { request: Z })
352
+ }),
353
+ /* @__PURE__ */ g(ae, {
354
+ toasts: v,
355
+ onDismiss: C
332
356
  })
333
357
  ]
334
358
  });
335
359
  }
336
360
  //#endregion
337
- export { b as TimeApprovalPage };
361
+ export { C as TimeApprovalPage };
338
362
 
339
363
  //# sourceMappingURL=TimeApprovalPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TimeApprovalPage.js","names":[],"sources":["../../../../src/timecampus/pages/time-approval/TimeApprovalPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n CheckCircle,\n XCircle,\n Clock,\n MessageSquare,\n Undo2,\n} from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { Modal } from \"../../components/Modal\";\nimport { ToastContainer, useToast } from \"../../components/Toast\";\nimport { getInitials, formatHours, formatDate } from \"../../utils/helpers\";\nimport {\n useListApprovalRequestsQuery,\n useGetApprovalStatsQuery,\n useApproveRequestMutation,\n useRejectRequestMutation,\n useRequestRevisionMutation,\n useWithdrawRequestMutation,\n type ApprovalRequestFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { ApprovalStatus } from \"@/generated/wspace-types\";\n\nconst PAGE_SIZE = 25;\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\";\n\nexport function TimeApprovalPage() {\n const { toasts, addToast, dismissToast } = useToast();\n\n const {\n data: requestsData,\n loading: requestsLoading,\n error: requestsError,\n refetch,\n fetchMore,\n } = useListApprovalRequestsQuery({\n variables: { limit: PAGE_SIZE, offset: 0 },\n notifyOnNetworkStatusChange: true,\n });\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n refetch: refetchStats,\n } = useGetApprovalStatsQuery();\n\n const [approveRequest] = useApproveRequestMutation();\n const [rejectRequest, { loading: rejecting }] = useRejectRequestMutation();\n const [requestRevision, { loading: requestingRevision }] =\n useRequestRevisionMutation();\n const [withdrawRequest] = useWithdrawRequestMutation();\n\n const requests = requestsData?.approvalRequests?.items ?? [];\n const total = requestsData?.approvalRequests?.total ?? 0;\n const hasMore = requestsData?.approvalRequests?.hasMore ?? false;\n const stats = statsData?.approvalStats;\n\n const [loadingMore, setLoadingMore] = useState(false);\n const [busyId, setBusyId] = useState<string | null>(null);\n\n // Reject reason modal\n const [rejectTarget, setRejectTarget] =\n useState<ApprovalRequestFieldsFragment | null>(null);\n const [rejectReason, setRejectReason] = useState(\"\");\n\n // Request revision modal\n const [revisionTarget, setRevisionTarget] =\n useState<ApprovalRequestFieldsFragment | null>(null);\n const [revisionNotes, setRevisionNotes] = useState(\"\");\n\n // Withdraw confirm modal\n const [withdrawTarget, setWithdrawTarget] =\n useState<ApprovalRequestFieldsFragment | null>(null);\n\n const refreshAll = async () => {\n await Promise.all([refetch(), refetchStats()]);\n };\n\n const handleApprove = async (id: string) => {\n setBusyId(id);\n try {\n await approveRequest({ variables: { id } });\n addToast({ title: \"Timesheet approved\", variant: \"success\" });\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to approve timesheet\", variant: \"error\" });\n } finally {\n setBusyId(null);\n }\n };\n\n const handleReject = async () => {\n if (!rejectTarget || !rejectReason.trim()) return;\n try {\n await rejectRequest({\n variables: { id: rejectTarget.id, reason: rejectReason.trim() },\n });\n addToast({ title: \"Timesheet rejected\", variant: \"success\" });\n setRejectTarget(null);\n setRejectReason(\"\");\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to reject timesheet\", variant: \"error\" });\n }\n };\n\n const handleRequestRevision = async () => {\n if (!revisionTarget || !revisionNotes.trim()) return;\n try {\n await requestRevision({\n variables: { id: revisionTarget.id, notes: revisionNotes.trim() },\n });\n addToast({ title: \"Revision requested\", variant: \"success\" });\n setRevisionTarget(null);\n setRevisionNotes(\"\");\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to request revision\", variant: \"error\" });\n }\n };\n\n const handleWithdraw = async () => {\n if (!withdrawTarget) return;\n try {\n await withdrawRequest({ variables: { id: withdrawTarget.id } });\n addToast({ title: \"Request withdrawn\", variant: \"success\" });\n setWithdrawTarget(null);\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to withdraw request\", variant: \"error\" });\n }\n };\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { limit: PAGE_SIZE, offset: requests.length },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n ...prev,\n approvalRequests: {\n ...fetchMoreResult.approvalRequests,\n items: [\n ...prev.approvalRequests.items,\n ...fetchMoreResult.approvalRequests.items,\n ],\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n if (requestsLoading && statsLoading && requests.length === 0) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (requestsError && statsError) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load approval data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n purpose=\"The reviewer's queue for signing off team timesheets before they're billed or paid — approve, reject or request changes in one place so payroll and invoices go out accurate and on time.\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Pending Approvals\"\n value={String(stats?.pendingCount ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-warning-bg text-status-warning-text\"\n />\n <StatCard\n title=\"Approved This Week\"\n value={String(stats?.approvedCount ?? 0)}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n title=\"Rejected\"\n value={String(stats?.rejectedCount ?? 0)}\n icon={<XCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Avg. Review Time\"\n value={\n stats?.averageReviewTime\n ? `${Math.round(stats.averageReviewTime)}h`\n : \"N/A\"\n }\n description=\"Target: 24h\"\n />\n </div>\n\n {requests.length === 0 && !requestsLoading ? (\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No approval requests found\"\n description=\"You're all caught up — submitted timesheets waiting on your review will appear here.\"\n />\n ) : (\n <>\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {requests.map((request: ApprovalRequestFieldsFragment) => {\n const isPending = request.status === ApprovalStatus.Pending;\n const rowBusy = busyId === request.id;\n return (\n <div\n key={request.id}\n className=\"flex flex-col gap-3 p-4 hover:bg-bg-sunken/50 transition-colors sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(request.userId)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {request.userId}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {request.period} &middot; Submitted{\" \"}\n {formatDate(request.submittedAt)}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatHours(request.totalHours)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatHours(request.billableHours)} billable\n </p>\n </div>\n <StatusBadge\n label={request.status.toLowerCase()}\n status={request.status.toLowerCase()}\n />\n {isPending && (\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => handleApprove(request.id)}\n disabled={rowBusy}\n title=\"Approve\"\n className=\"rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg disabled:opacity-50\"\n >\n <CheckCircle className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => {\n setRevisionTarget(request);\n setRevisionNotes(\"\");\n }}\n disabled={rowBusy}\n title=\"Request revision\"\n className=\"rounded-md p-1.5 text-status-warning-text hover:bg-status-warning-bg disabled:opacity-50\"\n >\n <MessageSquare className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => {\n setRejectTarget(request);\n setRejectReason(\"\");\n }}\n disabled={rowBusy}\n title=\"Reject\"\n className=\"rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg disabled:opacity-50\"\n >\n <XCircle className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setWithdrawTarget(request)}\n disabled={rowBusy}\n title=\"Withdraw request\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken disabled:opacity-50\"\n >\n <Undo2 className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Showing {requests.length} of {total}\n </p>\n {hasMore && (\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\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 {loadingMore ? \"Loading...\" : \"Load more\"}\n </button>\n )}\n </div>\n </>\n )}\n\n <Modal\n open={rejectTarget !== null}\n onClose={() => setRejectTarget(null)}\n title=\"Reject timesheet\"\n description={\n rejectTarget\n ? `Rejecting ${rejectTarget.userId}'s ${rejectTarget.period} timesheet.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setRejectTarget(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={handleReject}\n disabled={rejecting || !rejectReason.trim()}\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/80 disabled:opacity-50\"\n >\n {rejecting ? \"Rejecting...\" : \"Reject\"}\n </button>\n </>\n }\n >\n <label className=\"block text-sm font-medium text-text-primary\">\n Rejection reason\n </label>\n <textarea\n rows={3}\n placeholder=\"Explain why this timesheet is being rejected\"\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n className={inputClass}\n />\n </Modal>\n\n <Modal\n open={revisionTarget !== null}\n onClose={() => setRevisionTarget(null)}\n title=\"Request revision\"\n description={\n revisionTarget\n ? `Ask ${revisionTarget.userId} to revise their ${revisionTarget.period} timesheet.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setRevisionTarget(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={handleRequestRevision}\n disabled={requestingRevision || !revisionNotes.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 {requestingRevision ? \"Sending...\" : \"Request Revision\"}\n </button>\n </>\n }\n >\n <label className=\"block text-sm font-medium text-text-primary\">\n What needs changing?\n </label>\n <textarea\n rows={3}\n placeholder=\"Describe the changes needed before this can be approved\"\n value={revisionNotes}\n onChange={(e) => setRevisionNotes(e.target.value)}\n className={inputClass}\n />\n </Modal>\n\n <Modal\n open={withdrawTarget !== null}\n onClose={() => setWithdrawTarget(null)}\n title=\"Withdraw request?\"\n description={\n withdrawTarget\n ? `${withdrawTarget.userId}'s ${withdrawTarget.period} timesheet will be pulled from the review queue.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setWithdrawTarget(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={handleWithdraw}\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\"\n >\n Withdraw\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Withdrawing removes the request from the approval queue. It can be\n resubmitted later.\n </p>\n </Modal>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TimeApprovalPage.js","names":[],"sources":["../../../../src/timecampus/pages/time-approval/TimeApprovalPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n CheckCircle,\n XCircle,\n Clock,\n MessageSquare,\n MessagesSquare,\n Undo2,\n} from \"lucide-react\";\nimport { ContextConversationWidget } from \"@burdenoff/fe-libs/shared\";\nimport { useRegisterPageConversationContext } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { StatusBadge } from \"../../components/StatusBadge\";\nimport { Modal } from \"../../components/Modal\";\nimport { ToastContainer, useToast } from \"../../components/Toast\";\nimport { getInitials, formatHours, formatDate } from \"../../utils/helpers\";\nimport {\n useListApprovalRequestsQuery,\n useGetApprovalStatsQuery,\n useApproveRequestMutation,\n useRejectRequestMutation,\n useRequestRevisionMutation,\n useWithdrawRequestMutation,\n type ApprovalRequestFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { ApprovalStatus } from \"@/generated/wspace-types\";\n\nconst PAGE_SIZE = 25;\n\n// Rendered as a child of the review Modal so the registration hook only runs\n// while the modal is mounted — it scopes the shared page-conversation widget to\n// the selected approval request, then releases the context on close/unmount.\nfunction ApprovalReviewThread({\n request,\n}: {\n request: ApprovalRequestFieldsFragment;\n}) {\n useRegisterPageConversationContext({\n entityType: \"TIMECAMPUS_APPROVAL_REQUEST\",\n entityId: request.id,\n contextTitle: `Approval · ${request.period ?? request.id}`,\n });\n\n return <ContextConversationWidget widgetKey=\"approval-review\" />;\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\";\n\nexport function TimeApprovalPage() {\n const { toasts, addToast, dismissToast } = useToast();\n\n const {\n data: requestsData,\n loading: requestsLoading,\n error: requestsError,\n refetch,\n fetchMore,\n } = useListApprovalRequestsQuery({\n variables: { limit: PAGE_SIZE, offset: 0 },\n notifyOnNetworkStatusChange: true,\n });\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n refetch: refetchStats,\n } = useGetApprovalStatsQuery();\n\n const [approveRequest] = useApproveRequestMutation();\n const [rejectRequest, { loading: rejecting }] = useRejectRequestMutation();\n const [requestRevision, { loading: requestingRevision }] =\n useRequestRevisionMutation();\n const [withdrawRequest] = useWithdrawRequestMutation();\n\n const requests = requestsData?.approvalRequests?.items ?? [];\n const total = requestsData?.approvalRequests?.total ?? 0;\n const hasMore = requestsData?.approvalRequests?.hasMore ?? false;\n const stats = statsData?.approvalStats;\n\n const [loadingMore, setLoadingMore] = useState(false);\n const [busyId, setBusyId] = useState<string | null>(null);\n\n // Reject reason modal\n const [rejectTarget, setRejectTarget] =\n useState<ApprovalRequestFieldsFragment | null>(null);\n const [rejectReason, setRejectReason] = useState(\"\");\n\n // Request revision modal\n const [revisionTarget, setRevisionTarget] =\n useState<ApprovalRequestFieldsFragment | null>(null);\n const [revisionNotes, setRevisionNotes] = useState(\"\");\n\n // Withdraw confirm modal\n const [withdrawTarget, setWithdrawTarget] =\n useState<ApprovalRequestFieldsFragment | null>(null);\n\n // Review / discussion thread modal\n const [reviewTarget, setReviewTarget] =\n useState<ApprovalRequestFieldsFragment | null>(null);\n\n const refreshAll = async () => {\n await Promise.all([refetch(), refetchStats()]);\n };\n\n const handleApprove = async (id: string) => {\n setBusyId(id);\n try {\n await approveRequest({ variables: { id } });\n addToast({ title: \"Timesheet approved\", variant: \"success\" });\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to approve timesheet\", variant: \"error\" });\n } finally {\n setBusyId(null);\n }\n };\n\n const handleReject = async () => {\n if (!rejectTarget || !rejectReason.trim()) return;\n try {\n await rejectRequest({\n variables: { id: rejectTarget.id, reason: rejectReason.trim() },\n });\n addToast({ title: \"Timesheet rejected\", variant: \"success\" });\n setRejectTarget(null);\n setRejectReason(\"\");\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to reject timesheet\", variant: \"error\" });\n }\n };\n\n const handleRequestRevision = async () => {\n if (!revisionTarget || !revisionNotes.trim()) return;\n try {\n await requestRevision({\n variables: { id: revisionTarget.id, notes: revisionNotes.trim() },\n });\n addToast({ title: \"Revision requested\", variant: \"success\" });\n setRevisionTarget(null);\n setRevisionNotes(\"\");\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to request revision\", variant: \"error\" });\n }\n };\n\n const handleWithdraw = async () => {\n if (!withdrawTarget) return;\n try {\n await withdrawRequest({ variables: { id: withdrawTarget.id } });\n addToast({ title: \"Request withdrawn\", variant: \"success\" });\n setWithdrawTarget(null);\n await refreshAll();\n } catch {\n addToast({ title: \"Failed to withdraw request\", variant: \"error\" });\n }\n };\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { limit: PAGE_SIZE, offset: requests.length },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n ...prev,\n approvalRequests: {\n ...fetchMoreResult.approvalRequests,\n items: [\n ...prev.approvalRequests.items,\n ...fetchMoreResult.approvalRequests.items,\n ],\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n if (requestsLoading && statsLoading && requests.length === 0) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n >\n <LoadingSkeleton variant=\"list\" count={4} />\n </PageLayout>\n );\n }\n\n if (requestsError && statsError) {\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load approval data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Time Approval\"\n description=\"Review and approve team timesheets\"\n purpose=\"The reviewer's queue for signing off team timesheets before they're billed or paid — approve, reject or request changes in one place so payroll and invoices go out accurate and on time.\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Pending Approvals\"\n value={String(stats?.pendingCount ?? 0)}\n icon={<Clock className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-warning-bg text-status-warning-text\"\n />\n <StatCard\n title=\"Approved This Week\"\n value={String(stats?.approvedCount ?? 0)}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-success-bg text-status-success-text\"\n />\n <StatCard\n title=\"Rejected\"\n value={String(stats?.rejectedCount ?? 0)}\n icon={<XCircle className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Avg. Review Time\"\n value={\n stats?.averageReviewTime\n ? `${Math.round(stats.averageReviewTime)}h`\n : \"N/A\"\n }\n description=\"Target: 24h\"\n />\n </div>\n\n {requests.length === 0 && !requestsLoading ? (\n <EmptyState\n illustration=\"empty-notifications\"\n title=\"No approval requests found\"\n description=\"You're all caught up — submitted timesheets waiting on your review will appear here.\"\n />\n ) : (\n <>\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {requests.map((request: ApprovalRequestFieldsFragment) => {\n const isPending = request.status === ApprovalStatus.Pending;\n const rowBusy = busyId === request.id;\n return (\n <div\n key={request.id}\n className=\"flex flex-col gap-3 p-4 hover:bg-bg-sunken/50 transition-colors sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(request.userId)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {request.userId}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {request.period} &middot; Submitted{\" \"}\n {formatDate(request.submittedAt)}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatHours(request.totalHours)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {formatHours(request.billableHours)} billable\n </p>\n </div>\n <StatusBadge\n label={request.status.toLowerCase()}\n status={request.status.toLowerCase()}\n />\n <button\n onClick={() => setReviewTarget(request)}\n title=\"Review / Discussion\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken\"\n >\n <MessagesSquare className=\"h-4 w-4\" />\n </button>\n {isPending && (\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => handleApprove(request.id)}\n disabled={rowBusy}\n title=\"Approve\"\n className=\"rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg disabled:opacity-50\"\n >\n <CheckCircle className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => {\n setRevisionTarget(request);\n setRevisionNotes(\"\");\n }}\n disabled={rowBusy}\n title=\"Request revision\"\n className=\"rounded-md p-1.5 text-status-warning-text hover:bg-status-warning-bg disabled:opacity-50\"\n >\n <MessageSquare className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => {\n setRejectTarget(request);\n setRejectReason(\"\");\n }}\n disabled={rowBusy}\n title=\"Reject\"\n className=\"rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg disabled:opacity-50\"\n >\n <XCircle className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setWithdrawTarget(request)}\n disabled={rowBusy}\n title=\"Withdraw request\"\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken disabled:opacity-50\"\n >\n <Undo2 className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <div className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-xs text-text-secondary\">\n Showing {requests.length} of {total}\n </p>\n {hasMore && (\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\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 {loadingMore ? \"Loading...\" : \"Load more\"}\n </button>\n )}\n </div>\n </>\n )}\n\n <Modal\n open={rejectTarget !== null}\n onClose={() => setRejectTarget(null)}\n title=\"Reject timesheet\"\n description={\n rejectTarget\n ? `Rejecting ${rejectTarget.userId}'s ${rejectTarget.period} timesheet.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setRejectTarget(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={handleReject}\n disabled={rejecting || !rejectReason.trim()}\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/80 disabled:opacity-50\"\n >\n {rejecting ? \"Rejecting...\" : \"Reject\"}\n </button>\n </>\n }\n >\n <label className=\"block text-sm font-medium text-text-primary\">\n Rejection reason\n </label>\n <textarea\n rows={3}\n placeholder=\"Explain why this timesheet is being rejected\"\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n className={inputClass}\n />\n </Modal>\n\n <Modal\n open={revisionTarget !== null}\n onClose={() => setRevisionTarget(null)}\n title=\"Request revision\"\n description={\n revisionTarget\n ? `Ask ${revisionTarget.userId} to revise their ${revisionTarget.period} timesheet.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setRevisionTarget(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={handleRequestRevision}\n disabled={requestingRevision || !revisionNotes.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 {requestingRevision ? \"Sending...\" : \"Request Revision\"}\n </button>\n </>\n }\n >\n <label className=\"block text-sm font-medium text-text-primary\">\n What needs changing?\n </label>\n <textarea\n rows={3}\n placeholder=\"Describe the changes needed before this can be approved\"\n value={revisionNotes}\n onChange={(e) => setRevisionNotes(e.target.value)}\n className={inputClass}\n />\n </Modal>\n\n <Modal\n open={withdrawTarget !== null}\n onClose={() => setWithdrawTarget(null)}\n title=\"Withdraw request?\"\n description={\n withdrawTarget\n ? `${withdrawTarget.userId}'s ${withdrawTarget.period} timesheet will be pulled from the review queue.`\n : undefined\n }\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setWithdrawTarget(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={handleWithdraw}\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\"\n >\n Withdraw\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Withdrawing removes the request from the approval queue. It can be\n resubmitted later.\n </p>\n </Modal>\n\n <Modal\n open={reviewTarget !== null}\n onClose={() => setReviewTarget(null)}\n title=\"Review / Discussion\"\n description={\n reviewTarget\n ? `Discuss ${reviewTarget.userId}'s ${reviewTarget.period} timesheet with the team.`\n : undefined\n }\n size=\"lg\"\n >\n {reviewTarget && <ApprovalReviewThread request={reviewTarget} />}\n </Modal>\n\n <ToastContainer toasts={toasts} onDismiss={dismissToast} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-timecampus",
3
- "version": "2026.703.2",
3
+ "version": "2026.703.4",
4
4
  "description": "Burdenoff TimeCampus Microfrontend - AI-powered time management and productivity platform",
5
5
  "type": "module",
6
6
  "files": [