@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.
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +236 -159
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +275 -161
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.js +138 -75
- package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +120 -96
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
5
|
-
import { PageLayout as
|
|
6
|
-
import { EmptyState as
|
|
7
|
-
import { ApprovalStatus as
|
|
8
|
-
import { Modal as
|
|
9
|
-
import { StatusBadge as
|
|
10
|
-
import { ToastContainer as
|
|
11
|
-
import { useState as
|
|
12
|
-
import { CheckCircle as
|
|
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
|
|
16
|
-
function
|
|
17
|
-
|
|
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:
|
|
29
|
+
limit: b,
|
|
20
30
|
offset: 0
|
|
21
31
|
},
|
|
22
32
|
notifyOnNetworkStatusChange: !0
|
|
23
|
-
}), { data:
|
|
24
|
-
await Promise.all([
|
|
25
|
-
},
|
|
26
|
-
|
|
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
|
|
38
|
+
await O({ variables: { id: e } }), y({
|
|
29
39
|
title: "Timesheet approved",
|
|
30
40
|
variant: "success"
|
|
31
41
|
}), await $();
|
|
32
42
|
} catch {
|
|
33
|
-
|
|
43
|
+
y({
|
|
34
44
|
title: "Failed to approve timesheet",
|
|
35
45
|
variant: "error"
|
|
36
46
|
});
|
|
37
47
|
} finally {
|
|
38
|
-
|
|
48
|
+
B(null);
|
|
39
49
|
}
|
|
40
50
|
};
|
|
41
|
-
return
|
|
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
|
-
}) :
|
|
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__ */ _(
|
|
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(
|
|
67
|
-
icon: /* @__PURE__ */ g(
|
|
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(
|
|
73
|
-
icon: /* @__PURE__ */ g(
|
|
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(
|
|
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:
|
|
94
|
+
value: L?.averageReviewTime ? `${Math.round(L.averageReviewTime)}h` : "N/A",
|
|
85
95
|
description: "Target: 24h"
|
|
86
96
|
})
|
|
87
97
|
]
|
|
88
98
|
}),
|
|
89
|
-
|
|
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:
|
|
98
|
-
let i = r.status ===
|
|
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(
|
|
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: () =>
|
|
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(
|
|
159
|
+
children: /* @__PURE__ */ g(f, { className: "h-4 w-4" })
|
|
144
160
|
}),
|
|
145
161
|
/* @__PURE__ */ g("button", {
|
|
146
162
|
onClick: () => {
|
|
147
|
-
|
|
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
|
-
|
|
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: () =>
|
|
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(
|
|
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
|
-
|
|
199
|
+
P.length,
|
|
184
200
|
" of ",
|
|
185
|
-
|
|
201
|
+
F
|
|
186
202
|
]
|
|
187
|
-
}),
|
|
203
|
+
}), I && /* @__PURE__ */ g("button", {
|
|
188
204
|
onClick: async () => {
|
|
189
|
-
|
|
205
|
+
z(!0);
|
|
190
206
|
try {
|
|
191
|
-
await
|
|
207
|
+
await fe({
|
|
192
208
|
variables: {
|
|
193
|
-
limit:
|
|
194
|
-
offset:
|
|
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
|
-
|
|
221
|
+
z(!1);
|
|
206
222
|
}
|
|
207
223
|
},
|
|
208
|
-
disabled:
|
|
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:
|
|
226
|
+
children: R ? "Loading..." : "Load more"
|
|
211
227
|
})]
|
|
212
228
|
})] }),
|
|
213
|
-
/* @__PURE__ */ _(
|
|
214
|
-
open:
|
|
215
|
-
onClose: () =>
|
|
229
|
+
/* @__PURE__ */ _(u, {
|
|
230
|
+
open: V !== null,
|
|
231
|
+
onClose: () => H(null),
|
|
216
232
|
title: "Reject timesheet",
|
|
217
|
-
description:
|
|
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: () =>
|
|
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 (!(!
|
|
226
|
-
await
|
|
227
|
-
id:
|
|
228
|
-
reason:
|
|
229
|
-
} }),
|
|
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
|
-
}),
|
|
248
|
+
}), H(null), W(""), await $();
|
|
233
249
|
} catch {
|
|
234
|
-
|
|
250
|
+
y({
|
|
235
251
|
title: "Failed to reject timesheet",
|
|
236
252
|
variant: "error"
|
|
237
253
|
});
|
|
238
254
|
}
|
|
239
255
|
},
|
|
240
|
-
disabled:
|
|
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:
|
|
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:
|
|
251
|
-
onChange: (e) =>
|
|
252
|
-
className:
|
|
266
|
+
value: U,
|
|
267
|
+
onChange: (e) => W(e.target.value),
|
|
268
|
+
className: S
|
|
253
269
|
})]
|
|
254
270
|
}),
|
|
255
|
-
/* @__PURE__ */ _(
|
|
256
|
-
open:
|
|
257
|
-
onClose: () =>
|
|
271
|
+
/* @__PURE__ */ _(u, {
|
|
272
|
+
open: G !== null,
|
|
273
|
+
onClose: () => K(null),
|
|
258
274
|
title: "Request revision",
|
|
259
|
-
description:
|
|
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: () =>
|
|
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 (!(!
|
|
268
|
-
await
|
|
269
|
-
id:
|
|
270
|
-
notes:
|
|
271
|
-
} }),
|
|
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
|
-
}),
|
|
290
|
+
}), K(null), J(""), await $();
|
|
275
291
|
} catch {
|
|
276
|
-
|
|
292
|
+
y({
|
|
277
293
|
title: "Failed to request revision",
|
|
278
294
|
variant: "error"
|
|
279
295
|
});
|
|
280
296
|
}
|
|
281
297
|
},
|
|
282
|
-
disabled:
|
|
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:
|
|
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:
|
|
293
|
-
onChange: (e) =>
|
|
294
|
-
className:
|
|
308
|
+
value: q,
|
|
309
|
+
onChange: (e) => J(e.target.value),
|
|
310
|
+
className: S
|
|
295
311
|
})]
|
|
296
312
|
}),
|
|
297
|
-
/* @__PURE__ */ g(
|
|
298
|
-
open:
|
|
299
|
-
onClose: () =>
|
|
313
|
+
/* @__PURE__ */ g(u, {
|
|
314
|
+
open: Y !== null,
|
|
315
|
+
onClose: () => X(null),
|
|
300
316
|
title: "Withdraw request?",
|
|
301
|
-
description:
|
|
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: () =>
|
|
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 (
|
|
310
|
-
await
|
|
325
|
+
if (Y) try {
|
|
326
|
+
await N({ variables: { id: Y.id } }), y({
|
|
311
327
|
title: "Request withdrawn",
|
|
312
328
|
variant: "success"
|
|
313
|
-
}),
|
|
329
|
+
}), X(null), await $();
|
|
314
330
|
} catch {
|
|
315
|
-
|
|
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(
|
|
330
|
-
|
|
331
|
-
|
|
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 {
|
|
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} · 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} · 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