@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
@@ -2,22 +2,26 @@ import { useArchiveClient as e, useClients as t, useCreateClient as n, useUpdate
2
2
  import { useNav as i } from "../utils/nav.js";
3
3
  import { cn as a } from "../utils/cn.js";
4
4
  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 } from "../components/ui.js";
5
- import { useAnalytics as C } from "../hooks/useAnalytics.js";
6
- import { useMemo as w, useState as T } from "react";
7
- import { Archive as E, Building2 as D, ChevronRight as O, Pencil as k, Plus as A, Search as j, User as M, Users as N } from "lucide-react";
8
- import { Fragment as P, jsx as F, jsxs as I } from "react/jsx-runtime";
9
- import { Button as L } from "@burdenoff/fe-libs/ui";
5
+ import { t as C } from "../i18n/index.js";
6
+ import { useAnalytics as w } from "../hooks/useAnalytics.js";
7
+ import { impactFeedback as T } from "../utils/native.js";
8
+ import { TagPicker as E } from "../components/TagPicker.js";
9
+ import { LoadMore as D } from "../components/LoadMore.js";
10
+ import { useMemo as O, useState as k } from "react";
11
+ import { Archive as A, Building2 as ee, ChevronRight as te, Pencil as ne, Plus as j, Search as M, User as N, Users as P } from "lucide-react";
12
+ import { Fragment as F, jsx as I, jsxs as L } from "react/jsx-runtime";
13
+ import { Button as R } from "@burdenoff/fe-libs/ui";
10
14
  //#region src/proserviceworld/pages/ClientsPage.tsx
