@burdenoff/microfe-proserviceworld 2026.1014.1 → 2026.1015.1
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/proserviceworld/hooks/useProServiceWorldApi.d.ts +8 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts.map +1 -1
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +21 -13
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
- package/dist/proserviceworld/pages/CompliancePage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/CompliancePage.js +136 -118
- package/dist/proserviceworld/pages/CompliancePage.js.map +1 -1
- package/dist/proserviceworld/pages/ConflictsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/ConflictsPage.js +87 -71
- package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +104 -86
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/InvoicesPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/InvoicesPage.js +122 -104
- package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/TimePage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/TimePage.js +132 -114
- package/dist/proserviceworld/pages/TimePage.js.map +1 -1
- package/dist/proserviceworld/pages/TrustPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/TrustPage.js +226 -207
- package/dist/proserviceworld/pages/TrustPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,162 +1,180 @@
|
|
|
1
1
|
import { useApproveTimeEntry as e, useEngagements as t, useLogTimeEntry as n, useMatters as r, useSubmitTimeEntry as i, useTimeEntries as a } from "../hooks/useProServiceWorldApi.js";
|
|
2
2
|
import { Banner as o, Empty as s, ErrorState as c, Field as l, LoadingBlock as u, MetricCard as d, Modal as f, NativeSelect as p, PageHeader as m, SectionCard as h, StatusPill as g, TableWrap as _, Td as v, TextArea as y, TextInput as b, Th as x, enumOptions as S, fmtDate as C, fmtMinutesAsHours as w } from "../components/ui.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { useAnalytics as T } from "../hooks/useAnalytics.js";
|
|
4
|
+
import { useDeferredValue as E, useState as D } from "react";
|
|
5
|
+
import { CheckCircle2 as O, CircleSlash as ee, Clock as k, ListChecks as A, Plus as j, Search as M, Send as N, Wallet as P } from "lucide-react";
|
|
6
|
+
import { Fragment as F, jsx as I, jsxs as L } from "react/jsx-runtime";
|
|
7
|
+
import { Button as R } from "@burdenoff/fe-libs/ui";
|
|
7
8
|
//#region src/proserviceworld/pages/TimePage.tsx
|
|
8
|
-
var
|
|
9
|
+
var z = [
|
|
9
10
|
"DRAFT",
|
|
10
11
|
"SUBMITTED",
|
|
11
12
|
"APPROVED",
|
|
12
13
|
"REJECTED",
|
|
13
14
|
"BILLED"
|
|
14
|
-
],
|
|
15
|
+
], B = ({ billable: e }) => e ? /* @__PURE__ */ L("span", {
|
|
15
16
|
className: "inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-success-text",
|
|
16
|
-
children: [/* @__PURE__ */
|
|
17
|
-
}) : /* @__PURE__ */
|
|
17
|
+
children: [/* @__PURE__ */ I("span", { className: "h-1.5 w-1.5 rounded-full bg-current opacity-70" }), "Billable"]
|
|
18
|
+
}) : /* @__PURE__ */ L("span", {
|
|
18
19
|
className: "inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-tertiary",
|
|
19
|
-
children: [/* @__PURE__ */
|
|
20
|
-
}),
|
|
21
|
-
let [t, n] =
|
|
22
|
-
|
|
20
|
+
children: [/* @__PURE__ */ I("span", { className: "h-1.5 w-1.5 rounded-full bg-current opacity-60" }), "Non-billable"]
|
|
21
|
+
}), V = () => {
|
|
22
|
+
let [t, n] = D(""), [r, l] = D(""), f = E(r), [y, F] = D(!1), [V, U] = D(null), { data: W, isLoading: G, error: K, refetch: q } = a({
|
|
23
|
+
status: t || void 0,
|
|
24
|
+
search: f.trim() || void 0
|
|
25
|
+
}), { emitFact: J } = T(), Y = i(), X = e(), Z = (W?.items ?? []).reduce((e, t) => e + t.minutes, 0), Q = (W?.items ?? []).filter((e) => e.billable).reduce((e, t) => e + t.minutes, 0), te = Z > 0 ? Math.round(Q / Z * 100) : 0, $ = (e, t, n) => e.mutate(t, {
|
|
26
|
+
onSuccess: () => U({
|
|
23
27
|
tone: "success",
|
|
24
28
|
text: `Time entry ${n}.`
|
|
25
29
|
}),
|
|
26
|
-
onError: (e) =>
|
|
30
|
+
onError: (e) => U({
|
|
27
31
|
tone: "danger",
|
|
28
32
|
text: e instanceof Error ? e.message : "Failed."
|
|
29
33
|
})
|
|
30
34
|
});
|
|
31
|
-
return /* @__PURE__ */
|
|
35
|
+
return /* @__PURE__ */ L("div", {
|
|
32
36
|
className: "space-y-6",
|
|
33
37
|
children: [
|
|
34
|
-
/* @__PURE__ */
|
|
38
|
+
/* @__PURE__ */ I(m, {
|
|
35
39
|
title: "Time",
|
|
36
40
|
subtitle: "Capture expert effort against engagements and matters — the raw material of billing and utilization.",
|
|
37
|
-
icon: /* @__PURE__ */
|
|
38
|
-
actions: /* @__PURE__ */
|
|
39
|
-
onClick: () =>
|
|
40
|
-
children: [/* @__PURE__ */
|
|
41
|
+
icon: /* @__PURE__ */ I(k, { className: "h-6 w-6" }),
|
|
42
|
+
actions: /* @__PURE__ */ L(R, {
|
|
43
|
+
onClick: () => F(!0),
|
|
44
|
+
children: [/* @__PURE__ */ I(j, { className: "mr-1.5 h-4 w-4" }), "Log time"]
|
|
41
45
|
})
|
|
42
46
|
}),
|
|
43
|
-
|
|
44
|
-
tone:
|
|
45
|
-
onClose: () =>
|
|
46
|
-
children:
|
|
47
|
+
V && /* @__PURE__ */ I(o, {
|
|
48
|
+
tone: V.tone,
|
|
49
|
+
onClose: () => U(null),
|
|
50
|
+
children: V.text
|
|
47
51
|
}),
|
|
48
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ L("div", {
|
|
49
53
|
className: "grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
50
54
|
children: [
|
|
51
|
-
/* @__PURE__ */
|
|
55
|
+
/* @__PURE__ */ I(d, {
|
|
52
56
|
label: "Entries",
|
|
53
|
-
value:
|
|
54
|
-
icon: /* @__PURE__ */
|
|
57
|
+
value: W?.totalCount ?? 0,
|
|
58
|
+
icon: /* @__PURE__ */ I(A, { className: "h-4 w-4" })
|
|
55
59
|
}),
|
|
56
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ I(d, {
|
|
57
61
|
label: "Total hours",
|
|
58
|
-
value: w(
|
|
59
|
-
icon: /* @__PURE__ */
|
|
62
|
+
value: w(Z),
|
|
63
|
+
icon: /* @__PURE__ */ I(k, { className: "h-4 w-4" })
|
|
60
64
|
}),
|
|
61
|
-
/* @__PURE__ */
|
|
65
|
+
/* @__PURE__ */ I(d, {
|
|
62
66
|
label: "Billable hours",
|
|
63
|
-
value: w(
|
|
64
|
-
hint: `${
|
|
65
|
-
icon: /* @__PURE__ */ P
|
|
67
|
+
value: w(Q),
|
|
68
|
+
hint: `${te}% utilization`,
|
|
69
|
+
icon: /* @__PURE__ */ I(P, { className: "h-4 w-4" }),
|
|
66
70
|
tone: "accent"
|
|
67
71
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
72
|
+
/* @__PURE__ */ I(d, {
|
|
69
73
|
label: "Non-billable",
|
|
70
|
-
value: w(
|
|
71
|
-
icon: /* @__PURE__ */
|
|
74
|
+
value: w(Z - Q),
|
|
75
|
+
icon: /* @__PURE__ */ I(ee, { className: "h-4 w-4" })
|
|
72
76
|
})
|
|
73
77
|
]
|
|
74
78
|
}),
|
|
75
|
-
/* @__PURE__ */
|
|
76
|
-
className: "flex
|
|
77
|
-
children: /* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ L("div", {
|
|
80
|
+
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
81
|
+
children: [/* @__PURE__ */ L("div", {
|
|
82
|
+
className: "relative flex-1",
|
|
83
|
+
children: [/* @__PURE__ */ I(M, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ I(b, {
|
|
84
|
+
placeholder: "Search time entries…",
|
|
85
|
+
value: r,
|
|
86
|
+
onChange: (e) => l(e.target.value),
|
|
87
|
+
onKeyDown: (e) => {
|
|
88
|
+
e.key === "Enter" && r.trim() && J("psw.search.performed", {
|
|
89
|
+
entityType: "time-entry",
|
|
90
|
+
queryLength: r.trim().length
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
className: "pl-9"
|
|
94
|
+
})]
|
|
95
|
+
}), /* @__PURE__ */ L(p, {
|
|
78
96
|
value: t,
|
|
79
97
|
onChange: (e) => n(e.target.value),
|
|
80
98
|
className: "sm:w-52",
|
|
81
|
-
children: [/* @__PURE__ */
|
|
99
|
+
children: [/* @__PURE__ */ I("option", {
|
|
82
100
|
value: "",
|
|
83
101
|
children: "All statuses"
|
|
84
|
-
}), S(
|
|
85
|
-
})
|
|
102
|
+
}), S(z)]
|
|
103
|
+
})]
|
|
86
104
|
}),
|
|
87
|
-
|
|
88
|
-
error:
|
|
89
|
-
onRetry: () => void
|
|
90
|
-
}) : !
|
|
105
|
+
G ? /* @__PURE__ */ I(u, {}) : K ? /* @__PURE__ */ I(c, {
|
|
106
|
+
error: K,
|
|
107
|
+
onRetry: () => void q()
|
|
108
|
+
}) : !W || W.items.length === 0 ? /* @__PURE__ */ I(s, {
|
|
91
109
|
title: "No time logged",
|
|
92
110
|
description: "Log your first time entry against an engagement or matter.",
|
|
93
|
-
icon: /* @__PURE__ */
|
|
94
|
-
action: /* @__PURE__ */
|
|
95
|
-
onClick: () =>
|
|
96
|
-
children: [/* @__PURE__ */
|
|
111
|
+
icon: /* @__PURE__ */ I(k, { className: "h-8 w-8" }),
|
|
112
|
+
action: /* @__PURE__ */ L(R, {
|
|
113
|
+
onClick: () => F(!0),
|
|
114
|
+
children: [/* @__PURE__ */ I(j, { className: "mr-1.5 h-4 w-4" }), "Log time"]
|
|
97
115
|
})
|
|
98
|
-
}) : /* @__PURE__ */
|
|
116
|
+
}) : /* @__PURE__ */ I(h, {
|
|
99
117
|
title: "Time entries",
|
|
100
|
-
children: /* @__PURE__ */
|
|
101
|
-
/* @__PURE__ */
|
|
102
|
-
/* @__PURE__ */
|
|
103
|
-
/* @__PURE__ */
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
/* @__PURE__ */
|
|
118
|
+
children: /* @__PURE__ */ L(_, { children: [/* @__PURE__ */ I("thead", { children: /* @__PURE__ */ L("tr", { children: [
|
|
119
|
+
/* @__PURE__ */ I(x, { children: "Date" }),
|
|
120
|
+
/* @__PURE__ */ I(x, { children: "Narrative" }),
|
|
121
|
+
/* @__PURE__ */ I(x, { children: "Hours" }),
|
|
122
|
+
/* @__PURE__ */ I(x, { children: "Billable" }),
|
|
123
|
+
/* @__PURE__ */ I(x, { children: "Status" }),
|
|
124
|
+
/* @__PURE__ */ I(x, {
|
|
107
125
|
className: "text-right",
|
|
108
126
|
children: "Actions"
|
|
109
127
|
})
|
|
110
|
-
] }) }), /* @__PURE__ */
|
|
128
|
+
] }) }), /* @__PURE__ */ I("tbody", { children: W.items.map((e) => /* @__PURE__ */ L("tr", {
|
|
111
129
|
className: "transition-colors hover:bg-bg-sunken/60",
|
|
112
130
|
children: [
|
|
113
|
-
/* @__PURE__ */
|
|
131
|
+
/* @__PURE__ */ I(v, {
|
|
114
132
|
className: "whitespace-nowrap",
|
|
115
133
|
children: C(e.date)
|
|
116
134
|
}),
|
|
117
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ I(v, {
|
|
118
136
|
className: "max-w-xs truncate text-text-primary",
|
|
119
137
|
children: e.narrative || "—"
|
|
120
138
|
}),
|
|
121
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ I(v, {
|
|
122
140
|
className: "whitespace-nowrap font-medium text-text-primary",
|
|
123
141
|
children: w(e.minutes)
|
|
124
142
|
}),
|
|
125
|
-
/* @__PURE__ */
|
|
126
|
-
/* @__PURE__ */
|
|
127
|
-
/* @__PURE__ */
|
|
143
|
+
/* @__PURE__ */ I(v, { children: /* @__PURE__ */ I(B, { billable: e.billable }) }),
|
|
144
|
+
/* @__PURE__ */ I(v, { children: /* @__PURE__ */ I(g, { status: e.status }) }),
|
|
145
|
+
/* @__PURE__ */ I(v, {
|
|
128
146
|
className: "text-right",
|
|
129
|
-
children: /* @__PURE__ */
|
|
147
|
+
children: /* @__PURE__ */ L("div", {
|
|
130
148
|
className: "flex justify-end gap-1",
|
|
131
|
-
children: [e.status === "DRAFT" && /* @__PURE__ */
|
|
149
|
+
children: [e.status === "DRAFT" && /* @__PURE__ */ L(R, {
|
|
132
150
|
variant: "ghost",
|
|
133
151
|
size: "sm",
|
|
134
|
-
disabled:
|
|
135
|
-
onClick: () =>
|
|
136
|
-
children: [/* @__PURE__ */
|
|
137
|
-
}), e.status === "SUBMITTED" && /* @__PURE__ */
|
|
152
|
+
disabled: Y.isPending,
|
|
153
|
+
onClick: () => $(Y, e.id, "submitted"),
|
|
154
|
+
children: [/* @__PURE__ */ I(N, { className: "mr-1 h-3.5 w-3.5" }), "Submit"]
|
|
155
|
+
}), e.status === "SUBMITTED" && /* @__PURE__ */ L(R, {
|
|
138
156
|
variant: "ghost",
|
|
139
157
|
size: "sm",
|
|
140
|
-
disabled:
|
|
141
|
-
onClick: () =>
|
|
142
|
-
children: [/* @__PURE__ */
|
|
158
|
+
disabled: X.isPending,
|
|
159
|
+
onClick: () => $(X, e.id, "approved"),
|
|
160
|
+
children: [/* @__PURE__ */ I(O, { className: "mr-1 h-3.5 w-3.5" }), "Approve"]
|
|
143
161
|
})]
|
|
144
162
|
})
|
|
145
163
|
})
|
|
146
164
|
]
|
|
147
165
|
}, e.id)) })] })
|
|
148
166
|
}),
|
|
149
|
-
|
|
150
|
-
onClose: () =>
|
|
151
|
-
onDone: () =>
|
|
167
|
+
y && /* @__PURE__ */ I(H, {
|
|
168
|
+
onClose: () => F(!1),
|
|
169
|
+
onDone: () => U({
|
|
152
170
|
tone: "success",
|
|
153
171
|
text: "Time logged."
|
|
154
172
|
})
|
|
155
173
|
})
|
|
156
174
|
]
|
|
157
175
|
});
|
|
158
|
-
},
|
|
159
|
-
let a = n(), s = t({ pagination: { limit: 100 } }), c = r({ pagination: { limit: 100 } }), [u, d] =
|
|
176
|
+
}, H = ({ onClose: e, onDone: i }) => {
|
|
177
|
+
let a = n(), s = t({ pagination: { limit: 100 } }), c = r({ pagination: { limit: 100 } }), [u, d] = D({
|
|
160
178
|
against: "engagement",
|
|
161
179
|
engagementId: "",
|
|
162
180
|
matterId: "",
|
|
@@ -166,20 +184,20 @@ var L = [
|
|
|
166
184
|
rate: "",
|
|
167
185
|
billable: !0,
|
|
168
186
|
narrative: ""
|
|
169
|
-
}), [m, h] =
|
|
187
|
+
}), [m, h] = D(null), g = (e) => (t) => d((n) => ({
|
|
170
188
|
...n,
|
|
171
189
|
[e]: t.target.value
|
|
172
190
|
}));
|
|
173
|
-
return /* @__PURE__ */
|
|
191
|
+
return /* @__PURE__ */ I(f, {
|
|
174
192
|
open: !0,
|
|
175
193
|
onClose: e,
|
|
176
194
|
title: "Log time",
|
|
177
|
-
footer: /* @__PURE__ */ F
|
|
195
|
+
footer: /* @__PURE__ */ L(F, { children: [/* @__PURE__ */ I(R, {
|
|
178
196
|
variant: "outline",
|
|
179
197
|
onClick: e,
|
|
180
198
|
disabled: a.isPending,
|
|
181
199
|
children: "Cancel"
|
|
182
|
-
}), /* @__PURE__ */
|
|
200
|
+
}), /* @__PURE__ */ I(R, {
|
|
183
201
|
onClick: () => {
|
|
184
202
|
h(null);
|
|
185
203
|
let t = (Number(u.hours) || 0) * 60 + (Number(u.minutes) || 0);
|
|
@@ -204,70 +222,70 @@ var L = [
|
|
|
204
222
|
disabled: a.isPending,
|
|
205
223
|
children: a.isPending ? "Logging…" : "Log time"
|
|
206
224
|
})] }),
|
|
207
|
-
children: /* @__PURE__ */
|
|
225
|
+
children: /* @__PURE__ */ L("div", {
|
|
208
226
|
className: "space-y-3",
|
|
209
227
|
children: [
|
|
210
|
-
m && /* @__PURE__ */
|
|
228
|
+
m && /* @__PURE__ */ I(o, {
|
|
211
229
|
tone: "danger",
|
|
212
230
|
children: m
|
|
213
231
|
}),
|
|
214
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ I(l, {
|
|
215
233
|
label: "Against",
|
|
216
|
-
children: /* @__PURE__ */
|
|
234
|
+
children: /* @__PURE__ */ L(p, {
|
|
217
235
|
value: u.against,
|
|
218
236
|
onChange: g("against"),
|
|
219
|
-
children: [/* @__PURE__ */
|
|
237
|
+
children: [/* @__PURE__ */ I("option", {
|
|
220
238
|
value: "engagement",
|
|
221
239
|
children: "Engagement"
|
|
222
|
-
}), /* @__PURE__ */
|
|
240
|
+
}), /* @__PURE__ */ I("option", {
|
|
223
241
|
value: "matter",
|
|
224
242
|
children: "Matter"
|
|
225
243
|
})]
|
|
226
244
|
})
|
|
227
245
|
}),
|
|
228
|
-
u.against === "engagement" ? /* @__PURE__ */
|
|
246
|
+
u.against === "engagement" ? /* @__PURE__ */ I(l, {
|
|
229
247
|
label: "Engagement",
|
|
230
248
|
required: !0,
|
|
231
|
-
children: /* @__PURE__ */
|
|
249
|
+
children: /* @__PURE__ */ L(p, {
|
|
232
250
|
value: u.engagementId,
|
|
233
251
|
onChange: g("engagementId"),
|
|
234
|
-
children: [/* @__PURE__ */
|
|
252
|
+
children: [/* @__PURE__ */ I("option", {
|
|
235
253
|
value: "",
|
|
236
254
|
children: "Select an engagement…"
|
|
237
|
-
}), s.data?.items.map((e) => /* @__PURE__ */
|
|
255
|
+
}), s.data?.items.map((e) => /* @__PURE__ */ I("option", {
|
|
238
256
|
value: e.id,
|
|
239
257
|
children: e.name
|
|
240
258
|
}, e.id))]
|
|
241
259
|
})
|
|
242
|
-
}) : /* @__PURE__ */
|
|
260
|
+
}) : /* @__PURE__ */ I(l, {
|
|
243
261
|
label: "Matter",
|
|
244
262
|
required: !0,
|
|
245
|
-
children: /* @__PURE__ */
|
|
263
|
+
children: /* @__PURE__ */ L(p, {
|
|
246
264
|
value: u.matterId,
|
|
247
265
|
onChange: g("matterId"),
|
|
248
|
-
children: [/* @__PURE__ */
|
|
266
|
+
children: [/* @__PURE__ */ I("option", {
|
|
249
267
|
value: "",
|
|
250
268
|
children: "Select a matter…"
|
|
251
|
-
}), c.data?.items.map((e) => /* @__PURE__ */
|
|
269
|
+
}), c.data?.items.map((e) => /* @__PURE__ */ I("option", {
|
|
252
270
|
value: e.id,
|
|
253
271
|
children: e.title
|
|
254
272
|
}, e.id))]
|
|
255
273
|
})
|
|
256
274
|
}),
|
|
257
|
-
/* @__PURE__ */
|
|
275
|
+
/* @__PURE__ */ L("div", {
|
|
258
276
|
className: "grid grid-cols-3 gap-3",
|
|
259
277
|
children: [
|
|
260
|
-
/* @__PURE__ */
|
|
278
|
+
/* @__PURE__ */ I(l, {
|
|
261
279
|
label: "Date",
|
|
262
|
-
children: /* @__PURE__ */
|
|
280
|
+
children: /* @__PURE__ */ I(b, {
|
|
263
281
|
type: "date",
|
|
264
282
|
value: u.date,
|
|
265
283
|
onChange: g("date")
|
|
266
284
|
})
|
|
267
285
|
}),
|
|
268
|
-
/* @__PURE__ */
|
|
286
|
+
/* @__PURE__ */ I(l, {
|
|
269
287
|
label: "Hours",
|
|
270
|
-
children: /* @__PURE__ */
|
|
288
|
+
children: /* @__PURE__ */ I(b, {
|
|
271
289
|
type: "number",
|
|
272
290
|
min: "0",
|
|
273
291
|
value: u.hours,
|
|
@@ -275,9 +293,9 @@ var L = [
|
|
|
275
293
|
placeholder: "2"
|
|
276
294
|
})
|
|
277
295
|
}),
|
|
278
|
-
/* @__PURE__ */
|
|
296
|
+
/* @__PURE__ */ I(l, {
|
|
279
297
|
label: "Minutes",
|
|
280
|
-
children: /* @__PURE__ */
|
|
298
|
+
children: /* @__PURE__ */ I(b, {
|
|
281
299
|
type: "number",
|
|
282
300
|
min: "0",
|
|
283
301
|
max: "59",
|
|
@@ -288,9 +306,9 @@ var L = [
|
|
|
288
306
|
})
|
|
289
307
|
]
|
|
290
308
|
}),
|
|
291
|
-
/* @__PURE__ */
|
|
309
|
+
/* @__PURE__ */ I(l, {
|
|
292
310
|
label: "Rate (per hour)",
|
|
293
|
-
children: /* @__PURE__ */
|
|
311
|
+
children: /* @__PURE__ */ I(b, {
|
|
294
312
|
type: "number",
|
|
295
313
|
min: "0",
|
|
296
314
|
value: u.rate,
|
|
@@ -298,23 +316,23 @@ var L = [
|
|
|
298
316
|
placeholder: "450"
|
|
299
317
|
})
|
|
300
318
|
}),
|
|
301
|
-
/* @__PURE__ */
|
|
319
|
+
/* @__PURE__ */ L("label", {
|
|
302
320
|
className: "flex items-center gap-2.5 rounded-lg border border-border-seam bg-bg-canvas px-3 py-2.5 text-sm text-text-secondary",
|
|
303
|
-
children: [/* @__PURE__ */
|
|
321
|
+
children: [/* @__PURE__ */ I("input", {
|
|
304
322
|
type: "checkbox",
|
|
305
323
|
checked: u.billable,
|
|
306
324
|
onChange: (e) => d((t) => ({
|
|
307
325
|
...t,
|
|
308
326
|
billable: e.target.checked
|
|
309
327
|
}))
|
|
310
|
-
}), /* @__PURE__ */
|
|
328
|
+
}), /* @__PURE__ */ L("span", {
|
|
311
329
|
className: "flex items-center gap-1.5",
|
|
312
|
-
children: [/* @__PURE__ */ P
|
|
330
|
+
children: [/* @__PURE__ */ I(P, { className: "h-3.5 w-3.5 text-status-success-text" }), "Billable"]
|
|
313
331
|
})]
|
|
314
332
|
}),
|
|
315
|
-
/* @__PURE__ */
|
|
333
|
+
/* @__PURE__ */ I(l, {
|
|
316
334
|
label: "Narrative",
|
|
317
|
-
children: /* @__PURE__ */
|
|
335
|
+
children: /* @__PURE__ */ I(y, {
|
|
318
336
|
value: u.narrative,
|
|
319
337
|
onChange: g("narrative"),
|
|
320
338
|
placeholder: "Reviewed working papers and drafted findings memo…"
|
|
@@ -325,6 +343,6 @@ var L = [
|
|
|
325
343
|
});
|
|
326
344
|
};
|
|
327
345
|
//#endregion
|
|
328
|
-
export {
|
|
346
|
+
export { V as TimePage };
|
|
329
347
|
|
|
330
348
|
//# sourceMappingURL=TimePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimePage.js","names":[],"sources":["../../../src/proserviceworld/pages/TimePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Clock, Send, CheckCircle2, ListChecks, Wallet, CircleSlash } from 'lucide-react';\nimport {\n useTimeEntries,\n useLogTimeEntry,\n useSubmitTimeEntry,\n useApproveTimeEntry,\n useEngagements,\n useMatters,\n type PswTimeEntryStatus,\n} from '../hooks/useProServiceWorldApi';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Modal,\n Field,\n TextInput,\n TextArea,\n NativeSelect,\n Banner,\n MetricCard,\n TableWrap,\n Th,\n Td,\n enumOptions,\n fmtDate,\n fmtMinutesAsHours,\n} from '../components/ui';\n\nconst STATUSES: PswTimeEntryStatus[] = ['DRAFT', 'SUBMITTED', 'APPROVED', 'REJECTED', 'BILLED'];\n\n/** Compact billable / non-billable tag. */\nconst BillableTag: FC<{ billable: boolean }> = ({ billable }) =>\n billable ? (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-success-text\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-current opacity-70\" />\n Billable\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-tertiary\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-current opacity-60\" />\n Non-billable\n </span>\n );\n\nexport const TimePage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<PswTimeEntryStatus | ''>('');\n const [logging, setLogging] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useTimeEntries({ status: statusFilter || undefined });\n const submit = useSubmitTimeEntry();\n const approve = useApproveTimeEntry();\n\n const totalMinutes = (data?.items ?? []).reduce((s, t) => s + t.minutes, 0);\n const billableMinutes = (data?.items ?? [])\n .filter((t) => t.billable)\n .reduce((s, t) => s + t.minutes, 0);\n const utilization = totalMinutes > 0 ? Math.round((billableMinutes / totalMinutes) * 100) : 0;\n\n const act = (\n fn: {\n mutate: (id: string, opts: { onSuccess: () => void; onError: (e: unknown) => void }) => void;\n },\n id: string,\n verb: string\n ) =>\n fn.mutate(id, {\n onSuccess: () => setBanner({ tone: 'success', text: `Time entry ${verb}.` }),\n onError: (e) =>\n setBanner({ tone: 'danger', text: e instanceof Error ? e.message : 'Failed.' }),\n });\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Time\"\n subtitle=\"Capture expert effort against engagements and matters — the raw material of billing and utilization.\"\n icon={<Clock className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setLogging(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Log time\n </Button>\n }\n />\n\n {banner && (\n <Banner tone={banner.tone} onClose={() => setBanner(null)}>\n {banner.text}\n </Banner>\n )}\n\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <MetricCard\n label=\"Entries\"\n value={data?.totalCount ?? 0}\n icon={<ListChecks className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Total hours\"\n value={fmtMinutesAsHours(totalMinutes)}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Billable hours\"\n value={fmtMinutesAsHours(billableMinutes)}\n hint={`${utilization}% utilization`}\n icon={<Wallet className=\"h-4 w-4\" />}\n tone=\"accent\"\n />\n <MetricCard\n label=\"Non-billable\"\n value={fmtMinutesAsHours(totalMinutes - billableMinutes)}\n icon={<CircleSlash className=\"h-4 w-4\" />}\n />\n </div>\n\n <div className=\"flex justify-end\">\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswTimeEntryStatus | '')}\n className=\"sm:w-52\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\n </NativeSelect>\n </div>\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No time logged\"\n description=\"Log your first time entry against an engagement or matter.\"\n icon={<Clock className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setLogging(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Log time\n </Button>\n }\n />\n ) : (\n <SectionCard title=\"Time entries\">\n <TableWrap>\n <thead>\n <tr>\n <Th>Date</Th>\n <Th>Narrative</Th>\n <Th>Hours</Th>\n <Th>Billable</Th>\n <Th>Status</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((t) => (\n <tr key={t.id} className=\"transition-colors hover:bg-bg-sunken/60\">\n <Td className=\"whitespace-nowrap\">{fmtDate(t.date)}</Td>\n <Td className=\"max-w-xs truncate text-text-primary\">{t.narrative || '—'}</Td>\n <Td className=\"whitespace-nowrap font-medium text-text-primary\">\n {fmtMinutesAsHours(t.minutes)}\n </Td>\n <Td>\n <BillableTag billable={t.billable} />\n </Td>\n <Td>\n <StatusPill status={t.status} />\n </Td>\n <Td className=\"text-right\">\n <div className=\"flex justify-end gap-1\">\n {t.status === 'DRAFT' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={submit.isPending}\n onClick={() => act(submit, t.id, 'submitted')}\n >\n <Send className=\"mr-1 h-3.5 w-3.5\" />\n Submit\n </Button>\n )}\n {t.status === 'SUBMITTED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={approve.isPending}\n onClick={() => act(approve, t.id, 'approved')}\n >\n <CheckCircle2 className=\"mr-1 h-3.5 w-3.5\" />\n Approve\n </Button>\n )}\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {logging && (\n <LogTimeModal\n onClose={() => setLogging(false)}\n onDone={() => setBanner({ tone: 'success', text: 'Time logged.' })}\n />\n )}\n </div>\n );\n};\n\nconst LogTimeModal: FC<{ onClose: () => void; onDone: () => void }> = ({ onClose, onDone }) => {\n const log = useLogTimeEntry();\n const engagements = useEngagements({ pagination: { limit: 100 } });\n const matters = useMatters({ pagination: { limit: 100 } });\n const [form, setForm] = useState({\n against: 'engagement',\n engagementId: '',\n matterId: '',\n date: new Date().toISOString().slice(0, 10),\n hours: '',\n minutes: '',\n rate: '',\n billable: true,\n narrative: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n\n const submit = () => {\n setErr(null);\n const totalMinutes = (Number(form.hours) || 0) * 60 + (Number(form.minutes) || 0);\n if (totalMinutes <= 0) return setErr('Enter the time spent.');\n const ref =\n form.against === 'engagement'\n ? { engagementId: form.engagementId }\n : { matterId: form.matterId };\n if (form.against === 'engagement' && !form.engagementId) return setErr('Select an engagement.');\n if (form.against === 'matter' && !form.matterId) return setErr('Select a matter.');\n log.mutate(\n {\n ...ref,\n date: new Date(form.date).toISOString(),\n minutes: totalMinutes,\n rate: form.rate ? Number(form.rate) : 0,\n billable: form.billable,\n narrative: form.narrative.trim() || null,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Log time\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={log.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={log.isPending}>\n {log.isPending ? 'Logging…' : 'Log time'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Against\">\n <NativeSelect value={form.against} onChange={set('against')}>\n <option value=\"engagement\">Engagement</option>\n <option value=\"matter\">Matter</option>\n </NativeSelect>\n </Field>\n {form.against === 'engagement' ? (\n <Field label=\"Engagement\" required>\n <NativeSelect value={form.engagementId} onChange={set('engagementId')}>\n <option value=\"\">Select an engagement…</option>\n {engagements.data?.items.map((e) => (\n <option key={e.id} value={e.id}>\n {e.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n ) : (\n <Field label=\"Matter\" required>\n <NativeSelect value={form.matterId} onChange={set('matterId')}>\n <option value=\"\">Select a matter…</option>\n {matters.data?.items.map((m) => (\n <option key={m.id} value={m.id}>\n {m.title}\n </option>\n ))}\n </NativeSelect>\n </Field>\n )}\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Date\">\n <TextInput type=\"date\" value={form.date} onChange={set('date')} />\n </Field>\n <Field label=\"Hours\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.hours}\n onChange={set('hours')}\n placeholder=\"2\"\n />\n </Field>\n <Field label=\"Minutes\">\n <TextInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n value={form.minutes}\n onChange={set('minutes')}\n placeholder=\"30\"\n />\n </Field>\n </div>\n <Field label=\"Rate (per hour)\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.rate}\n onChange={set('rate')}\n placeholder=\"450\"\n />\n </Field>\n <label className=\"flex items-center gap-2.5 rounded-lg border border-border-seam bg-bg-canvas px-3 py-2.5 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.billable}\n onChange={(e) => setForm((f) => ({ ...f, billable: e.target.checked }))}\n />\n <span className=\"flex items-center gap-1.5\">\n <Wallet className=\"h-3.5 w-3.5 text-status-success-text\" />\n Billable\n </span>\n </label>\n <Field label=\"Narrative\">\n <TextArea\n value={form.narrative}\n onChange={set('narrative')}\n placeholder=\"Reviewed working papers and drafted findings memo…\"\n />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimePage.js","names":[],"sources":["../../../src/proserviceworld/pages/TimePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useDeferredValue, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport {\n Plus,\n Clock,\n Send,\n CheckCircle2,\n ListChecks,\n Wallet,\n CircleSlash,\n Search,\n} from 'lucide-react';\nimport {\n useTimeEntries,\n useLogTimeEntry,\n useSubmitTimeEntry,\n useApproveTimeEntry,\n useEngagements,\n useMatters,\n type PswTimeEntryStatus,\n} from '../hooks/useProServiceWorldApi';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Modal,\n Field,\n TextInput,\n TextArea,\n NativeSelect,\n Banner,\n MetricCard,\n TableWrap,\n Th,\n Td,\n enumOptions,\n fmtDate,\n fmtMinutesAsHours,\n} from '../components/ui';\nimport { useAnalytics } from '../hooks/useAnalytics';\n\nconst STATUSES: PswTimeEntryStatus[] = ['DRAFT', 'SUBMITTED', 'APPROVED', 'REJECTED', 'BILLED'];\n\n/** Compact billable / non-billable tag. */\nconst BillableTag: FC<{ billable: boolean }> = ({ billable }) =>\n billable ? (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-success-text\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-current opacity-70\" />\n Billable\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-tertiary\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-current opacity-60\" />\n Non-billable\n </span>\n );\n\nexport const TimePage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<PswTimeEntryStatus | ''>('');\n const [search, setSearch] = useState('');\n const deferredSearch = useDeferredValue(search);\n const [logging, setLogging] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useTimeEntries({\n status: statusFilter || undefined,\n search: deferredSearch.trim() || undefined,\n });\n const { emitFact } = useAnalytics();\n const submit = useSubmitTimeEntry();\n const approve = useApproveTimeEntry();\n\n const totalMinutes = (data?.items ?? []).reduce((s, t) => s + t.minutes, 0);\n const billableMinutes = (data?.items ?? [])\n .filter((t) => t.billable)\n .reduce((s, t) => s + t.minutes, 0);\n const utilization = totalMinutes > 0 ? Math.round((billableMinutes / totalMinutes) * 100) : 0;\n\n const act = (\n fn: {\n mutate: (id: string, opts: { onSuccess: () => void; onError: (e: unknown) => void }) => void;\n },\n id: string,\n verb: string\n ) =>\n fn.mutate(id, {\n onSuccess: () => setBanner({ tone: 'success', text: `Time entry ${verb}.` }),\n onError: (e) =>\n setBanner({ tone: 'danger', text: e instanceof Error ? e.message : 'Failed.' }),\n });\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Time\"\n subtitle=\"Capture expert effort against engagements and matters — the raw material of billing and utilization.\"\n icon={<Clock className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setLogging(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Log time\n </Button>\n }\n />\n\n {banner && (\n <Banner tone={banner.tone} onClose={() => setBanner(null)}>\n {banner.text}\n </Banner>\n )}\n\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <MetricCard\n label=\"Entries\"\n value={data?.totalCount ?? 0}\n icon={<ListChecks className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Total hours\"\n value={fmtMinutesAsHours(totalMinutes)}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Billable hours\"\n value={fmtMinutesAsHours(billableMinutes)}\n hint={`${utilization}% utilization`}\n icon={<Wallet className=\"h-4 w-4\" />}\n tone=\"accent\"\n />\n <MetricCard\n label=\"Non-billable\"\n value={fmtMinutesAsHours(totalMinutes - billableMinutes)}\n icon={<CircleSlash className=\"h-4 w-4\" />}\n />\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\" />\n <TextInput\n placeholder=\"Search time entries…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && search.trim()) {\n emitFact('psw.search.performed', {\n entityType: 'time-entry',\n queryLength: search.trim().length,\n });\n }\n }}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswTimeEntryStatus | '')}\n className=\"sm:w-52\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\n </NativeSelect>\n </div>\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No time logged\"\n description=\"Log your first time entry against an engagement or matter.\"\n icon={<Clock className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setLogging(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Log time\n </Button>\n }\n />\n ) : (\n <SectionCard title=\"Time entries\">\n <TableWrap>\n <thead>\n <tr>\n <Th>Date</Th>\n <Th>Narrative</Th>\n <Th>Hours</Th>\n <Th>Billable</Th>\n <Th>Status</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((t) => (\n <tr key={t.id} className=\"transition-colors hover:bg-bg-sunken/60\">\n <Td className=\"whitespace-nowrap\">{fmtDate(t.date)}</Td>\n <Td className=\"max-w-xs truncate text-text-primary\">{t.narrative || '—'}</Td>\n <Td className=\"whitespace-nowrap font-medium text-text-primary\">\n {fmtMinutesAsHours(t.minutes)}\n </Td>\n <Td>\n <BillableTag billable={t.billable} />\n </Td>\n <Td>\n <StatusPill status={t.status} />\n </Td>\n <Td className=\"text-right\">\n <div className=\"flex justify-end gap-1\">\n {t.status === 'DRAFT' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={submit.isPending}\n onClick={() => act(submit, t.id, 'submitted')}\n >\n <Send className=\"mr-1 h-3.5 w-3.5\" />\n Submit\n </Button>\n )}\n {t.status === 'SUBMITTED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={approve.isPending}\n onClick={() => act(approve, t.id, 'approved')}\n >\n <CheckCircle2 className=\"mr-1 h-3.5 w-3.5\" />\n Approve\n </Button>\n )}\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {logging && (\n <LogTimeModal\n onClose={() => setLogging(false)}\n onDone={() => setBanner({ tone: 'success', text: 'Time logged.' })}\n />\n )}\n </div>\n );\n};\n\nconst LogTimeModal: FC<{ onClose: () => void; onDone: () => void }> = ({ onClose, onDone }) => {\n const log = useLogTimeEntry();\n const engagements = useEngagements({ pagination: { limit: 100 } });\n const matters = useMatters({ pagination: { limit: 100 } });\n const [form, setForm] = useState({\n against: 'engagement',\n engagementId: '',\n matterId: '',\n date: new Date().toISOString().slice(0, 10),\n hours: '',\n minutes: '',\n rate: '',\n billable: true,\n narrative: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n\n const submit = () => {\n setErr(null);\n const totalMinutes = (Number(form.hours) || 0) * 60 + (Number(form.minutes) || 0);\n if (totalMinutes <= 0) return setErr('Enter the time spent.');\n const ref =\n form.against === 'engagement'\n ? { engagementId: form.engagementId }\n : { matterId: form.matterId };\n if (form.against === 'engagement' && !form.engagementId) return setErr('Select an engagement.');\n if (form.against === 'matter' && !form.matterId) return setErr('Select a matter.');\n log.mutate(\n {\n ...ref,\n date: new Date(form.date).toISOString(),\n minutes: totalMinutes,\n rate: form.rate ? Number(form.rate) : 0,\n billable: form.billable,\n narrative: form.narrative.trim() || null,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Log time\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={log.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={log.isPending}>\n {log.isPending ? 'Logging…' : 'Log time'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Against\">\n <NativeSelect value={form.against} onChange={set('against')}>\n <option value=\"engagement\">Engagement</option>\n <option value=\"matter\">Matter</option>\n </NativeSelect>\n </Field>\n {form.against === 'engagement' ? (\n <Field label=\"Engagement\" required>\n <NativeSelect value={form.engagementId} onChange={set('engagementId')}>\n <option value=\"\">Select an engagement…</option>\n {engagements.data?.items.map((e) => (\n <option key={e.id} value={e.id}>\n {e.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n ) : (\n <Field label=\"Matter\" required>\n <NativeSelect value={form.matterId} onChange={set('matterId')}>\n <option value=\"\">Select a matter…</option>\n {matters.data?.items.map((m) => (\n <option key={m.id} value={m.id}>\n {m.title}\n </option>\n ))}\n </NativeSelect>\n </Field>\n )}\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Date\">\n <TextInput type=\"date\" value={form.date} onChange={set('date')} />\n </Field>\n <Field label=\"Hours\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.hours}\n onChange={set('hours')}\n placeholder=\"2\"\n />\n </Field>\n <Field label=\"Minutes\">\n <TextInput\n type=\"number\"\n min=\"0\"\n max=\"59\"\n value={form.minutes}\n onChange={set('minutes')}\n placeholder=\"30\"\n />\n </Field>\n </div>\n <Field label=\"Rate (per hour)\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.rate}\n onChange={set('rate')}\n placeholder=\"450\"\n />\n </Field>\n <label className=\"flex items-center gap-2.5 rounded-lg border border-border-seam bg-bg-canvas px-3 py-2.5 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.billable}\n onChange={(e) => setForm((f) => ({ ...f, billable: e.target.checked }))}\n />\n <span className=\"flex items-center gap-1.5\">\n <Wallet className=\"h-3.5 w-3.5 text-status-success-text\" />\n Billable\n </span>\n </label>\n <Field label=\"Narrative\">\n <TextArea\n value={form.narrative}\n onChange={set('narrative')}\n placeholder=\"Reviewed working papers and drafted findings memo…\"\n />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TrustPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/TrustPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"TrustPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/TrustPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAiDhC;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,EAAE,EA0UvB,CAAC"}
|