@burdenoff/microfe-timecampus 2026.719.4 → 2026.731.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/generated/wspace-operations.d.ts +527 -0
  2. package/dist/generated/wspace-operations.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.js +1112 -880
  4. package/dist/generated/wspace-operations.js.map +1 -1
  5. package/dist/generated/wspace-types.d.ts +175 -0
  6. package/dist/generated/wspace-types.d.ts.map +1 -1
  7. package/dist/generated/wspace-types.js +5 -3
  8. package/dist/generated/wspace-types.js.map +1 -1
  9. package/dist/index.js +5 -5
  10. package/dist/timecampus/TimeCampusRoot.d.ts.map +1 -1
  11. package/dist/timecampus/TimeCampusRoot.js +81 -73
  12. package/dist/timecampus/TimeCampusRoot.js.map +1 -1
  13. package/dist/timecampus/TimeCampusRoutes.d.ts.map +1 -1
  14. package/dist/timecampus/TimeCampusRoutes.js +17 -13
  15. package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
  16. package/dist/timecampus/components/StatusBadge.d.ts.map +1 -1
  17. package/dist/timecampus/components/StatusBadge.js.map +1 -1
  18. package/dist/timecampus/pages/invoicing/InvoicingPage.d.ts +2 -0
  19. package/dist/timecampus/pages/invoicing/InvoicingPage.d.ts.map +1 -0
  20. package/dist/timecampus/pages/invoicing/InvoicingPage.js +595 -0
  21. package/dist/timecampus/pages/invoicing/InvoicingPage.js.map +1 -0
  22. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.d.ts.map +1 -1
  23. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js +88 -59
  24. package/dist/timecampus/pages/lifetime/PersonalCorporatePage.js.map +1 -1
  25. package/dist/timecampus/types/index.d.ts.map +1 -1
  26. package/dist/timecampus/utils/helpers.d.ts.map +1 -1
  27. package/dist/timecampus/utils/helpers.js +3 -1
  28. package/dist/timecampus/utils/helpers.js.map +1 -1
  29. package/dist/types/assistant.d.ts.map +1 -1
  30. package/dist/utils/pageContext.d.ts.map +1 -1
  31. package/dist/utils/pageContext.js +1 -0
  32. package/dist/utils/pageContext.js.map +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1,595 @@