11
- var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
15
+ var z = ["ORGANIZATION", "INDIVIDUAL"], B = [
12
16
  "PROSPECT",
13
17
  "ACTIVE",
14
18
  "INACTIVE",
15
19
  "ARCHIVED"
16
- ], B = (e) => e.split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0]?.toUpperCase() ?? "").join("") || "?", V = ({ name: e, className: t }) => /* @__PURE__ */ F("span", {
20
+ ], V = (e) => e.split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0]?.toUpperCase() ?? "").join("") || "?", H = ({ name: e, className: t }) => /* @__PURE__ */ I("span", {
17
21
  className: a("flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent ring-1 ring-inset ring-accent/15", t),
18
22
  "aria-hidden": !0,
19
- children: B(e)
20
- }), H = {
23
+ children: V(e)
24
+ }), U = {
21
25
  name: "",
22
26
  type: "ORGANIZATION",
23
27
  status: "PROSPECT",
@@ -26,124 +30,163 @@ var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
26
30
  contactEmail: "",
27
31
  contactPhone: "",
28
32
  notes: ""
29
- }, U = () => {
30
- let e = i(), { emitIntent: n, emitFact: r } = C(), [a, l] = T(""), [d, v] = T(""), [w, E] = T(null), [P, R] = T(!1), [B, H] = T(null), { data: U, isLoading: K, error: q, refetch: J } = t({
31
- search: a.trim() || void 0,
32
- status: d || void 0
33
- });
34
- return /* @__PURE__ */ I("div", {
33
+ }, W = 25, G = () => {
34
+ let e = i(), { emitIntent: n, emitFact: a } = w(), [l, d] = k(""), [v, A] = k(""), [F, z] = k(null), [V, U] = k(!1), [G, q] = k(null), [ie, J] = k({
35
+ limit: W,
36
+ offset: 0
37
+ }), { data: Y, isLoading: X, error: Z, refetch: Q } = t({
38
+ search: l.trim() || void 0,
39
+ status: v || void 0,
40
+ pagination: ie
41
+ }), ae = r(), $ = O(() => Y?.items ?? [], [Y]);
42
+ return /* @__PURE__ */ L("div", {
35
43
  className: "space-y-6",
36
44
  children: [
37
- /* @__PURE__ */ F(p, {
38
- title: "Clients",
39
- subtitle: "Every person or organization your firm serves — the record engagements, matters, documents and invoices hang off.",
40
- icon: /* @__PURE__ */ F(N, { className: "h-6 w-6" }),
41
- actions: /* @__PURE__ */ I(L, {
45
+ /* @__PURE__ */ I(p, {
46
+ title: C("psw.clients.title"),
47
+ subtitle: C("psw.clients.subtitle"),
48
+ icon: /* @__PURE__ */ I(P, { className: "h-6 w-6" }),
49
+ actions: /* @__PURE__ */ L(R, {
42
50
  onClick: () => {
43
- n("psw.client.create_clicked"), R(!0);
51
+ T("light"), n("psw.client.create_clicked"), U(!0);
44
52
  },
45
- children: [/* @__PURE__ */ F(A, { className: "mr-1.5 h-4 w-4" }), " New client"]
53
+ children: [
54
+ /* @__PURE__ */ I(j, { className: "mr-1.5 h-4 w-4" }),
55
+ " ",
56
+ C("psw.clients.new")
57
+ ]
46
58
  })
47
59
  }),
48
- B && /* @__PURE__ */ F(o, {
49
- tone: B.tone,
50
- onClose: () => H(null),
51
- children: B.text
60
+ G && /* @__PURE__ */ I(o, {
61
+ tone: G.tone,
62
+ onClose: () => q(null),
63
+ children: G.text
52
64
  }),
53
- /* @__PURE__ */ I("div", {
65
+ /* @__PURE__ */ L("div", {
54
66
  className: "flex flex-col gap-2 sm:flex-row sm:items-center",
55
- children: [/* @__PURE__ */ I("div", {
67
+ children: [/* @__PURE__ */ L("div", {
56
68
  className: "relative flex-1",
57
- children: [/* @__PURE__ */ F(j, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ F(y, {
58
- placeholder: "Search clients…",
59
- value: a,
60
- onChange: (e) => l(e.target.value),
69
+ children: [/* @__PURE__ */ I(M, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ I(y, {
70
+ placeholder: C("psw.clients.searchPlaceholder"),
71
+ value: l,
72
+ onChange: (e) => {
73
+ d(e.target.value), J({
74
+ limit: W,
75
+ offset: 0
76
+ });
77
+ },
61
78
  onKeyDown: (e) => {
62
- e.key === "Enter" && a.trim() && r("psw.search.performed", {
79
+ e.key === "Enter" && l.trim() && a("psw.search.performed", {
63
80
  entityType: "client",
64
- queryLength: a.trim().length
81
+ queryLength: l.trim().length
65
82
  });
66
83
  },
67
84
  className: "pl-9"
68
85
  })]
69
- }), /* @__PURE__ */ I(f, {
70
- value: d,
71
- onChange: (e) => v(e.target.value),
86
+ }), /* @__PURE__ */ L(f, {
87
+ value: v,
88
+ onChange: (e) => {
89
+ A(e.target.value), J({
90
+ limit: W,
91
+ offset: 0
92
+ });
93
+ },
72
94
  className: "sm:w-48",
73
- children: [/* @__PURE__ */ F("option", {
95
+ children: [/* @__PURE__ */ I("option", {
74
96
  value: "",
75
- children: "All statuses"
76
- }), x(z)]
97
+ children: C("psw.clients.allStatuses")
98
+ }), x(B)]
77
99
  })]
78
100
  }),
79
- K ? /* @__PURE__ */ F(u, {}) : q ? /* @__PURE__ */ F(c, {
80
- error: q,
81
- onRetry: () => void J()
82
- }) : !U || U.items.length === 0 ? /* @__PURE__ */ F(s, {
83
- title: "No clients yet",
84
- description: "Add your first client to start scoping engagements and matters.",
85
- icon: /* @__PURE__ */ F(N, { className: "h-8 w-8" }),
86
- action: /* @__PURE__ */ I(L, {
87
- onClick: () => R(!0),
88
- children: [/* @__PURE__ */ F(A, { className: "mr-1.5 h-4 w-4" }), " New client"]
101
+ X ? /* @__PURE__ */ I(u, {}) : Z ? /* @__PURE__ */ I(c, {
102
+ error: Z,
103
+ onRetry: () => void Q()
104
+ }) : !Y || Y.items.length === 0 ? /* @__PURE__ */ I(s, {
105
+ title: C("psw.clients.emptyTitle"),
106
+ description: C("psw.clients.emptyDescription"),
107
+ icon: /* @__PURE__ */ I(P, { className: "h-8 w-8" }),
108
+ action: /* @__PURE__ */ L(R, {
109
+ onClick: () => U(!0),
110
+ children: [
111
+ /* @__PURE__ */ I(j, { className: "mr-1.5 h-4 w-4" }),
112
+ " ",
113
+ C("psw.clients.new")
114
+ ]
89
115
  })
90
- }) : /* @__PURE__ */ F(m, {
91
- title: `${U.totalCount} client${U.totalCount === 1 ? "" : "s"}`,
92
- children: /* @__PURE__ */ I(g, { children: [/* @__PURE__ */ F("thead", { children: /* @__PURE__ */ I("tr", { children: [
93
- /* @__PURE__ */ F(b, { children: "Name" }),
94
- /* @__PURE__ */ F(b, { children: "Type" }),
95
- /* @__PURE__ */ F(b, { children: "Industry" }),
96
- /* @__PURE__ */ F(b, { children: "Status" }),
97
- /* @__PURE__ */ F(b, { children: "Added" }),
98
- /* @__PURE__ */ F(b, {
116
+ }) : /* @__PURE__ */ L(m, {
117
+ title: `${Y.totalCount} client${Y.totalCount === 1 ? "" : "s"}`,
118
+ children: [/* @__PURE__ */ L(g, { children: [/* @__PURE__ */ I("thead", { children: /* @__PURE__ */ L("tr", { children: [
119
+ /* @__PURE__ */ I(b, { children: C("psw.common.name") }),
120
+ /* @__PURE__ */ I(b, { children: C("psw.common.type") }),
121
+ /* @__PURE__ */ I(b, { children: "Industry" }),
122
+ /* @__PURE__ */ I(b, { children: C("psw.common.status") }),
123
+ /* @__PURE__ */ I(b, { children: C("psw.common.added") }),
124
+ /* @__PURE__ */ I(b, { children: "Tags" }),
125
+ /* @__PURE__ */ I(b, {
99
126
  className: "text-right",
100
- children: "Actions"
127
+ children: C("psw.common.actions")
101
128
  })
102
- ] }) }), /* @__PURE__ */ F("tbody", { children: U.items.map((t) => /* @__PURE__ */ I("tr", {
129
+ ] }) }), /* @__PURE__ */ I("tbody", { children: Y.items.map((t) => /* @__PURE__ */ L("tr", {
103
130
  className: "group cursor-pointer transition-colors hover:bg-bg-sunken/60",
104
131
  onClick: () => e(`clients/${t.id}`),
105
132
  children: [
106
- /* @__PURE__ */ F(_, { children: /* @__PURE__ */ I("div", {
133
+ /* @__PURE__ */ I(_, { children: /* @__PURE__ */ L("div", {
107
134
  className: "flex items-center gap-3",
108
- children: [/* @__PURE__ */ F(V, { name: t.name }), /* @__PURE__ */ I("div", {
135
+ children: [/* @__PURE__ */ I(H, { name: t.name }), /* @__PURE__ */ L("div", {
109
136
  className: "min-w-0",
110
- children: [/* @__PURE__ */ I("div", {
137
+ children: [/* @__PURE__ */ L("div", {
111
138
  className: "flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent",
112
- children: [/* @__PURE__ */ F("span", {
139
+ children: [/* @__PURE__ */ I("span", {
113
140
  className: "truncate",
114
141
  children: t.name
115
- }), /* @__PURE__ */ F(O, { className: "h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100" })]
116
- }), t.contactName ? /* @__PURE__ */ F("div", {
142
+ }), /* @__PURE__ */ I(te, { className: "h-3.5 w-3.5 shrink-0 text-text-tertiary opacity-0 transition-opacity group-hover:opacity-100" })]
143
+ }), t.contactName ? /* @__PURE__ */ I("div", {
117
144
  className: "truncate text-xs text-text-tertiary",
118
145
  children: t.contactName
119
146
  }) : null]
120
147
  })]
121
148
  }) }),
122
- /* @__PURE__ */ F(_, { children: /* @__PURE__ */ I("span", {
149
+ /* @__PURE__ */ I(_, { children: /* @__PURE__ */ L("span", {
123
150
  className: "inline-flex items-center gap-1.5 text-text-secondary",
124
- children: [t.type === "ORGANIZATION" ? /* @__PURE__ */ F(D, { className: "h-3.5 w-3.5 text-text-tertiary" }) : /* @__PURE__ */ F(M, { className: "h-3.5 w-3.5 text-text-tertiary" }), t.type === "ORGANIZATION" ? "Organization" : "Individual"]
151
+ children: [t.type === "ORGANIZATION" ? /* @__PURE__ */ I(ee, { className: "h-3.5 w-3.5 text-text-tertiary" }) : /* @__PURE__ */ I(N, { className: "h-3.5 w-3.5 text-text-tertiary" }), t.type === "ORGANIZATION" ? "Organization" : "Individual"]
125
152
  }) }),
126
- /* @__PURE__ */ F(_, { children: t.industry || "—" }),
127
- /* @__PURE__ */ F(_, { children: /* @__PURE__ */ F(h, { status: t.status }) }),
128
- /* @__PURE__ */ F(_, {
153
+ /* @__PURE__ */ I(_, { children: t.industry || "—" }),
154
+ /* @__PURE__ */ I(_, { children: /* @__PURE__ */ I(h, { status: t.status }) }),
155
+ /* @__PURE__ */ I(_, {
129
156
  className: "whitespace-nowrap",
130
157
  children: S(t.createdAt)
131
158
  }),
132
- /* @__PURE__ */ F(_, {
159
+ /* @__PURE__ */ I(_, { children: /* @__PURE__ */ I("div", {
160
+ onClick: (e) => e.stopPropagation(),
161
+ role: "presentation",
162
+ children: /* @__PURE__ */ I(E, {
163
+ entityType: "PswClient",
164
+ entityId: t.id,
165
+ tags: t.tags ?? [],
166
+ onChange: async (e) => {
167
+ await ae.mutateAsync({
168
+ id: t.id,
169
+ input: { tags: e }
170
+ }), Q();
171
+ },
172
+ maxTags: 5
173
+ })
174
+ }) }),
175
+ /* @__PURE__ */ I(_, {
133
176
  className: "text-right",
134
- children: /* @__PURE__ */ I("div", {
177
+ children: /* @__PURE__ */ L("div", {
135
178
  className: "flex justify-end gap-1",
136
179
  onClick: (e) => e.stopPropagation(),
137
180
  role: "presentation",
138
- children: [/* @__PURE__ */ F(L, {
181
+ children: [/* @__PURE__ */ I(R, {
139
182
  variant: "ghost",
140
183
  size: "sm",
141
- onClick: () => E(t),
184
+ onClick: () => z(t),
142
185
  "aria-label": "Edit",
143
- children: /* @__PURE__ */ F(k, { className: "h-4 w-4" })
144
- }), t.status !== "ARCHIVED" && /* @__PURE__ */ F(W, {
186
+ children: /* @__PURE__ */ I(ne, { className: "h-4 w-4" })
187
+ }), t.status !== "ARCHIVED" && /* @__PURE__ */ I(re, {
145
188
  id: t.id,
146
- onDone: (e) => H(e ? {
189
+ onDone: (e) => q(e ? {
147
190
  tone: "success",
148
191
  text: `${t.name} archived.`
149
192
  } : {
@@ -154,28 +197,37 @@ var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
154
197
  })
155
198
  })
156
199
  ]
157
- }, t.id)) })] })
200
+ }, t.id)) })] }), /* @__PURE__ */ I(D, {
201
+ hasMore: Y?.pageInfo?.hasMore ?? !1,
202
+ loading: X,
203
+ loadedCount: $.length,
204
+ totalCount: Y?.totalCount ?? $.length,
205
+ onLoadMore: () => J((e) => ({
206
+ limit: W,
207
+ offset: e.offset + W
208
+ }))
209
+ })]
158
210
  }),
159
- P && /* @__PURE__ */ F(G, {
160
- onClose: () => R(!1),
161
- onSaved: (e) => H({
211
+ V && /* @__PURE__ */ I(K, {
212
+ onClose: () => U(!1),
213
+ onSaved: (e) => q({
162
214
  tone: "success",
163
215
  text: `${e} created.`
164
216
  })
165
217
  }),
166
- w && /* @__PURE__ */ F(G, {
167
- client: w,
168
- onClose: () => E(null),
169
- onSaved: (e) => H({
218
+ F && /* @__PURE__ */ I(K, {
219
+ client: F,
220
+ onClose: () => z(null),
221
+ onSaved: (e) => q({
170
222
  tone: "success",
171
223
  text: `${e} updated.`
172
224
  })
173
225
  })
174
226
  ]
175
227
  });
176
- }, W = ({ id: t, onDone: n }) => {
177
- let r = e(), { emitFact: i } = C();
178
- return /* @__PURE__ */ F(L, {
228
+ }, re = ({ id: t, onDone: n }) => {
229
+ let r = e(), { emitFact: i } = w();
230
+ return /* @__PURE__ */ I(R, {
179
231
  variant: "ghost",
180
232
  size: "sm",
181
233
  "aria-label": "Archive",
@@ -188,10 +240,10 @@ var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
188
240
  onError: () => n(!1)
189
241
  });
190
242
  },
191
- children: /* @__PURE__ */ F(E, { className: "h-4 w-4" })
243
+ children: /* @__PURE__ */ I(A, { className: "h-4 w-4" })
192
244
  });
193
- }, G = ({ client: e, onClose: t, onSaved: i }) => {
194
- let a = n(), s = r(), { emitFact: c } = C(), [u, p] = T(e ? {
245
+ }, K = ({ client: e, onClose: t, onSaved: i }) => {
246
+ let a = n(), s = r(), { emitFact: c } = w(), [u, p] = k(e ? {
195
247
  name: e.name,
196
248
  type: e.type,
197
249
  status: e.status,
@@ -200,10 +252,10 @@ var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
200
252
  contactEmail: e.contactEmail ?? "",
201
253
  contactPhone: e.contactPhone ?? "",
202
254
  notes: e.notes ?? ""
203
- } : H), [m, h] = T(null), g = (e) => (t) => p((n) => ({
255
+ } : U), [m, h] = k(null), g = (e) => (t) => p((n) => ({
204
256
  ...n,
205
257
  [e]: t.target.value
206
- })), _ = a.isPending || s.isPending, b = w(() => ({
258
+ })), _ = a.isPending || s.isPending, b = O(() => ({
207
259
  name: u.name.trim(),
208
260
  type: u.type,
209
261
  status: u.status,
@@ -213,16 +265,16 @@ var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
213
265
  contactPhone: u.contactPhone.trim() || null,
214
266
  notes: u.notes.trim() || null
215
267
  }), [u]);
216
- return /* @__PURE__ */ F(d, {
268
+ return /* @__PURE__ */ I(d, {
217
269
  open: !0,
218
270
  onClose: t,
219
271
  title: e ? "Edit client" : "New client",
220
- footer: /* @__PURE__ */ I(P, { children: [/* @__PURE__ */ F(L, {
272
+ footer: /* @__PURE__ */ L(F, { children: [/* @__PURE__ */ I(R, {
221
273
  variant: "outline",
222
274
  onClick: t,
223
275
  disabled: _,
224
276
  children: "Cancel"
225
- }), /* @__PURE__ */ F(L, {
277
+ }), /* @__PURE__ */ I(R, {
226
278
  onClick: () => {
227
279
  if (h(null), !b.name) return h("Client name is required.");
228
280
  let n = (e) => h(e instanceof Error ? e.message : "Save failed.");
@@ -244,75 +296,75 @@ var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
244
296
  disabled: _,
245
297
  children: _ ? "Saving…" : e ? "Save changes" : "Create client"
246
298
  })] }),
247
- children: /* @__PURE__ */ I("div", {
299
+ children: /* @__PURE__ */ L("div", {
248
300
  className: "space-y-3",
249
301
  children: [
250
- m && /* @__PURE__ */ F(o, {
302
+ m && /* @__PURE__ */ I(o, {
251
303
  tone: "danger",
252
304
  children: m
253
305
  }),
254
- /* @__PURE__ */ F(l, {
306
+ /* @__PURE__ */ I(l, {
255
307
  label: "Name",
256
308
  required: !0,
257
- children: /* @__PURE__ */ F(y, {
309
+ children: /* @__PURE__ */ I(y, {
258
310
  value: u.name,
259
311
  onChange: g("name"),
260
312
  placeholder: "Acme Holdings Ltd."
261
313
  })
262
314
  }),
263
- /* @__PURE__ */ I("div", {
315
+ /* @__PURE__ */ L("div", {
264
316
  className: "grid grid-cols-2 gap-3",
265
- children: [/* @__PURE__ */ F(l, {
317
+ children: [/* @__PURE__ */ I(l, {
266
318
  label: "Type",
267
- children: /* @__PURE__ */ F(f, {
319
+ children: /* @__PURE__ */ I(f, {
268
320
  value: u.type,
269
321
  onChange: g("type"),
270
- children: x(R)
322
+ children: x(z)
271
323
  })
272
- }), /* @__PURE__ */ F(l, {
324
+ }), /* @__PURE__ */ I(l, {
273
325
  label: "Status",
274
- children: /* @__PURE__ */ F(f, {
326
+ children: /* @__PURE__ */ I(f, {
275
327
  value: u.status,
276
328
  onChange: g("status"),
277
- children: x(z)
329
+ children: x(B)
278
330
  })
279
331
  })]
280
332
  }),
281
- /* @__PURE__ */ F(l, {
333
+ /* @__PURE__ */ I(l, {
282
334
  label: "Industry",
283
- children: /* @__PURE__ */ F(y, {
335
+ children: /* @__PURE__ */ I(y, {
284
336
  value: u.industry,
285
337
  onChange: g("industry"),
286
338
  placeholder: "Financial services"
287
339
  })
288
340
  }),
289
- /* @__PURE__ */ I("div", {
341
+ /* @__PURE__ */ L("div", {
290
342
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
291
- children: [/* @__PURE__ */ F(l, {
343
+ children: [/* @__PURE__ */ I(l, {
292
344
  label: "Contact name",
293
- children: /* @__PURE__ */ F(y, {
345
+ children: /* @__PURE__ */ I(y, {
294
346
  value: u.contactName,
295
347
  onChange: g("contactName")
296
348
  })
297
- }), /* @__PURE__ */ F(l, {
349
+ }), /* @__PURE__ */ I(l, {
298
350
  label: "Contact email",
299
- children: /* @__PURE__ */ F(y, {
351
+ children: /* @__PURE__ */ I(y, {
300
352
  type: "email",
301
353
  value: u.contactEmail,
302
354
  onChange: g("contactEmail")
303
355
  })
304
356
  })]
305
357
  }),
306
- /* @__PURE__ */ F(l, {
358
+ /* @__PURE__ */ I(l, {
307
359
  label: "Contact phone",
308
- children: /* @__PURE__ */ F(y, {
360
+ children: /* @__PURE__ */ I(y, {
309
361
  value: u.contactPhone,
310
362
  onChange: g("contactPhone")
311
363
  })
312
364
  }),
313
- /* @__PURE__ */ F(l, {
365
+ /* @__PURE__ */ I(l, {
314
366
  label: "Notes",
315
- children: /* @__PURE__ */ F(v, {
367
+ children: /* @__PURE__ */ I(v, {
316
368
  value: u.notes,
317
369
  onChange: g("notes")
318
370
  })
@@ -322,6 +374,6 @@ var R = ["ORGANIZATION", "INDIVIDUAL"], z = [
322
374
  });
323
375
  };
324
376
  //#endregion
325
- export { U as ClientsPage };
377
+ export { H as Avatar, G as ClientsPage };
326
378
 
327
379
  //# sourceMappingURL=ClientsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClientsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ClientsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, Users, Pencil, Archive, Building2, User, ChevronRight } from 'lucide-react';\nimport {\n useClients,\n useCreateClient,\n useUpdateClient,\n useArchiveClient,\n type PswClient,\n type PswClientStatus,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { cn } from '../utils/cn';\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 fmtDate,\n} from '../components/ui';\n\nconst CLIENT_TYPES = ['ORGANIZATION', 'INDIVIDUAL'] as const;\nconst CLIENT_STATUSES: PswClientStatus[] = ['PROSPECT', 'ACTIVE', 'INACTIVE', 'ARCHIVED'];\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\n/** Tinted initials avatar (semantic tokens only). */\nexport const Avatar: FC<{ name: string; className?: string }> = ({ name, className }) => (\n <span\n className={cn(\n 'flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent ring-1 ring-inset ring-accent/15',\n className\n )}\n aria-hidden\n >\n {initials(name)}\n </span>\n);\n\ninterface FormState {\n name: string;\n type: string;\n status: string;\n industry: string;\n contactName: string;\n contactEmail: string;\n contactPhone: string;\n notes: string;\n}\nconst emptyForm: FormState = {\n name: '',\n type: 'ORGANIZATION',\n status: 'PROSPECT',\n industry: '',\n contactName: '',\n contactEmail: '',\n contactPhone: '',\n notes: '',\n};\n\nexport const ClientsPage: FC = () => {\n const nav = useNav();\n const { emitIntent, emitFact } = useAnalytics();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<PswClientStatus | ''>('');\n const [editing, setEditing] = useState<PswClient | null>(null);\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n\n const { data, isLoading, error, refetch } = useClients({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n });\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Clients\"\n subtitle=\"Every person or organization your firm serves — the record engagements, matters, documents and invoices hang off.\"\n icon={<Users className=\"h-6 w-6\" />}\n actions={\n <Button\n onClick={() => {\n emitIntent('psw.client.create_clicked');\n setCreating(true);\n }}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" /> New client\n </Button>\n }\n />\n\n {banner && (\n <Banner tone={banner.tone} onClose={() => setBanner(null)}>\n {banner.text}\n </Banner>\n )}\n\n <div className=\"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 clients…\"\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: 'client',\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 PswClientStatus | '')}\n className=\"sm:w-48\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(CLIENT_STATUSES)}\n </NativeSelect>\n </div>\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No clients yet\"\n description=\"Add your first client to start scoping engagements and matters.\"\n icon={<Users className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New client\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} client${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Name</Th>\n <Th>Type</Th>\n <Th>Industry</Th>\n <Th>Status</Th>\n <Th>Added</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((c) => (\n <tr\n key={c.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-sunken/60\"\n onClick={() => nav(`clients/${c.id}`)}\n >\n <Td>\n <div className=\"flex items-center gap-3\">\n <Avatar name={c.name} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent\">\n <span className=\"truncate\">{c.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 {c.contactName ? (\n <div className=\"truncate text-xs text-text-tertiary\">{c.contactName}</div>\n ) : null}\n </div>\n </div>\n </Td>\n <Td>\n <span className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n {c.type === 'ORGANIZATION' ? (\n <Building2 className=\"h-3.5 w-3.5 text-text-tertiary\" />\n ) : (\n <User className=\"h-3.5 w-3.5 text-text-tertiary\" />\n )}\n {c.type === 'ORGANIZATION' ? 'Organization' : 'Individual'}\n </span>\n </Td>\n <Td>{c.industry || '—'}</Td>\n <Td>\n <StatusPill status={c.status} />\n </Td>\n <Td className=\"whitespace-nowrap\">{fmtDate(c.createdAt)}</Td>\n <Td className=\"text-right\">\n <div\n className=\"flex justify-end gap-1\"\n onClick={(ev) => ev.stopPropagation()}\n role=\"presentation\"\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setEditing(c)}\n aria-label=\"Edit\"\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n {c.status !== 'ARCHIVED' && (\n <ArchiveButton\n id={c.id}\n onDone={(ok) =>\n setBanner(\n ok\n ? { tone: 'success', text: `${c.name} archived.` }\n : { tone: 'danger', text: 'Could not archive client.' }\n )\n }\n />\n )}\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <ClientModal\n onClose={() => setCreating(false)}\n onSaved={(name) => setBanner({ tone: 'success', text: `${name} created.` })}\n />\n )}\n {editing && (\n <ClientModal\n client={editing}\n onClose={() => setEditing(null)}\n onSaved={(name) => setBanner({ tone: 'success', text: `${name} updated.` })}\n />\n )}\n </div>\n );\n};\n\nconst ArchiveButton: FC<{ id: string; onDone: (ok: boolean) => void }> = ({ id, onDone }) => {\n const archive = useArchiveClient();\n const { emitFact } = useAnalytics();\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Archive\"\n disabled={archive.isPending}\n onClick={() => {\n if (!window.confirm('Archive this client? Their engagements and matters remain.')) return;\n archive.mutate(id, {\n onSuccess: (r) => {\n emitFact('psw.client.archived', { clientId: id });\n onDone(Boolean(r));\n },\n onError: () => onDone(false),\n });\n }}\n >\n <Archive className=\"h-4 w-4\" />\n </Button>\n );\n};\n\nconst ClientModal: FC<{\n client?: PswClient;\n onClose: () => void;\n onSaved: (name: string) => void;\n}> = ({ client, onClose, onSaved }) => {\n const create = useCreateClient();\n const update = useUpdateClient();\n const { emitFact } = useAnalytics();\n const [form, setForm] = useState<FormState>(\n client\n ? {\n name: client.name,\n type: client.type,\n status: client.status,\n industry: client.industry ?? '',\n contactName: client.contactName ?? '',\n contactEmail: client.contactEmail ?? '',\n contactPhone: client.contactPhone ?? '',\n notes: client.notes ?? '',\n }\n : emptyForm\n );\n const [err, setErr] = useState<string | null>(null);\n const set = (k: keyof FormState) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const pending = create.isPending || update.isPending;\n\n const payload = useMemo(\n () => ({\n name: form.name.trim(),\n type: form.type,\n status: form.status,\n industry: form.industry.trim() || null,\n contactName: form.contactName.trim() || null,\n contactEmail: form.contactEmail.trim() || null,\n contactPhone: form.contactPhone.trim() || null,\n notes: form.notes.trim() || null,\n }),\n [form]\n );\n\n const submit = () => {\n setErr(null);\n if (!payload.name) return setErr('Client name is required.');\n const onError = (e: unknown) => setErr(e instanceof Error ? e.message : 'Save failed.');\n if (client) {\n update.mutate(\n { id: client.id, input: payload },\n {\n onSuccess: () => {\n emitFact('psw.client.updated', { clientId: client.id });\n onSaved(payload.name);\n onClose();\n },\n onError,\n }\n );\n } else {\n create.mutate(payload, {\n onSuccess: (created) => {\n emitFact('psw.client.created', { clientId: created.pswCreateClient.id });\n onSaved(payload.name);\n onClose();\n },\n onError,\n });\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title={client ? 'Edit client' : 'New client'}\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={pending}>\n {pending ? 'Saving…' : client ? 'Save changes' : 'Create client'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"Acme Holdings Ltd.\" />\n </Field>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Type\">\n <NativeSelect value={form.type} onChange={set('type')}>\n {enumOptions(CLIENT_TYPES)}\n </NativeSelect>\n </Field>\n <Field label=\"Status\">\n <NativeSelect value={form.status} onChange={set('status')}>\n {enumOptions(CLIENT_STATUSES)}\n </NativeSelect>\n </Field>\n </div>\n <Field label=\"Industry\">\n <TextInput\n value={form.industry}\n onChange={set('industry')}\n placeholder=\"Financial services\"\n />\n </Field>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <Field label=\"Contact name\">\n <TextInput value={form.contactName} onChange={set('contactName')} />\n </Field>\n <Field label=\"Contact email\">\n <TextInput type=\"email\" value={form.contactEmail} onChange={set('contactEmail')} />\n </Field>\n </div>\n <Field label=\"Contact phone\">\n <TextInput value={form.contactPhone} onChange={set('contactPhone')} />\n </Field>\n <Field label=\"Notes\">\n <TextArea value={form.notes} onChange={set('notes')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ClientsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ClientsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, Users, Pencil, Archive, Building2, User, ChevronRight } from 'lucide-react';\nimport {\n useClients,\n useCreateClient,\n useUpdateClient,\n useArchiveClient,\n type PswClient,\n type PswClientStatus,\n} from '../hooks/useProServiceWorldApi';\nimport { useNav } from '../utils/nav';\nimport { cn } from '../utils/cn';\nimport { useAnalytics } from '../hooks/useAnalytics';\nimport { t } from '../i18n';\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 fmtDate,\n} from '../components/ui';\nimport { TagPicker } from '../components/TagPicker';\nimport { LoadMore } from '../components/LoadMore';\n\nconst CLIENT_TYPES = ['ORGANIZATION', 'INDIVIDUAL'] as const;\nconst CLIENT_STATUSES: PswClientStatus[] = ['PROSPECT', 'ACTIVE', 'INACTIVE', 'ARCHIVED'];\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\n/** Tinted initials avatar (semantic tokens only). */\nexport const Avatar: FC<{ name: string; className?: string }> = ({ name, className }) => (\n <span\n className={cn(\n 'flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-accent/10 text-xs font-semibold text-accent ring-1 ring-inset ring-accent/15',\n className\n )}\n aria-hidden\n >\n {initials(name)}\n </span>\n);\n\ninterface FormState {\n name: string;\n type: string;\n status: string;\n industry: string;\n contactName: string;\n contactEmail: string;\n contactPhone: string;\n notes: string;\n}\nconst emptyForm: FormState = {\n name: '',\n type: 'ORGANIZATION',\n status: 'PROSPECT',\n industry: '',\n contactName: '',\n contactEmail: '',\n contactPhone: '',\n notes: '',\n};\n\nconst PAGE_SIZE = 25;\n\nexport const ClientsPage: FC = () => {\n const nav = useNav();\n const { emitIntent, emitFact } = useAnalytics();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<PswClientStatus | ''>('');\n const [editing, setEditing] = useState<PswClient | null>(null);\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const [pagination, setPagination] = useState({ limit: PAGE_SIZE, offset: 0 });\n\n const { data, isLoading, error, refetch } = useClients({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n pagination,\n });\n const update = useUpdateClient();\n\n const allItems = useMemo(() => data?.items ?? [], [data]);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title={t('psw.clients.title')}\n subtitle={t('psw.clients.subtitle')}\n icon={<Users className=\"h-6 w-6\" />}\n actions={\n <Button\n onClick={() => {\n void impactFeedback('light');\n emitIntent('psw.client.create_clicked');\n setCreating(true);\n }}\n >\n <Plus className=\"mr-1.5 h-4 w-4\" /> {t('psw.clients.new')}\n </Button>\n }\n />\n\n {banner && (\n <Banner tone={banner.tone} onClose={() => setBanner(null)}>\n {banner.text}\n </Banner>\n )}\n\n <div className=\"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.clients.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: 'client',\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 PswClientStatus | '');\n setPagination({ limit: PAGE_SIZE, offset: 0 });\n }}\n className=\"sm:w-48\"\n >\n <option value=\"\">{t('psw.clients.allStatuses')}</option>\n {enumOptions(CLIENT_STATUSES)}\n </NativeSelect>\n </div>\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.items.length === 0 ? (\n <Empty\n title={t('psw.clients.emptyTitle')}\n description={t('psw.clients.emptyDescription')}\n icon={<Users className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> {t('psw.clients.new')}\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} client${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>{t('psw.common.name')}</Th>\n <Th>{t('psw.common.type')}</Th>\n <Th>Industry</Th>\n <Th>{t('psw.common.status')}</Th>\n <Th>{t('psw.common.added')}</Th>\n <Th>Tags</Th>\n <Th className=\"text-right\">{t('psw.common.actions')}</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((c) => (\n <tr\n key={c.id}\n className=\"group cursor-pointer transition-colors hover:bg-bg-sunken/60\"\n onClick={() => nav(`clients/${c.id}`)}\n >\n <Td>\n <div className=\"flex items-center gap-3\">\n <Avatar name={c.name} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1 font-medium text-text-primary transition-colors group-hover:text-accent\">\n <span className=\"truncate\">{c.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 {c.contactName ? (\n <div className=\"truncate text-xs text-text-tertiary\">{c.contactName}</div>\n ) : null}\n </div>\n </div>\n </Td>\n <Td>\n <span className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n {c.type === 'ORGANIZATION' ? (\n <Building2 className=\"h-3.5 w-3.5 text-text-tertiary\" />\n ) : (\n <User className=\"h-3.5 w-3.5 text-text-tertiary\" />\n )}\n {c.type === 'ORGANIZATION' ? 'Organization' : 'Individual'}\n </span>\n </Td>\n <Td>{c.industry || '—'}</Td>\n <Td>\n <StatusPill status={c.status} />\n </Td>\n <Td className=\"whitespace-nowrap\">{fmtDate(c.createdAt)}</Td>\n <Td>\n <div onClick={(ev) => ev.stopPropagation()} role=\"presentation\">\n <TagPicker\n entityType=\"PswClient\"\n entityId={c.id}\n tags={c.tags ?? []}\n onChange={async (tags) => {\n await update.mutateAsync({ id: c.id, input: { tags } });\n void refetch();\n }}\n maxTags={5}\n />\n </div>\n </Td>\n <Td className=\"text-right\">\n <div\n className=\"flex justify-end gap-1\"\n onClick={(ev) => ev.stopPropagation()}\n role=\"presentation\"\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setEditing(c)}\n aria-label=\"Edit\"\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n {c.status !== 'ARCHIVED' && (\n <ArchiveButton\n id={c.id}\n onDone={(ok) =>\n setBanner(\n ok\n ? { tone: 'success', text: `${c.name} archived.` }\n : { tone: 'danger', text: 'Could not archive client.' }\n )\n }\n />\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 <ClientModal\n onClose={() => setCreating(false)}\n onSaved={(name) => setBanner({ tone: 'success', text: `${name} created.` })}\n />\n )}\n {editing && (\n <ClientModal\n client={editing}\n onClose={() => setEditing(null)}\n onSaved={(name) => setBanner({ tone: 'success', text: `${name} updated.` })}\n />\n )}\n </div>\n );\n};\n\nconst ArchiveButton: FC<{ id: string; onDone: (ok: boolean) => void }> = ({ id, onDone }) => {\n const archive = useArchiveClient();\n const { emitFact } = useAnalytics();\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Archive\"\n disabled={archive.isPending}\n onClick={() => {\n if (!window.confirm('Archive this client? Their engagements and matters remain.')) return;\n archive.mutate(id, {\n onSuccess: (r) => {\n emitFact('psw.client.archived', { clientId: id });\n onDone(Boolean(r));\n },\n onError: () => onDone(false),\n });\n }}\n >\n <Archive className=\"h-4 w-4\" />\n </Button>\n );\n};\n\nconst ClientModal: FC<{\n client?: PswClient;\n onClose: () => void;\n onSaved: (name: string) => void;\n}> = ({ client, onClose, onSaved }) => {\n const create = useCreateClient();\n const update = useUpdateClient();\n const { emitFact } = useAnalytics();\n const [form, setForm] = useState<FormState>(\n client\n ? {\n name: client.name,\n type: client.type,\n status: client.status,\n industry: client.industry ?? '',\n contactName: client.contactName ?? '',\n contactEmail: client.contactEmail ?? '',\n contactPhone: client.contactPhone ?? '',\n notes: client.notes ?? '',\n }\n : emptyForm\n );\n const [err, setErr] = useState<string | null>(null);\n const set = (k: keyof FormState) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const pending = create.isPending || update.isPending;\n\n const payload = useMemo(\n () => ({\n name: form.name.trim(),\n type: form.type,\n status: form.status,\n industry: form.industry.trim() || null,\n contactName: form.contactName.trim() || null,\n contactEmail: form.contactEmail.trim() || null,\n contactPhone: form.contactPhone.trim() || null,\n notes: form.notes.trim() || null,\n }),\n [form]\n );\n\n const submit = () => {\n setErr(null);\n if (!payload.name) return setErr('Client name is required.');\n const onError = (e: unknown) => setErr(e instanceof Error ? e.message : 'Save failed.');\n if (client) {\n update.mutate(\n { id: client.id, input: payload },\n {\n onSuccess: () => {\n emitFact('psw.client.updated', { clientId: client.id });\n onSaved(payload.name);\n onClose();\n },\n onError,\n }\n );\n } else {\n create.mutate(payload, {\n onSuccess: (created) => {\n emitFact('psw.client.created', { clientId: created.pswCreateClient.id });\n onSaved(payload.name);\n onClose();\n },\n onError,\n });\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title={client ? 'Edit client' : 'New client'}\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={pending}>\n {pending ? 'Saving…' : client ? 'Save changes' : 'Create client'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"Acme Holdings Ltd.\" />\n </Field>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Type\">\n <NativeSelect value={form.type} onChange={set('type')}>\n {enumOptions(CLIENT_TYPES)}\n </NativeSelect>\n </Field>\n <Field label=\"Status\">\n <NativeSelect value={form.status} onChange={set('status')}>\n {enumOptions(CLIENT_STATUSES)}\n </NativeSelect>\n </Field>\n </div>\n <Field label=\"Industry\">\n <TextInput\n value={form.industry}\n onChange={set('industry')}\n placeholder=\"Financial services\"\n />\n </Field>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <Field label=\"Contact name\">\n <TextInput value={form.contactName} onChange={set('contactName')} />\n </Field>\n <Field label=\"Contact email\">\n <TextInput type=\"email\" value={form.contactEmail} onChange={set('contactEmail')} />\n </Field>\n </div>\n <Field label=\"Contact phone\">\n <TextInput value={form.contactPhone} onChange={set('contactPhone')} />\n </Field>\n <Field label=\"Notes\">\n <TextArea value={form.notes} onChange={set('notes')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}