@agentpaid/paid-nextjs-client 0.3.0-test.2 → 0.3.0
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/README.md +246 -72
- package/dist/components/PaidActivityLog.d.ts +11 -24
- package/dist/components/PaidActivityLog.d.ts.map +1 -1
- package/dist/components/PaidActivityLog.js +53 -26
- package/dist/components/PaidActivityLog.js.map +1 -1
- package/dist/components/PaidContainer.d.ts +11 -24
- package/dist/components/PaidContainer.d.ts.map +1 -1
- package/dist/components/PaidContainer.js +18 -9
- package/dist/components/PaidContainer.js.map +1 -1
- package/dist/components/PaidInvoiceTable.d.ts +11 -24
- package/dist/components/PaidInvoiceTable.d.ts.map +1 -1
- package/dist/components/PaidInvoiceTable.js +57 -30
- package/dist/components/PaidInvoiceTable.js.map +1 -1
- package/dist/components/PaidPaymentsTable.d.ts +11 -24
- package/dist/components/PaidPaymentsTable.d.ts.map +1 -1
- package/dist/components/PaidPaymentsTable.js +54 -28
- package/dist/components/PaidPaymentsTable.js.map +1 -1
- package/dist/components/components/PaidActivityLog.js +98 -57
- package/dist/components/components/PaidContainer.js +42 -32
- package/dist/components/components/PaidInvoiceTable.js +103 -86
- package/dist/components/components/PaidPaymentsTable.js +102 -69
- package/dist/components/components/ui/Pagination.js +168 -0
- package/dist/components/ui/Pagination.d.ts +10 -0
- package/dist/components/ui/Pagination.d.ts.map +1 -0
- package/dist/components/ui/Pagination.js +111 -0
- package/dist/components/ui/Pagination.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/styles/activity-log-table.css +94 -78
- package/dist/styles/paid-container.css +25 -16
- package/dist/styles/paid-invoice-table.css +135 -120
- package/dist/styles/paid-payments-table.css +65 -109
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/cache.js +1 -1
- package/dist/utils/cache.js.map +1 -1
- package/package.json +1 -1
@@ -3,34 +3,34 @@
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
4
4
|
import { useIsInContainer } from './PaidContainer';
|
5
5
|
import { cachedFetch, getCacheKey, CACHE_TTL } from '../utils/cache';
|
6
|
+
import { Pagination } from './ui/Pagination';
|
6
7
|
import '../styles/activity-log-table.css';
|
7
8
|
|
8
9
|
interface PaidStyleProperties {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
paidThBg?: string;
|
32
|
-
paidRowHoverBg?: string;
|
10
|
+
// Global - Font
|
11
|
+
fontFamily?: string;
|
12
|
+
|
13
|
+
// Global - Font Colors
|
14
|
+
primaryColor?: string;
|
15
|
+
secondaryColor?: string;
|
16
|
+
|
17
|
+
// Background Colors
|
18
|
+
containerBackgroundColor?: string;
|
19
|
+
tableBackgroundColor?: string;
|
20
|
+
tableHeaderBackgroundColor?: string;
|
21
|
+
|
22
|
+
// Tab Colors
|
23
|
+
tabBackgroundColor?: string;
|
24
|
+
tabActiveBackgroundColor?: string;
|
25
|
+
tabHoverBackgroundColor?: string;
|
26
|
+
|
27
|
+
// Table Hover
|
28
|
+
tableHoverColor?: string;
|
29
|
+
|
30
|
+
// Button Background (Status badges & Pagination)
|
31
|
+
buttonBgColor?: string;
|
33
32
|
}
|
33
|
+
|
34
34
|
interface UsageSummary {
|
35
35
|
id: string;
|
36
36
|
updatedAt: string;
|
@@ -47,6 +47,7 @@ interface UsageSummary {
|
|
47
47
|
orderLineAttributeId: string;
|
48
48
|
invoiceId: string | null;
|
49
49
|
invoiceLineId: string | null;
|
50
|
+
currency?: string;
|
50
51
|
}
|
51
52
|
|
52
53
|
interface UsageApiResponse {
|
@@ -67,29 +68,71 @@ export const PaidActivityLog: React.FC<PaidActivityLogProps> = ({
|
|
67
68
|
const [usageSummaries, setUsageSummaries] = useState<UsageSummary[]>([]);
|
68
69
|
const [loading, setLoading] = useState(true);
|
69
70
|
const [error, setError] = useState<string | null>(null);
|
71
|
+
const [currentPage, setCurrentPage] = useState(1);
|
72
|
+
const itemsPerPage = 8;
|
70
73
|
const isInContainer = useIsInContainer();
|
71
74
|
|
75
|
+
// Calculate pagination
|
76
|
+
const totalPages = Math.ceil(usageSummaries.length / itemsPerPage);
|
77
|
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
78
|
+
const endIndex = startIndex + itemsPerPage;
|
79
|
+
const currentUsageSummaries = usageSummaries.slice(startIndex, endIndex);
|
80
|
+
|
81
|
+
const handlePageChange = (page: number) => {
|
82
|
+
setCurrentPage(page);
|
83
|
+
};
|
84
|
+
|
72
85
|
// Convert paidStyle entries into CSS custom properties
|
73
86
|
const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
const
|
79
|
-
|
87
|
+
// Only set CSS variables if they are explicitly provided
|
88
|
+
// This allows inheritance from parent PaidContainer
|
89
|
+
if (value !== undefined && value !== null && value !== '') {
|
90
|
+
// Map simplified properties to CSS custom properties
|
91
|
+
const propertyMap: Record<string, string> = {
|
92
|
+
fontFamily: '--paid-font-family',
|
93
|
+
primaryColor: '--paid-primary-color',
|
94
|
+
secondaryColor: '--paid-secondary-color',
|
95
|
+
containerBackgroundColor: '--paid-container-background-color',
|
96
|
+
tableBackgroundColor: '--paid-table-background-color',
|
97
|
+
tableHeaderBackgroundColor: '--paid-table-header-background-color',
|
98
|
+
tabBackgroundColor: '--paid-tab-background-color',
|
99
|
+
tabActiveBackgroundColor: '--paid-tab-active-background-color',
|
100
|
+
tabHoverBackgroundColor: '--paid-tab-hover-background-color',
|
101
|
+
tableHoverColor: '--paid-table-hover-color',
|
102
|
+
buttonBgColor: '--paid-button-bg-color'
|
103
|
+
};
|
104
|
+
|
105
|
+
const cssProperty = propertyMap[key];
|
106
|
+
if (cssProperty) {
|
107
|
+
// @ts-ignore allow custom property
|
108
|
+
vars[cssProperty] = value;
|
109
|
+
}
|
80
110
|
}
|
81
|
-
|
82
|
-
vars[varName] = value;
|
111
|
+
|
83
112
|
return vars;
|
84
113
|
}, {} as React.CSSProperties);
|
85
114
|
|
86
|
-
const formatCurrency = (amount: number) => {
|
115
|
+
const formatCurrency = (amount: number, currency?: string) => {
|
116
|
+
const symbol = getCurrencySymbol(currency || 'USD');
|
87
117
|
return new Intl.NumberFormat("en-US", {
|
88
118
|
style: "currency",
|
89
119
|
currency: "USD",
|
90
120
|
minimumFractionDigits: 2,
|
91
121
|
maximumFractionDigits: 2,
|
92
|
-
}).format(amount / 100);
|
122
|
+
}).format(amount / 100).replace('$', symbol);
|
123
|
+
};
|
124
|
+
|
125
|
+
const getCurrencySymbol = (currency: string) => {
|
126
|
+
switch (currency.toUpperCase()) {
|
127
|
+
case 'USD':
|
128
|
+
return '$';
|
129
|
+
case 'EUR':
|
130
|
+
return '€';
|
131
|
+
case 'GBP':
|
132
|
+
return '£';
|
133
|
+
default:
|
134
|
+
return '$'; // Default to USD symbol
|
135
|
+
}
|
93
136
|
};
|
94
137
|
|
95
138
|
const formatEventName = (eventName: string) => {
|
@@ -112,20 +155,13 @@ export const PaidActivityLog: React.FC<PaidActivityLogProps> = ({
|
|
112
155
|
try {
|
113
156
|
setLoading(true);
|
114
157
|
|
115
|
-
//
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
// Direct fetch without caching
|
124
|
-
const response = await fetch(`/api/usage/${accountExternalId}`);
|
125
|
-
if (!response.ok) {
|
126
|
-
throw new Error(`Failed to fetch: ${response.statusText}`);
|
127
|
-
}
|
128
|
-
const data = await response.json();
|
158
|
+
// Use cached fetch for usage data
|
159
|
+
const cacheKey = getCacheKey.usage(accountExternalId);
|
160
|
+
const data = await cachedFetch<UsageApiResponse>(
|
161
|
+
`/api/usage/${accountExternalId}`,
|
162
|
+
cacheKey,
|
163
|
+
CACHE_TTL.DATA
|
164
|
+
);
|
129
165
|
|
130
166
|
const mappedUsageSummaries = (data.data.usageSummary || []).map((summary: any) => ({
|
131
167
|
...summary,
|
@@ -142,8 +178,6 @@ export const PaidActivityLog: React.FC<PaidActivityLogProps> = ({
|
|
142
178
|
fetchUsageData();
|
143
179
|
}, [accountExternalId]);
|
144
180
|
|
145
|
-
const displayedSummaries = usageSummaries;
|
146
|
-
|
147
181
|
if (loading) {
|
148
182
|
return <div>Loading usage data...</div>;
|
149
183
|
}
|
@@ -154,14 +188,14 @@ export const PaidActivityLog: React.FC<PaidActivityLogProps> = ({
|
|
154
188
|
|
155
189
|
return (
|
156
190
|
<div className="paid-activity-log-container" style={{ position: 'relative', minWidth: 0, ...cssVariables }}>
|
157
|
-
<div className="paid-activity-log-table-wrapper" style={{ position: '
|
191
|
+
<div className="paid-activity-log-table-wrapper" style={{ position: 'relative', width: '100%', height: 'auto', left: undefined, top: undefined, boxShadow: undefined, cursor: undefined }}>
|
158
192
|
{!isInContainer && (
|
159
193
|
<div className="paid-activity-log-header">
|
160
|
-
<h3 className="paid-activity-log-title">
|
194
|
+
<h3 className="paid-activity-log-title">Activity Log</h3>
|
161
195
|
</div>
|
162
196
|
)}
|
163
|
-
<div style={{ background: '#fff', overflow: 'auto' }}>
|
164
|
-
<table className="paid-activity-log-table">
|
197
|
+
<div style={{ background: '#fff', overflow: 'auto', width: '100%', boxSizing: 'border-box' }}>
|
198
|
+
<table className="paid-activity-log-table" style={{ width: '100%', maxWidth: '100%', tableLayout: 'fixed' }}>
|
165
199
|
<thead>
|
166
200
|
<tr>
|
167
201
|
<th>Event</th>
|
@@ -172,26 +206,33 @@ export const PaidActivityLog: React.FC<PaidActivityLogProps> = ({
|
|
172
206
|
</tr>
|
173
207
|
</thead>
|
174
208
|
<tbody>
|
175
|
-
{
|
209
|
+
{currentUsageSummaries.length === 0 ? (
|
176
210
|
<tr>
|
177
211
|
<td colSpan={5} className="paid-activity-log-empty">
|
178
212
|
No usage data found
|
179
213
|
</td>
|
180
214
|
</tr>
|
181
215
|
) : (
|
182
|
-
|
216
|
+
currentUsageSummaries.map((summary) => (
|
183
217
|
<tr key={summary.id}>
|
184
218
|
<td style={{ fontWeight: 500 }}>{formatEventName(summary.eventName)}</td>
|
185
219
|
<td>{formatDate(summary.startDate)}</td>
|
186
220
|
<td>{formatDate(summary.endDate)}</td>
|
187
221
|
<td style={{ textAlign: 'center' }}>{summary.eventsQuantity}</td>
|
188
|
-
<td style={{ textAlign: 'center', fontWeight: 500 }}>{formatCurrency(summary.subtotal)}</td>
|
222
|
+
<td style={{ textAlign: 'center', fontWeight: 500 }}>{formatCurrency(summary.subtotal, summary.currency)}</td>
|
189
223
|
</tr>
|
190
224
|
))
|
191
225
|
)}
|
192
226
|
</tbody>
|
193
227
|
</table>
|
194
228
|
</div>
|
229
|
+
|
230
|
+
{/* Pagination */}
|
231
|
+
<Pagination
|
232
|
+
currentPage={currentPage}
|
233
|
+
totalPages={totalPages}
|
234
|
+
onPageChange={handlePageChange}
|
235
|
+
/>
|
195
236
|
</div>
|
196
237
|
</div>
|
197
238
|
);
|
@@ -10,30 +10,28 @@ const PaidContainerContext = createContext<boolean>(false);
|
|
10
10
|
export const useIsInContainer = () => useContext(PaidContainerContext);
|
11
11
|
|
12
12
|
interface PaidStyleProperties {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
paidThBg?: string;
|
36
|
-
paidRowHoverBg?: string;
|
13
|
+
// Global - Font
|
14
|
+
fontFamily?: string;
|
15
|
+
|
16
|
+
// Global - Font Colors
|
17
|
+
primaryColor?: string;
|
18
|
+
secondaryColor?: string;
|
19
|
+
|
20
|
+
// Background Colors
|
21
|
+
containerBackgroundColor?: string;
|
22
|
+
tableBackgroundColor?: string;
|
23
|
+
tableHeaderBackgroundColor?: string;
|
24
|
+
|
25
|
+
// Tab Colors
|
26
|
+
tabBackgroundColor?: string;
|
27
|
+
tabActiveBackgroundColor?: string;
|
28
|
+
tabHoverBackgroundColor?: string;
|
29
|
+
|
30
|
+
// Table Hover
|
31
|
+
tableHoverColor?: string;
|
32
|
+
|
33
|
+
// Button Background (Status badges & Pagination)
|
34
|
+
buttonBgColor?: string;
|
37
35
|
}
|
38
36
|
|
39
37
|
interface PaidContainerTab {
|
@@ -63,15 +61,27 @@ export const PaidContainer: React.FC<PaidContainerProps> = ({
|
|
63
61
|
|
64
62
|
// Convert paidStyle entries into CSS custom properties
|
65
63
|
const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
64
|
+
// Map simplified properties to CSS custom properties
|
65
|
+
const propertyMap: Record<string, string> = {
|
66
|
+
fontFamily: '--paid-font-family',
|
67
|
+
primaryColor: '--paid-primary-color',
|
68
|
+
secondaryColor: '--paid-secondary-color',
|
69
|
+
containerBackgroundColor: '--paid-container-background-color',
|
70
|
+
tableBackgroundColor: '--paid-table-background-color',
|
71
|
+
tableHeaderBackgroundColor: '--paid-table-header-background-color',
|
72
|
+
tabBackgroundColor: '--paid-tab-background-color',
|
73
|
+
tabActiveBackgroundColor: '--paid-tab-active-background-color',
|
74
|
+
tabHoverBackgroundColor: '--paid-tab-hover-background-color',
|
75
|
+
tableHoverColor: '--paid-table-hover-color',
|
76
|
+
buttonBgColor: '--paid-button-bg-color'
|
77
|
+
};
|
78
|
+
|
79
|
+
const cssProperty = propertyMap[key];
|
80
|
+
if (cssProperty) {
|
81
|
+
// @ts-ignore allow custom property
|
82
|
+
vars[cssProperty] = value;
|
72
83
|
}
|
73
|
-
|
74
|
-
vars[varName] = value;
|
84
|
+
|
75
85
|
return vars;
|
76
86
|
}, {} as React.CSSProperties);
|
77
87
|
|
@@ -3,33 +3,32 @@
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
4
4
|
import { useIsInContainer } from './PaidContainer';
|
5
5
|
import { cachedFetch, getCacheKey, CACHE_TTL, dataCache } from '../utils/cache';
|
6
|
+
import { Pagination } from './ui/Pagination';
|
6
7
|
import '../styles/paid-invoice-table.css';
|
7
8
|
|
8
9
|
interface PaidStyleProperties {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
paidThBg?: string;
|
32
|
-
paidRowHoverBg?: string;
|
10
|
+
// Global - Font
|
11
|
+
fontFamily?: string;
|
12
|
+
|
13
|
+
// Global - Font Colors
|
14
|
+
primaryColor?: string;
|
15
|
+
secondaryColor?: string;
|
16
|
+
|
17
|
+
// Background Colors
|
18
|
+
containerBackgroundColor?: string;
|
19
|
+
tableBackgroundColor?: string;
|
20
|
+
tableHeaderBackgroundColor?: string;
|
21
|
+
|
22
|
+
// Tab Colors
|
23
|
+
tabBackgroundColor?: string;
|
24
|
+
tabActiveBackgroundColor?: string;
|
25
|
+
tabHoverBackgroundColor?: string;
|
26
|
+
|
27
|
+
// Table Hover
|
28
|
+
tableHoverColor?: string;
|
29
|
+
|
30
|
+
// Button Background (Status badges & Pagination)
|
31
|
+
buttonBgColor?: string;
|
33
32
|
}
|
34
33
|
|
35
34
|
interface Invoice {
|
@@ -37,7 +36,7 @@ interface Invoice {
|
|
37
36
|
number: string;
|
38
37
|
issueDate: string;
|
39
38
|
dueDate: string;
|
40
|
-
|
39
|
+
paymentStatus: string;
|
41
40
|
invoiceTotal: number;
|
42
41
|
currency: string;
|
43
42
|
customer?: {
|
@@ -66,30 +65,68 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
66
65
|
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
67
66
|
const [pdfResponse, setPdfResponse] = useState<string | null>(null);
|
68
67
|
const [selectedInvoice, setSelectedInvoice] = useState<Invoice | null>(null);
|
69
|
-
const [
|
68
|
+
const [loadingInvoiceId, setLoadingInvoiceId] = useState<string | null>(null);
|
69
|
+
const [currentPage, setCurrentPage] = useState(1);
|
70
|
+
const itemsPerPage = 8;
|
70
71
|
const isInContainer = useIsInContainer();
|
71
72
|
|
73
|
+
// Calculate pagination
|
74
|
+
const totalPages = Math.ceil(invoices.length / itemsPerPage);
|
75
|
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
76
|
+
const endIndex = startIndex + itemsPerPage;
|
77
|
+
const currentInvoices = invoices.slice(startIndex, endIndex);
|
78
|
+
|
72
79
|
// Convert paidStyle entries into CSS custom properties
|
73
80
|
const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
const
|
79
|
-
|
81
|
+
// Only set CSS variables if they are explicitly provided
|
82
|
+
// This allows inheritance from parent PaidContainer
|
83
|
+
if (value !== undefined && value !== null && value !== '') {
|
84
|
+
// Map simplified properties to CSS custom properties
|
85
|
+
const propertyMap: Record<string, string> = {
|
86
|
+
fontFamily: '--paid-font-family',
|
87
|
+
primaryColor: '--paid-primary-color',
|
88
|
+
secondaryColor: '--paid-secondary-color',
|
89
|
+
containerBackgroundColor: '--paid-container-background-color',
|
90
|
+
tableBackgroundColor: '--paid-table-background-color',
|
91
|
+
tableHeaderBackgroundColor: '--paid-table-header-background-color',
|
92
|
+
tabBackgroundColor: '--paid-tab-background-color',
|
93
|
+
tabActiveBackgroundColor: '--paid-tab-active-background-color',
|
94
|
+
tabHoverBackgroundColor: '--paid-tab-hover-background-color',
|
95
|
+
tableHoverColor: '--paid-table-hover-color',
|
96
|
+
buttonBgColor: '--paid-button-bg-color'
|
97
|
+
};
|
98
|
+
|
99
|
+
const cssProperty = propertyMap[key];
|
100
|
+
if (cssProperty) {
|
101
|
+
// @ts-ignore allow custom property
|
102
|
+
vars[cssProperty] = value;
|
103
|
+
}
|
80
104
|
}
|
81
|
-
|
82
|
-
vars[varName] = value;
|
105
|
+
|
83
106
|
return vars;
|
84
107
|
}, {} as React.CSSProperties);
|
85
108
|
|
86
|
-
const formatCurrency = (amount: number) => {
|
109
|
+
const formatCurrency = (amount: number, currency: string) => {
|
110
|
+
const symbol = getCurrencySymbol(currency);
|
87
111
|
return new Intl.NumberFormat("en-US", {
|
88
112
|
style: "currency",
|
89
113
|
currency: "USD",
|
90
114
|
minimumFractionDigits: 2,
|
91
115
|
maximumFractionDigits: 2,
|
92
|
-
}).format(amount / 100);
|
116
|
+
}).format(amount / 100).replace('$', symbol);
|
117
|
+
};
|
118
|
+
|
119
|
+
const getCurrencySymbol = (currency: string) => {
|
120
|
+
switch (currency.toUpperCase()) {
|
121
|
+
case 'USD':
|
122
|
+
return '$';
|
123
|
+
case 'EUR':
|
124
|
+
return '€';
|
125
|
+
case 'GBP':
|
126
|
+
return '£';
|
127
|
+
default:
|
128
|
+
return '$'; // Default to USD symbol
|
129
|
+
}
|
93
130
|
};
|
94
131
|
|
95
132
|
const formatDate = (dateString: string) => {
|
@@ -108,7 +145,7 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
108
145
|
|
109
146
|
const handlePreview = async (invoice: Invoice) => {
|
110
147
|
try {
|
111
|
-
|
148
|
+
setLoadingInvoiceId(invoice.id);
|
112
149
|
setSelectedInvoice(invoice);
|
113
150
|
|
114
151
|
// Check cache first for PDF
|
@@ -118,7 +155,7 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
118
155
|
if (cachedPdf) {
|
119
156
|
setPdfResponse(cachedPdf);
|
120
157
|
setIsPreviewOpen(true);
|
121
|
-
|
158
|
+
setLoadingInvoiceId(null);
|
122
159
|
return;
|
123
160
|
}
|
124
161
|
|
@@ -141,7 +178,7 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
141
178
|
console.error('Error fetching PDF:', error);
|
142
179
|
alert('Failed to load PDF preview');
|
143
180
|
} finally {
|
144
|
-
|
181
|
+
setLoadingInvoiceId(null);
|
145
182
|
}
|
146
183
|
};
|
147
184
|
|
@@ -166,25 +203,22 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
166
203
|
URL.revokeObjectURL(url);
|
167
204
|
};
|
168
205
|
|
206
|
+
const handlePageChange = (page: number) => {
|
207
|
+
setCurrentPage(page);
|
208
|
+
};
|
209
|
+
|
169
210
|
useEffect(() => {
|
170
211
|
const fetchInvoiceData = async () => {
|
171
212
|
try {
|
172
213
|
setLoading(true);
|
173
214
|
|
174
|
-
//
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
// Direct fetch without caching
|
183
|
-
const response = await fetch(`/api/invoices/${accountExternalId}`);
|
184
|
-
if (!response.ok) {
|
185
|
-
throw new Error(`Failed to fetch: ${response.statusText}`);
|
186
|
-
}
|
187
|
-
const data = await response.json();
|
215
|
+
// Use cached fetch for invoice data
|
216
|
+
const cacheKey = getCacheKey.invoices(accountExternalId);
|
217
|
+
const data = await cachedFetch<InvoiceApiResponse>(
|
218
|
+
`/api/invoices/${accountExternalId}`,
|
219
|
+
cacheKey,
|
220
|
+
CACHE_TTL.DATA
|
221
|
+
);
|
188
222
|
|
189
223
|
setInvoices(data.data || []);
|
190
224
|
} catch (err) {
|
@@ -214,8 +248,8 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
214
248
|
<h3 className="paid-invoice-title">Invoices</h3>
|
215
249
|
</div>
|
216
250
|
)}
|
217
|
-
<div style={{ background: '#fff', overflow: 'auto' }}>
|
218
|
-
<table className="paid-invoice-table">
|
251
|
+
<div style={{ background: '#fff', overflow: 'auto', width: '100%', boxSizing: 'border-box' }}>
|
252
|
+
<table className="paid-invoice-table" style={{ width: '100%', maxWidth: '100%', tableLayout: 'fixed' }}>
|
219
253
|
<thead>
|
220
254
|
<tr>
|
221
255
|
<th>Invoice Number</th>
|
@@ -223,32 +257,32 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
223
257
|
<th>Invoice Date</th>
|
224
258
|
<th>Due Date</th>
|
225
259
|
<th style={{ textAlign: 'right' }}>Total amount</th>
|
226
|
-
<th style={{ textAlign: 'center' }}>
|
260
|
+
<th style={{ textAlign: 'center' }}>Preview</th>
|
227
261
|
</tr>
|
228
262
|
</thead>
|
229
263
|
<tbody>
|
230
|
-
{
|
264
|
+
{currentInvoices.length === 0 ? (
|
231
265
|
<tr>
|
232
266
|
<td colSpan={6} className="paid-invoice-empty">
|
233
267
|
No invoices found
|
234
268
|
</td>
|
235
269
|
</tr>
|
236
270
|
) : (
|
237
|
-
|
271
|
+
currentInvoices.map((invoice) => (
|
238
272
|
<tr key={invoice.id}>
|
239
273
|
<td style={{ fontWeight: 500 }}>INV-{invoice.number}</td>
|
240
|
-
<td>{getStatusBadge(invoice.
|
274
|
+
<td>{getStatusBadge(invoice.paymentStatus)}</td>
|
241
275
|
<td>{formatDate(invoice.issueDate)}</td>
|
242
276
|
<td>{formatDate(invoice.dueDate)}</td>
|
243
|
-
<td style={{ textAlign: 'right', fontWeight: 500 }}>{formatCurrency(invoice.invoiceTotal)}</td>
|
277
|
+
<td style={{ textAlign: 'right', fontWeight: 500 }}>{formatCurrency(invoice.invoiceTotal, invoice.currency)}</td>
|
244
278
|
<td style={{ textAlign: 'center' }}>
|
245
279
|
<button
|
246
280
|
className="paid-invoice-action-btn"
|
247
281
|
onClick={() => handlePreview(invoice)}
|
248
|
-
disabled={
|
282
|
+
disabled={loadingInvoiceId === invoice.id}
|
249
283
|
title="Preview Invoice"
|
250
284
|
>
|
251
|
-
{
|
285
|
+
{loadingInvoiceId === invoice.id ? (
|
252
286
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
253
287
|
<circle cx="8" cy="8" r="6" stroke="currentColor" strokeWidth="2" fill="none" strokeDasharray="37.7" strokeDashoffset="37.7">
|
254
288
|
<animateTransform attributeName="transform" type="rotate" values="0 8 8;360 8 8" dur="1s" repeatCount="indefinite"/>
|
@@ -269,6 +303,13 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
269
303
|
</tbody>
|
270
304
|
</table>
|
271
305
|
</div>
|
306
|
+
|
307
|
+
{/* Pagination */}
|
308
|
+
<Pagination
|
309
|
+
currentPage={currentPage}
|
310
|
+
totalPages={totalPages}
|
311
|
+
onPageChange={handlePageChange}
|
312
|
+
/>
|
272
313
|
</div>
|
273
314
|
</div>
|
274
315
|
|
@@ -276,30 +317,6 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
276
317
|
{isPreviewOpen && (
|
277
318
|
<div className="paid-invoice-modal-overlay" onClick={() => setIsPreviewOpen(false)}>
|
278
319
|
<div className="paid-invoice-modal-content" onClick={(e) => e.stopPropagation()}>
|
279
|
-
<div className="paid-invoice-modal-header">
|
280
|
-
<h3>Invoice Preview - INV-{selectedInvoice?.number}</h3>
|
281
|
-
<div className="paid-invoice-modal-actions">
|
282
|
-
<button
|
283
|
-
className="paid-invoice-modal-btn paid-invoice-modal-btn-download"
|
284
|
-
onClick={handleDownload}
|
285
|
-
title="Download PDF"
|
286
|
-
>
|
287
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
288
|
-
<path d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>
|
289
|
-
</svg>
|
290
|
-
</button>
|
291
|
-
<button
|
292
|
-
className="paid-invoice-modal-btn paid-invoice-modal-btn-close"
|
293
|
-
onClick={() => setIsPreviewOpen(false)}
|
294
|
-
title="Close"
|
295
|
-
>
|
296
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
297
|
-
<line x1="18" y1="6" x2="6" y2="18"></line>
|
298
|
-
<line x1="6" y1="6" x2="18" y2="18"></line>
|
299
|
-
</svg>
|
300
|
-
</button>
|
301
|
-
</div>
|
302
|
-
</div>
|
303
320
|
<div className="paid-invoice-modal-body">
|
304
321
|
{pdfResponse ? (
|
305
322
|
<iframe
|