@burdenoff/microfe-proserviceworld 2026.728.1 → 2026.731.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/proserviceworld/ProServiceWorldRoot.d.ts.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoot.js +44 -20
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js +51 -23
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/ui.d.ts.map +1 -1
- package/dist/proserviceworld/components/ui.js +7 -1
- package/dist/proserviceworld/components/ui.js.map +1 -1
- package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts +110 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts.map +1 -1
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +223 -49
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
- package/dist/proserviceworld/pages/ClientPortalAccessPage.d.ts +4 -0
- package/dist/proserviceworld/pages/ClientPortalAccessPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/ClientPortalAccessPage.js +191 -0
- package/dist/proserviceworld/pages/ClientPortalAccessPage.js.map +1 -0
- package/dist/proserviceworld/pages/MyPortalPage.d.ts +4 -0
- package/dist/proserviceworld/pages/MyPortalPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/MyPortalPage.js +162 -0
- package/dist/proserviceworld/pages/MyPortalPage.js.map +1 -0
- package/dist/proserviceworld/pages/MySignaturesPage.d.ts +4 -0
- package/dist/proserviceworld/pages/MySignaturesPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/MySignaturesPage.js +240 -0
- package/dist/proserviceworld/pages/MySignaturesPage.js.map +1 -0
- package/dist/proserviceworld/pages/SignaturesPage.d.ts +4 -0
- package/dist/proserviceworld/pages/SignaturesPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/SignaturesPage.js +317 -0
- package/dist/proserviceworld/pages/SignaturesPage.js.map +1 -0
- package/package.json +6 -6
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useMyClientPortalClients as e, useMyDocuments as t, useMyInvoices as n, useMyMatters as r } from "../hooks/useProServiceWorldApi.js";
|
|
2
|
+
import { Banner as i, Empty as a, ErrorState as o, LoadingBlock as s, PageHeader as c, SectionCard as l, StatusPill as u, TableWrap as d, Td as f, Th as p, fmtDate as m, fmtMoney as h } from "../components/ui.js";
|
|
3
|
+
import { getDocumentDownloadUrl as g } from "../hooks/useFiles.js";
|
|
4
|
+
import { useState as _ } from "react";
|
|
5
|
+
import { Contact as v, Download as y, FileText as b, FolderKanban as x, Receipt as S } from "lucide-react";
|
|
6
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
7
|
+
import { Button as T } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/proserviceworld/pages/MyPortalPage.tsx
|
|
9
|
+
var E = () => {
|
|
10
|
+
let { data: t, isLoading: n, error: r } = e(), [i, l] = _("matters");
|
|
11
|
+
return n ? /* @__PURE__ */ C(s, {}) : r ? /* @__PURE__ */ C(o, { error: r }) : !t || t.length === 0 ? /* @__PURE__ */ w("div", {
|
|
12
|
+
className: "space-y-6",
|
|
13
|
+
children: [/* @__PURE__ */ C(c, {
|
|
14
|
+
title: "My portal",
|
|
15
|
+
subtitle: "View your matters, invoices, and documents.",
|
|
16
|
+
icon: /* @__PURE__ */ C(v, { className: "h-6 w-6" })
|
|
17
|
+
}), /* @__PURE__ */ C(a, {
|
|
18
|
+
title: "No portal access",
|
|
19
|
+
description: "You don't have client-portal access in this workspace yet. Ask your firm to grant it.",
|
|
20
|
+
icon: /* @__PURE__ */ C(v, { className: "h-8 w-8" })
|
|
21
|
+
})]
|
|
22
|
+
}) : /* @__PURE__ */ w("div", {
|
|
23
|
+
className: "space-y-6",
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ C(c, {
|
|
26
|
+
title: "My portal",
|
|
27
|
+
subtitle: `Viewing ${t.map((e) => e.name).join(", ")}`,
|
|
28
|
+
icon: /* @__PURE__ */ C(v, { className: "h-6 w-6" })
|
|
29
|
+
}),
|
|
30
|
+
/* @__PURE__ */ C("div", {
|
|
31
|
+
className: "flex gap-2 border-b border-border-seam",
|
|
32
|
+
children: [
|
|
33
|
+
{
|
|
34
|
+
id: "matters",
|
|
35
|
+
label: "My matters",
|
|
36
|
+
icon: x
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "invoices",
|
|
40
|
+
label: "My invoices",
|
|
41
|
+
icon: S
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "documents",
|
|
45
|
+
label: "My documents",
|
|
46
|
+
icon: b
|
|
47
|
+
}
|
|
48
|
+
].map((e) => /* @__PURE__ */ w("button", {
|
|
49
|
+
type: "button",
|
|
50
|
+
onClick: () => l(e.id),
|
|
51
|
+
className: `flex items-center gap-1.5 border-b-2 px-3 py-2 text-sm font-medium ${i === e.id ? "border-accent text-accent" : "border-transparent text-text-secondary hover:text-text-primary"}`,
|
|
52
|
+
children: [/* @__PURE__ */ C(e.icon, { className: "h-4 w-4" }), e.label]
|
|
53
|
+
}, e.id))
|
|
54
|
+
}),
|
|
55
|
+
i === "matters" && /* @__PURE__ */ C(D, {}),
|
|
56
|
+
i === "invoices" && /* @__PURE__ */ C(O, {}),
|
|
57
|
+
i === "documents" && /* @__PURE__ */ C(k, {})
|
|
58
|
+
]
|
|
59
|
+
});
|
|
60
|
+
}, D = () => {
|
|
61
|
+
let { data: e, isLoading: t, error: n, refetch: i } = r(), c = e?.items ?? [];
|
|
62
|
+
return t ? /* @__PURE__ */ C(s, {}) : n ? /* @__PURE__ */ C(o, {
|
|
63
|
+
error: n,
|
|
64
|
+
onRetry: () => void i()
|
|
65
|
+
}) : c.length === 0 ? /* @__PURE__ */ C(a, {
|
|
66
|
+
title: "No matters yet",
|
|
67
|
+
icon: /* @__PURE__ */ C(x, { className: "h-8 w-8" })
|
|
68
|
+
}) : /* @__PURE__ */ C(l, {
|
|
69
|
+
title: `${e?.totalCount ?? c.length} matter${(e?.totalCount ?? c.length) === 1 ? "" : "s"}`,
|
|
70
|
+
children: /* @__PURE__ */ w(d, { children: [/* @__PURE__ */ C("thead", { children: /* @__PURE__ */ w("tr", { children: [
|
|
71
|
+
/* @__PURE__ */ C(p, { children: "Title" }),
|
|
72
|
+
/* @__PURE__ */ C(p, { children: "Status" }),
|
|
73
|
+
/* @__PURE__ */ C(p, { children: "Priority" }),
|
|
74
|
+
/* @__PURE__ */ C(p, { children: "Due" })
|
|
75
|
+
] }) }), /* @__PURE__ */ C("tbody", { children: c.map((e) => /* @__PURE__ */ w("tr", { children: [
|
|
76
|
+
/* @__PURE__ */ C(f, {
|
|
77
|
+
className: "font-medium text-text-primary",
|
|
78
|
+
children: e.title
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ C(f, { children: /* @__PURE__ */ C(u, { status: e.status }) }),
|
|
81
|
+
/* @__PURE__ */ C(f, { children: /* @__PURE__ */ C(u, { status: e.priority }) }),
|
|
82
|
+
/* @__PURE__ */ C(f, { children: m(e.dueDate) })
|
|
83
|
+
] }, e.id)) })] })
|
|
84
|
+
});
|
|
85
|
+
}, O = () => {
|
|
86
|
+
let { data: e, isLoading: t, error: r, refetch: i } = n(), c = e?.items ?? [];
|
|
87
|
+
return t ? /* @__PURE__ */ C(s, {}) : r ? /* @__PURE__ */ C(o, {
|
|
88
|
+
error: r,
|
|
89
|
+
onRetry: () => void i()
|
|
90
|
+
}) : c.length === 0 ? /* @__PURE__ */ C(a, {
|
|
91
|
+
title: "No invoices yet",
|
|
92
|
+
icon: /* @__PURE__ */ C(S, { className: "h-8 w-8" })
|
|
93
|
+
}) : /* @__PURE__ */ C(l, {
|
|
94
|
+
title: `${e?.totalCount ?? c.length} invoice${(e?.totalCount ?? c.length) === 1 ? "" : "s"}`,
|
|
95
|
+
children: /* @__PURE__ */ w(d, { children: [/* @__PURE__ */ C("thead", { children: /* @__PURE__ */ w("tr", { children: [
|
|
96
|
+
/* @__PURE__ */ C(p, { children: "Number" }),
|
|
97
|
+
/* @__PURE__ */ C(p, { children: "Status" }),
|
|
98
|
+
/* @__PURE__ */ C(p, { children: "Amount" }),
|
|
99
|
+
/* @__PURE__ */ C(p, { children: "Due" })
|
|
100
|
+
] }) }), /* @__PURE__ */ C("tbody", { children: c.map((e) => /* @__PURE__ */ w("tr", { children: [
|
|
101
|
+
/* @__PURE__ */ C(f, {
|
|
102
|
+
className: "font-medium text-text-primary",
|
|
103
|
+
children: e.number
|
|
104
|
+
}),
|
|
105
|
+
/* @__PURE__ */ C(f, { children: /* @__PURE__ */ C(u, { status: e.status }) }),
|
|
106
|
+
/* @__PURE__ */ C(f, { children: h(e.total, e.currency) }),
|
|
107
|
+
/* @__PURE__ */ C(f, { children: m(e.dueDate) })
|
|
108
|
+
] }, e.id)) })] })
|
|
109
|
+
});
|
|
110
|
+
}, k = () => {
|
|
111
|
+
let { data: e, isLoading: n, error: r, refetch: c } = t(), u = e?.items ?? [], [h, v] = _(null), x = async (e) => {
|
|
112
|
+
try {
|
|
113
|
+
let t = await g(e);
|
|
114
|
+
window.open(t, "_blank", "noopener");
|
|
115
|
+
} catch (e) {
|
|
116
|
+
v(e instanceof Error ? e.message : "Could not open document.");
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
return n ? /* @__PURE__ */ C(s, {}) : r ? /* @__PURE__ */ C(o, {
|
|
120
|
+
error: r,
|
|
121
|
+
onRetry: () => void c()
|
|
122
|
+
}) : u.length === 0 ? /* @__PURE__ */ C(a, {
|
|
123
|
+
title: "No documents yet",
|
|
124
|
+
icon: /* @__PURE__ */ C(b, { className: "h-8 w-8" })
|
|
125
|
+
}) : /* @__PURE__ */ w("div", {
|
|
126
|
+
className: "space-y-3",
|
|
127
|
+
children: [h && /* @__PURE__ */ C(i, {
|
|
128
|
+
tone: "danger",
|
|
129
|
+
onClose: () => v(null),
|
|
130
|
+
children: h
|
|
131
|
+
}), /* @__PURE__ */ C(l, {
|
|
132
|
+
title: `${e?.totalCount ?? u.length} document${(e?.totalCount ?? u.length) === 1 ? "" : "s"}`,
|
|
133
|
+
children: /* @__PURE__ */ w(d, { children: [/* @__PURE__ */ C("thead", { children: /* @__PURE__ */ w("tr", { children: [
|
|
134
|
+
/* @__PURE__ */ C(p, { children: "Name" }),
|
|
135
|
+
/* @__PURE__ */ C(p, { children: "Added" }),
|
|
136
|
+
/* @__PURE__ */ C(p, {
|
|
137
|
+
className: "text-right",
|
|
138
|
+
children: "Actions"
|
|
139
|
+
})
|
|
140
|
+
] }) }), /* @__PURE__ */ C("tbody", { children: u.map((e) => /* @__PURE__ */ w("tr", { children: [
|
|
141
|
+
/* @__PURE__ */ C(f, {
|
|
142
|
+
className: "font-medium text-text-primary",
|
|
143
|
+
children: e.name
|
|
144
|
+
}),
|
|
145
|
+
/* @__PURE__ */ C(f, { children: m(e.createdAt) }),
|
|
146
|
+
/* @__PURE__ */ C(f, {
|
|
147
|
+
className: "text-right",
|
|
148
|
+
children: e.file?.id && /* @__PURE__ */ w(T, {
|
|
149
|
+
variant: "ghost",
|
|
150
|
+
size: "sm",
|
|
151
|
+
onClick: () => void x(e.file.id),
|
|
152
|
+
children: [/* @__PURE__ */ C(y, { className: "mr-1.5 h-4 w-4" }), "Download"]
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
] }, e.id)) })] })
|
|
156
|
+
})]
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
//#endregion
|
|
160
|
+
export { E as MyPortalPage };
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=MyPortalPage.js.map
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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"}
|