@djangocfg/layouts 1.0.4 → 1.0.6
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/package.json +5 -5
- package/src/layouts/AppLayout/AppLayout.tsx +21 -27
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +47 -65
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +118 -163
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +31 -45
- package/src/layouts/PaymentsLayout/components/index.ts +1 -4
- package/src/layouts/PaymentsLayout/events.ts +23 -84
- package/src/layouts/PaymentsLayout/index.ts +7 -11
- package/src/layouts/PaymentsLayout/types.ts +3 -16
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +45 -16
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +16 -12
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +3 -6
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +50 -30
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +1 -2
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +273 -0
- package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +1 -0
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +5 -17
- package/src/layouts/PaymentsLayout/README.md +0 -133
- package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +0 -172
- package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +0 -100
- package/src/layouts/PaymentsLayout/context/RootPaymentsContext.tsx +0 -129
- package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +0 -109
- package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +0 -194
- package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +0 -3
- package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +0 -19
- package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +0 -103
- package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +0 -29
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Payment Details Dialog
|
|
2
|
+
* Payment Details Dialog (v2.0 - Simplified)
|
|
3
3
|
* Shows payment details with QR code, address, and status
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -19,11 +19,7 @@ import {
|
|
|
19
19
|
import { Copy, ExternalLink, CheckCircle2, Clock, XCircle, AlertCircle, RefreshCw } from 'lucide-react';
|
|
20
20
|
import { Hooks, api } from '@djangocfg/api';
|
|
21
21
|
import type { API } from '@djangocfg/api';
|
|
22
|
-
|
|
23
|
-
export const PAYMENT_DETAILS_EVENTS = {
|
|
24
|
-
OPEN_PAYMENT_DETAILS: 'open-payment-details',
|
|
25
|
-
CLOSE_PAYMENT_DETAILS: 'close-payment-details',
|
|
26
|
-
} as const;
|
|
22
|
+
import { PAYMENT_EVENTS } from '../events';
|
|
27
23
|
|
|
28
24
|
export const PaymentDetailsDialog: React.FC = () => {
|
|
29
25
|
const [open, setOpen] = useState(false);
|
|
@@ -32,25 +28,17 @@ export const PaymentDetailsDialog: React.FC = () => {
|
|
|
32
28
|
const [timeLeft, setTimeLeft] = useState<string>('');
|
|
33
29
|
|
|
34
30
|
// Load payment data by ID using hook
|
|
35
|
-
// Only fetch when dialog is open and paymentId is set
|
|
36
31
|
const shouldFetch = open && !!paymentId;
|
|
37
|
-
const { data: payment, isLoading, error, mutate } = Hooks.
|
|
32
|
+
const { data: payment, isLoading, error, mutate } = Hooks.usePaymentsPaymentsRetrieve(
|
|
38
33
|
shouldFetch ? paymentId : '',
|
|
39
34
|
api as unknown as API
|
|
40
35
|
);
|
|
41
36
|
|
|
42
|
-
//
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
if (error) {
|
|
45
|
-
console.error('Payment loading error:', error);
|
|
46
|
-
}
|
|
47
|
-
}, [error]);
|
|
48
|
-
|
|
37
|
+
// Listen for open/close events
|
|
49
38
|
useEffect(() => {
|
|
50
39
|
const handleOpen = (event: Event) => {
|
|
51
|
-
const customEvent = event as CustomEvent<{
|
|
52
|
-
|
|
53
|
-
setPaymentId(customEvent.detail.paymentId);
|
|
40
|
+
const customEvent = event as CustomEvent<{ id: string }>;
|
|
41
|
+
setPaymentId(customEvent.detail.id);
|
|
54
42
|
setOpen(true);
|
|
55
43
|
};
|
|
56
44
|
|
|
@@ -59,17 +47,18 @@ export const PaymentDetailsDialog: React.FC = () => {
|
|
|
59
47
|
setPaymentId(null);
|
|
60
48
|
};
|
|
61
49
|
|
|
62
|
-
window.addEventListener(
|
|
63
|
-
window.addEventListener(
|
|
50
|
+
window.addEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
51
|
+
window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
|
|
64
52
|
|
|
65
53
|
return () => {
|
|
66
|
-
window.removeEventListener(
|
|
67
|
-
window.removeEventListener(
|
|
54
|
+
window.removeEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
55
|
+
window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
|
|
68
56
|
};
|
|
69
57
|
}, []);
|
|
70
58
|
|
|
71
59
|
const handleClose = () => {
|
|
72
60
|
setOpen(false);
|
|
61
|
+
setPaymentId(null);
|
|
73
62
|
};
|
|
74
63
|
|
|
75
64
|
const handleCopyAddress = async () => {
|
|
@@ -109,15 +98,19 @@ export const PaymentDetailsDialog: React.FC = () => {
|
|
|
109
98
|
|
|
110
99
|
// Get status icon and color
|
|
111
100
|
const getStatusInfo = () => {
|
|
112
|
-
switch (payment?.status) {
|
|
101
|
+
switch (payment?.status?.toLowerCase()) {
|
|
113
102
|
case 'pending':
|
|
114
103
|
return { icon: Clock, color: 'text-yellow-500', bg: 'bg-yellow-500/10' };
|
|
115
104
|
case 'completed':
|
|
105
|
+
case 'success':
|
|
116
106
|
return { icon: CheckCircle2, color: 'text-green-500', bg: 'bg-green-500/10' };
|
|
117
107
|
case 'failed':
|
|
108
|
+
case 'error':
|
|
118
109
|
return { icon: XCircle, color: 'text-red-500', bg: 'bg-red-500/10' };
|
|
119
110
|
case 'expired':
|
|
120
111
|
return { icon: AlertCircle, color: 'text-gray-500', bg: 'bg-gray-500/10' };
|
|
112
|
+
case 'confirming':
|
|
113
|
+
return { icon: RefreshCw, color: 'text-blue-500', bg: 'bg-blue-500/10' };
|
|
121
114
|
default:
|
|
122
115
|
return { icon: Clock, color: 'text-gray-500', bg: 'bg-gray-500/10' };
|
|
123
116
|
}
|
|
@@ -142,7 +135,7 @@ export const PaymentDetailsDialog: React.FC = () => {
|
|
|
142
135
|
);
|
|
143
136
|
}
|
|
144
137
|
|
|
145
|
-
// Error state
|
|
138
|
+
// Error state
|
|
146
139
|
if (shouldFetch && !isLoading && (error || !payment)) {
|
|
147
140
|
return (
|
|
148
141
|
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
|
|
@@ -166,7 +159,7 @@ export const PaymentDetailsDialog: React.FC = () => {
|
|
|
166
159
|
const statusInfo = getStatusInfo();
|
|
167
160
|
const StatusIcon = statusInfo.icon;
|
|
168
161
|
|
|
169
|
-
// Generate QR code URL
|
|
162
|
+
// Generate QR code URL
|
|
170
163
|
const qrCodeUrl = payment.pay_address
|
|
171
164
|
? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}`
|
|
172
165
|
: null;
|
|
@@ -200,29 +193,31 @@ export const PaymentDetailsDialog: React.FC = () => {
|
|
|
200
193
|
<div className="flex items-center justify-between p-4 bg-muted rounded-sm">
|
|
201
194
|
<span className="text-sm text-muted-foreground">Amount to send</span>
|
|
202
195
|
<div className="flex items-center gap-2">
|
|
203
|
-
<TokenIcon symbol={String(payment.
|
|
196
|
+
<TokenIcon symbol={String(payment.currency_code || 'BTC')} size={20} />
|
|
204
197
|
<span className="font-mono font-bold text-lg">
|
|
205
|
-
{payment.
|
|
198
|
+
{payment.pay_amount || '0.00000000'} {payment.currency_code}
|
|
206
199
|
</span>
|
|
207
200
|
</div>
|
|
208
201
|
</div>
|
|
209
202
|
|
|
210
203
|
<div className="flex items-center justify-between px-4">
|
|
211
204
|
<span className="text-sm text-muted-foreground">Equivalent to</span>
|
|
212
|
-
<span className="font-semibold text-lg"
|
|
205
|
+
<span className="font-semibold text-lg">
|
|
206
|
+
${parseFloat(payment.amount_usd || '0').toFixed(2)} USD
|
|
207
|
+
</span>
|
|
213
208
|
</div>
|
|
214
209
|
|
|
215
|
-
{payment.
|
|
210
|
+
{payment.internal_payment_id && (
|
|
216
211
|
<div className="flex items-center justify-between px-4">
|
|
217
212
|
<span className="text-sm text-muted-foreground">Payment Order #</span>
|
|
218
|
-
<span className="font-mono font-medium">{payment.
|
|
213
|
+
<span className="font-mono font-medium">{payment.internal_payment_id}</span>
|
|
219
214
|
</div>
|
|
220
215
|
)}
|
|
221
216
|
|
|
222
|
-
{payment.
|
|
217
|
+
{payment.currency_network && (
|
|
223
218
|
<div className="flex items-center justify-between px-4">
|
|
224
219
|
<span className="text-sm text-muted-foreground">Network</span>
|
|
225
|
-
<span className="font-medium">{payment.
|
|
220
|
+
<span className="font-medium">{payment.currency_network}</span>
|
|
226
221
|
</div>
|
|
227
222
|
)}
|
|
228
223
|
</div>
|
|
@@ -303,21 +298,12 @@ export const PaymentDetailsDialog: React.FC = () => {
|
|
|
303
298
|
<Button variant="outline" onClick={handleClose}>
|
|
304
299
|
Close
|
|
305
300
|
</Button>
|
|
301
|
+
<Button onClick={() => mutate()} variant="ghost" size="sm">
|
|
302
|
+
<RefreshCw className="h-4 w-4 mr-2" />
|
|
303
|
+
Refresh
|
|
304
|
+
</Button>
|
|
306
305
|
</DialogFooter>
|
|
307
306
|
</DialogContent>
|
|
308
307
|
</Dialog>
|
|
309
308
|
);
|
|
310
309
|
};
|
|
311
|
-
|
|
312
|
-
// Helper function to open payment details dialog
|
|
313
|
-
export const openPaymentDetails = (paymentId: string) => {
|
|
314
|
-
window.dispatchEvent(
|
|
315
|
-
new CustomEvent(PAYMENT_DETAILS_EVENTS.OPEN_PAYMENT_DETAILS, {
|
|
316
|
-
detail: { paymentId },
|
|
317
|
-
})
|
|
318
|
-
);
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
export const closePaymentDetails = () => {
|
|
322
|
-
window.dispatchEvent(new Event(PAYMENT_DETAILS_EVENTS.CLOSE_PAYMENT_DETAILS));
|
|
323
|
-
};
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
export { CreateApiKeyDialog } from './CreateApiKeyDialog';
|
|
2
|
-
export { DeleteApiKeyDialog } from './DeleteApiKeyDialog';
|
|
3
1
|
export { CreatePaymentDialog } from './CreatePaymentDialog';
|
|
4
|
-
export { PaymentDetailsDialog
|
|
5
|
-
|
|
2
|
+
export { PaymentDetailsDialog } from './PaymentDetailsDialog';
|
|
@@ -1,106 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Payment Events System (v2.0 - Simplified)
|
|
3
|
+
*
|
|
4
|
+
* Event-based communication for dialogs using DOM events
|
|
5
|
+
* Removed: API Keys events (deprecated)
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
|
-
import { events } from '@djangocfg/ui';
|
|
7
|
-
import type { APIKeyDetail, Payment } from './types';
|
|
8
|
-
|
|
9
8
|
// ─────────────────────────────────────────────────────────────────────────
|
|
10
|
-
//
|
|
9
|
+
// Event Names
|
|
11
10
|
// ─────────────────────────────────────────────────────────────────────────
|
|
12
11
|
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
OPEN_DELETE_APIKEY_DIALOG: 'OPEN_DELETE_APIKEY_DIALOG',
|
|
18
|
-
|
|
19
|
-
// Payments
|
|
20
|
-
OPEN_CREATE_PAYMENT_DIALOG: 'OPEN_CREATE_PAYMENT_DIALOG',
|
|
21
|
-
OPEN_PAYMENT_DETAILS_DIALOG: 'OPEN_PAYMENT_DETAILS_DIALOG',
|
|
22
|
-
OPEN_CANCEL_PAYMENT_DIALOG: 'OPEN_CANCEL_PAYMENT_DIALOG',
|
|
23
|
-
|
|
24
|
-
// Close
|
|
25
|
-
CLOSE_PAYMENTS_DIALOG: 'CLOSE_PAYMENTS_DIALOG',
|
|
12
|
+
export const PAYMENT_EVENTS = {
|
|
13
|
+
OPEN_CREATE_PAYMENT_DIALOG: 'payments:open-create-payment',
|
|
14
|
+
OPEN_PAYMENT_DETAILS_DIALOG: 'payments:open-payment-details',
|
|
15
|
+
CLOSE_DIALOG: 'payments:close-dialog',
|
|
26
16
|
} as const;
|
|
27
17
|
|
|
28
18
|
// ─────────────────────────────────────────────────────────────────────────
|
|
29
19
|
// Event Types
|
|
30
20
|
// ─────────────────────────────────────────────────────────────────────────
|
|
31
21
|
|
|
32
|
-
export
|
|
33
|
-
type:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
keyId?: string;
|
|
37
|
-
keyData?: APIKeyDetail;
|
|
38
|
-
initialKeyData?: Partial<APIKeyDetail>;
|
|
39
|
-
|
|
40
|
-
// Payments
|
|
41
|
-
paymentId?: string;
|
|
42
|
-
paymentData?: Payment;
|
|
43
|
-
initialPaymentData?: Partial<Payment>;
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
48
|
-
// API Keys Events
|
|
49
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
export const openCreateApiKeyDialog = (initialData?: Partial<APIKeyDetail>) => {
|
|
52
|
-
events.publish({
|
|
53
|
-
type: PAYMENTS_DIALOG_EVENTS.OPEN_CREATE_APIKEY_DIALOG,
|
|
54
|
-
payload: { initialKeyData: initialData },
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export const openEditApiKeyDialog = (keyId: string, keyData?: APIKeyDetail) => {
|
|
59
|
-
events.publish({
|
|
60
|
-
type: PAYMENTS_DIALOG_EVENTS.OPEN_EDIT_APIKEY_DIALOG,
|
|
61
|
-
payload: { keyId, keyData },
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const openDeleteApiKeyDialog = (keyId: string, keyData?: APIKeyDetail) => {
|
|
66
|
-
events.publish({
|
|
67
|
-
type: PAYMENTS_DIALOG_EVENTS.OPEN_DELETE_APIKEY_DIALOG,
|
|
68
|
-
payload: { keyId, keyData },
|
|
69
|
-
});
|
|
70
|
-
};
|
|
22
|
+
export type PaymentEvent =
|
|
23
|
+
| { type: 'OPEN_CREATE_PAYMENT' }
|
|
24
|
+
| { type: 'OPEN_PAYMENT_DETAILS'; id: string }
|
|
25
|
+
| { type: 'CLOSE_DIALOG' };
|
|
71
26
|
|
|
72
27
|
// ─────────────────────────────────────────────────────────────────────────
|
|
73
|
-
//
|
|
28
|
+
// Helper Functions
|
|
74
29
|
// ─────────────────────────────────────────────────────────────────────────
|
|
75
30
|
|
|
76
|
-
export const openCreatePaymentDialog = (
|
|
77
|
-
|
|
78
|
-
type: PAYMENTS_DIALOG_EVENTS.OPEN_CREATE_PAYMENT_DIALOG,
|
|
79
|
-
payload: { initialPaymentData: initialData },
|
|
80
|
-
});
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export const openPaymentDetailsDialog = (paymentId: string, paymentData?: Payment) => {
|
|
84
|
-
events.publish({
|
|
85
|
-
type: PAYMENTS_DIALOG_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG,
|
|
86
|
-
payload: { paymentId, paymentData },
|
|
87
|
-
});
|
|
31
|
+
export const openCreatePaymentDialog = () => {
|
|
32
|
+
window.dispatchEvent(new Event(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG));
|
|
88
33
|
};
|
|
89
34
|
|
|
90
|
-
export const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
35
|
+
export const openPaymentDetailsDialog = (id: string) => {
|
|
36
|
+
window.dispatchEvent(
|
|
37
|
+
new CustomEvent(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, {
|
|
38
|
+
detail: { id },
|
|
39
|
+
})
|
|
40
|
+
);
|
|
95
41
|
};
|
|
96
42
|
|
|
97
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
98
|
-
// Close Events
|
|
99
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
100
|
-
|
|
101
43
|
export const closePaymentsDialog = () => {
|
|
102
|
-
|
|
103
|
-
type: PAYMENTS_DIALOG_EVENTS.CLOSE_PAYMENTS_DIALOG,
|
|
104
|
-
});
|
|
44
|
+
window.dispatchEvent(new Event(PAYMENT_EVENTS.CLOSE_DIALOG));
|
|
105
45
|
};
|
|
106
|
-
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Payments Layout
|
|
3
|
-
*
|
|
2
|
+
* Payments Layout (v2.0 - Simplified)
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* - PaymentsLayout: Main layout component
|
|
6
|
+
* - PaymentTab: Tab type definition
|
|
7
|
+
* - Payment events: openCreatePaymentDialog, openPaymentDetailsDialog, closePaymentsDialog
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
export { PaymentsLayout } from './PaymentsLayout';
|
|
7
|
-
export type { PaymentsLayoutProps } from './PaymentsLayout';
|
|
8
11
|
export type { PaymentTab } from './types';
|
|
9
|
-
|
|
10
|
-
// Re-export events for convenience
|
|
11
12
|
export {
|
|
12
|
-
openCreateApiKeyDialog,
|
|
13
|
-
openEditApiKeyDialog,
|
|
14
|
-
openDeleteApiKeyDialog,
|
|
15
13
|
openCreatePaymentDialog,
|
|
16
14
|
openPaymentDetailsDialog,
|
|
17
|
-
|
|
18
|
-
closePaymentsDialog,
|
|
15
|
+
closePaymentsDialog
|
|
19
16
|
} from './events';
|
|
20
|
-
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Payments Layout Types
|
|
2
|
+
* Payments Layout Types (v2.0 - Simplified)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
// Tab types for navigation
|
|
6
|
-
export type PaymentTab = 'overview' | 'payments' | 'transactions'
|
|
7
|
-
|
|
8
|
-
// Re-export API types for convenience
|
|
9
|
-
export type {
|
|
10
|
-
Payment,
|
|
11
|
-
UserBalance,
|
|
12
|
-
Currency,
|
|
13
|
-
APIKeyDetail,
|
|
14
|
-
PaginatedPaymentListList,
|
|
15
|
-
PaginatedUserBalanceList,
|
|
16
|
-
PaginatedCurrencyListList,
|
|
17
|
-
PaginatedAPIKeyListList,
|
|
18
|
-
} from '@djangocfg/api/cfg/contexts';
|
|
19
|
-
|
|
5
|
+
// Tab types for navigation (removed apikeys and tariffs)
|
|
6
|
+
export type PaymentTab = 'overview' | 'payments' | 'transactions';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Balance Card Component
|
|
2
|
+
* Balance Card Component (v2.0 - Simplified)
|
|
3
3
|
* Display user balance with quick actions
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -21,9 +21,9 @@ import { openCreatePaymentDialog } from '../../../events';
|
|
|
21
21
|
|
|
22
22
|
export const BalanceCard: React.FC = () => {
|
|
23
23
|
const {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
balance,
|
|
25
|
+
isLoadingBalance,
|
|
26
|
+
refreshBalance
|
|
27
27
|
} = useOverviewContext();
|
|
28
28
|
|
|
29
29
|
const formatCurrency = (amount?: number | null) => {
|
|
@@ -35,7 +35,20 @@ export const BalanceCard: React.FC = () => {
|
|
|
35
35
|
}).format(amount);
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
const formatDate = (dateStr?: string) => {
|
|
39
|
+
if (!dateStr) return 'No transactions yet';
|
|
40
|
+
try {
|
|
41
|
+
return new Date(dateStr).toLocaleDateString('en-US', {
|
|
42
|
+
year: 'numeric',
|
|
43
|
+
month: 'short',
|
|
44
|
+
day: 'numeric',
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
return 'Invalid date';
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (isLoadingBalance) {
|
|
39
52
|
return (
|
|
40
53
|
<Card>
|
|
41
54
|
<CardHeader>
|
|
@@ -55,6 +68,14 @@ export const BalanceCard: React.FC = () => {
|
|
|
55
68
|
);
|
|
56
69
|
}
|
|
57
70
|
|
|
71
|
+
// Extract balance data from response: { success, balance: { amount_usd, total_deposited, total_withdrawn, last_transaction_at } }
|
|
72
|
+
const balanceData = balance?.balance || balance;
|
|
73
|
+
const amountUsd = balanceData?.amount_usd ?? 0;
|
|
74
|
+
const totalDeposited = balanceData?.total_deposited ?? 0;
|
|
75
|
+
const totalWithdrawn = balanceData?.total_withdrawn ?? 0;
|
|
76
|
+
const lastTransactionAt = balanceData?.last_transaction_at;
|
|
77
|
+
const isEmpty = amountUsd === 0 && totalDeposited === 0;
|
|
78
|
+
|
|
58
79
|
return (
|
|
59
80
|
<Card>
|
|
60
81
|
<CardHeader>
|
|
@@ -64,7 +85,7 @@ export const BalanceCard: React.FC = () => {
|
|
|
64
85
|
Account Balance
|
|
65
86
|
</div>
|
|
66
87
|
<div className="flex items-center gap-2">
|
|
67
|
-
<Button variant="ghost" size="sm" onClick={
|
|
88
|
+
<Button variant="ghost" size="sm" onClick={refreshBalance}>
|
|
68
89
|
<RefreshCw className="h-4 w-4" />
|
|
69
90
|
</Button>
|
|
70
91
|
<Button size="sm" onClick={() => openCreatePaymentDialog()}>
|
|
@@ -77,23 +98,31 @@ export const BalanceCard: React.FC = () => {
|
|
|
77
98
|
<CardContent className="space-y-4">
|
|
78
99
|
<div>
|
|
79
100
|
<div className="text-4xl font-bold">
|
|
80
|
-
{
|
|
101
|
+
{formatCurrency(amountUsd)}
|
|
81
102
|
</div>
|
|
82
103
|
<p className="text-sm text-muted-foreground mt-1">
|
|
83
|
-
|
|
104
|
+
Available balance • Last updated {formatDate(lastTransactionAt)}
|
|
84
105
|
</p>
|
|
85
106
|
</div>
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
<div
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
108
|
+
<div className="grid grid-cols-2 gap-4 pt-4 border-t">
|
|
109
|
+
<div>
|
|
110
|
+
<p className="text-xs text-muted-foreground">Total Deposited</p>
|
|
111
|
+
<p className="text-lg font-semibold text-green-600">{formatCurrency(totalDeposited)}</p>
|
|
112
|
+
</div>
|
|
113
|
+
<div>
|
|
114
|
+
<p className="text-xs text-muted-foreground">Total Withdrawn</p>
|
|
115
|
+
<p className="text-lg font-semibold text-red-600">{formatCurrency(totalWithdrawn)}</p>
|
|
93
116
|
</div>
|
|
94
|
-
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className="flex items-center gap-2">
|
|
120
|
+
<Badge variant={!isEmpty ? 'default' : 'secondary'}>
|
|
121
|
+
{!isEmpty ? 'Active' : 'New Account'}
|
|
122
|
+
</Badge>
|
|
123
|
+
{isEmpty && <Badge variant="outline">Empty Balance</Badge>}
|
|
124
|
+
</div>
|
|
95
125
|
</CardContent>
|
|
96
126
|
</Card>
|
|
97
127
|
);
|
|
98
128
|
};
|
|
99
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Recent Payments Component
|
|
3
|
-
* Display recent payment transactions
|
|
2
|
+
* Recent Payments Component (v2.0 - Simplified)
|
|
3
|
+
* Display recent payment transactions from payments list
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
@@ -20,15 +20,16 @@ import { useOverviewContext } from '@djangocfg/api/cfg/contexts';
|
|
|
20
20
|
import { openPaymentDetailsDialog } from '../../../events';
|
|
21
21
|
|
|
22
22
|
export const RecentPayments: React.FC = () => {
|
|
23
|
-
const {
|
|
23
|
+
const { payments, isLoadingPayments } = useOverviewContext();
|
|
24
24
|
|
|
25
|
-
const formatCurrency = (amount?: number | null) => {
|
|
25
|
+
const formatCurrency = (amount?: number | string | null) => {
|
|
26
26
|
if (amount === null || amount === undefined) return '$0.00';
|
|
27
|
+
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
27
28
|
return new Intl.NumberFormat('en-US', {
|
|
28
29
|
style: 'currency',
|
|
29
30
|
currency: 'USD',
|
|
30
31
|
minimumFractionDigits: 2,
|
|
31
|
-
}).format(
|
|
32
|
+
}).format(numAmount);
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const getRelativeTime = (date: string | null | undefined): string => {
|
|
@@ -52,16 +53,18 @@ export const RecentPayments: React.FC = () => {
|
|
|
52
53
|
case 'success':
|
|
53
54
|
return 'default';
|
|
54
55
|
case 'pending':
|
|
56
|
+
case 'confirming':
|
|
55
57
|
return 'secondary';
|
|
56
58
|
case 'failed':
|
|
57
59
|
case 'error':
|
|
60
|
+
case 'expired':
|
|
58
61
|
return 'destructive';
|
|
59
62
|
default:
|
|
60
63
|
return 'outline';
|
|
61
64
|
}
|
|
62
65
|
};
|
|
63
66
|
|
|
64
|
-
if (
|
|
67
|
+
if (isLoadingPayments) {
|
|
65
68
|
return (
|
|
66
69
|
<Card>
|
|
67
70
|
<CardHeader>
|
|
@@ -85,7 +88,8 @@ export const RecentPayments: React.FC = () => {
|
|
|
85
88
|
);
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
// Get first 5 payments for recent list
|
|
92
|
+
const recentPaymentsList = payments?.results?.slice(0, 5) || [];
|
|
89
93
|
|
|
90
94
|
return (
|
|
91
95
|
<Card>
|
|
@@ -102,18 +106,19 @@ export const RecentPayments: React.FC = () => {
|
|
|
102
106
|
</CardTitle>
|
|
103
107
|
</CardHeader>
|
|
104
108
|
<CardContent>
|
|
105
|
-
{
|
|
109
|
+
{recentPaymentsList.length === 0 ? (
|
|
106
110
|
<div className="text-center py-8 text-muted-foreground">
|
|
107
111
|
<History className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
|
108
112
|
<p>No recent payments</p>
|
|
113
|
+
<p className="text-sm mt-2">Create your first payment to get started</p>
|
|
109
114
|
</div>
|
|
110
115
|
) : (
|
|
111
116
|
<div className="space-y-3">
|
|
112
|
-
{
|
|
117
|
+
{recentPaymentsList.map((payment) => (
|
|
113
118
|
<div
|
|
114
119
|
key={payment.id}
|
|
115
120
|
className="flex items-center justify-between p-3 border rounded-sm hover:bg-accent cursor-pointer transition-colors"
|
|
116
|
-
onClick={() => openPaymentDetailsDialog(payment.id)}
|
|
121
|
+
onClick={() => openPaymentDetailsDialog(String(payment.id))}
|
|
117
122
|
>
|
|
118
123
|
<div className="flex-1">
|
|
119
124
|
<div className="flex items-center gap-2">
|
|
@@ -123,7 +128,7 @@ export const RecentPayments: React.FC = () => {
|
|
|
123
128
|
</Badge>
|
|
124
129
|
</div>
|
|
125
130
|
<p className="text-sm text-muted-foreground">
|
|
126
|
-
{getRelativeTime(payment.created_at)} • {payment.
|
|
131
|
+
{getRelativeTime(payment.created_at)} • {payment.currency_code || 'USD'}
|
|
127
132
|
</p>
|
|
128
133
|
</div>
|
|
129
134
|
<ExternalLink className="h-4 w-4 text-muted-foreground" />
|
|
@@ -135,4 +140,3 @@ export const RecentPayments: React.FC = () => {
|
|
|
135
140
|
</Card>
|
|
136
141
|
);
|
|
137
142
|
};
|
|
138
|
-
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Overview View
|
|
3
|
-
* Dashboard with
|
|
2
|
+
* Overview View (v2.0 - Simplified)
|
|
3
|
+
* Dashboard with balance and recent payments
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
8
|
import React from 'react';
|
|
9
|
-
import {
|
|
9
|
+
import { BalanceCard, RecentPayments } from './components';
|
|
10
10
|
|
|
11
11
|
export const OverviewView: React.FC = () => {
|
|
12
12
|
return (
|
|
13
13
|
<div className="space-y-6">
|
|
14
|
-
<MetricsCards />
|
|
15
|
-
|
|
16
14
|
<div className="grid gap-6 lg:grid-cols-2">
|
|
17
15
|
<BalanceCard />
|
|
18
16
|
<RecentPayments />
|
|
@@ -20,4 +18,3 @@ export const OverviewView: React.FC = () => {
|
|
|
20
18
|
</div>
|
|
21
19
|
);
|
|
22
20
|
};
|
|
23
|
-
|