@djangocfg/ext-payments 1.0.14 → 1.0.19

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 (76) hide show
  1. package/dist/config.cjs +5 -8
  2. package/dist/config.js +5 -8
  3. package/dist/index.cjs +1906 -1043
  4. package/dist/index.d.cts +644 -59
  5. package/dist/index.d.ts +644 -59
  6. package/dist/index.js +1886 -1040
  7. package/package.json +13 -16
  8. package/src/WalletPage.tsx +100 -0
  9. package/src/api/generated/ext_payments/CLAUDE.md +10 -4
  10. package/src/api/generated/ext_payments/_utils/fetchers/ext_payments__payments.ts +268 -5
  11. package/src/api/generated/ext_payments/_utils/hooks/ext_payments__payments.ts +102 -3
  12. package/src/api/generated/ext_payments/_utils/schemas/Balance.schema.ts +1 -1
  13. package/src/api/generated/ext_payments/_utils/schemas/PaginatedWithdrawalListList.schema.ts +24 -0
  14. package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateRequest.schema.ts +21 -0
  15. package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateResponse.schema.ts +22 -0
  16. package/src/api/generated/ext_payments/_utils/schemas/PaymentDetail.schema.ts +3 -3
  17. package/src/api/generated/ext_payments/_utils/schemas/PaymentList.schema.ts +2 -2
  18. package/src/api/generated/ext_payments/_utils/schemas/Transaction.schema.ts +1 -1
  19. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCancelResponse.schema.ts +22 -0
  20. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateRequest.schema.ts +21 -0
  21. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateResponse.schema.ts +22 -0
  22. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalDetail.schema.ts +42 -0
  23. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalList.schema.ts +29 -0
  24. package/src/api/generated/ext_payments/_utils/schemas/index.ts +8 -0
  25. package/src/api/generated/ext_payments/client.ts +1 -1
  26. package/src/api/generated/ext_payments/enums.ts +36 -0
  27. package/src/api/generated/ext_payments/ext_payments__payments/client.ts +104 -6
  28. package/src/api/generated/ext_payments/ext_payments__payments/models.ts +168 -8
  29. package/src/api/generated/ext_payments/index.ts +1 -1
  30. package/src/api/generated/ext_payments/schema.json +752 -42
  31. package/src/components/ActivityItem.tsx +118 -0
  32. package/src/components/ActivityList.tsx +93 -0
  33. package/src/components/AddFundsSheet.tsx +342 -0
  34. package/src/components/BalanceHero.tsx +102 -0
  35. package/src/components/CurrencyCombobox.tsx +49 -0
  36. package/src/components/PaymentSheet.tsx +352 -0
  37. package/src/components/WithdrawSheet.tsx +355 -0
  38. package/src/components/WithdrawalSheet.tsx +332 -0
  39. package/src/components/index.ts +11 -0
  40. package/src/config.ts +1 -0
  41. package/src/contexts/WalletContext.tsx +356 -0
  42. package/src/contexts/index.ts +13 -42
  43. package/src/contexts/types.ts +43 -37
  44. package/src/hooks/index.ts +3 -20
  45. package/src/hooks/useCurrencyOptions.ts +79 -0
  46. package/src/hooks/useEstimate.ts +113 -0
  47. package/src/hooks/useWithdrawalEstimate.ts +117 -0
  48. package/src/index.ts +9 -18
  49. package/src/types/index.ts +78 -0
  50. package/src/utils/errors.ts +36 -0
  51. package/src/utils/format.ts +65 -0
  52. package/src/utils/index.ts +3 -0
  53. package/src/contexts/BalancesContext.tsx +0 -63
  54. package/src/contexts/CurrenciesContext.tsx +0 -64
  55. package/src/contexts/OverviewContext.tsx +0 -173
  56. package/src/contexts/PaymentsContext.tsx +0 -122
  57. package/src/contexts/PaymentsExtensionProvider.tsx +0 -56
  58. package/src/contexts/README.md +0 -201
  59. package/src/contexts/RootPaymentsContext.tsx +0 -66
  60. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -90
  61. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -274
  62. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -287
  63. package/src/layouts/PaymentsLayout/components/index.ts +0 -2
  64. package/src/layouts/PaymentsLayout/events.ts +0 -47
  65. package/src/layouts/PaymentsLayout/index.ts +0 -16
  66. package/src/layouts/PaymentsLayout/types.ts +0 -6
  67. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -121
  68. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -139
  69. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  70. package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -21
  71. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -279
  72. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  73. package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -18
  74. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -260
  75. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
  76. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -18
