@burdenoff/microfe-proserviceworld 2026.626.2 → 2026.706.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -3
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/proserviceworld/ProServiceWorldRoot.js +92 -136
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js +18 -26
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/ui.js +273 -0
- package/dist/proserviceworld/components/ui.js.map +1 -0
- package/dist/proserviceworld/hooks/useFiles.js +45 -0
- package/dist/proserviceworld/hooks/useFiles.js.map +1 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +416 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -0
- package/dist/proserviceworld/index.js +0 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +163 -475
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ClientDetailPage.js +238 -0
- package/dist/proserviceworld/pages/ClientDetailPage.js.map +1 -0
- package/dist/proserviceworld/pages/ClientsPage.js +295 -0
- package/dist/proserviceworld/pages/ClientsPage.js.map +1 -0
- package/dist/proserviceworld/pages/CompliancePage.js +307 -0
- package/dist/proserviceworld/pages/CompliancePage.js.map +1 -0
- package/dist/proserviceworld/pages/ConflictsPage.js +234 -0
- package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -0
- package/dist/proserviceworld/pages/DashboardPage.js +179 -632
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +235 -539
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementDetailPage.js +444 -0
- package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -0
- package/dist/proserviceworld/pages/EngagementsPage.js +256 -0
- package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -0
- package/dist/proserviceworld/pages/InvoicesPage.js +303 -0
- package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -0
- package/dist/proserviceworld/pages/MattersPage.js +275 -0
- package/dist/proserviceworld/pages/MattersPage.js.map +1 -0
- package/dist/proserviceworld/pages/PracticeAreasPage.js +176 -0
- package/dist/proserviceworld/pages/PracticeAreasPage.js.map +1 -0
- package/dist/proserviceworld/pages/TimePage.js +310 -0
- package/dist/proserviceworld/pages/TimePage.js.map +1 -0
- package/dist/proserviceworld/utils/cn.js +10 -0
- package/dist/proserviceworld/utils/cn.js.map +1 -0
- package/dist/proserviceworld/utils/nav.js +11 -0
- package/dist/proserviceworld/utils/nav.js.map +1 -0
- package/package.json +3 -2
- package/dist/proserviceworld/pages/AdminReportsPage.js +0 -579
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +0 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +0 -468
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +0 -1
- package/dist/proserviceworld/pages/BookingsPage.js +0 -446
- package/dist/proserviceworld/pages/BookingsPage.js.map +0 -1
- package/dist/proserviceworld/pages/MessagesPage.js +0 -238
- package/dist/proserviceworld/pages/MessagesPage.js.map +0 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +0 -537
- package/dist/proserviceworld/pages/PaymentsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +0 -627
- package/dist/proserviceworld/pages/ProjectsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +0 -538
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +0 -347
- package/dist/proserviceworld/pages/ProvidersPage.js.map +0 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +0 -356
- package/dist/proserviceworld/pages/ReviewsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +0 -631
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ServicesPage.js +0 -344
- package/dist/proserviceworld/pages/ServicesPage.js.map +0 -1
- package/dist/proserviceworld/pages/VerificationPage.js +0 -370
- package/dist/proserviceworld/pages/VerificationPage.js.map +0 -1
- package/dist/proserviceworld/store/proserviceworldStore.js +0 -2490
- package/dist/proserviceworld/store/proserviceworldStore.js.map +0 -1
- package/dist/proserviceworld/utils/i18n.js +0 -15
- package/dist/proserviceworld/utils/i18n.js.map +0 -1
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { useArchiveClient as e, useClients as t, useCreateClient as n, useUpdateClient 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 } from "../components/ui.js";
|
|
4
|
+
import { useMemo as S, useState as C } from "react";
|
|
5
|
+
import { Archive as w, Pencil as T, Plus as E, Search as D, Users as O } from "lucide-react";
|
|
6
|
+
import { Fragment as k, jsx as A, jsxs as j } from "react/jsx-runtime";
|
|
7
|
+
import { Button as M } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/proserviceworld/pages/ClientsPage.tsx
|
|
9
|
+
var N = ["ORGANIZATION", "INDIVIDUAL"], P = [
|
|
10
|
+
"PROSPECT",
|
|
11
|
+
"ACTIVE",
|
|
12
|
+
"INACTIVE",
|
|
13
|
+
"ARCHIVED"
|
|
14
|
+
], F = {
|
|
15
|
+
name: "",
|
|
16
|
+
type: "ORGANIZATION",
|
|
17
|
+
status: "PROSPECT",
|
|
18
|
+
industry: "",
|
|
19
|
+
contactName: "",
|
|
20
|
+
contactEmail: "",
|
|
21
|
+
contactPhone: "",
|
|
22
|
+
notes: ""
|
|
23
|
+
}, I = () => {
|
|
24
|
+
let e = i(), [n, r] = C(""), [c, u] = C(""), [_, S] = C(null), [w, k] = C(!1), [N, F] = C(null), { data: I, isLoading: z, error: B, refetch: V } = t({
|
|
25
|
+
search: n.trim() || void 0,
|
|
26
|
+
status: c || void 0
|
|
27
|
+
});
|
|
28
|
+
return /* @__PURE__ */ j("div", {
|
|
29
|
+
className: "space-y-6",
|
|
30
|
+
children: [
|
|
31
|
+
/* @__PURE__ */ A(f, {
|
|
32
|
+
title: "Clients",
|
|
33
|
+
subtitle: "Every person or organization your firm serves — the record engagements, matters, documents and invoices hang off.",
|
|
34
|
+
icon: /* @__PURE__ */ A(O, { className: "h-6 w-6" }),
|
|
35
|
+
actions: /* @__PURE__ */ j(M, {
|
|
36
|
+
onClick: () => k(!0),
|
|
37
|
+
children: [/* @__PURE__ */ A(E, { className: "mr-1.5 h-4 w-4" }), " New client"]
|
|
38
|
+
})
|
|
39
|
+
}),
|
|
40
|
+
N && /* @__PURE__ */ A(a, {
|
|
41
|
+
tone: N.tone,
|
|
42
|
+
onClose: () => F(null),
|
|
43
|
+
children: N.text
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ j("div", {
|
|
46
|
+
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
47
|
+
children: [/* @__PURE__ */ j("div", {
|
|
48
|
+
className: "relative flex-1",
|
|
49
|
+
children: [/* @__PURE__ */ A(D, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ A(v, {
|
|
50
|
+
placeholder: "Search clients…",
|
|
51
|
+
value: n,
|
|
52
|
+
onChange: (e) => r(e.target.value),
|
|
53
|
+
className: "pl-9"
|
|
54
|
+
})]
|
|
55
|
+
}), /* @__PURE__ */ j(d, {
|
|
56
|
+
value: c,
|
|
57
|
+
onChange: (e) => u(e.target.value),
|
|
58
|
+
className: "sm:w-48",
|
|
59
|
+
children: [/* @__PURE__ */ A("option", {
|
|
60
|
+
value: "",
|
|
61
|
+
children: "All statuses"
|
|
62
|
+
}), b(P)]
|
|
63
|
+
})]
|
|
64
|
+
}),
|
|
65
|
+
z ? /* @__PURE__ */ A(l, {}) : B ? /* @__PURE__ */ A(s, {
|
|
66
|
+
error: B,
|
|
67
|
+
onRetry: () => void V()
|
|
68
|
+
}) : !I || I.items.length === 0 ? /* @__PURE__ */ A(o, {
|
|
69
|
+
title: "No clients yet",
|
|
70
|
+
description: "Add your first client to start scoping engagements and matters.",
|
|
71
|
+
icon: /* @__PURE__ */ A(O, { className: "h-8 w-8" }),
|
|
72
|
+
action: /* @__PURE__ */ j(M, {
|
|
73
|
+
onClick: () => k(!0),
|
|
74
|
+
children: [/* @__PURE__ */ A(E, { className: "mr-1.5 h-4 w-4" }), " New client"]
|
|
75
|
+
})
|
|
76
|
+
}) : /* @__PURE__ */ A(p, {
|
|
77
|
+
title: `${I.totalCount} client${I.totalCount === 1 ? "" : "s"}`,
|
|
78
|
+
children: /* @__PURE__ */ j(h, { children: [/* @__PURE__ */ A("thead", { children: /* @__PURE__ */ j("tr", { children: [
|
|
79
|
+
/* @__PURE__ */ A(y, { children: "Name" }),
|
|
80
|
+
/* @__PURE__ */ A(y, { children: "Type" }),
|
|
81
|
+
/* @__PURE__ */ A(y, { children: "Industry" }),
|
|
82
|
+
/* @__PURE__ */ A(y, { children: "Status" }),
|
|
83
|
+
/* @__PURE__ */ A(y, { children: "Added" }),
|
|
84
|
+
/* @__PURE__ */ A(y, {
|
|
85
|
+
className: "text-right",
|
|
86
|
+
children: "Actions"
|
|
87
|
+
})
|
|
88
|
+
] }) }), /* @__PURE__ */ A("tbody", { children: I.items.map((t) => /* @__PURE__ */ j("tr", {
|
|
89
|
+
className: "hover:bg-bg-sunken/50",
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ j(g, { children: [/* @__PURE__ */ A("button", {
|
|
92
|
+
type: "button",
|
|
93
|
+
className: "text-left font-medium text-text-primary hover:text-accent",
|
|
94
|
+
onClick: () => e(`clients/${t.id}`),
|
|
95
|
+
children: t.name
|
|
96
|
+
}), t.contactName ? /* @__PURE__ */ A("div", {
|
|
97
|
+
className: "text-xs text-text-tertiary",
|
|
98
|
+
children: t.contactName
|
|
99
|
+
}) : null] }),
|
|
100
|
+
/* @__PURE__ */ A(g, { children: t.type === "ORGANIZATION" ? "Organization" : "Individual" }),
|
|
101
|
+
/* @__PURE__ */ A(g, { children: t.industry || "—" }),
|
|
102
|
+
/* @__PURE__ */ A(g, { children: /* @__PURE__ */ A(m, { status: t.status }) }),
|
|
103
|
+
/* @__PURE__ */ A(g, { children: x(t.createdAt) }),
|
|
104
|
+
/* @__PURE__ */ A(g, {
|
|
105
|
+
className: "text-right",
|
|
106
|
+
children: /* @__PURE__ */ j("div", {
|
|
107
|
+
className: "flex justify-end gap-1",
|
|
108
|
+
children: [/* @__PURE__ */ A(M, {
|
|
109
|
+
variant: "ghost",
|
|
110
|
+
size: "sm",
|
|
111
|
+
onClick: () => S(t),
|
|
112
|
+
"aria-label": "Edit",
|
|
113
|
+
children: /* @__PURE__ */ A(T, { className: "h-4 w-4" })
|
|
114
|
+
}), t.status !== "ARCHIVED" && /* @__PURE__ */ A(L, {
|
|
115
|
+
id: t.id,
|
|
116
|
+
onDone: (e) => F(e ? {
|
|
117
|
+
tone: "success",
|
|
118
|
+
text: `${t.name} archived.`
|
|
119
|
+
} : {
|
|
120
|
+
tone: "danger",
|
|
121
|
+
text: "Could not archive client."
|
|
122
|
+
})
|
|
123
|
+
})]
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
}, t.id)) })] })
|
|
128
|
+
}),
|
|
129
|
+
w && /* @__PURE__ */ A(R, {
|
|
130
|
+
onClose: () => k(!1),
|
|
131
|
+
onSaved: (e) => F({
|
|
132
|
+
tone: "success",
|
|
133
|
+
text: `${e} created.`
|
|
134
|
+
})
|
|
135
|
+
}),
|
|
136
|
+
_ && /* @__PURE__ */ A(R, {
|
|
137
|
+
client: _,
|
|
138
|
+
onClose: () => S(null),
|
|
139
|
+
onSaved: (e) => F({
|
|
140
|
+
tone: "success",
|
|
141
|
+
text: `${e} updated.`
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
]
|
|
145
|
+
});
|
|
146
|
+
}, L = ({ id: t, onDone: n }) => {
|
|
147
|
+
let r = e();
|
|
148
|
+
return /* @__PURE__ */ A(M, {
|
|
149
|
+
variant: "ghost",
|
|
150
|
+
size: "sm",
|
|
151
|
+
"aria-label": "Archive",
|
|
152
|
+
disabled: r.isPending,
|
|
153
|
+
onClick: () => {
|
|
154
|
+
window.confirm("Archive this client? Their engagements and matters remain.") && r.mutate(t, {
|
|
155
|
+
onSuccess: (e) => n(!!e),
|
|
156
|
+
onError: () => n(!1)
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
children: /* @__PURE__ */ A(w, { className: "h-4 w-4" })
|
|
160
|
+
});
|
|
161
|
+
}, R = ({ client: e, onClose: t, onSaved: i }) => {
|
|
162
|
+
let o = n(), s = r(), [l, f] = C(e ? {
|
|
163
|
+
name: e.name,
|
|
164
|
+
type: e.type,
|
|
165
|
+
status: e.status,
|
|
166
|
+
industry: e.industry ?? "",
|
|
167
|
+
contactName: e.contactName ?? "",
|
|
168
|
+
contactEmail: e.contactEmail ?? "",
|
|
169
|
+
contactPhone: e.contactPhone ?? "",
|
|
170
|
+
notes: e.notes ?? ""
|
|
171
|
+
} : F), [p, m] = C(null), h = (e) => (t) => f((n) => ({
|
|
172
|
+
...n,
|
|
173
|
+
[e]: t.target.value
|
|
174
|
+
})), g = o.isPending || s.isPending, y = S(() => ({
|
|
175
|
+
name: l.name.trim(),
|
|
176
|
+
type: l.type,
|
|
177
|
+
status: l.status,
|
|
178
|
+
industry: l.industry.trim() || null,
|
|
179
|
+
contactName: l.contactName.trim() || null,
|
|
180
|
+
contactEmail: l.contactEmail.trim() || null,
|
|
181
|
+
contactPhone: l.contactPhone.trim() || null,
|
|
182
|
+
notes: l.notes.trim() || null
|
|
183
|
+
}), [l]);
|
|
184
|
+
return /* @__PURE__ */ A(u, {
|
|
185
|
+
open: !0,
|
|
186
|
+
onClose: t,
|
|
187
|
+
title: e ? "Edit client" : "New client",
|
|
188
|
+
footer: /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ A(M, {
|
|
189
|
+
variant: "outline",
|
|
190
|
+
onClick: t,
|
|
191
|
+
disabled: g,
|
|
192
|
+
children: "Cancel"
|
|
193
|
+
}), /* @__PURE__ */ A(M, {
|
|
194
|
+
onClick: () => {
|
|
195
|
+
if (m(null), !y.name) return m("Client name is required.");
|
|
196
|
+
let n = (e) => m(e instanceof Error ? e.message : "Save failed.");
|
|
197
|
+
e ? s.mutate({
|
|
198
|
+
id: e.id,
|
|
199
|
+
input: y
|
|
200
|
+
}, {
|
|
201
|
+
onSuccess: () => {
|
|
202
|
+
i(y.name), t();
|
|
203
|
+
},
|
|
204
|
+
onError: n
|
|
205
|
+
}) : o.mutate(y, {
|
|
206
|
+
onSuccess: () => {
|
|
207
|
+
i(y.name), t();
|
|
208
|
+
},
|
|
209
|
+
onError: n
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
disabled: g,
|
|
213
|
+
children: g ? "Saving…" : e ? "Save changes" : "Create client"
|
|
214
|
+
})] }),
|
|
215
|
+
children: /* @__PURE__ */ j("div", {
|
|
216
|
+
className: "space-y-3",
|
|
217
|
+
children: [
|
|
218
|
+
p && /* @__PURE__ */ A(a, {
|
|
219
|
+
tone: "danger",
|
|
220
|
+
children: p
|
|
221
|
+
}),
|
|
222
|
+
/* @__PURE__ */ A(c, {
|
|
223
|
+
label: "Name",
|
|
224
|
+
required: !0,
|
|
225
|
+
children: /* @__PURE__ */ A(v, {
|
|
226
|
+
value: l.name,
|
|
227
|
+
onChange: h("name"),
|
|
228
|
+
placeholder: "Acme Holdings Ltd."
|
|
229
|
+
})
|
|
230
|
+
}),
|
|
231
|
+
/* @__PURE__ */ j("div", {
|
|
232
|
+
className: "grid grid-cols-2 gap-3",
|
|
233
|
+
children: [/* @__PURE__ */ A(c, {
|
|
234
|
+
label: "Type",
|
|
235
|
+
children: /* @__PURE__ */ A(d, {
|
|
236
|
+
value: l.type,
|
|
237
|
+
onChange: h("type"),
|
|
238
|
+
children: b(N)
|
|
239
|
+
})
|
|
240
|
+
}), /* @__PURE__ */ A(c, {
|
|
241
|
+
label: "Status",
|
|
242
|
+
children: /* @__PURE__ */ A(d, {
|
|
243
|
+
value: l.status,
|
|
244
|
+
onChange: h("status"),
|
|
245
|
+
children: b(P)
|
|
246
|
+
})
|
|
247
|
+
})]
|
|
248
|
+
}),
|
|
249
|
+
/* @__PURE__ */ A(c, {
|
|
250
|
+
label: "Industry",
|
|
251
|
+
children: /* @__PURE__ */ A(v, {
|
|
252
|
+
value: l.industry,
|
|
253
|
+
onChange: h("industry"),
|
|
254
|
+
placeholder: "Financial services"
|
|
255
|
+
})
|
|
256
|
+
}),
|
|
257
|
+
/* @__PURE__ */ j("div", {
|
|
258
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
259
|
+
children: [/* @__PURE__ */ A(c, {
|
|
260
|
+
label: "Contact name",
|
|
261
|
+
children: /* @__PURE__ */ A(v, {
|
|
262
|
+
value: l.contactName,
|
|
263
|
+
onChange: h("contactName")
|
|
264
|
+
})
|
|
265
|
+
}), /* @__PURE__ */ A(c, {
|
|
266
|
+
label: "Contact email",
|
|
267
|
+
children: /* @__PURE__ */ A(v, {
|
|
268
|
+
type: "email",
|
|
269
|
+
value: l.contactEmail,
|
|
270
|
+
onChange: h("contactEmail")
|
|
271
|
+
})
|
|
272
|
+
})]
|
|
273
|
+
}),
|
|
274
|
+
/* @__PURE__ */ A(c, {
|
|
275
|
+
label: "Contact phone",
|
|
276
|
+
children: /* @__PURE__ */ A(v, {
|
|
277
|
+
value: l.contactPhone,
|
|
278
|
+
onChange: h("contactPhone")
|
|
279
|
+
})
|
|
280
|
+
}),
|
|
281
|
+
/* @__PURE__ */ A(c, {
|
|
282
|
+
label: "Notes",
|
|
283
|
+
children: /* @__PURE__ */ A(_, {
|
|
284
|
+
value: l.notes,
|
|
285
|
+
onChange: h("notes")
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
]
|
|
289
|
+
})
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
//#endregion
|
|
293
|
+
export { I as ClientsPage };
|
|
294
|
+
|
|
295
|
+
//# sourceMappingURL=ClientsPage.js.map
|
|
@@ -0,0 +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 } 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 {\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\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 [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 onClick={() => setCreating(true)}>\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 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 key={c.id} className=\"hover:bg-bg-sunken/50\">\n <Td>\n <button\n type=\"button\"\n className=\"text-left font-medium text-text-primary hover:text-accent\"\n onClick={() => nav(`clients/${c.id}`)}\n >\n {c.name}\n </button>\n {c.contactName ? (\n <div className=\"text-xs text-text-tertiary\">{c.contactName}</div>\n ) : null}\n </Td>\n <Td>{c.type === 'ORGANIZATION' ? 'Organization' : 'Individual'}</Td>\n <Td>{c.industry || '—'}</Td>\n <Td>\n <StatusPill status={c.status} />\n </Td>\n <Td>{fmtDate(c.createdAt)}</Td>\n <Td className=\"text-right\">\n <div className=\"flex justify-end gap-1\">\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 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, { onSuccess: (r) => onDone(Boolean(r)), onError: () => onDone(false) });\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 [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 onSaved(payload.name);\n onClose();\n },\n onError,\n }\n );\n } else {\n create.mutate(payload, {\n onSuccess: () => {\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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"}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { useClients as e, useComplianceChecks as t, useCreateComplianceCheck as n, useEngagements as r, useMatters as i, useResolveComplianceCheck as a } from "../hooks/useProServiceWorldApi.js";
|
|
2
|
+
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, humanize as C } from "../components/ui.js";
|
|
3
|
+
import { useState as w } from "react";
|
|
4
|
+
import { Plus as T, ShieldCheck as E } from "lucide-react";
|
|
5
|
+
import { Fragment as D, jsx as O, jsxs as k } from "react/jsx-runtime";
|
|
6
|
+
import { Button as A } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
//#region src/proserviceworld/pages/CompliancePage.tsx
|
|
8
|
+
var j = [
|
|
9
|
+
"ENGAGEMENT_ACCEPTANCE",
|
|
10
|
+
"INDEPENDENCE",
|
|
11
|
+
"LICENSING",
|
|
12
|
+
"CONTINUING_EDUCATION",
|
|
13
|
+
"DATA_RETENTION",
|
|
14
|
+
"LEGAL_HOLD"
|
|
15
|
+
], M = [
|
|
16
|
+
"IN_REVIEW",
|
|
17
|
+
"PASSED",
|
|
18
|
+
"FAILED",
|
|
19
|
+
"WAIVED"
|
|
20
|
+
], N = [
|
|
21
|
+
"CLIENT",
|
|
22
|
+
"ENGAGEMENT",
|
|
23
|
+
"MATTER"
|
|
24
|
+
], P = () => {
|
|
25
|
+
let [e, n] = w(""), [r, i] = w(!1), [a, l] = w(null), [d, v] = w(null), { data: y, isLoading: D, error: j, refetch: M } = t({ status: e || void 0 });
|
|
26
|
+
return /* @__PURE__ */ k("div", {
|
|
27
|
+
className: "space-y-6",
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ O(p, {
|
|
30
|
+
title: "Compliance",
|
|
31
|
+
subtitle: "The duties that travel with the work — acceptance, independence, licensing, CE, retention and legal hold — as gates, not afterthoughts.",
|
|
32
|
+
icon: /* @__PURE__ */ O(E, { className: "h-6 w-6" }),
|
|
33
|
+
actions: /* @__PURE__ */ k(A, {
|
|
34
|
+
onClick: () => i(!0),
|
|
35
|
+
children: [/* @__PURE__ */ O(T, { className: "mr-1.5 h-4 w-4" }), "New check"]
|
|
36
|
+
})
|
|
37
|
+
}),
|
|
38
|
+
d && /* @__PURE__ */ O(o, {
|
|
39
|
+
tone: "success",
|
|
40
|
+
onClose: () => v(null),
|
|
41
|
+
children: d
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ O("div", {
|
|
44
|
+
className: "flex justify-end",
|
|
45
|
+
children: /* @__PURE__ */ k(f, {
|
|
46
|
+
value: e,
|
|
47
|
+
onChange: (e) => n(e.target.value),
|
|
48
|
+
className: "sm:w-52",
|
|
49
|
+
children: [/* @__PURE__ */ O("option", {
|
|
50
|
+
value: "",
|
|
51
|
+
children: "All statuses"
|
|
52
|
+
}), x([
|
|
53
|
+
"PENDING",
|
|
54
|
+
"IN_REVIEW",
|
|
55
|
+
"PASSED",
|
|
56
|
+
"FAILED",
|
|
57
|
+
"WAIVED"
|
|
58
|
+
])]
|
|
59
|
+
})
|
|
60
|
+
}),
|
|
61
|
+
D ? /* @__PURE__ */ O(u, {}) : j ? /* @__PURE__ */ O(c, {
|
|
62
|
+
error: j,
|
|
63
|
+
onRetry: () => void M()
|
|
64
|
+
}) : !y || y.length === 0 ? /* @__PURE__ */ O(s, {
|
|
65
|
+
title: "No compliance checks",
|
|
66
|
+
description: "Raise a check to gate acceptance or track a professional obligation.",
|
|
67
|
+
icon: /* @__PURE__ */ O(E, { className: "h-8 w-8" }),
|
|
68
|
+
action: /* @__PURE__ */ k(A, {
|
|
69
|
+
onClick: () => i(!0),
|
|
70
|
+
children: [/* @__PURE__ */ O(T, { className: "mr-1.5 h-4 w-4" }), "New check"]
|
|
71
|
+
})
|
|
72
|
+
}) : /* @__PURE__ */ O(m, {
|
|
73
|
+
title: `${y.length} check${y.length === 1 ? "" : "s"}`,
|
|
74
|
+
children: /* @__PURE__ */ k(g, { children: [/* @__PURE__ */ O("thead", { children: /* @__PURE__ */ k("tr", { children: [
|
|
75
|
+
/* @__PURE__ */ O(b, { children: "Title" }),
|
|
76
|
+
/* @__PURE__ */ O(b, { children: "Type" }),
|
|
77
|
+
/* @__PURE__ */ O(b, { children: "Subject" }),
|
|
78
|
+
/* @__PURE__ */ O(b, { children: "Due" }),
|
|
79
|
+
/* @__PURE__ */ O(b, { children: "Status" }),
|
|
80
|
+
/* @__PURE__ */ O(b, {
|
|
81
|
+
className: "text-right",
|
|
82
|
+
children: "Actions"
|
|
83
|
+
})
|
|
84
|
+
] }) }), /* @__PURE__ */ O("tbody", { children: y.map((e) => /* @__PURE__ */ k("tr", {
|
|
85
|
+
className: "hover:bg-bg-sunken/50",
|
|
86
|
+
children: [
|
|
87
|
+
/* @__PURE__ */ O(_, { children: /* @__PURE__ */ O("span", {
|
|
88
|
+
className: "font-medium text-text-primary",
|
|
89
|
+
children: e.title
|
|
90
|
+
}) }),
|
|
91
|
+
/* @__PURE__ */ O(_, { children: C(e.type) }),
|
|
92
|
+
/* @__PURE__ */ O(_, { children: C(e.subjectType) }),
|
|
93
|
+
/* @__PURE__ */ O(_, { children: S(e.dueDate) }),
|
|
94
|
+
/* @__PURE__ */ O(_, { children: /* @__PURE__ */ O(h, { status: e.status }) }),
|
|
95
|
+
/* @__PURE__ */ O(_, {
|
|
96
|
+
className: "text-right",
|
|
97
|
+
children: e.status === "PENDING" || e.status === "IN_REVIEW" ? /* @__PURE__ */ O(A, {
|
|
98
|
+
variant: "ghost",
|
|
99
|
+
size: "sm",
|
|
100
|
+
onClick: () => l({
|
|
101
|
+
id: e.id,
|
|
102
|
+
title: e.title
|
|
103
|
+
}),
|
|
104
|
+
children: "Resolve"
|
|
105
|
+
}) : /* @__PURE__ */ O("span", {
|
|
106
|
+
className: "text-xs text-text-tertiary",
|
|
107
|
+
children: e.resolvedAt ? S(e.resolvedAt) : ""
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
]
|
|
111
|
+
}, e.id)) })] })
|
|
112
|
+
}),
|
|
113
|
+
r && /* @__PURE__ */ O(F, {
|
|
114
|
+
onClose: () => i(!1),
|
|
115
|
+
onDone: () => {
|
|
116
|
+
M(), v("Compliance check created.");
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
a && /* @__PURE__ */ O(I, {
|
|
120
|
+
check: a,
|
|
121
|
+
onClose: () => l(null),
|
|
122
|
+
onDone: () => {
|
|
123
|
+
M(), v("Compliance check resolved.");
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
}, F = ({ onClose: t, onDone: a }) => {
|
|
129
|
+
let s = n(), [c, u] = w({
|
|
130
|
+
type: "ENGAGEMENT_ACCEPTANCE",
|
|
131
|
+
subjectType: "ENGAGEMENT",
|
|
132
|
+
subjectId: "",
|
|
133
|
+
title: "",
|
|
134
|
+
dueDate: "",
|
|
135
|
+
details: ""
|
|
136
|
+
}), [p, m] = w(null), h = (e) => (t) => u((n) => ({
|
|
137
|
+
...n,
|
|
138
|
+
[e]: t.target.value
|
|
139
|
+
})), g = e({ pagination: { limit: 100 } }), _ = r({ pagination: { limit: 100 } }), b = i({ pagination: { limit: 100 } }), S = c.subjectType === "CLIENT" ? (g.data?.items ?? []).map((e) => ({
|
|
140
|
+
id: e.id,
|
|
141
|
+
label: e.name
|
|
142
|
+
})) : c.subjectType === "ENGAGEMENT" ? (_.data?.items ?? []).map((e) => ({
|
|
143
|
+
id: e.id,
|
|
144
|
+
label: e.name
|
|
145
|
+
})) : (b.data?.items ?? []).map((e) => ({
|
|
146
|
+
id: e.id,
|
|
147
|
+
label: e.title
|
|
148
|
+
}));
|
|
149
|
+
return /* @__PURE__ */ O(d, {
|
|
150
|
+
open: !0,
|
|
151
|
+
onClose: t,
|
|
152
|
+
title: "New compliance check",
|
|
153
|
+
footer: /* @__PURE__ */ k(D, { children: [/* @__PURE__ */ O(A, {
|
|
154
|
+
variant: "outline",
|
|
155
|
+
onClick: t,
|
|
156
|
+
disabled: s.isPending,
|
|
157
|
+
children: "Cancel"
|
|
158
|
+
}), /* @__PURE__ */ O(A, {
|
|
159
|
+
onClick: () => {
|
|
160
|
+
if (m(null), !c.title.trim()) return m("Title is required.");
|
|
161
|
+
if (!c.subjectId) return m("Select a subject.");
|
|
162
|
+
s.mutate({
|
|
163
|
+
type: c.type,
|
|
164
|
+
subjectType: c.subjectType,
|
|
165
|
+
subjectId: c.subjectId,
|
|
166
|
+
title: c.title.trim(),
|
|
167
|
+
dueDate: c.dueDate ? new Date(c.dueDate).toISOString() : null,
|
|
168
|
+
details: c.details.trim() || null
|
|
169
|
+
}, {
|
|
170
|
+
onSuccess: () => {
|
|
171
|
+
a(), t();
|
|
172
|
+
},
|
|
173
|
+
onError: (e) => m(e instanceof Error ? e.message : "Failed.")
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
disabled: s.isPending,
|
|
177
|
+
children: s.isPending ? "Creating…" : "Create check"
|
|
178
|
+
})] }),
|
|
179
|
+
children: /* @__PURE__ */ k("div", {
|
|
180
|
+
className: "space-y-3",
|
|
181
|
+
children: [
|
|
182
|
+
p && /* @__PURE__ */ O(o, {
|
|
183
|
+
tone: "danger",
|
|
184
|
+
children: p
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ O(l, {
|
|
187
|
+
label: "Title",
|
|
188
|
+
required: !0,
|
|
189
|
+
children: /* @__PURE__ */ O(y, {
|
|
190
|
+
value: c.title,
|
|
191
|
+
onChange: h("title"),
|
|
192
|
+
placeholder: "Independence confirmation — FY26 audit"
|
|
193
|
+
})
|
|
194
|
+
}),
|
|
195
|
+
/* @__PURE__ */ O(l, {
|
|
196
|
+
label: "Type",
|
|
197
|
+
children: /* @__PURE__ */ O(f, {
|
|
198
|
+
value: c.type,
|
|
199
|
+
onChange: h("type"),
|
|
200
|
+
children: x(j)
|
|
201
|
+
})
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ k("div", {
|
|
204
|
+
className: "grid grid-cols-2 gap-3",
|
|
205
|
+
children: [/* @__PURE__ */ O(l, {
|
|
206
|
+
label: "Subject type",
|
|
207
|
+
children: /* @__PURE__ */ O(f, {
|
|
208
|
+
value: c.subjectType,
|
|
209
|
+
onChange: (e) => u((t) => ({
|
|
210
|
+
...t,
|
|
211
|
+
subjectType: e.target.value,
|
|
212
|
+
subjectId: ""
|
|
213
|
+
})),
|
|
214
|
+
children: x(N)
|
|
215
|
+
})
|
|
216
|
+
}), /* @__PURE__ */ O(l, {
|
|
217
|
+
label: "Subject",
|
|
218
|
+
required: !0,
|
|
219
|
+
children: /* @__PURE__ */ k(f, {
|
|
220
|
+
value: c.subjectId,
|
|
221
|
+
onChange: h("subjectId"),
|
|
222
|
+
children: [/* @__PURE__ */ O("option", {
|
|
223
|
+
value: "",
|
|
224
|
+
children: "Select…"
|
|
225
|
+
}), S.map((e) => /* @__PURE__ */ O("option", {
|
|
226
|
+
value: e.id,
|
|
227
|
+
children: e.label
|
|
228
|
+
}, e.id))]
|
|
229
|
+
})
|
|
230
|
+
})]
|
|
231
|
+
}),
|
|
232
|
+
/* @__PURE__ */ O(l, {
|
|
233
|
+
label: "Due date",
|
|
234
|
+
children: /* @__PURE__ */ O(y, {
|
|
235
|
+
type: "date",
|
|
236
|
+
value: c.dueDate,
|
|
237
|
+
onChange: h("dueDate")
|
|
238
|
+
})
|
|
239
|
+
}),
|
|
240
|
+
/* @__PURE__ */ O(l, {
|
|
241
|
+
label: "Details",
|
|
242
|
+
children: /* @__PURE__ */ O(v, {
|
|
243
|
+
value: c.details,
|
|
244
|
+
onChange: h("details")
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
]
|
|
248
|
+
})
|
|
249
|
+
});
|
|
250
|
+
}, I = ({ check: e, onClose: t, onDone: n }) => {
|
|
251
|
+
let r = a(), [i, s] = w("PASSED"), [c, u] = w(""), [p, m] = w(null);
|
|
252
|
+
return /* @__PURE__ */ O(d, {
|
|
253
|
+
open: !0,
|
|
254
|
+
onClose: t,
|
|
255
|
+
title: `Resolve: ${e.title}`,
|
|
256
|
+
footer: /* @__PURE__ */ k(D, { children: [/* @__PURE__ */ O(A, {
|
|
257
|
+
variant: "outline",
|
|
258
|
+
onClick: t,
|
|
259
|
+
disabled: r.isPending,
|
|
260
|
+
children: "Cancel"
|
|
261
|
+
}), /* @__PURE__ */ O(A, {
|
|
262
|
+
onClick: () => r.mutate({
|
|
263
|
+
id: e.id,
|
|
264
|
+
status: i,
|
|
265
|
+
details: c.trim() || void 0
|
|
266
|
+
}, {
|
|
267
|
+
onSuccess: () => {
|
|
268
|
+
n(), t();
|
|
269
|
+
},
|
|
270
|
+
onError: (e) => m(e instanceof Error ? e.message : "Failed.")
|
|
271
|
+
}),
|
|
272
|
+
disabled: r.isPending,
|
|
273
|
+
children: r.isPending ? "Saving…" : "Resolve"
|
|
274
|
+
})] }),
|
|
275
|
+
children: /* @__PURE__ */ k("div", {
|
|
276
|
+
className: "space-y-3",
|
|
277
|
+
children: [
|
|
278
|
+
p && /* @__PURE__ */ O(o, {
|
|
279
|
+
tone: "danger",
|
|
280
|
+
children: p
|
|
281
|
+
}),
|
|
282
|
+
/* @__PURE__ */ O(l, {
|
|
283
|
+
label: "Outcome",
|
|
284
|
+
children: /* @__PURE__ */ O(f, {
|
|
285
|
+
value: i,
|
|
286
|
+
onChange: (e) => s(e.target.value),
|
|
287
|
+
children: M.map((e) => /* @__PURE__ */ O("option", {
|
|
288
|
+
value: e,
|
|
289
|
+
children: C(e)
|
|
290
|
+
}, e))
|
|
291
|
+
})
|
|
292
|
+
}),
|
|
293
|
+
/* @__PURE__ */ O(l, {
|
|
294
|
+
label: "Notes",
|
|
295
|
+
children: /* @__PURE__ */ O(v, {
|
|
296
|
+
value: c,
|
|
297
|
+
onChange: (e) => u(e.target.value)
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
]
|
|
301
|
+
})
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
//#endregion
|
|
305
|
+
export { P as CompliancePage };
|
|
306
|
+
|
|
307
|
+
//# sourceMappingURL=CompliancePage.js.map
|