@burdenoff/microfe-proserviceworld 2026.1014.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.
@@ -1,148 +1,166 @@
1
1
  import { useClients as e, useCreateInvoice as t, useEngagements as n, useInvoices as r, useMarkInvoicePaid as i } from "../hooks/useProServiceWorldApi.js";
2
2
  import { cn as a } from "../utils/cn.js";
3
3
  import { Banner as o, Empty as s, ErrorState as c, Field as l, LoadingBlock as u, MetricCard as d, Modal as f, NativeSelect as p, PageHeader as m, SectionCard as h, StatusPill as g, TableWrap as _, Td as v, TextArea as y, TextInput as b, Th as x, enumOptions as S, fmtDate as C, fmtMoney as w } from "../components/ui.js";
4
- import { useMemo as T, useState as E } from "react";
5
- import { AlertCircle as D, CheckCircle2 as O, Plus as k, Receipt as A, Wallet as j } from "lucide-react";
6
- import { Fragment as M, jsx as N, jsxs as P } from "react/jsx-runtime";
7
- import { Button as F } from "@burdenoff/fe-libs/ui";
4
+ import { useAnalytics as T } from "../hooks/useAnalytics.js";
5
+ import { useDeferredValue as E, useMemo as D, useState as O } from "react";
6
+ import { AlertCircle as k, CheckCircle2 as A, Plus as j, Receipt as M, Search as N, Wallet as P } from "lucide-react";
7
+ import { Fragment as F, jsx as I, jsxs as L } from "react/jsx-runtime";
8
+ import { Button as R } from "@burdenoff/fe-libs/ui";
8
9
  //#region src/proserviceworld/pages/InvoicesPage.tsx
