@burdenoff/microfe-billing 2026.909.1 → 2026.909.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/modules/billing/hooks/useInvoices.js +56 -40
- package/dist/billing/modules/billing/hooks/useInvoices.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +17 -4
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +8 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +236 -204
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/generated/global-operations.js +329 -312
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,93 +1,109 @@
|
|
|
1
|
-
import { GetInvoicesDocument as e, GetPaginatedInvoicesDocument as t,
|
|
2
|
-
import { useCallback as
|
|
3
|
-
import { useApolloClient as
|
|
1
|
+
import { GetInvoicesDocument as e, GetPaginatedInvoicesDocument as t, GetTransactionsByBillingAccountDocument as n, useGenerateInvoiceMutation as r, useGetInvoiceQuery as i, useGetPaginatedInvoicesQuery as a, usePayInvoiceMutation as o, useRetryInvoiceGenerationMutation as s, useVoidInvoiceMutation as c } from "../../../../generated/global-operations.js";
|
|
2
|
+
import { useCallback as l } from "react";
|
|
3
|
+
import { useApolloClient as u } from "@apollo/client/react";
|
|
4
4
|
//#region src/billing/modules/billing/hooks/useInvoices.ts
|
|
5
|
-
function
|
|
6
|
-
let { status: t, billingAccountId: n, page: r = 1, pageSize:
|
|
5
|
+
function d(e = {}) {
|
|
6
|
+
let { status: t, billingAccountId: n, page: r = 1, pageSize: i = 20, search: o } = e, { data: s, loading: c, error: u, refetch: d } = a({
|
|
7
7
|
variables: {
|
|
8
8
|
status: t,
|
|
9
9
|
billingAccountId: n,
|
|
10
10
|
page: r,
|
|
11
|
-
pageSize:
|
|
11
|
+
pageSize: i,
|
|
12
12
|
search: o || void 0
|
|
13
13
|
},
|
|
14
14
|
fetchPolicy: "cache-and-network"
|
|
15
|
-
}), f =
|
|
15
|
+
}), f = l(async () => {
|
|
16
16
|
await d();
|
|
17
17
|
}, [d]);
|
|
18
18
|
return {
|
|
19
|
-
invoices:
|
|
20
|
-
totalCount:
|
|
21
|
-
hasMore:
|
|
22
|
-
page:
|
|
23
|
-
pageSize:
|
|
24
|
-
totalPages:
|
|
25
|
-
isLoading:
|
|
19
|
+
invoices: s?.getPaginatedInvoices?.invoices || [],
|
|
20
|
+
totalCount: s?.getPaginatedInvoices?.totalCount || 0,
|
|
21
|
+
hasMore: s?.getPaginatedInvoices?.hasMore || !1,
|
|
22
|
+
page: s?.getPaginatedInvoices?.page || 1,
|
|
23
|
+
pageSize: s?.getPaginatedInvoices?.pageSize || i,
|
|
24
|
+
totalPages: s?.getPaginatedInvoices?.totalPages || 0,
|
|
25
|
+
isLoading: c,
|
|
26
26
|
error: u,
|
|
27
27
|
refetch: f
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
let { data: n, loading:
|
|
30
|
+
function f(e, t) {
|
|
31
|
+
let { data: n, loading: r, error: a, refetch: o } = i({
|
|
32
32
|
variables: {
|
|
33
33
|
id: e,
|
|
34
34
|
billingAccountId: t
|
|
35
35
|
},
|
|
36
36
|
skip: !e || !t,
|
|
37
37
|
fetchPolicy: "cache-and-network"
|
|
38
|
-
}),
|
|
38
|
+
}), s = l(async () => {
|
|
39
39
|
await o();
|
|
40
40
|
}, [o]);
|
|
41
41
|
return {
|
|
42
42
|
invoice: n?.getInvoice || null,
|
|
43
|
-
isLoading:
|
|
43
|
+
isLoading: r,
|
|
44
44
|
error: a,
|
|
45
|
-
refetch:
|
|
45
|
+
refetch: s
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
let
|
|
48
|
+
function p() {
|
|
49
|
+
let i = u(), [a, { loading: d }] = r(), [f, { loading: p }] = o(), [m, { loading: h }] = c(), [g, { loading: _ }] = s();
|
|
50
50
|
return {
|
|
51
|
-
generateInvoice:
|
|
51
|
+
generateInvoice: l(async (n, r) => {
|
|
52
52
|
try {
|
|
53
|
-
let { data: o } = await
|
|
53
|
+
let { data: o } = await a({ variables: {
|
|
54
54
|
subscriptionId: n,
|
|
55
|
-
billingAccountId:
|
|
55
|
+
billingAccountId: r
|
|
56
56
|
} });
|
|
57
|
-
return await
|
|
57
|
+
return await i.refetchQueries({ include: [e, t] }), o.generateInvoice;
|
|
58
58
|
} catch (e) {
|
|
59
59
|
throw console.error("Failed to generate invoice:", e), e;
|
|
60
60
|
}
|
|
61
|
-
}, [
|
|
62
|
-
payInvoice:
|
|
61
|
+
}, [a, i]),
|
|
62
|
+
payInvoice: l(async (n, r, a) => {
|
|
63
63
|
try {
|
|
64
|
-
let { data: o } = await
|
|
64
|
+
let { data: o } = await f({ variables: {
|
|
65
65
|
invoiceId: n,
|
|
66
|
-
paymentMethodId:
|
|
66
|
+
paymentMethodId: r,
|
|
67
67
|
billingAccountId: a
|
|
68
68
|
} });
|
|
69
|
-
return await
|
|
69
|
+
return await i.refetchQueries({ include: [e, t] }), o.payInvoice;
|
|
70
70
|
} catch (e) {
|
|
71
71
|
throw console.error("Failed to pay invoice:", e), e;
|
|
72
72
|
}
|
|
73
|
-
}, [
|
|
74
|
-
voidInvoice:
|
|
73
|
+
}, [f, i]),
|
|
74
|
+
voidInvoice: l(async (n, r) => {
|
|
75
75
|
try {
|
|
76
|
-
let { data: a } = await
|
|
76
|
+
let { data: a } = await m({ variables: {
|
|
77
77
|
invoiceId: n,
|
|
78
|
-
billingAccountId:
|
|
78
|
+
billingAccountId: r
|
|
79
79
|
} });
|
|
80
|
-
return await
|
|
80
|
+
return await i.refetchQueries({ include: [e, t] }), a.voidInvoice;
|
|
81
81
|
} catch (e) {
|
|
82
82
|
throw console.error("Failed to void invoice:", e), e;
|
|
83
83
|
}
|
|
84
|
-
}, [
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
}, [m, i]),
|
|
85
|
+
retryInvoiceGeneration: l(async (r, a) => {
|
|
86
|
+
try {
|
|
87
|
+
let { data: o } = await g({ variables: {
|
|
88
|
+
transactionId: r,
|
|
89
|
+
billingAccountId: a
|
|
90
|
+
} });
|
|
91
|
+
return await i.refetchQueries({ include: [
|
|
92
|
+
e,
|
|
93
|
+
t,
|
|
94
|
+
n
|
|
95
|
+
] }), o.retryInvoiceGeneration;
|
|
96
|
+
} catch (e) {
|
|
97
|
+
throw console.error("Failed to retry invoice generation:", e), e;
|
|
98
|
+
}
|
|
99
|
+
}, [g, i]),
|
|
100
|
+
isGenerating: d,
|
|
101
|
+
isPaying: p,
|
|
102
|
+
isVoiding: h,
|
|
103
|
+
isRetrying: _
|
|
88
104
|
};
|
|
89
105
|
}
|
|
90
106
|
//#endregion
|
|
91
|
-
export {
|
|
107
|
+
export { f as useInvoice, p as useInvoiceMutations, d as usePaginatedInvoices };
|
|
92
108
|
|
|
93
109
|
//# sourceMappingURL=useInvoices.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInvoices.js","names":[],"sources":["../../../../../src/billing/modules/billing/hooks/useInvoices.ts"],"sourcesContent":["/**\n * Billing Module - useInvoices Hook\n * Provides access to invoice data and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetInvoicesQuery,\n useGetInvoiceQuery,\n useGetInvoiceByNumberQuery,\n useGenerateInvoiceMutation,\n usePayInvoiceMutation,\n useVoidInvoiceMutation,\n GetInvoicesDocument,\n GetPaginatedInvoicesDocument,\n useGetPaginatedInvoicesQuery,\n} from '../../../../generated/global-operations';\nimport type { Invoice, InvoiceStatus } from '../../../shared/types';\n\ninterface UseInvoicesOptions {\n status?: InvoiceStatus;\n billingAccountId?: string;\n}\n\ninterface UseInvoicesResult {\n invoices: Invoice[];\n openInvoices: Invoice[];\n paidInvoices: Invoice[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UsePaginatedInvoicesOptions {\n status?: InvoiceStatus;\n billingAccountId?: string;\n page?: number;\n pageSize?: number;\n search?: string;\n}\n\ninterface UsePaginatedInvoicesResult {\n invoices: Invoice[];\n totalCount: number;\n hasMore: boolean;\n page: number;\n pageSize: number;\n totalPages: number;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseInvoiceResult {\n invoice: Invoice | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseInvoiceMutationsResult {\n generateInvoice: (subscriptionId: string, billingAccountId: string) => Promise<Invoice>;\n payInvoice: (\n invoiceId: string,\n paymentMethodId: string,\n billingAccountId: string\n ) => Promise<Invoice>;\n voidInvoice: (invoiceId: string, billingAccountId: string) => Promise<Invoice>;\n isGenerating: boolean;\n isPaying: boolean;\n isVoiding: boolean;\n}\n\n/**\n * Hook for fetching invoices\n */\nexport function useInvoices(options: UseInvoicesOptions = {}): UseInvoicesResult {\n const { status, billingAccountId } = options;\n\n const { data, loading, error, refetch } = useGetInvoicesQuery({\n variables: { status, billingAccountId },\n fetchPolicy: 'cache-and-network',\n });\n\n const invoices = useMemo<Invoice[]>(() => data?.getInvoices || [], [data?.getInvoices]);\n\n const openInvoices = useMemo(() => {\n return invoices.filter((inv) => inv.status === ('OPEN' as InvoiceStatus));\n }, [invoices]);\n\n const paidInvoices = useMemo(() => {\n return invoices.filter((inv) => inv.status === ('PAID' as InvoiceStatus));\n }, [invoices]);\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoices,\n openInvoices,\n paidInvoices,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching paginated invoices\n */\nexport function usePaginatedInvoices(\n options: UsePaginatedInvoicesOptions = {}\n): UsePaginatedInvoicesResult {\n const { status, billingAccountId, page = 1, pageSize = 20, search } = options;\n\n const { data, loading, error, refetch } = useGetPaginatedInvoicesQuery({\n variables: { status, billingAccountId, page, pageSize, search: search || undefined },\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoices: data?.getPaginatedInvoices?.invoices || [],\n totalCount: data?.getPaginatedInvoices?.totalCount || 0,\n hasMore: data?.getPaginatedInvoices?.hasMore || false,\n page: data?.getPaginatedInvoices?.page || 1,\n pageSize: data?.getPaginatedInvoices?.pageSize || pageSize,\n totalPages: data?.getPaginatedInvoices?.totalPages || 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching a single invoice by ID\n */\nexport function useInvoice(\n id: string | undefined,\n billingAccountId: string | undefined\n): UseInvoiceResult {\n const { data, loading, error, refetch } = useGetInvoiceQuery({\n variables: { id: id!, billingAccountId: billingAccountId! },\n skip: !id || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoice: data?.getInvoice || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching an invoice by number\n */\nexport function useInvoiceByNumber(\n invoiceNumber: string | undefined,\n billingAccountId: string | undefined\n): UseInvoiceResult {\n const { data, loading, error, refetch } = useGetInvoiceByNumberQuery({\n variables: { invoiceNumber: invoiceNumber!, billingAccountId: billingAccountId! },\n skip: !invoiceNumber || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoice: data?.getInvoiceByNumber || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for invoice mutations\n */\nexport function useInvoiceMutations(): UseInvoiceMutationsResult {\n const client = useApolloClient();\n\n const [generateInvoiceMutation, { loading: isGenerating }] = useGenerateInvoiceMutation();\n const [payInvoiceMutation, { loading: isPaying }] = usePayInvoiceMutation();\n const [voidInvoiceMutation, { loading: isVoiding }] = useVoidInvoiceMutation();\n\n const generateInvoice = useCallback(\n async (subscriptionId: string, billingAccountId: string): Promise<Invoice> => {\n try {\n const { data } = await generateInvoiceMutation({\n variables: { subscriptionId, billingAccountId },\n });\n await client.refetchQueries({ include: [GetInvoicesDocument, GetPaginatedInvoicesDocument] });\n return data!.generateInvoice;\n } catch (error) {\n console.error('Failed to generate invoice:', error);\n throw error;\n }\n },\n [generateInvoiceMutation, client]\n );\n\n const payInvoice = useCallback(\n async (\n invoiceId: string,\n paymentMethodId: string,\n billingAccountId: string\n ): Promise<Invoice> => {\n try {\n const { data } = await payInvoiceMutation({\n variables: { invoiceId, paymentMethodId, billingAccountId },\n });\n await client.refetchQueries({ include: [GetInvoicesDocument, GetPaginatedInvoicesDocument] });\n return data!.payInvoice;\n } catch (error) {\n console.error('Failed to pay invoice:', error);\n throw error;\n }\n },\n [payInvoiceMutation, client]\n );\n\n const voidInvoice = useCallback(\n async (invoiceId: string, billingAccountId: string): Promise<Invoice> => {\n try {\n const { data } = await voidInvoiceMutation({\n variables: { invoiceId, billingAccountId },\n });\n await client.refetchQueries({ include: [GetInvoicesDocument, GetPaginatedInvoicesDocument] });\n return data!.voidInvoice;\n } catch (error) {\n console.error('Failed to void invoice:', error);\n throw error;\n }\n },\n [voidInvoiceMutation, client]\n );\n\n return {\n generateInvoice,\n payInvoice,\n voidInvoice,\n isGenerating,\n isPaying,\n isVoiding,\n };\n}\n"],"mappings":";;;;AAgHA,SAAgB,EACd,IAAuC,CAAC,GACZ;CAC5B,IAAM,EAAE,WAAQ,qBAAkB,UAAO,GAAG,cAAW,IAAI,cAAW,GAEhE,EAAE,SAAM,YAAS,UAAO,eAAY,EAA6B;EACrE,WAAW;GAAE;GAAQ;GAAkB;GAAM;GAAU,QAAQ,KAAU,KAAA;EAAU;EACnF,aAAa;CACf,CAAC,GAEK,IAAgB,EAAY,YAAY;EAC5C,MAAM,EAAQ;CAChB,GAAG,CAAC,CAAO,CAAC;CAEZ,OAAO;EACL,UAAU,GAAM,sBAAsB,YAAY,CAAC;EACnD,YAAY,GAAM,sBAAsB,cAAc;EACtD,SAAS,GAAM,sBAAsB,WAAW;EAChD,MAAM,GAAM,sBAAsB,QAAQ;EAC1C,UAAU,GAAM,sBAAsB,YAAY;EAClD,YAAY,GAAM,sBAAsB,cAAc;EACtD,WAAW;EACJ;EACP,SAAS;CACX;AACF;AAKA,SAAgB,EACd,GACA,GACkB;CAClB,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAmB;EAC3D,WAAW;GAAM;GAAuB;EAAkB;EAC1D,MAAM,CAAC,KAAM,CAAC;EACd,aAAa;CACf,CAAC,GAEK,IAAgB,EAAY,YAAY;EAC5C,MAAM,EAAQ;CAChB,GAAG,CAAC,CAAO,CAAC;CAEZ,OAAO;EACL,SAAS,GAAM,cAAc;EAC7B,WAAW;EACJ;EACP,SAAS;CACX;AACF;AA8BA,SAAgB,IAAiD;CAC/D,IAAM,IAAS,EAAgB,GAEzB,CAAC,GAAyB,EAAE,SAAS,OAAkB,EAA2B,GAClF,CAAC,GAAoB,EAAE,SAAS,OAAc,EAAsB,GACpE,CAAC,GAAqB,EAAE,SAAS,OAAe,EAAuB;CAsD7E,OAAO;EACL,iBArDsB,EACtB,OAAO,GAAwB,MAA+C;GAC5E,IAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAwB,EAC7C,WAAW;KAAE;KAAgB;IAAiB,EAChD,CAAC;IAED,OADA,MAAM,EAAO,eAAe,EAAE,SAAS,CAAC,GAAqB,CAA4B,EAAE,CAAC,GACrF,EAAM;GACf,SAAS,GAAO;IAEd,MADA,QAAQ,MAAM,+BAA+B,CAAK,GAC5C;GACR;EACF,GACA,CAAC,GAAyB,CAAM,CAwChC;EACA,YAtCiB,EACjB,OACE,GACA,GACA,MACqB;GACrB,IAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAmB,EACxC,WAAW;KAAE;KAAW;KAAiB;IAAiB,EAC5D,CAAC;IAED,OADA,MAAM,EAAO,eAAe,EAAE,SAAS,CAAC,GAAqB,CAA4B,EAAE,CAAC,GACrF,EAAM;GACf,SAAS,GAAO;IAEd,MADA,QAAQ,MAAM,0BAA0B,CAAK,GACvC;GACR;EACF,GACA,CAAC,GAAoB,CAAM,CAqB3B;EACA,aAnBkB,EAClB,OAAO,GAAmB,MAA+C;GACvE,IAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAoB,EACzC,WAAW;KAAE;KAAW;IAAiB,EAC3C,CAAC;IAED,OADA,MAAM,EAAO,eAAe,EAAE,SAAS,CAAC,GAAqB,CAA4B,EAAE,CAAC,GACrF,EAAM;GACf,SAAS,GAAO;IAEd,MADA,QAAQ,MAAM,2BAA2B,CAAK,GACxC;GACR;EACF,GACA,CAAC,GAAqB,CAAM,CAM5B;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"useInvoices.js","names":[],"sources":["../../../../../src/billing/modules/billing/hooks/useInvoices.ts"],"sourcesContent":["/**\n * Billing Module - useInvoices Hook\n * Provides access to invoice data and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback, useMemo } from 'react';\nimport {\n useGetInvoicesQuery,\n useGetInvoiceQuery,\n useGetInvoiceByNumberQuery,\n useGenerateInvoiceMutation,\n usePayInvoiceMutation,\n useVoidInvoiceMutation,\n useRetryInvoiceGenerationMutation,\n GetInvoicesDocument,\n GetPaginatedInvoicesDocument,\n GetTransactionsByBillingAccountDocument,\n useGetPaginatedInvoicesQuery,\n} from '../../../../generated/global-operations';\nimport type { Invoice, InvoiceStatus } from '../../../shared/types';\n\ninterface UseInvoicesOptions {\n status?: InvoiceStatus;\n billingAccountId?: string;\n}\n\ninterface UseInvoicesResult {\n invoices: Invoice[];\n openInvoices: Invoice[];\n paidInvoices: Invoice[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UsePaginatedInvoicesOptions {\n status?: InvoiceStatus;\n billingAccountId?: string;\n page?: number;\n pageSize?: number;\n search?: string;\n}\n\ninterface UsePaginatedInvoicesResult {\n invoices: Invoice[];\n totalCount: number;\n hasMore: boolean;\n page: number;\n pageSize: number;\n totalPages: number;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseInvoiceResult {\n invoice: Invoice | null;\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseInvoiceMutationsResult {\n generateInvoice: (subscriptionId: string, billingAccountId: string) => Promise<Invoice>;\n payInvoice: (\n invoiceId: string,\n paymentMethodId: string,\n billingAccountId: string\n ) => Promise<Invoice>;\n voidInvoice: (invoiceId: string, billingAccountId: string) => Promise<Invoice>;\n /**\n * Retry invoice generation for a successful transaction that never got an\n * invoice recorded (a transient Zoho Books/Stripe Invoicing failure at\n * payment time). transactionId comes from getTransactionsByBillingAccount\n * / getTransaction — these transactions never appear in the invoice list,\n * which only shows transactions that already have one.\n */\n retryInvoiceGeneration: (transactionId: string, billingAccountId: string) => Promise<Invoice>;\n isGenerating: boolean;\n isPaying: boolean;\n isVoiding: boolean;\n isRetrying: boolean;\n}\n\n/**\n * Hook for fetching invoices\n */\nexport function useInvoices(options: UseInvoicesOptions = {}): UseInvoicesResult {\n const { status, billingAccountId } = options;\n\n const { data, loading, error, refetch } = useGetInvoicesQuery({\n variables: { status, billingAccountId },\n fetchPolicy: 'cache-and-network',\n });\n\n const invoices = useMemo<Invoice[]>(() => data?.getInvoices || [], [data?.getInvoices]);\n\n const openInvoices = useMemo(() => {\n return invoices.filter((inv) => inv.status === ('OPEN' as InvoiceStatus));\n }, [invoices]);\n\n const paidInvoices = useMemo(() => {\n return invoices.filter((inv) => inv.status === ('PAID' as InvoiceStatus));\n }, [invoices]);\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoices,\n openInvoices,\n paidInvoices,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching paginated invoices\n */\nexport function usePaginatedInvoices(\n options: UsePaginatedInvoicesOptions = {}\n): UsePaginatedInvoicesResult {\n const { status, billingAccountId, page = 1, pageSize = 20, search } = options;\n\n const { data, loading, error, refetch } = useGetPaginatedInvoicesQuery({\n variables: { status, billingAccountId, page, pageSize, search: search || undefined },\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoices: data?.getPaginatedInvoices?.invoices || [],\n totalCount: data?.getPaginatedInvoices?.totalCount || 0,\n hasMore: data?.getPaginatedInvoices?.hasMore || false,\n page: data?.getPaginatedInvoices?.page || 1,\n pageSize: data?.getPaginatedInvoices?.pageSize || pageSize,\n totalPages: data?.getPaginatedInvoices?.totalPages || 0,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching a single invoice by ID\n */\nexport function useInvoice(\n id: string | undefined,\n billingAccountId: string | undefined\n): UseInvoiceResult {\n const { data, loading, error, refetch } = useGetInvoiceQuery({\n variables: { id: id!, billingAccountId: billingAccountId! },\n skip: !id || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoice: data?.getInvoice || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching an invoice by number\n */\nexport function useInvoiceByNumber(\n invoiceNumber: string | undefined,\n billingAccountId: string | undefined\n): UseInvoiceResult {\n const { data, loading, error, refetch } = useGetInvoiceByNumberQuery({\n variables: { invoiceNumber: invoiceNumber!, billingAccountId: billingAccountId! },\n skip: !invoiceNumber || !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n invoice: data?.getInvoiceByNumber || null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for invoice mutations\n */\nexport function useInvoiceMutations(): UseInvoiceMutationsResult {\n const client = useApolloClient();\n\n const [generateInvoiceMutation, { loading: isGenerating }] = useGenerateInvoiceMutation();\n const [payInvoiceMutation, { loading: isPaying }] = usePayInvoiceMutation();\n const [voidInvoiceMutation, { loading: isVoiding }] = useVoidInvoiceMutation();\n const [retryInvoiceGenerationMutation, { loading: isRetrying }] =\n useRetryInvoiceGenerationMutation();\n\n const generateInvoice = useCallback(\n async (subscriptionId: string, billingAccountId: string): Promise<Invoice> => {\n try {\n const { data } = await generateInvoiceMutation({\n variables: { subscriptionId, billingAccountId },\n });\n await client.refetchQueries({\n include: [GetInvoicesDocument, GetPaginatedInvoicesDocument],\n });\n return data!.generateInvoice;\n } catch (error) {\n console.error('Failed to generate invoice:', error);\n throw error;\n }\n },\n [generateInvoiceMutation, client]\n );\n\n const payInvoice = useCallback(\n async (\n invoiceId: string,\n paymentMethodId: string,\n billingAccountId: string\n ): Promise<Invoice> => {\n try {\n const { data } = await payInvoiceMutation({\n variables: { invoiceId, paymentMethodId, billingAccountId },\n });\n await client.refetchQueries({\n include: [GetInvoicesDocument, GetPaginatedInvoicesDocument],\n });\n return data!.payInvoice;\n } catch (error) {\n console.error('Failed to pay invoice:', error);\n throw error;\n }\n },\n [payInvoiceMutation, client]\n );\n\n const voidInvoice = useCallback(\n async (invoiceId: string, billingAccountId: string): Promise<Invoice> => {\n try {\n const { data } = await voidInvoiceMutation({\n variables: { invoiceId, billingAccountId },\n });\n await client.refetchQueries({\n include: [GetInvoicesDocument, GetPaginatedInvoicesDocument],\n });\n return data!.voidInvoice;\n } catch (error) {\n console.error('Failed to void invoice:', error);\n throw error;\n }\n },\n [voidInvoiceMutation, client]\n );\n\n const retryInvoiceGeneration = useCallback(\n async (transactionId: string, billingAccountId: string): Promise<Invoice> => {\n try {\n const { data } = await retryInvoiceGenerationMutation({\n variables: { transactionId, billingAccountId },\n });\n // The transaction moves from \"no invoice yet\" to \"has an invoice\" —\n // refetch both the invoice list (it'll now include this one) and the\n // transactions list (its missing-invoice indicator should clear).\n await client.refetchQueries({\n include: [\n GetInvoicesDocument,\n GetPaginatedInvoicesDocument,\n GetTransactionsByBillingAccountDocument,\n ],\n });\n return data!.retryInvoiceGeneration;\n } catch (error) {\n console.error('Failed to retry invoice generation:', error);\n throw error;\n }\n },\n [retryInvoiceGenerationMutation, client]\n );\n\n return {\n generateInvoice,\n payInvoice,\n voidInvoice,\n retryInvoiceGeneration,\n isGenerating,\n isPaying,\n isVoiding,\n isRetrying,\n };\n}\n"],"mappings":"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"}
|
|
@@ -180,7 +180,7 @@ var C = () => {
|
|
|
180
180
|
]
|
|
181
181
|
})
|
|
182
182
|
});
|
|
183
|
-
let G = o(M.status), K = M.status === "OPEN", q = M.status === "DRAFT", J = M.items || [], Y = M.total - M.amountPaid, X = [
|
|
183
|
+
let G = o(M.status), K = M.status === "OPEN", q = M.status === "DRAFT", J = M.items || [], Y = M.total - M.amountPaid, X = M.metadata?.invoicePdfBackupUrl, Z = [
|
|
184
184
|
{
|
|
185
185
|
key: "description",
|
|
186
186
|
header: "Description",
|
|
@@ -217,7 +217,7 @@ var C = () => {
|
|
|
217
217
|
children: n(e.amount, M.currency)
|
|
218
218
|
})
|
|
219
219
|
}
|
|
220
|
-
],
|
|
220
|
+
], Q = [
|
|
221
221
|
...K && O.canPayInvoice ? [{
|
|
222
222
|
id: "pay",
|
|
223
223
|
label: "Pay this invoice",
|
|
@@ -230,6 +230,12 @@ var C = () => {
|
|
|
230
230
|
description: "Save a copy of this invoice for your records.",
|
|
231
231
|
href: M.pdfUrl
|
|
232
232
|
}] : [],
|
|
233
|
+
...X ? [{
|
|
234
|
+
id: "download-backup",
|
|
235
|
+
label: "Download the PDF (backup copy)",
|
|
236
|
+
description: "Our own stored copy — works even if the link above ever breaks.",
|
|
237
|
+
href: X
|
|
238
|
+
}] : [],
|
|
233
239
|
...M.subscription ? [{
|
|
234
240
|
id: "view-subscription",
|
|
235
241
|
label: "View the related subscription",
|
|
@@ -300,6 +306,13 @@ var C = () => {
|
|
|
300
306
|
className: "px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors",
|
|
301
307
|
children: "Download PDF"
|
|
302
308
|
}),
|
|
309
|
+
X && /* @__PURE__ */ g("a", {
|
|
310
|
+
href: X,
|
|
311
|
+
target: "_blank",
|
|
312
|
+
rel: "noopener noreferrer",
|
|
313
|
+
className: "px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors",
|
|
314
|
+
children: "Download PDF (backup)"
|
|
315
|
+
}),
|
|
303
316
|
M.invoiceUrl && /* @__PURE__ */ g("a", {
|
|
304
317
|
href: M.invoiceUrl,
|
|
305
318
|
target: "_blank",
|
|
@@ -360,7 +373,7 @@ var C = () => {
|
|
|
360
373
|
}) : /* @__PURE__ */ g(S, {
|
|
361
374
|
data: J,
|
|
362
375
|
rowKey: (e) => e.id,
|
|
363
|
-
columns:
|
|
376
|
+
columns: Z
|
|
364
377
|
}),
|
|
365
378
|
/* @__PURE__ */ g("div", {
|
|
366
379
|
className: "border-t border-border-subtle p-4",
|
|
@@ -521,7 +534,7 @@ var C = () => {
|
|
|
521
534
|
children: [
|
|
522
535
|
/* @__PURE__ */ g(b, {
|
|
523
536
|
storageKey: "billing-invoice-detail",
|
|
524
|
-
steps:
|
|
537
|
+
steps: Q
|
|
525
538
|
}),
|
|
526
539
|
/* @__PURE__ */ _("div", {
|
|
527
540
|
className: "border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoiceDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/billing/pages/InvoiceDetailPage.tsx"],"sourcesContent":["/**\n * Billing Module - Invoice Detail Page\n * Displays detailed information about a single invoice\n */\n\nimport { type FC, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport { useInvoice, useInvoiceMutations } from '../hooks/useInvoices';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useGetDefaultBillingAccountDashboardQuery } from '../../../../generated/global-operations';\nimport {\n formatCurrency,\n formatDate,\n formatInvoiceStatus,\n formatInvoiceNumber,\n} from '../../../shared/utils/format';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport { getInvoiceStatusColor, getStatusBadgeClasses } from '../../../shared/utils/status';\nimport type { InvoiceStatus } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ResponsiveTable,\n type ResponsiveTableColumn,\n PagePurpose,\n EmphasisPanel,\n NextSteps,\n type NextStep,\n} from '@burdenoff/fe-libs/ui';\n\nexport const InvoiceDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { invoiceId } = useParams<{ invoiceId: string }>();\n const navigateTo = useBillingNavigate();\n const { orgId } = useBilling();\n const permissions = useBillingPermissions();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n const { data: defaultBillingAccountData } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-first',\n });\n const billingAccountId =\n selectedAccountId ?? defaultBillingAccountData?.getDefaultBillingAccount?.id ?? undefined;\n const { invoice, isLoading, error, refetch } = useInvoice(invoiceId, billingAccountId);\n const { payInvoice, voidInvoice, isPaying, isVoiding } = useInvoiceMutations();\n const defaultPaymentMethodId =\n defaultBillingAccountData?.getDefaultBillingAccount?.defaultPaymentMethod;\n\n // Action error state\n const [actionError, setActionError] = useState<string | null>(null);\n\n // Handle pay invoice\n const handlePayInvoice = async () => {\n if (!invoice) return;\n if (!defaultPaymentMethodId) {\n setActionError('Add and set a default payment method before paying this invoice.');\n return;\n }\n\n if (!billingAccountId) {\n setActionError('No billing account context');\n return;\n }\n setActionError(null);\n try {\n await payInvoice(invoice.id, defaultPaymentMethodId, billingAccountId);\n await refetch();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to pay invoice');\n }\n };\n\n // Handle void invoice\n const handleVoidInvoice = async () => {\n if (!invoice) return;\n if (!billingAccountId) {\n setActionError('No billing account context');\n return;\n }\n\n setActionError(null);\n try {\n await voidInvoice(invoice.id, billingAccountId);\n await refetch();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to void invoice');\n }\n };\n\n // Permission check\n if (!permissions.canViewInvoices) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary max-w-sm\">\n {tr('billing.invoices.noViewPermission', \"You don't have permission to view invoices.\")}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"space-y-2\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-32 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-3/4 bg-bg-sunken animate-pulse rounded\" />\n </div>\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-4\">\n <div className=\"h-4 flex-1 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-16 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-16 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-20 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n </div>\n <div className=\"space-y-6\">\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"space-y-1\">\n <div className=\"h-3 w-20 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-28 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !invoice) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.invoiceNotFound', 'Invoice not found')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {error?.message ||\n tr(\n 'billing.invoices.invoiceNotFoundMessage',\n 'The invoice you are looking for does not exist.'\n )}\n </p>\n <div className=\"flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Try Again\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/invoices', billingAccountId))}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Back to Invoices\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n const statusColor = getInvoiceStatusColor(invoice.status);\n const isOpen = invoice.status === ('OPEN' as InvoiceStatus);\n const isDraft = invoice.status === ('DRAFT' as InvoiceStatus);\n const items = invoice.items || [];\n const amountDue = invoice.total - invoice.amountPaid;\n\n const lineItemColumns: ResponsiveTableColumn<(typeof items)[number]>[] = [\n {\n key: 'description',\n header: 'Description',\n priority: 'primary',\n cell: (item) => item.description,\n },\n {\n key: 'quantity',\n header: 'Qty',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (item) => <span className=\"tabular-nums\">{item.quantity}</span>,\n },\n {\n key: 'unitPrice',\n header: 'Unit Price',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (item) => (\n <span className=\"tabular-nums\">{formatCurrency(item.unitPrice, invoice.currency)}</span>\n ),\n },\n {\n key: 'amount',\n header: 'Amount',\n headerClassName: 'text-right',\n cellClassName: 'text-right font-medium',\n cell: (item) => (\n <span className=\"tabular-nums\">{formatCurrency(item.amount, invoice.currency)}</span>\n ),\n },\n ];\n\n // Recommended next actions for this invoice\n const nextSteps: NextStep[] = [\n ...(isOpen && permissions.canPayInvoice\n ? [\n {\n id: 'pay',\n label: 'Pay this invoice',\n description: defaultPaymentMethodId\n ? 'Settle the outstanding amount with your default payment method.'\n : 'Add a default payment method first, then pay.',\n onClick: handlePayInvoice,\n },\n ]\n : []),\n ...(invoice.pdfUrl\n ? [\n {\n id: 'download',\n label: 'Download the PDF',\n description: 'Save a copy of this invoice for your records.',\n href: invoice.pdfUrl,\n },\n ]\n : []),\n ...(invoice.subscription\n ? [\n {\n id: 'view-subscription',\n label: 'View the related subscription',\n description: 'See the plan and quotas this invoice billed for.',\n onClick: () =>\n navigateTo(\n withBillingAccountId(`/subscriptions/${invoice.subscription!.id}`, billingAccountId)\n ),\n },\n ]\n : []),\n {\n id: 'request-refund',\n label: 'Have an issue? Request a refund',\n description: 'Raise a refund request for a charge on this account.',\n onClick: () => navigateTo(withBillingAccountId('/refunds', billingAccountId)),\n },\n ];\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/invoices', billingAccountId))}\n className=\"p-2 -ml-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n Invoice {formatInvoiceNumber(invoice.invoiceNumber)}\n </h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatInvoiceStatus(invoice.status)}\n </span>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n Created {formatDate(invoice.createdAt, 'short')}\n {invoice.dueDate && ` · Due ${formatDate(invoice.dueDate, 'short')}`}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.invoices.detailPurpose',\n 'The full breakdown of a single invoice — its line items, taxes, what has been paid and what is still due. Use it to download the PDF, pay an outstanding balance, jump to the subscription it covers, or raise a refund.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 flex-wrap\">\n {invoice.pdfUrl && (\n <a\n href={invoice.pdfUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Download PDF\n </a>\n )}\n {invoice.invoiceUrl && (\n <a\n href={invoice.invoiceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n View on Portal\n </a>\n )}\n {isOpen && permissions.canPayInvoice && (\n <button\n type=\"button\"\n onClick={handlePayInvoice}\n disabled={isPaying || !defaultPaymentMethodId}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {!defaultPaymentMethodId\n ? 'Default Payment Method Required'\n : isPaying\n ? 'Processing...'\n : 'Pay Invoice'}\n </button>\n )}\n {(isOpen || isDraft) && permissions.canVoidInvoice && (\n <button\n type=\"button\"\n onClick={handleVoidInvoice}\n disabled={isVoiding}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-status-error-text rounded-button hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n {isVoiding\n ? tr('billing.invoices.voiding', 'Voiding...')\n : tr('billing.invoices.voidInvoice', 'Void Invoice')}\n </button>\n )}\n </div>\n </div>\n\n {/* Action Error */}\n {actionError && (\n <div className=\"p-3 bg-status-error-bg-subtle border border-status-error-border rounded-button\">\n <p className=\"text-sm text-status-error-text\">{actionError}</p>\n </div>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Line Items — primary emphasis zone (what was billed & what is due) */}\n <EmphasisPanel className=\"overflow-hidden p-0\">\n <div className=\"p-6 border-b border-border-subtle\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.lineItems', 'Line Items')} ({items.length})\n </h2>\n </div>\n {items.length === 0 ? (\n <div className=\"p-6\">\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.invoices.noLineItems', 'No line items on this invoice.')}\n </p>\n </div>\n ) : (\n <ResponsiveTable data={items} rowKey={(item) => item.id} columns={lineItemColumns} />\n )}\n\n {/* Totals */}\n <div className=\"border-t border-border-subtle p-4\">\n <div className=\"max-w-xs ml-auto space-y-2\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {tr('billing.invoices.subtotal', 'Subtotal')}\n </span>\n <span className=\"text-text-primary\">\n {formatCurrency(invoice.subtotal, invoice.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">{tr('billing.invoices.tax', 'Tax')}</span>\n <span className=\"text-text-primary\">\n {formatCurrency(invoice.tax, invoice.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm font-semibold border-t border-border-subtle pt-2\">\n <span className=\"text-text-primary\">{tr('billing.invoices.total', 'Total')}</span>\n <span className=\"text-text-primary\">\n {formatCurrency(invoice.total, invoice.currency)}\n </span>\n </div>\n {invoice.amountPaid > 0 && (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {tr('billing.invoices.amountPaid', 'Amount Paid')}\n </span>\n <span className=\"text-status-success-text\">\n -{formatCurrency(invoice.amountPaid, invoice.currency)}\n </span>\n </div>\n )}\n {amountDue > 0 && (\n <div className=\"flex items-center justify-between text-sm font-semibold border-t border-border-subtle pt-2\">\n <span className=\"text-text-primary\">\n {tr('billing.invoices.amountDue', 'Amount Due')}\n </span>\n <span className=\"text-status-warning-text\">\n {formatCurrency(amountDue, invoice.currency)}\n </span>\n </div>\n )}\n </div>\n </div>\n </EmphasisPanel>\n\n {/* Subscription Info */}\n {invoice.subscription && (\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.subscription', 'Subscription')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"font-medium text-text-primary\">\n {invoice.subscription.plan?.name || 'Unknown Plan'}\n </p>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Subscription ID:{' '}\n <span className=\"font-mono text-xs\">{invoice.subscription.id}</span>\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${invoice.subscription!.id}`,\n billingAccountId\n )\n )\n }\n className=\"px-3 py-1.5 text-sm font-medium text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n View\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Payment Info */}\n {invoice.payment && (\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.payment', 'Payment')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Payment ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">\n {invoice.payment.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Amount</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatCurrency(invoice.payment.amount, invoice.payment.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {invoice.payment.status.toLowerCase()}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Method</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {invoice.payment.method.toLowerCase().replace(/_/g, ' ')}\n </p>\n </div>\n {invoice.payment.provider && (\n <div>\n <p className=\"text-sm text-text-secondary\">Provider</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {invoice.payment.provider}\n </p>\n </div>\n )}\n {invoice.payment.paidAt && (\n <div>\n <p className=\"text-sm text-text-secondary\">Paid At</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(invoice.payment.paidAt, 'short')}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Next steps guidance */}\n <NextSteps storageKey=\"billing-invoice-detail\" steps={nextSteps} />\n\n {/* Invoice Summary */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.details', 'Details')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Invoice ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">{invoice.id}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Invoice Number</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatInvoiceNumber(invoice.invoiceNumber)}\n </p>\n </div>\n {invoice.itemDescription && (\n <div>\n <p className=\"text-sm text-text-secondary\">Description</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{invoice.itemDescription}</p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <span\n className={`inline-flex mt-0.5 px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatInvoiceStatus(invoice.status)}\n </span>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Currency</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{invoice.currency}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Total</p>\n <p className=\"text-sm font-semibold text-text-primary mt-0.5\">\n {formatCurrency(invoice.total, invoice.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Amount Paid</p>\n <p className=\"text-sm text-status-success-text mt-0.5\">\n {formatCurrency(invoice.amountPaid, invoice.currency)}\n </p>\n </div>\n {amountDue > 0 && (\n <div>\n <p className=\"text-sm text-text-secondary\">Amount Due</p>\n <p className=\"text-sm font-semibold text-status-warning-text mt-0.5\">\n {formatCurrency(amountDue, invoice.currency)}\n </p>\n </div>\n )}\n </div>\n </div>\n\n {/* Dates */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.dates', 'Dates')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Created</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.createdAt)}</p>\n </div>\n {invoice.dueDate && (\n <div>\n <p className=\"text-sm text-text-secondary\">Due Date</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.dueDate)}</p>\n </div>\n )}\n {invoice.paidAt && (\n <div>\n <p className=\"text-sm text-text-secondary\">Paid At</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.paidAt)}</p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-text-secondary\">Last Updated</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.updatedAt)}</p>\n </div>\n </div>\n </div>\n\n {/* Organization */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.organization', 'Organization')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Organization ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">\n {invoice.orgId}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InvoiceDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/billing/pages/InvoiceDetailPage.tsx"],"sourcesContent":["/**\n * Billing Module - Invoice Detail Page\n * Displays detailed information about a single invoice\n */\n\nimport { type FC, useState } from 'react';\nimport { useParams } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport { useInvoice, useInvoiceMutations } from '../hooks/useInvoices';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useGetDefaultBillingAccountDashboardQuery } from '../../../../generated/global-operations';\nimport {\n formatCurrency,\n formatDate,\n formatInvoiceStatus,\n formatInvoiceNumber,\n} from '../../../shared/utils/format';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport { getInvoiceStatusColor, getStatusBadgeClasses } from '../../../shared/utils/status';\nimport type { InvoiceStatus } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ResponsiveTable,\n type ResponsiveTableColumn,\n PagePurpose,\n EmphasisPanel,\n NextSteps,\n type NextStep,\n} from '@burdenoff/fe-libs/ui';\n\nexport const InvoiceDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { invoiceId } = useParams<{ invoiceId: string }>();\n const navigateTo = useBillingNavigate();\n const { orgId } = useBilling();\n const permissions = useBillingPermissions();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n const { data: defaultBillingAccountData } = useGetDefaultBillingAccountDashboardQuery({\n fetchPolicy: 'cache-first',\n });\n const billingAccountId =\n selectedAccountId ?? defaultBillingAccountData?.getDefaultBillingAccount?.id ?? undefined;\n const { invoice, isLoading, error, refetch } = useInvoice(invoiceId, billingAccountId);\n const { payInvoice, voidInvoice, isPaying, isVoiding } = useInvoiceMutations();\n const defaultPaymentMethodId =\n defaultBillingAccountData?.getDefaultBillingAccount?.defaultPaymentMethod;\n\n // Action error state\n const [actionError, setActionError] = useState<string | null>(null);\n\n // Handle pay invoice\n const handlePayInvoice = async () => {\n if (!invoice) return;\n if (!defaultPaymentMethodId) {\n setActionError('Add and set a default payment method before paying this invoice.');\n return;\n }\n\n if (!billingAccountId) {\n setActionError('No billing account context');\n return;\n }\n setActionError(null);\n try {\n await payInvoice(invoice.id, defaultPaymentMethodId, billingAccountId);\n await refetch();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to pay invoice');\n }\n };\n\n // Handle void invoice\n const handleVoidInvoice = async () => {\n if (!invoice) return;\n if (!billingAccountId) {\n setActionError('No billing account context');\n return;\n }\n\n setActionError(null);\n try {\n await voidInvoice(invoice.id, billingAccountId);\n await refetch();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Failed to void invoice');\n }\n };\n\n // Permission check\n if (!permissions.canViewInvoices) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-secondary\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary max-w-sm\">\n {tr('billing.invoices.noViewPermission', \"You don't have permission to view invoices.\")}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"space-y-2\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-32 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-3/4 bg-bg-sunken animate-pulse rounded\" />\n </div>\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-4\">\n <div className=\"h-4 flex-1 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-16 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-16 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-20 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n </div>\n <div className=\"space-y-6\">\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"space-y-1\">\n <div className=\"h-3 w-20 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-28 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !invoice) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.invoiceNotFound', 'Invoice not found')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {error?.message ||\n tr(\n 'billing.invoices.invoiceNotFoundMessage',\n 'The invoice you are looking for does not exist.'\n )}\n </p>\n <div className=\"flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Try Again\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/invoices', billingAccountId))}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Back to Invoices\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n const statusColor = getInvoiceStatusColor(invoice.status);\n const isOpen = invoice.status === ('OPEN' as InvoiceStatus);\n const isDraft = invoice.status === ('DRAFT' as InvoiceStatus);\n const items = invoice.items || [];\n const amountDue = invoice.total - invoice.amountPaid;\n // Our own durable backup copy — shown alongside the provider's pdfUrl, not\n // as a replacement, so the provider's link still works even if this one\n // was never stored (e.g. no S3 bucket configured for this environment).\n const invoicePdfBackupUrl = (invoice.metadata as Record<string, unknown> | undefined)\n ?.invoicePdfBackupUrl as string | undefined;\n\n const lineItemColumns: ResponsiveTableColumn<(typeof items)[number]>[] = [\n {\n key: 'description',\n header: 'Description',\n priority: 'primary',\n cell: (item) => item.description,\n },\n {\n key: 'quantity',\n header: 'Qty',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (item) => <span className=\"tabular-nums\">{item.quantity}</span>,\n },\n {\n key: 'unitPrice',\n header: 'Unit Price',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (item) => (\n <span className=\"tabular-nums\">{formatCurrency(item.unitPrice, invoice.currency)}</span>\n ),\n },\n {\n key: 'amount',\n header: 'Amount',\n headerClassName: 'text-right',\n cellClassName: 'text-right font-medium',\n cell: (item) => (\n <span className=\"tabular-nums\">{formatCurrency(item.amount, invoice.currency)}</span>\n ),\n },\n ];\n\n // Recommended next actions for this invoice\n const nextSteps: NextStep[] = [\n ...(isOpen && permissions.canPayInvoice\n ? [\n {\n id: 'pay',\n label: 'Pay this invoice',\n description: defaultPaymentMethodId\n ? 'Settle the outstanding amount with your default payment method.'\n : 'Add a default payment method first, then pay.',\n onClick: handlePayInvoice,\n },\n ]\n : []),\n ...(invoice.pdfUrl\n ? [\n {\n id: 'download',\n label: 'Download the PDF',\n description: 'Save a copy of this invoice for your records.',\n href: invoice.pdfUrl,\n },\n ]\n : []),\n ...(invoicePdfBackupUrl\n ? [\n {\n id: 'download-backup',\n label: 'Download the PDF (backup copy)',\n description: 'Our own stored copy — works even if the link above ever breaks.',\n href: invoicePdfBackupUrl,\n },\n ]\n : []),\n ...(invoice.subscription\n ? [\n {\n id: 'view-subscription',\n label: 'View the related subscription',\n description: 'See the plan and quotas this invoice billed for.',\n onClick: () =>\n navigateTo(\n withBillingAccountId(`/subscriptions/${invoice.subscription!.id}`, billingAccountId)\n ),\n },\n ]\n : []),\n {\n id: 'request-refund',\n label: 'Have an issue? Request a refund',\n description: 'Raise a refund request for a charge on this account.',\n onClick: () => navigateTo(withBillingAccountId('/refunds', billingAccountId)),\n },\n ];\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo(withBillingAccountId('/invoices', billingAccountId))}\n className=\"p-2 -ml-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n Invoice {formatInvoiceNumber(invoice.invoiceNumber)}\n </h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatInvoiceStatus(invoice.status)}\n </span>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n Created {formatDate(invoice.createdAt, 'short')}\n {invoice.dueDate && ` · Due ${formatDate(invoice.dueDate, 'short')}`}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.invoices.detailPurpose',\n 'The full breakdown of a single invoice — its line items, taxes, what has been paid and what is still due. Use it to download the PDF, pay an outstanding balance, jump to the subscription it covers, or raise a refund.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 flex-wrap\">\n {invoice.pdfUrl && (\n <a\n href={invoice.pdfUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Download PDF\n </a>\n )}\n {invoicePdfBackupUrl && (\n <a\n href={invoicePdfBackupUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Download PDF (backup)\n </a>\n )}\n {invoice.invoiceUrl && (\n <a\n href={invoice.invoiceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n View on Portal\n </a>\n )}\n {isOpen && permissions.canPayInvoice && (\n <button\n type=\"button\"\n onClick={handlePayInvoice}\n disabled={isPaying || !defaultPaymentMethodId}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {!defaultPaymentMethodId\n ? 'Default Payment Method Required'\n : isPaying\n ? 'Processing...'\n : 'Pay Invoice'}\n </button>\n )}\n {(isOpen || isDraft) && permissions.canVoidInvoice && (\n <button\n type=\"button\"\n onClick={handleVoidInvoice}\n disabled={isVoiding}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-status-error-text rounded-button hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n {isVoiding\n ? tr('billing.invoices.voiding', 'Voiding...')\n : tr('billing.invoices.voidInvoice', 'Void Invoice')}\n </button>\n )}\n </div>\n </div>\n\n {/* Action Error */}\n {actionError && (\n <div className=\"p-3 bg-status-error-bg-subtle border border-status-error-border rounded-button\">\n <p className=\"text-sm text-status-error-text\">{actionError}</p>\n </div>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Line Items — primary emphasis zone (what was billed & what is due) */}\n <EmphasisPanel className=\"overflow-hidden p-0\">\n <div className=\"p-6 border-b border-border-subtle\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.lineItems', 'Line Items')} ({items.length})\n </h2>\n </div>\n {items.length === 0 ? (\n <div className=\"p-6\">\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.invoices.noLineItems', 'No line items on this invoice.')}\n </p>\n </div>\n ) : (\n <ResponsiveTable data={items} rowKey={(item) => item.id} columns={lineItemColumns} />\n )}\n\n {/* Totals */}\n <div className=\"border-t border-border-subtle p-4\">\n <div className=\"max-w-xs ml-auto space-y-2\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {tr('billing.invoices.subtotal', 'Subtotal')}\n </span>\n <span className=\"text-text-primary\">\n {formatCurrency(invoice.subtotal, invoice.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">{tr('billing.invoices.tax', 'Tax')}</span>\n <span className=\"text-text-primary\">\n {formatCurrency(invoice.tax, invoice.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm font-semibold border-t border-border-subtle pt-2\">\n <span className=\"text-text-primary\">{tr('billing.invoices.total', 'Total')}</span>\n <span className=\"text-text-primary\">\n {formatCurrency(invoice.total, invoice.currency)}\n </span>\n </div>\n {invoice.amountPaid > 0 && (\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {tr('billing.invoices.amountPaid', 'Amount Paid')}\n </span>\n <span className=\"text-status-success-text\">\n -{formatCurrency(invoice.amountPaid, invoice.currency)}\n </span>\n </div>\n )}\n {amountDue > 0 && (\n <div className=\"flex items-center justify-between text-sm font-semibold border-t border-border-subtle pt-2\">\n <span className=\"text-text-primary\">\n {tr('billing.invoices.amountDue', 'Amount Due')}\n </span>\n <span className=\"text-status-warning-text\">\n {formatCurrency(amountDue, invoice.currency)}\n </span>\n </div>\n )}\n </div>\n </div>\n </EmphasisPanel>\n\n {/* Subscription Info */}\n {invoice.subscription && (\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.subscription', 'Subscription')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"font-medium text-text-primary\">\n {invoice.subscription.plan?.name || 'Unknown Plan'}\n </p>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n Subscription ID:{' '}\n <span className=\"font-mono text-xs\">{invoice.subscription.id}</span>\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${invoice.subscription!.id}`,\n billingAccountId\n )\n )\n }\n className=\"px-3 py-1.5 text-sm font-medium text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n View\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Payment Info */}\n {invoice.payment && (\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.payment', 'Payment')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Payment ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">\n {invoice.payment.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Amount</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatCurrency(invoice.payment.amount, invoice.payment.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {invoice.payment.status.toLowerCase()}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Method</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {invoice.payment.method.toLowerCase().replace(/_/g, ' ')}\n </p>\n </div>\n {invoice.payment.provider && (\n <div>\n <p className=\"text-sm text-text-secondary\">Provider</p>\n <p className=\"text-sm text-text-primary mt-0.5 capitalize\">\n {invoice.payment.provider}\n </p>\n </div>\n )}\n {invoice.payment.paidAt && (\n <div>\n <p className=\"text-sm text-text-secondary\">Paid At</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatDate(invoice.payment.paidAt, 'short')}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Next steps guidance */}\n <NextSteps storageKey=\"billing-invoice-detail\" steps={nextSteps} />\n\n {/* Invoice Summary */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.details', 'Details')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Invoice ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">{invoice.id}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Invoice Number</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatInvoiceNumber(invoice.invoiceNumber)}\n </p>\n </div>\n {invoice.itemDescription && (\n <div>\n <p className=\"text-sm text-text-secondary\">Description</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{invoice.itemDescription}</p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <span\n className={`inline-flex mt-0.5 px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatInvoiceStatus(invoice.status)}\n </span>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Currency</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{invoice.currency}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Total</p>\n <p className=\"text-sm font-semibold text-text-primary mt-0.5\">\n {formatCurrency(invoice.total, invoice.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Amount Paid</p>\n <p className=\"text-sm text-status-success-text mt-0.5\">\n {formatCurrency(invoice.amountPaid, invoice.currency)}\n </p>\n </div>\n {amountDue > 0 && (\n <div>\n <p className=\"text-sm text-text-secondary\">Amount Due</p>\n <p className=\"text-sm font-semibold text-status-warning-text mt-0.5\">\n {formatCurrency(amountDue, invoice.currency)}\n </p>\n </div>\n )}\n </div>\n </div>\n\n {/* Dates */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.dates', 'Dates')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Created</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.createdAt)}</p>\n </div>\n {invoice.dueDate && (\n <div>\n <p className=\"text-sm text-text-secondary\">Due Date</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.dueDate)}</p>\n </div>\n )}\n {invoice.paidAt && (\n <div>\n <p className=\"text-sm text-text-secondary\">Paid At</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.paidAt)}</p>\n </div>\n )}\n <div>\n <p className=\"text-sm text-text-secondary\">Last Updated</p>\n <p className=\"text-sm text-text-primary mt-0.5\">{formatDate(invoice.updatedAt)}</p>\n </div>\n </div>\n </div>\n\n {/* Organization */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 overflow-hidden\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.invoices.organization', 'Organization')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Organization ID</p>\n <p className=\"font-mono text-sm text-text-primary mt-0.5 break-all\">\n {invoice.orgId}\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -349,7 +349,7 @@ var O = () => {
|
|
|
349
349
|
e === !0 ? await f(s) : e === null && S(!0);
|
|
350
350
|
}, k = async () => {
|
|
351
351
|
await f(s), S(!1);
|
|
352
|
-
};
|
|
352
|
+
}, A = s.metadata?.invoicePdfBackupUrl;
|
|
353
353
|
return /* @__PURE__ */ T("tr", {
|
|
354
354
|
className: "hover:bg-bg-sunken transition-colors",
|
|
355
355
|
children: [
|
|
@@ -425,6 +425,13 @@ var O = () => {
|
|
|
425
425
|
className: "px-3 py-1.5 text-sm font-medium text-text-link hover:bg-[var(--color-accent-softHover)] rounded-button transition-colors duration-200",
|
|
426
426
|
children: y("billing.common.pdf", "PDF")
|
|
427
427
|
}),
|
|
428
|
+
A && /* @__PURE__ */ w("a", {
|
|
429
|
+
href: A,
|
|
430
|
+
target: "_blank",
|
|
431
|
+
rel: "noopener noreferrer",
|
|
432
|
+
className: "px-3 py-1.5 text-sm font-medium text-text-link hover:bg-[var(--color-accent-softHover)] rounded-button transition-colors duration-200",
|
|
433
|
+
children: y("billing.common.pdfBackup", "PDF (backup)")
|
|
434
|
+
}),
|
|
428
435
|
E && p && /* @__PURE__ */ w("button", {
|
|
429
436
|
type: "button",
|
|
430
437
|
onClick: () => void d(s),
|