@burdenoff/microfe-proserviceworld 2026.720.1 → 2026.728.2

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 (37) hide show
  1. package/dist/proserviceworld/ProServiceWorldRoot.d.ts.map +1 -1
  2. package/dist/proserviceworld/ProServiceWorldRoot.js +49 -19
  3. package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
  4. package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -1
  5. package/dist/proserviceworld/ProServiceWorldRoutes.js +62 -27
  6. package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
  7. package/dist/proserviceworld/components/ui.d.ts.map +1 -1
  8. package/dist/proserviceworld/components/ui.js +7 -1
  9. package/dist/proserviceworld/components/ui.js.map +1 -1
  10. package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts +187 -0
  11. package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts.map +1 -1
  12. package/dist/proserviceworld/hooks/useProServiceWorldApi.js +416 -140
  13. package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
  14. package/dist/proserviceworld/pages/ClientPortalAccessPage.d.ts +4 -0
  15. package/dist/proserviceworld/pages/ClientPortalAccessPage.d.ts.map +1 -0
  16. package/dist/proserviceworld/pages/ClientPortalAccessPage.js +191 -0
  17. package/dist/proserviceworld/pages/ClientPortalAccessPage.js.map +1 -0
  18. package/dist/proserviceworld/pages/DashboardPage.d.ts.map +1 -1
  19. package/dist/proserviceworld/pages/DashboardPage.js +94 -82
  20. package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
  21. package/dist/proserviceworld/pages/MyPortalPage.d.ts +4 -0
  22. package/dist/proserviceworld/pages/MyPortalPage.d.ts.map +1 -0
  23. package/dist/proserviceworld/pages/MyPortalPage.js +162 -0
  24. package/dist/proserviceworld/pages/MyPortalPage.js.map +1 -0
  25. package/dist/proserviceworld/pages/MySignaturesPage.d.ts +4 -0
  26. package/dist/proserviceworld/pages/MySignaturesPage.d.ts.map +1 -0
  27. package/dist/proserviceworld/pages/MySignaturesPage.js +240 -0
  28. package/dist/proserviceworld/pages/MySignaturesPage.js.map +1 -0
  29. package/dist/proserviceworld/pages/SignaturesPage.d.ts +4 -0
  30. package/dist/proserviceworld/pages/SignaturesPage.d.ts.map +1 -0
  31. package/dist/proserviceworld/pages/SignaturesPage.js +317 -0
  32. package/dist/proserviceworld/pages/SignaturesPage.js.map +1 -0
  33. package/dist/proserviceworld/pages/TrustPage.d.ts +14 -0
  34. package/dist/proserviceworld/pages/TrustPage.d.ts.map +1 -0
  35. package/dist/proserviceworld/pages/TrustPage.js +603 -0
  36. package/dist/proserviceworld/pages/TrustPage.js.map +1 -0
  37. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MyPortalPage.js","names":[],"sources":["../../../src/proserviceworld/pages/MyPortalPage.tsx"],"sourcesContent":["/**\n * My Portal — client-facing self-service view (BOFF-4283).\n *\n * MVP scope: view my matters, my invoices, view/download my (PUBLIC) documents.\n * Everything here is self-scoped server-side (pswMyMatters/pswMyInvoices/\n * pswMyDocuments/pswMyClientPortalClients) — the calling actor's ACTIVE\n * PswClientPortalUser link IS the access control; there is no clientId\n * argument this page could ever pass to see someone else's data.\n *\n * Deferred/future scope (not half-built): in-portal messaging, e-signature\n * requests surfaced directly here, and online payment — see BOFF-4283.\n */\nimport type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Contact, FolderKanban, Receipt, FileText, Download } from 'lucide-react';\nimport {\n useMyClientPortalClients,\n useMyMatters,\n useMyInvoices,\n useMyDocuments,\n} from '../hooks/useProServiceWorldApi';\nimport { getDocumentDownloadUrl } from '../hooks/useFiles';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Banner,\n TableWrap,\n Th,\n Td,\n fmtDate,\n fmtMoney,\n} from '../components/ui';\n\ntype Tab = 'matters' | 'invoices' | 'documents';\n\nexport const MyPortalPage: FC = () => {\n const {\n data: clients,\n isLoading: clientsLoading,\n error: clientsError,\n } = useMyClientPortalClients();\n const [tab, setTab] = useState<Tab>('matters');\n\n if (clientsLoading) return <LoadingBlock />;\n if (clientsError) return <ErrorState error={clientsError} />;\n\n if (!clients || clients.length === 0) {\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"My portal\"\n subtitle=\"View your matters, invoices, and documents.\"\n icon={<Contact className=\"h-6 w-6\" />}\n />\n <Empty\n title=\"No portal access\"\n description=\"You don't have client-portal access in this workspace yet. Ask your firm to grant it.\"\n icon={<Contact className=\"h-8 w-8\" />}\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"My portal\"\n subtitle={`Viewing ${clients.map((c) => c.name).join(', ')}`}\n icon={<Contact className=\"h-6 w-6\" />}\n />\n\n <div className=\"flex gap-2 border-b border-border-seam\">\n {(\n [\n { id: 'matters', label: 'My matters', icon: FolderKanban },\n { id: 'invoices', label: 'My invoices', icon: Receipt },\n { id: 'documents', label: 'My documents', icon: FileText },\n ] as const\n ).map((t) => (\n <button\n key={t.id}\n type=\"button\"\n onClick={() => setTab(t.id)}\n className={`flex items-center gap-1.5 border-b-2 px-3 py-2 text-sm font-medium ${\n tab === t.id\n ? 'border-accent text-accent'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n }`}\n >\n <t.icon className=\"h-4 w-4\" />\n {t.label}\n </button>\n ))}\n </div>\n\n {tab === 'matters' && <MyMattersTab />}\n {tab === 'invoices' && <MyInvoicesTab />}\n {tab === 'documents' && <MyDocumentsTab />}\n </div>\n );\n};\n\nconst MyMattersTab: FC = () => {\n const { data, isLoading, error, refetch } = useMyMatters();\n const items = data?.items ?? [];\n if (isLoading) return <LoadingBlock />;\n if (error) return <ErrorState error={error} onRetry={() => void refetch()} />;\n if (items.length === 0) {\n return <Empty title=\"No matters yet\" icon={<FolderKanban className=\"h-8 w-8\" />} />;\n }\n return (\n <SectionCard\n title={`${data?.totalCount ?? items.length} matter${(data?.totalCount ?? items.length) === 1 ? '' : 's'}`}\n >\n <TableWrap>\n <thead>\n <tr>\n <Th>Title</Th>\n <Th>Status</Th>\n <Th>Priority</Th>\n <Th>Due</Th>\n </tr>\n </thead>\n <tbody>\n {items.map((m) => (\n <tr key={m.id}>\n <Td className=\"font-medium text-text-primary\">{m.title}</Td>\n <Td>\n <StatusPill status={m.status} />\n </Td>\n <Td>\n <StatusPill status={m.priority} />\n </Td>\n <Td>{fmtDate(m.dueDate)}</Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n );\n};\n\nconst MyInvoicesTab: FC = () => {\n const { data, isLoading, error, refetch } = useMyInvoices();\n const items = data?.items ?? [];\n if (isLoading) return <LoadingBlock />;\n if (error) return <ErrorState error={error} onRetry={() => void refetch()} />;\n if (items.length === 0) {\n return <Empty title=\"No invoices yet\" icon={<Receipt className=\"h-8 w-8\" />} />;\n }\n return (\n <SectionCard\n title={`${data?.totalCount ?? items.length} invoice${(data?.totalCount ?? items.length) === 1 ? '' : 's'}`}\n >\n <TableWrap>\n <thead>\n <tr>\n <Th>Number</Th>\n <Th>Status</Th>\n <Th>Amount</Th>\n <Th>Due</Th>\n </tr>\n </thead>\n <tbody>\n {items.map((i) => (\n <tr key={i.id}>\n <Td className=\"font-medium text-text-primary\">{i.number}</Td>\n <Td>\n <StatusPill status={i.status} />\n </Td>\n <Td>{fmtMoney(i.total, i.currency)}</Td>\n <Td>{fmtDate(i.dueDate)}</Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n );\n};\n\nconst MyDocumentsTab: FC = () => {\n const { data, isLoading, error, refetch } = useMyDocuments();\n const items = data?.items ?? [];\n const [banner, setBanner] = useState<string | null>(null);\n\n const download = async (fileId: string) => {\n try {\n const url = await getDocumentDownloadUrl(fileId);\n window.open(url, '_blank', 'noopener');\n } catch (e) {\n setBanner(e instanceof Error ? e.message : 'Could not open document.');\n }\n };\n\n if (isLoading) return <LoadingBlock />;\n if (error) return <ErrorState error={error} onRetry={() => void refetch()} />;\n if (items.length === 0) {\n return <Empty title=\"No documents yet\" icon={<FileText className=\"h-8 w-8\" />} />;\n }\n return (\n <div className=\"space-y-3\">\n {banner && (\n <Banner tone=\"danger\" onClose={() => setBanner(null)}>\n {banner}\n </Banner>\n )}\n <SectionCard\n title={`${data?.totalCount ?? items.length} document${(data?.totalCount ?? items.length) === 1 ? '' : 's'}`}\n >\n <TableWrap>\n <thead>\n <tr>\n <Th>Name</Th>\n <Th>Added</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {items.map((d) => (\n <tr key={d.id}>\n <Td className=\"font-medium text-text-primary\">{d.name}</Td>\n <Td>{fmtDate(d.createdAt)}</Td>\n <Td className=\"text-right\">\n {d.file?.id && (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void download(d.file!.id)}>\n <Download className=\"mr-1.5 h-4 w-4\" />\n Download\n </Button>\n )}\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const MySignaturesPage: FC;
4
+ //# sourceMappingURL=MySignaturesPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MySignaturesPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/MySignaturesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA6BhC,eAAO,MAAM,gBAAgB,EAAE,EA+F9B,CAAC"}
@@ -0,0 +1,240 @@
1
+ import { useDeclineSignatureRequest as e, useMyPendingSignatureRequests as t, useSignDocument as n, useViewSignatureRequest as r } from "../hooks/useProServiceWorldApi.js";
2
+ import { Banner as i, Empty as a, ErrorState as o, Field as s, LoadingBlock as c, Modal as l, NativeSelect as u, PageHeader as d, SectionCard as f, StatusPill as p, TableWrap as m, Td as h, TextInput as g, Th as _, fmtDate as v } from "../components/ui.js";
3
+ import { useRef as y, useState as b } from "react";
4
+ import { Check as x, Eraser as S, PenTool as C, X as w } from "lucide-react";
5
+ import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
6
+ import { Button as O } from "@burdenoff/fe-libs/ui";
7
+ //#region src/proserviceworld/pages/MySignaturesPage.tsx
8
+ var k = () => {
9
+ let { data: e, isLoading: n, error: s, refetch: l } = t(), u = e?.items ?? [], [g, y] = b(null), [x, S] = b(null), w = r(), T = (e) => {
10
+ y(e), e.status === "PENDING" && w.mutate(e.id, { onSuccess: () => void l() });
11
+ };
12
+ return /* @__PURE__ */ D("div", {
13
+ className: "space-y-6",
14
+ children: [
15
+ /* @__PURE__ */ E(d, {
16
+ title: "My signatures",
17
+ subtitle: "Documents that need your signature.",
18
+ icon: /* @__PURE__ */ E(C, { className: "h-6 w-6" })
19
+ }),
20
+ x && /* @__PURE__ */ E(i, {
21
+ tone: x.tone,
22
+ onClose: () => S(null),
23
+ children: x.text
24
+ }),
25
+ n ? /* @__PURE__ */ E(c, {}) : s ? /* @__PURE__ */ E(o, {
26
+ error: s,
27
+ onRetry: () => void l()
28
+ }) : u.length === 0 ? /* @__PURE__ */ E(a, {
29
+ title: "Nothing to sign",
30
+ description: "You have no pending signature requests.",
31
+ icon: /* @__PURE__ */ E(C, { className: "h-8 w-8" })
32
+ }) : /* @__PURE__ */ E(f, {
33
+ title: `${u.length} pending`,
34
+ children: /* @__PURE__ */ D(m, { children: [/* @__PURE__ */ E("thead", { children: /* @__PURE__ */ D("tr", { children: [
35
+ /* @__PURE__ */ E(_, { children: "Document" }),
36
+ /* @__PURE__ */ E(_, { children: "Status" }),
37
+ /* @__PURE__ */ E(_, { children: "Requested" }),
38
+ /* @__PURE__ */ E(_, {
39
+ className: "text-right",
40
+ children: "Actions"
41
+ })
42
+ ] }) }), /* @__PURE__ */ E("tbody", { children: u.map((e) => /* @__PURE__ */ D("tr", {
43
+ className: "hover:bg-bg-sunken/50",
44
+ children: [
45
+ /* @__PURE__ */ D(h, { children: [/* @__PURE__ */ E("span", {
46
+ className: "font-medium text-text-primary",
47
+ children: e.signatureRequest?.document?.name ?? e.signatureRequest?.documentId ?? "—"
48
+ }), e.signatureRequest?.message && /* @__PURE__ */ E("p", {
49
+ className: "text-xs text-text-tertiary",
50
+ children: e.signatureRequest.message
51
+ })] }),
52
+ /* @__PURE__ */ E(h, { children: /* @__PURE__ */ E(p, { status: e.status }) }),
53
+ /* @__PURE__ */ E(h, { children: v(e.createdAt) }),
54
+ /* @__PURE__ */ E(h, {
55
+ className: "text-right",
56
+ children: /* @__PURE__ */ E(O, {
57
+ size: "sm",
58
+ onClick: () => T(e),
59
+ children: "Review & sign"
60
+ })
61
+ })
62
+ ]
63
+ }, e.id)) })] })
64
+ }),
65
+ g && /* @__PURE__ */ E(j, {
66
+ signer: g,
67
+ onClose: () => y(null),
68
+ onSigned: () => {
69
+ y(null), l(), S({
70
+ tone: "success",
71
+ text: "Signed."
72
+ });
73
+ },
74
+ onDeclined: () => {
75
+ y(null), l(), S({
76
+ tone: "success",
77
+ text: "Declined."
78
+ });
79
+ },
80
+ onFail: (e) => S({
81
+ tone: "danger",
82
+ text: e
83
+ })
84
+ })
85
+ ]
86
+ });
87
+ }, A = ({ onChange: e }) => {
88
+ let t = y(null), n = y(!1), [r, i] = b(!1), a = () => t.current?.getContext("2d") ?? null, o = (e) => {
89
+ let t = e.currentTarget.getBoundingClientRect();
90
+ return {
91
+ x: e.clientX - t.left,
92
+ y: e.clientY - t.top
93
+ };
94
+ }, s = (e) => {
95
+ n.current = !0;
96
+ let t = a();
97
+ if (!t) return;
98
+ let { x: r, y: i } = o(e);
99
+ t.beginPath(), t.moveTo(r, i);
100
+ }, c = (e) => {
101
+ if (!n.current) return;
102
+ let t = a();
103
+ if (!t) return;
104
+ let { x: r, y: s } = o(e);
105
+ t.lineTo(r, s), t.strokeStyle = getComputedStyle(e.currentTarget).color, t.lineWidth = 2, t.lineCap = "round", t.stroke(), i(!0);
106
+ }, l = () => {
107
+ n.current = !1;
108
+ let i = t.current;
109
+ e(i && r ? i.toDataURL("image/png") : null);
110
+ };
111
+ return /* @__PURE__ */ D("div", {
112
+ className: "space-y-2",
113
+ children: [/* @__PURE__ */ E("canvas", {
114
+ ref: t,
115
+ width: 480,
116
+ height: 160,
117
+ className: "w-full cursor-crosshair rounded-lg border border-border-seam bg-bg-canvas text-text-primary touch-none",
118
+ onPointerDown: s,
119
+ onPointerMove: c,
120
+ onPointerUp: l,
121
+ onPointerLeave: l
122
+ }), /* @__PURE__ */ D(O, {
123
+ type: "button",
124
+ variant: "outline",
125
+ size: "sm",
126
+ onClick: () => {
127
+ let n = t.current, r = a();
128
+ n && r && r.clearRect(0, 0, n.width, n.height), i(!1), e(null);
129
+ },
130
+ children: [/* @__PURE__ */ E(S, { className: "mr-1.5 h-3.5 w-3.5" }), "Clear"]
131
+ })]
132
+ });
133
+ }, j = ({ signer: t, onClose: r, onSigned: a, onDeclined: o, onFail: c }) => {
134
+ let d = n(), f = e(), [p, m] = b("TYPED"), [h, _] = b(""), [v, y] = b(null), [S, C] = b(!1), [k, j] = b(""), [M, N] = b(null), P = (e) => {
135
+ m(e), N(null);
136
+ }, F = () => {
137
+ if (N(null), p === "TYPED" && !h.trim()) return N("Type your full name to sign.");
138
+ if (p === "DRAWN" && !v) return N("Draw your signature to sign.");
139
+ d.mutate({
140
+ signerId: t.id,
141
+ input: p === "TYPED" ? {
142
+ signatureType: "TYPED",
143
+ typedName: h.trim()
144
+ } : {
145
+ signatureType: "DRAWN",
146
+ signatureImage: v
147
+ }
148
+ }, {
149
+ onSuccess: a,
150
+ onError: (e) => c(e instanceof Error ? e.message : "Failed to sign.")
151
+ });
152
+ }, I = () => {
153
+ f.mutate({
154
+ signerId: t.id,
155
+ reason: k.trim() || void 0
156
+ }, {
157
+ onSuccess: o,
158
+ onError: (e) => c(e instanceof Error ? e.message : "Failed to decline.")
159
+ });
160
+ }, L = d.isPending || f.isPending;
161
+ return /* @__PURE__ */ E(l, {
162
+ open: !0,
163
+ onClose: r,
164
+ title: `Sign — ${t.signatureRequest?.document?.name ?? "document"}`,
165
+ footer: S ? /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ E(O, {
166
+ variant: "outline",
167
+ onClick: () => C(!1),
168
+ disabled: L,
169
+ children: "Back"
170
+ }), /* @__PURE__ */ E(O, {
171
+ variant: "destructive",
172
+ onClick: I,
173
+ disabled: L,
174
+ children: f.isPending ? "Declining…" : "Confirm decline"
175
+ })] }) : /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ D(O, {
176
+ variant: "outline",
177
+ onClick: () => C(!0),
178
+ disabled: L,
179
+ children: [/* @__PURE__ */ E(w, { className: "mr-1.5 h-4 w-4" }), "Decline"]
180
+ }), /* @__PURE__ */ D(O, {
181
+ onClick: F,
182
+ disabled: L,
183
+ children: [/* @__PURE__ */ E(x, { className: "mr-1.5 h-4 w-4" }), d.isPending ? "Signing…" : "Sign"]
184
+ })] }),
185
+ children: /* @__PURE__ */ D("div", {
186
+ className: "space-y-3",
187
+ children: [
188
+ M && /* @__PURE__ */ E(i, {
189
+ tone: "danger",
190
+ children: M
191
+ }),
192
+ t.signatureRequest?.message && /* @__PURE__ */ E("p", {
193
+ className: "text-sm text-text-secondary",
194
+ children: t.signatureRequest.message
195
+ }),
196
+ S ? /* @__PURE__ */ E(s, {
197
+ label: "Reason (optional)",
198
+ children: /* @__PURE__ */ E(g, {
199
+ value: k,
200
+ onChange: (e) => j(e.target.value),
201
+ placeholder: "Why are you declining?"
202
+ })
203
+ }) : /* @__PURE__ */ D(T, { children: [
204
+ /* @__PURE__ */ E(s, {
205
+ label: "Signature method",
206
+ children: /* @__PURE__ */ D(u, {
207
+ value: p,
208
+ onChange: (e) => P(e.target.value),
209
+ children: [/* @__PURE__ */ E("option", {
210
+ value: "TYPED",
211
+ children: "Type my name"
212
+ }), /* @__PURE__ */ E("option", {
213
+ value: "DRAWN",
214
+ children: "Draw my signature"
215
+ })]
216
+ })
217
+ }),
218
+ p === "TYPED" ? /* @__PURE__ */ E(s, {
219
+ label: "Full legal name",
220
+ children: /* @__PURE__ */ E(g, {
221
+ value: h,
222
+ onChange: (e) => _(e.target.value)
223
+ })
224
+ }) : /* @__PURE__ */ E(s, {
225
+ label: "Draw below",
226
+ children: /* @__PURE__ */ E(A, { onChange: y })
227
+ }),
228
+ /* @__PURE__ */ E("p", {
229
+ className: "text-xs text-text-tertiary",
230
+ children: "By signing you confirm this is your legally binding signature on this document, recorded with your account, an immutable timestamp, and your IP address."
231
+ })
232
+ ] })
233
+ ]
234
+ })
235
+ });
236
+ };
237
+ //#endregion
238
+ export { k as MySignaturesPage };
239
+
240
+ //# sourceMappingURL=MySignaturesPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MySignaturesPage.js","names":[],"sources":["../../../src/proserviceworld/pages/MySignaturesPage.tsx"],"sourcesContent":["/**\n * My Signatures — signer-side self-service view (BOFF-4212).\n *\n * Self-scoped: shows only signature requests where the CALLING actor is a\n * signer (pswMyPendingSignatureRequests). Opening a request marks it VIEWED;\n * the signer then signs (typed name or a drawn signature captured from a\n * <canvas> pad, exported as a PNG data URI) or declines.\n */\nimport type { FC } from 'react';\nimport { useRef, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { PenTool, Eraser, Check, X } from 'lucide-react';\nimport {\n useMyPendingSignatureRequests,\n useViewSignatureRequest,\n useSignDocument,\n useDeclineSignatureRequest,\n type PswSignatureSigner,\n} from '../hooks/useProServiceWorldApi';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Modal,\n Field,\n TextInput,\n NativeSelect,\n Banner,\n TableWrap,\n Th,\n Td,\n fmtDate,\n} from '../components/ui';\n\nexport const MySignaturesPage: FC = () => {\n const { data, isLoading, error, refetch } = useMyPendingSignatureRequests();\n const items = data?.items ?? [];\n const [signing, setSigning] = useState<PswSignatureSigner | null>(null);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const view = useViewSignatureRequest();\n\n const open = (signer: PswSignatureSigner) => {\n setSigning(signer);\n if (signer.status === 'PENDING') {\n view.mutate(signer.id, { onSuccess: () => void refetch() });\n }\n };\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"My signatures\"\n subtitle=\"Documents that need your signature.\"\n icon={<PenTool className=\"h-6 w-6\" />}\n />\n\n {banner && (\n <Banner tone={banner.tone} onClose={() => setBanner(null)}>\n {banner.text}\n </Banner>\n )}\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : items.length === 0 ? (\n <Empty\n title=\"Nothing to sign\"\n description=\"You have no pending signature requests.\"\n icon={<PenTool className=\"h-8 w-8\" />}\n />\n ) : (\n <SectionCard title={`${items.length} pending`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Document</Th>\n <Th>Status</Th>\n <Th>Requested</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {items.map((s) => (\n <tr key={s.id} className=\"hover:bg-bg-sunken/50\">\n <Td>\n <span className=\"font-medium text-text-primary\">\n {s.signatureRequest?.document?.name ?? s.signatureRequest?.documentId ?? '—'}\n </span>\n {s.signatureRequest?.message && (\n <p className=\"text-xs text-text-tertiary\">{s.signatureRequest.message}</p>\n )}\n </Td>\n <Td>\n <StatusPill status={s.status} />\n </Td>\n <Td>{fmtDate(s.createdAt)}</Td>\n <Td className=\"text-right\">\n <Button size=\"sm\" onClick={() => open(s)}>\n Review & sign\n </Button>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {signing && (\n <SignModal\n signer={signing}\n onClose={() => setSigning(null)}\n onSigned={() => {\n setSigning(null);\n void refetch();\n setBanner({ tone: 'success', text: 'Signed.' });\n }}\n onDeclined={() => {\n setSigning(null);\n void refetch();\n setBanner({ tone: 'success', text: 'Declined.' });\n }}\n onFail={(msg) => setBanner({ tone: 'danger', text: msg })}\n />\n )}\n </div>\n );\n};\n\n/** Minimal pointer-driven signature pad — exports a PNG data URI. */\nconst SignaturePad: FC<{ onChange: (dataUri: string | null) => void }> = ({ onChange }) => {\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const drawingRef = useRef(false);\n const [hasInk, setHasInk] = useState(false);\n\n const getCtx = () => canvasRef.current?.getContext('2d') ?? null;\n\n const point = (e: React.PointerEvent<HTMLCanvasElement>) => {\n const rect = e.currentTarget.getBoundingClientRect();\n return { x: e.clientX - rect.left, y: e.clientY - rect.top };\n };\n\n const start = (e: React.PointerEvent<HTMLCanvasElement>) => {\n drawingRef.current = true;\n const ctx = getCtx();\n if (!ctx) return;\n const { x, y } = point(e);\n ctx.beginPath();\n ctx.moveTo(x, y);\n };\n const move = (e: React.PointerEvent<HTMLCanvasElement>) => {\n if (!drawingRef.current) return;\n const ctx = getCtx();\n if (!ctx) return;\n const { x, y } = point(e);\n ctx.lineTo(x, y);\n // Token-driven ink colour: the canvas carries `text-text-primary`, so its\n // computed `color` resolves the semantic token rather than a raw hex.\n ctx.strokeStyle = getComputedStyle(e.currentTarget).color;\n ctx.lineWidth = 2;\n ctx.lineCap = 'round';\n ctx.stroke();\n setHasInk(true);\n };\n const end = () => {\n drawingRef.current = false;\n const canvas = canvasRef.current;\n onChange(canvas && hasInk ? canvas.toDataURL('image/png') : null);\n };\n\n const clear = () => {\n const canvas = canvasRef.current;\n const ctx = getCtx();\n if (canvas && ctx) ctx.clearRect(0, 0, canvas.width, canvas.height);\n setHasInk(false);\n onChange(null);\n };\n\n return (\n <div className=\"space-y-2\">\n <canvas\n ref={canvasRef}\n width={480}\n height={160}\n className=\"w-full cursor-crosshair rounded-lg border border-border-seam bg-bg-canvas text-text-primary touch-none\"\n onPointerDown={start}\n onPointerMove={move}\n onPointerUp={end}\n onPointerLeave={end}\n />\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={clear}>\n <Eraser className=\"mr-1.5 h-3.5 w-3.5\" />\n Clear\n </Button>\n </div>\n );\n};\n\nconst SignModal: FC<{\n signer: PswSignatureSigner;\n onClose: () => void;\n onSigned: () => void;\n onDeclined: () => void;\n onFail: (msg: string) => void;\n}> = ({ signer, onClose, onSigned, onDeclined, onFail }) => {\n const sign = useSignDocument();\n const decline = useDeclineSignatureRequest();\n const [mode, setMode] = useState<'TYPED' | 'DRAWN'>('TYPED');\n const [typedName, setTypedName] = useState('');\n const [drawn, setDrawn] = useState<string | null>(null);\n const [declining, setDeclining] = useState(false);\n const [reason, setReason] = useState('');\n const [err, setErr] = useState<string | null>(null);\n\n const changeMode = (next: 'TYPED' | 'DRAWN') => {\n setMode(next);\n setErr(null);\n };\n\n const submitSign = () => {\n setErr(null);\n if (mode === 'TYPED' && !typedName.trim()) return setErr('Type your full name to sign.');\n if (mode === 'DRAWN' && !drawn) return setErr('Draw your signature to sign.');\n sign.mutate(\n {\n signerId: signer.id,\n input:\n mode === 'TYPED'\n ? { signatureType: 'TYPED', typedName: typedName.trim() }\n : { signatureType: 'DRAWN', signatureImage: drawn },\n },\n {\n onSuccess: onSigned,\n onError: (e) => onFail(e instanceof Error ? e.message : 'Failed to sign.'),\n }\n );\n };\n\n const submitDecline = () => {\n decline.mutate(\n { signerId: signer.id, reason: reason.trim() || undefined },\n {\n onSuccess: onDeclined,\n onError: (e) => onFail(e instanceof Error ? e.message : 'Failed to decline.'),\n }\n );\n };\n\n const busy = sign.isPending || decline.isPending;\n\n return (\n <Modal\n open\n onClose={onClose}\n title={`Sign — ${signer.signatureRequest?.document?.name ?? 'document'}`}\n footer={\n declining ? (\n <>\n <Button variant=\"outline\" onClick={() => setDeclining(false)} disabled={busy}>\n Back\n </Button>\n <Button variant=\"destructive\" onClick={submitDecline} disabled={busy}>\n {decline.isPending ? 'Declining…' : 'Confirm decline'}\n </Button>\n </>\n ) : (\n <>\n <Button variant=\"outline\" onClick={() => setDeclining(true)} disabled={busy}>\n <X className=\"mr-1.5 h-4 w-4\" />\n Decline\n </Button>\n <Button onClick={submitSign} disabled={busy}>\n <Check className=\"mr-1.5 h-4 w-4\" />\n {sign.isPending ? 'Signing…' : 'Sign'}\n </Button>\n </>\n )\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n {signer.signatureRequest?.message && (\n <p className=\"text-sm text-text-secondary\">{signer.signatureRequest.message}</p>\n )}\n\n {declining ? (\n <Field label=\"Reason (optional)\">\n <TextInput\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n placeholder=\"Why are you declining?\"\n />\n </Field>\n ) : (\n <>\n <Field label=\"Signature method\">\n <NativeSelect\n value={mode}\n onChange={(e) => changeMode(e.target.value as 'TYPED' | 'DRAWN')}\n >\n <option value=\"TYPED\">Type my name</option>\n <option value=\"DRAWN\">Draw my signature</option>\n </NativeSelect>\n </Field>\n {mode === 'TYPED' ? (\n <Field label=\"Full legal name\">\n <TextInput value={typedName} onChange={(e) => setTypedName(e.target.value)} />\n </Field>\n ) : (\n <Field label=\"Draw below\">\n <SignaturePad onChange={setDrawn} />\n </Field>\n )}\n <p className=\"text-xs text-text-tertiary\">\n By signing you confirm this is your legally binding signature on this document,\n recorded with your account, an immutable timestamp, and your IP address.\n </p>\n </>\n )}\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const SignaturesPage: FC;
4
+ //# sourceMappingURL=SignaturesPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignaturesPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/SignaturesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAkChC,eAAO,MAAM,cAAc,EAAE,EA+J5B,CAAC"}
@@ -0,0 +1,317 @@
1
+ import { useCancelSignatureRequest as e, useCreateSignatureRequest as t, useDocuments as n, useSendSignatureRequest as r, useSignatureAuditEvents as i, useSignatureRequests 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, fmtDate as x } from "../components/ui.js";
3
+ import { useState as S } from "react";
4
+ import { FileSignature as C, History as w, Plus as T, Send as E, Trash2 as D, XCircle as O } from "lucide-react";
5
+ import { Fragment as k, jsx as A, jsxs as j } from "react/jsx-runtime";
6
+ import { Button as M } from "@burdenoff/fe-libs/ui";
7
+ //#region src/proserviceworld/pages/SignaturesPage.tsx
8
+ var N = /* @__PURE__ */ new Set([
9
+ "DRAFT",
10
+ "SENT",
11
+ "VIEWED",
12
+ "PARTIALLY_SIGNED"
13
+ ]), P = () => {
14
+ let { data: t, isLoading: n, error: i, refetch: l } = a(), d = t?.items ?? [], [f, v] = S(!1), [y, D] = S(null), [k, P] = S(null), L = r(), R = e();
15
+ return /* @__PURE__ */ j("div", {
16
+ className: "space-y-6",
17
+ children: [
18
+ /* @__PURE__ */ A(p, {
19
+ title: "Signatures",
20
+ subtitle: "Request, track, and audit e-signatures on your practice's documents.",
21
+ icon: /* @__PURE__ */ A(C, { className: "h-6 w-6" }),
22
+ actions: /* @__PURE__ */ j(M, {
23
+ onClick: () => v(!0),
24
+ children: [/* @__PURE__ */ A(T, { className: "mr-1.5 h-4 w-4" }), "New signature request"]
25
+ })
26
+ }),
27
+ k && /* @__PURE__ */ A(o, {
28
+ tone: k.tone,
29
+ onClose: () => P(null),
30
+ children: k.text
31
+ }),
32
+ n ? /* @__PURE__ */ A(u, {}) : i ? /* @__PURE__ */ A(c, {
33
+ error: i,
34
+ onRetry: () => void l()
35
+ }) : d.length === 0 ? /* @__PURE__ */ A(s, {
36
+ title: "No signature requests",
37
+ description: "Request a signature on an engagement letter or other document.",
38
+ icon: /* @__PURE__ */ A(C, { className: "h-8 w-8" }),
39
+ action: /* @__PURE__ */ j(M, {
40
+ onClick: () => v(!0),
41
+ children: [/* @__PURE__ */ A(T, { className: "mr-1.5 h-4 w-4" }), "New signature request"]
42
+ })
43
+ }) : /* @__PURE__ */ A(m, {
44
+ title: `${t?.totalCount ?? d.length} request${(t?.totalCount ?? d.length) === 1 ? "" : "s"}`,
45
+ children: /* @__PURE__ */ j(g, { children: [/* @__PURE__ */ A("thead", { children: /* @__PURE__ */ j("tr", { children: [
46
+ /* @__PURE__ */ A(b, { children: "Document" }),
47
+ /* @__PURE__ */ A(b, { children: "Status" }),
48
+ /* @__PURE__ */ A(b, { children: "Signers" }),
49
+ /* @__PURE__ */ A(b, { children: "Sent" }),
50
+ /* @__PURE__ */ A(b, {
51
+ className: "text-right",
52
+ children: "Actions"
53
+ })
54
+ ] }) }), /* @__PURE__ */ A("tbody", { children: d.map((e) => /* @__PURE__ */ j("tr", {
55
+ className: "hover:bg-bg-sunken/50",
56
+ children: [
57
+ /* @__PURE__ */ A(_, { children: /* @__PURE__ */ A("span", {
58
+ className: "font-medium text-text-primary",
59
+ children: e.document?.name ?? e.documentId
60
+ }) }),
61
+ /* @__PURE__ */ A(_, { children: /* @__PURE__ */ A(h, { status: e.status }) }),
62
+ /* @__PURE__ */ A(_, { children: /* @__PURE__ */ A("div", {
63
+ className: "flex flex-col gap-0.5",
64
+ children: e.signers.map((e) => /* @__PURE__ */ j("span", {
65
+ className: "text-xs text-text-secondary",
66
+ children: [
67
+ e.name,
68
+ " — ",
69
+ /* @__PURE__ */ A(h, { status: e.status })
70
+ ]
71
+ }, e.id))
72
+ }) }),
73
+ /* @__PURE__ */ A(_, { children: x(e.sentAt) }),
74
+ /* @__PURE__ */ A(_, {
75
+ className: "text-right",
76
+ children: /* @__PURE__ */ j("div", {
77
+ className: "flex justify-end gap-1",
78
+ children: [
79
+ e.status === "DRAFT" && /* @__PURE__ */ A(M, {
80
+ variant: "ghost",
81
+ size: "sm",
82
+ "aria-label": "Send",
83
+ disabled: L.isPending,
84
+ onClick: () => L.mutate(e.id, {
85
+ onSuccess: () => P({
86
+ tone: "success",
87
+ text: "Signature request sent."
88
+ }),
89
+ onError: (e) => P({
90
+ tone: "danger",
91
+ text: e instanceof Error ? e.message : "Failed."
92
+ })
93
+ }),
94
+ children: /* @__PURE__ */ A(E, { className: "h-4 w-4" })
95
+ }),
96
+ /* @__PURE__ */ A(M, {
97
+ variant: "ghost",
98
+ size: "sm",
99
+ "aria-label": "Audit trail",
100
+ onClick: () => D(e),
101
+ children: /* @__PURE__ */ A(w, { className: "h-4 w-4" })
102
+ }),
103
+ N.has(e.status) && /* @__PURE__ */ A(M, {
104
+ variant: "ghost",
105
+ size: "sm",
106
+ "aria-label": "Cancel",
107
+ disabled: R.isPending,
108
+ onClick: () => R.mutate(e.id, {
109
+ onSuccess: () => P({
110
+ tone: "success",
111
+ text: "Signature request cancelled."
112
+ }),
113
+ onError: (e) => P({
114
+ tone: "danger",
115
+ text: e instanceof Error ? e.message : "Failed."
116
+ })
117
+ }),
118
+ children: /* @__PURE__ */ A(O, { className: "h-4 w-4" })
119
+ })
120
+ ]
121
+ })
122
+ })
123
+ ]
124
+ }, e.id)) })] })
125
+ }),
126
+ f && /* @__PURE__ */ A(I, {
127
+ onClose: () => v(!1),
128
+ onDone: () => {
129
+ l(), P({
130
+ tone: "success",
131
+ text: "Signature request created and sent."
132
+ });
133
+ },
134
+ onFail: (e) => P({
135
+ tone: "danger",
136
+ text: e
137
+ })
138
+ }),
139
+ y && /* @__PURE__ */ A(F, {
140
+ request: y,
141
+ onClose: () => D(null)
142
+ })
143
+ ]
144
+ });
145
+ }, F = ({ request: e, onClose: t }) => {
146
+ let { data: n, isLoading: r, error: a } = i(e.id);
147
+ return /* @__PURE__ */ A(d, {
148
+ open: !0,
149
+ onClose: t,
150
+ title: `Audit trail — ${e.document?.name ?? e.documentId}`,
151
+ children: r ? /* @__PURE__ */ A(u, {}) : a ? /* @__PURE__ */ A(c, { error: a }) : !n || n.items.length === 0 ? /* @__PURE__ */ A(s, {
152
+ title: "No events yet",
153
+ icon: /* @__PURE__ */ A(w, { className: "h-8 w-8" })
154
+ }) : /* @__PURE__ */ A("ul", {
155
+ className: "space-y-2 text-sm",
156
+ children: n.items.map((e) => /* @__PURE__ */ j("li", {
157
+ className: "rounded-lg border border-border-seam p-2",
158
+ children: [/* @__PURE__ */ j("div", {
159
+ className: "flex items-center justify-between",
160
+ children: [/* @__PURE__ */ A("span", {
161
+ className: "font-medium text-text-primary",
162
+ children: e.eventType
163
+ }), /* @__PURE__ */ A("span", {
164
+ className: "text-text-tertiary",
165
+ children: x(e.createdAt)
166
+ })]
167
+ }), /* @__PURE__ */ j("div", {
168
+ className: "text-text-secondary",
169
+ children: [
170
+ e.actorLabel,
171
+ e.ipAddress ? ` · ${e.ipAddress}` : "",
172
+ e.documentVersion == null ? "" : ` · doc v${e.documentVersion}`
173
+ ]
174
+ })]
175
+ }, e.id))
176
+ })
177
+ });
178
+ }, I = ({ onClose: e, onDone: i, onFail: a }) => {
179
+ let { data: s } = n(), c = t(), u = r(), [p, m] = S(""), [h, g] = S(""), [_, b] = S([{
180
+ actorId: "",
181
+ name: "",
182
+ email: ""
183
+ }]), [x, C] = S(null), w = (e, t) => b((n) => n.map((n, r) => r === e ? {
184
+ ...n,
185
+ ...t
186
+ } : n)), E = async () => {
187
+ if (C(null), !p) return C("Choose a document.");
188
+ let t = _.filter((e) => e.actorId.trim() && e.name.trim() && e.email.trim());
189
+ if (t.length === 0) return C("Add at least one signer (platform user ID, name, email).");
190
+ c.mutate({
191
+ documentId: p,
192
+ message: h.trim() || null,
193
+ signers: t.map((e) => ({
194
+ actorId: e.actorId.trim(),
195
+ name: e.name.trim(),
196
+ email: e.email.trim()
197
+ }))
198
+ }, {
199
+ onSuccess: (t) => {
200
+ let n = t.pswCreateSignatureRequest?.id;
201
+ if (!n) {
202
+ i(), e();
203
+ return;
204
+ }
205
+ u.mutate(n, {
206
+ onSuccess: () => {
207
+ i(), e();
208
+ },
209
+ onError: (e) => a(e instanceof Error ? e.message : "Created but failed to send.")
210
+ });
211
+ },
212
+ onError: (e) => C(e instanceof Error ? e.message : "Failed.")
213
+ });
214
+ }, O = c.isPending || u.isPending;
215
+ return /* @__PURE__ */ A(d, {
216
+ open: !0,
217
+ onClose: e,
218
+ title: "New signature request",
219
+ footer: /* @__PURE__ */ j(k, { children: [/* @__PURE__ */ A(M, {
220
+ variant: "outline",
221
+ onClick: e,
222
+ disabled: O,
223
+ children: "Cancel"
224
+ }), /* @__PURE__ */ A(M, {
225
+ onClick: () => void E(),
226
+ disabled: O,
227
+ children: O ? "Sending…" : "Create & send"
228
+ })] }),
229
+ children: /* @__PURE__ */ j("div", {
230
+ className: "space-y-3",
231
+ children: [
232
+ x && /* @__PURE__ */ A(o, {
233
+ tone: "danger",
234
+ children: x
235
+ }),
236
+ /* @__PURE__ */ A(l, {
237
+ label: "Document",
238
+ required: !0,
239
+ children: /* @__PURE__ */ j(f, {
240
+ value: p,
241
+ onChange: (e) => m(e.target.value),
242
+ children: [/* @__PURE__ */ A("option", {
243
+ value: "",
244
+ children: "Select a document…"
245
+ }), (s?.items ?? []).map((e) => /* @__PURE__ */ j("option", {
246
+ value: e.id,
247
+ children: [
248
+ e.name,
249
+ " (v",
250
+ e.version,
251
+ ")"
252
+ ]
253
+ }, e.id))]
254
+ })
255
+ }),
256
+ /* @__PURE__ */ A(l, {
257
+ label: "Message",
258
+ hint: "Optional note shown to signers.",
259
+ children: /* @__PURE__ */ A(v, {
260
+ value: h,
261
+ onChange: (e) => g(e.target.value)
262
+ })
263
+ }),
264
+ /* @__PURE__ */ A(l, {
265
+ label: "Signers",
266
+ hint: "Each signer must already be a platform actor — a workspace member or a client-portal contact (see Client Portal Access for their platform user ID).",
267
+ children: /* @__PURE__ */ j("div", {
268
+ className: "space-y-2",
269
+ children: [_.map((e, t) => /* @__PURE__ */ j("div", {
270
+ className: "grid grid-cols-3 gap-2",
271
+ children: [
272
+ /* @__PURE__ */ A(y, {
273
+ placeholder: "Platform user ID",
274
+ value: e.actorId,
275
+ onChange: (e) => w(t, { actorId: e.target.value })
276
+ }),
277
+ /* @__PURE__ */ A(y, {
278
+ placeholder: "Name",
279
+ value: e.name,
280
+ onChange: (e) => w(t, { name: e.target.value })
281
+ }),
282
+ /* @__PURE__ */ A(y, {
283
+ placeholder: "Email",
284
+ value: e.email,
285
+ onChange: (e) => w(t, { email: e.target.value })
286
+ })
287
+ ]
288
+ }, t)), /* @__PURE__ */ j("div", {
289
+ className: "flex items-center gap-2",
290
+ children: [/* @__PURE__ */ j(M, {
291
+ type: "button",
292
+ variant: "outline",
293
+ size: "sm",
294
+ onClick: () => b((e) => [...e, {
295
+ actorId: "",
296
+ name: "",
297
+ email: ""
298
+ }]),
299
+ children: [/* @__PURE__ */ A(T, { className: "mr-1 h-3.5 w-3.5" }), "Add signer"]
300
+ }), _.length > 1 && /* @__PURE__ */ j(M, {
301
+ type: "button",
302
+ variant: "ghost",
303
+ size: "sm",
304
+ onClick: () => b((e) => e.slice(0, -1)),
305
+ children: [/* @__PURE__ */ A(D, { className: "mr-1 h-3.5 w-3.5" }), "Remove last"]
306
+ })]
307
+ })]
308
+ })
309
+ })
310
+ ]
311
+ })
312
+ });
313
+ };
314
+ //#endregion
315
+ export { P as SignaturesPage };
316
+
317
+ //# sourceMappingURL=SignaturesPage.js.map