@burdenoff/microfe-proserviceworld 2026.709.1 → 2026.709.3
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 -1
- 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.d.ts.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js +95 -42
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/DiscussionThread.js.map +1 -1
- package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts +8 -0
- package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts.map +1 -0
- package/dist/proserviceworld/components/FeatureFlaggedPage.js +17 -0
- package/dist/proserviceworld/components/FeatureFlaggedPage.js.map +1 -0
- package/dist/proserviceworld/components/LoadMore.d.ts +12 -0
- package/dist/proserviceworld/components/LoadMore.d.ts.map +1 -0
- package/dist/proserviceworld/components/LoadMore.js +30 -0
- package/dist/proserviceworld/components/LoadMore.js.map +1 -0
- package/dist/proserviceworld/components/TagPicker.d.ts +11 -0
- package/dist/proserviceworld/components/TagPicker.d.ts.map +1 -0
- package/dist/proserviceworld/components/TagPicker.js +31 -0
- package/dist/proserviceworld/components/TagPicker.js.map +1 -0
- 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.d.ts +10 -0
- package/dist/proserviceworld/hooks/usePageViewAnalytics.d.ts.map +1 -0
- package/dist/proserviceworld/hooks/usePageViewAnalytics.js +24 -0
- package/dist/proserviceworld/hooks/usePageViewAnalytics.js.map +1 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +47 -27
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
- package/dist/proserviceworld/i18n/index.d.ts +13 -0
- package/dist/proserviceworld/i18n/index.d.ts.map +1 -0
- package/dist/proserviceworld/i18n/index.js +171 -0
- package/dist/proserviceworld/i18n/index.js.map +1 -0
- package/dist/proserviceworld/index.d.ts +1 -0
- package/dist/proserviceworld/index.d.ts.map +1 -1
- package/dist/proserviceworld/index.js +5 -3
- 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 +173 -121
- 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 +168 -120
- 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 +183 -144
- 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.d.ts +23 -0
- package/dist/proserviceworld/utils/native.d.ts.map +1 -0
- package/dist/proserviceworld/utils/native.js +14 -0
- package/dist/proserviceworld/utils/native.js.map +1 -0
- 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,13 +1,16 @@
|
|
|
1
|
-
import { useClients as e, useCreateEngagement as t, useEngagements as n, usePracticeAreas as r } from "../hooks/useProServiceWorldApi.js";
|
|
2
|
-
import { useNav as
|
|
3
|
-
import { Banner as
|
|
1
|
+
import { useClients as e, useCreateEngagement as t, useEngagements as n, usePracticeAreas as r, useUpdateEngagement as i } from "../hooks/useProServiceWorldApi.js";
|
|
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 ee, humanize as C } from "../components/ui.js";
|
|
4
4
|
import { useAnalytics as w } from "../hooks/useAnalytics.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { impactFeedback as T } from "../utils/native.js";
|
|
6
|
+
import { TagPicker as E } from "../components/TagPicker.js";
|
|
7
|
+
import { LoadMore as te } from "../components/LoadMore.js";
|
|
8
|
+
import { useMemo as ne, useState as D } from "react";
|
|
9
|
+
import { Briefcase as O, ChevronRight as k, Plus as A, Search as re } from "lucide-react";
|
|
10
|
+
import { Fragment as j, jsx as M, jsxs as N } from "react/jsx-runtime";
|
|
11
|
+
import { Button as P } from "@burdenoff/fe-libs/ui";
|
|
9
12
|
//#region src/proserviceworld/pages/EngagementsPage.tsx
|
|
10
|
-
var
|
|
13
|
+
var F = [
|
|
11
14
|
"PROPOSAL",
|
|
12
15
|
"ACCEPTED",
|
|
13
16
|
"IN_PROGRESS",
|
|
@@ -16,148 +19,193 @@ var P = [
|
|
|
16
19
|
"INVOICED",
|
|
17
20
|
"CLOSED",
|
|
18
21
|
"CANCELLED"
|
|
19
|
-
],
|
|
22
|
+
], I = [
|
|
20
23
|
"FIXED",
|
|
21
24
|
"HOURLY",
|
|
22
25
|
"RETAINER",
|
|
23
26
|
"VALUE_BASED"
|
|
24
|
-
],
|
|
27
|
+
], L = (e) => e.split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0]?.toUpperCase() ?? "").join("") || "?", ie = ({ name: e }) => /* @__PURE__ */ M("span", {
|
|
25
28
|
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",
|
|
26
29
|
"aria-hidden": !0,
|
|
27
|
-
children:
|
|
28
|
-
}), R = () => {
|
|
29
|
-
let t =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
children: L(e)
|
|
31
|
+
}), R = 25, z = () => {
|
|
32
|
+
let t = a(), { emitIntent: r, emitFact: l } = w(), [d, v] = D(""), [j, I] = D(""), [L, z] = D(""), [V, H] = D(!1), [U, W] = D(null), [G, K] = D({
|
|
33
|
+
limit: R,
|
|
34
|
+
offset: 0
|
|
35
|
+
}), q = e({ pagination: { limit: 100 } }), J = i(), { data: Y, isLoading: X, error: Z, refetch: Q } = n({
|
|
36
|
+
search: d.trim() || void 0,
|
|
37
|
+
status: j || void 0,
|
|
38
|
+
clientId: L || void 0,
|
|
39
|
+
pagination: G
|
|
40
|
+
}), $ = ne(() => Y?.items ?? [], [Y]);
|
|
41
|
+
return /* @__PURE__ */ N("div", {
|
|
35
42
|
className: "space-y-6",
|
|
36
43
|
children: [
|
|
37
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ M(p, {
|
|
38
45
|
title: "Engagements",
|
|
39
46
|
subtitle: "The commercial containers of contracted work — scope, fee arrangement, delivery lifecycle, and billing.",
|
|
40
|
-
icon: /* @__PURE__ */
|
|
41
|
-
actions: /* @__PURE__ */
|
|
47
|
+
icon: /* @__PURE__ */ M(O, { className: "h-6 w-6" }),
|
|
48
|
+
actions: /* @__PURE__ */ N(P, {
|
|
42
49
|
onClick: () => {
|
|
43
|
-
r("psw.engagement.create_clicked"),
|
|
50
|
+
T("light"), r("psw.engagement.create_clicked"), H(!0);
|
|
44
51
|
},
|
|
45
|
-
children: [/* @__PURE__ */
|
|
52
|
+
children: [/* @__PURE__ */ M(A, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
|
|
46
53
|
})
|
|
47
54
|
}),
|
|
48
|
-
|
|
55
|
+
U && /* @__PURE__ */ M(o, {
|
|
49
56
|
tone: "success",
|
|
50
|
-
onClose: () =>
|
|
51
|
-
children:
|
|
57
|
+
onClose: () => W(null),
|
|
58
|
+
children: U
|
|
52
59
|
}),
|
|
53
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ N("div", {
|
|
54
61
|
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
55
62
|
children: [
|
|
56
|
-
/* @__PURE__ */
|
|
63
|
+
/* @__PURE__ */ N("div", {
|
|
57
64
|
className: "relative flex-1",
|
|
58
|
-
children: [/* @__PURE__ */
|
|
65
|
+
children: [/* @__PURE__ */ M(re, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ M(y, {
|
|
59
66
|
placeholder: "Search engagements…",
|
|
60
|
-
value:
|
|
61
|
-
onChange: (e) =>
|
|
67
|
+
value: d,
|
|
68
|
+
onChange: (e) => {
|
|
69
|
+
v(e.target.value), K({
|
|
70
|
+
limit: R,
|
|
71
|
+
offset: 0
|
|
72
|
+
});
|
|
73
|
+
},
|
|
62
74
|
onKeyDown: (e) => {
|
|
63
|
-
e.key === "Enter" &&
|
|
75
|
+
e.key === "Enter" && d.trim() && l("psw.search.performed", {
|
|
64
76
|
entityType: "engagement",
|
|
65
|
-
queryLength:
|
|
77
|
+
queryLength: d.trim().length
|
|
66
78
|
});
|
|
67
79
|
},
|
|
68
80
|
className: "pl-9"
|
|
69
81
|
})]
|
|
70
82
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
72
|
-
value:
|
|
73
|
-
onChange: (e) =>
|
|
83
|
+
/* @__PURE__ */ N(f, {
|
|
84
|
+
value: j,
|
|
85
|
+
onChange: (e) => {
|
|
86
|
+
I(e.target.value), K({
|
|
87
|
+
limit: R,
|
|
88
|
+
offset: 0
|
|
89
|
+
});
|
|
90
|
+
},
|
|
74
91
|
className: "sm:w-44",
|
|
75
|
-
children: [/* @__PURE__ */
|
|
92
|
+
children: [/* @__PURE__ */ M("option", {
|
|
76
93
|
value: "",
|
|
77
94
|
children: "All statuses"
|
|
78
|
-
}),
|
|
95
|
+
}), x(F)]
|
|
79
96
|
}),
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
value:
|
|
82
|
-
onChange: (e) =>
|
|
97
|
+
/* @__PURE__ */ N(f, {
|
|
98
|
+
value: L,
|
|
99
|
+
onChange: (e) => {
|
|
100
|
+
z(e.target.value), K({
|
|
101
|
+
limit: R,
|
|
102
|
+
offset: 0
|
|
103
|
+
});
|
|
104
|
+
},
|
|
83
105
|
className: "sm:w-48",
|
|
84
|
-
children: [/* @__PURE__ */
|
|
106
|
+
children: [/* @__PURE__ */ M("option", {
|
|
85
107
|
value: "",
|
|
86
108
|
children: "All clients"
|
|
87
|
-
}),
|
|
109
|
+
}), q.data?.items.map((e) => /* @__PURE__ */ M("option", {
|
|
88
110
|
value: e.id,
|
|
89
111
|
children: e.name
|
|
90
112
|
}, e.id))]
|
|
91
113
|
})
|
|
92
114
|
]
|
|
93
115
|
}),
|
|
94
|
-
|
|
95
|
-
error:
|
|
96
|
-
onRetry: () => void
|
|
97
|
-
}) : !
|
|
116
|
+
X ? /* @__PURE__ */ M(u, {}) : Z ? /* @__PURE__ */ M(c, {
|
|
117
|
+
error: Z,
|
|
118
|
+
onRetry: () => void Q()
|
|
119
|
+
}) : !Y || Y.items.length === 0 ? /* @__PURE__ */ M(s, {
|
|
98
120
|
title: "No engagements yet",
|
|
99
121
|
description: "Scope your first engagement against a client to begin delivery and billing.",
|
|
100
|
-
icon: /* @__PURE__ */
|
|
101
|
-
action: /* @__PURE__ */
|
|
102
|
-
onClick: () =>
|
|
103
|
-
children: [/* @__PURE__ */
|
|
122
|
+
icon: /* @__PURE__ */ M(O, { className: "h-8 w-8" }),
|
|
123
|
+
action: /* @__PURE__ */ N(P, {
|
|
124
|
+
onClick: () => H(!0),
|
|
125
|
+
children: [/* @__PURE__ */ M(A, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
|
|
104
126
|
})
|
|
105
|
-
}) : /* @__PURE__ */
|
|
106
|
-
title: `${
|
|
107
|
-
children: /* @__PURE__ */
|
|
108
|
-
/* @__PURE__ */
|
|
109
|
-
/* @__PURE__ */
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
-
/* @__PURE__ */
|
|
112
|
-
/* @__PURE__ */
|
|
113
|
-
|
|
127
|
+
}) : /* @__PURE__ */ N(m, {
|
|
128
|
+
title: `${Y.totalCount} engagement${Y.totalCount === 1 ? "" : "s"}`,
|
|
129
|
+
children: [/* @__PURE__ */ N(g, { children: [/* @__PURE__ */ M("thead", { children: /* @__PURE__ */ N("tr", { children: [
|
|
130
|
+
/* @__PURE__ */ M(b, { children: "Engagement" }),
|
|
131
|
+
/* @__PURE__ */ M(b, { children: "Client" }),
|
|
132
|
+
/* @__PURE__ */ M(b, { children: "Fee" }),
|
|
133
|
+
/* @__PURE__ */ M(b, { children: "Status" }),
|
|
134
|
+
/* @__PURE__ */ M(b, { children: "Started" }),
|
|
135
|
+
/* @__PURE__ */ M(b, { children: "Tags" })
|
|
136
|
+
] }) }), /* @__PURE__ */ M("tbody", { children: Y.items.map((e) => /* @__PURE__ */ N("tr", {
|
|
114
137
|
className: "group cursor-pointer transition-colors hover:bg-bg-sunken/60",
|
|
115
138
|
onClick: () => t(`engagements/${e.id}`),
|
|
116
139
|
children: [
|
|
117
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ N(_, { children: [/* @__PURE__ */ N("div", {
|
|
118
141
|
className: "flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
120
|
-
}), e.description ? /* @__PURE__ */
|
|
142
|
+
children: [/* @__PURE__ */ M("span", { children: e.name }), /* @__PURE__ */ M(k, { className: "h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100" })]
|
|
143
|
+
}), e.description ? /* @__PURE__ */ M("div", {
|
|
121
144
|
className: "mt-0.5 max-w-md truncate text-xs text-text-tertiary",
|
|
122
145
|
children: e.description
|
|
123
146
|
}) : null] }),
|
|
124
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ M(_, { children: /* @__PURE__ */ N("span", {
|
|
125
148
|
className: "inline-flex items-center gap-2.5",
|
|
126
|
-
children: [/* @__PURE__ */
|
|
149
|
+
children: [/* @__PURE__ */ M(ie, { name: e.client?.name ?? "—" }), /* @__PURE__ */ M("span", {
|
|
127
150
|
className: "text-text-secondary",
|
|
128
151
|
children: e.client?.name ?? "—"
|
|
129
152
|
})]
|
|
130
153
|
}) }),
|
|
131
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ N(_, {
|
|
132
155
|
className: "whitespace-nowrap",
|
|
133
156
|
children: [
|
|
134
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ M("span", {
|
|
135
158
|
className: "font-medium text-text-primary",
|
|
136
|
-
children:
|
|
159
|
+
children: ee(e.feeAmount, e.currency)
|
|
137
160
|
}),
|
|
138
161
|
" ",
|
|
139
|
-
/* @__PURE__ */
|
|
162
|
+
/* @__PURE__ */ N("span", {
|
|
140
163
|
className: "text-xs text-text-tertiary",
|
|
141
164
|
children: ["/ ", C(e.feeArrangement)]
|
|
142
165
|
})
|
|
143
166
|
]
|
|
144
167
|
}),
|
|
145
|
-
/* @__PURE__ */
|
|
146
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ M(_, { children: /* @__PURE__ */ M(h, { status: e.status }) }),
|
|
169
|
+
/* @__PURE__ */ M(_, {
|
|
147
170
|
className: "whitespace-nowrap",
|
|
148
|
-
children:
|
|
149
|
-
})
|
|
171
|
+
children: S(e.startDate)
|
|
172
|
+
}),
|
|
173
|
+
/* @__PURE__ */ M(_, { children: /* @__PURE__ */ M("div", {
|
|
174
|
+
onClick: (e) => e.stopPropagation(),
|
|
175
|
+
role: "presentation",
|
|
176
|
+
children: /* @__PURE__ */ M(E, {
|
|
177
|
+
entityType: "PswEngagement",
|
|
178
|
+
entityId: e.id,
|
|
179
|
+
tags: e.tags ?? [],
|
|
180
|
+
onChange: async (t) => {
|
|
181
|
+
await J.mutateAsync({
|
|
182
|
+
id: e.id,
|
|
183
|
+
input: { tags: t }
|
|
184
|
+
}), Q();
|
|
185
|
+
},
|
|
186
|
+
maxTags: 5
|
|
187
|
+
})
|
|
188
|
+
}) })
|
|
150
189
|
]
|
|
151
|
-
}, e.id)) })] })
|
|
190
|
+
}, e.id)) })] }), /* @__PURE__ */ M(te, {
|
|
191
|
+
hasMore: Y?.pageInfo?.hasMore ?? !1,
|
|
192
|
+
loading: X,
|
|
193
|
+
loadedCount: $.length,
|
|
194
|
+
totalCount: Y?.totalCount ?? $.length,
|
|
195
|
+
onLoadMore: () => K((e) => ({
|
|
196
|
+
limit: R,
|
|
197
|
+
offset: e.offset + R
|
|
198
|
+
}))
|
|
199
|
+
})]
|
|
152
200
|
}),
|
|
153
|
-
|
|
154
|
-
onClose: () =>
|
|
155
|
-
onSaved: (e) =>
|
|
201
|
+
V && /* @__PURE__ */ M(B, {
|
|
202
|
+
onClose: () => H(!1),
|
|
203
|
+
onSaved: (e) => W(`${e} created.`)
|
|
156
204
|
})
|
|
157
205
|
]
|
|
158
206
|
});
|
|
159
|
-
},
|
|
160
|
-
let
|
|
207
|
+
}, B = ({ onClose: n, onSaved: i }) => {
|
|
208
|
+
let a = t(), { emitFact: s } = w(), c = e({ pagination: { limit: 100 } }), u = r(), [p, m] = D({
|
|
161
209
|
clientId: "",
|
|
162
210
|
practiceAreaId: "",
|
|
163
211
|
name: "",
|
|
@@ -166,24 +214,24 @@ var P = [
|
|
|
166
214
|
feeAmount: "",
|
|
167
215
|
currency: "USD",
|
|
168
216
|
startDate: ""
|
|
169
|
-
}), [h, g] =
|
|
217
|
+
}), [h, g] = D(null), _ = (e) => (t) => m((n) => ({
|
|
170
218
|
...n,
|
|
171
219
|
[e]: t.target.value
|
|
172
220
|
}));
|
|
173
|
-
return /* @__PURE__ */
|
|
221
|
+
return /* @__PURE__ */ M(d, {
|
|
174
222
|
open: !0,
|
|
175
223
|
onClose: n,
|
|
176
224
|
title: "New engagement",
|
|
177
|
-
footer: /* @__PURE__ */
|
|
225
|
+
footer: /* @__PURE__ */ N(j, { children: [/* @__PURE__ */ M(P, {
|
|
178
226
|
variant: "outline",
|
|
179
227
|
onClick: n,
|
|
180
|
-
disabled:
|
|
228
|
+
disabled: a.isPending,
|
|
181
229
|
children: "Cancel"
|
|
182
|
-
}), /* @__PURE__ */
|
|
230
|
+
}), /* @__PURE__ */ M(P, {
|
|
183
231
|
onClick: () => {
|
|
184
232
|
if (g(null), !p.clientId) return g("Select a client.");
|
|
185
233
|
if (!p.name.trim()) return g("Engagement name is required.");
|
|
186
|
-
|
|
234
|
+
a.mutate({
|
|
187
235
|
clientId: p.clientId,
|
|
188
236
|
practiceAreaId: p.practiceAreaId || null,
|
|
189
237
|
name: p.name.trim(),
|
|
@@ -203,98 +251,98 @@ var P = [
|
|
|
203
251
|
onError: (e) => g(e instanceof Error ? e.message : "Failed.")
|
|
204
252
|
});
|
|
205
253
|
},
|
|
206
|
-
disabled:
|
|
207
|
-
children:
|
|
254
|
+
disabled: a.isPending,
|
|
255
|
+
children: a.isPending ? "Creating…" : "Create engagement"
|
|
208
256
|
})] }),
|
|
209
|
-
children: /* @__PURE__ */
|
|
257
|
+
children: /* @__PURE__ */ N("div", {
|
|
210
258
|
className: "space-y-3",
|
|
211
259
|
children: [
|
|
212
|
-
h && /* @__PURE__ */
|
|
260
|
+
h && /* @__PURE__ */ M(o, {
|
|
213
261
|
tone: "danger",
|
|
214
262
|
children: h
|
|
215
263
|
}),
|
|
216
|
-
/* @__PURE__ */
|
|
264
|
+
/* @__PURE__ */ M(l, {
|
|
217
265
|
label: "Client",
|
|
218
266
|
required: !0,
|
|
219
|
-
children: /* @__PURE__ */
|
|
267
|
+
children: /* @__PURE__ */ N(f, {
|
|
220
268
|
value: p.clientId,
|
|
221
|
-
onChange:
|
|
222
|
-
children: [/* @__PURE__ */
|
|
269
|
+
onChange: _("clientId"),
|
|
270
|
+
children: [/* @__PURE__ */ M("option", {
|
|
223
271
|
value: "",
|
|
224
272
|
children: "Select a client…"
|
|
225
|
-
}),
|
|
273
|
+
}), c.data?.items.map((e) => /* @__PURE__ */ M("option", {
|
|
226
274
|
value: e.id,
|
|
227
275
|
children: e.name
|
|
228
276
|
}, e.id))]
|
|
229
277
|
})
|
|
230
278
|
}),
|
|
231
|
-
/* @__PURE__ */
|
|
279
|
+
/* @__PURE__ */ M(l, {
|
|
232
280
|
label: "Name",
|
|
233
281
|
required: !0,
|
|
234
|
-
children: /* @__PURE__ */
|
|
282
|
+
children: /* @__PURE__ */ M(y, {
|
|
235
283
|
value: p.name,
|
|
236
|
-
onChange:
|
|
284
|
+
onChange: _("name"),
|
|
237
285
|
placeholder: "FY26 Statutory Audit"
|
|
238
286
|
})
|
|
239
287
|
}),
|
|
240
|
-
/* @__PURE__ */
|
|
288
|
+
/* @__PURE__ */ M(l, {
|
|
241
289
|
label: "Practice area",
|
|
242
|
-
children: /* @__PURE__ */
|
|
290
|
+
children: /* @__PURE__ */ N(f, {
|
|
243
291
|
value: p.practiceAreaId,
|
|
244
|
-
onChange:
|
|
245
|
-
children: [/* @__PURE__ */
|
|
292
|
+
onChange: _("practiceAreaId"),
|
|
293
|
+
children: [/* @__PURE__ */ M("option", {
|
|
246
294
|
value: "",
|
|
247
295
|
children: "Unassigned"
|
|
248
|
-
}),
|
|
296
|
+
}), u.data?.map((e) => /* @__PURE__ */ M("option", {
|
|
249
297
|
value: e.id,
|
|
250
298
|
children: e.name
|
|
251
299
|
}, e.id))]
|
|
252
300
|
})
|
|
253
301
|
}),
|
|
254
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ N("div", {
|
|
255
303
|
className: "grid grid-cols-3 gap-3",
|
|
256
304
|
children: [
|
|
257
|
-
/* @__PURE__ */
|
|
305
|
+
/* @__PURE__ */ M(l, {
|
|
258
306
|
label: "Fee type",
|
|
259
|
-
children: /* @__PURE__ */
|
|
307
|
+
children: /* @__PURE__ */ M(f, {
|
|
260
308
|
value: p.feeArrangement,
|
|
261
|
-
onChange:
|
|
262
|
-
children:
|
|
309
|
+
onChange: _("feeArrangement"),
|
|
310
|
+
children: x(I)
|
|
263
311
|
})
|
|
264
312
|
}),
|
|
265
|
-
/* @__PURE__ */
|
|
313
|
+
/* @__PURE__ */ M(l, {
|
|
266
314
|
label: "Fee amount",
|
|
267
|
-
children: /* @__PURE__ */
|
|
315
|
+
children: /* @__PURE__ */ M(y, {
|
|
268
316
|
type: "number",
|
|
269
317
|
min: "0",
|
|
270
318
|
value: p.feeAmount,
|
|
271
|
-
onChange:
|
|
319
|
+
onChange: _("feeAmount"),
|
|
272
320
|
placeholder: "50000"
|
|
273
321
|
})
|
|
274
322
|
}),
|
|
275
|
-
/* @__PURE__ */
|
|
323
|
+
/* @__PURE__ */ M(l, {
|
|
276
324
|
label: "Currency",
|
|
277
|
-
children: /* @__PURE__ */
|
|
325
|
+
children: /* @__PURE__ */ M(y, {
|
|
278
326
|
value: p.currency,
|
|
279
|
-
onChange:
|
|
327
|
+
onChange: _("currency"),
|
|
280
328
|
maxLength: 3
|
|
281
329
|
})
|
|
282
330
|
})
|
|
283
331
|
]
|
|
284
332
|
}),
|
|
285
|
-
/* @__PURE__ */
|
|
333
|
+
/* @__PURE__ */ M(l, {
|
|
286
334
|
label: "Start date",
|
|
287
|
-
children: /* @__PURE__ */
|
|
335
|
+
children: /* @__PURE__ */ M(y, {
|
|
288
336
|
type: "date",
|
|
289
337
|
value: p.startDate,
|
|
290
|
-
onChange:
|
|
338
|
+
onChange: _("startDate")
|
|
291
339
|
})
|
|
292
340
|
}),
|
|
293
|
-
/* @__PURE__ */
|
|
341
|
+
/* @__PURE__ */ M(l, {
|
|
294
342
|
label: "Description",
|
|
295
|
-
children: /* @__PURE__ */
|
|
343
|
+
children: /* @__PURE__ */ M(v, {
|
|
296
344
|
value: p.description,
|
|
297
|
-
onChange:
|
|
345
|
+
onChange: _("description")
|
|
298
346
|
})
|
|
299
347
|
})
|
|
300
348
|
]
|
|
@@ -302,6 +350,6 @@ var P = [
|
|
|
302
350
|
});
|
|
303
351
|
};
|
|
304
352
|
//#endregion
|
|
305
|
-
export {
|
|
353
|
+
export { z as EngagementsPage };
|
|
306
354
|
|
|
307
355
|
//# 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 { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, Briefcase, ChevronRight } from 'lucide-react';\nimport {\n useEngagements,\n useCreateEngagement,\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 {\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';\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\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 clients = useClients({ pagination: { limit: 100 } });\n const { data, isLoading, error, refetch } = useEngagements({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n clientId: clientFilter || undefined,\n });\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 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) => setSearch(e.target.value)}\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) => setStatusFilter(e.target.value as PswEngagementStatus | '')}\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) => setClientFilter(e.target.value)}\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 </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 </tr>\n ))}\n </tbody>\n </TableWrap>\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":"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"}
|
|
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":"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"}
|