@burdenoff/microfe-proserviceworld 2026.1013.1 → 2026.1015.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/generated/wspace-types.d.ts +98750 -26838
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoot.d.ts.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoot.js +6 -0
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js +42 -35
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/CredentialDirectory.d.ts +3 -0
- package/dist/proserviceworld/components/CredentialDirectory.d.ts.map +1 -0
- package/dist/proserviceworld/components/CredentialDirectory.js +253 -0
- package/dist/proserviceworld/components/CredentialDirectory.js.map +1 -0
- package/dist/proserviceworld/components/CredentialEvidencePicker.d.ts +9 -0
- package/dist/proserviceworld/components/CredentialEvidencePicker.d.ts.map +1 -0
- package/dist/proserviceworld/components/CredentialEvidencePicker.js +161 -0
- package/dist/proserviceworld/components/CredentialEvidencePicker.js.map +1 -0
- package/dist/proserviceworld/components/CredentialRecordForm.d.ts +24 -0
- package/dist/proserviceworld/components/CredentialRecordForm.d.ts.map +1 -0
- package/dist/proserviceworld/components/CredentialRecordForm.js +251 -0
- package/dist/proserviceworld/components/CredentialRecordForm.js.map +1 -0
- package/dist/proserviceworld/components/CredentialReferencePicker.d.ts +22 -0
- package/dist/proserviceworld/components/CredentialReferencePicker.d.ts.map +1 -0
- package/dist/proserviceworld/components/CredentialReferencePicker.js +122 -0
- package/dist/proserviceworld/components/CredentialReferencePicker.js.map +1 -0
- package/dist/proserviceworld/components/CredentialReminderPanel.d.ts +5 -0
- package/dist/proserviceworld/components/CredentialReminderPanel.d.ts.map +1 -0
- package/dist/proserviceworld/components/CredentialReminderPanel.js +141 -0
- package/dist/proserviceworld/components/CredentialReminderPanel.js.map +1 -0
- package/dist/proserviceworld/components/credentialFormDefinitions.d.ts +4 -0
- package/dist/proserviceworld/components/credentialFormDefinitions.d.ts.map +1 -0
- package/dist/proserviceworld/components/credentialFormDefinitions.js +344 -0
- package/dist/proserviceworld/components/credentialFormDefinitions.js.map +1 -0
- package/dist/proserviceworld/hooks/credentialWrites.d.ts +16 -0
- package/dist/proserviceworld/hooks/credentialWrites.d.ts.map +1 -0
- package/dist/proserviceworld/hooks/credentialWrites.js +38 -0
- package/dist/proserviceworld/hooks/credentialWrites.js.map +1 -0
- package/dist/proserviceworld/hooks/usePractitionerCredentials.d.ts +41 -0
- package/dist/proserviceworld/hooks/usePractitionerCredentials.d.ts.map +1 -0
- package/dist/proserviceworld/hooks/usePractitionerCredentials.js +105 -0
- package/dist/proserviceworld/hooks/usePractitionerCredentials.js.map +1 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts +9 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.d.ts.map +1 -1
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +24 -15
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -1
- package/dist/proserviceworld/pages/CompliancePage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/CompliancePage.js +136 -118
- package/dist/proserviceworld/pages/CompliancePage.js.map +1 -1
- package/dist/proserviceworld/pages/ConflictsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/ConflictsPage.js +87 -71
- package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +104 -86
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/InvoicesPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/InvoicesPage.js +122 -104
- package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/PractitionerCredentialsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/PractitionerCredentialsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/PractitionerCredentialsPage.js +268 -0
- package/dist/proserviceworld/pages/PractitionerCredentialsPage.js.map +1 -0
- package/dist/proserviceworld/pages/TimePage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/TimePage.js +132 -114
- package/dist/proserviceworld/pages/TimePage.js.map +1 -1
- package/dist/proserviceworld/pages/TrustPage.d.ts.map +1 -1
- package/dist/proserviceworld/pages/TrustPage.js +226 -207
- package/dist/proserviceworld/pages/TrustPage.js.map +1 -1
- package/dist/proserviceworld/utils/credentialSourceLink.d.ts +3 -0
- package/dist/proserviceworld/utils/credentialSourceLink.d.ts.map +1 -0
- package/dist/proserviceworld/utils/credentialSourceLink.js +17 -0
- package/dist/proserviceworld/utils/credentialSourceLink.js.map +1 -0
- package/package.json +21 -5
|
@@ -1,136 +1,154 @@
|
|
|
1
1
|
import { useArchiveDocument as e, useCreateDocument as t, useDocuments as n } from "../hooks/useProServiceWorldApi.js";
|
|
2
2
|
import { Banner as r, Empty as i, ErrorState as a, Field as o, LoadingBlock as s, Modal as c, NativeSelect as l, PageHeader as u, SectionCard as d, StatusPill as f, TableWrap as p, Td as m, TextArea as h, TextInput as g, Th as _, enumOptions as v, fmtDate as y, humanize as b } from "../components/ui.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { useAnalytics as x } from "../hooks/useAnalytics.js";
|
|
4
|
+
import { getDocumentDownloadUrl as S, uploadDocumentFile as C } from "../hooks/useFiles.js";
|
|
5
|
+
import { useDeferredValue as w, useState as T } from "react";
|
|
6
|
+
import { Archive as E, Download as D, FileText as O, Lock as k, Plus as A, Search as j, Upload as M } from "lucide-react";
|
|
7
|
+
import { Fragment as N, jsx as P, jsxs as F } from "react/jsx-runtime";
|
|
8
|
+
import { Button as I } from "@burdenoff/fe-libs/ui";
|
|
8
9
|
//#region src/proserviceworld/pages/DocumentsPage.tsx
|
|
9
|
-
var
|
|
10
|
+
var L = [
|
|
10
11
|
"PUBLIC",
|
|
11
12
|
"INTERNAL",
|
|
12
13
|
"CONFIDENTIAL",
|
|
13
14
|
"PRIVILEGED"
|
|
14
|
-
],
|
|
15
|
-
let [t, o] =
|
|
15
|
+
], R = () => {
|
|
16
|
+
let [t, o] = T(""), [c, h] = T(""), C = w(c), [M, N] = T(!1), [R, B] = T(null), { data: V, isLoading: H, error: U, refetch: W } = n({
|
|
17
|
+
classification: t || void 0,
|
|
18
|
+
search: C.trim() || void 0
|
|
19
|
+
}), { emitFact: G } = x(), K = e(), q = async (e) => {
|
|
16
20
|
try {
|
|
17
|
-
let t = await
|
|
21
|
+
let t = await S(e);
|
|
18
22
|
window.open(t, "_blank", "noopener");
|
|
19
23
|
} catch (e) {
|
|
20
|
-
|
|
24
|
+
B({
|
|
21
25
|
tone: "danger",
|
|
22
26
|
text: e instanceof Error ? e.message : "Could not open document."
|
|
23
27
|
});
|
|
24
28
|
}
|
|
25
29
|
};
|
|
26
|
-
return /* @__PURE__ */
|
|
30
|
+
return /* @__PURE__ */ F("div", {
|
|
27
31
|
className: "space-y-6",
|
|
28
32
|
children: [
|
|
29
|
-
/* @__PURE__ */
|
|
33
|
+
/* @__PURE__ */ P(u, {
|
|
30
34
|
title: "Documents",
|
|
31
35
|
subtitle: "Engagement letters, opinions, filings and evidence — metadata, classification and legal hold here; the file itself lives in the shared Files service.",
|
|
32
|
-
icon: /* @__PURE__ */
|
|
33
|
-
actions: /* @__PURE__ */
|
|
34
|
-
onClick: () =>
|
|
35
|
-
children: [/* @__PURE__ */
|
|
36
|
+
icon: /* @__PURE__ */ P(O, { className: "h-6 w-6" }),
|
|
37
|
+
actions: /* @__PURE__ */ F(I, {
|
|
38
|
+
onClick: () => N(!0),
|
|
39
|
+
children: [/* @__PURE__ */ P(A, { className: "mr-1.5 h-4 w-4" }), "Add document"]
|
|
36
40
|
})
|
|
37
41
|
}),
|
|
38
|
-
|
|
39
|
-
tone:
|
|
40
|
-
onClose: () =>
|
|
41
|
-
children:
|
|
42
|
+
R && /* @__PURE__ */ P(r, {
|
|
43
|
+
tone: R.tone,
|
|
44
|
+
onClose: () => B(null),
|
|
45
|
+
children: R.text
|
|
42
46
|
}),
|
|
43
|
-
/* @__PURE__ */
|
|
44
|
-
className: "flex
|
|
45
|
-
children: /* @__PURE__ */
|
|
47
|
+
/* @__PURE__ */ F("div", {
|
|
48
|
+
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
49
|
+
children: [/* @__PURE__ */ F("div", {
|
|
50
|
+
className: "relative flex-1",
|
|
51
|
+
children: [/* @__PURE__ */ P(j, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ P(g, {
|
|
52
|
+
placeholder: "Search documents…",
|
|
53
|
+
value: c,
|
|
54
|
+
onChange: (e) => h(e.target.value),
|
|
55
|
+
onKeyDown: (e) => {
|
|
56
|
+
e.key === "Enter" && c.trim() && G("psw.search.performed", {
|
|
57
|
+
entityType: "document",
|
|
58
|
+
queryLength: c.trim().length
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
className: "pl-9"
|
|
62
|
+
})]
|
|
63
|
+
}), /* @__PURE__ */ F(l, {
|
|
46
64
|
value: t,
|
|
47
65
|
onChange: (e) => o(e.target.value),
|
|
48
66
|
className: "sm:w-52",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ P("option", {
|
|
50
68
|
value: "",
|
|
51
69
|
children: "All classifications"
|
|
52
|
-
}), v(
|
|
53
|
-
})
|
|
70
|
+
}), v(L)]
|
|
71
|
+
})]
|
|
54
72
|
}),
|
|
55
|
-
|
|
56
|
-
error:
|
|
57
|
-
onRetry: () => void
|
|
58
|
-
}) : !
|
|
73
|
+
H ? /* @__PURE__ */ P(s, {}) : U ? /* @__PURE__ */ P(a, {
|
|
74
|
+
error: U,
|
|
75
|
+
onRetry: () => void W()
|
|
76
|
+
}) : !V || V.items.length === 0 ? /* @__PURE__ */ P(i, {
|
|
59
77
|
title: "No documents",
|
|
60
78
|
description: "Add a document — upload the file to the shared Files service and record it here.",
|
|
61
|
-
icon: /* @__PURE__ */
|
|
62
|
-
action: /* @__PURE__ */
|
|
63
|
-
onClick: () =>
|
|
64
|
-
children: [/* @__PURE__ */
|
|
79
|
+
icon: /* @__PURE__ */ P(O, { className: "h-8 w-8" }),
|
|
80
|
+
action: /* @__PURE__ */ F(I, {
|
|
81
|
+
onClick: () => N(!0),
|
|
82
|
+
children: [/* @__PURE__ */ P(A, { className: "mr-1.5 h-4 w-4" }), "Add document"]
|
|
65
83
|
})
|
|
66
|
-
}) : /* @__PURE__ */
|
|
67
|
-
title: `${
|
|
68
|
-
children: /* @__PURE__ */
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
-
/* @__PURE__ */
|
|
71
|
-
/* @__PURE__ */
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
-
/* @__PURE__ */
|
|
84
|
+
}) : /* @__PURE__ */ P(d, {
|
|
85
|
+
title: `${V.totalCount} document${V.totalCount === 1 ? "" : "s"}`,
|
|
86
|
+
children: /* @__PURE__ */ F(p, { children: [/* @__PURE__ */ P("thead", { children: /* @__PURE__ */ F("tr", { children: [
|
|
87
|
+
/* @__PURE__ */ P(_, { children: "Name" }),
|
|
88
|
+
/* @__PURE__ */ P(_, { children: "Classification" }),
|
|
89
|
+
/* @__PURE__ */ P(_, { children: "Status" }),
|
|
90
|
+
/* @__PURE__ */ P(_, { children: "Added" }),
|
|
91
|
+
/* @__PURE__ */ P(_, {
|
|
74
92
|
className: "text-right",
|
|
75
93
|
children: "Actions"
|
|
76
94
|
})
|
|
77
|
-
] }) }), /* @__PURE__ */
|
|
95
|
+
] }) }), /* @__PURE__ */ P("tbody", { children: V.items.map((e) => /* @__PURE__ */ F("tr", {
|
|
78
96
|
className: "hover:bg-bg-sunken/50",
|
|
79
97
|
children: [
|
|
80
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ P(m, { children: /* @__PURE__ */ F("span", {
|
|
81
99
|
className: "flex items-center gap-1.5 font-medium text-text-primary",
|
|
82
100
|
children: [
|
|
83
|
-
e.legalHold && /* @__PURE__ */
|
|
101
|
+
e.legalHold && /* @__PURE__ */ P(k, {
|
|
84
102
|
className: "h-3.5 w-3.5 text-status-warning-text",
|
|
85
103
|
"aria-label": "Legal hold"
|
|
86
104
|
}),
|
|
87
105
|
e.name,
|
|
88
106
|
" ",
|
|
89
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ F("span", {
|
|
90
108
|
className: "text-xs text-text-tertiary",
|
|
91
109
|
children: ["v", e.version]
|
|
92
110
|
})
|
|
93
111
|
]
|
|
94
112
|
}) }),
|
|
95
|
-
/* @__PURE__ */
|
|
96
|
-
/* @__PURE__ */
|
|
97
|
-
/* @__PURE__ */
|
|
98
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ P(m, { children: b(e.classification) }),
|
|
114
|
+
/* @__PURE__ */ P(m, { children: /* @__PURE__ */ P(f, { status: e.status }) }),
|
|
115
|
+
/* @__PURE__ */ P(m, { children: y(e.createdAt) }),
|
|
116
|
+
/* @__PURE__ */ P(m, {
|
|
99
117
|
className: "text-right",
|
|
100
|
-
children: /* @__PURE__ */
|
|
118
|
+
children: /* @__PURE__ */ F("div", {
|
|
101
119
|
className: "flex justify-end gap-1",
|
|
102
|
-
children: [e.file?.id && /* @__PURE__ */
|
|
120
|
+
children: [e.file?.id && /* @__PURE__ */ P(I, {
|
|
103
121
|
variant: "ghost",
|
|
104
122
|
size: "sm",
|
|
105
|
-
onClick: () => void
|
|
123
|
+
onClick: () => void q(e.file.id),
|
|
106
124
|
"aria-label": "Download",
|
|
107
|
-
children: /* @__PURE__ */
|
|
108
|
-
}), e.status !== "ARCHIVED" && /* @__PURE__ */
|
|
125
|
+
children: /* @__PURE__ */ P(D, { className: "h-4 w-4" })
|
|
126
|
+
}), e.status !== "ARCHIVED" && /* @__PURE__ */ P(I, {
|
|
109
127
|
variant: "ghost",
|
|
110
128
|
size: "sm",
|
|
111
129
|
"aria-label": "Archive",
|
|
112
|
-
disabled:
|
|
113
|
-
onClick: () =>
|
|
114
|
-
onSuccess: () =>
|
|
130
|
+
disabled: K.isPending,
|
|
131
|
+
onClick: () => K.mutate(e.id, {
|
|
132
|
+
onSuccess: () => B({
|
|
115
133
|
tone: "success",
|
|
116
134
|
text: `${e.name} archived.`
|
|
117
135
|
}),
|
|
118
|
-
onError: (e) =>
|
|
136
|
+
onError: (e) => B({
|
|
119
137
|
tone: "danger",
|
|
120
138
|
text: e instanceof Error ? e.message : "Failed."
|
|
121
139
|
})
|
|
122
140
|
}),
|
|
123
|
-
children: /* @__PURE__ */
|
|
141
|
+
children: /* @__PURE__ */ P(E, { className: "h-4 w-4" })
|
|
124
142
|
})]
|
|
125
143
|
})
|
|
126
144
|
})
|
|
127
145
|
]
|
|
128
146
|
}, e.id)) })] })
|
|
129
147
|
}),
|
|
130
|
-
|
|
131
|
-
onClose: () =>
|
|
148
|
+
M && /* @__PURE__ */ P(z, {
|
|
149
|
+
onClose: () => N(!1),
|
|
132
150
|
onDone: () => {
|
|
133
|
-
|
|
151
|
+
W(), B({
|
|
134
152
|
tone: "success",
|
|
135
153
|
text: "Document added."
|
|
136
154
|
});
|
|
@@ -138,18 +156,18 @@ var P = [
|
|
|
138
156
|
})
|
|
139
157
|
]
|
|
140
158
|
});
|
|
141
|
-
},
|
|
142
|
-
let i = t(), [a, s] =
|
|
159
|
+
}, z = ({ onClose: e, onDone: n }) => {
|
|
160
|
+
let i = t(), [a, s] = T({
|
|
143
161
|
name: "",
|
|
144
162
|
classification: "INTERNAL",
|
|
145
163
|
description: "",
|
|
146
164
|
legalHold: !1
|
|
147
|
-
}), [u, d] =
|
|
165
|
+
}), [u, d] = T(null), [f, p] = T(!1), [m, _] = T(null), y = async () => {
|
|
148
166
|
if (_(null), !a.name.trim()) return _("Document name is required.");
|
|
149
167
|
let t = null;
|
|
150
168
|
if (u) {
|
|
151
169
|
try {
|
|
152
|
-
p(!0), t = (await
|
|
170
|
+
p(!0), t = (await C(u)).fileId;
|
|
153
171
|
} catch (e) {
|
|
154
172
|
return p(!1), _(e instanceof Error ? e.message : "Upload failed.");
|
|
155
173
|
}
|
|
@@ -168,31 +186,31 @@ var P = [
|
|
|
168
186
|
onError: (e) => _(e instanceof Error ? e.message : "Failed.")
|
|
169
187
|
});
|
|
170
188
|
}, b = f || i.isPending;
|
|
171
|
-
return /* @__PURE__ */
|
|
189
|
+
return /* @__PURE__ */ P(c, {
|
|
172
190
|
open: !0,
|
|
173
191
|
onClose: e,
|
|
174
192
|
title: "Add document",
|
|
175
|
-
footer: /* @__PURE__ */
|
|
193
|
+
footer: /* @__PURE__ */ F(N, { children: [/* @__PURE__ */ P(I, {
|
|
176
194
|
variant: "outline",
|
|
177
195
|
onClick: e,
|
|
178
196
|
disabled: b,
|
|
179
197
|
children: "Cancel"
|
|
180
|
-
}), /* @__PURE__ */
|
|
198
|
+
}), /* @__PURE__ */ P(I, {
|
|
181
199
|
onClick: () => void y(),
|
|
182
200
|
disabled: b,
|
|
183
201
|
children: f ? "Uploading…" : i.isPending ? "Saving…" : "Add document"
|
|
184
202
|
})] }),
|
|
185
|
-
children: /* @__PURE__ */
|
|
203
|
+
children: /* @__PURE__ */ F("div", {
|
|
186
204
|
className: "space-y-3",
|
|
187
205
|
children: [
|
|
188
|
-
m && /* @__PURE__ */
|
|
206
|
+
m && /* @__PURE__ */ P(r, {
|
|
189
207
|
tone: "danger",
|
|
190
208
|
children: m
|
|
191
209
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
210
|
+
/* @__PURE__ */ P(o, {
|
|
193
211
|
label: "Name",
|
|
194
212
|
required: !0,
|
|
195
|
-
children: /* @__PURE__ */
|
|
213
|
+
children: /* @__PURE__ */ P(g, {
|
|
196
214
|
value: a.name,
|
|
197
215
|
onChange: (e) => s((t) => ({
|
|
198
216
|
...t,
|
|
@@ -201,26 +219,26 @@ var P = [
|
|
|
201
219
|
placeholder: "Engagement letter — Acme FY26"
|
|
202
220
|
})
|
|
203
221
|
}),
|
|
204
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ P(o, {
|
|
205
223
|
label: "Classification",
|
|
206
|
-
children: /* @__PURE__ */
|
|
224
|
+
children: /* @__PURE__ */ P(l, {
|
|
207
225
|
value: a.classification,
|
|
208
226
|
onChange: (e) => s((t) => ({
|
|
209
227
|
...t,
|
|
210
228
|
classification: e.target.value
|
|
211
229
|
})),
|
|
212
|
-
children: v(
|
|
230
|
+
children: v(L)
|
|
213
231
|
})
|
|
214
232
|
}),
|
|
215
|
-
/* @__PURE__ */
|
|
233
|
+
/* @__PURE__ */ P(o, {
|
|
216
234
|
label: "File",
|
|
217
235
|
hint: "Optional — stored in the shared Files service.",
|
|
218
|
-
children: /* @__PURE__ */
|
|
236
|
+
children: /* @__PURE__ */ F("label", {
|
|
219
237
|
className: "flex cursor-pointer items-center gap-2 rounded-lg border border-dashed border-border-seam bg-bg-canvas px-3 py-3 text-sm text-text-secondary hover:border-accent",
|
|
220
238
|
children: [
|
|
221
|
-
/* @__PURE__ */
|
|
239
|
+
/* @__PURE__ */ P(M, { className: "h-4 w-4 text-text-tertiary" }),
|
|
222
240
|
u ? u.name : "Choose a file…",
|
|
223
|
-
/* @__PURE__ */
|
|
241
|
+
/* @__PURE__ */ P("input", {
|
|
224
242
|
type: "file",
|
|
225
243
|
className: "hidden",
|
|
226
244
|
onChange: (e) => d(e.target.files?.[0] ?? null)
|
|
@@ -228,9 +246,9 @@ var P = [
|
|
|
228
246
|
]
|
|
229
247
|
})
|
|
230
248
|
}),
|
|
231
|
-
/* @__PURE__ */
|
|
249
|
+
/* @__PURE__ */ P(o, {
|
|
232
250
|
label: "Description",
|
|
233
|
-
children: /* @__PURE__ */
|
|
251
|
+
children: /* @__PURE__ */ P(h, {
|
|
234
252
|
value: a.description,
|
|
235
253
|
onChange: (e) => s((t) => ({
|
|
236
254
|
...t,
|
|
@@ -238,9 +256,9 @@ var P = [
|
|
|
238
256
|
}))
|
|
239
257
|
})
|
|
240
258
|
}),
|
|
241
|
-
/* @__PURE__ */
|
|
259
|
+
/* @__PURE__ */ F("label", {
|
|
242
260
|
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
243
|
-
children: [/* @__PURE__ */
|
|
261
|
+
children: [/* @__PURE__ */ P("input", {
|
|
244
262
|
type: "checkbox",
|
|
245
263
|
checked: a.legalHold,
|
|
246
264
|
onChange: (e) => s((t) => ({
|
|
@@ -254,6 +272,6 @@ var P = [
|
|
|
254
272
|
});
|
|
255
273
|
};
|
|
256
274
|
//#endregion
|
|
257
|
-
export {
|
|
275
|
+
export { R as DocumentsPage };
|
|
258
276
|
|
|
259
277
|
//# sourceMappingURL=DocumentsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/DocumentsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, FileText, Download, Archive, Lock, Upload } from 'lucide-react';\nimport {\n useDocuments,\n useCreateDocument,\n useArchiveDocument,\n type PswDocumentClassification,\n} from '../hooks/useProServiceWorldApi';\nimport { uploadDocumentFile, getDocumentDownloadUrl } from '../hooks/useFiles';\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 humanize,\n} from '../components/ui';\n\nconst CLASSIFICATIONS = ['PUBLIC', 'INTERNAL', 'CONFIDENTIAL', 'PRIVILEGED'] as const;\n\nexport const DocumentsPage: FC = () => {\n const [classFilter, setClassFilter] = useState<PswDocumentClassification | ''>('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useDocuments({\n classification: classFilter || undefined,\n });\n const archive = useArchiveDocument();\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({\n tone: 'danger',\n text: e instanceof Error ? e.message : 'Could not open document.',\n });\n }\n };\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Documents\"\n subtitle=\"Engagement letters, opinions, filings and evidence — metadata, classification and legal hold here; the file itself lives in the shared Files service.\"\n icon={<FileText className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add document\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 justify-end\">\n <NativeSelect\n value={classFilter}\n onChange={(e) => setClassFilter(e.target.value as PswDocumentClassification | '')}\n className=\"sm:w-52\"\n >\n <option value=\"\">All classifications</option>\n {enumOptions(CLASSIFICATIONS)}\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 documents\"\n description=\"Add a document — upload the file to the shared Files service and record it here.\"\n icon={<FileText className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add document\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} document${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Name</Th>\n <Th>Classification</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((d) => (\n <tr key={d.id} className=\"hover:bg-bg-sunken/50\">\n <Td>\n <span className=\"flex items-center gap-1.5 font-medium text-text-primary\">\n {d.legalHold && (\n <Lock\n className=\"h-3.5 w-3.5 text-status-warning-text\"\n aria-label=\"Legal hold\"\n />\n )}\n {d.name} <span className=\"text-xs text-text-tertiary\">v{d.version}</span>\n </span>\n </Td>\n <Td>{humanize(d.classification)}</Td>\n <Td>\n <StatusPill status={d.status} />\n </Td>\n <Td>{fmtDate(d.createdAt)}</Td>\n <Td className=\"text-right\">\n <div className=\"flex justify-end gap-1\">\n {d.file?.id && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void download(d.file!.id)}\n aria-label=\"Download\"\n >\n <Download className=\"h-4 w-4\" />\n </Button>\n )}\n {d.status !== 'ARCHIVED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Archive\"\n disabled={archive.isPending}\n onClick={() =>\n archive.mutate(d.id, {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `${d.name} archived.` }),\n onError: (e) =>\n setBanner({\n tone: 'danger',\n text: e instanceof Error ? e.message : 'Failed.',\n }),\n })\n }\n >\n <Archive className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <DocumentModal\n onClose={() => setCreating(false)}\n onDone={() => {\n void refetch();\n setBanner({ tone: 'success', text: 'Document added.' });\n }}\n />\n )}\n </div>\n );\n};\n\nconst DocumentModal: FC<{ onClose: () => void; onDone: () => void }> = ({ onClose, onDone }) => {\n const create = useCreateDocument();\n const [form, setForm] = useState({\n name: '',\n classification: 'INTERNAL',\n description: '',\n legalHold: false,\n });\n const [file, setFile] = useState<File | null>(null);\n const [uploading, setUploading] = useState(false);\n const [err, setErr] = useState<string | null>(null);\n\n const submit = async () => {\n setErr(null);\n if (!form.name.trim()) return setErr('Document name is required.');\n let fileId: string | null = null;\n if (file) {\n try {\n setUploading(true);\n const uploaded = await uploadDocumentFile(file);\n fileId = uploaded.fileId;\n } catch (e) {\n setUploading(false);\n return setErr(e instanceof Error ? e.message : 'Upload failed.');\n }\n setUploading(false);\n }\n create.mutate(\n {\n name: form.name.trim(),\n classification: form.classification,\n description: form.description.trim() || null,\n legalHold: form.legalHold,\n fileId,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n\n const busy = uploading || create.isPending;\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add document\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={busy}>\n Cancel\n </Button>\n <Button onClick={() => void submit()} disabled={busy}>\n {uploading ? 'Uploading…' : create.isPending ? 'Saving…' : 'Add document'}\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\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Engagement letter — Acme FY26\"\n />\n </Field>\n <Field label=\"Classification\">\n <NativeSelect\n value={form.classification}\n onChange={(e) => setForm((f) => ({ ...f, classification: e.target.value }))}\n >\n {enumOptions(CLASSIFICATIONS)}\n </NativeSelect>\n </Field>\n <Field label=\"File\" hint=\"Optional — stored in the shared Files service.\">\n <label className=\"flex cursor-pointer items-center gap-2 rounded-lg border border-dashed border-border-seam bg-bg-canvas px-3 py-3 text-sm text-text-secondary hover:border-accent\">\n <Upload className=\"h-4 w-4 text-text-tertiary\" />\n {file ? file.name : 'Choose a file…'}\n <input\n type=\"file\"\n className=\"hidden\"\n onChange={(e) => setFile(e.target.files?.[0] ?? null)}\n />\n </label>\n </Field>\n <Field label=\"Description\">\n <TextArea\n value={form.description}\n onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}\n />\n </Field>\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.legalHold}\n onChange={(e) => setForm((f) => ({ ...f, legalHold: e.target.checked }))}\n />\n Place under legal hold (cannot be disposed)\n </label>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocumentsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/DocumentsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useDeferredValue, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, FileText, Download, Archive, Lock, Upload, Search } from 'lucide-react';\nimport {\n useDocuments,\n useCreateDocument,\n useArchiveDocument,\n type PswDocumentClassification,\n} from '../hooks/useProServiceWorldApi';\nimport { uploadDocumentFile, getDocumentDownloadUrl } from '../hooks/useFiles';\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 humanize,\n} from '../components/ui';\nimport { useAnalytics } from '../hooks/useAnalytics';\n\nconst CLASSIFICATIONS = ['PUBLIC', 'INTERNAL', 'CONFIDENTIAL', 'PRIVILEGED'] as const;\n\nexport const DocumentsPage: FC = () => {\n const [classFilter, setClassFilter] = useState<PswDocumentClassification | ''>('');\n const [search, setSearch] = useState('');\n const deferredSearch = useDeferredValue(search);\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useDocuments({\n classification: classFilter || undefined,\n search: deferredSearch.trim() || undefined,\n });\n const { emitFact } = useAnalytics();\n const archive = useArchiveDocument();\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({\n tone: 'danger',\n text: e instanceof Error ? e.message : 'Could not open document.',\n });\n }\n };\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Documents\"\n subtitle=\"Engagement letters, opinions, filings and evidence — metadata, classification and legal hold here; the file itself lives in the shared Files service.\"\n icon={<FileText className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add document\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 documents…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && search.trim()) {\n emitFact('psw.search.performed', {\n entityType: 'document',\n queryLength: search.trim().length,\n });\n }\n }}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={classFilter}\n onChange={(e) => setClassFilter(e.target.value as PswDocumentClassification | '')}\n className=\"sm:w-52\"\n >\n <option value=\"\">All classifications</option>\n {enumOptions(CLASSIFICATIONS)}\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 documents\"\n description=\"Add a document — upload the file to the shared Files service and record it here.\"\n icon={<FileText className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n Add document\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} document${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Name</Th>\n <Th>Classification</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((d) => (\n <tr key={d.id} className=\"hover:bg-bg-sunken/50\">\n <Td>\n <span className=\"flex items-center gap-1.5 font-medium text-text-primary\">\n {d.legalHold && (\n <Lock\n className=\"h-3.5 w-3.5 text-status-warning-text\"\n aria-label=\"Legal hold\"\n />\n )}\n {d.name} <span className=\"text-xs text-text-tertiary\">v{d.version}</span>\n </span>\n </Td>\n <Td>{humanize(d.classification)}</Td>\n <Td>\n <StatusPill status={d.status} />\n </Td>\n <Td>{fmtDate(d.createdAt)}</Td>\n <Td className=\"text-right\">\n <div className=\"flex justify-end gap-1\">\n {d.file?.id && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => void download(d.file!.id)}\n aria-label=\"Download\"\n >\n <Download className=\"h-4 w-4\" />\n </Button>\n )}\n {d.status !== 'ARCHIVED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Archive\"\n disabled={archive.isPending}\n onClick={() =>\n archive.mutate(d.id, {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `${d.name} archived.` }),\n onError: (e) =>\n setBanner({\n tone: 'danger',\n text: e instanceof Error ? e.message : 'Failed.',\n }),\n })\n }\n >\n <Archive className=\"h-4 w-4\" />\n </Button>\n )}\n </div>\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <DocumentModal\n onClose={() => setCreating(false)}\n onDone={() => {\n void refetch();\n setBanner({ tone: 'success', text: 'Document added.' });\n }}\n />\n )}\n </div>\n );\n};\n\nconst DocumentModal: FC<{ onClose: () => void; onDone: () => void }> = ({ onClose, onDone }) => {\n const create = useCreateDocument();\n const [form, setForm] = useState({\n name: '',\n classification: 'INTERNAL',\n description: '',\n legalHold: false,\n });\n const [file, setFile] = useState<File | null>(null);\n const [uploading, setUploading] = useState(false);\n const [err, setErr] = useState<string | null>(null);\n\n const submit = async () => {\n setErr(null);\n if (!form.name.trim()) return setErr('Document name is required.');\n let fileId: string | null = null;\n if (file) {\n try {\n setUploading(true);\n const uploaded = await uploadDocumentFile(file);\n fileId = uploaded.fileId;\n } catch (e) {\n setUploading(false);\n return setErr(e instanceof Error ? e.message : 'Upload failed.');\n }\n setUploading(false);\n }\n create.mutate(\n {\n name: form.name.trim(),\n classification: form.classification,\n description: form.description.trim() || null,\n legalHold: form.legalHold,\n fileId,\n },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n\n const busy = uploading || create.isPending;\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add document\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={busy}>\n Cancel\n </Button>\n <Button onClick={() => void submit()} disabled={busy}>\n {uploading ? 'Uploading…' : create.isPending ? 'Saving…' : 'Add document'}\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\n value={form.name}\n onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}\n placeholder=\"Engagement letter — Acme FY26\"\n />\n </Field>\n <Field label=\"Classification\">\n <NativeSelect\n value={form.classification}\n onChange={(e) => setForm((f) => ({ ...f, classification: e.target.value }))}\n >\n {enumOptions(CLASSIFICATIONS)}\n </NativeSelect>\n </Field>\n <Field label=\"File\" hint=\"Optional — stored in the shared Files service.\">\n <label className=\"flex cursor-pointer items-center gap-2 rounded-lg border border-dashed border-border-seam bg-bg-canvas px-3 py-3 text-sm text-text-secondary hover:border-accent\">\n <Upload className=\"h-4 w-4 text-text-tertiary\" />\n {file ? file.name : 'Choose a file…'}\n <input\n type=\"file\"\n className=\"hidden\"\n onChange={(e) => setFile(e.target.files?.[0] ?? null)}\n />\n </label>\n </Field>\n <Field label=\"Description\">\n <TextArea\n value={form.description}\n onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}\n />\n </Field>\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.legalHold}\n onChange={(e) => setForm((f) => ({ ...f, legalHold: e.target.checked }))}\n />\n Place under legal hold (cannot be disposed)\n </label>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoicesPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/InvoicesPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"InvoicesPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/InvoicesPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAsChC,eAAO,MAAM,YAAY,EAAE,EAuL1B,CAAC"}
|