@burdenoff/microfe-proserviceworld 2026.703.1 → 2026.706.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/index.js +2 -3
  2. package/dist/node_modules/clsx/dist/clsx.js +18 -0
  3. package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
  4. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
  5. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  6. package/dist/proserviceworld/ProServiceWorldRoot.js +92 -136
  7. package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
  8. package/dist/proserviceworld/ProServiceWorldRoutes.js +18 -26
  9. package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
  10. package/dist/proserviceworld/components/ui.js +273 -0
  11. package/dist/proserviceworld/components/ui.js.map +1 -0
  12. package/dist/proserviceworld/hooks/useFiles.js +45 -0
  13. package/dist/proserviceworld/hooks/useFiles.js.map +1 -0
  14. package/dist/proserviceworld/hooks/useProServiceWorldApi.js +416 -0
  15. package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -0
  16. package/dist/proserviceworld/index.js +0 -1
  17. package/dist/proserviceworld/pages/AnalyticsPage.js +163 -475
  18. package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
  19. package/dist/proserviceworld/pages/ClientDetailPage.js +238 -0
  20. package/dist/proserviceworld/pages/ClientDetailPage.js.map +1 -0
  21. package/dist/proserviceworld/pages/ClientsPage.js +295 -0
  22. package/dist/proserviceworld/pages/ClientsPage.js.map +1 -0
  23. package/dist/proserviceworld/pages/CompliancePage.js +307 -0
  24. package/dist/proserviceworld/pages/CompliancePage.js.map +1 -0
  25. package/dist/proserviceworld/pages/ConflictsPage.js +234 -0
  26. package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -0
  27. package/dist/proserviceworld/pages/DashboardPage.js +179 -632
  28. package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
  29. package/dist/proserviceworld/pages/DocumentsPage.js +235 -539
  30. package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
  31. package/dist/proserviceworld/pages/EngagementDetailPage.js +444 -0
  32. package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -0
  33. package/dist/proserviceworld/pages/EngagementsPage.js +256 -0
  34. package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -0
  35. package/dist/proserviceworld/pages/InvoicesPage.js +303 -0
  36. package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -0
  37. package/dist/proserviceworld/pages/MattersPage.js +275 -0
  38. package/dist/proserviceworld/pages/MattersPage.js.map +1 -0
  39. package/dist/proserviceworld/pages/PracticeAreasPage.js +176 -0
  40. package/dist/proserviceworld/pages/PracticeAreasPage.js.map +1 -0
  41. package/dist/proserviceworld/pages/TimePage.js +310 -0
  42. package/dist/proserviceworld/pages/TimePage.js.map +1 -0
  43. package/dist/proserviceworld/utils/cn.js +10 -0
  44. package/dist/proserviceworld/utils/cn.js.map +1 -0
  45. package/dist/proserviceworld/utils/nav.js +11 -0
  46. package/dist/proserviceworld/utils/nav.js.map +1 -0
  47. package/package.json +1 -1
  48. package/dist/proserviceworld/pages/AdminReportsPage.js +0 -579
  49. package/dist/proserviceworld/pages/AdminReportsPage.js.map +0 -1
  50. package/dist/proserviceworld/pages/AdminUsersPage.js +0 -468
  51. package/dist/proserviceworld/pages/AdminUsersPage.js.map +0 -1
  52. package/dist/proserviceworld/pages/BookingsPage.js +0 -446
  53. package/dist/proserviceworld/pages/BookingsPage.js.map +0 -1
  54. package/dist/proserviceworld/pages/MessagesPage.js +0 -238
  55. package/dist/proserviceworld/pages/MessagesPage.js.map +0 -1
  56. package/dist/proserviceworld/pages/PaymentsPage.js +0 -537
  57. package/dist/proserviceworld/pages/PaymentsPage.js.map +0 -1
  58. package/dist/proserviceworld/pages/ProjectsPage.js +0 -627
  59. package/dist/proserviceworld/pages/ProjectsPage.js.map +0 -1
  60. package/dist/proserviceworld/pages/ProviderDetailPage.js +0 -538
  61. package/dist/proserviceworld/pages/ProviderDetailPage.js.map +0 -1
  62. package/dist/proserviceworld/pages/ProvidersPage.js +0 -347
  63. package/dist/proserviceworld/pages/ProvidersPage.js.map +0 -1
  64. package/dist/proserviceworld/pages/ReviewsPage.js +0 -356
  65. package/dist/proserviceworld/pages/ReviewsPage.js.map +0 -1
  66. package/dist/proserviceworld/pages/ServiceDetailsPage.js +0 -631
  67. package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +0 -1
  68. package/dist/proserviceworld/pages/ServicesPage.js +0 -344
  69. package/dist/proserviceworld/pages/ServicesPage.js.map +0 -1
  70. package/dist/proserviceworld/pages/VerificationPage.js +0 -370
  71. package/dist/proserviceworld/pages/VerificationPage.js.map +0 -1
  72. package/dist/proserviceworld/store/proserviceworldStore.js +0 -2490
  73. package/dist/proserviceworld/store/proserviceworldStore.js.map +0 -1
  74. package/dist/proserviceworld/utils/i18n.js +0 -15
  75. package/dist/proserviceworld/utils/i18n.js.map +0 -1
