@djangocfg/ext-payments 1.0.14 → 1.0.17

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 (57) hide show
  1. package/dist/config.cjs +5 -8
  2. package/dist/config.js +5 -8
  3. package/dist/index.cjs +1085 -1107
  4. package/dist/index.d.cts +480 -41
  5. package/dist/index.d.ts +480 -41
  6. package/dist/index.js +1037 -1093
  7. package/package.json +13 -16
  8. package/src/api/generated/ext_payments/CLAUDE.md +7 -3
  9. package/src/api/generated/ext_payments/_utils/fetchers/ext_payments__payments.ts +237 -5
  10. package/src/api/generated/ext_payments/_utils/hooks/ext_payments__payments.ts +71 -3
  11. package/src/api/generated/ext_payments/_utils/schemas/PaginatedWithdrawalListList.schema.ts +24 -0
  12. package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateRequest.schema.ts +21 -0
  13. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateRequest.schema.ts +21 -0
  14. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalDetail.schema.ts +42 -0
  15. package/src/api/generated/ext_payments/_utils/schemas/WithdrawalList.schema.ts +29 -0
  16. package/src/api/generated/ext_payments/_utils/schemas/index.ts +5 -0
  17. package/src/api/generated/ext_payments/enums.ts +36 -0
  18. package/src/api/generated/ext_payments/ext_payments__payments/client.ts +58 -5
  19. package/src/api/generated/ext_payments/ext_payments__payments/models.ts +141 -0
  20. package/src/api/generated/ext_payments/schema.json +579 -3
  21. package/src/components/ActivityItem.tsx +118 -0
  22. package/src/components/ActivityList.tsx +93 -0
  23. package/src/components/AddFundsSheet.tsx +258 -0
  24. package/src/components/BalanceHero.tsx +102 -0
  25. package/src/components/PaymentSheet.tsx +290 -0
  26. package/src/components/ResponsiveSheet.tsx +151 -0
  27. package/src/components/WithdrawSheet.tsx +329 -0
  28. package/src/components/index.ts +18 -0
  29. package/src/contexts/WalletContext.tsx +355 -0
  30. package/src/contexts/index.ts +12 -45
  31. package/src/index.ts +6 -18
  32. package/src/contexts/BalancesContext.tsx +0 -63
  33. package/src/contexts/CurrenciesContext.tsx +0 -64
  34. package/src/contexts/OverviewContext.tsx +0 -173
  35. package/src/contexts/PaymentsContext.tsx +0 -122
  36. package/src/contexts/PaymentsExtensionProvider.tsx +0 -56
  37. package/src/contexts/README.md +0 -201
  38. package/src/contexts/RootPaymentsContext.tsx +0 -66
  39. package/src/contexts/types.ts +0 -40
  40. package/src/hooks/index.ts +0 -20
  41. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -90
  42. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -274
  43. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -287
  44. package/src/layouts/PaymentsLayout/components/index.ts +0 -2
  45. package/src/layouts/PaymentsLayout/events.ts +0 -47
  46. package/src/layouts/PaymentsLayout/index.ts +0 -16
  47. package/src/layouts/PaymentsLayout/types.ts +0 -6
  48. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -121
  49. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -139
  50. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  51. package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -21
  52. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -279
  53. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  54. package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -18
  55. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -260
  56. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
  57. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -18
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Activity Item (Apple-style)
3
+ *
4
+ * Single row in the activity list
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import { ArrowDownLeft, ArrowUpRight, Clock, CheckCircle2, XCircle, AlertCircle, Loader2 } from 'lucide-react';
10
+ import moment from 'moment';
11
+
12
+ import { TokenIcon } from '@djangocfg/ui-core';
13
+ import { cn } from '@djangocfg/ui-core/lib';
14
+
15
+ import type { ActivityItem as ActivityItemType } from '../contexts/WalletContext';
16
+
17
+ interface ActivityItemProps {
18
+ item: ActivityItemType;
19
+ onClick?: () => void;
20
+ }
21
+
22
+ const statusConfig: Record<string, { icon: any; color: string; bg: string; animate?: boolean }> = {
23
+ pending: {
24
+ icon: Clock,
25
+ color: 'text-yellow-500',
26
+ bg: 'bg-yellow-500/10',
27
+ },
28
+ confirming: {
29
+ icon: Loader2,
30
+ color: 'text-blue-500',
31
+ bg: 'bg-blue-500/10',
32
+ animate: true,
33
+ },
34
+ completed: {
35
+ icon: CheckCircle2,
36
+ color: 'text-green-500',
37
+ bg: 'bg-green-500/10',
38
+ },
39
+ failed: {
40
+ icon: XCircle,
41
+ color: 'text-red-500',
42
+ bg: 'bg-red-500/10',
43
+ },
44
+ expired: {
45
+ icon: AlertCircle,
46
+ color: 'text-muted-foreground',
47
+ bg: 'bg-muted',
48
+ },
49
+ };
50
+
51
+ export function ActivityItem({ item, onClick }: ActivityItemProps) {
52
+ const config = statusConfig[item.status];
53
+ const StatusIcon = config.icon;
54
+
55
+ const isPositive = item.type === 'payment' || item.type === 'deposit';
56
+ const DirectionIcon = isPositive ? ArrowDownLeft : ArrowUpRight;
57
+
58
+ const relativeTime = moment(item.createdAt).fromNow();
59
+
60
+ return (
61
+ <button
62
+ onClick={onClick}
63
+ className={cn(
64
+ 'w-full flex items-center gap-4 p-4 rounded-xl',
65
+ 'cursor-pointer',
66
+ 'hover:bg-accent active:bg-accent/80 transition-colors',
67
+ 'text-left'
68
+ )}
69
+ >
70
+ {/* Icon */}
71
+ <div className={cn(
72
+ 'flex items-center justify-center w-10 h-10 rounded-full',
73
+ isPositive ? 'bg-green-500/10' : 'bg-red-500/10'
74
+ )}>
75
+ {item.currency ? (
76
+ <TokenIcon symbol={item.currency} size={24} />
77
+ ) : (
78
+ <DirectionIcon className={cn(
79
+ 'h-5 w-5',
80
+ isPositive ? 'text-green-500' : 'text-red-500'
81
+ )} />
82
+ )}
83
+ </div>
84
+
85
+ {/* Content */}
86
+ <div className="flex-1 min-w-0">
87
+ <div className="flex items-center gap-2">
88
+ <span className="font-medium truncate">{item.description}</span>
89
+ </div>
90
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
91
+ <span>{relativeTime}</span>
92
+ {item.status !== 'completed' && (
93
+ <>
94
+ <span>·</span>
95
+ <span className={cn('flex items-center gap-1', config.color)}>
96
+ <StatusIcon className={cn('h-3 w-3', config.animate && 'animate-spin')} />
97
+ {item.statusDisplay}
98
+ </span>
99
+ </>
100
+ )}
101
+ </div>
102
+ </div>
103
+
104
+ {/* Amount */}
105
+ <div className="text-right">
106
+ <span className={cn(
107
+ 'font-semibold tabular-nums',
108
+ isPositive ? 'text-green-600 dark:text-green-400' : 'text-foreground'
109
+ )}>
110
+ {item.amountDisplay}
111
+ </span>
112
+ {item.status === 'completed' && (
113
+ <CheckCircle2 className="h-4 w-4 text-green-500 ml-2 inline-block" />
114
+ )}
115
+ </div>
116
+ </button>
117
+ );
118
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Activity List (Apple-style)
3
+ *
4
+ * List of recent activity (payments + transactions)
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import { History, ChevronRight } from 'lucide-react';
10
+
11
+ import { Button, Skeleton } from '@djangocfg/ui-core';
12
+ import { cn } from '@djangocfg/ui-core/lib';
13
+
14
+ import { useWallet, type ActivityItem as ActivityItemType } from '../contexts/WalletContext';
15
+ import { ActivityItem } from './ActivityItem';
16
+
17
+ interface ActivityListProps {
18
+ onItemClick?: (item: ActivityItemType) => void;
19
+ onViewAll?: () => void;
20
+ limit?: number;
21
+ className?: string;
22
+ }
23
+
24
+ export function ActivityList({
25
+ onItemClick,
26
+ onViewAll,
27
+ limit = 10,
28
+ className,
29
+ }: ActivityListProps) {
30
+ const { activity, isLoadingActivity, hasMoreActivity } = useWallet();
31
+
32
+ const displayedActivity = limit ? activity.slice(0, limit) : activity;
33
+
34
+ if (isLoadingActivity) {
35
+ return (
36
+ <div className={cn('space-y-2', className)}>
37
+ <div className="flex items-center justify-between px-4 py-2">
38
+ <Skeleton className="h-5 w-32" />
39
+ </div>
40
+ {[1, 2, 3, 4, 5].map((i) => (
41
+ <div key={i} className="flex items-center gap-4 p-4">
42
+ <Skeleton className="h-10 w-10 rounded-full" />
43
+ <div className="flex-1 space-y-2">
44
+ <Skeleton className="h-4 w-32" />
45
+ <Skeleton className="h-3 w-24" />
46
+ </div>
47
+ <Skeleton className="h-5 w-16" />
48
+ </div>
49
+ ))}
50
+ </div>
51
+ );
52
+ }
53
+
54
+ if (activity.length === 0) {
55
+ return (
56
+ <div className={cn('text-center py-12', className)}>
57
+ <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-muted mb-4">
58
+ <History className="h-8 w-8 text-muted-foreground" />
59
+ </div>
60
+ <h3 className="font-semibold mb-1">No Activity Yet</h3>
61
+ <p className="text-sm text-muted-foreground">
62
+ Your transactions will appear here
63
+ </p>
64
+ </div>
65
+ );
66
+ }
67
+
68
+ return (
69
+ <div className={cn('pt-6', className)}>
70
+ {/* Header */}
71
+ <div className="flex items-center justify-between px-4 py-2">
72
+ <h2 className="font-semibold text-lg">Recent Activity</h2>
73
+ {hasMoreActivity && onViewAll && (
74
+ <Button variant="ghost" size="sm" onClick={onViewAll} className="text-primary">
75
+ View All
76
+ <ChevronRight className="h-4 w-4 ml-1" />
77
+ </Button>
78
+ )}
79
+ </div>
80
+
81
+ {/* List */}
82
+ <div className="divide-y divide-border/50">
83
+ {displayedActivity.map((item) => (
84
+ <ActivityItem
85
+ key={item.id}
86
+ item={item}
87
+ onClick={() => onItemClick?.(item)}
88
+ />
89
+ ))}
90
+ </div>
91
+ </div>
92
+ );
93
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Add Funds Sheet (Apple-style)
3
+ *
4
+ * Responsive: Dialog on desktop, Drawer on mobile
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import { useState, useMemo, useCallback } from 'react';
10
+ import { RefreshCw } from 'lucide-react';
11
+ import { useForm } from 'react-hook-form';
12
+ import { z } from 'zod';
13
+ import { zodResolver } from '@hookform/resolvers/zod';
14
+
15
+ import {
16
+ Alert,
17
+ AlertDescription,
18
+ Button,
19
+ Combobox,
20
+ Form,
21
+ FormControl,
22
+ FormField,
23
+ FormItem,
24
+ FormLabel,
25
+ FormMessage,
26
+ Input,
27
+ TokenIcon,
28
+ ResponsiveSheet,
29
+ ResponsiveSheetContent,
30
+ ResponsiveSheetDescription,
31
+ ResponsiveSheetHeader,
32
+ ResponsiveSheetTitle,
33
+ } from '@djangocfg/ui-core';
34
+
35
+ import { useWallet } from '../contexts/WalletContext';
36
+ import type { PaymentDetail } from '../api/generated/ext_payments/_utils/schemas';
37
+
38
+ // ─────────────────────────────────────────────────────────────────────────────
39
+ // Schema
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+
42
+ const AddFundsSchema = z.object({
43
+ amount: z.number().min(1, 'Minimum $1.00'),
44
+ currency: z.string().min(1, 'Select a currency'),
45
+ });
46
+
47
+ type AddFundsForm = z.infer<typeof AddFundsSchema>;
48
+
49
+ // ─────────────────────────────────────────────────────────────────────────────
50
+ // Props
51
+ // ─────────────────────────────────────────────────────────────────────────────
52
+
53
+ interface AddFundsSheetProps {
54
+ open: boolean;
55
+ onOpenChange: (open: boolean) => void;
56
+ onSuccess?: (payment: PaymentDetail) => void;
57
+ }
58
+
59
+ // ─────────────────────────────────────────────────────────────────────────────
60
+ // Component
61
+ // ─────────────────────────────────────────────────────────────────────────────
62
+
63
+ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetProps) {
64
+ const { currencies, isLoadingCurrencies, addFunds } = useWallet();
65
+ const [isSubmitting, setIsSubmitting] = useState(false);
66
+ const [error, setError] = useState<string | null>(null);
67
+
68
+ const form = useForm<AddFundsForm>({
69
+ resolver: zodResolver(AddFundsSchema),
70
+ defaultValues: {
71
+ amount: 100,
72
+ currency: '',
73
+ },
74
+ });
75
+
76
+ // Currency options for combobox
77
+ const currencyOptions = useMemo(() => {
78
+ return currencies.map((c) => ({
79
+ value: c.code,
80
+ label: c.network ? `${c.code} (${c.network})` : c.code,
81
+ rate: c.rate,
82
+ network: c.network,
83
+ }));
84
+ }, [currencies]);
85
+
86
+ // Set default currency when loaded
87
+ useMemo(() => {
88
+ if (currencyOptions.length > 0 && !form.getValues('currency')) {
89
+ const usdt = currencyOptions.find(c => c.value.includes('USDT'));
90
+ form.setValue('currency', usdt?.value || currencyOptions[0].value);
91
+ }
92
+ }, [currencyOptions, form]);
93
+
94
+ // Calculate crypto amount
95
+ const selectedCurrency = currencyOptions.find(c => c.value === form.watch('currency'));
96
+ const cryptoAmount = useMemo(() => {
97
+ const amount = form.watch('amount');
98
+ if (!selectedCurrency?.rate || !amount) return null;
99
+ return amount / selectedCurrency.rate;
100
+ }, [form.watch('amount'), selectedCurrency]);
101
+
102
+ // Handle submit
103
+ const handleSubmit = useCallback(async (data: AddFundsForm) => {
104
+ try {
105
+ setIsSubmitting(true);
106
+ setError(null);
107
+
108
+ const result = await addFunds({
109
+ amount_usd: String(data.amount),
110
+ currency_code: data.currency,
111
+ });
112
+
113
+ form.reset();
114
+ onOpenChange(false);
115
+ onSuccess?.(result);
116
+ } catch (err: any) {
117
+ const message = err?.response?.data?.message
118
+ || err?.response?.data?.detail
119
+ || err?.message
120
+ || 'Failed to create payment';
121
+ setError(message);
122
+ } finally {
123
+ setIsSubmitting(false);
124
+ }
125
+ }, [addFunds, form, onOpenChange, onSuccess]);
126
+
127
+ // Reset on close
128
+ const handleOpenChange = useCallback((open: boolean) => {
129
+ if (!open) {
130
+ setError(null);
131
+ form.reset();
132
+ }
133
+ onOpenChange(open);
134
+ }, [form, onOpenChange]);
135
+
136
+ return (
137
+ <ResponsiveSheet open={open} onOpenChange={handleOpenChange}>
138
+ <ResponsiveSheetContent className="sm:max-w-md">
139
+ <ResponsiveSheetHeader>
140
+ <ResponsiveSheetTitle>Add Funds</ResponsiveSheetTitle>
141
+ <ResponsiveSheetDescription>
142
+ Add funds to your wallet using cryptocurrency
143
+ </ResponsiveSheetDescription>
144
+ </ResponsiveSheetHeader>
145
+
146
+ <Form {...form}>
147
+ <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6 p-4 sm:p-0 sm:mt-4">
148
+ {/* Amount Input */}
149
+ <FormField
150
+ control={form.control}
151
+ name="amount"
152
+ render={({ field }) => (
153
+ <FormItem>
154
+ <FormLabel>Amount (USD)</FormLabel>
155
+ <FormControl>
156
+ <div className="relative">
157
+ <span className="absolute left-4 top-1/2 -translate-y-1/2 text-muted-foreground text-lg">
158
+ $
159
+ </span>
160
+ <Input
161
+ type="number"
162
+ step="0.01"
163
+ min="1"
164
+ placeholder="100.00"
165
+ className="pl-8 text-2xl h-14 font-semibold"
166
+ {...field}
167
+ onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)}
168
+ />
169
+ </div>
170
+ </FormControl>
171
+ <FormMessage />
172
+ </FormItem>
173
+ )}
174
+ />
175
+
176
+ {/* Currency Selection */}
177
+ <FormField
178
+ control={form.control}
179
+ name="currency"
180
+ render={({ field }) => (
181
+ <FormItem>
182
+ <FormLabel>Pay with</FormLabel>
183
+ <FormControl>
184
+ <Combobox
185
+ options={currencyOptions}
186
+ value={field.value}
187
+ onValueChange={field.onChange}
188
+ placeholder="Select currency..."
189
+ searchPlaceholder="Search..."
190
+ disabled={isLoadingCurrencies}
191
+ className="h-14"
192
+ renderOption={(option) => (
193
+ <div className="flex items-center gap-3 flex-1">
194
+ <TokenIcon symbol={option.value} size={24} />
195
+ <span className="font-medium">{option.label}</span>
196
+ </div>
197
+ )}
198
+ renderValue={(option) => option && (
199
+ <div className="flex items-center gap-3">
200
+ <TokenIcon symbol={option.value} size={24} />
201
+ <span className="font-medium">{option.label}</span>
202
+ </div>
203
+ )}
204
+ />
205
+ </FormControl>
206
+ <FormMessage />
207
+ </FormItem>
208
+ )}
209
+ />
210
+
211
+ {/* Conversion Preview */}
212
+ {cryptoAmount !== null && selectedCurrency && (
213
+ <div className="bg-muted rounded-xl p-4 space-y-2">
214
+ <div className="flex items-center justify-between">
215
+ <span className="text-muted-foreground">You will send</span>
216
+ <div className="flex items-center gap-2">
217
+ <TokenIcon symbol={selectedCurrency.value} size={20} />
218
+ <span className="font-mono font-semibold">
219
+ {cryptoAmount.toFixed(8)} {selectedCurrency.value}
220
+ </span>
221
+ </div>
222
+ </div>
223
+ <div className="flex items-center justify-between text-sm text-muted-foreground">
224
+ <span>Rate</span>
225
+ <span>1 {selectedCurrency.value} = ${selectedCurrency.rate.toFixed(2)}</span>
226
+ </div>
227
+ </div>
228
+ )}
229
+
230
+ {/* Error */}
231
+ {error && (
232
+ <Alert variant="destructive">
233
+ <AlertDescription>{error}</AlertDescription>
234
+ </Alert>
235
+ )}
236
+
237
+ {/* Submit Button */}
238
+ <Button
239
+ type="submit"
240
+ size="lg"
241
+ className="w-full h-14 text-lg rounded-xl"
242
+ disabled={isSubmitting || currencyOptions.length === 0}
243
+ >
244
+ {isSubmitting ? (
245
+ <>
246
+ <RefreshCw className="h-5 w-5 mr-2 animate-spin" />
247
+ Creating...
248
+ </>
249
+ ) : (
250
+ 'Continue'
251
+ )}
252
+ </Button>
253
+ </form>
254
+ </Form>
255
+ </ResponsiveSheetContent>
256
+ </ResponsiveSheet>
257
+ );
258
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Balance Hero (Apple-style)
3
+ *
4
+ * Large centered balance display with action buttons
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import { Plus, ArrowUpRight, RefreshCw } from 'lucide-react';
10
+
11
+ import { Button, Skeleton } from '@djangocfg/ui-core';
12
+ import { cn } from '@djangocfg/ui-core/lib';
13
+
14
+ import { useWallet } from '../contexts/WalletContext';
15
+
16
+ interface BalanceHeroProps {
17
+ onAddFunds?: () => void;
18
+ onWithdraw?: () => void;
19
+ className?: string;
20
+ }
21
+
22
+ export function BalanceHero({ onAddFunds, onWithdraw, className }: BalanceHeroProps) {
23
+ const { balance, balanceAmount, isLoadingBalance, refreshWallet } = useWallet();
24
+
25
+ const formattedBalance = new Intl.NumberFormat('en-US', {
26
+ style: 'currency',
27
+ currency: 'USD',
28
+ minimumFractionDigits: 2,
29
+ maximumFractionDigits: 2,
30
+ }).format(balanceAmount);
31
+
32
+ return (
33
+ <div className={cn('flex flex-col items-center py-16 px-4', className)}>
34
+ {/* Balance Display */}
35
+ <div className="text-center mb-6">
36
+ {isLoadingBalance ? (
37
+ <>
38
+ <Skeleton className="h-12 w-48 mx-auto mb-2" />
39
+ <Skeleton className="h-4 w-24 mx-auto" />
40
+ </>
41
+ ) : (
42
+ <>
43
+ <h1 className="text-5xl font-bold tracking-tight tabular-nums">
44
+ {formattedBalance}
45
+ </h1>
46
+ <p className="text-muted-foreground mt-1">Available Balance</p>
47
+ </>
48
+ )}
49
+ </div>
50
+
51
+ {/* Action Buttons */}
52
+ <div className="flex items-center gap-3">
53
+ <Button
54
+ size="lg"
55
+ onClick={onAddFunds}
56
+ className="rounded-full px-6"
57
+ >
58
+ <Plus className="h-5 w-5 mr-2" />
59
+ Add Funds
60
+ </Button>
61
+
62
+ <Button
63
+ size="lg"
64
+ variant="outline"
65
+ onClick={onWithdraw}
66
+ className="rounded-full px-6"
67
+ >
68
+ <ArrowUpRight className="h-5 w-5 mr-2" />
69
+ Withdraw
70
+ </Button>
71
+
72
+ <Button
73
+ size="icon"
74
+ variant="ghost"
75
+ onClick={() => refreshWallet()}
76
+ className="rounded-full"
77
+ >
78
+ <RefreshCw className="h-4 w-4" />
79
+ </Button>
80
+ </div>
81
+
82
+ {/* Stats (subtle) */}
83
+ {balance && !isLoadingBalance && (
84
+ <div className="flex items-center gap-6 mt-6 text-sm text-muted-foreground">
85
+ <div className="text-center">
86
+ <span className="block font-medium text-foreground">
87
+ ${parseFloat(balance.total_deposited || '0').toFixed(2)}
88
+ </span>
89
+ <span>Total Deposited</span>
90
+ </div>
91
+ <div className="h-8 w-px bg-border" />
92
+ <div className="text-center">
93
+ <span className="block font-medium text-foreground">
94
+ ${parseFloat(balance.total_withdrawn || '0').toFixed(2)}
95
+ </span>
96
+ <span>Total Withdrawn</span>
97
+ </div>
98
+ </div>
99
+ )}
100
+ </div>
101
+ );
102
+ }