@djangocfg/layouts 1.0.3 → 1.0.5
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/layouts/AuthLayout/AuthHelp.tsx +2 -2
- package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +6 -6
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +1 -1
- package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +6 -6
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +1 -1
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +43 -133
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenuUserCard.tsx +150 -0
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +2 -2
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +47 -65
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +121 -144
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +103 -48
- 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 +18 -14
- 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 +51 -31
- 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/SupportLayout/hooks/useInfiniteMessages.ts +2 -3
- package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +2 -3
- package/src/snippets/Chat/components/SessionList.tsx +1 -1
- package/src/snippets/Chat/hooks/useInfiniteSessions.ts +2 -2
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +1 -1
- 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 -134
- 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
|
|
|
@@ -15,37 +15,50 @@ import {
|
|
|
15
15
|
DialogTitle,
|
|
16
16
|
Button,
|
|
17
17
|
TokenIcon,
|
|
18
|
-
useEventListener,
|
|
19
18
|
} from '@djangocfg/ui';
|
|
20
|
-
import { Copy, ExternalLink, CheckCircle2, Clock, XCircle, AlertCircle } from 'lucide-react';
|
|
21
|
-
import
|
|
19
|
+
import { Copy, ExternalLink, CheckCircle2, Clock, XCircle, AlertCircle, RefreshCw } from 'lucide-react';
|
|
20
|
+
import { Hooks, api } from '@djangocfg/api';
|
|
21
|
+
import type { API } from '@djangocfg/api';
|
|
22
|
+
import { PAYMENT_EVENTS } from '../events';
|
|
22
23
|
|
|
23
|
-
export const
|
|
24
|
-
OPEN_PAYMENT_DETAILS: 'open-payment-details',
|
|
25
|
-
CLOSE_PAYMENT_DETAILS: 'close-payment-details',
|
|
26
|
-
} as const;
|
|
27
|
-
|
|
28
|
-
export interface PaymentDetailsDialogProps {
|
|
29
|
-
payment?: Payment | null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ payment: initialPayment }) => {
|
|
24
|
+
export const PaymentDetailsDialog: React.FC = () => {
|
|
33
25
|
const [open, setOpen] = useState(false);
|
|
34
|
-
const [
|
|
26
|
+
const [paymentId, setPaymentId] = useState<string | null>(null);
|
|
35
27
|
const [copied, setCopied] = useState(false);
|
|
36
28
|
const [timeLeft, setTimeLeft] = useState<string>('');
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
// Load payment data by ID using hook
|
|
31
|
+
const shouldFetch = open && !!paymentId;
|
|
32
|
+
const { data: payment, isLoading, error, mutate } = Hooks.usePaymentsPaymentsRetrieve(
|
|
33
|
+
shouldFetch ? paymentId : '',
|
|
34
|
+
api as unknown as API
|
|
35
|
+
);
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
// Listen for open/close events
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const handleOpen = (event: Event) => {
|
|
40
|
+
const customEvent = event as CustomEvent<{ id: string }>;
|
|
41
|
+
setPaymentId(customEvent.detail.id);
|
|
42
|
+
setOpen(true);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleClose = () => {
|
|
46
|
+
setOpen(false);
|
|
47
|
+
setPaymentId(null);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
window.addEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
51
|
+
window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
|
|
52
|
+
|
|
53
|
+
return () => {
|
|
54
|
+
window.removeEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
|
|
55
|
+
window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
|
|
56
|
+
};
|
|
57
|
+
}, []);
|
|
46
58
|
|
|
47
59
|
const handleClose = () => {
|
|
48
60
|
setOpen(false);
|
|
61
|
+
setPaymentId(null);
|
|
49
62
|
};
|
|
50
63
|
|
|
51
64
|
const handleCopyAddress = async () => {
|
|
@@ -85,26 +98,68 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
|
|
|
85
98
|
|
|
86
99
|
// Get status icon and color
|
|
87
100
|
const getStatusInfo = () => {
|
|
88
|
-
switch (payment?.status) {
|
|
101
|
+
switch (payment?.status?.toLowerCase()) {
|
|
89
102
|
case 'pending':
|
|
90
103
|
return { icon: Clock, color: 'text-yellow-500', bg: 'bg-yellow-500/10' };
|
|
91
104
|
case 'completed':
|
|
105
|
+
case 'success':
|
|
92
106
|
return { icon: CheckCircle2, color: 'text-green-500', bg: 'bg-green-500/10' };
|
|
93
107
|
case 'failed':
|
|
108
|
+
case 'error':
|
|
94
109
|
return { icon: XCircle, color: 'text-red-500', bg: 'bg-red-500/10' };
|
|
95
110
|
case 'expired':
|
|
96
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' };
|
|
97
114
|
default:
|
|
98
115
|
return { icon: Clock, color: 'text-gray-500', bg: 'bg-gray-500/10' };
|
|
99
116
|
}
|
|
100
117
|
};
|
|
101
118
|
|
|
102
|
-
if (!
|
|
119
|
+
if (!open) return null;
|
|
120
|
+
|
|
121
|
+
// Loading state
|
|
122
|
+
if (isLoading) {
|
|
123
|
+
return (
|
|
124
|
+
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
|
|
125
|
+
<DialogContent className="sm:max-w-lg">
|
|
126
|
+
<DialogHeader>
|
|
127
|
+
<DialogTitle>Payment Details</DialogTitle>
|
|
128
|
+
<DialogDescription>Loading payment information...</DialogDescription>
|
|
129
|
+
</DialogHeader>
|
|
130
|
+
<div className="flex items-center justify-center py-12">
|
|
131
|
+
<RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
132
|
+
</div>
|
|
133
|
+
</DialogContent>
|
|
134
|
+
</Dialog>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Error state
|
|
139
|
+
if (shouldFetch && !isLoading && (error || !payment)) {
|
|
140
|
+
return (
|
|
141
|
+
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
|
|
142
|
+
<DialogContent className="sm:max-w-lg">
|
|
143
|
+
<DialogHeader>
|
|
144
|
+
<DialogTitle>Payment Details</DialogTitle>
|
|
145
|
+
<DialogDescription>Failed to load payment information</DialogDescription>
|
|
146
|
+
</DialogHeader>
|
|
147
|
+
<div className="flex flex-col items-center justify-center py-12 space-y-4">
|
|
148
|
+
<XCircle className="h-12 w-12 text-destructive" />
|
|
149
|
+
<p className="text-sm text-muted-foreground">
|
|
150
|
+
{error ? `Error: ${error}` : 'Payment not found'}
|
|
151
|
+
</p>
|
|
152
|
+
<Button onClick={() => mutate()}>Try Again</Button>
|
|
153
|
+
</div>
|
|
154
|
+
</DialogContent>
|
|
155
|
+
</Dialog>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
103
158
|
|
|
104
159
|
const statusInfo = getStatusInfo();
|
|
105
160
|
const StatusIcon = statusInfo.icon;
|
|
106
161
|
|
|
107
|
-
// Generate QR code URL
|
|
162
|
+
// Generate QR code URL
|
|
108
163
|
const qrCodeUrl = payment.pay_address
|
|
109
164
|
? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}`
|
|
110
165
|
: null;
|
|
@@ -121,7 +176,7 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
|
|
|
121
176
|
|
|
122
177
|
<div className="space-y-6">
|
|
123
178
|
{/* Status Badge */}
|
|
124
|
-
<div className={`flex items-center gap-3 p-4 rounded-
|
|
179
|
+
<div className={`flex items-center gap-3 p-4 rounded-sm ${statusInfo.bg}`}>
|
|
125
180
|
<StatusIcon className={`h-5 w-5 ${statusInfo.color}`} />
|
|
126
181
|
<div className="flex-1">
|
|
127
182
|
<div className="font-semibold capitalize">{payment.status}</div>
|
|
@@ -135,32 +190,41 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
|
|
|
135
190
|
|
|
136
191
|
{/* Amount Information */}
|
|
137
192
|
<div className="space-y-3">
|
|
138
|
-
<div className="flex items-center justify-between p-4 bg-muted rounded-
|
|
193
|
+
<div className="flex items-center justify-between p-4 bg-muted rounded-sm">
|
|
139
194
|
<span className="text-sm text-muted-foreground">Amount to send</span>
|
|
140
195
|
<div className="flex items-center gap-2">
|
|
141
|
-
<TokenIcon symbol={payment.
|
|
196
|
+
<TokenIcon symbol={String(payment.currency_code || 'BTC')} size={20} />
|
|
142
197
|
<span className="font-mono font-bold text-lg">
|
|
143
|
-
{payment.
|
|
198
|
+
{payment.pay_amount || '0.00000000'} {payment.currency_code}
|
|
144
199
|
</span>
|
|
145
200
|
</div>
|
|
146
201
|
</div>
|
|
147
202
|
|
|
148
203
|
<div className="flex items-center justify-between px-4">
|
|
149
204
|
<span className="text-sm text-muted-foreground">Equivalent to</span>
|
|
150
|
-
<span className="font-semibold text-lg"
|
|
205
|
+
<span className="font-semibold text-lg">
|
|
206
|
+
${parseFloat(payment.amount_usd || '0').toFixed(2)} USD
|
|
207
|
+
</span>
|
|
151
208
|
</div>
|
|
152
209
|
|
|
153
|
-
{payment.
|
|
210
|
+
{payment.internal_payment_id && (
|
|
211
|
+
<div className="flex items-center justify-between px-4">
|
|
212
|
+
<span className="text-sm text-muted-foreground">Payment Order #</span>
|
|
213
|
+
<span className="font-mono font-medium">{payment.internal_payment_id}</span>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
|
|
217
|
+
{payment.currency_network && (
|
|
154
218
|
<div className="flex items-center justify-between px-4">
|
|
155
219
|
<span className="text-sm text-muted-foreground">Network</span>
|
|
156
|
-
<span className="font-medium">{payment.
|
|
220
|
+
<span className="font-medium">{payment.currency_network}</span>
|
|
157
221
|
</div>
|
|
158
222
|
)}
|
|
159
223
|
</div>
|
|
160
224
|
|
|
161
225
|
{/* QR Code */}
|
|
162
226
|
{qrCodeUrl && payment.status === 'pending' && (
|
|
163
|
-
<div className="flex justify-center p-6 bg-white rounded-
|
|
227
|
+
<div className="flex justify-center p-6 bg-white rounded-sm">
|
|
164
228
|
<img src={qrCodeUrl} alt="Payment QR Code" className="w-48 h-48" />
|
|
165
229
|
</div>
|
|
166
230
|
)}
|
|
@@ -170,7 +234,7 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
|
|
|
170
234
|
<div className="space-y-2">
|
|
171
235
|
<label className="text-sm font-medium">Payment Address</label>
|
|
172
236
|
<div className="flex items-center gap-2">
|
|
173
|
-
<div className="flex-1 p-3 bg-muted rounded-
|
|
237
|
+
<div className="flex-1 p-3 bg-muted rounded-sm font-mono text-sm break-all">
|
|
174
238
|
{payment.pay_address}
|
|
175
239
|
</div>
|
|
176
240
|
<Button
|
|
@@ -193,7 +257,7 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
|
|
|
193
257
|
{payment.transaction_hash && (
|
|
194
258
|
<div className="space-y-2">
|
|
195
259
|
<label className="text-sm font-medium">Transaction Hash</label>
|
|
196
|
-
<div className="p-3 bg-muted rounded-
|
|
260
|
+
<div className="p-3 bg-muted rounded-sm font-mono text-sm break-all">
|
|
197
261
|
{payment.transaction_hash}
|
|
198
262
|
</div>
|
|
199
263
|
</div>
|
|
@@ -234,21 +298,12 @@ export const PaymentDetailsDialog: React.FC<PaymentDetailsDialogProps> = ({ paym
|
|
|
234
298
|
<Button variant="outline" onClick={handleClose}>
|
|
235
299
|
Close
|
|
236
300
|
</Button>
|
|
301
|
+
<Button onClick={() => mutate()} variant="ghost" size="sm">
|
|
302
|
+
<RefreshCw className="h-4 w-4 mr-2" />
|
|
303
|
+
Refresh
|
|
304
|
+
</Button>
|
|
237
305
|
</DialogFooter>
|
|
238
306
|
</DialogContent>
|
|
239
307
|
</Dialog>
|
|
240
308
|
);
|
|
241
309
|
};
|
|
242
|
-
|
|
243
|
-
// Helper function to open payment details dialog
|
|
244
|
-
export const openPaymentDetails = (payment: Payment) => {
|
|
245
|
-
window.dispatchEvent(
|
|
246
|
-
new CustomEvent(PAYMENT_DETAILS_EVENTS.OPEN_PAYMENT_DETAILS, {
|
|
247
|
-
detail: { payment },
|
|
248
|
-
})
|
|
249
|
-
);
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
export const closePaymentDetails = () => {
|
|
253
|
-
window.dispatchEvent(new Event(PAYMENT_DETAILS_EVENTS.CLOSE_PAYMENT_DETAILS));
|
|
254
|
-
};
|
|
@@ -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>
|
|
@@ -72,7 +75,7 @@ export const RecentPayments: React.FC = () => {
|
|
|
72
75
|
</CardHeader>
|
|
73
76
|
<CardContent className="space-y-3">
|
|
74
77
|
{Array.from({ length: 5 }).map((_, i) => (
|
|
75
|
-
<div key={i} className="flex items-center justify-between p-3 border rounded-
|
|
78
|
+
<div key={i} className="flex items-center justify-between p-3 border rounded-sm">
|
|
76
79
|
<div className="space-y-2">
|
|
77
80
|
<Skeleton className="h-4 w-32" />
|
|
78
81
|
<Skeleton className="h-3 w-24" />
|
|
@@ -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
|
-
className="flex items-center justify-between p-3 border rounded-
|
|
116
|
-
onClick={() => openPaymentDetailsDialog(payment.id)}
|
|
120
|
+
className="flex items-center justify-between p-3 border rounded-sm hover:bg-accent cursor-pointer transition-colors"
|
|
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
|
-
|