@burdenoff/microfe-proserviceworld 2026.626.2 → 2026.706.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/index.js +2 -3
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/proserviceworld/ProServiceWorldRoot.js +92 -136
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js +18 -26
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/ui.js +273 -0
- package/dist/proserviceworld/components/ui.js.map +1 -0
- package/dist/proserviceworld/hooks/useFiles.js +45 -0
- package/dist/proserviceworld/hooks/useFiles.js.map +1 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +416 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -0
- package/dist/proserviceworld/index.js +0 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +163 -475
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ClientDetailPage.js +238 -0
- package/dist/proserviceworld/pages/ClientDetailPage.js.map +1 -0
- package/dist/proserviceworld/pages/ClientsPage.js +295 -0
- package/dist/proserviceworld/pages/ClientsPage.js.map +1 -0
- package/dist/proserviceworld/pages/CompliancePage.js +307 -0
- package/dist/proserviceworld/pages/CompliancePage.js.map +1 -0
- package/dist/proserviceworld/pages/ConflictsPage.js +234 -0
- package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -0
- package/dist/proserviceworld/pages/DashboardPage.js +179 -632
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +235 -539
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementDetailPage.js +444 -0
- package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -0
- package/dist/proserviceworld/pages/EngagementsPage.js +256 -0
- package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -0
- package/dist/proserviceworld/pages/InvoicesPage.js +303 -0
- package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -0
- package/dist/proserviceworld/pages/MattersPage.js +275 -0
- package/dist/proserviceworld/pages/MattersPage.js.map +1 -0
- package/dist/proserviceworld/pages/PracticeAreasPage.js +176 -0
- package/dist/proserviceworld/pages/PracticeAreasPage.js.map +1 -0
- package/dist/proserviceworld/pages/TimePage.js +310 -0
- package/dist/proserviceworld/pages/TimePage.js.map +1 -0
- package/dist/proserviceworld/utils/cn.js +10 -0
- package/dist/proserviceworld/utils/cn.js.map +1 -0
- package/dist/proserviceworld/utils/nav.js +11 -0
- package/dist/proserviceworld/utils/nav.js.map +1 -0
- package/package.json +3 -2
- package/dist/proserviceworld/pages/AdminReportsPage.js +0 -579
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +0 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +0 -468
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +0 -1
- package/dist/proserviceworld/pages/BookingsPage.js +0 -446
- package/dist/proserviceworld/pages/BookingsPage.js.map +0 -1
- package/dist/proserviceworld/pages/MessagesPage.js +0 -238
- package/dist/proserviceworld/pages/MessagesPage.js.map +0 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +0 -537
- package/dist/proserviceworld/pages/PaymentsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +0 -627
- package/dist/proserviceworld/pages/ProjectsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +0 -538
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +0 -347
- package/dist/proserviceworld/pages/ProvidersPage.js.map +0 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +0 -356
- package/dist/proserviceworld/pages/ReviewsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +0 -631
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ServicesPage.js +0 -344
- package/dist/proserviceworld/pages/ServicesPage.js.map +0 -1
- package/dist/proserviceworld/pages/VerificationPage.js +0 -370
- package/dist/proserviceworld/pages/VerificationPage.js.map +0 -1
- package/dist/proserviceworld/store/proserviceworldStore.js +0 -2490
- package/dist/proserviceworld/store/proserviceworldStore.js.map +0 -1
- package/dist/proserviceworld/utils/i18n.js +0 -15
- package/dist/proserviceworld/utils/i18n.js.map +0 -1
|
@@ -0,0 +1,310 @@
|
|
|
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
|
+
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 { useState as T } from "react";
|
|
4
|
+
import { CheckCircle2 as E, Clock as D, Plus as O, Send as k } from "lucide-react";
|
|
5
|
+
import { Fragment as A, jsx as j, jsxs as M } from "react/jsx-runtime";
|
|
6
|
+
import { Button as N } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
//#region src/proserviceworld/pages/TimePage.tsx
|
|
8
|
+
var P = [
|
|
9
|
+
"DRAFT",
|
|
10
|
+
"SUBMITTED",
|
|
11
|
+
"APPROVED",
|
|
12
|
+
"REJECTED",
|
|
13
|
+
"BILLED"
|
|
14
|
+
], F = () => {
|
|
15
|
+
let [t, n] = T(""), [r, l] = T(!1), [f, y] = T(null), { data: b, isLoading: A, error: F, refetch: L } = a({ status: t || void 0 }), R = i(), z = e(), B = (b?.items ?? []).reduce((e, t) => e + t.minutes, 0), V = (b?.items ?? []).filter((e) => e.billable).reduce((e, t) => e + t.minutes, 0), H = (e, t, n) => e.mutate(t, {
|
|
16
|
+
onSuccess: () => y({
|
|
17
|
+
tone: "success",
|
|
18
|
+
text: `Time entry ${n}.`
|
|
19
|
+
}),
|
|
20
|
+
onError: (e) => y({
|
|
21
|
+
tone: "danger",
|
|
22
|
+
text: e instanceof Error ? e.message : "Failed."
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
return /* @__PURE__ */ M("div", {
|
|
26
|
+
className: "space-y-6",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ j(m, {
|
|
29
|
+
title: "Time",
|
|
30
|
+
subtitle: "Capture expert effort against engagements and matters — the raw material of billing and utilization.",
|
|
31
|
+
icon: /* @__PURE__ */ j(D, { className: "h-6 w-6" }),
|
|
32
|
+
actions: /* @__PURE__ */ M(N, {
|
|
33
|
+
onClick: () => l(!0),
|
|
34
|
+
children: [/* @__PURE__ */ j(O, { className: "mr-1.5 h-4 w-4" }), "Log time"]
|
|
35
|
+
})
|
|
36
|
+
}),
|
|
37
|
+
f && /* @__PURE__ */ j(o, {
|
|
38
|
+
tone: f.tone,
|
|
39
|
+
onClose: () => y(null),
|
|
40
|
+
children: f.text
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ M("div", {
|
|
43
|
+
className: "grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ j(d, {
|
|
46
|
+
label: "Entries",
|
|
47
|
+
value: b?.totalCount ?? 0
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ j(d, {
|
|
50
|
+
label: "Total hours",
|
|
51
|
+
value: w(B)
|
|
52
|
+
}),
|
|
53
|
+
/* @__PURE__ */ j(d, {
|
|
54
|
+
label: "Billable hours",
|
|
55
|
+
value: w(V),
|
|
56
|
+
tone: "accent"
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ j(d, {
|
|
59
|
+
label: "Non-billable",
|
|
60
|
+
value: w(B - V)
|
|
61
|
+
})
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
/* @__PURE__ */ j("div", {
|
|
65
|
+
className: "flex justify-end",
|
|
66
|
+
children: /* @__PURE__ */ M(p, {
|
|
67
|
+
value: t,
|
|
68
|
+
onChange: (e) => n(e.target.value),
|
|
69
|
+
className: "sm:w-52",
|
|
70
|
+
children: [/* @__PURE__ */ j("option", {
|
|
71
|
+
value: "",
|
|
72
|
+
children: "All statuses"
|
|
73
|
+
}), S(P)]
|
|
74
|
+
})
|
|
75
|
+
}),
|
|
76
|
+
A ? /* @__PURE__ */ j(u, {}) : F ? /* @__PURE__ */ j(c, {
|
|
77
|
+
error: F,
|
|
78
|
+
onRetry: () => void L()
|
|
79
|
+
}) : !b || b.items.length === 0 ? /* @__PURE__ */ j(s, {
|
|
80
|
+
title: "No time logged",
|
|
81
|
+
description: "Log your first time entry against an engagement or matter.",
|
|
82
|
+
icon: /* @__PURE__ */ j(D, { className: "h-8 w-8" }),
|
|
83
|
+
action: /* @__PURE__ */ M(N, {
|
|
84
|
+
onClick: () => l(!0),
|
|
85
|
+
children: [/* @__PURE__ */ j(O, { className: "mr-1.5 h-4 w-4" }), "Log time"]
|
|
86
|
+
})
|
|
87
|
+
}) : /* @__PURE__ */ j(h, {
|
|
88
|
+
title: "Time entries",
|
|
89
|
+
children: /* @__PURE__ */ M(_, { children: [/* @__PURE__ */ j("thead", { children: /* @__PURE__ */ M("tr", { children: [
|
|
90
|
+
/* @__PURE__ */ j(x, { children: "Date" }),
|
|
91
|
+
/* @__PURE__ */ j(x, { children: "Narrative" }),
|
|
92
|
+
/* @__PURE__ */ j(x, { children: "Hours" }),
|
|
93
|
+
/* @__PURE__ */ j(x, { children: "Billable" }),
|
|
94
|
+
/* @__PURE__ */ j(x, { children: "Status" }),
|
|
95
|
+
/* @__PURE__ */ j(x, {
|
|
96
|
+
className: "text-right",
|
|
97
|
+
children: "Actions"
|
|
98
|
+
})
|
|
99
|
+
] }) }), /* @__PURE__ */ j("tbody", { children: b.items.map((e) => /* @__PURE__ */ M("tr", {
|
|
100
|
+
className: "hover:bg-bg-sunken/50",
|
|
101
|
+
children: [
|
|
102
|
+
/* @__PURE__ */ j(v, { children: C(e.date) }),
|
|
103
|
+
/* @__PURE__ */ j(v, {
|
|
104
|
+
className: "max-w-xs truncate text-text-primary",
|
|
105
|
+
children: e.narrative || "—"
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ j(v, { children: w(e.minutes) }),
|
|
108
|
+
/* @__PURE__ */ j(v, { children: e.billable ? "Yes" : "No" }),
|
|
109
|
+
/* @__PURE__ */ j(v, { children: /* @__PURE__ */ j(g, { status: e.status }) }),
|
|
110
|
+
/* @__PURE__ */ j(v, {
|
|
111
|
+
className: "text-right",
|
|
112
|
+
children: /* @__PURE__ */ M("div", {
|
|
113
|
+
className: "flex justify-end gap-1",
|
|
114
|
+
children: [e.status === "DRAFT" && /* @__PURE__ */ M(N, {
|
|
115
|
+
variant: "ghost",
|
|
116
|
+
size: "sm",
|
|
117
|
+
disabled: R.isPending,
|
|
118
|
+
onClick: () => H(R, e.id, "submitted"),
|
|
119
|
+
children: [/* @__PURE__ */ j(k, { className: "mr-1 h-3.5 w-3.5" }), "Submit"]
|
|
120
|
+
}), e.status === "SUBMITTED" && /* @__PURE__ */ M(N, {
|
|
121
|
+
variant: "ghost",
|
|
122
|
+
size: "sm",
|
|
123
|
+
disabled: z.isPending,
|
|
124
|
+
onClick: () => H(z, e.id, "approved"),
|
|
125
|
+
children: [/* @__PURE__ */ j(E, { className: "mr-1 h-3.5 w-3.5" }), "Approve"]
|
|
126
|
+
})]
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
}, e.id)) })] })
|
|
131
|
+
}),
|
|
132
|
+
r && /* @__PURE__ */ j(I, {
|
|
133
|
+
onClose: () => l(!1),
|
|
134
|
+
onDone: () => y({
|
|
135
|
+
tone: "success",
|
|
136
|
+
text: "Time logged."
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
]
|
|
140
|
+
});
|
|
141
|
+
}, I = ({ onClose: e, onDone: i }) => {
|
|
142
|
+
let a = n(), s = t({ pagination: { limit: 100 } }), c = r({ pagination: { limit: 100 } }), [u, d] = T({
|
|
143
|
+
against: "engagement",
|
|
144
|
+
engagementId: "",
|
|
145
|
+
matterId: "",
|
|
146
|
+
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
147
|
+
hours: "",
|
|
148
|
+
minutes: "",
|
|
149
|
+
rate: "",
|
|
150
|
+
billable: !0,
|
|
151
|
+
narrative: ""
|
|
152
|
+
}), [m, h] = T(null), g = (e) => (t) => d((n) => ({
|
|
153
|
+
...n,
|
|
154
|
+
[e]: t.target.value
|
|
155
|
+
}));
|
|
156
|
+
return /* @__PURE__ */ j(f, {
|
|
157
|
+
open: !0,
|
|
158
|
+
onClose: e,
|
|
159
|
+
title: "Log time",
|
|
160
|
+
footer: /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j(N, {
|
|
161
|
+
variant: "outline",
|
|
162
|
+
onClick: e,
|
|
163
|
+
disabled: a.isPending,
|
|
164
|
+
children: "Cancel"
|
|
165
|
+
}), /* @__PURE__ */ j(N, {
|
|
166
|
+
onClick: () => {
|
|
167
|
+
h(null);
|
|
168
|
+
let t = (Number(u.hours) || 0) * 60 + (Number(u.minutes) || 0);
|
|
169
|
+
if (t <= 0) return h("Enter the time spent.");
|
|
170
|
+
let n = u.against === "engagement" ? { engagementId: u.engagementId } : { matterId: u.matterId };
|
|
171
|
+
if (u.against === "engagement" && !u.engagementId) return h("Select an engagement.");
|
|
172
|
+
if (u.against === "matter" && !u.matterId) return h("Select a matter.");
|
|
173
|
+
a.mutate({
|
|
174
|
+
...n,
|
|
175
|
+
date: new Date(u.date).toISOString(),
|
|
176
|
+
minutes: t,
|
|
177
|
+
rate: u.rate ? Number(u.rate) : 0,
|
|
178
|
+
billable: u.billable,
|
|
179
|
+
narrative: u.narrative.trim() || null
|
|
180
|
+
}, {
|
|
181
|
+
onSuccess: () => {
|
|
182
|
+
i(), e();
|
|
183
|
+
},
|
|
184
|
+
onError: (e) => h(e instanceof Error ? e.message : "Failed.")
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
disabled: a.isPending,
|
|
188
|
+
children: a.isPending ? "Logging…" : "Log time"
|
|
189
|
+
})] }),
|
|
190
|
+
children: /* @__PURE__ */ M("div", {
|
|
191
|
+
className: "space-y-3",
|
|
192
|
+
children: [
|
|
193
|
+
m && /* @__PURE__ */ j(o, {
|
|
194
|
+
tone: "danger",
|
|
195
|
+
children: m
|
|
196
|
+
}),
|
|
197
|
+
/* @__PURE__ */ j(l, {
|
|
198
|
+
label: "Against",
|
|
199
|
+
children: /* @__PURE__ */ M(p, {
|
|
200
|
+
value: u.against,
|
|
201
|
+
onChange: g("against"),
|
|
202
|
+
children: [/* @__PURE__ */ j("option", {
|
|
203
|
+
value: "engagement",
|
|
204
|
+
children: "Engagement"
|
|
205
|
+
}), /* @__PURE__ */ j("option", {
|
|
206
|
+
value: "matter",
|
|
207
|
+
children: "Matter"
|
|
208
|
+
})]
|
|
209
|
+
})
|
|
210
|
+
}),
|
|
211
|
+
u.against === "engagement" ? /* @__PURE__ */ j(l, {
|
|
212
|
+
label: "Engagement",
|
|
213
|
+
required: !0,
|
|
214
|
+
children: /* @__PURE__ */ M(p, {
|
|
215
|
+
value: u.engagementId,
|
|
216
|
+
onChange: g("engagementId"),
|
|
217
|
+
children: [/* @__PURE__ */ j("option", {
|
|
218
|
+
value: "",
|
|
219
|
+
children: "Select an engagement…"
|
|
220
|
+
}), s.data?.items.map((e) => /* @__PURE__ */ j("option", {
|
|
221
|
+
value: e.id,
|
|
222
|
+
children: e.name
|
|
223
|
+
}, e.id))]
|
|
224
|
+
})
|
|
225
|
+
}) : /* @__PURE__ */ j(l, {
|
|
226
|
+
label: "Matter",
|
|
227
|
+
required: !0,
|
|
228
|
+
children: /* @__PURE__ */ M(p, {
|
|
229
|
+
value: u.matterId,
|
|
230
|
+
onChange: g("matterId"),
|
|
231
|
+
children: [/* @__PURE__ */ j("option", {
|
|
232
|
+
value: "",
|
|
233
|
+
children: "Select a matter…"
|
|
234
|
+
}), c.data?.items.map((e) => /* @__PURE__ */ j("option", {
|
|
235
|
+
value: e.id,
|
|
236
|
+
children: e.title
|
|
237
|
+
}, e.id))]
|
|
238
|
+
})
|
|
239
|
+
}),
|
|
240
|
+
/* @__PURE__ */ M("div", {
|
|
241
|
+
className: "grid grid-cols-3 gap-3",
|
|
242
|
+
children: [
|
|
243
|
+
/* @__PURE__ */ j(l, {
|
|
244
|
+
label: "Date",
|
|
245
|
+
children: /* @__PURE__ */ j(b, {
|
|
246
|
+
type: "date",
|
|
247
|
+
value: u.date,
|
|
248
|
+
onChange: g("date")
|
|
249
|
+
})
|
|
250
|
+
}),
|
|
251
|
+
/* @__PURE__ */ j(l, {
|
|
252
|
+
label: "Hours",
|
|
253
|
+
children: /* @__PURE__ */ j(b, {
|
|
254
|
+
type: "number",
|
|
255
|
+
min: "0",
|
|
256
|
+
value: u.hours,
|
|
257
|
+
onChange: g("hours"),
|
|
258
|
+
placeholder: "2"
|
|
259
|
+
})
|
|
260
|
+
}),
|
|
261
|
+
/* @__PURE__ */ j(l, {
|
|
262
|
+
label: "Minutes",
|
|
263
|
+
children: /* @__PURE__ */ j(b, {
|
|
264
|
+
type: "number",
|
|
265
|
+
min: "0",
|
|
266
|
+
max: "59",
|
|
267
|
+
value: u.minutes,
|
|
268
|
+
onChange: g("minutes"),
|
|
269
|
+
placeholder: "30"
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
]
|
|
273
|
+
}),
|
|
274
|
+
/* @__PURE__ */ j(l, {
|
|
275
|
+
label: "Rate (per hour)",
|
|
276
|
+
children: /* @__PURE__ */ j(b, {
|
|
277
|
+
type: "number",
|
|
278
|
+
min: "0",
|
|
279
|
+
value: u.rate,
|
|
280
|
+
onChange: g("rate"),
|
|
281
|
+
placeholder: "450"
|
|
282
|
+
})
|
|
283
|
+
}),
|
|
284
|
+
/* @__PURE__ */ M("label", {
|
|
285
|
+
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
286
|
+
children: [/* @__PURE__ */ j("input", {
|
|
287
|
+
type: "checkbox",
|
|
288
|
+
checked: u.billable,
|
|
289
|
+
onChange: (e) => d((t) => ({
|
|
290
|
+
...t,
|
|
291
|
+
billable: e.target.checked
|
|
292
|
+
}))
|
|
293
|
+
}), "Billable"]
|
|
294
|
+
}),
|
|
295
|
+
/* @__PURE__ */ j(l, {
|
|
296
|
+
label: "Narrative",
|
|
297
|
+
children: /* @__PURE__ */ j(y, {
|
|
298
|
+
value: u.narrative,
|
|
299
|
+
onChange: g("narrative"),
|
|
300
|
+
placeholder: "Reviewed working papers and drafted findings memo…"
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
]
|
|
304
|
+
})
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
//#endregion
|
|
308
|
+
export { F as TimePage };
|
|
309
|
+
|
|
310
|
+
//# sourceMappingURL=TimePage.js.map
|
|
@@ -0,0 +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 } 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\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\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 label=\"Entries\" value={data?.totalCount ?? 0} />\n <MetricCard label=\"Total hours\" value={fmtMinutesAsHours(totalMinutes)} />\n <MetricCard\n label=\"Billable hours\"\n value={fmtMinutesAsHours(billableMinutes)}\n tone=\"accent\"\n />\n <MetricCard\n label=\"Non-billable\"\n value={fmtMinutesAsHours(totalMinutes - billableMinutes)}\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=\"hover:bg-bg-sunken/50\">\n <Td>{fmtDate(t.date)}</Td>\n <Td className=\"max-w-xs truncate text-text-primary\">{t.narrative || '—'}</Td>\n <Td>{fmtMinutesAsHours(t.minutes)}</Td>\n <Td>{t.billable ? 'Yes' : 'No'}</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 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 Billable\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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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import e from "../../node_modules/clsx/dist/clsx.js";
|
|
2
|
+
import { twMerge as t } from "../../node_modules/tailwind-merge/dist/bundle-mjs.js";
|
|
3
|
+
//#region src/proserviceworld/utils/cn.ts
|
|
4
|
+
function n(...n) {
|
|
5
|
+
return t(e(n));
|
|
6
|
+
}
|
|
7
|
+
//#endregion
|
|
8
|
+
export { n as cn };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=cn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cn.js","names":[],"sources":["../../../src/proserviceworld/utils/cn.ts"],"sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;AAGA,SAAgB,EAAG,GAAG,GAAsB;AAC1C,QAAO,EAAQ,EAAK,EAAO,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useProServiceWorld as e } from "../providers/ProServiceWorldProvider.js";
|
|
2
|
+
import { useNavigate as t } from "react-router-dom";
|
|
3
|
+
//#region src/proserviceworld/utils/nav.ts
|
|
4
|
+
function n() {
|
|
5
|
+
let n = t(), { basePath: r = "" } = e();
|
|
6
|
+
return (e) => n(`${r}/${e}`.replace(/\/{2,}/g, "/"));
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { n as useNav };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=nav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.js","names":[],"sources":["../../../src/proserviceworld/utils/nav.ts"],"sourcesContent":["import { useNavigate } from 'react-router-dom';\nimport { useProServiceWorld } from '../providers/ProServiceWorldProvider';\n\n/**\n * Navigate to a path relative to the MFE's basePath. Works both mounted in a\n * host shell (basePath set, shell BrowserRouter) and standalone (MemoryRouter).\n */\nexport function useNav(): (relative: string) => void {\n const navigate = useNavigate();\n const { basePath = '' } = useProServiceWorld();\n return (relative: string) => navigate(`${basePath}/${relative}`.replace(/\\/{2,}/g, '/'));\n}\n"],"mappings":";;;AAOA,SAAgB,IAAqC;CACnD,IAAM,IAAW,GAAa,EACxB,EAAE,cAAW,OAAO,GAAoB;AAC9C,SAAQ,MAAqB,EAAS,GAAG,EAAS,GAAG,IAAW,QAAQ,WAAW,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-proserviceworld",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.706.1",
|
|
4
4
|
"description": "Burdenoff ProServiceWorld Microfrontend",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
"dev": "vite",
|
|
25
25
|
"build": "tsc && vite build",
|
|
26
26
|
"lint": "eslint .",
|
|
27
|
+
"lint:sanity": "if [ \"${CI:-}\" = \"true\" ]; then bun run lint; else bun run lint -- --cache --cache-location .eslintcache; fi",
|
|
27
28
|
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
28
29
|
"type:check": "tsc --noEmit",
|
|
29
|
-
"sanity": "bun run codegen && bun run lint && bun run format && bun run type:check && bun run build",
|
|
30
|
+
"sanity": "bun run codegen && bun run lint:sanity && bun run format && bun run type:check && bun run build",
|
|
30
31
|
"schema:pull": "graphql-codegen --project schema-wspace && graphql-codegen --project schema-global",
|
|
31
32
|
"codegen": "graphql-codegen --project wspace && graphql-codegen --project global",
|
|
32
33
|
"codegen:all": "bun run schema:pull && bun run codegen",
|