@agentpaid/paid-nextjs-client 0.3.0-test.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -79
- package/dist/api/api/handleBlocks.ts +144 -0
- package/dist/api/handleBlocks.d.ts +9 -0
- package/dist/api/handleBlocks.d.ts.map +1 -0
- package/dist/api/handleBlocks.js +107 -0
- package/dist/api/handleBlocks.js.map +1 -0
- package/dist/components/PaidActivityLog.d.ts +13 -26
- package/dist/components/PaidActivityLog.d.ts.map +1 -1
- package/dist/components/PaidActivityLog.js +70 -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 +12 -25
- package/dist/components/PaidInvoiceTable.d.ts.map +1 -1
- package/dist/components/PaidInvoiceTable.js +78 -30
- package/dist/components/PaidInvoiceTable.js.map +1 -1
- package/dist/components/PaidPaymentsTable.d.ts +12 -25
- package/dist/components/PaidPaymentsTable.d.ts.map +1 -1
- package/dist/components/PaidPaymentsTable.js +63 -29
- package/dist/components/PaidPaymentsTable.js.map +1 -1
- package/dist/components/components/PaidActivityLog.js +115 -58
- package/dist/components/components/PaidContainer.js +42 -32
- package/dist/components/components/PaidInvoiceTable.js +126 -89
- package/dist/components/components/PaidPaymentsTable.js +111 -72
- 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/hooks/useCache.d.ts +2 -2
- package/dist/hooks/useCache.d.ts.map +1 -1
- package/dist/hooks/useCache.js +10 -17
- package/dist/hooks/useCache.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/styles/paid-activity-log.css +154 -0
- 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/apiClient.d.ts +17 -0
- package/dist/utils/apiClient.d.ts.map +1 -0
- package/dist/utils/apiClient.js +60 -0
- package/dist/utils/apiClient.js.map +1 -0
- package/dist/utils/cache.js +1 -1
- package/dist/utils/cache.js.map +1 -1
- package/package.json +1 -1
- package/dist/api/api/handlePaidInvoicePdf.ts +0 -79
- package/dist/api/api/handlePaidInvoices.ts +0 -77
- package/dist/api/api/handlePaidPayments.ts +0 -77
- package/dist/api/api/handlePaidUsage.ts +0 -52
- package/dist/api/handlePaidInvoicePdf.d.ts +0 -6
- package/dist/api/handlePaidInvoicePdf.d.ts.map +0 -1
- package/dist/api/handlePaidInvoicePdf.js +0 -60
- package/dist/api/handlePaidInvoicePdf.js.map +0 -1
- package/dist/api/handlePaidInvoices.d.ts +0 -6
- package/dist/api/handlePaidInvoices.d.ts.map +0 -1
- package/dist/api/handlePaidInvoices.js +0 -56
- package/dist/api/handlePaidInvoices.js.map +0 -1
- package/dist/api/handlePaidPayments.d.ts +0 -6
- package/dist/api/handlePaidPayments.d.ts.map +0 -1
- package/dist/api/handlePaidPayments.js +0 -56
- package/dist/api/handlePaidPayments.js.map +0 -1
- package/dist/api/handlePaidUsage.d.ts +0 -6
- package/dist/api/handlePaidUsage.d.ts.map +0 -1
- package/dist/api/handlePaidUsage.js +0 -35
- package/dist/api/handlePaidUsage.js.map +0 -1
- package/dist/styles/activity-log-table.css +0 -138
@@ -2,34 +2,34 @@
|
|
2
2
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
4
4
|
import { useIsInContainer } from './PaidContainer';
|
5
|
-
import {
|
5
|
+
import { getCacheKey, CACHE_TTL, dataCache } from '../utils/cache';
|
6
|
+
import { Pagination } from './ui/Pagination';
|
7
|
+
import { fetchPaidData } from '../utils/apiClient';
|
6
8
|
import '../styles/paid-invoice-table.css';
|
7
9
|
|
8
10
|
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;
|
11
|
+
// Global - Font
|
12
|
+
fontFamily?: string;
|
13
|
+
|
14
|
+
// Global - Font Colors
|
15
|
+
primaryColor?: string;
|
16
|
+
secondaryColor?: string;
|
17
|
+
|
18
|
+
// Background Colors
|
19
|
+
containerBackgroundColor?: string;
|
20
|
+
tableBackgroundColor?: string;
|
21
|
+
tableHeaderBackgroundColor?: string;
|
22
|
+
|
23
|
+
// Tab Colors
|
24
|
+
tabBackgroundColor?: string;
|
25
|
+
tabActiveBackgroundColor?: string;
|
26
|
+
tabHoverBackgroundColor?: string;
|
27
|
+
|
28
|
+
// Table Hover
|
29
|
+
tableHoverColor?: string;
|
30
|
+
|
31
|
+
// Button Background (Status badges & Pagination)
|
32
|
+
buttonBgColor?: string;
|
33
33
|
}
|
34
34
|
|
35
35
|
interface Invoice {
|
@@ -37,7 +37,7 @@ interface Invoice {
|
|
37
37
|
number: string;
|
38
38
|
issueDate: string;
|
39
39
|
dueDate: string;
|
40
|
-
|
40
|
+
paymentStatus: string;
|
41
41
|
invoiceTotal: number;
|
42
42
|
currency: string;
|
43
43
|
customer?: {
|
@@ -52,12 +52,12 @@ interface InvoiceApiResponse {
|
|
52
52
|
}
|
53
53
|
|
54
54
|
interface PaidInvoiceTableProps {
|
55
|
-
|
55
|
+
customerExternalId: string;
|
56
56
|
paidStyle?: PaidStyleProperties;
|
57
57
|
}
|
58
58
|
|
59
59
|
export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
60
|
-
|
60
|
+
customerExternalId,
|
61
61
|
paidStyle = {}
|
62
62
|
}) => {
|
63
63
|
const [invoices, setInvoices] = useState<Invoice[]>([]);
|
@@ -66,30 +66,68 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
66
66
|
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
67
67
|
const [pdfResponse, setPdfResponse] = useState<string | null>(null);
|
68
68
|
const [selectedInvoice, setSelectedInvoice] = useState<Invoice | null>(null);
|
69
|
-
const [
|
69
|
+
const [loadingInvoiceId, setLoadingInvoiceId] = useState<string | null>(null);
|
70
|
+
const [currentPage, setCurrentPage] = useState(1);
|
71
|
+
const itemsPerPage = 8;
|
70
72
|
const isInContainer = useIsInContainer();
|
71
73
|
|
74
|
+
// Calculate pagination
|
75
|
+
const totalPages = Math.ceil(invoices.length / itemsPerPage);
|
76
|
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
77
|
+
const endIndex = startIndex + itemsPerPage;
|
78
|
+
const currentInvoices = invoices.slice(startIndex, endIndex);
|
79
|
+
|
72
80
|
// Convert paidStyle entries into CSS custom properties
|
73
81
|
const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
const
|
79
|
-
|
82
|
+
// Only set CSS variables if they are explicitly provided
|
83
|
+
// This allows inheritance from parent PaidContainer
|
84
|
+
if (value !== undefined && value !== null && value !== '') {
|
85
|
+
// Map simplified properties to CSS custom properties
|
86
|
+
const propertyMap: Record<string, string> = {
|
87
|
+
fontFamily: '--paid-font-family',
|
88
|
+
primaryColor: '--paid-primary-color',
|
89
|
+
secondaryColor: '--paid-secondary-color',
|
90
|
+
containerBackgroundColor: '--paid-container-background-color',
|
91
|
+
tableBackgroundColor: '--paid-table-background-color',
|
92
|
+
tableHeaderBackgroundColor: '--paid-table-header-background-color',
|
93
|
+
tabBackgroundColor: '--paid-tab-background-color',
|
94
|
+
tabActiveBackgroundColor: '--paid-tab-active-background-color',
|
95
|
+
tabHoverBackgroundColor: '--paid-tab-hover-background-color',
|
96
|
+
tableHoverColor: '--paid-table-hover-color',
|
97
|
+
buttonBgColor: '--paid-button-bg-color'
|
98
|
+
};
|
99
|
+
|
100
|
+
const cssProperty = propertyMap[key];
|
101
|
+
if (cssProperty) {
|
102
|
+
// @ts-ignore allow custom property
|
103
|
+
vars[cssProperty] = value;
|
104
|
+
}
|
80
105
|
}
|
81
|
-
|
82
|
-
vars[varName] = value;
|
106
|
+
|
83
107
|
return vars;
|
84
108
|
}, {} as React.CSSProperties);
|
85
109
|
|
86
|
-
const formatCurrency = (amount: number) => {
|
110
|
+
const formatCurrency = (amount: number, currency: string) => {
|
111
|
+
const symbol = getCurrencySymbol(currency);
|
87
112
|
return new Intl.NumberFormat("en-US", {
|
88
113
|
style: "currency",
|
89
114
|
currency: "USD",
|
90
115
|
minimumFractionDigits: 2,
|
91
116
|
maximumFractionDigits: 2,
|
92
|
-
}).format(amount / 100);
|
117
|
+
}).format(amount / 100).replace('$', symbol);
|
118
|
+
};
|
119
|
+
|
120
|
+
const getCurrencySymbol = (currency: string) => {
|
121
|
+
switch (currency.toUpperCase()) {
|
122
|
+
case 'USD':
|
123
|
+
return '$';
|
124
|
+
case 'EUR':
|
125
|
+
return '€';
|
126
|
+
case 'GBP':
|
127
|
+
return '£';
|
128
|
+
default:
|
129
|
+
return '$'; // Default to USD symbol
|
130
|
+
}
|
93
131
|
};
|
94
132
|
|
95
133
|
const formatDate = (dateString: string) => {
|
@@ -108,7 +146,7 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
108
146
|
|
109
147
|
const handlePreview = async (invoice: Invoice) => {
|
110
148
|
try {
|
111
|
-
|
149
|
+
setLoadingInvoiceId(invoice.id);
|
112
150
|
setSelectedInvoice(invoice);
|
113
151
|
|
114
152
|
// Check cache first for PDF
|
@@ -118,12 +156,15 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
118
156
|
if (cachedPdf) {
|
119
157
|
setPdfResponse(cachedPdf);
|
120
158
|
setIsPreviewOpen(true);
|
121
|
-
|
159
|
+
setLoadingInvoiceId(null);
|
122
160
|
return;
|
123
161
|
}
|
124
162
|
|
125
|
-
// Fetch PDF if not cached
|
126
|
-
const response = await
|
163
|
+
// Fetch PDF if not cached using new API client
|
164
|
+
const response = await fetchPaidData({
|
165
|
+
paidEndpoint: 'invoice-pdf',
|
166
|
+
invoiceId: invoice.id
|
167
|
+
});
|
127
168
|
|
128
169
|
if (!response.ok) {
|
129
170
|
throw new Error('Failed to fetch PDF');
|
@@ -141,7 +182,7 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
141
182
|
console.error('Error fetching PDF:', error);
|
142
183
|
alert('Failed to load PDF preview');
|
143
184
|
} finally {
|
144
|
-
|
185
|
+
setLoadingInvoiceId(null);
|
145
186
|
}
|
146
187
|
};
|
147
188
|
|
@@ -166,26 +207,26 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
166
207
|
URL.revokeObjectURL(url);
|
167
208
|
};
|
168
209
|
|
210
|
+
const handlePageChange = (page: number) => {
|
211
|
+
setCurrentPage(page);
|
212
|
+
};
|
213
|
+
|
169
214
|
useEffect(() => {
|
170
215
|
const fetchInvoiceData = async () => {
|
171
216
|
try {
|
172
217
|
setLoading(true);
|
173
218
|
|
174
|
-
//
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
// CACHE_TTL.DATA
|
180
|
-
// );
|
219
|
+
// Use new API client for invoice data
|
220
|
+
const response = await fetchPaidData({
|
221
|
+
paidEndpoint: 'invoices',
|
222
|
+
customerExternalId
|
223
|
+
});
|
181
224
|
|
182
|
-
// Direct fetch without caching
|
183
|
-
const response = await fetch(`/api/invoices/${accountExternalId}`);
|
184
225
|
if (!response.ok) {
|
185
|
-
throw new Error(`
|
226
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
186
227
|
}
|
187
|
-
const data = await response.json();
|
188
228
|
|
229
|
+
const data = await response.json() as InvoiceApiResponse;
|
189
230
|
setInvoices(data.data || []);
|
190
231
|
} catch (err) {
|
191
232
|
setError(err instanceof Error ? err.message : 'An error occurred');
|
@@ -195,7 +236,20 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
195
236
|
};
|
196
237
|
|
197
238
|
fetchInvoiceData();
|
198
|
-
|
239
|
+
|
240
|
+
// Listen for cache refresh events
|
241
|
+
const handleCacheRefresh = (event: CustomEvent) => {
|
242
|
+
if (event.detail?.customerId === customerExternalId || event.detail?.type === 'all') {
|
243
|
+
fetchInvoiceData();
|
244
|
+
}
|
245
|
+
};
|
246
|
+
|
247
|
+
window.addEventListener('cache-refresh', handleCacheRefresh as EventListener);
|
248
|
+
|
249
|
+
return () => {
|
250
|
+
window.removeEventListener('cache-refresh', handleCacheRefresh as EventListener);
|
251
|
+
};
|
252
|
+
}, [customerExternalId]);
|
199
253
|
|
200
254
|
if (loading) {
|
201
255
|
return <div>Loading invoice data...</div>;
|
@@ -214,8 +268,8 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
214
268
|
<h3 className="paid-invoice-title">Invoices</h3>
|
215
269
|
</div>
|
216
270
|
)}
|
217
|
-
<div style={{ background: '#fff', overflow: 'auto' }}>
|
218
|
-
<table className="paid-invoice-table">
|
271
|
+
<div style={{ background: '#fff', overflow: 'auto', width: '100%', boxSizing: 'border-box' }}>
|
272
|
+
<table className="paid-invoice-table" style={{ width: '100%', maxWidth: '100%', tableLayout: 'fixed' }}>
|
219
273
|
<thead>
|
220
274
|
<tr>
|
221
275
|
<th>Invoice Number</th>
|
@@ -223,32 +277,32 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
223
277
|
<th>Invoice Date</th>
|
224
278
|
<th>Due Date</th>
|
225
279
|
<th style={{ textAlign: 'right' }}>Total amount</th>
|
226
|
-
<th style={{ textAlign: 'center' }}>
|
280
|
+
<th style={{ textAlign: 'center' }}>Preview</th>
|
227
281
|
</tr>
|
228
282
|
</thead>
|
229
283
|
<tbody>
|
230
|
-
{
|
284
|
+
{currentInvoices.length === 0 ? (
|
231
285
|
<tr>
|
232
286
|
<td colSpan={6} className="paid-invoice-empty">
|
233
287
|
No invoices found
|
234
288
|
</td>
|
235
289
|
</tr>
|
236
290
|
) : (
|
237
|
-
|
291
|
+
currentInvoices.map((invoice) => (
|
238
292
|
<tr key={invoice.id}>
|
239
293
|
<td style={{ fontWeight: 500 }}>INV-{invoice.number}</td>
|
240
|
-
<td>{getStatusBadge(invoice.
|
294
|
+
<td>{getStatusBadge(invoice.paymentStatus)}</td>
|
241
295
|
<td>{formatDate(invoice.issueDate)}</td>
|
242
296
|
<td>{formatDate(invoice.dueDate)}</td>
|
243
|
-
<td style={{ textAlign: 'right', fontWeight: 500 }}>{formatCurrency(invoice.invoiceTotal)}</td>
|
297
|
+
<td style={{ textAlign: 'right', fontWeight: 500 }}>{formatCurrency(invoice.invoiceTotal, invoice.currency)}</td>
|
244
298
|
<td style={{ textAlign: 'center' }}>
|
245
299
|
<button
|
246
300
|
className="paid-invoice-action-btn"
|
247
301
|
onClick={() => handlePreview(invoice)}
|
248
|
-
disabled={
|
302
|
+
disabled={loadingInvoiceId === invoice.id}
|
249
303
|
title="Preview Invoice"
|
250
304
|
>
|
251
|
-
{
|
305
|
+
{loadingInvoiceId === invoice.id ? (
|
252
306
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
253
307
|
<circle cx="8" cy="8" r="6" stroke="currentColor" strokeWidth="2" fill="none" strokeDasharray="37.7" strokeDashoffset="37.7">
|
254
308
|
<animateTransform attributeName="transform" type="rotate" values="0 8 8;360 8 8" dur="1s" repeatCount="indefinite"/>
|
@@ -269,6 +323,13 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
269
323
|
</tbody>
|
270
324
|
</table>
|
271
325
|
</div>
|
326
|
+
|
327
|
+
{/* Pagination */}
|
328
|
+
<Pagination
|
329
|
+
currentPage={currentPage}
|
330
|
+
totalPages={totalPages}
|
331
|
+
onPageChange={handlePageChange}
|
332
|
+
/>
|
272
333
|
</div>
|
273
334
|
</div>
|
274
335
|
|
@@ -276,30 +337,6 @@ export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
|
276
337
|
{isPreviewOpen && (
|
277
338
|
<div className="paid-invoice-modal-overlay" onClick={() => setIsPreviewOpen(false)}>
|
278
339
|
<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
340
|
<div className="paid-invoice-modal-body">
|
304
341
|
{pdfResponse ? (
|
305
342
|
<iframe
|
@@ -2,41 +2,41 @@
|
|
2
2
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
4
4
|
import { useIsInContainer } from './PaidContainer';
|
5
|
-
import {
|
5
|
+
import { getCacheKey } from '../utils/cache';
|
6
|
+
import { Pagination } from './ui/Pagination';
|
6
7
|
import '../styles/paid-payments-table.css';
|
8
|
+
import { fetchPaidData } from '../utils/apiClient';
|
7
9
|
|
8
10
|
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;
|
11
|
+
// Global - Font
|
12
|
+
fontFamily?: string;
|
13
|
+
|
14
|
+
// Global - Font Colors
|
15
|
+
primaryColor?: string;
|
16
|
+
secondaryColor?: string;
|
17
|
+
|
18
|
+
// Background Colors
|
19
|
+
containerBackgroundColor?: string;
|
20
|
+
tableBackgroundColor?: string;
|
21
|
+
tableHeaderBackgroundColor?: string;
|
22
|
+
|
23
|
+
// Tab Colors
|
24
|
+
tabBackgroundColor?: string;
|
25
|
+
tabActiveBackgroundColor?: string;
|
26
|
+
tabHoverBackgroundColor?: string;
|
27
|
+
|
28
|
+
// Table Hover
|
29
|
+
tableHoverColor?: string;
|
30
|
+
|
31
|
+
// Button Background (Status badges & Pagination)
|
32
|
+
buttonBgColor?: string;
|
33
33
|
}
|
34
34
|
|
35
35
|
interface Payment {
|
36
36
|
id: string;
|
37
37
|
paymentType: string;
|
38
38
|
paymentDate: string;
|
39
|
-
|
39
|
+
paymentStatus: string;
|
40
40
|
amount: number;
|
41
41
|
currency: string;
|
42
42
|
}
|
@@ -46,40 +46,80 @@ interface PaymentApiResponse {
|
|
46
46
|
}
|
47
47
|
|
48
48
|
interface PaidPaymentsTableProps {
|
49
|
-
|
49
|
+
customerExternalId: string;
|
50
50
|
paidStyle?: PaidStyleProperties;
|
51
51
|
}
|
52
52
|
|
53
53
|
export const PaidPaymentsTable: React.FC<PaidPaymentsTableProps> = ({
|
54
|
-
|
54
|
+
customerExternalId,
|
55
55
|
paidStyle = {}
|
56
56
|
}) => {
|
57
57
|
const [payments, setPayments] = useState<Payment[]>([]);
|
58
58
|
const [loading, setLoading] = useState(true);
|
59
59
|
const [error, setError] = useState<string | null>(null);
|
60
|
+
const [currentPage, setCurrentPage] = useState(1);
|
61
|
+
const itemsPerPage = 8;
|
60
62
|
const isInContainer = useIsInContainer();
|
61
63
|
|
64
|
+
// Calculate pagination
|
65
|
+
const totalPages = Math.ceil(payments.length / itemsPerPage);
|
66
|
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
67
|
+
const endIndex = startIndex + itemsPerPage;
|
68
|
+
const currentPayments = payments.slice(startIndex, endIndex);
|
69
|
+
|
70
|
+
const handlePageChange = (page: number) => {
|
71
|
+
setCurrentPage(page);
|
72
|
+
};
|
73
|
+
|
62
74
|
// Convert paidStyle entries into CSS custom properties
|
63
75
|
const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
76
|
+
if (value !== undefined && value !== null && value !== '') {
|
77
|
+
// Map simplified properties to CSS custom properties
|
78
|
+
const propertyMap: Record<string, string> = {
|
79
|
+
fontFamily: '--paid-font-family',
|
80
|
+
primaryColor: '--paid-primary-color',
|
81
|
+
secondaryColor: '--paid-secondary-color',
|
82
|
+
containerBackgroundColor: '--paid-container-background-color',
|
83
|
+
tableBackgroundColor: '--paid-table-background-color',
|
84
|
+
tableHeaderBackgroundColor: '--paid-table-header-background-color',
|
85
|
+
tabBackgroundColor: '--paid-tab-background-color',
|
86
|
+
tabActiveBackgroundColor: '--paid-tab-active-background-color',
|
87
|
+
tabHoverBackgroundColor: '--paid-tab-hover-background-color',
|
88
|
+
tableHoverColor: '--paid-table-hover-color',
|
89
|
+
buttonBgColor: '--paid-button-bg-color'
|
90
|
+
};
|
91
|
+
|
92
|
+
const cssProperty = propertyMap[key];
|
93
|
+
if (cssProperty) {
|
94
|
+
// @ts-ignore allow custom property
|
95
|
+
vars[cssProperty] = value;
|
96
|
+
}
|
70
97
|
}
|
71
|
-
|
72
|
-
vars[varName] = value;
|
98
|
+
|
73
99
|
return vars;
|
74
100
|
}, {} as React.CSSProperties);
|
75
101
|
|
76
|
-
const formatCurrency = (amount: number) => {
|
102
|
+
const formatCurrency = (amount: number, currency: string) => {
|
103
|
+
const symbol = getCurrencySymbol(currency);
|
77
104
|
return new Intl.NumberFormat("en-US", {
|
78
105
|
style: "currency",
|
79
106
|
currency: "USD",
|
80
|
-
minimumFractionDigits:
|
81
|
-
maximumFractionDigits:
|
82
|
-
}).format(amount / 100);
|
107
|
+
minimumFractionDigits: 2,
|
108
|
+
maximumFractionDigits: 2,
|
109
|
+
}).format(amount / 100).replace('$', symbol);
|
110
|
+
};
|
111
|
+
|
112
|
+
const getCurrencySymbol = (currency: string) => {
|
113
|
+
switch (currency.toUpperCase()) {
|
114
|
+
case 'USD':
|
115
|
+
return '$';
|
116
|
+
case 'EUR':
|
117
|
+
return '€';
|
118
|
+
case 'GBP':
|
119
|
+
return '£';
|
120
|
+
default:
|
121
|
+
return '$'; // Default to USD symbol
|
122
|
+
}
|
83
123
|
};
|
84
124
|
|
85
125
|
const formatDate = (dateString: string) => {
|
@@ -105,24 +145,22 @@ export const PaidPaymentsTable: React.FC<PaidPaymentsTableProps> = ({
|
|
105
145
|
const fetchPaymentData = async () => {
|
106
146
|
try {
|
107
147
|
setLoading(true);
|
108
|
-
console.log('PaidPaymentsTable: Fetching payment data for',
|
148
|
+
console.log('PaidPaymentsTable: Fetching payment data for', customerExternalId);
|
109
149
|
|
110
|
-
//
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
150
|
+
// Use cached fetch for payment data
|
151
|
+
const cacheKey = getCacheKey.payments(customerExternalId);
|
152
|
+
console.log('PaidPaymentsTable: Using cache key', cacheKey);
|
153
|
+
|
154
|
+
const response = await fetchPaidData({
|
155
|
+
paidEndpoint: 'payments',
|
156
|
+
customerExternalId
|
157
|
+
});
|
118
158
|
|
119
|
-
// Direct fetch without caching
|
120
|
-
const response = await fetch(`/api/payments/${accountExternalId}`);
|
121
159
|
if (!response.ok) {
|
122
|
-
throw new Error(`
|
160
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
123
161
|
}
|
124
|
-
const data = await response.json();
|
125
162
|
|
163
|
+
const data = await response.json() as PaymentApiResponse;
|
126
164
|
console.log('PaidPaymentsTable: Received data', data);
|
127
165
|
setPayments(data.data || []);
|
128
166
|
} catch (err) {
|
@@ -138,7 +176,7 @@ export const PaidPaymentsTable: React.FC<PaidPaymentsTableProps> = ({
|
|
138
176
|
// Listen for cache refresh events
|
139
177
|
const handleCacheRefresh = (event: CustomEvent) => {
|
140
178
|
console.log('PaidPaymentsTable: Cache refresh event received', event.detail);
|
141
|
-
if (event.detail?.
|
179
|
+
if (event.detail?.customerId === customerExternalId || event.detail?.type === 'all') {
|
142
180
|
console.log('PaidPaymentsTable: Refetching data due to cache refresh');
|
143
181
|
fetchPaymentData();
|
144
182
|
}
|
@@ -149,7 +187,7 @@ export const PaidPaymentsTable: React.FC<PaidPaymentsTableProps> = ({
|
|
149
187
|
return () => {
|
150
188
|
window.removeEventListener('cache-refresh', handleCacheRefresh as EventListener);
|
151
189
|
};
|
152
|
-
}, [
|
190
|
+
}, [customerExternalId]);
|
153
191
|
|
154
192
|
if (loading) {
|
155
193
|
return <div>Loading payment data...</div>;
|
@@ -161,58 +199,59 @@ export const PaidPaymentsTable: React.FC<PaidPaymentsTableProps> = ({
|
|
161
199
|
|
162
200
|
return (
|
163
201
|
<div className="paid-payment-container" style={{ position: 'relative', minWidth: 0, ...cssVariables }}>
|
164
|
-
<div className="paid-payment-table-wrapper" style={{ position: '
|
202
|
+
<div className="paid-payment-table-wrapper" style={{ position: 'relative', width: '100%', height: 'auto', left: undefined, top: undefined, boxShadow: undefined, cursor: undefined }}>
|
165
203
|
{!isInContainer && (
|
166
204
|
<div className="paid-payment-header">
|
167
205
|
<h3 className="paid-payment-title">Payments</h3>
|
168
206
|
</div>
|
169
207
|
)}
|
170
|
-
<div style={{ background: '#fff', overflow: 'auto' }}>
|
171
|
-
<table className="paid-payment-table">
|
208
|
+
<div style={{ background: '#fff', overflow: 'auto', width: '100%', boxSizing: 'border-box' }}>
|
209
|
+
<table className="paid-payment-table" style={{ width: '100%', maxWidth: '100%', tableLayout: 'fixed' }}>
|
172
210
|
<thead>
|
173
211
|
<tr>
|
174
212
|
<th>Payment Number</th>
|
175
213
|
<th>Payment type</th>
|
176
214
|
<th>Due date</th>
|
177
215
|
<th>Status</th>
|
178
|
-
<th style={{ textAlign: '
|
179
|
-
<th></th>
|
216
|
+
<th style={{ textAlign: 'center' }}>Amount</th>
|
180
217
|
</tr>
|
181
218
|
</thead>
|
182
219
|
<tbody>
|
183
|
-
{
|
220
|
+
{currentPayments.length === 0 ? (
|
184
221
|
<tr>
|
185
|
-
<td colSpan={
|
222
|
+
<td colSpan={5} className="paid-payment-empty">
|
186
223
|
No payments found
|
187
224
|
</td>
|
188
225
|
</tr>
|
189
226
|
) : (
|
190
|
-
|
227
|
+
currentPayments.map((payment) => (
|
191
228
|
<tr key={payment.id}>
|
192
|
-
<td>
|
229
|
+
<td style={{ fontWeight: 500 }}>
|
193
230
|
<div className="paid-payment-number">
|
194
|
-
<span className="paid-payment-dollar">$</span>
|
195
231
|
<span>PAY-1</span>
|
196
232
|
</div>
|
197
233
|
</td>
|
198
234
|
<td>{payment.paymentType}</td>
|
199
235
|
<td>{formatDate(payment.paymentDate)}</td>
|
200
|
-
<td>{getStatusBadge(payment.
|
201
|
-
<td style={{ textAlign: '
|
236
|
+
<td>{getStatusBadge(payment.paymentStatus)}</td>
|
237
|
+
<td style={{ textAlign: 'center' }}>
|
202
238
|
<div className="paid-payment-amount">
|
203
|
-
<span>{formatCurrency(payment.amount)}</span>
|
204
|
-
<span className="paid-payment-dollar-right">$</span>
|
239
|
+
<span className="amount-number">{formatCurrency(payment.amount, payment.currency)}</span>
|
205
240
|
</div>
|
206
241
|
</td>
|
207
|
-
<td style={{ textAlign: 'center' }}>
|
208
|
-
<span className="paid-payment-currency">{payment.currency}</span>
|
209
|
-
</td>
|
210
242
|
</tr>
|
211
243
|
))
|
212
244
|
)}
|
213
245
|
</tbody>
|
214
246
|
</table>
|
215
247
|
</div>
|
248
|
+
|
249
|
+
{/* Pagination */}
|
250
|
+
<Pagination
|
251
|
+
currentPage={currentPage}
|
252
|
+
totalPages={totalPages}
|
253
|
+
onPageChange={handlePageChange}
|
254
|
+
/>
|
216
255
|
</div>
|
217
256
|
</div>
|
218
257
|
);
|