@agentpaid/paid-nextjs-client 0.2.1 → 0.3.0-test.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/dist/api/api/handlePaidInvoicePdf.ts +79 -0
- package/dist/api/api/handlePaidInvoices.ts +77 -0
- package/dist/api/api/handlePaidPayments.ts +77 -0
- package/dist/api/handlePaidInvoicePdf.d.ts +6 -0
- package/dist/api/handlePaidInvoicePdf.d.ts.map +1 -0
- package/dist/api/handlePaidInvoicePdf.js +60 -0
- package/dist/api/handlePaidInvoicePdf.js.map +1 -0
- package/dist/api/handlePaidInvoices.d.ts +6 -0
- package/dist/api/handlePaidInvoices.d.ts.map +1 -0
- package/dist/api/handlePaidInvoices.js +56 -0
- package/dist/api/handlePaidInvoices.js.map +1 -0
- package/dist/api/handlePaidPayments.d.ts +6 -0
- package/dist/api/handlePaidPayments.d.ts.map +1 -0
- package/dist/api/handlePaidPayments.js +56 -0
- package/dist/api/handlePaidPayments.js.map +1 -0
- package/dist/components/PaidActivityLog.d.ts +0 -1
- package/dist/components/PaidActivityLog.d.ts.map +1 -1
- package/dist/components/PaidActivityLog.js +13 -3
- package/dist/components/PaidActivityLog.js.map +1 -1
- package/dist/components/PaidContainer.d.ts +45 -0
- package/dist/components/PaidContainer.d.ts.map +1 -0
- package/dist/components/PaidContainer.js +29 -0
- package/dist/components/PaidContainer.js.map +1 -0
- package/dist/components/PaidInvoiceTable.d.ts +35 -0
- package/dist/components/PaidInvoiceTable.d.ts.map +1 -0
- package/dist/components/PaidInvoiceTable.js +138 -0
- package/dist/components/PaidInvoiceTable.js.map +1 -0
- package/dist/components/PaidPaymentsTable.d.ts +35 -0
- package/dist/components/PaidPaymentsTable.d.ts.map +1 -0
- package/dist/components/PaidPaymentsTable.js +99 -0
- package/dist/components/PaidPaymentsTable.js.map +1 -0
- package/dist/components/components/PaidActivityLog.js +31 -9
- package/dist/components/components/PaidContainer.js +113 -0
- package/dist/components/components/PaidInvoiceTable.js +321 -0
- package/dist/components/components/PaidPaymentsTable.js +216 -0
- package/dist/hooks/useCache.d.ts +12 -0
- package/dist/hooks/useCache.d.ts.map +1 -0
- package/dist/hooks/useCache.js +43 -0
- package/dist/hooks/useCache.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/styles/paid-container.css +139 -0
- package/dist/styles/paid-invoice-table.css +344 -0
- package/dist/styles/paid-payments-table.css +238 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/cache.d.ts +30 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +101 -0
- package/dist/utils/cache.js.map +1 -0
- package/package.json +9 -3
@@ -0,0 +1,321 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import React, { useEffect, useState } from 'react';
|
4
|
+
import { useIsInContainer } from './PaidContainer';
|
5
|
+
import { cachedFetch, getCacheKey, CACHE_TTL, dataCache } from '../utils/cache';
|
6
|
+
import '../styles/paid-invoice-table.css';
|
7
|
+
|
8
|
+
interface PaidStyleProperties {
|
9
|
+
paidTitleColor?: string;
|
10
|
+
paidTitleFontWeight?: string;
|
11
|
+
paidFontFamily?: string;
|
12
|
+
paidWrapperBorder?: string;
|
13
|
+
paidHeaderBorderBottom?: string;
|
14
|
+
paidThBorderBottom?: string;
|
15
|
+
paidTdBorderBottom?: string;
|
16
|
+
paidTdBg?: string;
|
17
|
+
paidTdFontWeight?: string;
|
18
|
+
paidTitleFontSize?: string;
|
19
|
+
paidToggleFontSize?: string;
|
20
|
+
paidToggleFontWeight?: string;
|
21
|
+
paidToggleColor?: string;
|
22
|
+
paidThFontSize?: string;
|
23
|
+
paidThFontWeight?: string;
|
24
|
+
paidThColor?: string;
|
25
|
+
paidTdFontSize?: string;
|
26
|
+
paidTdColor?: string;
|
27
|
+
paidEmptyColor?: string;
|
28
|
+
paidWrapperBg?: string;
|
29
|
+
paidHeaderBg?: string;
|
30
|
+
paidTableBg?: string;
|
31
|
+
paidThBg?: string;
|
32
|
+
paidRowHoverBg?: string;
|
33
|
+
}
|
34
|
+
|
35
|
+
interface Invoice {
|
36
|
+
id: string;
|
37
|
+
number: string;
|
38
|
+
issueDate: string;
|
39
|
+
dueDate: string;
|
40
|
+
status: string;
|
41
|
+
invoiceTotal: number;
|
42
|
+
currency: string;
|
43
|
+
customer?: {
|
44
|
+
id: string;
|
45
|
+
name: string;
|
46
|
+
externalId: string;
|
47
|
+
};
|
48
|
+
}
|
49
|
+
|
50
|
+
interface InvoiceApiResponse {
|
51
|
+
data: Invoice[];
|
52
|
+
}
|
53
|
+
|
54
|
+
interface PaidInvoiceTableProps {
|
55
|
+
accountExternalId: string;
|
56
|
+
paidStyle?: PaidStyleProperties;
|
57
|
+
}
|
58
|
+
|
59
|
+
export const PaidInvoiceTable: React.FC<PaidInvoiceTableProps> = ({
|
60
|
+
accountExternalId,
|
61
|
+
paidStyle = {}
|
62
|
+
}) => {
|
63
|
+
const [invoices, setInvoices] = useState<Invoice[]>([]);
|
64
|
+
const [loading, setLoading] = useState(true);
|
65
|
+
const [error, setError] = useState<string | null>(null);
|
66
|
+
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
67
|
+
const [pdfResponse, setPdfResponse] = useState<string | null>(null);
|
68
|
+
const [selectedInvoice, setSelectedInvoice] = useState<Invoice | null>(null);
|
69
|
+
const [loadingPdf, setLoadingPdf] = useState(false);
|
70
|
+
const isInContainer = useIsInContainer();
|
71
|
+
|
72
|
+
// Convert paidStyle entries into CSS custom properties
|
73
|
+
const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
|
74
|
+
let varName: string;
|
75
|
+
if (key.startsWith('--')) {
|
76
|
+
varName = key;
|
77
|
+
} else {
|
78
|
+
const raw = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
79
|
+
varName = raw.startsWith('--') ? raw : `--${raw}`;
|
80
|
+
}
|
81
|
+
// @ts-ignore allow custom property
|
82
|
+
vars[varName] = value;
|
83
|
+
return vars;
|
84
|
+
}, {} as React.CSSProperties);
|
85
|
+
|
86
|
+
const formatCurrency = (amount: number) => {
|
87
|
+
return new Intl.NumberFormat("en-US", {
|
88
|
+
style: "currency",
|
89
|
+
currency: "USD",
|
90
|
+
minimumFractionDigits: 2,
|
91
|
+
maximumFractionDigits: 2,
|
92
|
+
}).format(amount / 100);
|
93
|
+
};
|
94
|
+
|
95
|
+
const formatDate = (dateString: string) => {
|
96
|
+
return new Date(dateString).toLocaleDateString('en-US', {
|
97
|
+
year: 'numeric',
|
98
|
+
month: 'short',
|
99
|
+
day: 'numeric'
|
100
|
+
});
|
101
|
+
};
|
102
|
+
|
103
|
+
const getStatusBadge = (status: string) => {
|
104
|
+
const statusClass = `paid-invoice-status paid-invoice-status-${status.toLowerCase()}`;
|
105
|
+
const displayStatus = status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
|
106
|
+
return <span className={statusClass}>{displayStatus}</span>;
|
107
|
+
};
|
108
|
+
|
109
|
+
const handlePreview = async (invoice: Invoice) => {
|
110
|
+
try {
|
111
|
+
setLoadingPdf(true);
|
112
|
+
setSelectedInvoice(invoice);
|
113
|
+
|
114
|
+
// Check cache first for PDF
|
115
|
+
const pdfCacheKey = getCacheKey.invoicePdf(invoice.id);
|
116
|
+
const cachedPdf = dataCache.get<string>(pdfCacheKey);
|
117
|
+
|
118
|
+
if (cachedPdf) {
|
119
|
+
setPdfResponse(cachedPdf);
|
120
|
+
setIsPreviewOpen(true);
|
121
|
+
setLoadingPdf(false);
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
|
125
|
+
// Fetch PDF if not cached
|
126
|
+
const response = await fetch(`/api/invoice-pdf/${invoice.id}`);
|
127
|
+
|
128
|
+
if (!response.ok) {
|
129
|
+
throw new Error('Failed to fetch PDF');
|
130
|
+
}
|
131
|
+
|
132
|
+
const data = await response.json();
|
133
|
+
const pdfData = "data:application/pdf;base64," + data.data.pdfBytes;
|
134
|
+
|
135
|
+
// Cache the PDF data
|
136
|
+
dataCache.set(pdfCacheKey, pdfData, CACHE_TTL.PDF);
|
137
|
+
|
138
|
+
setPdfResponse(pdfData);
|
139
|
+
setIsPreviewOpen(true);
|
140
|
+
} catch (error) {
|
141
|
+
console.error('Error fetching PDF:', error);
|
142
|
+
alert('Failed to load PDF preview');
|
143
|
+
} finally {
|
144
|
+
setLoadingPdf(false);
|
145
|
+
}
|
146
|
+
};
|
147
|
+
|
148
|
+
const handleDownload = () => {
|
149
|
+
if (!pdfResponse || !selectedInvoice) return;
|
150
|
+
|
151
|
+
const base64WithoutPrefix = pdfResponse.split(",")[1];
|
152
|
+
const byteCharacters = atob(base64WithoutPrefix);
|
153
|
+
const byteNumbers = new Array(byteCharacters.length)
|
154
|
+
.fill(0)
|
155
|
+
.map((_, i) => byteCharacters.charCodeAt(i));
|
156
|
+
const byteArray = new Uint8Array(byteNumbers);
|
157
|
+
const blob = new Blob([byteArray], { type: "application/pdf" });
|
158
|
+
|
159
|
+
const url = URL.createObjectURL(blob);
|
160
|
+
const a = document.createElement("a");
|
161
|
+
a.href = url;
|
162
|
+
a.download = `invoice-${selectedInvoice.number}.pdf`;
|
163
|
+
document.body.appendChild(a);
|
164
|
+
a.click();
|
165
|
+
document.body.removeChild(a);
|
166
|
+
URL.revokeObjectURL(url);
|
167
|
+
};
|
168
|
+
|
169
|
+
useEffect(() => {
|
170
|
+
const fetchInvoiceData = async () => {
|
171
|
+
try {
|
172
|
+
setLoading(true);
|
173
|
+
|
174
|
+
// TEMPORARILY DISABLED: Use cached fetch for invoice data
|
175
|
+
// const cacheKey = getCacheKey.invoices(accountExternalId);
|
176
|
+
// const data = await cachedFetch<InvoiceApiResponse>(
|
177
|
+
// `/api/invoices/${accountExternalId}`,
|
178
|
+
// cacheKey,
|
179
|
+
// CACHE_TTL.DATA
|
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();
|
188
|
+
|
189
|
+
setInvoices(data.data || []);
|
190
|
+
} catch (err) {
|
191
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
192
|
+
} finally {
|
193
|
+
setLoading(false);
|
194
|
+
}
|
195
|
+
};
|
196
|
+
|
197
|
+
fetchInvoiceData();
|
198
|
+
}, [accountExternalId]);
|
199
|
+
|
200
|
+
if (loading) {
|
201
|
+
return <div>Loading invoice data...</div>;
|
202
|
+
}
|
203
|
+
|
204
|
+
if (error) {
|
205
|
+
return <div>Error: {error}</div>;
|
206
|
+
}
|
207
|
+
|
208
|
+
return (
|
209
|
+
<>
|
210
|
+
<div className="paid-invoice-container" style={{ ...cssVariables }}>
|
211
|
+
<div className="paid-invoice-table-wrapper">
|
212
|
+
{!isInContainer && (
|
213
|
+
<div className="paid-invoice-header">
|
214
|
+
<h3 className="paid-invoice-title">Invoices</h3>
|
215
|
+
</div>
|
216
|
+
)}
|
217
|
+
<div style={{ background: '#fff', overflow: 'auto' }}>
|
218
|
+
<table className="paid-invoice-table">
|
219
|
+
<thead>
|
220
|
+
<tr>
|
221
|
+
<th>Invoice Number</th>
|
222
|
+
<th>Status</th>
|
223
|
+
<th>Invoice Date</th>
|
224
|
+
<th>Due Date</th>
|
225
|
+
<th style={{ textAlign: 'right' }}>Total amount</th>
|
226
|
+
<th style={{ textAlign: 'center' }}>Actions</th>
|
227
|
+
</tr>
|
228
|
+
</thead>
|
229
|
+
<tbody>
|
230
|
+
{invoices.length === 0 ? (
|
231
|
+
<tr>
|
232
|
+
<td colSpan={6} className="paid-invoice-empty">
|
233
|
+
No invoices found
|
234
|
+
</td>
|
235
|
+
</tr>
|
236
|
+
) : (
|
237
|
+
invoices.map((invoice) => (
|
238
|
+
<tr key={invoice.id}>
|
239
|
+
<td style={{ fontWeight: 500 }}>INV-{invoice.number}</td>
|
240
|
+
<td>{getStatusBadge(invoice.status)}</td>
|
241
|
+
<td>{formatDate(invoice.issueDate)}</td>
|
242
|
+
<td>{formatDate(invoice.dueDate)}</td>
|
243
|
+
<td style={{ textAlign: 'right', fontWeight: 500 }}>{formatCurrency(invoice.invoiceTotal)}</td>
|
244
|
+
<td style={{ textAlign: 'center' }}>
|
245
|
+
<button
|
246
|
+
className="paid-invoice-action-btn"
|
247
|
+
onClick={() => handlePreview(invoice)}
|
248
|
+
disabled={loadingPdf}
|
249
|
+
title="Preview Invoice"
|
250
|
+
>
|
251
|
+
{loadingPdf ? (
|
252
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
253
|
+
<circle cx="8" cy="8" r="6" stroke="currentColor" strokeWidth="2" fill="none" strokeDasharray="37.7" strokeDashoffset="37.7">
|
254
|
+
<animateTransform attributeName="transform" type="rotate" values="0 8 8;360 8 8" dur="1s" repeatCount="indefinite"/>
|
255
|
+
</circle>
|
256
|
+
</svg>
|
257
|
+
) : (
|
258
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
259
|
+
<path d="M2 2h8l4 4v8a1 1 0 01-1 1H2a1 1 0 01-1-1V3a1 1 0 011-1z" stroke="currentColor" strokeWidth="1.5" fill="none"/>
|
260
|
+
<path d="M10 2v4h4" stroke="currentColor" strokeWidth="1.5" fill="none"/>
|
261
|
+
<path d="M5 9h6M5 11h4" stroke="currentColor" strokeWidth="1.5"/>
|
262
|
+
</svg>
|
263
|
+
)}
|
264
|
+
</button>
|
265
|
+
</td>
|
266
|
+
</tr>
|
267
|
+
))
|
268
|
+
)}
|
269
|
+
</tbody>
|
270
|
+
</table>
|
271
|
+
</div>
|
272
|
+
</div>
|
273
|
+
</div>
|
274
|
+
|
275
|
+
{/* PDF Preview Modal */}
|
276
|
+
{isPreviewOpen && (
|
277
|
+
<div className="paid-invoice-modal-overlay" onClick={() => setIsPreviewOpen(false)}>
|
278
|
+
<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
|
+
<div className="paid-invoice-modal-body">
|
304
|
+
{pdfResponse ? (
|
305
|
+
<iframe
|
306
|
+
src={pdfResponse}
|
307
|
+
width="100%"
|
308
|
+
height="100%"
|
309
|
+
style={{ border: 'none' }}
|
310
|
+
title="Invoice PDF"
|
311
|
+
/>
|
312
|
+
) : (
|
313
|
+
<div className="paid-invoice-modal-loading">Loading PDF...</div>
|
314
|
+
)}
|
315
|
+
</div>
|
316
|
+
</div>
|
317
|
+
</div>
|
318
|
+
)}
|
319
|
+
</>
|
320
|
+
);
|
321
|
+
};
|
@@ -0,0 +1,216 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import React, { useEffect, useState } from 'react';
|
4
|
+
import { useIsInContainer } from './PaidContainer';
|
5
|
+
import { cachedFetch, getCacheKey, CACHE_TTL } from '../utils/cache';
|
6
|
+
import '../styles/paid-payments-table.css';
|
7
|
+
|
8
|
+
interface PaidStyleProperties {
|
9
|
+
paidTitleColor?: string;
|
10
|
+
paidTitleFontWeight?: string;
|
11
|
+
paidFontFamily?: string;
|
12
|
+
paidWrapperBorder?: string;
|
13
|
+
paidHeaderBorderBottom?: string;
|
14
|
+
paidThBorderBottom?: string;
|
15
|
+
paidTdBorderBottom?: string;
|
16
|
+
paidTdBg?: string;
|
17
|
+
paidTdFontWeight?: string;
|
18
|
+
paidTitleFontSize?: string;
|
19
|
+
paidToggleFontSize?: string;
|
20
|
+
paidToggleFontWeight?: string;
|
21
|
+
paidToggleColor?: string;
|
22
|
+
paidThFontSize?: string;
|
23
|
+
paidThFontWeight?: string;
|
24
|
+
paidThColor?: string;
|
25
|
+
paidTdFontSize?: string;
|
26
|
+
paidTdColor?: string;
|
27
|
+
paidEmptyColor?: string;
|
28
|
+
paidWrapperBg?: string;
|
29
|
+
paidHeaderBg?: string;
|
30
|
+
paidTableBg?: string;
|
31
|
+
paidThBg?: string;
|
32
|
+
paidRowHoverBg?: string;
|
33
|
+
}
|
34
|
+
|
35
|
+
interface Payment {
|
36
|
+
id: string;
|
37
|
+
paymentType: string;
|
38
|
+
paymentDate: string;
|
39
|
+
status: string;
|
40
|
+
amount: number;
|
41
|
+
currency: string;
|
42
|
+
}
|
43
|
+
|
44
|
+
interface PaymentApiResponse {
|
45
|
+
data: Payment[];
|
46
|
+
}
|
47
|
+
|
48
|
+
interface PaidPaymentsTableProps {
|
49
|
+
accountExternalId: string;
|
50
|
+
paidStyle?: PaidStyleProperties;
|
51
|
+
}
|
52
|
+
|
53
|
+
export const PaidPaymentsTable: React.FC<PaidPaymentsTableProps> = ({
|
54
|
+
accountExternalId,
|
55
|
+
paidStyle = {}
|
56
|
+
}) => {
|
57
|
+
const [payments, setPayments] = useState<Payment[]>([]);
|
58
|
+
const [loading, setLoading] = useState(true);
|
59
|
+
const [error, setError] = useState<string | null>(null);
|
60
|
+
const isInContainer = useIsInContainer();
|
61
|
+
|
62
|
+
// Convert paidStyle entries into CSS custom properties
|
63
|
+
const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
|
64
|
+
let varName: string;
|
65
|
+
if (key.startsWith('--')) {
|
66
|
+
varName = key;
|
67
|
+
} else {
|
68
|
+
const raw = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
69
|
+
varName = raw.startsWith('--') ? raw : `--${raw}`;
|
70
|
+
}
|
71
|
+
// @ts-ignore allow custom property
|
72
|
+
vars[varName] = value;
|
73
|
+
return vars;
|
74
|
+
}, {} as React.CSSProperties);
|
75
|
+
|
76
|
+
const formatCurrency = (amount: number) => {
|
77
|
+
return new Intl.NumberFormat("en-US", {
|
78
|
+
style: "currency",
|
79
|
+
currency: "USD",
|
80
|
+
minimumFractionDigits: 0,
|
81
|
+
maximumFractionDigits: 0,
|
82
|
+
}).format(amount / 100);
|
83
|
+
};
|
84
|
+
|
85
|
+
const formatDate = (dateString: string) => {
|
86
|
+
return new Date(dateString).toLocaleDateString('en-US', {
|
87
|
+
day: '2-digit',
|
88
|
+
month: 'short',
|
89
|
+
year: 'numeric'
|
90
|
+
});
|
91
|
+
};
|
92
|
+
|
93
|
+
const getStatusBadge = (status: string) => {
|
94
|
+
const statusClass = `paid-payment-status paid-payment-status-${status.toLowerCase()}`;
|
95
|
+
const displayStatus = status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
|
96
|
+
return <span className={statusClass}>{displayStatus}</span>;
|
97
|
+
};
|
98
|
+
|
99
|
+
useEffect(() => {
|
100
|
+
const fetchPaymentData = async () => {
|
101
|
+
try {
|
102
|
+
setLoading(true);
|
103
|
+
console.log('PaidPaymentsTable: Fetching payment data for', accountExternalId);
|
104
|
+
|
105
|
+
// TEMPORARILY DISABLED: Use cached fetch for payment data
|
106
|
+
// const cacheKey = getCacheKey.payments(accountExternalId);
|
107
|
+
// console.log('PaidPaymentsTable: Using cache key', cacheKey);
|
108
|
+
// const data = await cachedFetch<PaymentApiResponse>(
|
109
|
+
// `/api/payments/${accountExternalId}`,
|
110
|
+
// cacheKey,
|
111
|
+
// CACHE_TTL.DATA
|
112
|
+
// );
|
113
|
+
|
114
|
+
// Direct fetch without caching
|
115
|
+
const response = await fetch(`/api/payments/${accountExternalId}`);
|
116
|
+
if (!response.ok) {
|
117
|
+
throw new Error(`Failed to fetch: ${response.statusText}`);
|
118
|
+
}
|
119
|
+
const data = await response.json();
|
120
|
+
|
121
|
+
console.log('PaidPaymentsTable: Received data', data);
|
122
|
+
setPayments(data.data || []);
|
123
|
+
} catch (err) {
|
124
|
+
console.error('PaidPaymentsTable: Error fetching data', err);
|
125
|
+
setError(err instanceof Error ? err.message : 'An error occurred');
|
126
|
+
} finally {
|
127
|
+
setLoading(false);
|
128
|
+
}
|
129
|
+
};
|
130
|
+
|
131
|
+
fetchPaymentData();
|
132
|
+
|
133
|
+
// Listen for cache refresh events
|
134
|
+
const handleCacheRefresh = (event: CustomEvent) => {
|
135
|
+
console.log('PaidPaymentsTable: Cache refresh event received', event.detail);
|
136
|
+
if (event.detail?.accountId === accountExternalId || event.detail?.type === 'all') {
|
137
|
+
console.log('PaidPaymentsTable: Refetching data due to cache refresh');
|
138
|
+
fetchPaymentData();
|
139
|
+
}
|
140
|
+
};
|
141
|
+
|
142
|
+
window.addEventListener('cache-refresh', handleCacheRefresh as EventListener);
|
143
|
+
|
144
|
+
return () => {
|
145
|
+
window.removeEventListener('cache-refresh', handleCacheRefresh as EventListener);
|
146
|
+
};
|
147
|
+
}, [accountExternalId]);
|
148
|
+
|
149
|
+
if (loading) {
|
150
|
+
return <div>Loading payment data...</div>;
|
151
|
+
}
|
152
|
+
|
153
|
+
if (error) {
|
154
|
+
return <div>Error: {error}</div>;
|
155
|
+
}
|
156
|
+
|
157
|
+
return (
|
158
|
+
<div className="paid-payment-container" style={{ position: 'relative', minWidth: 0, ...cssVariables }}>
|
159
|
+
<div className="paid-payment-table-wrapper" style={{ position: 'static', width: '100%', height: 'auto', left: undefined, top: undefined, boxShadow: undefined, cursor: undefined }}>
|
160
|
+
{!isInContainer && (
|
161
|
+
<div className="paid-payment-header">
|
162
|
+
<h3 className="paid-payment-title">Payments</h3>
|
163
|
+
</div>
|
164
|
+
)}
|
165
|
+
<div style={{ background: '#fff', overflow: 'auto' }}>
|
166
|
+
<table className="paid-payment-table">
|
167
|
+
<thead>
|
168
|
+
<tr>
|
169
|
+
<th>Payment Number</th>
|
170
|
+
<th>Payment type</th>
|
171
|
+
<th>Due date</th>
|
172
|
+
<th>Status</th>
|
173
|
+
<th style={{ textAlign: 'right' }}>Amount</th>
|
174
|
+
<th></th>
|
175
|
+
</tr>
|
176
|
+
</thead>
|
177
|
+
<tbody>
|
178
|
+
{payments.length === 0 ? (
|
179
|
+
<tr>
|
180
|
+
<td colSpan={6} className="paid-payment-empty">
|
181
|
+
No payments found
|
182
|
+
</td>
|
183
|
+
</tr>
|
184
|
+
) : (
|
185
|
+
payments.map((payment) => (
|
186
|
+
<tr key={payment.id}>
|
187
|
+
<td>
|
188
|
+
<div className="paid-payment-number">
|
189
|
+
<span className="paid-payment-dollar">$</span>
|
190
|
+
<span>PAY-1</span>
|
191
|
+
</div>
|
192
|
+
</td>
|
193
|
+
<td>{payment.paymentType}</td>
|
194
|
+
<td>{formatDate(payment.paymentDate)}</td>
|
195
|
+
<td>{getStatusBadge(payment.status)}</td>
|
196
|
+
<td style={{ textAlign: 'right' }}>
|
197
|
+
<div className="paid-payment-amount">
|
198
|
+
<span>{formatCurrency(payment.amount)}</span>
|
199
|
+
<span className="paid-payment-dollar-right">$</span>
|
200
|
+
</div>
|
201
|
+
</td>
|
202
|
+
<td style={{ textAlign: 'center' }}>
|
203
|
+
<span className="paid-payment-currency">{payment.currency}</span>
|
204
|
+
</td>
|
205
|
+
</tr>
|
206
|
+
))
|
207
|
+
)}
|
208
|
+
</tbody>
|
209
|
+
</table>
|
210
|
+
</div>
|
211
|
+
</div>
|
212
|
+
</div>
|
213
|
+
);
|
214
|
+
};
|
215
|
+
|
216
|
+
export default PaidPaymentsTable;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
export declare const useCache: () => {
|
2
|
+
clearAllCache: () => void;
|
3
|
+
clearAccountCache: (accountId: string) => void;
|
4
|
+
clearInvoicePdfCache: (invoiceId: string) => void;
|
5
|
+
refreshAccountData: (accountId: string) => void;
|
6
|
+
getCacheStats: () => {
|
7
|
+
size: number;
|
8
|
+
keys: string[];
|
9
|
+
};
|
10
|
+
isCached: (key: string) => boolean;
|
11
|
+
};
|
12
|
+
//# sourceMappingURL=useCache.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"useCache.d.ts","sourceRoot":"","sources":["../../src/hooks/useCache.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ;;mCAO+B,MAAM;sCAOH,MAAM;oCAKR,MAAM;;;;;oBActB,MAAM;CAY1C,CAAC"}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { useCallback } from 'react';
|
2
|
+
import { dataCache, getCacheKey } from '../utils/cache';
|
3
|
+
export const useCache = () => {
|
4
|
+
// Clear all cache
|
5
|
+
const clearAllCache = useCallback(() => {
|
6
|
+
dataCache.clear();
|
7
|
+
}, []);
|
8
|
+
// Clear cache for specific account
|
9
|
+
const clearAccountCache = useCallback((accountId) => {
|
10
|
+
dataCache.delete(getCacheKey.invoices(accountId));
|
11
|
+
dataCache.delete(getCacheKey.payments(accountId));
|
12
|
+
dataCache.delete(getCacheKey.usage(accountId));
|
13
|
+
}, []);
|
14
|
+
// Clear PDF cache for specific invoice
|
15
|
+
const clearInvoicePdfCache = useCallback((invoiceId) => {
|
16
|
+
dataCache.delete(getCacheKey.invoicePdf(invoiceId));
|
17
|
+
}, []);
|
18
|
+
// Force refresh data for account (clears cache and triggers refetch)
|
19
|
+
const refreshAccountData = useCallback((accountId) => {
|
20
|
+
clearAccountCache(accountId);
|
21
|
+
// Trigger a custom event that components can listen to for refetching
|
22
|
+
window.dispatchEvent(new CustomEvent('cache-refresh', {
|
23
|
+
detail: { accountId, type: 'account' }
|
24
|
+
}));
|
25
|
+
}, [clearAccountCache]);
|
26
|
+
// Get cache statistics
|
27
|
+
const getCacheStats = useCallback(() => {
|
28
|
+
return dataCache.getStats();
|
29
|
+
}, []);
|
30
|
+
// Check if data is cached
|
31
|
+
const isCached = useCallback((key) => {
|
32
|
+
return dataCache.has(key);
|
33
|
+
}, []);
|
34
|
+
return {
|
35
|
+
clearAllCache,
|
36
|
+
clearAccountCache,
|
37
|
+
clearInvoicePdfCache,
|
38
|
+
refreshAccountData,
|
39
|
+
getCacheStats,
|
40
|
+
isCached,
|
41
|
+
};
|
42
|
+
};
|
43
|
+
//# sourceMappingURL=useCache.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"useCache.js","sourceRoot":"","sources":["../../src/hooks/useCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,EAAE;IAC3B,kBAAkB;IAClB,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,SAAS,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,mCAAmC;IACnC,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,SAAiB,EAAE,EAAE;QAC1D,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAClD,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IACjD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,uCAAuC;IACvC,MAAM,oBAAoB,GAAG,WAAW,CAAC,CAAC,SAAiB,EAAE,EAAE;QAC7D,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IACtD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,qEAAqE;IACrE,MAAM,kBAAkB,GAAG,WAAW,CAAC,CAAC,SAAiB,EAAE,EAAE;QAC3D,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC7B,sEAAsE;QACtE,MAAM,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,eAAe,EAAE;YACpD,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;SACvC,CAAC,CAAC,CAAC;IACN,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAExB,uBAAuB;IACvB,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAC;IAC9B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,GAAW,EAAE,EAAE;QAC3C,OAAO,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,aAAa;QACb,iBAAiB;QACjB,oBAAoB;QACpB,kBAAkB;QAClB,aAAa;QACb,QAAQ;KACT,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
export { PaidActivityLog } from './components/PaidActivityLog';
|
2
|
+
export { PaidContainer, useIsInContainer } from './components/PaidContainer';
|
3
|
+
export { PaidInvoiceTable } from './components/PaidInvoiceTable';
|
4
|
+
export { PaidPaymentsTable } from './components/PaidPaymentsTable';
|
2
5
|
export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './components/ui/table';
|
3
|
-
export { handlePaidUsage } from './api/handlePaidUsage';
|
6
|
+
export { handlePaidUsage } from './api/handlePaidUsage';
|
7
|
+
export { handlePaidInvoices } from './api/handlePaidInvoices';
|
8
|
+
export { handlePaidInvoicePdf } from './api/handlePaidInvoicePdf';
|
9
|
+
export { handlePaidPayments } from './api/handlePaidPayments';
|
package/dist/index.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
export { PaidActivityLog } from './components/PaidActivityLog';
|
2
|
+
export { PaidContainer, useIsInContainer } from './components/PaidContainer';
|
3
|
+
export { PaidInvoiceTable } from './components/PaidInvoiceTable';
|
4
|
+
export { PaidPaymentsTable } from './components/PaidPaymentsTable';
|
2
5
|
export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './components/ui/table';
|
3
|
-
export { handlePaidUsage } from './api/handlePaidUsage';
|
6
|
+
export { handlePaidUsage } from './api/handlePaidUsage';
|
7
|
+
export { handlePaidInvoices } from './api/handlePaidInvoices';
|
8
|
+
export { handlePaidInvoicePdf } from './api/handlePaidInvoicePdf';
|
9
|
+
export { handlePaidPayments } from './api/handlePaidPayments';
|
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
|