@burdenoff/microfe-proserviceworld 2026.709.2 → 2026.709.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node_modules/clsx/dist/clsx.js +1 -1
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +130 -130
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoot.js +4 -2
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/DiscussionThread.js.map +1 -1
- package/dist/proserviceworld/components/FeatureFlaggedPage.js.map +1 -1
- package/dist/proserviceworld/components/LoadMore.js.map +1 -1
- package/dist/proserviceworld/components/TagPicker.js.map +1 -1
- package/dist/proserviceworld/components/ui.js.map +1 -1
- package/dist/proserviceworld/hooks/useAnalytics.js.map +1 -1
- package/dist/proserviceworld/hooks/useEntityComments.js.map +1 -1
- package/dist/proserviceworld/hooks/useFiles.js.map +1 -1
- package/dist/proserviceworld/hooks/usePageViewAnalytics.js.map +1 -1
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
- package/dist/proserviceworld/i18n/index.d.ts.map +1 -1
- package/dist/proserviceworld/i18n/index.js +30 -1
- package/dist/proserviceworld/i18n/index.js.map +1 -1
- package/dist/proserviceworld/index.js +5 -4
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ClientDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/ClientsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/ClientsPage.js +1 -1
- package/dist/proserviceworld/pages/ClientsPage.js.map +1 -1
- package/dist/proserviceworld/pages/CompliancePage.js.map +1 -1
- package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/EngagementsPage.js +123 -122
- package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -1
- package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/MattersPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/MattersPage.js +124 -123
- package/dist/proserviceworld/pages/MattersPage.js.map +1 -1
- package/dist/proserviceworld/pages/PracticeAreasPage.js.map +1 -1
- package/dist/proserviceworld/pages/TimePage.js.map +1 -1
- package/dist/proserviceworld/providers/ProServiceWorldProvider.js.map +1 -1
- package/dist/proserviceworld/services/assistantApi.js.map +1 -1
- package/dist/proserviceworld/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/proserviceworld/utils/cn.js +1 -1
- package/dist/proserviceworld/utils/cn.js.map +1 -1
- package/dist/proserviceworld/utils/native.js.map +1 -1
- package/dist/proserviceworld/utils/nav.js.map +1 -1
- package/dist/proserviceworld/utils/navigation.js.map +1 -1
- package/dist/proserviceworld/utils/pageContext.js +14 -16
- package/dist/proserviceworld/utils/pageContext.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { useClients as e, useCreateEngagement as t, useEngagements as n, usePracticeAreas as r, useUpdateEngagement as i } from "../hooks/useProServiceWorldApi.js";
|
|
2
2
|
import { useNav as a } from "../utils/nav.js";
|
|
3
|
-
import { Banner as o, Empty as s, ErrorState as c, Field as l, LoadingBlock as u, Modal as d, NativeSelect as f, PageHeader as p, SectionCard as m, StatusPill as h, TableWrap as g, Td as _, TextArea as v, TextInput as y, Th as b, enumOptions as x, fmtDate as S, fmtMoney as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { Banner as o, Empty as s, ErrorState as c, Field as l, LoadingBlock as u, Modal as d, NativeSelect as f, PageHeader as p, SectionCard as m, StatusPill as h, TableWrap as g, Td as _, TextArea as v, TextInput as y, Th as b, enumOptions as x, fmtDate as S, fmtMoney as C, humanize as ee } from "../components/ui.js";
|
|
4
|
+
import { t as w } from "../i18n/index.js";
|
|
5
|
+
import { useAnalytics as T } from "../hooks/useAnalytics.js";
|
|
6
|
+
import { impactFeedback as E } from "../utils/native.js";
|
|
7
|
+
import { TagPicker as D } from "../components/TagPicker.js";
|
|
7
8
|
import { LoadMore as te } from "../components/LoadMore.js";
|
|
8
|
-
import { useMemo as
|
|
9
|
-
import { Briefcase as
|
|
10
|
-
import { Fragment as
|
|
11
|
-
import { Button as
|
|
9
|
+
import { useMemo as O, useState as k } from "react";
|
|
10
|
+
import { Briefcase as A, ChevronRight as j, Plus as M, Search as ne } from "lucide-react";
|
|
11
|
+
import { Fragment as N, jsx as P, jsxs as F } from "react/jsx-runtime";
|
|
12
|
+
import { Button as I } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
//#region src/proserviceworld/pages/EngagementsPage.tsx
|
|
13
|
-
var
|
|
14
|
+
var re = [
|
|
14
15
|
"PROPOSAL",
|
|
15
16
|
"ACCEPTED",
|
|
16
17
|
"IN_PROGRESS",
|
|
@@ -19,55 +20,55 @@ var F = [
|
|
|
19
20
|
"INVOICED",
|
|
20
21
|
"CLOSED",
|
|
21
22
|
"CANCELLED"
|
|
22
|
-
],
|
|
23
|
+
], L = [
|
|
23
24
|
"FIXED",
|
|
24
25
|
"HOURLY",
|
|
25
26
|
"RETAINER",
|
|
26
27
|
"VALUE_BASED"
|
|
27
|
-
],
|
|
28
|
+
], R = (e) => e.split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0]?.toUpperCase() ?? "").join("") || "?", ie = ({ name: e }) => /* @__PURE__ */ P("span", {
|
|
28
29
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-[10px] font-semibold text-accent ring-1 ring-inset ring-accent/15",
|
|
29
30
|
"aria-hidden": !0,
|
|
30
|
-
children:
|
|
31
|
-
}),
|
|
32
|
-
let t = a(), { emitIntent: r, emitFact: l } =
|
|
33
|
-
limit:
|
|
31
|
+
children: R(e)
|
|
32
|
+
}), z = 25, B = () => {
|
|
33
|
+
let t = a(), { emitIntent: r, emitFact: l } = T(), [d, v] = k(""), [N, L] = k(""), [R, B] = k(""), [H, U] = k(!1), [W, G] = k(null), [K, q] = k({
|
|
34
|
+
limit: z,
|
|
34
35
|
offset: 0
|
|
35
|
-
}),
|
|
36
|
+
}), J = e({ pagination: { limit: 100 } }), ae = i(), { data: Y, isLoading: X, error: Z, refetch: Q } = n({
|
|
36
37
|
search: d.trim() || void 0,
|
|
37
|
-
status:
|
|
38
|
-
clientId:
|
|
39
|
-
pagination:
|
|
40
|
-
}), $ =
|
|
41
|
-
return /* @__PURE__ */
|
|
38
|
+
status: N || void 0,
|
|
39
|
+
clientId: R || void 0,
|
|
40
|
+
pagination: K
|
|
41
|
+
}), $ = O(() => Y?.items ?? [], [Y]);
|
|
42
|
+
return /* @__PURE__ */ F("div", {
|
|
42
43
|
className: "space-y-6",
|
|
43
44
|
children: [
|
|
44
|
-
/* @__PURE__ */
|
|
45
|
-
title: "
|
|
46
|
-
subtitle: "
|
|
47
|
-
icon: /* @__PURE__ */
|
|
48
|
-
actions: /* @__PURE__ */
|
|
45
|
+
/* @__PURE__ */ P(p, {
|
|
46
|
+
title: w("psw.engagements.title"),
|
|
47
|
+
subtitle: w("psw.engagements.subtitle"),
|
|
48
|
+
icon: /* @__PURE__ */ P(A, { className: "h-6 w-6" }),
|
|
49
|
+
actions: /* @__PURE__ */ F(I, {
|
|
49
50
|
onClick: () => {
|
|
50
|
-
|
|
51
|
+
E("light"), r("psw.engagement.create_clicked"), U(!0);
|
|
51
52
|
},
|
|
52
|
-
children: [/* @__PURE__ */ M
|
|
53
|
+
children: [/* @__PURE__ */ P(M, { className: "mr-1.5 h-4 w-4" }), w("psw.engagements.new")]
|
|
53
54
|
})
|
|
54
55
|
}),
|
|
55
|
-
|
|
56
|
+
W && /* @__PURE__ */ P(o, {
|
|
56
57
|
tone: "success",
|
|
57
|
-
onClose: () =>
|
|
58
|
-
children:
|
|
58
|
+
onClose: () => G(null),
|
|
59
|
+
children: W
|
|
59
60
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
+
/* @__PURE__ */ F("div", {
|
|
61
62
|
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
62
63
|
children: [
|
|
63
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ F("div", {
|
|
64
65
|
className: "relative flex-1",
|
|
65
|
-
children: [/* @__PURE__ */
|
|
66
|
-
placeholder: "
|
|
66
|
+
children: [/* @__PURE__ */ P(ne, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ P(y, {
|
|
67
|
+
placeholder: w("psw.engagements.searchPlaceholder"),
|
|
67
68
|
value: d,
|
|
68
69
|
onChange: (e) => {
|
|
69
|
-
v(e.target.value),
|
|
70
|
-
limit:
|
|
70
|
+
v(e.target.value), q({
|
|
71
|
+
limit: z,
|
|
71
72
|
offset: 0
|
|
72
73
|
});
|
|
73
74
|
},
|
|
@@ -80,105 +81,105 @@ var F = [
|
|
|
80
81
|
className: "pl-9"
|
|
81
82
|
})]
|
|
82
83
|
}),
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
value:
|
|
84
|
+
/* @__PURE__ */ F(f, {
|
|
85
|
+
value: N,
|
|
85
86
|
onChange: (e) => {
|
|
86
|
-
|
|
87
|
-
limit:
|
|
87
|
+
L(e.target.value), q({
|
|
88
|
+
limit: z,
|
|
88
89
|
offset: 0
|
|
89
90
|
});
|
|
90
91
|
},
|
|
91
92
|
className: "sm:w-44",
|
|
92
|
-
children: [/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ P("option", {
|
|
93
94
|
value: "",
|
|
94
|
-
children: "
|
|
95
|
-
}), x(
|
|
95
|
+
children: w("psw.engagements.allStatuses")
|
|
96
|
+
}), x(re)]
|
|
96
97
|
}),
|
|
97
|
-
/* @__PURE__ */
|
|
98
|
-
value:
|
|
98
|
+
/* @__PURE__ */ F(f, {
|
|
99
|
+
value: R,
|
|
99
100
|
onChange: (e) => {
|
|
100
|
-
|
|
101
|
-
limit:
|
|
101
|
+
B(e.target.value), q({
|
|
102
|
+
limit: z,
|
|
102
103
|
offset: 0
|
|
103
104
|
});
|
|
104
105
|
},
|
|
105
106
|
className: "sm:w-48",
|
|
106
|
-
children: [/* @__PURE__ */
|
|
107
|
+
children: [/* @__PURE__ */ P("option", {
|
|
107
108
|
value: "",
|
|
108
|
-
children: "
|
|
109
|
-
}),
|
|
109
|
+
children: w("psw.engagements.allClients")
|
|
110
|
+
}), J.data?.items.map((e) => /* @__PURE__ */ P("option", {
|
|
110
111
|
value: e.id,
|
|
111
112
|
children: e.name
|
|
112
113
|
}, e.id))]
|
|
113
114
|
})
|
|
114
115
|
]
|
|
115
116
|
}),
|
|
116
|
-
X ? /* @__PURE__ */
|
|
117
|
+
X ? /* @__PURE__ */ P(u, {}) : Z ? /* @__PURE__ */ P(c, {
|
|
117
118
|
error: Z,
|
|
118
119
|
onRetry: () => void Q()
|
|
119
|
-
}) : !Y || Y.items.length === 0 ? /* @__PURE__ */
|
|
120
|
-
title: "
|
|
121
|
-
description: "
|
|
122
|
-
icon: /* @__PURE__ */
|
|
123
|
-
action: /* @__PURE__ */
|
|
124
|
-
onClick: () =>
|
|
125
|
-
children: [/* @__PURE__ */ M
|
|
120
|
+
}) : !Y || Y.items.length === 0 ? /* @__PURE__ */ P(s, {
|
|
121
|
+
title: w("psw.engagements.emptyTitle"),
|
|
122
|
+
description: w("psw.engagements.emptyDescription"),
|
|
123
|
+
icon: /* @__PURE__ */ P(A, { className: "h-8 w-8" }),
|
|
124
|
+
action: /* @__PURE__ */ F(I, {
|
|
125
|
+
onClick: () => U(!0),
|
|
126
|
+
children: [/* @__PURE__ */ P(M, { className: "mr-1.5 h-4 w-4" }), w("psw.engagements.new")]
|
|
126
127
|
})
|
|
127
|
-
}) : /* @__PURE__ */
|
|
128
|
+
}) : /* @__PURE__ */ F(m, {
|
|
128
129
|
title: `${Y.totalCount} engagement${Y.totalCount === 1 ? "" : "s"}`,
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
-
/* @__PURE__ */
|
|
131
|
-
/* @__PURE__ */
|
|
132
|
-
/* @__PURE__ */
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
-
/* @__PURE__ */
|
|
135
|
-
/* @__PURE__ */
|
|
136
|
-
] }) }), /* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ F(g, { children: [/* @__PURE__ */ P("thead", { children: /* @__PURE__ */ F("tr", { children: [
|
|
131
|
+
/* @__PURE__ */ P(b, { children: w("psw.engagements.tableEngagement") }),
|
|
132
|
+
/* @__PURE__ */ P(b, { children: w("psw.engagements.tableClient") }),
|
|
133
|
+
/* @__PURE__ */ P(b, { children: w("psw.engagements.tableFee") }),
|
|
134
|
+
/* @__PURE__ */ P(b, { children: w("psw.engagements.tableStatus") }),
|
|
135
|
+
/* @__PURE__ */ P(b, { children: w("psw.engagements.tableStarted") }),
|
|
136
|
+
/* @__PURE__ */ P(b, { children: w("psw.engagements.tableTags") })
|
|
137
|
+
] }) }), /* @__PURE__ */ P("tbody", { children: Y.items.map((e) => /* @__PURE__ */ F("tr", {
|
|
137
138
|
className: "group cursor-pointer transition-colors hover:bg-bg-sunken/60",
|
|
138
139
|
onClick: () => t(`engagements/${e.id}`),
|
|
139
140
|
children: [
|
|
140
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ F(_, { children: [/* @__PURE__ */ F("div", {
|
|
141
142
|
className: "flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent",
|
|
142
|
-
children: [/* @__PURE__ */
|
|
143
|
-
}), e.description ? /* @__PURE__ */
|
|
143
|
+
children: [/* @__PURE__ */ P("span", { children: e.name }), /* @__PURE__ */ P(j, { className: "h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100" })]
|
|
144
|
+
}), e.description ? /* @__PURE__ */ P("div", {
|
|
144
145
|
className: "mt-0.5 max-w-md truncate text-xs text-text-tertiary",
|
|
145
146
|
children: e.description
|
|
146
147
|
}) : null] }),
|
|
147
|
-
/* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ P(_, { children: /* @__PURE__ */ F("span", {
|
|
148
149
|
className: "inline-flex items-center gap-2.5",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
150
|
+
children: [/* @__PURE__ */ P(ie, { name: e.client?.name ?? "—" }), /* @__PURE__ */ P("span", {
|
|
150
151
|
className: "text-text-secondary",
|
|
151
152
|
children: e.client?.name ?? "—"
|
|
152
153
|
})]
|
|
153
154
|
}) }),
|
|
154
|
-
/* @__PURE__ */
|
|
155
|
+
/* @__PURE__ */ F(_, {
|
|
155
156
|
className: "whitespace-nowrap",
|
|
156
157
|
children: [
|
|
157
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ P("span", {
|
|
158
159
|
className: "font-medium text-text-primary",
|
|
159
|
-
children:
|
|
160
|
+
children: C(e.feeAmount, e.currency)
|
|
160
161
|
}),
|
|
161
162
|
" ",
|
|
162
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ F("span", {
|
|
163
164
|
className: "text-xs text-text-tertiary",
|
|
164
|
-
children: ["/ ",
|
|
165
|
+
children: ["/ ", ee(e.feeArrangement)]
|
|
165
166
|
})
|
|
166
167
|
]
|
|
167
168
|
}),
|
|
168
|
-
/* @__PURE__ */
|
|
169
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ P(_, { children: /* @__PURE__ */ P(h, { status: e.status }) }),
|
|
170
|
+
/* @__PURE__ */ P(_, {
|
|
170
171
|
className: "whitespace-nowrap",
|
|
171
172
|
children: S(e.startDate)
|
|
172
173
|
}),
|
|
173
|
-
/* @__PURE__ */
|
|
174
|
+
/* @__PURE__ */ P(_, { children: /* @__PURE__ */ P("div", {
|
|
174
175
|
onClick: (e) => e.stopPropagation(),
|
|
175
176
|
role: "presentation",
|
|
176
|
-
children: /* @__PURE__ */
|
|
177
|
+
children: /* @__PURE__ */ P(D, {
|
|
177
178
|
entityType: "PswEngagement",
|
|
178
179
|
entityId: e.id,
|
|
179
180
|
tags: e.tags ?? [],
|
|
180
181
|
onChange: async (t) => {
|
|
181
|
-
await
|
|
182
|
+
await ae.mutateAsync({
|
|
182
183
|
id: e.id,
|
|
183
184
|
input: { tags: t }
|
|
184
185
|
}), Q();
|
|
@@ -187,25 +188,25 @@ var F = [
|
|
|
187
188
|
})
|
|
188
189
|
}) })
|
|
189
190
|
]
|
|
190
|
-
}, e.id)) })] }), /* @__PURE__ */
|
|
191
|
+
}, e.id)) })] }), /* @__PURE__ */ P(te, {
|
|
191
192
|
hasMore: Y?.pageInfo?.hasMore ?? !1,
|
|
192
193
|
loading: X,
|
|
193
194
|
loadedCount: $.length,
|
|
194
195
|
totalCount: Y?.totalCount ?? $.length,
|
|
195
|
-
onLoadMore: () =>
|
|
196
|
-
limit:
|
|
197
|
-
offset: e.offset +
|
|
196
|
+
onLoadMore: () => q((e) => ({
|
|
197
|
+
limit: z,
|
|
198
|
+
offset: e.offset + z
|
|
198
199
|
}))
|
|
199
200
|
})]
|
|
200
201
|
}),
|
|
201
|
-
|
|
202
|
-
onClose: () =>
|
|
203
|
-
onSaved: (e) =>
|
|
202
|
+
H && /* @__PURE__ */ P(V, {
|
|
203
|
+
onClose: () => U(!1),
|
|
204
|
+
onSaved: (e) => G(`${e} created.`)
|
|
204
205
|
})
|
|
205
206
|
]
|
|
206
207
|
});
|
|
207
|
-
},
|
|
208
|
-
let a = t(), { emitFact: s } =
|
|
208
|
+
}, V = ({ onClose: n, onSaved: i }) => {
|
|
209
|
+
let a = t(), { emitFact: s } = T(), c = e({ pagination: { limit: 100 } }), u = r(), [p, m] = k({
|
|
209
210
|
clientId: "",
|
|
210
211
|
practiceAreaId: "",
|
|
211
212
|
name: "",
|
|
@@ -214,20 +215,20 @@ var F = [
|
|
|
214
215
|
feeAmount: "",
|
|
215
216
|
currency: "USD",
|
|
216
217
|
startDate: ""
|
|
217
|
-
}), [h, g] =
|
|
218
|
+
}), [h, g] = k(null), _ = (e) => (t) => m((n) => ({
|
|
218
219
|
...n,
|
|
219
220
|
[e]: t.target.value
|
|
220
221
|
}));
|
|
221
|
-
return /* @__PURE__ */
|
|
222
|
+
return /* @__PURE__ */ P(d, {
|
|
222
223
|
open: !0,
|
|
223
224
|
onClose: n,
|
|
224
|
-
title: "
|
|
225
|
-
footer: /* @__PURE__ */ N
|
|
225
|
+
title: w("psw.engagements.newTitle"),
|
|
226
|
+
footer: /* @__PURE__ */ F(N, { children: [/* @__PURE__ */ P(I, {
|
|
226
227
|
variant: "outline",
|
|
227
228
|
onClick: n,
|
|
228
229
|
disabled: a.isPending,
|
|
229
230
|
children: "Cancel"
|
|
230
|
-
}), /* @__PURE__ */
|
|
231
|
+
}), /* @__PURE__ */ P(I, {
|
|
231
232
|
onClick: () => {
|
|
232
233
|
if (g(null), !p.clientId) return g("Select a client.");
|
|
233
234
|
if (!p.name.trim()) return g("Engagement name is required.");
|
|
@@ -254,65 +255,65 @@ var F = [
|
|
|
254
255
|
disabled: a.isPending,
|
|
255
256
|
children: a.isPending ? "Creating…" : "Create engagement"
|
|
256
257
|
})] }),
|
|
257
|
-
children: /* @__PURE__ */
|
|
258
|
+
children: /* @__PURE__ */ F("div", {
|
|
258
259
|
className: "space-y-3",
|
|
259
260
|
children: [
|
|
260
|
-
h && /* @__PURE__ */
|
|
261
|
+
h && /* @__PURE__ */ P(o, {
|
|
261
262
|
tone: "danger",
|
|
262
263
|
children: h
|
|
263
264
|
}),
|
|
264
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ P(l, {
|
|
265
266
|
label: "Client",
|
|
266
267
|
required: !0,
|
|
267
|
-
children: /* @__PURE__ */
|
|
268
|
+
children: /* @__PURE__ */ F(f, {
|
|
268
269
|
value: p.clientId,
|
|
269
270
|
onChange: _("clientId"),
|
|
270
|
-
children: [/* @__PURE__ */
|
|
271
|
+
children: [/* @__PURE__ */ P("option", {
|
|
271
272
|
value: "",
|
|
272
273
|
children: "Select a client…"
|
|
273
|
-
}), c.data?.items.map((e) => /* @__PURE__ */
|
|
274
|
+
}), c.data?.items.map((e) => /* @__PURE__ */ P("option", {
|
|
274
275
|
value: e.id,
|
|
275
276
|
children: e.name
|
|
276
277
|
}, e.id))]
|
|
277
278
|
})
|
|
278
279
|
}),
|
|
279
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ P(l, {
|
|
280
281
|
label: "Name",
|
|
281
282
|
required: !0,
|
|
282
|
-
children: /* @__PURE__ */
|
|
283
|
+
children: /* @__PURE__ */ P(y, {
|
|
283
284
|
value: p.name,
|
|
284
285
|
onChange: _("name"),
|
|
285
286
|
placeholder: "FY26 Statutory Audit"
|
|
286
287
|
})
|
|
287
288
|
}),
|
|
288
|
-
/* @__PURE__ */
|
|
289
|
+
/* @__PURE__ */ P(l, {
|
|
289
290
|
label: "Practice area",
|
|
290
|
-
children: /* @__PURE__ */
|
|
291
|
+
children: /* @__PURE__ */ F(f, {
|
|
291
292
|
value: p.practiceAreaId,
|
|
292
293
|
onChange: _("practiceAreaId"),
|
|
293
|
-
children: [/* @__PURE__ */
|
|
294
|
+
children: [/* @__PURE__ */ P("option", {
|
|
294
295
|
value: "",
|
|
295
296
|
children: "Unassigned"
|
|
296
|
-
}), u.data?.map((e) => /* @__PURE__ */
|
|
297
|
+
}), u.data?.map((e) => /* @__PURE__ */ P("option", {
|
|
297
298
|
value: e.id,
|
|
298
299
|
children: e.name
|
|
299
300
|
}, e.id))]
|
|
300
301
|
})
|
|
301
302
|
}),
|
|
302
|
-
/* @__PURE__ */
|
|
303
|
+
/* @__PURE__ */ F("div", {
|
|
303
304
|
className: "grid grid-cols-3 gap-3",
|
|
304
305
|
children: [
|
|
305
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ P(l, {
|
|
306
307
|
label: "Fee type",
|
|
307
|
-
children: /* @__PURE__ */
|
|
308
|
+
children: /* @__PURE__ */ P(f, {
|
|
308
309
|
value: p.feeArrangement,
|
|
309
310
|
onChange: _("feeArrangement"),
|
|
310
|
-
children: x(
|
|
311
|
+
children: x(L)
|
|
311
312
|
})
|
|
312
313
|
}),
|
|
313
|
-
/* @__PURE__ */
|
|
314
|
+
/* @__PURE__ */ P(l, {
|
|
314
315
|
label: "Fee amount",
|
|
315
|
-
children: /* @__PURE__ */
|
|
316
|
+
children: /* @__PURE__ */ P(y, {
|
|
316
317
|
type: "number",
|
|
317
318
|
min: "0",
|
|
318
319
|
value: p.feeAmount,
|
|
@@ -320,9 +321,9 @@ var F = [
|
|
|
320
321
|
placeholder: "50000"
|
|
321
322
|
})
|
|
322
323
|
}),
|
|
323
|
-
/* @__PURE__ */
|
|
324
|
+
/* @__PURE__ */ P(l, {
|
|
324
325
|
label: "Currency",
|
|
325
|
-
children: /* @__PURE__ */
|
|
326
|
+
children: /* @__PURE__ */ P(y, {
|
|
326
327
|
value: p.currency,
|
|
327
328
|
onChange: _("currency"),
|
|
328
329
|
maxLength: 3
|
|
@@ -330,17 +331,17 @@ var F = [
|
|
|
330
331
|
})
|
|
331
332
|
]
|
|
332
333
|
}),
|
|
333
|
-
/* @__PURE__ */
|
|
334
|
+
/* @__PURE__ */ P(l, {
|
|
334
335
|
label: "Start date",
|
|
335
|
-
children: /* @__PURE__ */
|
|
336
|
+
children: /* @__PURE__ */ P(y, {
|
|
336
337
|
type: "date",
|
|
337
338
|
value: p.startDate,
|
|
338
339
|
onChange: _("startDate")
|
|
339
340
|
})
|
|
340
341
|
}),
|
|
341
|
-
/* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ P(l, {
|
|
342
343
|
label: "Description",
|
|
343
|
-
children: /* @__PURE__ */
|
|
344
|
+
children: /* @__PURE__ */ P(v, {
|
|
344
345
|
value: p.description,
|
|
345
346
|
onChange: _("description")
|
|
346
347
|
})
|
|
@@ -350,6 +351,6 @@ var F = [
|
|
|
350
351
|
});
|
|
351
352
|
};
|
|
352
353
|
//#endregion
|
|
353
|
-
export {
|
|
354
|
+
export { B as EngagementsPage };
|
|
354
355
|
|
|
355
356
|
//# sourceMappingURL=EngagementsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EngagementsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/EngagementsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, Briefcase, ChevronRight } from 'lucide-react';\nimport {\n useEngagements,\n useCreateEngagement,\n useUpdateEngagement,\n useClients,\n usePracticeAreas,\n type PswEngagementStatus,\n type PswEngagement,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { useAnalytics } from '../hooks/useAnalytics';\nimport { impactFeedback } from '../utils/native';\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 TableWrap,\n Th,\n Td,\n enumOptions,\n fmtMoney,\n fmtDate,\n humanize,\n} from '../components/ui';\nimport { TagPicker } from '../components/TagPicker';\nimport { LoadMore } from '../components/LoadMore';\n\nconst STATUSES: PswEngagementStatus[] = [\n 'PROPOSAL',\n 'ACCEPTED',\n 'IN_PROGRESS',\n 'REVIEW',\n 'DELIVERED',\n 'INVOICED',\n 'CLOSED',\n 'CANCELLED',\n];\nconst FEE_ARRANGEMENTS = ['FIXED', 'HOURLY', 'RETAINER', 'VALUE_BASED'] as const;\n\nconst initials = (name: string): string =>\n name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? '')\n .join('') || '?';\n\nconst ClientAvatar: FC<{ name: string }> = ({ name }) => (\n <span\n className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-[10px] font-semibold text-accent ring-1 ring-inset ring-accent/15\"\n aria-hidden\n >\n {initials(name)}\n </span>\n);\n\nconst PAGE_SIZE = 25;\n\nexport const EngagementsPage: FC = () => {\n const nav = useNav();\n const { emitIntent, emitFact } = useAnalytics();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<PswEngagementStatus | ''>('');\n const [clientFilter, setClientFilter] = useState('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<string | null>(null);\n const [pagination, setPagination] = useState({ limit: PAGE_SIZE, offset: 0 });\n const clients = useClients({ pagination: { limit: 100 } });\n const update = useUpdateEngagement();\n const { data, isLoading, error, refetch } = useEngagements({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n clientId: clientFilter || undefined,\n pagination,\n });\n\n const allItems = useMemo(() => data?.items ?? [], [data]);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Engagements\"\n subtitle=\"The commercial containers of contracted work — scope, fee arrangement, delivery lifecycle, and billing.\"\n icon={<Briefcase className=\"h-6 w-6\" />}\n actions={\n <Button\n onClick={() => {\n void impactFeedback('light');\n emitIntent('psw.engagement.create_clicked');\n setCreating(true);\n }}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New engagement\n </Button>\n }\n />\n\n {banner && (\n <Banner tone=\"success\" onClose={() => setBanner(null)}>\n {banner}\n </Banner>\n )}\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 engagements…\"\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && search.trim()) {\n emitFact('psw.search.performed', {\n entityType: 'engagement',\n queryLength: search.trim().length,\n });\n }\n }}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as PswEngagementStatus | '');\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n className=\"sm:w-44\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\n </NativeSelect>\n <NativeSelect\n value={clientFilter}\n onChange={(e) => {\n setClientFilter(e.target.value);\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n className=\"sm:w-48\"\n >\n <option value=\"\">All clients</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\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 engagements yet\"\n description=\"Scope your first engagement against a client to begin delivery and billing.\"\n icon={<Briefcase className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New engagement\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} engagement${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Engagement</Th>\n <Th>Client</Th>\n <Th>Fee</Th>\n <Th>Status</Th>\n <Th>Started</Th>\n <Th>Tags</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((e) => (\n <tr\n key={e.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-sunken/60\"\n onClick={() => nav(`engagements/${e.id}`)}\n >\n <Td>\n <div className=\"flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent\">\n <span>{e.name}</span>\n <ChevronRight className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100\" />\n </div>\n {e.description ? (\n <div className=\"mt-0.5 max-w-md truncate text-xs text-text-tertiary\">\n {e.description}\n </div>\n ) : null}\n </Td>\n <Td>\n <span className=\"inline-flex items-center gap-2.5\">\n <ClientAvatar name={e.client?.name ?? '—'} />\n <span className=\"text-text-secondary\">{e.client?.name ?? '—'}</span>\n </span>\n </Td>\n <Td className=\"whitespace-nowrap\">\n <span className=\"font-medium text-text-primary\">\n {fmtMoney(e.feeAmount, e.currency)}\n </span>{' '}\n <span className=\"text-xs text-text-tertiary\">\n / {humanize(e.feeArrangement)}\n </span>\n </Td>\n <Td>\n <StatusPill status={e.status} />\n </Td>\n <Td className=\"whitespace-nowrap\">{fmtDate(e.startDate)}</Td>\n <Td>\n <div onClick={(ev) => ev.stopPropagation()} role=\"presentation\">\n <TagPicker\n entityType=\"PswEngagement\"\n entityId={e.id}\n tags={e.tags ?? []}\n onChange={async (tags) => {\n await update.mutateAsync({ id: e.id, input: { tags } });\n void refetch();\n }}\n maxTags={5}\n />\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n <LoadMore\n hasMore={data?.pageInfo?.hasMore ?? false}\n loading={isLoading}\n loadedCount={allItems.length}\n totalCount={data?.totalCount ?? allItems.length}\n onLoadMore={() =>\n setPagination((p) => ({ limit: PAGE_SIZE, offset: p.offset + PAGE_SIZE }))\n }\n />\n </SectionCard>\n )}\n\n {creating && (\n <EngagementModal\n onClose={() => setCreating(false)}\n onSaved={(name) => setBanner(`${name} created.`)}\n />\n )}\n </div>\n );\n};\n\nconst EngagementModal: FC<{ onClose: () => void; onSaved: (name: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateEngagement();\n const { emitFact } = useAnalytics();\n const clients = useClients({ pagination: { limit: 100 } });\n const practiceAreas = usePracticeAreas();\n const [form, setForm] = useState({\n clientId: '',\n practiceAreaId: '',\n name: '',\n description: '',\n feeArrangement: 'FIXED',\n feeAmount: '',\n currency: 'USD',\n startDate: '',\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 if (!form.clientId) return setErr('Select a client.');\n if (!form.name.trim()) return setErr('Engagement name is required.');\n create.mutate(\n {\n clientId: form.clientId,\n practiceAreaId: form.practiceAreaId || null,\n name: form.name.trim(),\n description: form.description.trim() || null,\n feeArrangement: form.feeArrangement,\n feeAmount: form.feeAmount ? Number(form.feeAmount) : 0,\n currency: form.currency || 'USD',\n startDate: form.startDate ? new Date(form.startDate).toISOString() : null,\n },\n {\n onSuccess: (created) => {\n const engagement = (created as { pswCreateEngagement: PswEngagement })\n .pswCreateEngagement;\n emitFact('psw.engagement.created', {\n engagementId: engagement.id,\n clientId: form.clientId,\n });\n onSaved(form.name.trim());\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=\"New engagement\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={create.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={create.isPending}>\n {create.isPending ? 'Creating…' : 'Create engagement'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select a client…</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"FY26 Statutory Audit\" />\n </Field>\n <Field label=\"Practice area\">\n <NativeSelect value={form.practiceAreaId} onChange={set('practiceAreaId')}>\n <option value=\"\">Unassigned</option>\n {practiceAreas.data?.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Fee type\">\n <NativeSelect value={form.feeArrangement} onChange={set('feeArrangement')}>\n {enumOptions(FEE_ARRANGEMENTS)}\n </NativeSelect>\n </Field>\n <Field label=\"Fee amount\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.feeAmount}\n onChange={set('feeAmount')}\n placeholder=\"50000\"\n />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <Field label=\"Start date\">\n <TextInput type=\"date\" value={form.startDate} onChange={set('startDate')} />\n </Field>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAwCA,IAAM,IAAkC;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EACK,IAAmB;CAAC;CAAS;CAAU;CAAY;CAAc,EAEjE,KAAY,MAChB,EACG,MAAM,MAAM,CACZ,OAAO,QAAQ,CACf,MAAM,GAAG,EAAE,CACX,KAAK,MAAM,EAAE,IAAI,aAAa,IAAI,GAAG,CACrC,KAAK,GAAG,IAAI,KAEX,MAAsC,EAAE,cAC5C,kBAAC,QAAD;CACE,WAAU;CACV,eAAA;WAEC,EAAS,EAAK;CACV,CAAA,EAGH,IAAY,IAEL,UAA4B;CACvC,IAAM,IAAM,GAAQ,EACd,EAAE,eAAY,gBAAa,GAAc,EACzC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAc,KAAmB,EAAmC,GAAG,EACxE,CAAC,GAAc,KAAmB,EAAS,GAAG,EAC9C,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAQ,KAAa,EAAwB,KAAK,EACnD,CAAC,GAAY,KAAiB,EAAS;EAAE,OAAO;EAAW,QAAQ;EAAG,CAAC,EACvE,IAAU,EAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACpD,IAAS,GAAqB,EAC9B,EAAE,SAAM,cAAW,UAAO,eAAY,EAAe;EACzD,QAAQ,EAAO,MAAM,IAAI,KAAA;EACzB,QAAQ,KAAgB,KAAA;EACxB,UAAU,KAAgB,KAAA;EAC1B;EACD,CAAC,EAEI,IAAW,SAAc,GAAM,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC;AAEzD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAM;IACN,UAAS;IACT,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;IACvC,SACE,kBAAC,GAAD;KACE,eAAe;AAGb,MAFK,EAAe,QAAQ,EAC5B,EAAW,gCAAgC,EAC3C,EAAY,GAAK;;eAJrB,CAOE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,iBAE5B;;IAEX,CAAA;GAED,KACC,kBAAC,GAAD;IAAQ,MAAK;IAAU,eAAe,EAAU,KAAK;cAClD;IACM,CAAA;GAGX,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD,EAAQ,WAAU,2FAA4F,CAAA,EAC9G,kBAAC,GAAD;OACE,aAAY;OACZ,OAAO;OACP,WAAW,MAAM;AAEf,QADA,EAAU,EAAE,OAAO,MAAM,EACzB,EAAc;SAAE,OAAO;SAAW,QAAQ;SAAG,CAAC;;OAEhD,YAAY,MAAM;AAChB,QAAI,EAAE,QAAQ,WAAW,EAAO,MAAM,IACpC,EAAS,wBAAwB;SAC/B,YAAY;SACZ,aAAa,EAAO,MAAM,CAAC;SAC5B,CAAC;;OAGN,WAAU;OACV,CAAA,CACE;;KACN,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM;AAEf,OADA,EAAgB,EAAE,OAAO,MAAkC,EAC3D,EAAc;QAAE,OAAO;QAAW,QAAQ;QAAG,CAAC;;MAEhD,WAAU;gBANZ,CAQE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAqB,CAAA,EACrC,EAAY,EAAS,CACT;;KACf,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM;AAEf,OADA,EAAgB,EAAE,OAAO,MAAM,EAC/B,EAAc;QAAE,OAAO;QAAW,QAAQ;QAAG,CAAC;;MAEhD,WAAU;gBANZ,CAQE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAoB,CAAA,EACpC,EAAQ,MAAM,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACX;;GAEL,IACC,kBAAC,GAAD,EAAgB,CAAA,GACd,IACF,kBAAC,GAAD;IAAmB;IAAO,eAAe,KAAK,GAAS;IAAI,CAAA,GACzD,CAAC,KAAQ,EAAK,MAAM,WAAW,IACjC,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;IACZ,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;IACvC,QACE,kBAAC,GAAD;KAAQ,eAAe,EAAY,GAAK;eAAxC,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,iBAE5B;;IAEX,CAAA,GAEF,kBAAC,GAAD;IAAa,OAAO,GAAG,EAAK,WAAW,aAAa,EAAK,eAAe,IAAI,KAAK;cAAjF,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD,EAAA,UAAA;KACE,kBAAC,GAAD,EAAA,UAAI,cAAe,CAAA;KACnB,kBAAC,GAAD,EAAA,UAAI,UAAW,CAAA;KACf,kBAAC,GAAD,EAAA,UAAI,OAAQ,CAAA;KACZ,kBAAC,GAAD,EAAA,UAAI,UAAW,CAAA;KACf,kBAAC,GAAD,EAAA,UAAI,WAAY,CAAA;KAChB,kBAAC,GAAD,EAAA,UAAI,QAAS,CAAA;KACV,EAAA,CAAA,EACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAK,MAAM,KAAK,MACf,kBAAC,MAAD;KAEE,WAAU;KACV,eAAe,EAAI,eAAe,EAAE,KAAK;eAH3C;MAKE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAE,MAAY,CAAA,EACrB,kBAAC,GAAD,EAAc,WAAU,gGAAiG,CAAA,CACrH;UACL,EAAE,cACD,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAE;OACC,CAAA,GACJ,KACD,EAAA,CAAA;MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,IAAD,EAAc,MAAM,EAAE,QAAQ,QAAQ,KAAO,CAAA,EAC7C,kBAAC,QAAD;QAAM,WAAU;kBAAuB,EAAE,QAAQ,QAAQ;QAAW,CAAA,CAC/D;UACJ,CAAA;MACL,kBAAC,GAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,QAAD;SAAM,WAAU;mBACb,GAAS,EAAE,WAAW,EAAE,SAAS;SAC7B,CAAA;QAAC;QACR,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAA6C,MACxC,EAAS,EAAE,eAAe,CACxB;;QACJ;;MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAY,QAAQ,EAAE,QAAU,CAAA,EAC7B,CAAA;MACL,kBAAC,GAAD;OAAI,WAAU;iBAAqB,EAAQ,EAAE,UAAU;OAAM,CAAA;MAC7D,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,UAAU,MAAO,EAAG,iBAAiB;OAAE,MAAK;iBAC/C,kBAAC,GAAD;QACE,YAAW;QACX,UAAU,EAAE;QACZ,MAAM,EAAE,QAAQ,EAAE;QAClB,UAAU,OAAO,MAAS;AAEnB,SADL,MAAM,EAAO,YAAY;UAAE,IAAI,EAAE;UAAI,OAAO,EAAE,SAAM;UAAE,CAAC,EAClD,GAAS;;QAEhB,SAAS;QACT,CAAA;OACE,CAAA,EACH,CAAA;MACF;OA/CE,EAAE,GA+CJ,CACL,EACI,CAAA,CACE,EAAA,CAAA,EACZ,kBAAC,IAAD;KACE,SAAS,GAAM,UAAU,WAAW;KACpC,SAAS;KACT,aAAa,EAAS;KACtB,YAAY,GAAM,cAAc,EAAS;KACzC,kBACE,GAAe,OAAO;MAAE,OAAO;MAAW,QAAQ,EAAE,SAAS;MAAW,EAAE;KAE5E,CAAA,CACU;;GAGf,KACC,kBAAC,GAAD;IACE,eAAe,EAAY,GAAM;IACjC,UAAU,MAAS,EAAU,GAAG,EAAK,WAAW;IAChD,CAAA;GAEA;;GAIJ,KAAiF,EACrF,YACA,iBACI;CACJ,IAAM,IAAS,GAAqB,EAC9B,EAAE,gBAAa,GAAc,EAC7B,IAAU,EAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACpD,IAAgB,GAAkB,EAClC,CAAC,GAAM,KAAW,EAAS;EAC/B,UAAU;EACV,gBAAgB;EAChB,MAAM;EACN,aAAa;EACb,gBAAgB;EAChB,WAAW;EACX,UAAU;EACV,WAAW;EACZ,CAAC,EACI,CAAC,GAAK,KAAU,EAAwB,KAAK,EAC7C,KAAO,OAA0B,MACrC,GAAS,OAAO;EAAE,GAAG;GAAI,IAAI,EAAE,OAAO;EAAO,EAAE;AAiCjD,QACE,kBAAC,GAAD;EACE,MAAA;EACS;EACT,OAAM;EACN,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,SAAS;GAAS,UAAU,EAAO;aAAW;GAE/D,CAAA,EACT,kBAAC,GAAD;GAAQ,eAzCK;AAEnB,QADA,EAAO,KAAK,EACR,CAAC,EAAK,SAAU,QAAO,EAAO,mBAAmB;AACrD,QAAI,CAAC,EAAK,KAAK,MAAM,CAAE,QAAO,EAAO,+BAA+B;AACpE,MAAO,OACL;KACE,UAAU,EAAK;KACf,gBAAgB,EAAK,kBAAkB;KACvC,MAAM,EAAK,KAAK,MAAM;KACtB,aAAa,EAAK,YAAY,MAAM,IAAI;KACxC,gBAAgB,EAAK;KACrB,WAAW,EAAK,YAAY,OAAO,EAAK,UAAU,GAAG;KACrD,UAAU,EAAK,YAAY;KAC3B,WAAW,EAAK,YAAY,IAAI,KAAK,EAAK,UAAU,CAAC,aAAa,GAAG;KACtE,EACD;KACE,YAAY,MAAY;MACtB,IAAM,IAAc,EACjB;AAMH,MALA,EAAS,0BAA0B;OACjC,cAAc,EAAW;OACzB,UAAU,EAAK;OAChB,CAAC,EACF,EAAQ,EAAK,KAAK,MAAM,CAAC,EACzB,GAAS;;KAEX,UAAU,MAAM,EAAO,aAAa,QAAQ,EAAE,UAAU,UAAU;KACnE,CACF;;GAa8B,UAAU,EAAO;aACvC,EAAO,YAAY,cAAc;GAC3B,CAAA,CACR,EAAA,CAAA;YAGL,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,KAAO,kBAAC,GAAD;KAAQ,MAAK;eAAU;KAAa,CAAA;IAC5C,kBAAC,GAAD;KAAO,OAAM;KAAS,UAAA;eACpB,kBAAC,GAAD;MAAc,OAAO,EAAK;MAAU,UAAU,EAAI,WAAW;gBAA7D,CACE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAyB,CAAA,EACzC,EAAQ,MAAM,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACT,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;KAAO,UAAA;eAClB,kBAAC,GAAD;MAAW,OAAO,EAAK;MAAM,UAAU,EAAI,OAAO;MAAE,aAAY;MAAyB,CAAA;KACnF,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAc,OAAO,EAAK;MAAgB,UAAU,EAAI,iBAAiB;gBAAzE,CACE,kBAAC,UAAD;OAAQ,OAAM;iBAAG;OAAmB,CAAA,EACnC,EAAc,MAAM,KAAK,MACxB,kBAAC,UAAD;OAAmB,OAAO,EAAE;iBACzB,EAAE;OACI,EAFI,EAAE,GAEN,CACT,CACW;;KACT,CAAA;IACR,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QAAc,OAAO,EAAK;QAAgB,UAAU,EAAI,iBAAiB;kBACtE,EAAY,EAAiB;QACjB,CAAA;OACT,CAAA;MACR,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QACE,MAAK;QACL,KAAI;QACJ,OAAO,EAAK;QACZ,UAAU,EAAI,YAAY;QAC1B,aAAY;QACZ,CAAA;OACI,CAAA;MACR,kBAAC,GAAD;OAAO,OAAM;iBACX,kBAAC,GAAD;QAAW,OAAO,EAAK;QAAU,UAAU,EAAI,WAAW;QAAE,WAAW;QAAK,CAAA;OACtE,CAAA;MACJ;;IACN,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAW,MAAK;MAAO,OAAO,EAAK;MAAW,UAAU,EAAI,YAAY;MAAI,CAAA;KACtE,CAAA;IACR,kBAAC,GAAD;KAAO,OAAM;eACX,kBAAC,GAAD;MAAU,OAAO,EAAK;MAAa,UAAU,EAAI,cAAc;MAAI,CAAA;KAC7D,CAAA;IACJ;;EACA,CAAA"}
|
|
1
|
+
{"version":3,"file":"EngagementsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/EngagementsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, Briefcase, ChevronRight } from 'lucide-react';\nimport {\n useEngagements,\n useCreateEngagement,\n useUpdateEngagement,\n useClients,\n usePracticeAreas,\n type PswEngagementStatus,\n type PswEngagement,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { useAnalytics } from '../hooks/useAnalytics';\nimport { impactFeedback } from '../utils/native';\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 TableWrap,\n Th,\n Td,\n enumOptions,\n fmtMoney,\n fmtDate,\n humanize,\n} from '../components/ui';\nimport { TagPicker } from '../components/TagPicker';\nimport { LoadMore } from '../components/LoadMore';\nimport { t } from '../i18n';\n\nconst STATUSES: PswEngagementStatus[] = [\n 'PROPOSAL',\n 'ACCEPTED',\n 'IN_PROGRESS',\n 'REVIEW',\n 'DELIVERED',\n 'INVOICED',\n 'CLOSED',\n 'CANCELLED',\n];\nconst FEE_ARRANGEMENTS = ['FIXED', 'HOURLY', 'RETAINER', 'VALUE_BASED'] as const;\n\nconst initials = (name: string): string =>\n name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((w) => w[0]?.toUpperCase() ?? '')\n .join('') || '?';\n\nconst ClientAvatar: FC<{ name: string }> = ({ name }) => (\n <span\n className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-accent/10 text-[10px] font-semibold text-accent ring-1 ring-inset ring-accent/15\"\n aria-hidden\n >\n {initials(name)}\n </span>\n);\n\nconst PAGE_SIZE = 25;\n\nexport const EngagementsPage: FC = () => {\n const nav = useNav();\n const { emitIntent, emitFact } = useAnalytics();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<PswEngagementStatus | ''>('');\n const [clientFilter, setClientFilter] = useState('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<string | null>(null);\n const [pagination, setPagination] = useState({ limit: PAGE_SIZE, offset: 0 });\n const clients = useClients({ pagination: { limit: 100 } });\n const update = useUpdateEngagement();\n const { data, isLoading, error, refetch } = useEngagements({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n clientId: clientFilter || undefined,\n pagination,\n });\n\n const allItems = useMemo(() => data?.items ?? [], [data]);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title={t('psw.engagements.title')}\n subtitle={t('psw.engagements.subtitle')}\n icon={<Briefcase className=\"h-6 w-6\" />}\n actions={\n <Button\n onClick={() => {\n void impactFeedback('light');\n emitIntent('psw.engagement.create_clicked');\n setCreating(true);\n }}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {t('psw.engagements.new')}\n </Button>\n }\n />\n\n {banner && (\n <Banner tone=\"success\" onClose={() => setBanner(null)}>\n {banner}\n </Banner>\n )}\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={t('psw.engagements.searchPlaceholder')}\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && search.trim()) {\n emitFact('psw.search.performed', {\n entityType: 'engagement',\n queryLength: search.trim().length,\n });\n }\n }}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as PswEngagementStatus | '');\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n className=\"sm:w-44\"\n >\n <option value=\"\">{t('psw.engagements.allStatuses')}</option>\n {enumOptions(STATUSES)}\n </NativeSelect>\n <NativeSelect\n value={clientFilter}\n onChange={(e) => {\n setClientFilter(e.target.value);\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n className=\"sm:w-48\"\n >\n <option value=\"\">{t('psw.engagements.allClients')}</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\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={t('psw.engagements.emptyTitle')}\n description={t('psw.engagements.emptyDescription')}\n icon={<Briefcase className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n {t('psw.engagements.new')}\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} engagement${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>{t('psw.engagements.tableEngagement')}</Th>\n <Th>{t('psw.engagements.tableClient')}</Th>\n <Th>{t('psw.engagements.tableFee')}</Th>\n <Th>{t('psw.engagements.tableStatus')}</Th>\n <Th>{t('psw.engagements.tableStarted')}</Th>\n <Th>{t('psw.engagements.tableTags')}</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((e) => (\n <tr\n key={e.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-sunken/60\"\n onClick={() => nav(`engagements/${e.id}`)}\n >\n <Td>\n <div className=\"flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent\">\n <span>{e.name}</span>\n <ChevronRight className=\"h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100\" />\n </div>\n {e.description ? (\n <div className=\"mt-0.5 max-w-md truncate text-xs text-text-tertiary\">\n {e.description}\n </div>\n ) : null}\n </Td>\n <Td>\n <span className=\"inline-flex items-center gap-2.5\">\n <ClientAvatar name={e.client?.name ?? '—'} />\n <span className=\"text-text-secondary\">{e.client?.name ?? '—'}</span>\n </span>\n </Td>\n <Td className=\"whitespace-nowrap\">\n <span className=\"font-medium text-text-primary\">\n {fmtMoney(e.feeAmount, e.currency)}\n </span>{' '}\n <span className=\"text-xs text-text-tertiary\">\n / {humanize(e.feeArrangement)}\n </span>\n </Td>\n <Td>\n <StatusPill status={e.status} />\n </Td>\n <Td className=\"whitespace-nowrap\">{fmtDate(e.startDate)}</Td>\n <Td>\n <div onClick={(ev) => ev.stopPropagation()} role=\"presentation\">\n <TagPicker\n entityType=\"PswEngagement\"\n entityId={e.id}\n tags={e.tags ?? []}\n onChange={async (tags) => {\n await update.mutateAsync({ id: e.id, input: { tags } });\n void refetch();\n }}\n maxTags={5}\n />\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n <LoadMore\n hasMore={data?.pageInfo?.hasMore ?? false}\n loading={isLoading}\n loadedCount={allItems.length}\n totalCount={data?.totalCount ?? allItems.length}\n onLoadMore={() =>\n setPagination((p) => ({ limit: PAGE_SIZE, offset: p.offset + PAGE_SIZE }))\n }\n />\n </SectionCard>\n )}\n\n {creating && (\n <EngagementModal\n onClose={() => setCreating(false)}\n onSaved={(name) => setBanner(`${name} created.`)}\n />\n )}\n </div>\n );\n};\n\nconst EngagementModal: FC<{ onClose: () => void; onSaved: (name: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateEngagement();\n const { emitFact } = useAnalytics();\n const clients = useClients({ pagination: { limit: 100 } });\n const practiceAreas = usePracticeAreas();\n const [form, setForm] = useState({\n clientId: '',\n practiceAreaId: '',\n name: '',\n description: '',\n feeArrangement: 'FIXED',\n feeAmount: '',\n currency: 'USD',\n startDate: '',\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 if (!form.clientId) return setErr('Select a client.');\n if (!form.name.trim()) return setErr('Engagement name is required.');\n create.mutate(\n {\n clientId: form.clientId,\n practiceAreaId: form.practiceAreaId || null,\n name: form.name.trim(),\n description: form.description.trim() || null,\n feeArrangement: form.feeArrangement,\n feeAmount: form.feeAmount ? Number(form.feeAmount) : 0,\n currency: form.currency || 'USD',\n startDate: form.startDate ? new Date(form.startDate).toISOString() : null,\n },\n {\n onSuccess: (created) => {\n const engagement = (created as { pswCreateEngagement: PswEngagement })\n .pswCreateEngagement;\n emitFact('psw.engagement.created', {\n engagementId: engagement.id,\n clientId: form.clientId,\n });\n onSaved(form.name.trim());\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={t('psw.engagements.newTitle')}\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={create.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={create.isPending}>\n {create.isPending ? 'Creating…' : 'Create engagement'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select a client…</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"FY26 Statutory Audit\" />\n </Field>\n <Field label=\"Practice area\">\n <NativeSelect value={form.practiceAreaId} onChange={set('practiceAreaId')}>\n <option value=\"\">Unassigned</option>\n {practiceAreas.data?.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Fee type\">\n <NativeSelect value={form.feeArrangement} onChange={set('feeArrangement')}>\n {enumOptions(FEE_ARRANGEMENTS)}\n </NativeSelect>\n </Field>\n <Field label=\"Fee amount\">\n <TextInput\n type=\"number\"\n min=\"0\"\n value={form.feeAmount}\n onChange={set('feeAmount')}\n placeholder=\"50000\"\n />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <Field label=\"Start date\">\n <TextInput type=\"date\" value={form.startDate} onChange={set('startDate')} />\n </Field>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
|