@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.
Files changed (73) hide show
  1. package/dist/index.js +2 -1
  2. package/dist/node_modules/clsx/dist/clsx.js +1 -1
  3. package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
  4. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +130 -130
  5. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
  6. package/dist/proserviceworld/ProServiceWorldRoot.js +4 -2
  7. package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
  8. package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -1
  9. package/dist/proserviceworld/ProServiceWorldRoutes.js +95 -42
  10. package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
  11. package/dist/proserviceworld/components/DiscussionThread.js.map +1 -1
  12. package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts +8 -0
  13. package/dist/proserviceworld/components/FeatureFlaggedPage.d.ts.map +1 -0
  14. package/dist/proserviceworld/components/FeatureFlaggedPage.js +17 -0
  15. package/dist/proserviceworld/components/FeatureFlaggedPage.js.map +1 -0
  16. package/dist/proserviceworld/components/LoadMore.d.ts +12 -0
  17. package/dist/proserviceworld/components/LoadMore.d.ts.map +1 -0
  18. package/dist/proserviceworld/components/LoadMore.js +30 -0
  19. package/dist/proserviceworld/components/LoadMore.js.map +1 -0
  20. package/dist/proserviceworld/components/TagPicker.d.ts +11 -0
  21. package/dist/proserviceworld/components/TagPicker.d.ts.map +1 -0
  22. package/dist/proserviceworld/components/TagPicker.js +31 -0
  23. package/dist/proserviceworld/components/TagPicker.js.map +1 -0
  24. package/dist/proserviceworld/components/ui.js.map +1 -1
  25. package/dist/proserviceworld/hooks/useAnalytics.js.map +1 -1
  26. package/dist/proserviceworld/hooks/useEntityComments.js.map +1 -1
  27. package/dist/proserviceworld/hooks/useFiles.js.map +1 -1
  28. package/dist/proserviceworld/hooks/usePageViewAnalytics.d.ts +10 -0
  29. package/dist/proserviceworld/hooks/usePageViewAnalytics.d.ts.map +1 -0
  30. package/dist/proserviceworld/hooks/usePageViewAnalytics.js +24 -0
  31. package/dist/proserviceworld/hooks/usePageViewAnalytics.js.map +1 -0
  32. package/dist/proserviceworld/hooks/useProServiceWorldApi.js +47 -27
  33. package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
  34. package/dist/proserviceworld/i18n/index.d.ts +13 -0
  35. package/dist/proserviceworld/i18n/index.d.ts.map +1 -0
  36. package/dist/proserviceworld/i18n/index.js +171 -0
  37. package/dist/proserviceworld/i18n/index.js.map +1 -0
  38. package/dist/proserviceworld/index.d.ts +1 -0
  39. package/dist/proserviceworld/index.d.ts.map +1 -1
  40. package/dist/proserviceworld/index.js +5 -3
  41. package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
  42. package/dist/proserviceworld/pages/ClientDetailPage.js.map +1 -1
  43. package/dist/proserviceworld/pages/ClientsPage.d.ts.map +1 -1
  44. package/dist/proserviceworld/pages/ClientsPage.js +173 -121
  45. package/dist/proserviceworld/pages/ClientsPage.js.map +1 -1
  46. package/dist/proserviceworld/pages/CompliancePage.js.map +1 -1
  47. package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -1
  48. package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
  49. package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
  50. package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -1
  51. package/dist/proserviceworld/pages/EngagementsPage.d.ts.map +1 -1
  52. package/dist/proserviceworld/pages/EngagementsPage.js +168 -120
  53. package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -1
  54. package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -1
  55. package/dist/proserviceworld/pages/MattersPage.d.ts.map +1 -1
  56. package/dist/proserviceworld/pages/MattersPage.js +183 -144
  57. package/dist/proserviceworld/pages/MattersPage.js.map +1 -1
  58. package/dist/proserviceworld/pages/PracticeAreasPage.js.map +1 -1
  59. package/dist/proserviceworld/pages/TimePage.js.map +1 -1
  60. package/dist/proserviceworld/providers/ProServiceWorldProvider.js.map +1 -1
  61. package/dist/proserviceworld/services/assistantApi.js.map +1 -1
  62. package/dist/proserviceworld/services/createSandboxAssistantTransport.js.map +1 -1
  63. package/dist/proserviceworld/utils/cn.js +1 -1
  64. package/dist/proserviceworld/utils/cn.js.map +1 -1
  65. package/dist/proserviceworld/utils/native.d.ts +23 -0
  66. package/dist/proserviceworld/utils/native.d.ts.map +1 -0
  67. package/dist/proserviceworld/utils/native.js +14 -0
  68. package/dist/proserviceworld/utils/native.js.map +1 -0
  69. package/dist/proserviceworld/utils/nav.js.map +1 -1
  70. package/dist/proserviceworld/utils/navigation.js.map +1 -1
  71. package/dist/proserviceworld/utils/pageContext.js +14 -16
  72. package/dist/proserviceworld/utils/pageContext.js.map +1 -1
  73. 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 i } from "../utils/nav.js";
