@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.
Files changed (41) hide show
  1. package/package.json +5 -5
  2. package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +2 -2
  3. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +6 -6
  4. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +1 -1
  5. package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +6 -6
  6. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +1 -1
  7. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +43 -133
  8. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenuUserCard.tsx +150 -0
  9. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +2 -2
  10. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +47 -65
  11. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +121 -144
  12. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +103 -48
  13. package/src/layouts/PaymentsLayout/components/index.ts +1 -4
  14. package/src/layouts/PaymentsLayout/events.ts +23 -84
  15. package/src/layouts/PaymentsLayout/index.ts +7 -11
  16. package/src/layouts/PaymentsLayout/types.ts +3 -16
  17. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +45 -16
  18. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +18 -14
  19. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  20. package/src/layouts/PaymentsLayout/views/overview/index.tsx +3 -6
  21. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +51 -31
  22. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  23. package/src/layouts/PaymentsLayout/views/payments/index.tsx +1 -2
  24. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +273 -0
  25. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +1 -0
  26. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +5 -17
  27. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +2 -3
  28. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +2 -3
  29. package/src/snippets/Chat/components/SessionList.tsx +1 -1
  30. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +2 -2
  31. package/src/snippets/VideoPlayer/VideoPlayer.tsx +1 -1
  32. package/src/layouts/PaymentsLayout/README.md +0 -133
  33. package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +0 -172
  34. package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +0 -100
  35. package/src/layouts/PaymentsLayout/context/RootPaymentsContext.tsx +0 -134
  36. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +0 -109
  37. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +0 -194
  38. package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +0 -3
  39. package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +0 -19
  40. package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +0 -103
  41. package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +0 -29
@@ -1,6 +1,8 @@
1
1
  /**
2
- * Payments Layout
3
- * Main layout with tabs for different payment-related views
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, Key, Crown } from 'lucide-react';
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 { ApiKeysView } from './views/apikeys';
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 & Billing</h1>
37
+ <h1 className="text-3xl font-bold tracking-tight">Payments</h1>
39
38
  <p className="text-muted-foreground">
40
- Manage your payments, subscriptions, API keys, and account balance
39
+ Manage your payments, balance, and transaction history
41
40
  </p>
42
41
  </div>
43
42
 
44
- {/* Main Content with Tabs */}
45
- <Tabs defaultValue="overview" className="space-y-6">
46
- <TabsList className="inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground">
47
- <TabsTrigger value="overview" className="inline-flex items-center gap-2 px-3 py-1.5">
48
- <Wallet className="h-4 w-4" />
49
- <span className="hidden sm:inline">Overview</span>
50
- </TabsTrigger>
51
- <TabsTrigger value="payments" className="inline-flex items-center gap-2 px-3 py-1.5">
52
- <CreditCard className="h-4 w-4" />
53
- <span className="hidden sm:inline">Payments</span>
54
- </TabsTrigger>
55
- <TabsTrigger value="transactions" className="inline-flex items-center gap-2 px-3 py-1.5">
56
- <History className="h-4 w-4" />
57
- <span className="hidden sm:inline">Transactions</span>
58
- </TabsTrigger>
59
- <TabsTrigger value="apikeys" className="inline-flex items-center gap-2 px-3 py-1.5">
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
- {/* Each tab wrapped in its own provider - loads only when tab is active */}
70
- <TabsContent value="overview" className="space-y-6">
71
- <OverviewProvider>
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
- <OverviewView />
73
+ <PaymentsView />
74
74
  <CreatePaymentDialog />
75
75
  </PaymentsProvider>
76
- </OverviewProvider>
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
- <TabsContent value="apikeys" className="space-y-6">
91
- <ApiKeysProvider>
92
- <ApiKeysView />
93
- <CreateApiKeyDialog />
94
- <DeleteApiKeyDialog />
95
- </ApiKeysProvider>
96
- </TabsContent>
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
- <TabsContent value="tariffs" className="space-y-6">
99
- <TariffsView />
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,95 +21,95 @@ import {
21
21
  FormLabel,
22
22
  FormMessage,
23
23
  Input,
24
- Combobox,
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 { Schemas, Enums } from '@djangocfg/api/cfg/generated';
34
- import { paymentsLogger } from '../../../utils/logger';
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
- const { PaymentCreateRequestSchema } = Schemas;
41
- type PaymentCreateRequest = Schemas.PaymentCreateRequest;
42
- const { PaymentCreateRequestCurrencyCode, PaymentCreateRequestProvider } = Enums;
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>;
43
47
 
44
48
  export const CreatePaymentDialog: React.FC = () => {
45
49
  const [open, setOpen] = useState(false);
46
50
  const [isSubmitting, setIsSubmitting] = useState(false);
47
51
 
48
52
  const { createPayment } = usePaymentsContext();
49
- const {
50
- providerCurrencies,
51
- isLoadingProviderCurrencies,
52
- } = useRootPaymentsContext();
53
+ const { currencies, isLoadingCurrencies } = useRootPaymentsContext();
53
54
 
54
55
  const form = useForm<PaymentCreateRequest>({
55
- resolver: zodResolver(PaymentCreateRequestSchema),
56
+ resolver: zodResolver(PaymentCreateSchema),
56
57
  defaultValues: {
57
58
  amount_usd: 10,
58
- currency_code: PaymentCreateRequestCurrencyCode.USDT,
59
+ currency_code: 'USDT',
59
60
  },
60
61
  });
61
62
 
62
- // Group currencies by token and create combobox options
63
- const currencyOptions = useMemo((): ComboboxOption[] => {
64
- if (!providerCurrencies?.results) return [];
65
-
66
- const enabledCurrencies = providerCurrencies.results.filter(pc => pc.is_enabled);
67
-
68
- return enabledCurrencies.map((pc) => {
69
- const networkInfo = pc.network ? ` (${pc.network.name})` : '';
70
- const label = `${pc.currency.code} - ${pc.currency.name}${networkInfo}`;
71
- const description = pc.network ? pc.network.code : 'No network';
72
-
73
- return {
74
- value: pc.provider_currency_code,
75
- label,
76
- description,
77
- };
78
- });
79
- }, [providerCurrencies]);
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]);
80
68
 
