@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Layout system and components for Unrealon applications",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "DjangoCFG",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"check": "tsc --noEmit"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@djangocfg/api": "^1.0.
|
|
57
|
-
"@djangocfg/og-image": "^1.0.
|
|
58
|
-
"@djangocfg/ui": "^1.0.
|
|
56
|
+
"@djangocfg/api": "^1.0.6",
|
|
57
|
+
"@djangocfg/og-image": "^1.0.6",
|
|
58
|
+
"@djangocfg/ui": "^1.0.6",
|
|
59
59
|
"@hookform/resolvers": "^5.2.0",
|
|
60
60
|
"consola": "^3.4.2",
|
|
61
61
|
"lucide-react": "^0.468.0",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"vidstack": "0.6.15"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@djangocfg/typescript-config": "^1.0.
|
|
79
|
+
"@djangocfg/typescript-config": "^1.0.6",
|
|
80
80
|
"@types/node": "^24.7.2",
|
|
81
81
|
"@types/react": "19.2.2",
|
|
82
82
|
"@types/react-dom": "19.2.1",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
'use client';
|
|
19
19
|
|
|
20
|
-
import React, { ReactNode,
|
|
20
|
+
import React, { ReactNode, useEffect, useState } from 'react';
|
|
21
21
|
import { useRouter } from 'next/router';
|
|
22
22
|
import { AppContextProvider } from './context';
|
|
23
23
|
import { CoreProviders } from './providers';
|
|
@@ -50,41 +50,35 @@ function LayoutRouter({ children }: { children: ReactNode }) {
|
|
|
50
50
|
setIsMounted(true);
|
|
51
51
|
}, []);
|
|
52
52
|
|
|
53
|
-
//
|
|
54
|
-
const
|
|
53
|
+
// Determine layout mode based on route (synchronous - works with SSR)
|
|
54
|
+
const getLayoutMode = (): 'public' | 'private' | 'auth' => {
|
|
55
|
+
if (router.pathname.startsWith('/auth')) return 'auth';
|
|
56
|
+
if (router.pathname.startsWith('/private')) return 'private';
|
|
57
|
+
return 'public';
|
|
58
|
+
};
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
// This will be properly determined by AppContext
|
|
58
|
-
// For now, simple detection
|
|
59
|
-
if (router.pathname.startsWith('/auth')) {
|
|
60
|
-
setLayoutMode('auth');
|
|
61
|
-
} else if (router.pathname.startsWith('/private')) {
|
|
62
|
-
setLayoutMode('private');
|
|
63
|
-
} else {
|
|
64
|
-
setLayoutMode('public');
|
|
65
|
-
}
|
|
66
|
-
}, [router.pathname]);
|
|
67
|
-
|
|
68
|
-
// Show loading during SSR or auth check
|
|
69
|
-
if (!isMounted || isLoading) {
|
|
70
|
-
return (
|
|
71
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
72
|
-
<div className="text-muted-foreground">Loading...</div>
|
|
73
|
-
</div>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
60
|
+
const layoutMode = getLayoutMode();
|
|
76
61
|
|
|
77
62
|
// Render appropriate layout
|
|
78
63
|
switch (layoutMode) {
|
|
64
|
+
// Public routes: render immediately (SSR enabled)
|
|
65
|
+
case 'public':
|
|
66
|
+
return <PublicLayout>{children}</PublicLayout>;
|
|
67
|
+
|
|
68
|
+
// Auth routes: render immediately (SSR enabled)
|
|
79
69
|
case 'auth':
|
|
80
70
|
return <AuthLayout>{children}</AuthLayout>;
|
|
81
71
|
|
|
72
|
+
// Private routes: wait for client-side hydration and auth check
|
|
82
73
|
case 'private':
|
|
74
|
+
if (!isMounted || isLoading) {
|
|
75
|
+
return (
|
|
76
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
77
|
+
<div className="text-muted-foreground">Loading...</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
83
81
|
return <PrivateLayout>{children}</PrivateLayout>;
|
|
84
|
-
|
|
85
|
-
case 'public':
|
|
86
|
-
default:
|
|
87
|
-
return <PublicLayout>{children}</PublicLayout>;
|
|
88
82
|
}
|
|
89
83
|
}
|
|
90
84
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Payments Layout
|
|
3
|
-
*
|
|
2
|
+
* Payments Layout (v2.0 - Simplified)
|
|
3
|
+
*
|
|
4
|
+
* Simplified layout with 3 tabs: Overview, Payments, Transactions
|
|
5
|
+
* Removed: API Keys, Tariffs (deprecated in v2.0)
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
'use client';
|
|
@@ -8,18 +10,15 @@
|
|
|
8
10
|
import React from 'react';
|
|
9
11
|
import {
|
|
10
12
|
PaymentsProvider,
|
|
11
|
-
ApiKeysProvider,
|
|
12
13
|
OverviewProvider,
|
|
13
14
|
RootPaymentsProvider,
|
|
14
15
|
} from '@djangocfg/api/cfg/contexts';
|
|
15
16
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@djangocfg/ui';
|
|
16
|
-
import { Wallet, CreditCard, History
|
|
17
|
+
import { Wallet, CreditCard, History } from 'lucide-react';
|
|
17
18
|
import { OverviewView } from './views/overview';
|
|
18
19
|
import { PaymentsView } from './views/payments';
|
|
19
20
|
import { TransactionsView } from './views/transactions';
|
|
20
|
-
import {
|
|
21
|
-
import { TariffsView } from './views/tariffs';
|
|
22
|
-
import { CreateApiKeyDialog, DeleteApiKeyDialog, CreatePaymentDialog, PaymentDetailsDialog } from './components';
|
|
21
|
+
import { CreatePaymentDialog, PaymentDetailsDialog } from './components';
|
|
23
22
|
|
|
24
23
|
// ─────────────────────────────────────────────────────────────────────────
|
|
25
24
|
// Payments Layout
|
|
@@ -35,75 +34,58 @@ export const PaymentsLayout: React.FC<PaymentsLayoutProps> = () => {
|
|
|
35
34
|
<div className="h-full p-6 space-y-6">
|
|
36
35
|
{/* Page Header */}
|
|
37
36
|
<div>
|
|
38
|
-
<h1 className="text-3xl font-bold tracking-tight">Payments
|
|
37
|
+
<h1 className="text-3xl font-bold tracking-tight">Payments</h1>
|
|
39
38
|
<p className="text-muted-foreground">
|
|
40
|
-
Manage your payments,
|
|
39
|
+
Manage your payments, balance, and transaction history
|
|
41
40
|
</p>
|
|
42
41
|
</div>
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<Key className="h-4 w-4" />
|
|
61
|
-
<span className="hidden sm:inline">API Keys</span>
|
|
62
|
-
</TabsTrigger>
|
|
63
|
-
<TabsTrigger value="tariffs" className="inline-flex items-center gap-2 px-3 py-1.5">
|
|
64
|
-
<Crown className="h-4 w-4" />
|
|
65
|
-
<span className="hidden sm:inline">Tariffs</span>
|
|
66
|
-
</TabsTrigger>
|
|
67
|
-
</TabsList>
|
|
43
|
+
{/* Main Content with Tabs */}
|
|
44
|
+
<Tabs defaultValue="overview" className="space-y-6">
|
|
45
|
+
<TabsList className="inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground">
|
|
46
|
+
<TabsTrigger value="overview" className="inline-flex items-center gap-2 px-3 py-1.5">
|
|
47
|
+
<Wallet className="h-4 w-4" />
|
|
48
|
+
<span className="hidden sm:inline">Overview</span>
|
|
49
|
+
</TabsTrigger>
|
|
50
|
+
<TabsTrigger value="payments" className="inline-flex items-center gap-2 px-3 py-1.5">
|
|
51
|
+
<CreditCard className="h-4 w-4" />
|
|
52
|
+
<span className="hidden sm:inline">Payments</span>
|
|
53
|
+
</TabsTrigger>
|
|
54
|
+
<TabsTrigger value="transactions" className="inline-flex items-center gap-2 px-3 py-1.5">
|
|
55
|
+
<History className="h-4 w-4" />
|
|
56
|
+
<span className="hidden sm:inline">Transactions</span>
|
|
57
|
+
</TabsTrigger>
|
|
58
|
+
</TabsList>
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
{/* Overview Tab - Balance + Recent Payments */}
|
|
61
|
+
<TabsContent value="overview" className="space-y-6">
|
|
62
|
+
<OverviewProvider>
|
|
63
|
+
<PaymentsProvider>
|
|
64
|
+
<OverviewView />
|
|
65
|
+
<CreatePaymentDialog />
|
|
66
|
+
</PaymentsProvider>
|
|
67
|
+
</OverviewProvider>
|
|
68
|
+
</TabsContent>
|
|
69
|
+
|
|
70
|
+
{/* Payments Tab - Full Payment List */}
|
|
71
|
+
<TabsContent value="payments" className="space-y-6">
|
|
72
72
|
<PaymentsProvider>
|
|
73
|
-
<
|
|
73
|
+
<PaymentsView />
|
|
74
74
|
<CreatePaymentDialog />
|
|
75
75
|
</PaymentsProvider>
|
|
76
|
-
</
|
|
77
|
-
</TabsContent>
|
|
78
|
-
|
|
79
|
-
<TabsContent value="payments" className="space-y-6">
|
|
80
|
-
<PaymentsProvider>
|
|
81
|
-
<PaymentsView />
|
|
82
|
-
<CreatePaymentDialog />
|
|
83
|
-
</PaymentsProvider>
|
|
84
|
-
</TabsContent>
|
|
85
|
-
|
|
86
|
-
<TabsContent value="transactions" className="space-y-6">
|
|
87
|
-
<TransactionsView />
|
|
88
|
-
</TabsContent>
|
|
76
|
+
</TabsContent>
|
|
89
77
|
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
</
|
|
96
|
-
</
|
|
78
|
+
{/* Transactions Tab - Transaction History */}
|
|
79
|
+
<TabsContent value="transactions" className="space-y-6">
|
|
80
|
+
<OverviewProvider>
|
|
81
|
+
<TransactionsView />
|
|
82
|
+
</OverviewProvider>
|
|
83
|
+
</TabsContent>
|
|
84
|
+
</Tabs>
|
|
97
85
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
</TabsContent>
|
|
101
|
-
</Tabs>
|
|
102
|
-
|
|
103
|
-
{/* Global Payment Details Dialog */}
|
|
104
|
-
<PaymentDetailsDialog />
|
|
86
|
+
{/* Global Payment Details Dialog */}
|
|
87
|
+
<PaymentDetailsDialog />
|
|
105
88
|
</div>
|
|
106
89
|
</RootPaymentsProvider>
|
|
107
90
|
);
|
|
108
91
|
};
|
|
109
|
-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Create Payment Dialog
|
|
2
|
+
* Create Payment Dialog (v2.0 - Simplified)
|
|
3
3
|
* Dialog for creating new payments
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -21,115 +21,95 @@ import {
|
|
|
21
21
|
FormLabel,
|
|
22
22
|
FormMessage,
|
|
23
23
|
Input,
|
|
24
|
-
|
|
24
|
+
Select,
|
|
25
|
+
SelectContent,
|
|
26
|
+
SelectItem,
|
|
27
|
+
SelectTrigger,
|
|
28
|
+
SelectValue,
|
|
25
29
|
Button,
|
|
26
30
|
TokenIcon,
|
|
27
|
-
useEventListener,
|
|
28
31
|
} from '@djangocfg/ui';
|
|
29
32
|
import { Plus, RefreshCw } from 'lucide-react';
|
|
30
33
|
import { useForm } from 'react-hook-form';
|
|
31
34
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
35
|
+
import { z } from 'zod';
|
|
32
36
|
import { usePaymentsContext, useRootPaymentsContext } from '@djangocfg/api/cfg/contexts';
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { PAYMENTS_DIALOG_EVENTS, closePaymentsDialog } from '../events';
|
|
36
|
-
import { openPaymentDetails } from './PaymentDetailsDialog';
|
|
37
|
-
import type { ProviderCurrency } from '@djangocfg/api/cfg/contexts';
|
|
38
|
-
import type { ComboboxOption } from '@djangocfg/ui';
|
|
37
|
+
import { PAYMENT_EVENTS, closePaymentsDialog } from '../events';
|
|
38
|
+
import { openPaymentDetailsDialog } from '../events';
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// Payment creation schema
|
|
41
|
+
const PaymentCreateSchema = z.object({
|
|
42
|
+
amount_usd: z.number().min(0.01, 'Amount must be at least $0.01'),
|
|
43
|
+
currency_code: z.string().min(1, 'Please select a currency'),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
type PaymentCreateRequest = z.infer<typeof PaymentCreateSchema>;
|
|
42
47
|
|
|
43
48
|
export const CreatePaymentDialog: React.FC = () => {
|
|
44
49
|
const [open, setOpen] = useState(false);
|
|
45
50
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
46
51
|
|
|
47
52
|
const { createPayment } = usePaymentsContext();
|
|
48
|
-
const {
|
|
49
|
-
providerCurrencies,
|
|
50
|
-
isLoadingProviderCurrencies,
|
|
51
|
-
} = useRootPaymentsContext();
|
|
53
|
+
const { currencies, isLoadingCurrencies } = useRootPaymentsContext();
|
|
52
54
|
|
|
53
55
|
const form = useForm<PaymentCreateRequest>({
|
|
54
|
-
resolver: zodResolver(
|
|
56
|
+
resolver: zodResolver(PaymentCreateSchema),
|
|
55
57
|
defaultValues: {
|
|
56
58
|
amount_usd: 10,
|
|
57
|
-
currency_code: 'USDT',
|
|
59
|
+
currency_code: 'USDT',
|
|
58
60
|
},
|
|
59
61
|
});
|
|
60
62
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return Array.from(currencyMap.values()).map(({ pc, networks }) => {
|
|
82
|
-
// Show network info only if there's exactly one network, otherwise backend will choose
|
|
83
|
-
let label = pc.currency.code;
|
|
84
|
-
let description = pc.currency.name;
|
|
85
|
-
|
|
86
|
-
if (networks.length === 1) {
|
|
87
|
-
label = `${pc.currency.code} (${networks[0]})`;
|
|
88
|
-
} else if (networks.length > 1) {
|
|
89
|
-
description = `${pc.currency.name} • Multiple networks available`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
// Use currency code (e.g. "USDT") as value for API
|
|
94
|
-
value: pc.currency.code.toUpperCase(),
|
|
95
|
-
label,
|
|
96
|
-
description,
|
|
97
|
-
};
|
|
98
|
-
});
|
|
99
|
-
}, [providerCurrencies]);
|
|
100
|
-
|
|
101
|
-
// Get first ProviderCurrency by currency code (e.g. "USDT")
|
|
102
|
-
const getProviderCurrency = (currencyCode: string) => {
|
|
103
|
-
return providerCurrencies?.results?.find(
|
|
104
|
-
pc => pc.is_enabled && pc.currency.code.toUpperCase() === currencyCode.toUpperCase()
|
|
105
|
-
);
|
|
106
|
-
};
|
|
63
|
+
// Extract currencies list from response (handle different possible structures)
|
|
64
|
+
const currenciesList = useMemo(() => {
|
|
65
|
+
const data = currencies?.currencies || currencies?.results || currencies || [];
|
|
66
|
+
return Array.isArray(data) ? data : [];
|
|
67
|
+
}, [currencies]);
|
|
68
|
+
|
|
69
|
+
// Get currency options for select
|
|
70
|
+
const currencyOptions = useMemo(() => {
|
|
71
|
+
return currenciesList
|
|
72
|
+
.filter((curr: any) => curr.is_enabled !== false)
|
|
73
|
+
.map((curr: any) => ({
|
|
74
|
+
code: curr.code || curr.currency_code || curr.symbol,
|
|
75
|
+
name: curr.name || curr.code || curr.currency_code,
|
|
76
|
+
usd_rate: curr.usd_rate || curr.rate || 1,
|
|
77
|
+
network: curr.network || null,
|
|
78
|
+
}));
|
|
79
|
+
}, [currenciesList]);
|
|
107
80
|
|
|
108
81
|
// Calculate crypto amount from USD
|
|
109
82
|
const calculateCryptoAmount = useMemo(() => {
|
|
110
83
|
const amountUsd = form.watch('amount_usd');
|
|
111
84
|
const currencyCode = form.watch('currency_code');
|
|
112
|
-
const
|
|
85
|
+
const currency = currencyOptions.find((c: any) => c.code === currencyCode);
|
|
113
86
|
|
|
114
|
-
if (!
|
|
87
|
+
if (!currency || !currency.usd_rate || !amountUsd) {
|
|
115
88
|
return null;
|
|
116
89
|
}
|
|
117
90
|
|
|
118
|
-
const cryptoAmount = amountUsd /
|
|
91
|
+
const cryptoAmount = amountUsd / currency.usd_rate;
|
|
119
92
|
return {
|
|
120
93
|
amount: cryptoAmount,
|
|
121
|
-
currency:
|
|
122
|
-
|
|
94
|
+
currency: currency.code,
|
|
95
|
+
rate: currency.usd_rate,
|
|
96
|
+
network: currency.network,
|
|
123
97
|
};
|
|
124
|
-
}, [form.watch('amount_usd'), form.watch('currency_code'),
|
|
98
|
+
}, [form.watch('amount_usd'), form.watch('currency_code'), currencyOptions]);
|
|
125
99
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
100
|
+
// Listen for open/close events
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
const handleOpen = () => setOpen(true);
|
|
103
|
+
const handleClose = () => setOpen(false);
|
|
129
104
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
105
|
+
window.addEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
|
|
106
|
+
window.addEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
|
|
107
|
+
|
|
108
|
+
return () => {
|
|
109
|
+
window.removeEventListener(PAYMENT_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, handleOpen);
|
|
110
|
+
window.removeEventListener(PAYMENT_EVENTS.CLOSE_DIALOG, handleClose);
|
|
111
|
+
};
|
|
112
|
+
}, []);
|
|
133
113
|
|
|
134
114
|
const handleClose = () => {
|
|
135
115
|
setOpen(false);
|
|
@@ -139,7 +119,7 @@ export const CreatePaymentDialog: React.FC = () => {
|
|
|
139
119
|
// Initialize default currency if not set
|
|
140
120
|
useEffect(() => {
|
|
141
121
|
if (currencyOptions.length > 0 && !form.getValues('currency_code')) {
|
|
142
|
-
form.setValue('currency_code', currencyOptions[0].
|
|
122
|
+
form.setValue('currency_code', currencyOptions[0].code);
|
|
143
123
|
}
|
|
144
124
|
}, [currencyOptions, form]);
|
|
145
125
|
|
|
@@ -147,20 +127,19 @@ export const CreatePaymentDialog: React.FC = () => {
|
|
|
147
127
|
try {
|
|
148
128
|
setIsSubmitting(true);
|
|
149
129
|
|
|
150
|
-
const result = await createPayment(
|
|
130
|
+
const result = await createPayment();
|
|
151
131
|
handleClose();
|
|
152
132
|
closePaymentsDialog();
|
|
153
133
|
|
|
154
|
-
//
|
|
155
|
-
// Extract the payment ID from the result
|
|
134
|
+
// Extract payment ID from result
|
|
156
135
|
const paymentData = result as any;
|
|
157
136
|
const paymentId = paymentData?.payment?.id || paymentData?.id;
|
|
158
137
|
|
|
159
138
|
if (paymentId) {
|
|
160
|
-
|
|
139
|
+
openPaymentDetailsDialog(String(paymentId));
|
|
161
140
|
}
|
|
162
141
|
} catch (error) {
|
|
163
|
-
|
|
142
|
+
console.error('Failed to create payment:', error);
|
|
164
143
|
} finally {
|
|
165
144
|
setIsSubmitting(false);
|
|
166
145
|
}
|
|
@@ -208,49 +187,32 @@ export const CreatePaymentDialog: React.FC = () => {
|
|
|
208
187
|
render={({ field }) => (
|
|
209
188
|
<FormItem>
|
|
210
189
|
<FormLabel>Currency</FormLabel>
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (!pc) return option.label;
|
|
225
|
-
return (
|
|
190
|
+
<Select
|
|
191
|
+
onValueChange={field.onChange}
|
|
192
|
+
defaultValue={field.value}
|
|
193
|
+
disabled={isLoadingCurrencies}
|
|
194
|
+
>
|
|
195
|
+
<FormControl>
|
|
196
|
+
<SelectTrigger>
|
|
197
|
+
<SelectValue placeholder="Select currency..." />
|
|
198
|
+
</SelectTrigger>
|
|
199
|
+
</FormControl>
|
|
200
|
+
<SelectContent>
|
|
201
|
+
{currencyOptions.map((curr: any) => (
|
|
202
|
+
<SelectItem key={curr.code} value={curr.code}>
|
|
226
203
|
<div className="flex items-center gap-2">
|
|
227
|
-
<TokenIcon symbol={
|
|
228
|
-
<span>{
|
|
229
|
-
{
|
|
204
|
+
<TokenIcon symbol={curr.code} size={16} />
|
|
205
|
+
<span>{curr.code}</span>
|
|
206
|
+
{curr.network && (
|
|
230
207
|
<span className="text-xs text-muted-foreground">
|
|
231
|
-
({
|
|
208
|
+
({curr.network})
|
|
232
209
|
</span>
|
|
233
210
|
)}
|
|
234
211
|
</div>
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (!pc) return option.label;
|
|
240
|
-
return (
|
|
241
|
-
<div className="flex items-center gap-2 flex-1 min-w-0">
|
|
242
|
-
<TokenIcon symbol={pc.currency.code} size={20} className="shrink-0" />
|
|
243
|
-
<div className="flex flex-col flex-1 min-w-0">
|
|
244
|
-
<span className="font-medium truncate">
|
|
245
|
-
{pc.currency.code}
|
|
246
|
-
{pc.network && ` (${pc.network.name})`}
|
|
247
|
-
</span>
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
);
|
|
251
|
-
}}
|
|
252
|
-
/>
|
|
253
|
-
</FormControl>
|
|
212
|
+
</SelectItem>
|
|
213
|
+
))}
|
|
214
|
+
</SelectContent>
|
|
215
|
+
</Select>
|
|
254
216
|
<FormDescription>
|
|
255
217
|
The cryptocurrency to use for payment.
|
|
256
218
|
</FormDescription>
|
|
@@ -259,59 +221,53 @@ export const CreatePaymentDialog: React.FC = () => {
|
|
|
259
221
|
)}
|
|
260
222
|
/>
|
|
261
223
|
|
|
262
|
-
{/* Conversion
|
|
263
|
-
{calculateCryptoAmount && (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
<span className="text-sm text-muted-foreground">You will send</span>
|
|
273
|
-
<div className="flex items-center gap-2">
|
|
274
|
-
<TokenIcon symbol={calculateCryptoAmount.currency} size={16} />
|
|
275
|
-
<span className="font-mono font-semibold">
|
|
276
|
-
{calculateCryptoAmount.amount.toFixed(8)} {calculateCryptoAmount.currency}
|
|
277
|
-
</span>
|
|
278
|
-
</div>
|
|
279
|
-
</div>
|
|
280
|
-
|
|
281
|
-
{/* USD Amount Received */}
|
|
282
|
-
<div className="flex items-center justify-between">
|
|
283
|
-
<span className="text-sm text-muted-foreground">You will receive</span>
|
|
284
|
-
<span className="text-lg font-bold">
|
|
285
|
-
${amountUsd?.toFixed(2)} USD
|
|
224
|
+
{/* Conversion Information */}
|
|
225
|
+
{calculateCryptoAmount && (
|
|
226
|
+
<div className="rounded-sm bg-muted p-4 space-y-3">
|
|
227
|
+
{/* Amount to Send in Crypto */}
|
|
228
|
+
<div className="flex items-center justify-between">
|
|
229
|
+
<span className="text-sm text-muted-foreground">You will send</span>
|
|
230
|
+
<div className="flex items-center gap-2">
|
|
231
|
+
<TokenIcon symbol={calculateCryptoAmount.currency} size={16} />
|
|
232
|
+
<span className="font-mono font-semibold">
|
|
233
|
+
{calculateCryptoAmount.amount.toFixed(8)} {calculateCryptoAmount.currency}
|
|
286
234
|
</span>
|
|
287
235
|
</div>
|
|
236
|
+
</div>
|
|
288
237
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
238
|
+
{/* USD Amount Received */}
|
|
239
|
+
<div className="flex items-center justify-between">
|
|
240
|
+
<span className="text-sm text-muted-foreground">You will receive</span>
|
|
241
|
+
<span className="text-lg font-bold">
|
|
242
|
+
${form.watch('amount_usd')?.toFixed(2)} USD
|
|
243
|
+
</span>
|
|
244
|
+
</div>
|
|
296
245
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
</div>
|
|
304
|
-
</div>
|
|
305
|
-
)}
|
|
246
|
+
{/* Exchange Rate */}
|
|
247
|
+
<div className="flex items-center justify-between text-xs">
|
|
248
|
+
<span className="text-muted-foreground">Rate</span>
|
|
249
|
+
<span className="font-medium">
|
|
250
|
+
1 {calculateCryptoAmount.currency} = ${calculateCryptoAmount.rate?.toFixed(2)}
|
|
251
|
+
</span>
|
|
306
252
|
</div>
|
|
307
|
-
|
|
308
|
-
|
|
253
|
+
|
|
254
|
+
{/* Network Info */}
|
|
255
|
+
{calculateCryptoAmount.network && (
|
|
256
|
+
<div className="border-t pt-3">
|
|
257
|
+
<div className="flex items-center justify-between">
|
|
258
|
+
<span className="text-sm text-muted-foreground">Network</span>
|
|
259
|
+
<span className="text-sm font-medium">{calculateCryptoAmount.network}</span>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
263
|
+
</div>
|
|
264
|
+
)}
|
|
309
265
|
|
|
310
266
|
<DialogFooter>
|
|
311
267
|
<Button type="button" variant="outline" onClick={handleClose} disabled={isSubmitting}>
|
|
312
268
|
Cancel
|
|
313
269
|
</Button>
|
|
314
|
-
<Button type="submit" disabled={isSubmitting}>
|
|
270
|
+
<Button type="submit" disabled={isSubmitting || currencyOptions.length === 0}>
|
|
315
271
|
{isSubmitting ? (
|
|
316
272
|
<>
|
|
317
273
|
<RefreshCw className="h-4 w-4 mr-2 animate-spin" />
|
|
@@ -331,4 +287,3 @@ export const CreatePaymentDialog: React.FC = () => {
|
|
|
331
287
|
</Dialog>
|
|
332
288
|
);
|
|
333
289
|
};
|
|
334
|
-
|