@agentpaid/paid-nextjs-client 0.1.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.
Files changed (54) hide show
  1. package/README.md +57 -0
  2. package/dist/api/api/handlePaidInvoicePdf.ts +79 -0
  3. package/dist/api/api/handlePaidInvoices.ts +77 -0
  4. package/dist/api/api/handlePaidPayments.ts +77 -0
  5. package/dist/api/handlePaidInvoicePdf.d.ts +6 -0
  6. package/dist/api/handlePaidInvoicePdf.d.ts.map +1 -0
  7. package/dist/api/handlePaidInvoicePdf.js +60 -0
  8. package/dist/api/handlePaidInvoicePdf.js.map +1 -0
  9. package/dist/api/handlePaidInvoices.d.ts +6 -0
  10. package/dist/api/handlePaidInvoices.d.ts.map +1 -0
  11. package/dist/api/handlePaidInvoices.js +56 -0
  12. package/dist/api/handlePaidInvoices.js.map +1 -0
  13. package/dist/api/handlePaidPayments.d.ts +6 -0
  14. package/dist/api/handlePaidPayments.d.ts.map +1 -0
  15. package/dist/api/handlePaidPayments.js +56 -0
  16. package/dist/api/handlePaidPayments.js.map +1 -0
  17. package/dist/components/PaidActivityLog.d.ts +27 -1
  18. package/dist/components/PaidActivityLog.d.ts.map +1 -1
  19. package/dist/components/PaidActivityLog.js +28 -6
  20. package/dist/components/PaidActivityLog.js.map +1 -1
  21. package/dist/components/PaidContainer.d.ts +45 -0
  22. package/dist/components/PaidContainer.d.ts.map +1 -0
  23. package/dist/components/PaidContainer.js +29 -0
  24. package/dist/components/PaidContainer.js.map +1 -0
  25. package/dist/components/PaidInvoiceTable.d.ts +35 -0
  26. package/dist/components/PaidInvoiceTable.d.ts.map +1 -0
  27. package/dist/components/PaidInvoiceTable.js +138 -0
  28. package/dist/components/PaidInvoiceTable.js.map +1 -0
  29. package/dist/components/PaidPaymentsTable.d.ts +35 -0
  30. package/dist/components/PaidPaymentsTable.d.ts.map +1 -0
  31. package/dist/components/PaidPaymentsTable.js +99 -0
  32. package/dist/components/PaidPaymentsTable.js.map +1 -0
  33. package/dist/components/components/PaidActivityLog.js +73 -20
  34. package/dist/components/components/PaidContainer.js +113 -0
  35. package/dist/components/components/PaidInvoiceTable.js +321 -0
  36. package/dist/components/components/PaidPaymentsTable.js +216 -0
  37. package/dist/hooks/useCache.d.ts +12 -0
  38. package/dist/hooks/useCache.d.ts.map +1 -0
  39. package/dist/hooks/useCache.js +43 -0
  40. package/dist/hooks/useCache.js.map +1 -0
  41. package/dist/index.d.ts +7 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +7 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/styles/activity-log-table.css +29 -27
  46. package/dist/styles/paid-container.css +139 -0
  47. package/dist/styles/paid-invoice-table.css +344 -0
  48. package/dist/styles/paid-payments-table.css +238 -0
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/dist/utils/cache.d.ts +30 -0
  51. package/dist/utils/cache.d.ts.map +1 -0
  52. package/dist/utils/cache.js +101 -0
  53. package/dist/utils/cache.js.map +1 -0
  54. package/package.json +9 -3
@@ -0,0 +1,113 @@
1
+ 'use client';
2
+
3
+ import React, { useState, createContext, useContext } from 'react';
4
+ import '../styles/paid-container.css';
5
+
6
+ // Create context to detect if components are inside a container
7
+ const PaidContainerContext = createContext<boolean>(false);
8
+
9
+ // Export hook for components to use
10
+ export const useIsInContainer = () => useContext(PaidContainerContext);
11
+
12
+ interface PaidStyleProperties {
13
+ paidTitleColor?: string;
14
+ paidTitleFontWeight?: string;
15
+ paidFontFamily?: string;
16
+ paidWrapperBorder?: string;
17
+ paidHeaderBorderBottom?: string;
18
+ paidThBorderBottom?: string;
19
+ paidTdBorderBottom?: string;
20
+ paidTdBg?: string;
21
+ paidTdFontWeight?: string;
22
+ paidTitleFontSize?: string;
23
+ paidToggleFontSize?: string;
24
+ paidToggleFontWeight?: string;
25
+ paidToggleColor?: string;
26
+ paidThFontSize?: string;
27
+ paidThFontWeight?: string;
28
+ paidThColor?: string;
29
+ paidTdFontSize?: string;
30
+ paidTdColor?: string;
31
+ paidEmptyColor?: string;
32
+ paidWrapperBg?: string;
33
+ paidHeaderBg?: string;
34
+ paidTableBg?: string;
35
+ paidThBg?: string;
36
+ paidRowHoverBg?: string;
37
+ }
38
+
39
+ interface PaidContainerTab {
40
+ id: string;
41
+ label: string;
42
+ component: React.ReactNode;
43
+ }
44
+
45
+ interface PaidContainerProps {
46
+ title: string;
47
+ description?: string;
48
+ tabs?: PaidContainerTab[];
49
+ defaultActiveTab?: string;
50
+ paidStyle?: PaidStyleProperties;
51
+ children?: React.ReactNode;
52
+ }
53
+
54
+ export const PaidContainer: React.FC<PaidContainerProps> = ({
55
+ title,
56
+ description,
57
+ tabs = [],
58
+ defaultActiveTab,
59
+ paidStyle = {},
60
+ children
61
+ }) => {
62
+ const [activeTab, setActiveTab] = useState(defaultActiveTab || tabs[0]?.id);
63
+
64
+ // Convert paidStyle entries into CSS custom properties
65
+ const cssVariables: React.CSSProperties = Object.entries(paidStyle).reduce((vars, [key, value]) => {
66
+ let varName: string;
67
+ if (key.startsWith('--')) {
68
+ varName = key;
69
+ } else {
70
+ const raw = key.replace(/([A-Z])/g, '-$1').toLowerCase();
71
+ varName = raw.startsWith('--') ? raw : `--${raw}`;
72
+ }
73
+ // @ts-ignore allow custom property
74
+ vars[varName] = value;
75
+ return vars;
76
+ }, {} as React.CSSProperties);
77
+
78
+ const activeTabData = tabs.find(tab => tab.id === activeTab);
79
+
80
+ return (
81
+ <PaidContainerContext.Provider value={true}>
82
+ <div className="paid-container" style={{ ...cssVariables }}>
83
+ <div className="paid-container-wrapper">
84
+ <div className="paid-container-header">
85
+ <div className="paid-container-header-content">
86
+ <h2 className="paid-container-title">{title}</h2>
87
+ {description && (
88
+ <p className="paid-container-description">{description}</p>
89
+ )}
90
+ </div>
91
+
92
+ {tabs.length > 1 && (
93
+ <div className="paid-container-tabs">
94
+ {tabs.map((tab) => (
95
+ <button
96
+ key={tab.id}
97
+ className={`paid-container-tab ${activeTab === tab.id ? 'active' : ''}`}
98
+ onClick={() => setActiveTab(tab.id)}
99
+ >
100
+ {tab.label}
101
+ </button>
102
+ ))}
103
+ </div>
104
+ )}
105
+ </div>
106
+ <div className="paid-container-content">
107
+ {children || activeTabData?.component}
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </PaidContainerContext.Provider>
112
+ );
113
+ };
@@ -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"}