@@ -0,0 +1,275 @@
1
+ import { useClients as e, useCloseMatter as t, useCreateMatter as n, useMatters as r, usePracticeAreas as i } from "../hooks/useProServiceWorldApi.js";
2
+ import { Banner as a, Empty as o, ErrorState as s, Field as c, LoadingBlock as l, Modal as u, NativeSelect as d, PageHeader as f, SectionCard as p, StatusPill as m, TableWrap as h, Td as g, TextArea as _, TextInput as v, Th as y, enumOptions as b, fmtDate as x } from "../components/ui.js";
3
+ import { useState as S } from "react";
4
+ import { CheckCircle2 as C, FolderKanban as w, Lock as T, Plus as E, Search as D } from "lucide-react";
5
+ import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
6
+ import { Button as j } from "@burdenoff/fe-libs/ui";
7
+ //#region src/proserviceworld/pages/MattersPage.tsx
8
+ var M = [
9
+ "OPEN",
10
+ "ON_HOLD",
11
+ "CLOSED",
12
+ "ARCHIVED"
13
+ ], N = [
14
+ "LOW",
15
+ "MEDIUM",
16
+ "HIGH",
17
+ "CRITICAL"
18
+ ], P = () => {
19
+ let [e, n] = S(""), [i, c] = S(""), [u, _] = S(!1), [O, N] = S(null), { data: P, isLoading: I, error: L, refetch: R } = r({
20
+ search: e.trim() || void 0,
21
+ status: i || void 0
22
+ }), z = t();
23
+ return /* @__PURE__ */ A("div", {
24
+ className: "space-y-6",
25
+ children: [
26
+ /* @__PURE__ */ k(f, {
27
+ title: "Matters",
28
+ subtitle: "The regulated threads of work — legal matters, audits, filings — with their own deadlines and sensitive-document handling.",
29
+ icon: /* @__PURE__ */ k(w, { className: "h-6 w-6" }),
30
+ actions: /* @__PURE__ */ A(j, {
31
+ onClick: () => _(!0),
32
+ children: [/* @__PURE__ */ k(E, { className: "mr-1.5 h-4 w-4" }), "New matter"]
33
+ })
34
+ }),
35
+ O && /* @__PURE__ */ k(a, {
36
+ tone: O.tone,
37
+ onClose: () => N(null),
38
+ children: O.text
39
+ }),
40
+ /* @__PURE__ */ A("div", {
41
+ className: "flex flex-col gap-2 sm:flex-row sm:items-center",
42
+ children: [/* @__PURE__ */ A("div", {
43
+ className: "relative flex-1",
44
+ children: [/* @__PURE__ */ k(D, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ k(v, {
45
+ placeholder: "Search matters…",
46
+ value: e,
47
+ onChange: (e) => n(e.target.value),
48
+ className: "pl-9"
49
+ })]
50
+ }), /* @__PURE__ */ A(d, {
51
+ value: i,
52
+ onChange: (e) => c(e.target.value),
53
+ className: "sm:w-48",
54
+ children: [/* @__PURE__ */ k("option", {
55
+ value: "",
56
+ children: "All statuses"
57
+ }), b(M)]
58
+ })]
59
+ }),
60
+ I ? /* @__PURE__ */ k(l, {}) : L ? /* @__PURE__ */ k(s, {
61
+ error: L,
62
+ onRetry: () => void R()
63
+ }) : !P || P.items.length === 0 ? /* @__PURE__ */ k(o, {
64
+ title: "No matters yet",
65
+ description: "Open a matter to track a regulated thread of work for a client.",
66
+ icon: /* @__PURE__ */ k(w, { className: "h-8 w-8" }),
67
+ action: /* @__PURE__ */ A(j, {
68
+ onClick: () => _(!0),
69
+ children: [/* @__PURE__ */ k(E, { className: "mr-1.5 h-4 w-4" }), "New matter"]
70
+ })
71
+ }) : /* @__PURE__ */ k(p, {
72
+ title: `${P.totalCount} matter${P.totalCount === 1 ? "" : "s"}`,
73
+ children: /* @__PURE__ */ A(h, { children: [/* @__PURE__ */ k("thead", { children: /* @__PURE__ */ A("tr", { children: [
74
+ /* @__PURE__ */ k(y, { children: "Matter" }),
75
+ /* @__PURE__ */ k(y, { children: "Client" }),
76
+ /* @__PURE__ */ k(y, { children: "Priority" }),
77
+ /* @__PURE__ */ k(y, { children: "Status" }),
78
+ /* @__PURE__ */ k(y, { children: "Due" }),
79
+ /* @__PURE__ */ k(y, {
80
+ className: "text-right",
81
+ children: "Actions"
82
+ })
83
+ ] }) }), /* @__PURE__ */ k("tbody", { children: P.items.map((e) => /* @__PURE__ */ A("tr", {
84
+ className: "hover:bg-bg-sunken/50",
85
+ children: [
86
+ /* @__PURE__ */ A(g, { children: [/* @__PURE__ */ A("span", {
87
+ className: "flex items-center gap-1.5 font-medium text-text-primary",
88
+ children: [e.isPrivileged && /* @__PURE__ */ k(T, {
89
+ className: "h-3.5 w-3.5 text-status-warning-text",
90
+ "aria-label": "Privileged"
91
+ }), e.title]
92
+ }), e.reference ? /* @__PURE__ */ k("div", {
93
+ className: "text-xs text-text-tertiary",
94
+ children: e.reference
95
+ }) : null] }),
96
+ /* @__PURE__ */ k(g, { children: e.client?.name ?? "—" }),
97
+ /* @__PURE__ */ k(g, { children: /* @__PURE__ */ k(m, { status: e.priority }) }),
98
+ /* @__PURE__ */ k(g, { children: /* @__PURE__ */ k(m, { status: e.status }) }),
99
+ /* @__PURE__ */ k(g, { children: x(e.dueDate) }),
100
+ /* @__PURE__ */ k(g, {
101
+ className: "text-right",
102
+ children: e.status !== "CLOSED" && e.status !== "ARCHIVED" && /* @__PURE__ */ A(j, {
103
+ variant: "ghost",
104
+ size: "sm",
105
+ disabled: z.isPending,
106
+ onClick: () => z.mutate(e.id, {
107
+ onSuccess: () => N({
108
+ tone: "success",
109
+ text: `${e.title} closed.`
110
+ }),
111
+ onError: (e) => N({
112
+ tone: "danger",
113
+ text: e instanceof Error ? e.message : "Failed."
114
+ })
115
+ }),
116
+ children: [/* @__PURE__ */ k(C, { className: "mr-1 h-4 w-4" }), " Close"]
117
+ })
118
+ })
119
+ ]
120
+ }, e.id)) })] })
121
+ }),
122
+ u && /* @__PURE__ */ k(F, {
123
+ onClose: () => _(!1),
124
+ onSaved: (e) => N({
125
+ tone: "success",
126
+ text: `${e} opened.`
127
+ })
128
+ })
129
+ ]
130
+ });
131
+ }, F = ({ onClose: t, onSaved: r }) => {
132
+ let o = n(), s = e({ pagination: { limit: 100 } }), l = i(), [f, p] = S({
133
+ clientId: "",
134
+ practiceAreaId: "",
135
+ title: "",
136
+ reference: "",
137
+ description: "",
138
+ priority: "MEDIUM",
139
+ dueDate: "",
140
+ isPrivileged: !1
141
+ }), [m, h] = S(null), g = (e) => (t) => p((n) => ({
142
+ ...n,
143
+ [e]: t.target.value
144
+ }));
145
+ return /* @__PURE__ */ k(u, {
146
+ open: !0,
147
+ onClose: t,
148
+ title: "New matter",
149
+ footer: /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k(j, {
150
+ variant: "outline",
151
+ onClick: t,
152
+ disabled: o.isPending,
153
+ children: "Cancel"
154
+ }), /* @__PURE__ */ k(j, {
155
+ onClick: () => {
156
+ if (h(null), !f.clientId) return h("Select a client.");
157
+ if (!f.title.trim()) return h("Matter title is required.");
158
+ o.mutate({
159
+ clientId: f.clientId,
160
+ practiceAreaId: f.practiceAreaId || null,
161
+ title: f.title.trim(),
162
+ reference: f.reference.trim() || null,
163
+ description: f.description.trim() || null,
164
+ priority: f.priority,
165
+ isPrivileged: f.isPrivileged,
166
+ dueDate: f.dueDate ? new Date(f.dueDate).toISOString() : null
167
+ }, {
168
+ onSuccess: () => {
169
+ r(f.title.trim()), t();
170
+ },
171
+ onError: (e) => h(e instanceof Error ? e.message : "Failed.")
172
+ });
173
+ },
174
+ disabled: o.isPending,
175
+ children: o.isPending ? "Opening…" : "Open matter"
176
+ })] }),
177
+ children: /* @__PURE__ */ A("div", {
178
+ className: "space-y-3",
179
+ children: [
180
+ m && /* @__PURE__ */ k(a, {
181
+ tone: "danger",
182
+ children: m
183
+ }),
184
+ /* @__PURE__ */ k(c, {
185
+ label: "Client",
186
+ required: !0,
187
+ children: /* @__PURE__ */ A(d, {
188
+ value: f.clientId,
189
+ onChange: g("clientId"),
190
+ children: [/* @__PURE__ */ k("option", {
191
+ value: "",
192
+ children: "Select a client…"
193
+ }), s.data?.items.map((e) => /* @__PURE__ */ k("option", {
194
+ value: e.id,
195
+ children: e.name
196
+ }, e.id))]
197
+ })
198
+ }),
199
+ /* @__PURE__ */ k(c, {
200
+ label: "Title",
201
+ required: !0,
202
+ children: /* @__PURE__ */ k(v, {
203
+ value: f.title,
204
+ onChange: g("title"),
205
+ placeholder: "Regulatory investigation — SEC inquiry"
206
+ })
207
+ }),
208
+ /* @__PURE__ */ A("div", {
209
+ className: "grid grid-cols-2 gap-3",
210
+ children: [/* @__PURE__ */ k(c, {
211
+ label: "Reference",
212
+ children: /* @__PURE__ */ k(v, {
213
+ value: f.reference,
214
+ onChange: g("reference"),
215
+ placeholder: "MAT-2026-014"
216
+ })
217
+ }), /* @__PURE__ */ k(c, {
218
+ label: "Priority",
219
+ children: /* @__PURE__ */ k(d, {
220
+ value: f.priority,
221
+ onChange: g("priority"),
222
+ children: b(N)
223
+ })
224
+ })]
225
+ }),
226
+ /* @__PURE__ */ A("div", {
227
+ className: "grid grid-cols-2 gap-3",
228
+ children: [/* @__PURE__ */ k(c, {
229
+ label: "Practice area",
230
+ children: /* @__PURE__ */ A(d, {
231
+ value: f.practiceAreaId,
232
+ onChange: g("practiceAreaId"),
233
+ children: [/* @__PURE__ */ k("option", {
234
+ value: "",
235
+ children: "Unassigned"
236
+ }), l.data?.map((e) => /* @__PURE__ */ k("option", {
237
+ value: e.id,
238
+ children: e.name
239
+ }, e.id))]
240
+ })
241
+ }), /* @__PURE__ */ k(c, {
242
+ label: "Due date",
243
+ children: /* @__PURE__ */ k(v, {
244
+ type: "date",
245
+ value: f.dueDate,
246
+ onChange: g("dueDate")
247
+ })
248
+ })]
249
+ }),
250
+ /* @__PURE__ */ k(c, {
251
+ label: "Description",
252
+ children: /* @__PURE__ */ k(_, {
253
+ value: f.description,
254
+ onChange: g("description")
255
+ })
256
+ }),
257
+ /* @__PURE__ */ A("label", {
258
+ className: "flex items-center gap-2 text-sm text-text-secondary",
259
+ children: [/* @__PURE__ */ k("input", {
260
+ type: "checkbox",
261
+ checked: f.isPrivileged,
262
+ onChange: (e) => p((t) => ({
263
+ ...t,
264
+ isPrivileged: e.target.checked
265
+ }))
266
+ }), "Privileged / confidential matter"]
267
+ })
268
+ ]
269
+ })
270
+ });
271
+ };
272
+ //#endregion
273
+ export { P as MattersPage };
274
+
275
+ //# sourceMappingURL=MattersPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MattersPage.js","names":[],"sources":["../../../src/proserviceworld/pages/MattersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Search, FolderKanban, Lock, CheckCircle2 } from 'lucide-react';\nimport {\n useMatters,\n useCreateMatter,\n useCloseMatter,\n useClients,\n usePracticeAreas,\n type PswMatterStatus,\n} from '../hooks/useProServiceWorldApi';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Modal,\n Field,\n TextInput,\n TextArea,\n NativeSelect,\n Banner,\n TableWrap,\n Th,\n Td,\n enumOptions,\n fmtDate,\n} from '../components/ui';\n\nconst STATUSES: PswMatterStatus[] = ['OPEN', 'ON_HOLD', 'CLOSED', 'ARCHIVED'];\nconst PRIORITIES = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'] as const;\n\nexport const MattersPage: FC = () => {\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<PswMatterStatus | ''>('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useMatters({\n search: search.trim() || undefined,\n status: statusFilter || undefined,\n });\n const close = useCloseMatter();\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Matters\"\n subtitle=\"The regulated threads of work — legal matters, audits, filings — with their own deadlines and sensitive-document handling.\"\n icon={<FolderKanban className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New matter\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 matters…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswMatterStatus | '')}\n className=\"sm:w-48\"\n >\n <option value=\"\">All statuses</option>\n {enumOptions(STATUSES)}\n </NativeSelect>\n </div>\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.items.length === 0 ? (\n <Empty\n title=\"No matters yet\"\n description=\"Open a matter to track a regulated thread of work for a client.\"\n icon={<FolderKanban className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New matter\n </Button>\n }\n />\n ) : (\n <SectionCard title={`${data.totalCount} matter${data.totalCount === 1 ? '' : 's'}`}>\n <TableWrap>\n <thead>\n <tr>\n <Th>Matter</Th>\n <Th>Client</Th>\n <Th>Priority</Th>\n <Th>Status</Th>\n <Th>Due</Th>\n <Th className=\"text-right\">Actions</Th>\n </tr>\n </thead>\n <tbody>\n {data.items.map((m) => (\n <tr key={m.id} className=\"hover:bg-bg-sunken/50\">\n <Td>\n <span className=\"flex items-center gap-1.5 font-medium text-text-primary\">\n {m.isPrivileged && (\n <Lock\n className=\"h-3.5 w-3.5 text-status-warning-text\"\n aria-label=\"Privileged\"\n />\n )}\n {m.title}\n </span>\n {m.reference ? (\n <div className=\"text-xs text-text-tertiary\">{m.reference}</div>\n ) : null}\n </Td>\n <Td>{m.client?.name ?? '—'}</Td>\n <Td>\n <StatusPill status={m.priority} />\n </Td>\n <Td>\n <StatusPill status={m.status} />\n </Td>\n <Td>{fmtDate(m.dueDate)}</Td>\n <Td className=\"text-right\">\n {m.status !== 'CLOSED' && m.status !== 'ARCHIVED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={close.isPending}\n onClick={() =>\n close.mutate(m.id, {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `${m.title} closed.` }),\n onError: (e) =>\n setBanner({\n tone: 'danger',\n text: e instanceof Error ? e.message : 'Failed.',\n }),\n })\n }\n >\n <CheckCircle2 className=\"mr-1 h-4 w-4\" /> Close\n </Button>\n )}\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <MatterModal\n onClose={() => setCreating(false)}\n onSaved={(t) => setBanner({ tone: 'success', text: `${t} opened.` })}\n />\n )}\n </div>\n );\n};\n\nconst MatterModal: FC<{ onClose: () => void; onSaved: (title: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateMatter();\n const clients = useClients({ pagination: { limit: 100 } });\n const practiceAreas = usePracticeAreas();\n const [form, setForm] = useState({\n clientId: '',\n practiceAreaId: '',\n title: '',\n reference: '',\n description: '',\n priority: 'MEDIUM',\n dueDate: '',\n isPrivileged: false,\n });\n const [err, setErr] = useState<string | null>(null);\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n\n const submit = () => {\n setErr(null);\n if (!form.clientId) return setErr('Select a client.');\n if (!form.title.trim()) return setErr('Matter title is required.');\n create.mutate(\n {\n clientId: form.clientId,\n practiceAreaId: form.practiceAreaId || null,\n title: form.title.trim(),\n reference: form.reference.trim() || null,\n description: form.description.trim() || null,\n priority: form.priority,\n isPrivileged: form.isPrivileged,\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n },\n {\n onSuccess: () => {\n onSaved(form.title.trim());\n onClose();\n },\n onError: (e) => setErr(e instanceof Error ? e.message : 'Failed.'),\n }\n );\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"New matter\"\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={create.isPending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={create.isPending}>\n {create.isPending ? 'Opening…' : 'Open matter'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select a client…</option>\n {clients.data?.items.map((c) => (\n <option key={c.id} value={c.id}>\n {c.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <Field label=\"Title\" required>\n <TextInput\n value={form.title}\n onChange={set('title')}\n placeholder=\"Regulatory investigation — SEC inquiry\"\n />\n </Field>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Reference\">\n <TextInput\n value={form.reference}\n onChange={set('reference')}\n placeholder=\"MAT-2026-014\"\n />\n </Field>\n <Field label=\"Priority\">\n <NativeSelect value={form.priority} onChange={set('priority')}>\n {enumOptions(PRIORITIES)}\n </NativeSelect>\n </Field>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Practice area\">\n <NativeSelect value={form.practiceAreaId} onChange={set('practiceAreaId')}>\n <option value=\"\">Unassigned</option>\n {practiceAreas.data?.map((p) => (\n <option key={p.id} value={p.id}>\n {p.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <Field label=\"Due date\">\n <TextInput type=\"date\" value={form.dueDate} onChange={set('dueDate')} />\n </Field>\n </div>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.isPrivileged}\n onChange={(e) => setForm((f) => ({ ...f, isPrivileged: e.target.checked }))}\n />\n Privileged / confidential matter\n </label>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,176 @@
1
+ import { useCreatePracticeArea as e, usePracticeAreas as t, useUpdatePracticeArea as n } from "../hooks/useProServiceWorldApi.js";
2
+ import { Banner as r, Empty as i, ErrorState as a, Field as o, LoadingBlock as s, Modal as c, PageHeader as l, SectionCard as u, StatusPill as d, TextArea as f, TextInput as p } from "../components/ui.js";
3
+ import { useState as m } from "react";
4
+ import { LayoutGrid as h, Pencil as g, Plus as _ } from "lucide-react";
5
+ import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
6
+ import { Button as x } from "@burdenoff/fe-libs/ui";
7
+ //#region src/proserviceworld/pages/PracticeAreasPage.tsx
8
+ var S = () => {
9
+ let { data: e, isLoading: n, error: o, refetch: c } = t(), [f, p] = m(!1), [v, S] = m(null), [w, T] = m(null);
10
+ return /* @__PURE__ */ b("div", {
11
+ className: "space-y-6",
12
+ children: [
13
+ /* @__PURE__ */ y(l, {
14
+ title: "Practice areas",
15
+ subtitle: "The domains of service your firm offers — Tax, Audit, Litigation, Advisory — that engagements and matters classify against.",
16
+ icon: /* @__PURE__ */ y(h, { className: "h-6 w-6" }),
17
+ actions: /* @__PURE__ */ b(x, {
18
+ onClick: () => p(!0),
19
+ children: [/* @__PURE__ */ y(_, { className: "mr-1.5 h-4 w-4" }), "New area"]
20
+ })
21
+ }),
22
+ w && /* @__PURE__ */ y(r, {
23
+ tone: "success",
24
+ onClose: () => T(null),
25
+ children: w
26
+ }),
27
+ n ? /* @__PURE__ */ y(s, {}) : o ? /* @__PURE__ */ y(a, {
28
+ error: o,
29
+ onRetry: () => void c()
30
+ }) : !e || e.length === 0 ? /* @__PURE__ */ y(i, {
31
+ title: "No practice areas",
32
+ description: "Define the service domains your firm offers.",
33
+ icon: /* @__PURE__ */ y(h, { className: "h-8 w-8" }),
34
+ action: /* @__PURE__ */ b(x, {
35
+ onClick: () => p(!0),
36
+ children: [/* @__PURE__ */ y(_, { className: "mr-1.5 h-4 w-4" }), "New area"]
37
+ })
38
+ }) : /* @__PURE__ */ y("div", {
39
+ className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3",
40
+ children: e.map((e) => /* @__PURE__ */ b(u, { children: [/* @__PURE__ */ b("div", {
41
+ className: "flex items-start justify-between",
42
+ children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ b("div", {
43
+ className: "flex items-center gap-2",
44
+ children: [/* @__PURE__ */ y("h3", {
45
+ className: "text-sm font-semibold text-text-primary",
46
+ children: e.name
47
+ }), e.code ? /* @__PURE__ */ y("span", {
48
+ className: "rounded bg-bg-sunken px-1.5 py-0.5 text-xs text-text-tertiary",
49
+ children: e.code
50
+ }) : null]
51
+ }), /* @__PURE__ */ y(d, {
52
+ status: e.isActive ? "ACTIVE" : "INACTIVE",
53
+ className: "mt-1.5"
54
+ })] }), /* @__PURE__ */ y(x, {
55
+ variant: "ghost",
56
+ size: "sm",
57
+ "aria-label": "Edit",
58
+ onClick: () => S(e),
59
+ children: /* @__PURE__ */ y(g, { className: "h-4 w-4" })
60
+ })]
61
+ }), e.description ? /* @__PURE__ */ y("p", {
62
+ className: "mt-2 text-sm text-text-secondary",
63
+ children: e.description
64
+ }) : null] }, e.id))
65
+ }),
66
+ f && /* @__PURE__ */ y(C, {
67
+ onClose: () => p(!1),
68
+ onDone: () => {
69
+ c(), T("Practice area created.");
70
+ }
71
+ }),
72
+ v && /* @__PURE__ */ y(C, {
73
+ area: v,
74
+ onClose: () => S(null),
75
+ onDone: () => {
76
+ c(), T("Practice area updated.");
77
+ }
78
+ })
79
+ ]
80
+ });
81
+ }, C = ({ area: t, onClose: i, onDone: a }) => {
82
+ let s = e(), l = n(), [u, d] = m({
83
+ name: t?.name ?? "",
84
+ code: t?.code ?? "",
85
+ description: t?.description ?? "",
86
+ isActive: t?.isActive ?? !0
87
+ }), [h, g] = m(null), _ = s.isPending || l.isPending, S = (e) => (t) => d((n) => ({
88
+ ...n,
89
+ [e]: t.target.value
90
+ }));
91
+ return /* @__PURE__ */ y(c, {
92
+ open: !0,
93
+ onClose: i,
94
+ title: t ? "Edit practice area" : "New practice area",
95
+ footer: /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(x, {
96
+ variant: "outline",
97
+ onClick: i,
98
+ disabled: _,
99
+ children: "Cancel"
100
+ }), /* @__PURE__ */ y(x, {
101
+ onClick: () => {
102
+ if (g(null), !u.name.trim()) return g("Name is required.");
103
+ let e = {
104
+ name: u.name.trim(),
105
+ code: u.code.trim() || null,
106
+ description: u.description.trim() || null,
107
+ isActive: u.isActive
108
+ }, n = (e) => g(e instanceof Error ? e.message : "Failed.");
109
+ t ? l.mutate({
110
+ id: t.id,
111
+ input: e
112
+ }, {
113
+ onSuccess: () => {
114
+ a(), i();
115
+ },
116
+ onError: n
117
+ }) : s.mutate(e, {
118
+ onSuccess: () => {
119
+ a(), i();
120
+ },
121
+ onError: n
122
+ });
123
+ },
124
+ disabled: _,
125
+ children: _ ? "Saving…" : t ? "Save" : "Create"
126
+ })] }),
127
+ children: /* @__PURE__ */ b("div", {
128
+ className: "space-y-3",
129
+ children: [
130
+ h && /* @__PURE__ */ y(r, {
131
+ tone: "danger",
132
+ children: h
133
+ }),
134
+ /* @__PURE__ */ y(o, {
135
+ label: "Name",
136
+ required: !0,
137
+ children: /* @__PURE__ */ y(p, {
138
+ value: u.name,
139
+ onChange: S("name"),
140
+ placeholder: "Tax Advisory"
141
+ })
142
+ }),
143
+ /* @__PURE__ */ y(o, {
144
+ label: "Code",
145
+ children: /* @__PURE__ */ y(p, {
146
+ value: u.code,
147
+ onChange: S("code"),
148
+ placeholder: "TAX"
149
+ })
150
+ }),
151
+ /* @__PURE__ */ y(o, {
152
+ label: "Description",
153
+ children: /* @__PURE__ */ y(f, {
154
+ value: u.description,
155
+ onChange: S("description")
156
+ })
157
+ }),
158
+ /* @__PURE__ */ b("label", {
159
+ className: "flex items-center gap-2 text-sm text-text-secondary",
160
+ children: [/* @__PURE__ */ y("input", {
161
+ type: "checkbox",
162
+ checked: u.isActive,
163
+ onChange: (e) => d((t) => ({
164
+ ...t,
165
+ isActive: e.target.checked
166
+ }))
167
+ }), "Active"]
168
+ })
169
+ ]
170
+ })
171
+ });
172
+ };
173
+ //#endregion
174
+ export { S as PracticeAreasPage };
175
+
176
+ //# sourceMappingURL=PracticeAreasPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PracticeAreasPage.js","names":[],"sources":["../../../src/proserviceworld/pages/PracticeAreasPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, LayoutGrid, Pencil } from 'lucide-react';\nimport {\n usePracticeAreas,\n useCreatePracticeArea,\n useUpdatePracticeArea,\n type PswPracticeArea,\n} from '../hooks/useProServiceWorldApi';\nimport {\n PageHeader,\n SectionCard,\n LoadingBlock,\n ErrorState,\n Empty,\n StatusPill,\n Modal,\n Field,\n TextInput,\n TextArea,\n Banner,\n} from '../components/ui';\n\nexport const PracticeAreasPage: FC = () => {\n const { data, isLoading, error, refetch } = usePracticeAreas();\n const [creating, setCreating] = useState(false);\n const [editing, setEditing] = useState<PswPracticeArea | null>(null);\n const [banner, setBanner] = useState<string | null>(null);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Practice areas\"\n subtitle=\"The domains of service your firm offers — Tax, Audit, Litigation, Advisory — that engagements and matters classify against.\"\n icon={<LayoutGrid className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New area\n </Button>\n }\n />\n\n {banner && (\n <Banner tone=\"success\" onClose={() => setBanner(null)}>\n {banner}\n </Banner>\n )}\n\n {isLoading ? (\n <LoadingBlock />\n ) : error ? (\n <ErrorState error={error} onRetry={() => void refetch()} />\n ) : !data || data.length === 0 ? (\n <Empty\n title=\"No practice areas\"\n description=\"Define the service domains your firm offers.\"\n icon={<LayoutGrid className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New area\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {data.map((p) => (\n <SectionCard key={p.id}>\n <div className=\"flex items-start justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">{p.name}</h3>\n {p.code ? (\n <span className=\"rounded bg-bg-sunken px-1.5 py-0.5 text-xs text-text-tertiary\">\n {p.code}\n </span>\n ) : null}\n </div>\n <StatusPill status={p.isActive ? 'ACTIVE' : 'INACTIVE'} className=\"mt-1.5\" />\n </div>\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Edit\" onClick={() => setEditing(p)}>\n <Pencil className=\"h-4 w-4\" />\n </Button>\n </div>\n {p.description ? (\n <p className=\"mt-2 text-sm text-text-secondary\">{p.description}</p>\n ) : null}\n </SectionCard>\n ))}\n </div>\n )}\n\n {creating && (\n <AreaModal\n onClose={() => setCreating(false)}\n onDone={() => {\n void refetch();\n setBanner('Practice area created.');\n }}\n />\n )}\n {editing && (\n <AreaModal\n area={editing}\n onClose={() => setEditing(null)}\n onDone={() => {\n void refetch();\n setBanner('Practice area updated.');\n }}\n />\n )}\n </div>\n );\n};\n\nconst AreaModal: FC<{ area?: PswPracticeArea; onClose: () => void; onDone: () => void }> = ({\n area,\n onClose,\n onDone,\n}) => {\n const create = useCreatePracticeArea();\n const update = useUpdatePracticeArea();\n const [form, setForm] = useState({\n name: area?.name ?? '',\n code: area?.code ?? '',\n description: area?.description ?? '',\n isActive: area?.isActive ?? true,\n });\n const [err, setErr] = useState<string | null>(null);\n const pending = create.isPending || update.isPending;\n const set = (k: 'name' | 'code' | 'description') => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n\n const submit = () => {\n setErr(null);\n if (!form.name.trim()) return setErr('Name is required.');\n const payload = {\n name: form.name.trim(),\n code: form.code.trim() || null,\n description: form.description.trim() || null,\n isActive: form.isActive,\n };\n const onError = (e: unknown) => setErr(e instanceof Error ? e.message : 'Failed.');\n if (area)\n update.mutate(\n { id: area.id, input: payload },\n {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError,\n }\n );\n else\n create.mutate(payload, {\n onSuccess: () => {\n onDone();\n onClose();\n },\n onError,\n });\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title={area ? 'Edit practice area' : 'New practice area'}\n footer={\n <>\n <Button variant=\"outline\" onClick={onClose} disabled={pending}>\n Cancel\n </Button>\n <Button onClick={submit} disabled={pending}>\n {pending ? 'Saving…' : area ? 'Save' : 'Create'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <Field label=\"Name\" required>\n <TextInput value={form.name} onChange={set('name')} placeholder=\"Tax Advisory\" />\n </Field>\n <Field label=\"Code\">\n <TextInput value={form.code} onChange={set('code')} placeholder=\"TAX\" />\n </Field>\n <Field label=\"Description\">\n <TextArea value={form.description} onChange={set('description')} />\n </Field>\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <input\n type=\"checkbox\"\n checked={form.isActive}\n onChange={(e) => setForm((f) => ({ ...f, isActive: e.target.checked }))}\n />\n Active\n </label>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}