81
- // Get ProviderCurrency by currency_code
82
- const getProviderCurrency = (currencyCode: string) => {
83
- return providerCurrencies?.results?.find(
84
- pc => pc.provider_currency_code === currencyCode
85
- );
86
- };
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]);
87
80
 
88
81
  // Calculate crypto amount from USD
89
82
  const calculateCryptoAmount = useMemo(() => {
90
83
  const amountUsd = form.watch('amount_usd');
91
84
  const currencyCode = form.watch('currency_code');
92
- const pc = getProviderCurrency(currencyCode);
85
+ const currency = currencyOptions.find((c: any) => c.code === currencyCode);
93
86
 
94
- if (!pc || !pc.currency.usd_rate || !amountUsd) {
87
+ if (!currency || !currency.usd_rate || !amountUsd) {
95
88
  return null;
96
89
  }
97
90
 
98
- const cryptoAmount = amountUsd / pc.currency.usd_rate;
91
+ const cryptoAmount = amountUsd / currency.usd_rate;
99
92
  return {
100
93
  amount: cryptoAmount,
101
- currency: pc.currency.code,
102
- symbol: pc.currency.symbol || pc.currency.code,
94
+ currency: currency.code,
95
+ rate: currency.usd_rate,
96
+ network: currency.network,
103
97
  };
104
- }, [form.watch('amount_usd'), form.watch('currency_code'), providerCurrencies]);
98
+ }, [form.watch('amount_usd'), form.watch('currency_code'), currencyOptions]);
105
99
 
106
- useEventListener(PAYMENTS_DIALOG_EVENTS.OPEN_CREATE_PAYMENT_DIALOG, () => {
107
- setOpen(true);
108
- });
100
+ // Listen for open/close events
101
+ useEffect(() => {
102
+ const handleOpen = () => setOpen(true);
103
+ const handleClose = () => setOpen(false);
109
104
 
110
- useEventListener(PAYMENTS_DIALOG_EVENTS.CLOSE_PAYMENTS_DIALOG, () => {
111
- setOpen(false);
112
- });
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
+ }, []);
113
113
 