3
- import { Banner as a, Empty as o, ErrorState as s, Field as c, LoadingBlock as l, Modal as u, NativeSelect as d, PageHeader as f, SectionCard as p, StatusPill as m, TableWrap as h, Td as g, TextArea as _, TextInput as v, Th as y, enumOptions as b, fmtDate as x, fmtMoney as S, humanize as C } from "../components/ui.js";
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 { useState as T } from "react";
6
- import { Briefcase as E, ChevronRight as D, Plus as O, Search as k } from "lucide-react";
7
- import { Fragment as A, jsx as j, jsxs as M } from "react/jsx-runtime";
8
- import { Button as N } from "@burdenoff/fe-libs/ui";
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 P = [
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
- ], F = [
22
+ ], I = [
20
23
  "FIXED",
21
24
  "HOURLY",
22
25
  "RETAINER",
23
26
  "VALUE_BASED"
24
- ], I = (e) => e.split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0]?.toUpperCase() ?? "").join("") || "?", L = ({ name: e }) => /* @__PURE__ */ j("span", {
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: I(e)
28
- }), R = () => {
29
- let t = i(), { emitIntent: r, emitFact: c } = w(), [u, _] = T(""), [A, F] = T(""), [I, R] = T(""), [B, V] = T(!1), [H, U] = T(null), W = e({ pagination: { limit: 100 } }), { data: G, isLoading: K, error: q, refetch: J } = n({
30
- search: u.trim() || void 0,
31
- status: A || void 0,
32
- clientId: I || void 0
33
- });
34
- return /* @__PURE__ */ M("div", {
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__ */ j(f, {
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__ */ j(E, { className: "h-6 w-6" }),
41
- actions: /* @__PURE__ */ M(N, {
47
+ icon: /* @__PURE__ */ M(O, { className: "h-6 w-6" }),
48
+ actions: /* @__PURE__ */ N(P, {
42
49
  onClick: () => {
43
- r("psw.engagement.create_clicked"), V(!0);
50
+ T("light"), r("psw.engagement.create_clicked"), H(!0);
44
51
  },
45
- children: [/* @__PURE__ */ j(O, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
52
+ children: [/* @__PURE__ */ M(A, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
46
53
  })
47
54
  }),
48
- H && /* @__PURE__ */ j(a, {
55
+ U && /* @__PURE__ */ M(o, {
49
56
  tone: "success",
50
- onClose: () => U(null),
51
- children: H
57
+ onClose: () => W(null),
58
+ children: U
52
59
  }),
53
- /* @__PURE__ */ M("div", {
60
+ /* @__PURE__ */ N("div", {
54
61
  className: "flex flex-col gap-2 sm:flex-row sm:items-center",
55
62
  children: [
56
- /* @__PURE__ */ M("div", {
63
+ /* @__PURE__ */ N("div", {
57
64
  className: "relative flex-1",
58
- children: [/* @__PURE__ */ j(k, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ j(v, {
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: u,
61
- onChange: (e) => _(e.target.value),
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" && u.trim() && c("psw.search.performed", {
75
+ e.key === "Enter" && d.trim() && l("psw.search.performed", {
64
76
  entityType: "engagement",
65
- queryLength: u.trim().length
77
+ queryLength: d.trim().length
66
78
  });
67
79
  },
68
80
  className: "pl-9"
69
81
  })]
70
82
  }),
71
- /* @__PURE__ */ M(d, {
72
- value: A,
73
- onChange: (e) => F(e.target.value),
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__ */ j("option", {
92
+ children: [/* @__PURE__ */ M("option", {
76
93
  value: "",
77
94
  children: "All statuses"
78
- }), b(P)]
95
+ }), x(F)]
79
96
  }),
80
- /* @__PURE__ */ M(d, {
81
- value: I,
82
- onChange: (e) => R(e.target.value),
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__ */ j("option", {
106
+ children: [/* @__PURE__ */ M("option", {
85
107
  value: "",
86
108
  children: "All clients"
87
- }), W.data?.items.map((e) => /* @__PURE__ */ j("option", {
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
- K ? /* @__PURE__ */ j(l, {}) : q ? /* @__PURE__ */ j(s, {
95
- error: q,
96
- onRetry: () => void J()
97
- }) : !G || G.items.length === 0 ? /* @__PURE__ */ j(o, {
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__ */ j(E, { className: "h-8 w-8" }),
101
- action: /* @__PURE__ */ M(N, {
102
- onClick: () => V(!0),
103
- children: [/* @__PURE__ */ j(O, { className: "mr-1.5 h-4 w-4" }), "New engagement"]
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__ */ j(p, {
106
- title: `${G.totalCount} engagement${G.totalCount === 1 ? "" : "s"}`,
107
- children: /* @__PURE__ */ M(h, { children: [/* @__PURE__ */ j("thead", { children: /* @__PURE__ */ M("tr", { children: [
108
- /* @__PURE__ */ j(y, { children: "Engagement" }),
109
- /* @__PURE__ */ j(y, { children: "Client" }),
110
- /* @__PURE__ */ j(y, { children: "Fee" }),
111
- /* @__PURE__ */ j(y, { children: "Status" }),
112
- /* @__PURE__ */ j(y, { children: "Started" })
113
- ] }) }), /* @__PURE__ */ j("tbody", { children: G.items.map((e) => /* @__PURE__ */ M("tr", {
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__ */ M(g, { children: [/* @__PURE__ */ M("div", {
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__ */ j("span", { children: e.name }), /* @__PURE__ */ j(D, { className: "h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100" })]
120
- }), e.description ? /* @__PURE__ */ j("div", {
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__ */ j(g, { children: /* @__PURE__ */ M("span", {
147
+ /* @__PURE__ */ M(_, { children: /* @__PURE__ */ N("span", {
125
148
  className: "inline-flex items-center gap-2.5",
126
- children: [/* @__PURE__ */ j(L, { name: e.client?.name ?? "—" }), /* @__PURE__ */ j("span", {
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__ */ M(g, {
154
+ /* @__PURE__ */ N(_, {
132
155
  className: "whitespace-nowrap",
133
156
  children: [
134
- /* @__PURE__ */ j("span", {
157
+ /* @__PURE__ */ M("span", {
135
158
  className: "font-medium text-text-primary",
136
- children: S(e.feeAmount, e.currency)
159
+ children: ee(e.feeAmount, e.currency)
137
160
  }),
138
161
  " ",
139
- /* @__PURE__ */ M("span", {
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__ */ j(g, { children: /* @__PURE__ */ j(m, { status: e.status }) }),
146
- /* @__PURE__ */ j(g, {
168
+ /* @__PURE__ */ M(_, { children: /* @__PURE__ */ M(h, { status: e.status }) }),
169
+ /* @__PURE__ */ M(_, {
147
170
  className: "whitespace-nowrap",
148
- children: x(e.startDate)
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
- B && /* @__PURE__ */ j(z, {
154
- onClose: () => V(!1),
155
- onSaved: (e) => U(`${e} created.`)
201
+ V && /* @__PURE__ */ M(B, {
202
+ onClose: () => H(!1),
203
+ onSaved: (e) => W(`${e} created.`)
156
204
  })
157
205
  ]
158
206
  });
159
- }, z = ({ onClose: n, onSaved: i }) => {
160
- let o = t(), { emitFact: s } = w(), l = e({ pagination: { limit: 100 } }), f = r(), [p, m] = T({
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] = T(null), y = (e) => (t) => m((n) => ({
217
+ }), [h, g] = D(null), _ = (e) => (t) => m((n) => ({
170
218
  ...n,
171
219
  [e]: t.target.value
172
220
  }));
173
- return /* @__PURE__ */ j(u, {
221
+ return /* @__PURE__ */ M(d, {
174
222
  open: !0,
175
223
  onClose: n,
176
224
  title: "New engagement",
177
- footer: /* @__PURE__ */ M(A, { children: [/* @__PURE__ */ j(N, {
225
+ footer: /* @__PURE__ */ N(j, { children: [/* @__PURE__ */ M(P, {
178
226
  variant: "outline",
179
227
  onClick: n,
180
- disabled: o.isPending,
228
+ disabled: a.isPending,
181
229
  children: "Cancel"
182
- }), /* @__PURE__ */ j(N, {
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
- o.mutate({
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: o.isPending,
207
- children: o.isPending ? "Creating…" : "Create engagement"
254
+ disabled: a.isPending,
255
+ children: a.isPending ? "Creating…" : "Create engagement"
208
256
  })] }),
209
- children: /* @__PURE__ */ M("div", {
257
+ children: /* @__PURE__ */ N("div", {
210
258
  className: "space-y-3",
211
259
  children: [
212
- h && /* @__PURE__ */ j(a, {
260
+ h && /* @__PURE__ */ M(o, {
213
261
  tone: "danger",
214
262
  children: h
215
263
  }),
216
- /* @__PURE__ */ j(c, {
264
+ /* @__PURE__ */ M(l, {
217
265
  label: "Client",
218
266
  required: !0,
219
- children: /* @__PURE__ */ M(d, {
267
+ children: /* @__PURE__ */ N(f, {
220
268
  value: p.clientId,
221
- onChange: y("clientId"),
222
- children: [/* @__PURE__ */ j("option", {
269
+ onChange: _("clientId"),
270
+ children: [/* @__PURE__ */ M("option", {
223
271
  value: "",
224
272
  children: "Select a client…"
225
- }), l.data?.items.map((e) => /* @__PURE__ */ j("option", {
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__ */ j(c, {
279
+ /* @__PURE__ */ M(l, {
232
280
  label: "Name",
233
281
  required: !0,
234
- children: /* @__PURE__ */ j(v, {
282
+ children: /* @__PURE__ */ M(y, {
235
283
  value: p.name,
236
- onChange: y("name"),
284
+ onChange: _("name"),
237
285
  placeholder: "FY26 Statutory Audit"
238
286
  })
239
287
  }),
240
- /* @__PURE__ */ j(c, {
288
+ /* @__PURE__ */ M(l, {
241
289
  label: "Practice area",
242
- children: /* @__PURE__ */ M(d, {
290
+ children: /* @__PURE__ */ N(f, {
243
291
  value: p.practiceAreaId,
244
- onChange: y("practiceAreaId"),
245
- children: [/* @__PURE__ */ j("option", {
292
+ onChange: _("practiceAreaId"),
293
+ children: [/* @__PURE__ */ M("option", {
246
294
  value: "",
247
295
  children: "Unassigned"
248
- }), f.data?.map((e) => /* @__PURE__ */ j("option", {
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__ */ M("div", {
302
+ /* @__PURE__ */ N("div", {
255
303
  className: "grid grid-cols-3 gap-3",
256
304
  children: [
257
- /* @__PURE__ */ j(c, {
305
+ /* @__PURE__ */ M(l, {
258
306
  label: "Fee type",
259
- children: /* @__PURE__ */ j(d, {
307
+ children: /* @__PURE__ */ M(f, {
260
308
  value: p.feeArrangement,
261
- onChange: y("feeArrangement"),
262
- children: b(F)
309
+ onChange: _("feeArrangement"),
310
+ children: x(I)
263
311
  })
264
312
  }),
265
- /* @__PURE__ */ j(c, {
313
+ /* @__PURE__ */ M(l, {
266
314
  label: "Fee amount",
267
- children: /* @__PURE__ */ j(v, {
315
+ children: /* @__PURE__ */ M(y, {
268
316
  type: "number",
269
317
  min: "0",
270
318
  value: p.feeAmount,
271
- onChange: y("feeAmount"),
319
+ onChange: _("feeAmount"),
272
320
  placeholder: "50000"
273
321
  })
274
322
  }),
275
- /* @__PURE__ */ j(c, {
323
+ /* @__PURE__ */ M(l, {
276
324
  label: "Currency",
277
- children: /* @__PURE__ */ j(v, {
325
+ children: /* @__PURE__ */ M(y, {
278
326
  value: p.currency,
279
- onChange: y("currency"),
327
+ onChange: _("currency"),
280
328
  maxLength: 3
281
329
  })
282
330
  })
283
331
  ]
284
332
  }),
285
- /* @__PURE__ */ j(c, {
333
+ /* @__PURE__ */ M(l, {
286
334
  label: "Start date",
287
- children: /* @__PURE__ */ j(v, {
335
+ children: /* @__PURE__ */ M(y, {
288
336
  type: "date",
289
337
  value: p.startDate,
290
- onChange: y("startDate")
338
+ onChange: _("startDate")
291
339
  })
292
340
  }),
293
- /* @__PURE__ */ j(c, {
341
+ /* @__PURE__ */ M(l, {
294
342
  label: "Description",
295
- children: /* @__PURE__ */ j(_, {
343
+ children: /* @__PURE__ */ M(v, {
296
344
  value: p.description,
297
- onChange: y("description")
345
+ onChange: _("description")
298
346
  })
299
347
  })
300
348
  ]
@@ -302,6 +350,6 @@ var P = [
302
350
  });
303
351
  };
304
352
  //#endregion
305
- export { R as EngagementsPage };
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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"}