9
- var I = [
10
+ var z = [
10
11
  "DRAFT",
11
12
  "SENT",
12
13
  "PAID",
13
14
  "OVERDUE",
14
15
  "CANCELLED"
15
- ], L = () => {
16
- let [t, n] = E(""), [l, f] = E(!1), [y, b] = E(null), { data: M, isLoading: L, error: z, refetch: B } = r({ status: t || void 0 }), V = i(), H = e({ pagination: { limit: 100 } }), U = T(() => new Map((H.data?.items ?? []).map((e) => [e.id, e.name])), [H.data]), W = (M?.items ?? []).filter((e) => e.status === "SENT" || e.status === "OVERDUE"), G = (M?.items ?? []).filter((e) => e.status === "PAID"), K = W.reduce((e, t) => e + t.total, 0), q = G.reduce((e, t) => e + t.total, 0);
17
- return /* @__PURE__ */ P("div", {
16
+ ], B = () => {
17
+ let [t, n] = O(""), [l, f] = O(""), y = E(l), [F, B] = O(!1), [H, U] = O(null), { data: W, isLoading: G, error: K, refetch: q } = r({
18
+ status: t || void 0,
19
+ search: y.trim() || void 0
20
+ }), { emitFact: J } = T(), Y = i(), X = e({ pagination: { limit: 100 } }), Z = D(() => new Map((X.data?.items ?? []).map((e) => [e.id, e.name])), [X.data]), Q = (W?.items ?? []).filter((e) => e.status === "SENT" || e.status === "OVERDUE"), $ = (W?.items ?? []).filter((e) => e.status === "PAID"), ee = Q.reduce((e, t) => e + t.total, 0), te = $.reduce((e, t) => e + t.total, 0);
21
+ return /* @__PURE__ */ L("div", {
18
22
  className: "space-y-6",
19
23
  children: [
20
- /* @__PURE__ */ N(m, {
24
+ /* @__PURE__ */ I(m, {
21
25
  title: "Invoices",
22
26
  subtitle: "Assemble invoices from time, milestones and fixed-fee schedules — and track what's outstanding.",
23
- icon: /* @__PURE__ */ N(A, { className: "h-6 w-6" }),
24
- actions: /* @__PURE__ */ P(F, {
25
- onClick: () => f(!0),
26
- children: [/* @__PURE__ */ N(k, { className: "mr-1.5 h-4 w-4" }), "New invoice"]
27
+ icon: /* @__PURE__ */ I(M, { className: "h-6 w-6" }),
28
+ actions: /* @__PURE__ */ L(R, {
29
+ onClick: () => B(!0),
30
+ children: [/* @__PURE__ */ I(j, { className: "mr-1.5 h-4 w-4" }), "New invoice"]
27
31
  })
28
32
  }),
29
- y && /* @__PURE__ */ N(o, {
30
- tone: y.tone,
31
- onClose: () => b(null),
32
- children: y.text
33
+ H && /* @__PURE__ */ I(o, {
34
+ tone: H.tone,
35
+ onClose: () => U(null),
36
+ children: H.text
33
37
  }),
34
- /* @__PURE__ */ P("div", {
38
+ /* @__PURE__ */ L("div", {
35
39
  className: "grid grid-cols-1 gap-3 sm:grid-cols-3",
36
40
  children: [
37
- /* @__PURE__ */ N(d, {
41
+ /* @__PURE__ */ I(d, {
38
42
  label: "Invoices",
39
- value: M?.totalCount ?? 0,
40
- icon: /* @__PURE__ */ N(A, { className: "h-4 w-4" })
43
+ value: W?.totalCount ?? 0,
44
+ icon: /* @__PURE__ */ I(M, { className: "h-4 w-4" })
41
45
  }),
42
- /* @__PURE__ */ N(d, {
46
+ /* @__PURE__ */ I(d, {
43
47
  label: "Outstanding",
44
- value: w(K),
45
- hint: `${W.length} sent + overdue`,
46
- icon: /* @__PURE__ */ N(D, { className: "h-4 w-4" }),
48
+ value: w(ee),
49
+ hint: `${Q.length} sent + overdue`,
50
+ icon: /* @__PURE__ */ I(k, { className: "h-4 w-4" }),
47
51
  tone: "warning"
48
52
  }),
49
- /* @__PURE__ */ N(d, {
53
+ /* @__PURE__ */ I(d, {
50
54
  label: "Collected",
51
- value: w(q),
52
- hint: `${G.length} paid`,
53
- icon: /* @__PURE__ */ N(j, { className: "h-4 w-4" }),
55
+ value: w(te),
56
+ hint: `${$.length} paid`,
57
+ icon: /* @__PURE__ */ I(P, { className: "h-4 w-4" }),
54
58
  tone: "success"
55
59
  })
56
60
  ]
57
61
  }),
58
- /* @__PURE__ */ N("div", {
59
- className: "flex justify-end",
60
- children: /* @__PURE__ */ P(p, {
62
+ /* @__PURE__ */ L("div", {
63
+ className: "flex flex-col gap-2 sm:flex-row sm:items-center",
64
+ children: [/* @__PURE__ */ L("div", {
65
+ className: "relative flex-1",
66
+ children: [/* @__PURE__ */ I(N, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ I(b, {
67
+ placeholder: "Search invoices…",
68
+ value: l,
69
+ onChange: (e) => f(e.target.value),
70
+ onKeyDown: (e) => {
71
+ e.key === "Enter" && l.trim() && J("psw.search.performed", {
72
+ entityType: "invoice",
73
+ queryLength: l.trim().length
74
+ });
75
+ },
76
+ className: "pl-9"
77
+ })]
78
+ }), /* @__PURE__ */ L(p, {
61
79
  value: t,
62
80
  onChange: (e) => n(e.target.value),
63
81
  className: "sm:w-48",
64
- children: [/* @__PURE__ */ N("option", {
82
+ children: [/* @__PURE__ */ I("option", {
65
83
  value: "",
66
84
  children: "All statuses"
67
- }), S(I)]
68
- })
85
+ }), S(z)]
86
+ })]
69
87
  }),
70
- L ? /* @__PURE__ */ N(u, {}) : z ? /* @__PURE__ */ N(c, {
71
- error: z,
72
- onRetry: () => void B()
73
- }) : !M || M.items.length === 0 ? /* @__PURE__ */ N(s, {
88
+ G ? /* @__PURE__ */ I(u, {}) : K ? /* @__PURE__ */ I(c, {
89
+ error: K,
90
+ onRetry: () => void q()
91
+ }) : !W || W.items.length === 0 ? /* @__PURE__ */ I(s, {
74
92
  title: "No invoices yet",
75
93
  description: "Raise an invoice for a client to start tracking receivables.",
76
- icon: /* @__PURE__ */ N(A, { className: "h-8 w-8" }),
77
- action: /* @__PURE__ */ P(F, {
78
- onClick: () => f(!0),
79
- children: [/* @__PURE__ */ N(k, { className: "mr-1.5 h-4 w-4" }), "New invoice"]
94
+ icon: /* @__PURE__ */ I(M, { className: "h-8 w-8" }),
95
+ action: /* @__PURE__ */ L(R, {
96
+ onClick: () => B(!0),
97
+ children: [/* @__PURE__ */ I(j, { className: "mr-1.5 h-4 w-4" }), "New invoice"]
80
98
  })
81
- }) : /* @__PURE__ */ N(h, {
99
+ }) : /* @__PURE__ */ I(h, {
82
100
  title: "Invoices",
83
- children: /* @__PURE__ */ P(_, { children: [/* @__PURE__ */ N("thead", { children: /* @__PURE__ */ P("tr", { children: [
84
- /* @__PURE__ */ N(x, { children: "Number" }),
85
- /* @__PURE__ */ N(x, { children: "Client" }),
86
- /* @__PURE__ */ N(x, { children: "Total" }),
87
- /* @__PURE__ */ N(x, { children: "Status" }),
88
- /* @__PURE__ */ N(x, { children: "Due" }),
89
- /* @__PURE__ */ N(x, {
101
+ children: /* @__PURE__ */ L(_, { children: [/* @__PURE__ */ I("thead", { children: /* @__PURE__ */ L("tr", { children: [
102
+ /* @__PURE__ */ I(x, { children: "Number" }),
103
+ /* @__PURE__ */ I(x, { children: "Client" }),
104
+ /* @__PURE__ */ I(x, { children: "Total" }),
105
+ /* @__PURE__ */ I(x, { children: "Status" }),
106
+ /* @__PURE__ */ I(x, { children: "Due" }),
107
+ /* @__PURE__ */ I(x, {
90
108
  className: "text-right",
91
109
  children: "Actions"
92
110
  })
93
- ] }) }), /* @__PURE__ */ N("tbody", { children: M.items.map((e) => /* @__PURE__ */ P("tr", {
111
+ ] }) }), /* @__PURE__ */ I("tbody", { children: W.items.map((e) => /* @__PURE__ */ L("tr", {
94
112
  className: "transition-colors hover:bg-bg-sunken/60",
95
113
  children: [
96
- /* @__PURE__ */ N(v, { children: /* @__PURE__ */ N("span", {
114
+ /* @__PURE__ */ I(v, { children: /* @__PURE__ */ I("span", {
97
115
  className: "font-medium text-text-primary",
98
116
  children: e.number
99
117
  }) }),
100
- /* @__PURE__ */ N(v, {
118
+ /* @__PURE__ */ I(v, {
101
119
  className: "text-text-primary",
102
- children: U.get(e.clientId) ?? "—"
120
+ children: Z.get(e.clientId) ?? "—"
103
121
  }),
104
- /* @__PURE__ */ N(v, {
122
+ /* @__PURE__ */ I(v, {
105
123
  className: "whitespace-nowrap font-semibold text-text-primary",
106
124
  children: w(e.total, e.currency)
107
125
  }),
108
- /* @__PURE__ */ N(v, { children: /* @__PURE__ */ N(g, { status: e.status }) }),
109
- /* @__PURE__ */ N(v, {
126
+ /* @__PURE__ */ I(v, { children: /* @__PURE__ */ I(g, { status: e.status }) }),
127
+ /* @__PURE__ */ I(v, {
110
128
  className: a("whitespace-nowrap", e.status === "OVERDUE" && "font-medium text-status-error-text"),
111
129
  children: C(e.dueDate)
112
130
  }),
113
- /* @__PURE__ */ N(v, {
131
+ /* @__PURE__ */ I(v, {
114
132
  className: "text-right",
115
- children: e.status !== "PAID" && e.status !== "CANCELLED" && /* @__PURE__ */ P(F, {
133
+ children: e.status !== "PAID" && e.status !== "CANCELLED" && /* @__PURE__ */ L(R, {
116
134
  variant: "ghost",
117
135
  size: "sm",
118
- disabled: V.isPending,
119
- onClick: () => V.mutate(e.id, {
120
- onSuccess: () => b({
136
+ disabled: Y.isPending,
137
+ onClick: () => Y.mutate(e.id, {
138
+ onSuccess: () => U({
121
139
  tone: "success",
122
140
  text: `${e.number} marked paid.`
123
141
  }),
124
- onError: (e) => b({
142
+ onError: (e) => U({
125
143
  tone: "danger",
126
144
  text: e instanceof Error ? e.message : "Failed."
127
145
  })
128
146
  }),
129
- children: [/* @__PURE__ */ N(O, { className: "mr-1 h-4 w-4" }), "Mark paid"]
147
+ children: [/* @__PURE__ */ I(A, { className: "mr-1 h-4 w-4" }), "Mark paid"]
130
148
  })
131
149
  })
132
150
  ]
133
151
  }, e.id)) })] })
134
152
  }),
135
- l && /* @__PURE__ */ N(R, {
136
- onClose: () => f(!1),
137
- onSaved: (e) => b({
153
+ F && /* @__PURE__ */ I(V, {
154
+ onClose: () => B(!1),
155
+ onSaved: (e) => U({
138
156
  tone: "success",
139
157
  text: `${e} created.`
140
158
  })
141
159
  })
142
160
  ]
143
161
  });
144
- }, R = ({ onClose: r, onSaved: i }) => {
145
- let a = t(), s = e({ pagination: { limit: 100 } }), [c, u] = E({
162
+ }, V = ({ onClose: r, onSaved: i }) => {
163
+ let a = t(), s = e({ pagination: { limit: 100 } }), [c, u] = O({
146
164
  clientId: "",
147
165
  engagementId: "",
148
166
  number: "",
@@ -152,23 +170,23 @@ var I = [
152
170
  issueDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
153
171
  dueDate: "",
154
172
  notes: ""
155
- }), [d, m] = E(null), h = n({
173
+ }), [d, m] = O(null), h = n({
156
174
  clientId: c.clientId || void 0,
157
175
  pagination: { limit: 100 }
158
176
  }), g = (e) => (t) => u((n) => ({
159
177
  ...n,
160
178
  [e]: t.target.value
161
179
  })), _ = (Number(c.subtotal) || 0) + (Number(c.taxAmount) || 0);
162
- return /* @__PURE__ */ N(f, {
180
+ return /* @__PURE__ */ I(f, {
163
181
  open: !0,
164
182
  onClose: r,
165
183
  title: "New invoice",
166
- footer: /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ N(F, {
184
+ footer: /* @__PURE__ */ L(F, { children: [/* @__PURE__ */ I(R, {
167
185
  variant: "outline",
168
186
  onClick: r,
169
187
  disabled: a.isPending,
170
188
  children: "Cancel"
171
- }), /* @__PURE__ */ N(F, {
189
+ }), /* @__PURE__ */ I(R, {
172
190
  onClick: () => {
173
191
  if (m(null), !c.clientId) return m("Select a client.");
174
192
  if (!c.number.trim()) return m("Invoice number is required.");
@@ -193,78 +211,78 @@ var I = [
193
211
  disabled: a.isPending,
194
212
  children: a.isPending ? "Creating…" : "Create invoice"
195
213
  })] }),
196
- children: /* @__PURE__ */ P("div", {
214
+ children: /* @__PURE__ */ L("div", {
197
215
  className: "space-y-3",
198
216
  children: [
199
- d && /* @__PURE__ */ N(o, {
217
+ d && /* @__PURE__ */ I(o, {
200
218
  tone: "danger",
201
219
  children: d
202
220
  }),
203
- /* @__PURE__ */ P("div", {
221
+ /* @__PURE__ */ L("div", {
204
222
  className: "grid grid-cols-2 gap-3",
205
- children: [/* @__PURE__ */ N(l, {
223
+ children: [/* @__PURE__ */ I(l, {
206
224
  label: "Client",
207
225
  required: !0,
208
- children: /* @__PURE__ */ P(p, {
226
+ children: /* @__PURE__ */ L(p, {
209
227
  value: c.clientId,
210
228
  onChange: g("clientId"),
211
- children: [/* @__PURE__ */ N("option", {
229
+ children: [/* @__PURE__ */ I("option", {
212
230
  value: "",
213
231
  children: "Select…"
214
- }), s.data?.items.map((e) => /* @__PURE__ */ N("option", {
232
+ }), s.data?.items.map((e) => /* @__PURE__ */ I("option", {
215
233
  value: e.id,
216
234
  children: e.name
217
235
  }, e.id))]
218
236
  })
219
- }), /* @__PURE__ */ N(l, {
237
+ }), /* @__PURE__ */ I(l, {
220
238
  label: "Number",
221
239
  required: !0,
222
- children: /* @__PURE__ */ N(b, {
240
+ children: /* @__PURE__ */ I(b, {
223
241
  value: c.number,
224
242
  onChange: g("number"),
225
243
  placeholder: "INV-2026-014"
226
244
  })
227
245
  })]
228
246
  }),
229
- /* @__PURE__ */ N(l, {
247
+ /* @__PURE__ */ I(l, {
230
248
  label: "Engagement (optional)",
231
- children: /* @__PURE__ */ P(p, {
249
+ children: /* @__PURE__ */ L(p, {
232
250
  value: c.engagementId,
233
251
  onChange: g("engagementId"),
234
252
  disabled: !c.clientId,
235
- children: [/* @__PURE__ */ N("option", {
253
+ children: [/* @__PURE__ */ I("option", {
236
254
  value: "",
237
255
  children: "None"
238
- }), h.data?.items.map((e) => /* @__PURE__ */ N("option", {
256
+ }), h.data?.items.map((e) => /* @__PURE__ */ I("option", {
239
257
  value: e.id,
240
258
  children: e.name
241
259
  }, e.id))]
242
260
  })
243
261
  }),
244
- /* @__PURE__ */ P("div", {
262
+ /* @__PURE__ */ L("div", {
245
263
  className: "grid grid-cols-3 gap-3",
246
264
  children: [
247
- /* @__PURE__ */ N(l, {
265
+ /* @__PURE__ */ I(l, {
248
266
  label: "Subtotal",
249
- children: /* @__PURE__ */ N(b, {
267
+ children: /* @__PURE__ */ I(b, {
250
268
  type: "number",
251
269
  min: "0",
252
270
  value: c.subtotal,
253
271
  onChange: g("subtotal")
254
272
  })
255
273
  }),
256
- /* @__PURE__ */ N(l, {
274
+ /* @__PURE__ */ I(l, {
257
275
  label: "Tax",
258
- children: /* @__PURE__ */ N(b, {
276
+ children: /* @__PURE__ */ I(b, {
259
277
  type: "number",
260
278
  min: "0",
261
279
  value: c.taxAmount,
262
280
  onChange: g("taxAmount")
263
281
  })
264
282
  }),
265
- /* @__PURE__ */ N(l, {
283
+ /* @__PURE__ */ I(l, {
266
284
  label: "Currency",
267
- children: /* @__PURE__ */ N(b, {
285
+ children: /* @__PURE__ */ I(b, {
268
286
  value: c.currency,
269
287
  onChange: g("currency"),
270
288
  maxLength: 3
@@ -272,38 +290,38 @@ var I = [
272
290
  })
273
291
  ]
274
292
  }),
275
- /* @__PURE__ */ P("div", {
293
+ /* @__PURE__ */ L("div", {
276
294
  className: "rounded-lg bg-bg-sunken px-3 py-2 text-sm text-text-secondary",
277
295
  children: [
278
296
  "Total:",
279
297
  " ",
280
- /* @__PURE__ */ N("span", {
298
+ /* @__PURE__ */ I("span", {
281
299
  className: "font-semibold text-text-primary",
282
300
  children: w(_, c.currency)
283
301
  })
284
302
  ]
285
303
  }),
286
- /* @__PURE__ */ P("div", {
304
+ /* @__PURE__ */ L("div", {
287
305
  className: "grid grid-cols-2 gap-3",
288
- children: [/* @__PURE__ */ N(l, {
306
+ children: [/* @__PURE__ */ I(l, {
289
307
  label: "Issue date",
290
- children: /* @__PURE__ */ N(b, {
308
+ children: /* @__PURE__ */ I(b, {
291
309
  type: "date",
292
310
  value: c.issueDate,
293
311
  onChange: g("issueDate")
294
312
  })
295
- }), /* @__PURE__ */ N(l, {
313
+ }), /* @__PURE__ */ I(l, {
296
314
  label: "Due date",
297
- children: /* @__PURE__ */ N(b, {
315
+ children: /* @__PURE__ */ I(b, {
298
316
  type: "date",
299
317
  value: c.dueDate,
300
318
  onChange: g("dueDate")
301
319
  })
302
320
  })]
303
321
  }),
304
- /* @__PURE__ */ N(l, {
322
+ /* @__PURE__ */ I(l, {
305
323
  label: "Notes",
306
- children: /* @__PURE__ */ N(y, {
324
+ children: /* @__PURE__ */ I(y, {
307
325
  value: c.notes,
308
326
  onChange: g("notes")
309
327
  })
@@ -313,6 +331,6 @@ var I = [
313
331
  });
314
332
  };
315
333
  //#endregion
316
- export { L as InvoicesPage };
334
+ export { B as InvoicesPage };
317
335
 
318
336
  //# sourceMappingURL=InvoicesPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/proserviceworld/pages/InvoicesPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Receipt, CheckCircle2, AlertCircle, Wallet } from 'lucide-react';\nimport {\n useInvoices,\n useCreateInvoice,\n useMarkInvoicePaid,\n useClients,\n useEngagements,\n type PswInvoiceStatus,\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 MetricCard,\n TableWrap,\n Th,\n Td,\n enumOptions,\n fmtDate,\n fmtMoney,\n} from '../components/ui';\nimport { cn } from '../utils/cn';\n\nconst STATUSES: PswInvoiceStatus[] = ['DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED'];\n\nexport const InvoicesPage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<PswInvoiceStatus | ''>('');\n const [creating, setCreating] = useState(false);\n const [banner, setBanner] = useState<{ tone: 'success' | 'danger'; text: string } | null>(null);\n const { data, isLoading, error, refetch } = useInvoices({ status: statusFilter || undefined });\n const markPaid = useMarkInvoicePaid();\n const clients = useClients({ pagination: { limit: 100 } });\n const clientName = useMemo(\n () => new Map((clients.data?.items ?? []).map((c) => [c.id, c.name])),\n [clients.data]\n );\n\n const outstandingInvoices = (data?.items ?? []).filter(\n (i) => i.status === 'SENT' || i.status === 'OVERDUE'\n );\n const paidInvoices = (data?.items ?? []).filter((i) => i.status === 'PAID');\n const outstanding = outstandingInvoices.reduce((s, i) => s + i.total, 0);\n const collected = paidInvoices.reduce((s, i) => s + i.total, 0);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Invoices\"\n subtitle=\"Assemble invoices from time, milestones and fixed-fee schedules — and track what's outstanding.\"\n icon={<Receipt className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\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=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <MetricCard\n label=\"Invoices\"\n value={data?.totalCount ?? 0}\n icon={<Receipt className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Outstanding\"\n value={fmtMoney(outstanding)}\n hint={`${outstandingInvoices.length} sent + overdue`}\n icon={<AlertCircle className=\"h-4 w-4\" />}\n tone=\"warning\"\n />\n <MetricCard\n label=\"Collected\"\n value={fmtMoney(collected)}\n hint={`${paidInvoices.length} paid`}\n icon={<Wallet className=\"h-4 w-4\" />}\n tone=\"success\"\n />\n </div>\n\n <div className=\"flex justify-end\">\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswInvoiceStatus | '')}\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 invoices yet\"\n description=\"Raise an invoice for a client to start tracking receivables.\"\n icon={<Receipt className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\n </Button>\n }\n />\n ) : (\n <SectionCard title=\"Invoices\">\n <TableWrap>\n <thead>\n <tr>\n <Th>Number</Th>\n <Th>Client</Th>\n <Th>Total</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((i) => (\n <tr key={i.id} className=\"transition-colors hover:bg-bg-sunken/60\">\n <Td>\n <span className=\"font-medium text-text-primary\">{i.number}</span>\n </Td>\n <Td className=\"text-text-primary\">{clientName.get(i.clientId) ?? '—'}</Td>\n <Td className=\"whitespace-nowrap font-semibold text-text-primary\">\n {fmtMoney(i.total, i.currency)}\n </Td>\n <Td>\n <StatusPill status={i.status} />\n </Td>\n <Td\n className={cn(\n 'whitespace-nowrap',\n i.status === 'OVERDUE' && 'font-medium text-status-error-text'\n )}\n >\n {fmtDate(i.dueDate)}\n </Td>\n <Td className=\"text-right\">\n {i.status !== 'PAID' && i.status !== 'CANCELLED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={markPaid.isPending}\n onClick={() =>\n markPaid.mutate(i.id, {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `${i.number} marked paid.` }),\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\" />\n Mark paid\n </Button>\n )}\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <InvoiceModal\n onClose={() => setCreating(false)}\n onSaved={(n) => setBanner({ tone: 'success', text: `${n} created.` })}\n />\n )}\n </div>\n );\n};\n\nconst InvoiceModal: FC<{ onClose: () => void; onSaved: (number: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateInvoice();\n const clients = useClients({ pagination: { limit: 100 } });\n const [form, setForm] = useState({\n clientId: '',\n engagementId: '',\n number: '',\n currency: 'USD',\n subtotal: '',\n taxAmount: '',\n issueDate: new Date().toISOString().slice(0, 10),\n dueDate: '',\n notes: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const engagements = useEngagements({\n clientId: form.clientId || undefined,\n pagination: { limit: 100 },\n });\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const total = (Number(form.subtotal) || 0) + (Number(form.taxAmount) || 0);\n\n const submit = () => {\n setErr(null);\n if (!form.clientId) return setErr('Select a client.');\n if (!form.number.trim()) return setErr('Invoice number is required.');\n create.mutate(\n {\n clientId: form.clientId,\n engagementId: form.engagementId || null,\n number: form.number.trim(),\n currency: form.currency || 'USD',\n subtotal: Number(form.subtotal) || 0,\n taxAmount: Number(form.taxAmount) || 0,\n total,\n issueDate: form.issueDate ? new Date(form.issueDate).toISOString() : null,\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n notes: form.notes.trim() || null,\n },\n {\n onSuccess: () => {\n onSaved(form.number.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 invoice\"\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 ? 'Creating…' : 'Create invoice'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select…</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=\"Number\" required>\n <TextInput value={form.number} onChange={set('number')} placeholder=\"INV-2026-014\" />\n </Field>\n </div>\n <Field label=\"Engagement (optional)\">\n <NativeSelect\n value={form.engagementId}\n onChange={set('engagementId')}\n disabled={!form.clientId}\n >\n <option value=\"\">None</option>\n {engagements.data?.items.map((e) => (\n <option key={e.id} value={e.id}>\n {e.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Subtotal\">\n <TextInput type=\"number\" min=\"0\" value={form.subtotal} onChange={set('subtotal')} />\n </Field>\n <Field label=\"Tax\">\n <TextInput type=\"number\" min=\"0\" value={form.taxAmount} onChange={set('taxAmount')} />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <div className=\"rounded-lg bg-bg-sunken px-3 py-2 text-sm text-text-secondary\">\n Total:{' '}\n <span className=\"font-semibold text-text-primary\">{fmtMoney(total, form.currency)}</span>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Issue date\">\n <TextInput type=\"date\" value={form.issueDate} onChange={set('issueDate')} />\n </Field>\n <Field label=\"Due date\">\n <TextInput type=\"date\" value={form.dueDate} onChange={set('dueDate')} />\n </Field>\n </div>\n <Field label=\"Notes\">\n <TextArea value={form.notes} onChange={set('notes')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"InvoicesPage.js","names":[],"sources":["../../../src/proserviceworld/pages/InvoicesPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useDeferredValue, useMemo, useState } from 'react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Plus, Receipt, CheckCircle2, AlertCircle, Wallet, Search } from 'lucide-react';\nimport {\n useInvoices,\n useCreateInvoice,\n useMarkInvoicePaid,\n useClients,\n useEngagements,\n type PswInvoiceStatus,\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 MetricCard,\n TableWrap,\n Th,\n Td,\n enumOptions,\n fmtDate,\n fmtMoney,\n} from '../components/ui';\nimport { useAnalytics } from '../hooks/useAnalytics';\nimport { cn } from '../utils/cn';\n\nconst STATUSES: PswInvoiceStatus[] = ['DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED'];\n\nexport const InvoicesPage: FC = () => {\n const [statusFilter, setStatusFilter] = useState<PswInvoiceStatus | ''>('');\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 } = useInvoices({\n status: statusFilter || undefined,\n search: deferredSearch.trim() || undefined,\n });\n const { emitFact } = useAnalytics();\n const markPaid = useMarkInvoicePaid();\n const clients = useClients({ pagination: { limit: 100 } });\n const clientName = useMemo(\n () => new Map((clients.data?.items ?? []).map((c) => [c.id, c.name])),\n [clients.data]\n );\n\n const outstandingInvoices = (data?.items ?? []).filter(\n (i) => i.status === 'SENT' || i.status === 'OVERDUE'\n );\n const paidInvoices = (data?.items ?? []).filter((i) => i.status === 'PAID');\n const outstanding = outstandingInvoices.reduce((s, i) => s + i.total, 0);\n const collected = paidInvoices.reduce((s, i) => s + i.total, 0);\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Invoices\"\n subtitle=\"Assemble invoices from time, milestones and fixed-fee schedules — and track what's outstanding.\"\n icon={<Receipt className=\"h-6 w-6\" />}\n actions={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\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=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <MetricCard\n label=\"Invoices\"\n value={data?.totalCount ?? 0}\n icon={<Receipt className=\"h-4 w-4\" />}\n />\n <MetricCard\n label=\"Outstanding\"\n value={fmtMoney(outstanding)}\n hint={`${outstandingInvoices.length} sent + overdue`}\n icon={<AlertCircle className=\"h-4 w-4\" />}\n tone=\"warning\"\n />\n <MetricCard\n label=\"Collected\"\n value={fmtMoney(collected)}\n hint={`${paidInvoices.length} paid`}\n icon={<Wallet className=\"h-4 w-4\" />}\n tone=\"success\"\n />\n </div>\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 invoices…\"\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: 'invoice',\n queryLength: search.trim().length,\n });\n }\n }}\n className=\"pl-9\"\n />\n </div>\n <NativeSelect\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as PswInvoiceStatus | '')}\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 invoices yet\"\n description=\"Raise an invoice for a client to start tracking receivables.\"\n icon={<Receipt className=\"h-8 w-8\" />}\n action={\n <Button onClick={() => setCreating(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" />\n New invoice\n </Button>\n }\n />\n ) : (\n <SectionCard title=\"Invoices\">\n <TableWrap>\n <thead>\n <tr>\n <Th>Number</Th>\n <Th>Client</Th>\n <Th>Total</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((i) => (\n <tr key={i.id} className=\"transition-colors hover:bg-bg-sunken/60\">\n <Td>\n <span className=\"font-medium text-text-primary\">{i.number}</span>\n </Td>\n <Td className=\"text-text-primary\">{clientName.get(i.clientId) ?? '—'}</Td>\n <Td className=\"whitespace-nowrap font-semibold text-text-primary\">\n {fmtMoney(i.total, i.currency)}\n </Td>\n <Td>\n <StatusPill status={i.status} />\n </Td>\n <Td\n className={cn(\n 'whitespace-nowrap',\n i.status === 'OVERDUE' && 'font-medium text-status-error-text'\n )}\n >\n {fmtDate(i.dueDate)}\n </Td>\n <Td className=\"text-right\">\n {i.status !== 'PAID' && i.status !== 'CANCELLED' && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={markPaid.isPending}\n onClick={() =>\n markPaid.mutate(i.id, {\n onSuccess: () =>\n setBanner({ tone: 'success', text: `${i.number} marked paid.` }),\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\" />\n Mark paid\n </Button>\n )}\n </Td>\n </tr>\n ))}\n </tbody>\n </TableWrap>\n </SectionCard>\n )}\n\n {creating && (\n <InvoiceModal\n onClose={() => setCreating(false)}\n onSaved={(n) => setBanner({ tone: 'success', text: `${n} created.` })}\n />\n )}\n </div>\n );\n};\n\nconst InvoiceModal: FC<{ onClose: () => void; onSaved: (number: string) => void }> = ({\n onClose,\n onSaved,\n}) => {\n const create = useCreateInvoice();\n const clients = useClients({ pagination: { limit: 100 } });\n const [form, setForm] = useState({\n clientId: '',\n engagementId: '',\n number: '',\n currency: 'USD',\n subtotal: '',\n taxAmount: '',\n issueDate: new Date().toISOString().slice(0, 10),\n dueDate: '',\n notes: '',\n });\n const [err, setErr] = useState<string | null>(null);\n const engagements = useEngagements({\n clientId: form.clientId || undefined,\n pagination: { limit: 100 },\n });\n const set = (k: keyof typeof form) => (e: { target: { value: string } }) =>\n setForm((f) => ({ ...f, [k]: e.target.value }));\n const total = (Number(form.subtotal) || 0) + (Number(form.taxAmount) || 0);\n\n const submit = () => {\n setErr(null);\n if (!form.clientId) return setErr('Select a client.');\n if (!form.number.trim()) return setErr('Invoice number is required.');\n create.mutate(\n {\n clientId: form.clientId,\n engagementId: form.engagementId || null,\n number: form.number.trim(),\n currency: form.currency || 'USD',\n subtotal: Number(form.subtotal) || 0,\n taxAmount: Number(form.taxAmount) || 0,\n total,\n issueDate: form.issueDate ? new Date(form.issueDate).toISOString() : null,\n dueDate: form.dueDate ? new Date(form.dueDate).toISOString() : null,\n notes: form.notes.trim() || null,\n },\n {\n onSuccess: () => {\n onSaved(form.number.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 invoice\"\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 ? 'Creating…' : 'Create invoice'}\n </Button>\n </>\n }\n >\n <div className=\"space-y-3\">\n {err && <Banner tone=\"danger\">{err}</Banner>}\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Client\" required>\n <NativeSelect value={form.clientId} onChange={set('clientId')}>\n <option value=\"\">Select…</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=\"Number\" required>\n <TextInput value={form.number} onChange={set('number')} placeholder=\"INV-2026-014\" />\n </Field>\n </div>\n <Field label=\"Engagement (optional)\">\n <NativeSelect\n value={form.engagementId}\n onChange={set('engagementId')}\n disabled={!form.clientId}\n >\n <option value=\"\">None</option>\n {engagements.data?.items.map((e) => (\n <option key={e.id} value={e.id}>\n {e.name}\n </option>\n ))}\n </NativeSelect>\n </Field>\n <div className=\"grid grid-cols-3 gap-3\">\n <Field label=\"Subtotal\">\n <TextInput type=\"number\" min=\"0\" value={form.subtotal} onChange={set('subtotal')} />\n </Field>\n <Field label=\"Tax\">\n <TextInput type=\"number\" min=\"0\" value={form.taxAmount} onChange={set('taxAmount')} />\n </Field>\n <Field label=\"Currency\">\n <TextInput value={form.currency} onChange={set('currency')} maxLength={3} />\n </Field>\n </div>\n <div className=\"rounded-lg bg-bg-sunken px-3 py-2 text-sm text-text-secondary\">\n Total:{' '}\n <span className=\"font-semibold text-text-primary\">{fmtMoney(total, form.currency)}</span>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Issue date\">\n <TextInput type=\"date\" value={form.issueDate} onChange={set('issueDate')} />\n </Field>\n <Field label=\"Due date\">\n <TextInput type=\"date\" value={form.dueDate} onChange={set('dueDate')} />\n </Field>\n </div>\n <Field label=\"Notes\">\n <TextArea value={form.notes} onChange={set('notes')} />\n </Field>\n </div>\n </Modal>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TimePage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/TimePage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAmDhC,eAAO,MAAM,QAAQ,EAAE,EAuKtB,CAAC"}
1
+ {"version":3,"file":"TimePage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/TimePage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA6DhC,eAAO,MAAM,QAAQ,EAAE,EA8LtB,CAAC"}