1
+ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
+ import { formatCurrency as t, formatDate as n } from "../../utils/helpers.js";
3
+ import { StatCard as r } from "../../components/StatCard.js";
4
+ import { LoadingSkeleton as i } from "../../components/LoadingSkeleton.js";
5
+ import { useDeleteInvoiceDraftMutation as a, useGenerateInvoiceDraftMutation as o, useGetInvoiceStatsQuery as ee, useListInvoicesQuery as s, useUpdateInvoiceStatusMutation as te } from "../../../generated/wspace-operations.js";
6
+ import { PageLayout as c } from "../../components/PageLayout.js";
7
+ import { StatusBadge as ne } from "../../components/StatusBadge.js";
8
+ import { EmptyState as re } from "../../components/EmptyState.js";
9
+ import { Modal as l } from "../../components/Modal.js";
10
+ import { ToastContainer as ie, useToast as ae } from "../../components/Toast.js";
11
+ import { TimeCampusInvoiceStatus as u } from "../../../generated/wspace-types.js";
12
+ import { useState as d } from "react";
13
+ import { AlertTriangle as oe, CheckCircle2 as se, DollarSign as ce, Download as f, Eye as le, FileText as p, Plus as ue, Send as de, Trash2 as fe } from "lucide-react";
14
+ import { gql as m } from "@apollo/client";
15
+ import { useApolloClient as h } from "@apollo/client/react";
16
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
17
+ //#region src/timecampus/pages/invoicing/InvoicingPage.tsx
18
+ var y = 25, b = "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary", pe = m`
19
+ query TimeCampusInvoicing_GetInvoiceHtml($id: ID!, $workspaceId: ID!) {
20
+ timeCampusInvoiceHtml(id: $id, workspaceId: $workspaceId)
21
+ }
22
+ `, me = m`
23
+ query TimeCampusInvoicing_GetInvoicePdf($id: ID!, $workspaceId: ID!) {
24
+ timeCampusInvoicePdf(id: $id, workspaceId: $workspaceId)
25
+ }
26
+ `;
27
+ function x() {
28
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
29
+ }
30
+ function S() {
31
+ let e = /* @__PURE__ */ new Date();
32
+ return new Date(e.getFullYear(), e.getMonth(), 1).toISOString().slice(0, 10);
33
+ }
34
+ var C = {
35
+ clientName: "",
36
+ clientEmail: "",
37
+ clientAddress: "",
38
+ periodStart: S(),
39
+ periodEnd: x(),
40
+ taxRatePercent: "0",
41
+ currency: "USD",
42
+ dueDate: "",
43
+ notes: "",
44
+ projectIds: ""
45
+ };
46
+ function he(e, t) {
47
+ let n = atob(e), r = new Uint8Array(n.length);
48
+ for (let e = 0; e < n.length; e++) r[e] = n.charCodeAt(e);
49
+ let i = new Blob([r], { type: "application/pdf" }), a = URL.createObjectURL(i), o = document.createElement("a");
50
+ o.href = a, o.download = t, document.body.appendChild(o), o.click(), o.remove(), URL.revokeObjectURL(a);
51
+ }
52
+ function w() {
53
+ let { workspaceId: m = "" } = e(), { toasts: w, addToast: T, dismissToast: ge } = ae(), E = h(), { data: D, loading: O, error: _e, refetch: ve, fetchMore: ye } = s({
54
+ variables: {
55
+ workspaceId: m,
56
+ pagination: {
57
+ limit: y,
58
+ offset: 0
59
+ }
60
+ },
61
+ notifyOnNetworkStatusChange: !0
62
+ }), { data: be, loading: xe, refetch: Se } = ee({ variables: { workspaceId: m } }), [Ce, { loading: k }] = o(), [A] = te(), [we, { loading: j }] = a(), M = D?.timeCampusInvoices?.items ?? [], Te = D?.timeCampusInvoices?.total ?? 0, N = be?.timeCampusInvoiceStats, [P, F] = d(!1), [Ee, De] = d(!0), [Oe, I] = d(!1), [L, R] = d(C), [z, B] = d(null), [V, H] = d(null), [ke, U] = d(!1), [W, G] = d(null), [K, q] = d(null), [Ae, J] = d(null), Y = async () => {
63
+ await Promise.all([ve(), Se()]);
64
+ }, je = () => {
65
+ R({
66
+ ...C,
67
+ periodStart: S(),
68
+ periodEnd: x()
69
+ }), I(!0);
70
+ }, X = () => I(!1), Me = async () => {
71
+ if (!L.clientName.trim() || !L.periodStart || !L.periodEnd) return;
72
+ let e = Number(L.taxRatePercent || "0") / 100;
73
+ if (Number.isNaN(e) || e < 0 || e > 1) {
74
+ T({
75
+ title: "Tax rate must be between 0 and 100%",
76
+ variant: "error"
77
+ });
78
+ return;
79
+ }
80
+ try {
81
+ await Ce({ variables: { input: {
82
+ workspaceId: m,
83
+ clientName: L.clientName.trim(),
84
+ clientEmail: L.clientEmail.trim() || void 0,
85
+ clientAddress: L.clientAddress.trim() || void 0,
86
+ periodStart: new Date(L.periodStart).toISOString(),
87
+ periodEnd: (/* @__PURE__ */ new Date(new Date(L.periodEnd).getTime() + 864e5 - 1)).toISOString(),
88
+ taxRate: e,
89
+ currency: L.currency.trim() || "USD",
90
+ dueDate: L.dueDate ? new Date(L.dueDate).toISOString() : void 0,
91
+ notes: L.notes.trim() || void 0,
92
+ projectIds: L.projectIds.trim() ? L.projectIds.split(",").map((e) => e.trim()).filter(Boolean) : void 0
93
+ } } }), T({
94
+ title: "Draft invoice generated",
95
+ variant: "success"
96
+ }), X(), await Y();
97
+ } catch (e) {
98
+ T({
99
+ title: e instanceof Error ? e.message : "Failed to generate invoice — check the billing period has un-invoiced billable time",
100
+ variant: "error"
101
+ });
102
+ }
103
+ }, Z = async (e) => {
104
+ J(e.id);
105
+ try {
106
+ await A({ variables: { input: {
107
+ id: e.id,
108
+ workspaceId: m,
109
+ status: u.Sent
110
+ } } }), T({
111
+ title: "Invoice marked as sent",
112
+ variant: "success"
113
+ }), await Y();
114
+ } catch {
115
+ T({
116
+ title: "Failed to update invoice status",
117
+ variant: "error"
118
+ });
119
+ } finally {
120
+ J(null);
121
+ }
122
+ }, Ne = async (e) => {
123
+ J(e.id);
124
+ try {
125
+ await A({ variables: { input: {
126
+ id: e.id,
127
+ workspaceId: m,
128
+ status: u.Paid
129
+ } } }), T({
130
+ title: "Invoice marked as paid",
131
+ variant: "success"
132
+ }), await Y();
133
+ } catch {
134
+ T({
135
+ title: "Failed to update invoice status",
136
+ variant: "error"
137
+ });
138
+ } finally {
139
+ J(null);
140
+ }
141
+ }, Pe = async () => {
142
+ if (K) try {
143
+ await we({ variables: {
144
+ id: K.id,
145
+ workspaceId: m
146
+ } }), T({
147
+ title: "Draft invoice deleted",
148
+ variant: "success"
149
+ }), q(null), await Y();
150
+ } catch {
151
+ T({
152
+ title: "Failed to delete invoice",
153
+ variant: "error"
154
+ });
155
+ }
156
+ }, Fe = async (e) => {
157
+ B(e), H(null), U(!0);
158
+ try {
159
+ let { data: t } = await E.query({
160
+ query: pe,
161
+ variables: {
162
+ id: e.id,
163
+ workspaceId: m
164
+ },
165
+ fetchPolicy: "no-cache"
166
+ }), n = t?.timeCampusInvoiceHtml;
167
+ H(n ?? null);
168
+ } catch {
169
+ T({
170
+ title: "Failed to load invoice preview",
171
+ variant: "error"
172
+ });
173
+ } finally {
174
+ U(!1);
175
+ }
176
+ }, Q = async (e) => {
177
+ G(e.id);
178
+ try {
179
+ let { data: t } = await E.query({
180
+ query: me,
181
+ variables: {
182
+ id: e.id,
183
+ workspaceId: m
184
+ },
185
+ fetchPolicy: "no-cache"
186
+ }), n = t?.timeCampusInvoicePdf;
187
+ if (!n) throw Error("Empty PDF payload");
188
+ he(n, `${e.invoiceNumber}.pdf`);
189
+ } catch {
190
+ T({
191
+ title: "Failed to download PDF",
192
+ variant: "error"
193
+ });
194
+ } finally {
195
+ G(null);
196
+ }
197
+ }, Ie = async () => {
198
+ F(!0);
199
+ try {
200
+ ((await ye({
201
+ variables: {
202
+ workspaceId: m,
203
+ pagination: {
204
+ limit: y,
205
+ offset: M.length
206
+ }
207
+ },
208
+ updateQuery: (e, { fetchMoreResult: t }) => t ? {
209
+ ...e,
210
+ timeCampusInvoices: {
211
+ ...t.timeCampusInvoices,
212
+ items: [...e.timeCampusInvoices.items, ...t.timeCampusInvoices.items]
213
+ }
214
+ } : e
215
+ })).data?.timeCampusInvoices?.items?.length ?? 0) < y && De(!1);
216
+ } finally {
217
+ F(!1);
218
+ }
219
+ };
220
+ if (O && xe && M.length === 0) return /* @__PURE__ */ _(c, {
221
+ title: "Invoicing",
222
+ description: "Bill clients for billable time",
223
+ children: /* @__PURE__ */ _(i, {
224
+ variant: "list",
225
+ count: 4
226
+ })
227
+ });
228
+ if (_e) return /* @__PURE__ */ _(c, {
229
+ title: "Invoicing",
230
+ description: "Bill clients for billable time",
231
+ children: /* @__PURE__ */ _("div", {
232
+ className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
233
+ children: "Failed to load invoices. Please try again later."
234
+ })
235
+ });
236
+ let $ = /* @__PURE__ */ v("button", {
237
+ onClick: je,
238
+ className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
239
+ children: [/* @__PURE__ */ _(ue, { className: "h-4 w-4" }), "Generate Invoice"]
240
+ });
241
+ return /* @__PURE__ */ v(c, {
242
+ title: "Invoicing",
243
+ description: "Bill clients for billable time",
244
+ purpose: "Turn logged, billable hours into a real invoice — pick a client and a billing period, and TimeCampus aggregates every un-invoiced billable time entry by project and task, applying each entry's own rate, into a draft you can review, send, and mark paid.",
245
+ actions: $,
246
+ children: [
247
+ /* @__PURE__ */ v("div", {
248
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
249
+ children: [
250
+ /* @__PURE__ */ _(r, {
251
+ featured: !0,
252
+ title: "Total Invoiced",
253
+ value: t(N?.totalInvoiced ?? 0),
254
+ icon: /* @__PURE__ */ _(ce, { className: "h-5 w-5" })
255
+ }),
256
+ /* @__PURE__ */ _(r, {
257
+ title: "Outstanding",
258
+ value: t(N?.totalOutstanding ?? 0),
259
+ description: `${N?.sentCount ?? 0} sent, awaiting payment`
260
+ }),
261
+ /* @__PURE__ */ _(r, {
262
+ title: "Paid",
263
+ value: t(N?.totalPaid ?? 0),
264
+ description: `${N?.paidCount ?? 0} invoices paid`
265
+ }),
266
+ /* @__PURE__ */ _(r, {
267
+ title: "Drafts",
268
+ value: String(N?.draftCount ?? 0),
269
+ icon: /* @__PURE__ */ _(p, { className: "h-5 w-5" }),
270
+ description: "not yet sent"
271
+ })
272
+ ]
273
+ }),
274
+ M.length === 0 && !O ? /* @__PURE__ */ _(re, {
275
+ illustration: "empty-data",
276
+ title: "No invoices yet",
277
+ description: "Generate a draft invoice from real billable time entries to bill a client.",
278
+ action: $
279
+ }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("div", {
280
+ className: "mt-4 rounded-xl border border-border-seam bg-gloss-card shadow-pop",
281
+ children: /* @__PURE__ */ _("div", {
282
+ className: "divide-y divide-border",
283
+ children: M.map((e) => {
284
+ let r = Ae === e.id, i = e.status === u.Draft, a = e.status === u.Sent;
285
+ return /* @__PURE__ */ v("div", {
286
+ className: "flex flex-col gap-3 p-4 hover:bg-bg-sunken/50 transition-colors sm:flex-row sm:items-center sm:justify-between",
287
+ children: [/* @__PURE__ */ v("div", {
288
+ className: "flex items-center gap-3",
289
+ children: [/* @__PURE__ */ _(p, { className: "h-5 w-5 shrink-0 text-text-secondary" }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ v("p", {
290
+ className: "text-sm font-medium text-text-primary",
291
+ children: [
292
+ e.invoiceNumber,
293
+ " · ",
294
+ e.clientName
295
+ ]
296
+ }), /* @__PURE__ */ v("p", {
297
+ className: "text-xs text-text-secondary",
298
+ children: [
299
+ n(e.periodStart),
300
+ " –",
301
+ " ",
302
+ n(e.periodEnd),
303
+ e.dueDate && /* @__PURE__ */ v(g, { children: [" · Due ", n(e.dueDate)] })
304
+ ]
305
+ })] })]
306
+ }), /* @__PURE__ */ v("div", {
307
+ className: "flex items-center gap-3",
308
+ children: [
309
+ /* @__PURE__ */ _("p", {
310
+ className: "text-sm font-medium text-text-primary",
311
+ children: t(e.total, e.currency)
312
+ }),
313
+ /* @__PURE__ */ _(ne, {
314
+ label: e.status.toLowerCase(),
315
+ status: e.status.toLowerCase()
316
+ }),
317
+ e.isOverdue && /* @__PURE__ */ v("span", {
318
+ title: "Past due date",
319
+ className: "inline-flex items-center gap-1 text-xs font-medium text-status-error-text",
320
+ children: [/* @__PURE__ */ _(oe, { className: "h-3.5 w-3.5" }), "Overdue"]
321
+ }),
322
+ /* @__PURE__ */ v("div", {
323
+ className: "flex items-center gap-1",
324
+ children: [
325
+ /* @__PURE__ */ _("button", {
326
+ type: "button",
327
+ onClick: () => void Fe(e),
328
+ title: "View invoice",
329
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken",
330
+ children: /* @__PURE__ */ _(le, { className: "h-4 w-4" })
331
+ }),
332
+ /* @__PURE__ */ _("button", {
333
+ type: "button",
334
+ disabled: W === e.id,
335
+ onClick: () => void Q(e),
336
+ title: "Download PDF",
337
+ className: "rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken disabled:opacity-50",
338
+ children: /* @__PURE__ */ _(f, { className: "h-4 w-4" })
339
+ }),
340
+ i && /* @__PURE__ */ _("button", {
341
+ type: "button",
342
+ disabled: r,
343
+ onClick: () => Z(e),
344
+ title: "Mark as sent",
345
+ className: "rounded-md p-1.5 text-status-info-text hover:bg-status-info-bg disabled:opacity-50",
346
+ children: /* @__PURE__ */ _(de, { className: "h-4 w-4" })
347
+ }),
348
+ a && /* @__PURE__ */ _("button", {
349
+ type: "button",
350
+ disabled: r,
351
+ onClick: () => Ne(e),
352
+ title: "Mark as paid",
353
+ className: "rounded-md p-1.5 text-status-success-text hover:bg-status-success-bg disabled:opacity-50",
354
+ children: /* @__PURE__ */ _(se, { className: "h-4 w-4" })
355
+ }),
356
+ i && /* @__PURE__ */ _("button", {
357
+ type: "button",
358
+ onClick: () => q(e),
359
+ title: "Delete draft",
360
+ className: "rounded-md p-1.5 text-status-error-text hover:bg-status-error-bg",
361
+ children: /* @__PURE__ */ _(fe, { className: "h-4 w-4" })
362
+ })
363
+ ]
364
+ })
365
+ ]
366
+ })]
367
+ }, e.id);
368
+ })
369
+ })
370
+ }), Ee && M.length < Te && /* @__PURE__ */ _("div", {
371
+ className: "mt-4 flex justify-center",
372
+ children: /* @__PURE__ */ _("button", {
373
+ onClick: Ie,
374
+ disabled: P,
375
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50",
376
+ children: P ? "Loading..." : "Load more"
377
+ })
378
+ })] }),
379
+ /* @__PURE__ */ _(l, {
380
+ open: Oe,
381
+ onClose: X,
382
+ title: "Generate Invoice",
383
+ description: "Aggregates every billable, un-invoiced time entry in the selected period into a draft invoice.",
384
+ footer: /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
385
+ onClick: X,
386
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
387
+ children: "Cancel"
388
+ }), /* @__PURE__ */ _("button", {
389
+ onClick: Me,
390
+ disabled: k || !L.clientName.trim() || !L.periodStart || !L.periodEnd,
391
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
392
+ children: k ? "Generating..." : "Generate Draft"
393
+ })] }),
394
+ children: /* @__PURE__ */ v("div", {
395
+ className: "space-y-4",
396
+ children: [
397
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
398
+ className: "block text-sm font-medium text-text-primary",
399
+ children: "Client name"
400
+ }), /* @__PURE__ */ _("input", {
401
+ type: "text",
402
+ placeholder: "Acme Corp",
403
+ value: L.clientName,
404
+ onChange: (e) => R({
405
+ ...L,
406
+ clientName: e.target.value
407
+ }),
408
+ className: b
409
+ })] }),
410
+ /* @__PURE__ */ v("div", {
411
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
412
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
413
+ className: "block text-sm font-medium text-text-primary",
414
+ children: "Client email"
415
+ }), /* @__PURE__ */ _("input", {
416
+ type: "email",
417
+ placeholder: "billing@acme.com",
418
+ value: L.clientEmail,
419
+ onChange: (e) => R({
420
+ ...L,
421
+ clientEmail: e.target.value
422
+ }),
423
+ className: b
424
+ })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
425
+ className: "block text-sm font-medium text-text-primary",
426
+ children: "Currency"
427
+ }), /* @__PURE__ */ _("input", {
428
+ type: "text",
429
+ placeholder: "USD",
430
+ value: L.currency,
431
+ onChange: (e) => R({
432
+ ...L,
433
+ currency: e.target.value
434
+ }),
435
+ className: b
436
+ })] })]
437
+ }),
438
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
439
+ className: "block text-sm font-medium text-text-primary",
440
+ children: "Client address"
441
+ }), /* @__PURE__ */ _("input", {
442
+ type: "text",
443
+ placeholder: "Optional billing address",
444
+ value: L.clientAddress,
445
+ onChange: (e) => R({
446
+ ...L,
447
+ clientAddress: e.target.value
448
+ }),
449
+ className: b
450
+ })] }),
451
+ /* @__PURE__ */ v("div", {
452
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
453
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
454
+ className: "block text-sm font-medium text-text-primary",
455
+ children: "Billing period start"
456
+ }), /* @__PURE__ */ _("input", {
457
+ type: "date",
458
+ value: L.periodStart,
459
+ onChange: (e) => R({
460
+ ...L,
461
+ periodStart: e.target.value
462
+ }),
463
+ className: b
464
+ })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
465
+ className: "block text-sm font-medium text-text-primary",
466
+ children: "Billing period end"
467
+ }), /* @__PURE__ */ _("input", {
468
+ type: "date",
469
+ value: L.periodEnd,
470
+ onChange: (e) => R({
471
+ ...L,
472
+ periodEnd: e.target.value
473
+ }),
474
+ className: b
475
+ })] })]
476
+ }),
477
+ /* @__PURE__ */ v("div", {
478
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
479
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
480
+ className: "block text-sm font-medium text-text-primary",
481
+ children: "Tax rate (%)"
482
+ }), /* @__PURE__ */ _("input", {
483
+ type: "number",
484
+ min: 0,
485
+ max: 100,
486
+ step: "0.1",
487
+ value: L.taxRatePercent,
488
+ onChange: (e) => R({
489
+ ...L,
490
+ taxRatePercent: e.target.value
491
+ }),
492
+ className: b
493
+ })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
494
+ className: "block text-sm font-medium text-text-primary",
495
+ children: "Due date"
496
+ }), /* @__PURE__ */ _("input", {
497
+ type: "date",
498
+ value: L.dueDate,
499
+ onChange: (e) => R({
500
+ ...L,
501
+ dueDate: e.target.value
502
+ }),
503
+ className: b
504
+ })] })]
505
+ }),
506
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
507
+ className: "block text-sm font-medium text-text-primary",
508
+ children: "Project IDs"
509
+ }), /* @__PURE__ */ _("input", {
510
+ type: "text",
511
+ placeholder: "Optional, comma-separated. Leave blank for all projects.",
512
+ value: L.projectIds,
513
+ onChange: (e) => R({
514
+ ...L,
515
+ projectIds: e.target.value
516
+ }),
517
+ className: b
518
+ })] }),
519
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
520
+ className: "block text-sm font-medium text-text-primary",
521
+ children: "Notes"
522
+ }), /* @__PURE__ */ _("textarea", {
523
+ rows: 2,
524
+ placeholder: "Optional notes printed on the invoice",
525
+ value: L.notes,
526
+ onChange: (e) => R({
527
+ ...L,
528
+ notes: e.target.value
529
+ }),
530
+ className: b
531
+ })] })
532
+ ]
533
+ })
534
+ }),
535
+ /* @__PURE__ */ _(l, {
536
+ open: z !== null,
537
+ onClose: () => B(null),
538
+ title: z ? `Invoice ${z.invoiceNumber}` : "Invoice",
539
+ description: z?.clientName,
540
+ size: "xl",
541
+ footer: z && /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
542
+ onClick: () => B(null),
543
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
544
+ children: "Close"
545
+ }), /* @__PURE__ */ v("button", {
546
+ onClick: () => void Q(z),
547
+ disabled: W === z.id,
548
+ className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
549
+ children: [/* @__PURE__ */ _(f, { className: "h-4 w-4" }), W === z.id ? "Downloading..." : "Download PDF"]
550
+ })] }),
551
+ children: ke ? /* @__PURE__ */ _(i, {
552
+ variant: "detail",
553
+ count: 1
554
+ }) : V ? /* @__PURE__ */ _("iframe", {
555
+ title: "Invoice preview",
556
+ srcDoc: V,
557
+ sandbox: "",
558
+ className: "h-[60vh] w-full rounded-md border border-border-subtle bg-white"
559
+ }) : /* @__PURE__ */ _("p", {
560
+ className: "text-sm text-text-secondary",
561
+ children: "Failed to load invoice preview."
562
+ })
563
+ }),
564
+ /* @__PURE__ */ _(l, {
565
+ open: K !== null,
566
+ onClose: () => q(null),
567
+ title: "Delete draft invoice?",
568
+ description: K ? `${K.invoiceNumber} · ${t(K.total, K.currency)} will be removed and its time entries released back to the un-invoiced pool.` : void 0,
569
+ size: "sm",
570
+ footer: /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _("button", {
571
+ onClick: () => q(null),
572
+ className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
573
+ children: "Cancel"
574
+ }), /* @__PURE__ */ _("button", {
575
+ onClick: Pe,
576
+ disabled: j,
577
+ className: "rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50",
578
+ children: j ? "Deleting..." : "Delete"
579
+ })] }),
580
+ children: /* @__PURE__ */ _("p", {
581
+ className: "text-sm text-text-secondary",
582
+ children: "This permanently removes the draft. This action cannot be undone."
583
+ })
584
+ }),
585
+ /* @__PURE__ */ _(ie, {
586
+ toasts: w,
587
+ onDismiss: ge
588
+ })
589
+ ]
590
+ });
591
+ }
592
+ //#endregion
593
+ export { w as InvoicingPage };
594
+
595
+ //# sourceMappingURL=InvoicingPage.js.map