@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.
@@ -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 { useState as T } from "react";
4
- import { CheckCircle2 as E, CircleSlash as D, Clock as O, ListChecks as k, Plus as A, Send as j, Wallet as M } from "lucide-react";
5
- import { Fragment as N, jsx as P, jsxs as F } from "react/jsx-runtime";
6
- import { Button as I } from "@burdenoff/fe-libs/ui";
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 L = [
9
+ var z = [
9
10
  "DRAFT",
10
11
  "SUBMITTED",
11
12
  "APPROVED",
12
13
  "REJECTED",
13
14
  "BILLED"
14
- ], R = ({ billable: e }) => e ? /* @__PURE__ */ F("span", {
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__ */ P("span", { className: "h-1.5 w-1.5 rounded-full bg-current opacity-70" }), "Billable"]
17
- }) : /* @__PURE__ */ F("span", {
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__ */ P("span", { className: "h-1.5 w-1.5 rounded-full bg-current opacity-60" }), "Non-billable"]
20
- }), z = () => {
21
- let [t, n] = T(""), [r, l] = T(!1), [f, y] = T(null), { data: b, isLoading: N, error: z, refetch: V } = a({ status: t || void 0 }), H = i(), U = e(), W = (b?.items ?? []).reduce((e, t) => e + t.minutes, 0), G = (b?.items ?? []).filter((e) => e.billable).reduce((e, t) => e + t.minutes, 0), K = W > 0 ? Math.round(G / W * 100) : 0, q = (e, t, n) => e.mutate(t, {
22
- onSuccess: () => y({
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) => y({
30
+ onError: (e) => U({
27
31
  tone: "danger",
28
32
  text: e instanceof Error ? e.message : "Failed."
29
33
  })
30
34
  });
31
- return /* @__PURE__ */ F("div", {
35
+ return /* @__PURE__ */ L("div", {
32
36
  className: "space-y-6",
33
37
  children: [
34
- /* @__PURE__ */ P(m, {
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__ */ P(O, { className: "h-6 w-6" }),
38
- actions: /* @__PURE__ */ F(I, {
39
- onClick: () => l(!0),
40
- children: [/* @__PURE__ */ P(A, { className: "mr-1.5 h-4 w-4" }), "Log time"]
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
- f && /* @__PURE__ */ P(o, {
44
- tone: f.tone,
45
- onClose: () => y(null),
46
- children: f.text
47
+ V && /* @__PURE__ */ I(o, {
48
+ tone: V.tone,
49
+ onClose: () => U(null),
50
+ children: V.text
47
51
  }),
48
- /* @__PURE__ */ F("div", {
52
+ /* @__PURE__ */ L("div", {
49
53
  className: "grid grid-cols-2 gap-3 sm:grid-cols-4",
50
54
  children: [
51
- /* @__PURE__ */ P(d, {
55
+ /* @__PURE__ */ I(d, {
52
56
  label: "Entries",
53
- value: b?.totalCount ?? 0,
54
- icon: /* @__PURE__ */ P(k, { className: "h-4 w-4" })
57
+ value: W?.totalCount ?? 0,
58
+ icon: /* @__PURE__ */ I(A, { className: "h-4 w-4" })
55
59
  }),
56
- /* @__PURE__ */ P(d, {
60
+ /* @__PURE__ */ I(d, {
57
61
  label: "Total hours",
58
- value: w(W),
59
- icon: /* @__PURE__ */ P(O, { className: "h-4 w-4" })
62
+ value: w(Z),
63
+ icon: /* @__PURE__ */ I(k, { className: "h-4 w-4" })
60
64
  }),
61
- /* @__PURE__ */ P(d, {
65
+ /* @__PURE__ */ I(d, {
62
66
  label: "Billable hours",
63
- value: w(G),
64
- hint: `${K}% utilization`,
65
- icon: /* @__PURE__ */ P(M, { className: "h-4 w-4" }),
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__ */ P(d, {
72
+ /* @__PURE__ */ I(d, {
69
73
  label: "Non-billable",
70
- value: w(W - G),
71
- icon: /* @__PURE__ */ P(D, { className: "h-4 w-4" })
74
+ value: w(Z - Q),
75
+ icon: /* @__PURE__ */ I(ee, { className: "h-4 w-4" })
72
76
  })
73
77
  ]
74
78
  }),
75
- /* @__PURE__ */ P("div", {
76
- className: "flex justify-end",
77
- children: /* @__PURE__ */ F(p, {
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__ */ P("option", {
99
+ children: [/* @__PURE__ */ I("option", {
82
100
  value: "",
83
101
  children: "All statuses"
84
- }), S(L)]
85
- })
102
+ }), S(z)]
103
+ })]
86
104
  }),
87
- N ? /* @__PURE__ */ P(u, {}) : z ? /* @__PURE__ */ P(c, {
88
- error: z,
89
- onRetry: () => void V()
90
- }) : !b || b.items.length === 0 ? /* @__PURE__ */ P(s, {
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__ */ P(O, { className: "h-8 w-8" }),
94
- action: /* @__PURE__ */ F(I, {
95
- onClick: () => l(!0),
96
- children: [/* @__PURE__ */ P(A, { className: "mr-1.5 h-4 w-4" }), "Log time"]
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__ */ P(h, {
116
+ }) : /* @__PURE__ */ I(h, {
99
117
  title: "Time entries",
100
- children: /* @__PURE__ */ F(_, { children: [/* @__PURE__ */ P("thead", { children: /* @__PURE__ */ F("tr", { children: [
101
- /* @__PURE__ */ P(x, { children: "Date" }),
102
- /* @__PURE__ */ P(x, { children: "Narrative" }),
103
- /* @__PURE__ */ P(x, { children: "Hours" }),
104
- /* @__PURE__ */ P(x, { children: "Billable" }),
105
- /* @__PURE__ */ P(x, { children: "Status" }),
106
- /* @__PURE__ */ P(x, {
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__ */ P("tbody", { children: b.items.map((e) => /* @__PURE__ */ F("tr", {
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__ */ P(v, {
131
+ /* @__PURE__ */ I(v, {
114
132
  className: "whitespace-nowrap",
115
133
  children: C(e.date)
116
134
  }),
117
- /* @__PURE__ */ P(v, {
135
+ /* @__PURE__ */ I(v, {
118
136
  className: "max-w-xs truncate text-text-primary",
119
137
  children: e.narrative || "—"
120
138
  }),
121
- /* @__PURE__ */ P(v, {
139
+ /* @__PURE__ */ I(v, {
122
140
  className: "whitespace-nowrap font-medium text-text-primary",
123
141
  children: w(e.minutes)
124
142
  }),
125
- /* @__PURE__ */ P(v, { children: /* @__PURE__ */ P(R, { billable: e.billable }) }),
126
- /* @__PURE__ */ P(v, { children: /* @__PURE__ */ P(g, { status: e.status }) }),
127
- /* @__PURE__ */ P(v, {
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__ */ F("div", {
147
+ children: /* @__PURE__ */ L("div", {
130
148
  className: "flex justify-end gap-1",
131
- children: [e.status === "DRAFT" && /* @__PURE__ */ F(I, {
149
+ children: [e.status === "DRAFT" && /* @__PURE__ */ L(R, {
132
150
  variant: "ghost",
133
151
  size: "sm",
134
- disabled: H.isPending,
135
- onClick: () => q(H, e.id, "submitted"),
136
- children: [/* @__PURE__ */ P(j, { className: "mr-1 h-3.5 w-3.5" }), "Submit"]
137
- }), e.status === "SUBMITTED" && /* @__PURE__ */ F(I, {
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: U.isPending,
141
- onClick: () => q(U, e.id, "approved"),
142
- children: [/* @__PURE__ */ P(E, { className: "mr-1 h-3.5 w-3.5" }), "Approve"]
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
- r && /* @__PURE__ */ P(B, {
150
- onClose: () => l(!1),
151
- onDone: () => y({
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
- }, B = ({ onClose: e, onDone: i }) => {
159
- let a = n(), s = t({ pagination: { limit: 100 } }), c = r({ pagination: { limit: 100 } }), [u, d] = T({
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] = T(null), g = (e) => (t) => d((n) => ({
187
+ }), [m, h] = D(null), g = (e) => (t) => d((n) => ({
170
188
  ...n,
171
189
  [e]: t.target.value
172
190
  }));
173
- return /* @__PURE__ */ P(f, {
191
+ return /* @__PURE__ */ I(f, {
174
192
  open: !0,
175
193
  onClose: e,
176
194
  title: "Log time",
177
- footer: /* @__PURE__ */ F(N, { children: [/* @__PURE__ */ P(I, {
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__ */ P(I, {
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__ */ F("div", {
225
+ children: /* @__PURE__ */ L("div", {
208
226
  className: "space-y-3",
209
227
  children: [
210
- m && /* @__PURE__ */ P(o, {
228
+ m && /* @__PURE__ */ I(o, {
211
229
  tone: "danger",
212
230
  children: m
213
231
  }),
214
- /* @__PURE__ */ P(l, {
232
+ /* @__PURE__ */ I(l, {
215
233
  label: "Against",
216
- children: /* @__PURE__ */ F(p, {
234
+ children: /* @__PURE__ */ L(p, {
217
235
  value: u.against,
218
236
  onChange: g("against"),
219
- children: [/* @__PURE__ */ P("option", {
237
+ children: [/* @__PURE__ */ I("option", {
220
238
  value: "engagement",
221
239
  children: "Engagement"
222
- }), /* @__PURE__ */ P("option", {
240
+ }), /* @__PURE__ */ I("option", {
223
241
  value: "matter",
224
242
  children: "Matter"
225
243
  })]
226
244
  })
227
245
  }),
228
- u.against === "engagement" ? /* @__PURE__ */ P(l, {
246
+ u.against === "engagement" ? /* @__PURE__ */ I(l, {
229
247
  label: "Engagement",
230
248
  required: !0,
231
- children: /* @__PURE__ */ F(p, {
249
+ children: /* @__PURE__ */ L(p, {
232
250
  value: u.engagementId,
233
251
  onChange: g("engagementId"),
234
- children: [/* @__PURE__ */ P("option", {
252
+ children: [/* @__PURE__ */ I("option", {
235
253
  value: "",
236
254
  children: "Select an engagement…"
237
- }), s.data?.items.map((e) => /* @__PURE__ */ P("option", {
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__ */ P(l, {
260
+ }) : /* @__PURE__ */ I(l, {
243
261
  label: "Matter",
244
262
  required: !0,
245
- children: /* @__PURE__ */ F(p, {
263
+ children: /* @__PURE__ */ L(p, {
246
264
  value: u.matterId,
247
265
  onChange: g("matterId"),
248
- children: [/* @__PURE__ */ P("option", {
266
+ children: [/* @__PURE__ */ I("option", {
249
267
  value: "",
250
268
  children: "Select a matter…"
251
- }), c.data?.items.map((e) => /* @__PURE__ */ P("option", {
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__ */ F("div", {
275
+ /* @__PURE__ */ L("div", {
258
276
  className: "grid grid-cols-3 gap-3",
259
277
  children: [
260
- /* @__PURE__ */ P(l, {
278
+ /* @__PURE__ */ I(l, {
261
279
  label: "Date",
262
- children: /* @__PURE__ */ P(b, {
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__ */ P(l, {
286
+ /* @__PURE__ */ I(l, {
269
287
  label: "Hours",
270
- children: /* @__PURE__ */ P(b, {
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__ */ P(l, {
296
+ /* @__PURE__ */ I(l, {
279
297
  label: "Minutes",
280
- children: /* @__PURE__ */ P(b, {
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__ */ P(l, {
309
+ /* @__PURE__ */ I(l, {
292
310
  label: "Rate (per hour)",
293
- children: /* @__PURE__ */ P(b, {
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__ */ F("label", {
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__ */ P("input", {
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__ */ F("span", {
328
+ }), /* @__PURE__ */ L("span", {
311
329
  className: "flex items-center gap-1.5",
312
- children: [/* @__PURE__ */ P(M, { className: "h-3.5 w-3.5 text-status-success-text" }), "Billable"]
330
+ children: [/* @__PURE__ */ I(P, { className: "h-3.5 w-3.5 text-status-success-text" }), "Billable"]
313
331
  })]
314
332
  }),
315
- /* @__PURE__ */ P(l, {
333
+ /* @__PURE__ */ I(l, {
316
334
  label: "Narrative",
317
- children: /* @__PURE__ */ P(y, {
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 { z as TimePage };
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;AA+ChC;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,EAAE,EAmTvB,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"}