114
114
  const handleClose = () => {
115
115
  setOpen(false);
@@ -119,7 +119,7 @@ export const CreatePaymentDialog: React.FC = () => {
119
119
  // Initialize default currency if not set
120
120
  useEffect(() => {
121
121
  if (currencyOptions.length > 0 && !form.getValues('currency_code')) {
122
- form.setValue('currency_code', currencyOptions[0].value as any);
122
+ form.setValue('currency_code', currencyOptions[0].code);
123
123
  }
124
124
  }, [currencyOptions, form]);
125
125
 
@@ -127,16 +127,19 @@ export const CreatePaymentDialog: React.FC = () => {
127
127
  try {
128
128
  setIsSubmitting(true);
129
129
 
130
- const payment = await createPayment(data);
130
+ const result = await createPayment();
131
131
  handleClose();
132
132
  closePaymentsDialog();
133
133
 
134
- // Open payment details dialog with created payment
135
- if (payment) {
136
- openPaymentDetails(payment);
134
+ // Extract payment ID from result
135
+ const paymentData = result as any;
136
+ const paymentId = paymentData?.payment?.id || paymentData?.id;
137
+
138
+ if (paymentId) {
139
+ openPaymentDetailsDialog(String(paymentId));
137
140
  }
138
141
  } catch (error) {
139
- paymentsLogger.error('Failed to create payment:', error);
142
+ console.error('Failed to create payment:', error);
140
143
  } finally {
141
144
  setIsSubmitting(false);
142
145
  }
@@ -184,51 +187,32 @@ export const CreatePaymentDialog: React.FC = () => {
184
187
  render={({ field }) => (
185
188
  <FormItem>
186
189
  <FormLabel>Currency</FormLabel>
187
- <FormControl>
188
- <Combobox
189
- options={currencyOptions}
190
- value={field.value}
191
- onValueChange={field.onChange}
192
- placeholder="Select currency..."
193
- searchPlaceholder="Search currencies..."
194
- emptyText="No currencies found."
195
- disabled={isLoadingProviderCurrencies}
196
- className="w-full"
197
- renderValue={(option) => {
198
- if (!option) return null;
199
- const pc = getProviderCurrency(option.value);
200
- if (!pc) return option.label;
201
- 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}>
202
203
  <div className="flex items-center gap-2">
203
- <TokenIcon symbol={pc.currency.code} size={20} />
204
- <span>{pc.currency.code}</span>
205
- {pc.network && (
204
+ <TokenIcon symbol={curr.code} size={16} />
205
+ <span>{curr.code}</span>
206
+ {curr.network && (
206
207
  <span className="text-xs text-muted-foreground">
207
- ({pc.network.name})
208
+ ({curr.network})
208
209
  </span>
209
210
  )}
210
211
  </div>
211
- );
212
- }}
213
- renderOption={(option) => {
214
- const pc = getProviderCurrency(option.value);
215
- if (!pc) return option.label;
216
- return (
217
- <div className="flex items-center gap-2 flex-1">
218
- <TokenIcon symbol={pc.currency.code} size={24} />
219
- <div className="flex flex-col flex-1">
220
- <span className="font-medium">{pc.currency.code} - {pc.currency.name}</span>
221
- {pc.network && (
222
- <span className="text-xs text-muted-foreground">
223
- Network: {pc.network.name} ({pc.network.code})
224
- </span>
225
- )}
226
- </div>
227
- </div>
228
- );
229
- }}
230
- />
231
- </FormControl>
212
+ </SelectItem>
213
+ ))}
214
+ </SelectContent>
215
+ </Select>
232
216
  <FormDescription>
233
217
  The cryptocurrency to use for payment.
234
218
  </FormDescription>
@@ -237,59 +221,53 @@ export const CreatePaymentDialog: React.FC = () => {
237
221
  )}
238
222
  />
239
223
 
240
- {/* Conversion and Fee Information */}
241
- {calculateCryptoAmount && (() => {
242
- const pc = getProviderCurrency(form.watch('currency_code'));
243
- const amountUsd = form.watch('amount_usd');
244
- if (!pc) return null;
245
-
246
- return (
247
- <div className="rounded-lg bg-muted p-4 space-y-3">
248
- {/* Amount to Send in Crypto */}
249
- <div className="flex items-center justify-between">
250
- <span className="text-sm text-muted-foreground">You will send</span>
251
- <div className="flex items-center gap-2">
252
- <TokenIcon symbol={calculateCryptoAmount.currency} size={16} />
253
- <span className="font-mono font-semibold">
254
- {calculateCryptoAmount.amount.toFixed(8)} {calculateCryptoAmount.currency}
255
- </span>
256
- </div>
257
- </div>
258
-
259
- {/* USD Amount Received */}
260
- <div className="flex items-center justify-between">
261
- <span className="text-sm text-muted-foreground">You will receive</span>
262
- <span className="text-lg font-bold">
263
- ${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}
264
234
  </span>
265
235
  </div>
236
+ </div>
266
237
 
267
- {/* Exchange Rate */}
268
- <div className="flex items-center justify-between text-xs">
269
- <span className="text-muted-foreground">Rate</span>
270
- <span className="font-medium">
271
- 1 {calculateCryptoAmount.currency} = ${pc.currency.usd_rate?.toFixed(2)}
272
- </span>
273
- </div>
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>
274
245
 
275
- {/* Network Info */}
276
- {pc.network && (
277
- <div className="border-t pt-3">
278
- <div className="flex items-center justify-between">
279
- <span className="text-sm text-muted-foreground">Network</span>
280
- <span className="text-sm font-medium">{pc.network.name}</span>
281
- </div>
282
- </div>
283
- )}
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>
284
252
  </div>
285
- );
286
- })()}
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
+ )}
287
265
 
288
266
  <DialogFooter>
289
267
  <Button type="button" variant="outline" onClick={handleClose} disabled={isSubmitting}>
290
268
  Cancel
291
269
  </Button>
292
- <Button type="submit" disabled={isSubmitting}>
270
+ <Button type="submit" disabled={isSubmitting || currencyOptions.length === 0}>
293
271
  {isSubmitting ? (
294
272
  <>
295
273
  <RefreshCw className="h-4 w-4 mr-2 animate-spin" />
@@ -309,4 +287,3 @@ export const CreatePaymentDialog: React.FC = () => {
309
287
  </Dialog>
310
288
  );
311
289
  };
312
-