@@ -1,90 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Payments Layout (v2.0 - Simplified)
4
- *
5
- * Simplified layout with 3 tabs: Overview, Payments, Transactions
6
- * Removed: API Keys, Tariffs (deprecated in v2.0)
7
- */
8
-
9
- 'use client';
10
-
11
- import { CreditCard, History, Wallet } from 'lucide-react';
12
- import React from 'react';
13
-
14
- import { Tabs, TabsContent, TabsList, TabsTrigger } from '@djangocfg/ui-core';
15
-
16
- import { OverviewProvider, PaymentsProvider, RootPaymentsProvider } from '../../contexts';
17
- import { CreatePaymentDialog, PaymentDetailsDialog } from './components';
18
- import { OverviewView } from './views/overview';
19
- import { PaymentsView } from './views/payments';
20
- import { TransactionsView } from './views/transactions';
21
-
22
- // ─────────────────────────────────────────────────────────────────────────
23
- // Payments Layout
24
- // ─────────────────────────────────────────────────────────────────────────
25
-
26
- export interface PaymentsLayoutProps {
27
- children?: React.ReactNode;
28
- }
29
-
30
- export const PaymentsLayout: React.FC<PaymentsLayoutProps> = () => {
31
- return (
32
- <RootPaymentsProvider>
33
- <div className="h-full p-6 space-y-6">
34
- {/* Page Header */}
35
- <div>
36
- <h1 className="text-3xl font-bold tracking-tight">Payments</h1>
37
- <p className="text-muted-foreground">
38
- Manage your payments, balance, and transaction history
39
- </p>
40
- </div>
41
-
42
- {/* Main Content with Tabs */}
43
- <Tabs defaultValue="overview" className="space-y-6">
44
- <TabsList className="inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground">
45
- <TabsTrigger value="overview" className="inline-flex items-center gap-2 px-3 py-1.5">
46
- <Wallet className="h-4 w-4" />
47
- <span className="hidden sm:inline">Overview</span>
48
- </TabsTrigger>
49
- <TabsTrigger value="payments" className="inline-flex items-center gap-2 px-3 py-1.5">
50
- <CreditCard className="h-4 w-4" />
51
- <span className="hidden sm:inline">Payments</span>
52
- </TabsTrigger>
53
- <TabsTrigger value="transactions" className="inline-flex items-center gap-2 px-3 py-1.5">
54
- <History className="h-4 w-4" />
55
- <span className="hidden sm:inline">Transactions</span>
56
- </TabsTrigger>
57
- </TabsList>
58
-
59
- {/* Overview Tab - Balance + Recent Payments */}
60
- <TabsContent value="overview" className="space-y-6">
61
- <OverviewProvider>
62
- <PaymentsProvider>
63
- <OverviewView />
64
- <CreatePaymentDialog />
65
- </PaymentsProvider>
66
- </OverviewProvider>
67
- </TabsContent>
68
-
69
- {/* Payments Tab - Full Payment List */}
70
- <TabsContent value="payments" className="space-y-6">
71
- <PaymentsProvider>
72
- <PaymentsView />
73
- <CreatePaymentDialog />
74
- </PaymentsProvider>
75
- </TabsContent>
76
-
77
- {/* Transactions Tab - Transaction History */}
78
- <TabsContent value="transactions" className="space-y-6">
79
- <OverviewProvider>
80
- <TransactionsView />
81
- </OverviewProvider>
82
- </TabsContent>
83
- </Tabs>
84
-
85
- {/* Global Payment Details Dialog */}
86
- <PaymentDetailsDialog />
87
- </div>
88
- </RootPaymentsProvider>
89
- );
90
- };
@@ -1,274 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Create Payment Dialog (v2.0 - Simplified)
4
- * Dialog for creating new payments
5
- */
6
-
7
- 'use client';
8
-
9
- import { Plus, RefreshCw } from 'lucide-react';
10
- import React, { useEffect, useMemo, useState } from 'react';
11
- import { useForm } from 'react-hook-form';
12
- import { z } from 'zod';
13
-
14
- import {
15
- Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Form,
16
- FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Input, Select,
17
- SelectContent, SelectItem, SelectTrigger, SelectValue, TokenIcon
18
- } from '@djangocfg/ui-core';
19
- import { zodResolver } from '@hookform/resolvers/zod';
20
-
21
- import { usePaymentsContext, useRootPaymentsContext } from '../../../contexts';
22
- import { paymentsLogger } from '../../../utils/logger';
23
- import { closePaymentsDialog, openPaymentDetailsDialog, PAYMENT_EVENTS } from '../events';
24
-
25
- // Payment creation schema
26
- const PaymentCreateSchema = z.object({
27
- amount_usd: z.number().min(0.01, 'Amount must be at least $0.01'),
28
- currency_code: z.string().min(1, 'Please select a currency'),
29
- });
30
-
31
- type PaymentCreateRequest = z.infer<typeof PaymentCreateSchema>;
32
-
33
- export const CreatePaymentDialog: React.FC = () => {
34
- const [open, setOpen] = useState(false);
35
- const [isSubmitting, setIsSubmitting] = useState(false);
36
-
37
- const { createPayment } = usePaymentsContext();
38
- const { currencies, isLoadingCurrencies } = useRootPaymentsContext();
39
-
40
- const form = useForm<PaymentCreateRequest>({
41
- resolver: zodResolver(PaymentCreateSchema),
42
- defaultValues: {
43
- amount_usd: 10,
44
- currency_code: 'USDT',
45
- },
46
- });
47
-
48
- // Extract currencies list from response (handle different possible structures)
49
- const currenciesList = useMemo(() => {
50
- const data = currencies?.currencies || currencies?.results || currencies || [];
51
- return Array.isArray(data) ? data : [];
52
- }, [currencies]);
53
-
54
- // Get currency options for select
55
- const currencyOptions = useMemo(() => {
56
- return currenciesList
57
- .filter((curr: any) => curr.is_enabled !== false)
58
- .map((curr: any) => ({
59
- code: curr.code || curr.currency_code || curr.symbol,
60
- name: curr.name || curr.code || curr.currency_code,
61
- usd_rate: curr.usd_rate || curr.rate || 1,
62
- network: curr.network || null,
63
- }));
64
- }, [currenciesList]);
65
-
66
- // Calculate crypto amount from USD
67
- const calculateCryptoAmount = useMemo(() => {
68
- const amountUsd = form.watch('amount_usd');
69
- const currencyCode = form.watch('currency_code');
70
- const currency = currencyOptions.find((c: any) => c.code === currencyCode);
71
-
72
- if (!currency || !currency.usd_rate || !amountUsd) {
73
- return null;
74
- }
75
-
76
- const cryptoAmount = amountUsd / currency.usd_rate;
77
- return {
78
- amount: cryptoAmount,
79
- currency: currency.code,
80
- rate: currency.usd_rate,
81
- network: currency.network,
82
- };
83
- }, [form.watch('amount_usd'), form.watch('currency_code'), currencyOptions]);
84
-
85
- // Listen for open/close events
86
- useEffect(() => {
87
- const handleOpen = () => setOpen(true);
88
- const handleClose = () => setOpen(false);
89
-
90
- window.addEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
91
- window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
92
-
93
- return () => {
94
- window.removeEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
95
- window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
96
- };
97
- }, []);
98
-
99
- const handleClose = () => {
100
- setOpen(false);
101
- form.reset();
102
- };
103
-
104
- // Initialize default currency if not set
105
- useEffect(() => {
106
- if (currencyOptions.length > 0 && !form.getValues('currency_code')) {
107
- form.setValue('currency_code', currencyOptions[0].code);
108
- }
109
- }, [currencyOptions, form]);
110
-
111
- const handleSubmit = async (data: PaymentCreateRequest) => {
112
- try {
113
- setIsSubmitting(true);
114
-
115
- const result = await createPayment();
116
- handleClose();
117
- closePaymentsDialog();
118
-
119
- // Extract payment ID from result
120
- const paymentData = result as any;
121
- const paymentId = paymentData?.payment?.id || paymentData?.id;
122
-
123
- if (paymentId) {
124
- openPaymentDetailsDialog(String(paymentId));
125
- }
126
- } catch (error) {
127
- paymentsLogger.error('Failed to create payment:', error);
128
- } finally {
129
- setIsSubmitting(false);
130
- }
131
- };
132
-
133
- return (
134
- <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
135
- <DialogContent className="sm:max-w-md">
136
- <DialogHeader>
137
- <DialogTitle>Create Payment</DialogTitle>
138
- <DialogDescription>
139
- Create a new payment to add funds to your account.
140
- </DialogDescription>
141
- </DialogHeader>
142
-
143
- <Form {...form}>
144
- <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
145
- <FormField
146
- control={form.control}
147
- name="amount_usd"
148
- render={({ field }) => (
149
- <FormItem>
150
- <FormLabel>Amount (USD)</FormLabel>
151
- <FormControl>
152
- <Input
153
- type="number"
154
- step="0.01"
155
- min="0.01"
156
- placeholder="10.00"
157
- {...field}
158
- onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)}
159
- />
160
- </FormControl>
161
- <FormDescription>
162
- The amount you want to pay in USD.
163
- </FormDescription>
164
- <FormMessage />
165
- </FormItem>
166
- )}
167
- />
168
-
169
- <FormField
170
- control={form.control}
171
- name="currency_code"
172
- render={({ field }) => (
173
- <FormItem>
174
- <FormLabel>Currency</FormLabel>
175
- <Select
176
- onValueChange={field.onChange}
177
- defaultValue={field.value}
178
- disabled={isLoadingCurrencies}
179
- >
180
- <FormControl>
181
- <SelectTrigger>
182
- <SelectValue placeholder="Select currency..." />
183
- </SelectTrigger>
184
- </FormControl>
185
- <SelectContent>
186
- {currencyOptions.map((curr: any) => (
187
- <SelectItem key={curr.code} value={curr.code}>
188
- <div className="flex items-center gap-2">
189
- <TokenIcon symbol={curr.code} size={16} />
190
- <span>{curr.code}</span>
191
- {curr.network && (
192
- <span className="text-xs text-muted-foreground">
193
- ({curr.network})
194
- </span>
195
- )}
196
- </div>
197
- </SelectItem>
198
- ))}
199
- </SelectContent>
200
- </Select>
201
- <FormDescription>
202
- The cryptocurrency to use for payment.
203
- </FormDescription>
204
- <FormMessage />
205
- </FormItem>
206
- )}
207
- />
208
-
209
- {/* Conversion Information */}
210
- {calculateCryptoAmount && (
211
- <div className="rounded-sm bg-muted p-4 space-y-3">
212
- {/* Amount to Send in Crypto */}
213
- <div className="flex items-center justify-between">
214
- <span className="text-sm text-muted-foreground">You will send</span>
215
- <div className="flex items-center gap-2">
216
- <TokenIcon symbol={calculateCryptoAmount.currency} size={16} />
217
- <span className="font-mono font-semibold">
218
- {calculateCryptoAmount.amount.toFixed(8)} {calculateCryptoAmount.currency}
219
- </span>
220
- </div>
221
- </div>
222
-
223
- {/* USD Amount Received */}
224
- <div className="flex items-center justify-between">
225
- <span className="text-sm text-muted-foreground">You will receive</span>
226
- <span className="text-lg font-bold">
227
- ${form.watch('amount_usd')?.toFixed(2)} USD
228
- </span>
229
- </div>
230
-
231
- {/* Exchange Rate */}
232
- <div className="flex items-center justify-between text-xs">
233
- <span className="text-muted-foreground">Rate</span>
234
- <span className="font-medium">
235
- 1 {calculateCryptoAmount.currency} = ${calculateCryptoAmount.rate?.toFixed(2)}
236
- </span>
237
- </div>
238
-
239
- {/* Network Info */}
240
- {calculateCryptoAmount.network && (
241
- <div className="border-t pt-3">
242
- <div className="flex items-center justify-between">
243
- <span className="text-sm text-muted-foreground">Network</span>
244
- <span className="text-sm font-medium">{calculateCryptoAmount.network}</span>
245
- </div>
246
- </div>
247
- )}
248
- </div>
249
- )}
250
-
251
- <DialogFooter>
252
- <Button type="button" variant="outline" onClick={handleClose} disabled={isSubmitting}>
253
- Cancel
254
- </Button>
255
- <Button type="submit" disabled={isSubmitting || currencyOptions.length === 0}>
256
- {isSubmitting ? (
257
- <>
258
- <RefreshCw className="h-4 w-4 mr-2 animate-spin" />
259
- Creating...
260
- </>
261
- ) : (
262
- <>
263
- <Plus className="h-4 w-4 mr-2" />
264
- Create Payment
265
- </>
266
- )}
267
- </Button>
268
- </DialogFooter>
269
- </form>
270
- </Form>
271
- </DialogContent>
272
- </Dialog>
273
- );
274
- };
@@ -1,287 +0,0 @@
1
- /**
2
- * Payment Details Dialog (v2.0 - Simplified)
3
- * Shows payment details with QR code, address, and status
4
- */
5
-
6
- 'use client';
7
-
8
- import { AlertCircle, CheckCircle2, Clock, ExternalLink, RefreshCw, XCircle } from 'lucide-react';
9
- import moment from 'moment';
10
- import React, { useEffect, useState } from 'react';
11
-
12
- import {
13
- Button, CopyButton, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader,
14
- DialogTitle, TokenIcon
15
- } from '@djangocfg/ui-core';
16
-
17
- import { apiPayments } from '../../../api';
18
- import { usePaymentsPaymentsRetrieve } from '../../../api/generated/ext_payments/_utils/hooks';
19
- import { PAYMENT_EVENTS } from '../events';
20
-
21
- export const PaymentDetailsDialog: React.FC = () => {
22
- const [open, setOpen] = useState(false);
23
- const [paymentId, setPaymentId] = useState<string | null>(null);
24
- const [timeLeft, setTimeLeft] = useState<string>('');
25
-
26
- // Load payment data by ID using hook
27
- const shouldFetch = open && !!paymentId;
28
- const { data: payment, isLoading, error, mutate } = usePaymentsPaymentsRetrieve(
29
- shouldFetch ? paymentId : '',
30
- apiPayments
31
- );
32
-
33
- // Listen for open/close events
34
- useEffect(() => {
35
- const handleOpen = (event: Event) => {
36
- const customEvent = event as CustomEvent<{ id: string }>;
37
- setPaymentId(customEvent.detail.id);
38
- setOpen(true);
39
- };
40
-
41
- const handleClose = () => {
42
- setOpen(false);
43
- setPaymentId(null);
44
- };
45
-
46
- window.addEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
47
- window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
48
-
49
- return () => {
50
- window.removeEventListener(PAYMENT_EVENTS.OPEN_PAYMENT_DETAILS_DIALOG, handleOpen);
51
- window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
52
- };
53
- }, []);
54
-
55
- const handleClose = () => {
56
- setOpen(false);
57
- setPaymentId(null);
58
- };
59
-
60
- // Calculate time left until expiration
61
- useEffect(() => {
62
- if (!payment?.expires_at) return;
63
-
64
- const updateTimeLeft = () => {
65
- const now = moment();
66
- const expires = moment.utc(payment.expires_at!);
67
- const diff = expires.diff(now);
68
-
69
- if (diff <= 0) {
70
- setTimeLeft('Expired');
71
- return;
72
- }
73
-
74
- const duration = moment.duration(diff);
75
- const hours = Math.floor(duration.asHours());
76
- const minutes = duration.minutes();
77
- const seconds = duration.seconds();
78
-
79
- setTimeLeft(`${hours}h ${minutes}m ${seconds}s`);
80
- };
81
-
82
- updateTimeLeft();
83
- const interval = setInterval(updateTimeLeft, 1000);
84
-
85
- return () => clearInterval(interval);
86
- }, [payment?.expires_at]);
87
-
88
- // Get status icon and color
89
- const getStatusInfo = () => {
90
- switch (payment?.status?.toLowerCase()) {
91
- case 'pending':
92
- return { icon: Clock, color: 'text-yellow-500', bg: 'bg-yellow-500/10' };
93
- case 'completed':
94
- case 'success':
95
- return { icon: CheckCircle2, color: 'text-green-500', bg: 'bg-green-500/10' };
96
- case 'failed':
97
- case 'error':
98
- return { icon: XCircle, color: 'text-red-500', bg: 'bg-red-500/10' };
99
- case 'expired':
100
- return { icon: AlertCircle, color: 'text-gray-500', bg: 'bg-gray-500/10' };
101
- case 'confirming':
102
- return { icon: RefreshCw, color: 'text-blue-500', bg: 'bg-blue-500/10' };
103
- default:
104
- return { icon: Clock, color: 'text-gray-500', bg: 'bg-gray-500/10' };
105
- }
106
- };
107
-
108
- if (!open) return null;
109
-
110
- // Loading state
111
- if (isLoading) {
112
- return (
113
- <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
114
- <DialogContent className="sm:max-w-lg">
115
- <DialogHeader>
116
- <DialogTitle>Payment Details</DialogTitle>
117
- <DialogDescription>Loading payment information...</DialogDescription>
118
- </DialogHeader>
119
- <div className="flex items-center justify-center py-12">
120
- <RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" />
121
- </div>
122
- </DialogContent>
123
- </Dialog>
124
- );
125
- }
126
-
127
- // Error state
128
- if (shouldFetch && !isLoading && (error || !payment)) {
129
- return (
130
- <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
131
- <DialogContent className="sm:max-w-lg">
132
- <DialogHeader>
133
- <DialogTitle>Payment Details</DialogTitle>
134
- <DialogDescription>Failed to load payment information</DialogDescription>
135
- </DialogHeader>
136
- <div className="flex flex-col items-center justify-center py-12 space-y-4">
137
- <XCircle className="h-12 w-12 text-destructive" />
138
- <p className="text-sm text-muted-foreground">
139
- {error ? `Error: ${error}` : 'Payment not found'}
140
- </p>
141
- <Button onClick={() => mutate()}>Try Again</Button>
142
- </div>
143
- </DialogContent>
144
- </Dialog>
145
- );
146
- }
147
-
148
- const statusInfo = getStatusInfo();
149
- const StatusIcon = statusInfo.icon;
150
-
151
- // Generate QR code URL
152
- const qrCodeUrl = payment.pay_address
153
- ? `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(payment.pay_address)}`
154
- : null;
155
-
156
- return (
157
- <Dialog open={open} onOpenChange={(isOpen) => !isOpen && handleClose()}>
158
- <DialogContent className="sm:max-w-lg">
159
- <DialogHeader>
160
- <DialogTitle>Payment Details</DialogTitle>
161
- <DialogDescription>
162
- Send cryptocurrency to complete your payment
163
- </DialogDescription>
164
- </DialogHeader>
165
-
166
- <div className="space-y-6">
167
- {/* Status Badge */}
168
- <div className={`flex items-center gap-3 p-4 rounded-sm ${statusInfo.bg}`}>
169
- <StatusIcon className={`h-5 w-5 ${statusInfo.color}`} />
170
- <div className="flex-1">
171
- <div className="font-semibold capitalize">{payment.status}</div>
172
- {payment.status === 'pending' && timeLeft && (
173
- <div className="text-sm text-muted-foreground">
174
- Expires in {timeLeft}
175
- </div>
176
- )}
177
- </div>
178
- </div>
179
-
180
- {/* Amount Information */}
181
- <div className="space-y-3">
182
- <div className="flex items-center justify-between p-4 bg-muted rounded-sm">
183
- <span className="text-sm text-muted-foreground">Amount to send</span>
184
- <div className="flex items-center gap-2">
185
- <TokenIcon symbol={String(payment.currency_code || 'BTC')} size={20} />
186
- <span className="font-mono font-bold text-lg">
187
- {payment.pay_amount || '0.00000000'} {payment.currency_code}
188
- </span>
189
- </div>
190
- </div>
191
-
192
- <div className="flex items-center justify-between px-4">
193
- <span className="text-sm text-muted-foreground">Equivalent to</span>
194
- <span className="font-semibold text-lg">
195
- ${parseFloat(payment.amount_usd || '0').toFixed(2)} USD
196
- </span>
197
- </div>
198
-
199
- {payment.internal_payment_id && (
200
- <div className="flex items-center justify-between px-4">
201
- <span className="text-sm text-muted-foreground">Payment Order #</span>
202
- <span className="font-mono font-medium">{payment.internal_payment_id}</span>
203
- </div>
204
- )}
205
-
206
- {payment.currency_network && (
207
- <div className="flex items-center justify-between px-4">
208
- <span className="text-sm text-muted-foreground">Network</span>
209
- <span className="font-medium">{payment.currency_network}</span>
210
- </div>
211
- )}
212
- </div>
213
-
214
- {/* QR Code */}
215
- {qrCodeUrl && payment.status === 'pending' && (
216
- <div className="flex justify-center p-6 bg-white rounded-sm">
217
- <img src={qrCodeUrl} alt="Payment QR Code" className="w-48 h-48" />
218
- </div>
219
- )}
220
-
221
- {/* Payment Address */}
222
- {payment.pay_address && payment.status === 'pending' && (
223
- <div className="space-y-2">
224
- <label className="text-sm font-medium">Payment Address</label>
225
- <div className="flex items-center gap-2">
226
- <div className="flex-1 p-3 bg-muted rounded-sm font-mono text-sm break-all">
227
- {payment.pay_address}
228
- </div>
229
- <CopyButton value={payment.pay_address} variant="outline" />
230
- </div>
231
- </div>
232
- )}
233
-
234
- {/* Transaction Hash */}
235
- {payment.transaction_hash && (
236
- <div className="space-y-2">
237
- <label className="text-sm font-medium">Transaction Hash</label>
238
- <div className="p-3 bg-muted rounded-sm font-mono text-sm break-all">
239
- {payment.transaction_hash}
240
- </div>
241
- </div>
242
- )}
243
-
244
- {/* Payment URL */}
245
- {payment.payment_url && payment.status === 'pending' && (
246
- <Button
247
- variant="outline"
248
- className="w-full"
249
- onClick={() => window.open(payment.payment_url!, '_blank')}
250
- >
251
- <ExternalLink className="h-4 w-4 mr-2" />
252
- Open in Payment Provider
253
- </Button>
254
- )}
255
-
256
- {/* Additional Info */}
257
- <div className="pt-4 border-t space-y-2 text-xs text-muted-foreground">
258
- <div className="flex justify-between">
259
- <span>Payment ID</span>
260
- <span className="font-mono">{payment.id}</span>
261
- </div>
262
- <div className="flex justify-between">
263
- <span>Created</span>
264
- <span>{moment.utc(payment.created_at!).local().format('MMM D, YYYY HH:mm')}</span>
265
- </div>
266
- {payment.confirmations_count !== undefined && (
267
- <div className="flex justify-between">
268
- <span>Confirmations</span>
269
- <span>{payment.confirmations_count}</span>
270
- </div>
271
- )}
272
- </div>
273
- </div>
274
-
275
- <DialogFooter>
276
- <Button variant="outline" onClick={handleClose}>
277
- Close
278
- </Button>
279
- <Button onClick={() => mutate()} variant="ghost" size="sm">
280
- <RefreshCw className="h-4 w-4 mr-2" />
281
- Refresh
282
- </Button>
283
- </DialogFooter>
284
- </DialogContent>
285
- </Dialog>
286
- );
287
- };
@@ -1,2 +0,0 @@
1
- export { CreatePaymentDialog } from './CreatePaymentDialog';
2
- export { PaymentDetailsDialog } from './PaymentDetailsDialog';