@djangocfg/ext-payments 1.0.21 → 1.0.22
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/dist/config.cjs +8 -1
- package/dist/config.js +8 -1
- package/dist/i18n.cjs +420 -0
- package/dist/i18n.d.cts +171 -0
- package/dist/i18n.d.ts +171 -0
- package/dist/i18n.js +391 -0
- package/dist/index.cjs +641 -109
- package/dist/index.js +642 -110
- package/package.json +16 -9
- package/src/components/ActivityList.tsx +18 -4
- package/src/components/AddFundsSheet.tsx +37 -14
- package/src/components/BalanceHero.tsx +20 -5
- package/src/components/CurrencyCombobox.tsx +16 -3
- package/src/components/PaymentSheet.tsx +75 -29
- package/src/components/WithdrawSheet.tsx +39 -15
- package/src/components/WithdrawalSheet.tsx +78 -32
- package/src/i18n/index.ts +35 -0
- package/src/i18n/locales/en.ts +136 -0
- package/src/i18n/locales/ko.ts +136 -0
- package/src/i18n/locales/ru.ts +136 -0
- package/src/i18n/types.ts +163 -0
|
@@ -13,6 +13,9 @@ import { useForm } from 'react-hook-form';
|
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
15
15
|
|
|
16
|
+
import { createTypedExtensionT } from '@djangocfg/ext-base/i18n';
|
|
17
|
+
import { useT } from '@djangocfg/i18n';
|
|
18
|
+
import { PAYMENTS_NAMESPACE, type PaymentsTranslations } from '../i18n';
|
|
16
19
|
import {
|
|
17
20
|
Alert,
|
|
18
21
|
AlertDescription,
|
|
@@ -74,10 +77,31 @@ interface WithdrawSaved {
|
|
|
74
77
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
75
78
|
|
|
76
79
|
export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetProps) {
|
|
80
|
+
const baseT = useT();
|
|
81
|
+
const pt = createTypedExtensionT<typeof PAYMENTS_NAMESPACE, PaymentsTranslations>(baseT, PAYMENTS_NAMESPACE);
|
|
77
82
|
const { currencies, isLoadingCurrencies, withdraw, balanceAmount } = useWallet();
|
|
78
83
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
79
84
|
const [error, setError] = useState<string | null>(null);
|
|
80
85
|
|
|
86
|
+
// Prepare labels before render
|
|
87
|
+
const labels = useMemo(() => ({
|
|
88
|
+
title: pt('sheets.withdrawTitle'),
|
|
89
|
+
description: pt('sheets.withdrawDescription'),
|
|
90
|
+
amountUsd: pt('form.amountUsd'),
|
|
91
|
+
withdrawAs: pt('form.withdrawAs'),
|
|
92
|
+
walletAddress: pt('form.walletAddress'),
|
|
93
|
+
enterWalletAddress: pt('form.enterWalletAddress'),
|
|
94
|
+
amount: pt('form.amount'),
|
|
95
|
+
serviceFee: pt('estimate.serviceFee'),
|
|
96
|
+
networkFee: pt('estimate.networkFee'),
|
|
97
|
+
youWillReceive: pt('estimate.youWillReceive'),
|
|
98
|
+
calculating: pt('estimate.calculating'),
|
|
99
|
+
insufficientBalance: pt('withdraw.insufficientBalance'),
|
|
100
|
+
approvalWarning: pt('withdraw.approvalWarning'),
|
|
101
|
+
submitting: pt('withdraw.submitting'),
|
|
102
|
+
requestWithdrawal: pt('actions.requestWithdrawal'),
|
|
103
|
+
}), [pt]);
|
|
104
|
+
|
|
81
105
|
// Remember last used currency and wallet address
|
|
82
106
|
const [saved, setSaved] = useLocalStorage<WithdrawSaved>(STORAGE_KEY, {
|
|
83
107
|
currency: '',
|
|
@@ -178,9 +202,9 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
178
202
|
<ResponsiveSheet open={open} onOpenChange={handleOpenChange}>
|
|
179
203
|
<ResponsiveSheetContent className="sm:max-w-md">
|
|
180
204
|
<ResponsiveSheetHeader>
|
|
181
|
-
<ResponsiveSheetTitle>
|
|
205
|
+
<ResponsiveSheetTitle>{labels.title}</ResponsiveSheetTitle>
|
|
182
206
|
<ResponsiveSheetDescription>
|
|
183
|
-
|
|
207
|
+
{labels.description}
|
|
184
208
|
</ResponsiveSheetDescription>
|
|
185
209
|
</ResponsiveSheetHeader>
|
|
186
210
|
|
|
@@ -192,7 +216,7 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
192
216
|
name="amount"
|
|
193
217
|
render={({ field }) => (
|
|
194
218
|
<FormItem>
|
|
195
|
-
<FormLabel>
|
|
219
|
+
<FormLabel>{labels.amountUsd}</FormLabel>
|
|
196
220
|
<FormControl>
|
|
197
221
|
<div className="relative">
|
|
198
222
|
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-muted-foreground text-lg">
|
|
@@ -212,7 +236,7 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
212
236
|
<FormMessage />
|
|
213
237
|
{insufficientBalance && (
|
|
214
238
|
<p className="text-sm text-destructive mt-1">
|
|
215
|
-
|
|
239
|
+
{labels.insufficientBalance} (Available: ${formatUsdAmount(balanceAmount)})
|
|
216
240
|
</p>
|
|
217
241
|
)}
|
|
218
242
|
</FormItem>
|
|
@@ -225,7 +249,7 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
225
249
|
name="currency"
|
|
226
250
|
render={({ field }) => (
|
|
227
251
|
<FormItem>
|
|
228
|
-
<FormLabel>
|
|
252
|
+
<FormLabel>{labels.withdrawAs}</FormLabel>
|
|
229
253
|
<FormControl>
|
|
230
254
|
<CurrencyCombobox
|
|
231
255
|
options={currencyOptions}
|
|
@@ -245,10 +269,10 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
245
269
|
name="wallet_address"
|
|
246
270
|
render={({ field }) => (
|
|
247
271
|
<FormItem>
|
|
248
|
-
<FormLabel>
|
|
272
|
+
<FormLabel>{labels.walletAddress}</FormLabel>
|
|
249
273
|
<FormControl>
|
|
250
274
|
<Input
|
|
251
|
-
placeholder=
|
|
275
|
+
placeholder={labels.enterWalletAddress}
|
|
252
276
|
className="font-mono text-sm"
|
|
253
277
|
{...field}
|
|
254
278
|
/>
|
|
@@ -262,33 +286,33 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
262
286
|
{amount >= 10 && selectedCurrency && (
|
|
263
287
|
<div className="bg-muted rounded-xl p-4 space-y-2">
|
|
264
288
|
<div className="flex items-center justify-between text-sm">
|
|
265
|
-
<span className="text-muted-foreground">
|
|
289
|
+
<span className="text-muted-foreground">{labels.amount}</span>
|
|
266
290
|
<span>${formatUsdAmount(amount)}</span>
|
|
267
291
|
</div>
|
|
268
292
|
{isLoadingEstimate ? (
|
|
269
293
|
<div className="flex items-center gap-2 text-sm text-muted-foreground py-2">
|
|
270
294
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
271
|
-
<span>
|
|
295
|
+
<span>{labels.calculating}</span>
|
|
272
296
|
</div>
|
|
273
297
|
) : estimate ? (
|
|
274
298
|
<>
|
|
275
299
|
<div className="flex items-center justify-between text-sm">
|
|
276
300
|
<span className="text-muted-foreground">
|
|
277
|
-
|
|
301
|
+
{labels.serviceFee} ({estimate.serviceFeePercent}%)
|
|
278
302
|
</span>
|
|
279
303
|
<span className="text-destructive">
|
|
280
304
|
-${formatUsdAmount(estimate.serviceFeeUsd)}
|
|
281
305
|
</span>
|
|
282
306
|
</div>
|
|
283
307
|
<div className="flex items-center justify-between text-sm">
|
|
284
|
-
<span className="text-muted-foreground">
|
|
308
|
+
<span className="text-muted-foreground">{labels.networkFee}</span>
|
|
285
309
|
<span className="text-destructive">
|
|
286
310
|
-${formatUsdAmount(estimate.networkFeeUsd)}
|
|
287
311
|
</span>
|
|
288
312
|
</div>
|
|
289
313
|
<div className="border-t pt-2 mt-2">
|
|
290
314
|
<div className="flex items-center justify-between">
|
|
291
|
-
<span className="font-medium">
|
|
315
|
+
<span className="font-medium">{labels.youWillReceive}</span>
|
|
292
316
|
<div className="text-right">
|
|
293
317
|
<div className="font-semibold">
|
|
294
318
|
${formatUsdAmount(estimate.amountToReceive)}
|
|
@@ -313,7 +337,7 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
313
337
|
<Alert>
|
|
314
338
|
<AlertCircle className="h-4 w-4" />
|
|
315
339
|
<AlertDescription>
|
|
316
|
-
|
|
340
|
+
{labels.approvalWarning}
|
|
317
341
|
</AlertDescription>
|
|
318
342
|
</Alert>
|
|
319
343
|
|
|
@@ -341,10 +365,10 @@ export function WithdrawSheet({ open, onOpenChange, onSuccess }: WithdrawSheetPr
|
|
|
341
365
|
{isSubmitting ? (
|
|
342
366
|
<>
|
|
343
367
|
<RefreshCw className="h-5 w-5 mr-2 animate-spin" />
|
|
344
|
-
|
|
368
|
+
{labels.submitting}
|
|
345
369
|
</>
|
|
346
370
|
) : (
|
|
347
|
-
|
|
371
|
+
labels.requestWithdrawal
|
|
348
372
|
)}
|
|
349
373
|
</Button>
|
|
350
374
|
</form>
|
|
@@ -19,6 +19,9 @@ import {
|
|
|
19
19
|
import moment from 'moment';
|
|
20
20
|
import useSWR from 'swr';
|
|
21
21
|
|
|
22
|
+
import { createTypedExtensionT } from '@djangocfg/ext-base/i18n';
|
|
23
|
+
import { useT } from '@djangocfg/i18n';
|
|
24
|
+
import { PAYMENTS_NAMESPACE, type PaymentsTranslations } from '../i18n';
|
|
22
25
|
import {
|
|
23
26
|
Button,
|
|
24
27
|
CopyButton,
|
|
@@ -49,43 +52,37 @@ interface WithdrawalSheetProps {
|
|
|
49
52
|
// Status Config
|
|
50
53
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
54
|
|
|
52
|
-
const statusConfig: Record<string, { icon: any; color: string; bg: string;
|
|
55
|
+
const statusConfig: Record<string, { icon: any; color: string; bg: string; animate?: boolean }> = {
|
|
53
56
|
pending: {
|
|
54
57
|
icon: Clock,
|
|
55
58
|
color: 'text-yellow-500',
|
|
56
59
|
bg: 'bg-yellow-500/10',
|
|
57
|
-
label: 'Pending Approval',
|
|
58
60
|
},
|
|
59
61
|
approved: {
|
|
60
62
|
icon: CheckCircle2,
|
|
61
63
|
color: 'text-blue-500',
|
|
62
64
|
bg: 'bg-blue-500/10',
|
|
63
|
-
label: 'Approved',
|
|
64
65
|
},
|
|
65
66
|
processing: {
|
|
66
67
|
icon: RefreshCw,
|
|
67
68
|
color: 'text-blue-500',
|
|
68
69
|
bg: 'bg-blue-500/10',
|
|
69
|
-
label: 'Processing',
|
|
70
70
|
animate: true,
|
|
71
71
|
},
|
|
72
72
|
completed: {
|
|
73
73
|
icon: CheckCircle2,
|
|
74
74
|
color: 'text-green-500',
|
|
75
75
|
bg: 'bg-green-500/10',
|
|
76
|
-
label: 'Completed',
|
|
77
76
|
},
|
|
78
77
|
rejected: {
|
|
79
78
|
icon: XCircle,
|
|
80
79
|
color: 'text-red-500',
|
|
81
80
|
bg: 'bg-red-500/10',
|
|
82
|
-
label: 'Rejected',
|
|
83
81
|
},
|
|
84
82
|
cancelled: {
|
|
85
83
|
icon: Ban,
|
|
86
84
|
color: 'text-muted-foreground',
|
|
87
85
|
bg: 'bg-muted',
|
|
88
|
-
label: 'Cancelled',
|
|
89
86
|
},
|
|
90
87
|
};
|
|
91
88
|
|
|
@@ -94,8 +91,46 @@ const statusConfig: Record<string, { icon: any; color: string; bg: string; label
|
|
|
94
91
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
95
92
|
|
|
96
93
|
export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: WithdrawalSheetProps) {
|
|
94
|
+
const baseT = useT();
|
|
95
|
+
const pt = createTypedExtensionT<typeof PAYMENTS_NAMESPACE, PaymentsTranslations>(baseT, PAYMENTS_NAMESPACE);
|
|
97
96
|
const { getWithdrawalDetails, cancelWithdrawal, refreshWallet } = useWallet();
|
|
98
97
|
|
|
98
|
+
// Prepare labels before render
|
|
99
|
+
const labels = useMemo(() => ({
|
|
100
|
+
title: pt('sheets.withdrawalTitle'),
|
|
101
|
+
// Status labels
|
|
102
|
+
pending: pt('withdrawalStatus.pending'),
|
|
103
|
+
approved: pt('withdrawalStatus.approved'),
|
|
104
|
+
processing: pt('withdrawalStatus.processing'),
|
|
105
|
+
completed: pt('withdrawalStatus.completed'),
|
|
106
|
+
rejected: pt('withdrawalStatus.rejected'),
|
|
107
|
+
cancelled: pt('withdrawalStatus.cancelled'),
|
|
108
|
+
// Descriptions
|
|
109
|
+
pendingApproval: pt('withdrawalDescriptions.pendingApproval'),
|
|
110
|
+
processingDesc: pt('withdrawalDescriptions.processing'),
|
|
111
|
+
completedDesc: pt('withdrawalDescriptions.completed'),
|
|
112
|
+
rejectedDesc: pt('withdrawalDescriptions.rejected'),
|
|
113
|
+
cancelledDesc: pt('withdrawalDescriptions.cancelled'),
|
|
114
|
+
// Form
|
|
115
|
+
amount: pt('form.amount'),
|
|
116
|
+
totalFees: pt('estimate.serviceFee'),
|
|
117
|
+
youReceive: pt('estimate.youWillReceive'),
|
|
118
|
+
cryptoAmount: pt('form.amount'),
|
|
119
|
+
destinationWallet: pt('form.walletAddress'),
|
|
120
|
+
transactionHash: pt('form.transactionHash'),
|
|
121
|
+
withdrawalId: pt('form.paymentId'),
|
|
122
|
+
reference: pt('form.orderId'),
|
|
123
|
+
created: pt('form.created'),
|
|
124
|
+
network: pt('form.network'),
|
|
125
|
+
// Actions
|
|
126
|
+
viewOnExplorer: pt('actions.openInPaymentProvider'),
|
|
127
|
+
cancel: pt('actions.cancel'),
|
|
128
|
+
refreshStatus: pt('actions.refreshStatus'),
|
|
129
|
+
tryAgain: pt('actions.tryAgain'),
|
|
130
|
+
// Messages
|
|
131
|
+
failedToLoad: pt('messages.failedToLoadPayment'),
|
|
132
|
+
}), [pt]);
|
|
133
|
+
|
|
99
134
|
// Fetch withdrawal details when sheet is open
|
|
100
135
|
const { data: withdrawal, isLoading, error, mutate } = useSWR<WithdrawalDetail>(
|
|
101
136
|
open && withdrawalId ? ['withdrawal-details', withdrawalId] : null,
|
|
@@ -115,13 +150,23 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
115
150
|
const isProcessing = s === 'processing' || s === 'approved';
|
|
116
151
|
const canCancel = isPending;
|
|
117
152
|
|
|
153
|
+
// Status label from translations
|
|
154
|
+
const statusLabels: Record<string, string> = {
|
|
155
|
+
pending: labels.pending,
|
|
156
|
+
approved: labels.approved,
|
|
157
|
+
processing: labels.processing,
|
|
158
|
+
completed: labels.completed,
|
|
159
|
+
rejected: labels.rejected,
|
|
160
|
+
cancelled: labels.cancelled,
|
|
161
|
+
};
|
|
162
|
+
|
|
118
163
|
// Description text
|
|
119
164
|
let description = '';
|
|
120
|
-
if (isPending) description =
|
|
121
|
-
else if (isProcessing) description =
|
|
122
|
-
else if (isCompleted) description =
|
|
123
|
-
else if (isRejected) description =
|
|
124
|
-
else if (isCancelled) description =
|
|
165
|
+
if (isPending) description = labels.pendingApproval;
|
|
166
|
+
else if (isProcessing) description = labels.processingDesc;
|
|
167
|
+
else if (isCompleted) description = labels.completedDesc;
|
|
168
|
+
else if (isRejected) description = labels.rejectedDesc;
|
|
169
|
+
else if (isCancelled) description = labels.cancelledDesc;
|
|
125
170
|
|
|
126
171
|
// Formatted values
|
|
127
172
|
const amountUsd = withdrawal?.amount_usd ? `$${parseFloat(withdrawal.amount_usd).toFixed(2)}` : '';
|
|
@@ -137,6 +182,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
137
182
|
return {
|
|
138
183
|
status: s,
|
|
139
184
|
config,
|
|
185
|
+
statusLabel: statusLabels[s] || statusLabels.pending,
|
|
140
186
|
isPending,
|
|
141
187
|
isCompleted,
|
|
142
188
|
isRejected,
|
|
@@ -150,9 +196,9 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
150
196
|
createdAt,
|
|
151
197
|
completedAt,
|
|
152
198
|
};
|
|
153
|
-
}, [withdrawal]);
|
|
199
|
+
}, [withdrawal, labels]);
|
|
154
200
|
|
|
155
|
-
const { config, canCancel, description, amountUsd, finalAmountUsd, totalFeeUsd, createdAt, completedAt } = displayData;
|
|
201
|
+
const { config, statusLabel, canCancel, description, amountUsd, finalAmountUsd, totalFeeUsd, createdAt, completedAt } = displayData;
|
|
156
202
|
const StatusIcon = config.icon;
|
|
157
203
|
|
|
158
204
|
// Handle cancel
|
|
@@ -171,7 +217,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
171
217
|
<ResponsiveSheet open={open} onOpenChange={onOpenChange}>
|
|
172
218
|
<ResponsiveSheetContent className="sm:max-w-lg">
|
|
173
219
|
<ResponsiveSheetHeader>
|
|
174
|
-
<ResponsiveSheetTitle>
|
|
220
|
+
<ResponsiveSheetTitle>{labels.title}</ResponsiveSheetTitle>
|
|
175
221
|
<ResponsiveSheetDescription>{description}</ResponsiveSheetDescription>
|
|
176
222
|
</ResponsiveSheetHeader>
|
|
177
223
|
|
|
@@ -187,8 +233,8 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
187
233
|
{error && (
|
|
188
234
|
<div className="flex flex-col items-center justify-center py-12">
|
|
189
235
|
<XCircle className="h-12 w-12 text-destructive mb-4" />
|
|
190
|
-
<p className="text-sm text-muted-foreground mb-4">
|
|
191
|
-
<Button onClick={() => mutate()}>
|
|
236
|
+
<p className="text-sm text-muted-foreground mb-4">{labels.failedToLoad}</p>
|
|
237
|
+
<Button onClick={() => mutate()}>{labels.tryAgain}</Button>
|
|
192
238
|
</div>
|
|
193
239
|
)}
|
|
194
240
|
|
|
@@ -198,7 +244,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
198
244
|
<div className={cn('flex items-center gap-3 p-4 rounded-xl', config.bg)}>
|
|
199
245
|
<StatusIcon className={cn('h-6 w-6', config.color, config.animate && 'animate-spin')} />
|
|
200
246
|
<div className="flex-1">
|
|
201
|
-
<div className="font-semibold">{
|
|
247
|
+
<div className="font-semibold">{statusLabel}</div>
|
|
202
248
|
{withdrawal.admin_notes && (
|
|
203
249
|
<div className="text-sm text-muted-foreground">{withdrawal.admin_notes}</div>
|
|
204
250
|
)}
|
|
@@ -208,17 +254,17 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
208
254
|
{/* Amount Breakdown */}
|
|
209
255
|
<div className="bg-muted rounded-xl p-4 space-y-3">
|
|
210
256
|
<div className="flex items-center justify-between">
|
|
211
|
-
<span className="text-muted-foreground">
|
|
257
|
+
<span className="text-muted-foreground">{labels.amount}</span>
|
|
212
258
|
<span className="font-semibold">{amountUsd}</span>
|
|
213
259
|
</div>
|
|
214
260
|
{totalFeeUsd && (
|
|
215
261
|
<div className="flex items-center justify-between text-sm">
|
|
216
|
-
<span className="text-muted-foreground">
|
|
262
|
+
<span className="text-muted-foreground">{labels.totalFees}</span>
|
|
217
263
|
<span className="text-destructive">-{totalFeeUsd}</span>
|
|
218
264
|
</div>
|
|
219
265
|
)}
|
|
220
266
|
<div className="flex items-center justify-between pt-2 border-t">
|
|
221
|
-
<span className="text-muted-foreground">
|
|
267
|
+
<span className="text-muted-foreground">{labels.youReceive}</span>
|
|
222
268
|
<div className="flex items-center gap-2">
|
|
223
269
|
<TokenIcon symbol={withdrawal.currency_code} size={24} />
|
|
224
270
|
<span className="font-mono font-bold text-lg">
|
|
@@ -228,7 +274,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
228
274
|
</div>
|
|
229
275
|
{withdrawal.crypto_amount && (
|
|
230
276
|
<div className="flex items-center justify-between text-sm">
|
|
231
|
-
<span className="text-muted-foreground">
|
|
277
|
+
<span className="text-muted-foreground">{labels.cryptoAmount}</span>
|
|
232
278
|
<span className="font-mono">{withdrawal.crypto_amount} {withdrawal.currency_token}</span>
|
|
233
279
|
</div>
|
|
234
280
|
)}
|
|
@@ -237,7 +283,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
237
283
|
{/* Wallet Address */}
|
|
238
284
|
{withdrawal.wallet_address && (
|
|
239
285
|
<div className="space-y-2">
|
|
240
|
-
<label className="text-sm font-medium">
|
|
286
|
+
<label className="text-sm font-medium">{labels.destinationWallet}</label>
|
|
241
287
|
<div className="flex items-center gap-2">
|
|
242
288
|
<div className="flex-1 p-3 bg-muted rounded-xl font-mono text-sm break-all">
|
|
243
289
|
{withdrawal.wallet_address}
|
|
@@ -250,7 +296,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
250
296
|
{/* Transaction Hash (if completed) */}
|
|
251
297
|
{withdrawal.transaction_hash && (
|
|
252
298
|
<div className="space-y-2">
|
|
253
|
-
<label className="text-sm font-medium">
|
|
299
|
+
<label className="text-sm font-medium">{labels.transactionHash}</label>
|
|
254
300
|
<div className="flex items-center gap-2">
|
|
255
301
|
<div className="flex-1 p-3 bg-muted rounded-xl font-mono text-sm break-all">
|
|
256
302
|
{withdrawal.transaction_hash}
|
|
@@ -268,7 +314,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
268
314
|
onClick={() => window.open(withdrawal.explorer_link!, '_blank')}
|
|
269
315
|
>
|
|
270
316
|
<ExternalLink className="h-4 w-4 mr-2" />
|
|
271
|
-
|
|
317
|
+
{labels.viewOnExplorer}
|
|
272
318
|
</Button>
|
|
273
319
|
)}
|
|
274
320
|
|
|
@@ -280,35 +326,35 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
280
326
|
onClick={handleCancel}
|
|
281
327
|
>
|
|
282
328
|
<Ban className="h-4 w-4 mr-2" />
|
|
283
|
-
|
|
329
|
+
{labels.cancel}
|
|
284
330
|
</Button>
|
|
285
331
|
)}
|
|
286
332
|
|
|
287
333
|
{/* Metadata */}
|
|
288
334
|
<div className="space-y-2 text-xs text-muted-foreground pt-4 border-t">
|
|
289
335
|
<div className="flex justify-between">
|
|
290
|
-
<span>
|
|
336
|
+
<span>{labels.withdrawalId}</span>
|
|
291
337
|
<span className="font-mono">{withdrawal.id}</span>
|
|
292
338
|
</div>
|
|
293
339
|
{withdrawal.internal_withdrawal_id && (
|
|
294
340
|
<div className="flex justify-between">
|
|
295
|
-
<span>
|
|
341
|
+
<span>{labels.reference}</span>
|
|
296
342
|
<span className="font-mono">{withdrawal.internal_withdrawal_id}</span>
|
|
297
343
|
</div>
|
|
298
344
|
)}
|
|
299
345
|
<div className="flex justify-between">
|
|
300
|
-
<span>
|
|
346
|
+
<span>{labels.created}</span>
|
|
301
347
|
<span>{createdAt}</span>
|
|
302
348
|
</div>
|
|
303
349
|
{completedAt && (
|
|
304
350
|
<div className="flex justify-between">
|
|
305
|
-
<span>
|
|
351
|
+
<span>{labels.completed}</span>
|
|
306
352
|
<span>{completedAt}</span>
|
|
307
353
|
</div>
|
|
308
354
|
)}
|
|
309
355
|
{withdrawal.currency_network && (
|
|
310
356
|
<div className="flex justify-between">
|
|
311
|
-
<span>
|
|
357
|
+
<span>{labels.network}</span>
|
|
312
358
|
<span>{withdrawal.currency_network}</span>
|
|
313
359
|
</div>
|
|
314
360
|
)}
|
|
@@ -321,7 +367,7 @@ export function WithdrawalSheet({ withdrawalId, open, onOpenChange }: Withdrawal
|
|
|
321
367
|
onClick={() => mutate()}
|
|
322
368
|
>
|
|
323
369
|
<RefreshCw className="h-4 w-4 mr-2" />
|
|
324
|
-
|
|
370
|
+
{labels.refreshStatus}
|
|
325
371
|
</Button>
|
|
326
372
|
</div>
|
|
327
373
|
)}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payments Extension I18n
|
|
3
|
+
*
|
|
4
|
+
* Provides translations for the payments extension.
|
|
5
|
+
* Can be merged with app translations using mergeExtensionTranslations from @djangocfg/ext-base/i18n
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createExtensionI18n } from '@djangocfg/ext-base/i18n';
|
|
9
|
+
import type { PaymentsTranslations } from './types';
|
|
10
|
+
import { en } from './locales/en';
|
|
11
|
+
import { ru } from './locales/ru';
|
|
12
|
+
import { ko } from './locales/ko';
|
|
13
|
+
|
|
14
|
+
/** Payments extension namespace */
|
|
15
|
+
export const PAYMENTS_NAMESPACE = 'payments' as const;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Payments extension i18n instance
|
|
19
|
+
*/
|
|
20
|
+
export const paymentsI18n = createExtensionI18n<PaymentsTranslations>({
|
|
21
|
+
namespace: PAYMENTS_NAMESPACE,
|
|
22
|
+
defaultLocale: 'en',
|
|
23
|
+
locales: { en, ru, ko },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* All payments translations ready to merge with app translations
|
|
28
|
+
*/
|
|
29
|
+
export const paymentsTranslations = paymentsI18n.getAllTranslations();
|
|
30
|
+
|
|
31
|
+
// Types
|
|
32
|
+
export type { PaymentsTranslations, PaymentsKeys, PaymentsLocalKeys } from './types';
|
|
33
|
+
|
|
34
|
+
// Re-export locales for direct access if needed
|
|
35
|
+
export { en, ru, ko };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { PaymentsTranslations } from '../types';
|
|
2
|
+
|
|
3
|
+
export const en: PaymentsTranslations = {
|
|
4
|
+
balance: {
|
|
5
|
+
available: 'Available Balance',
|
|
6
|
+
totalDeposited: 'Total Deposited',
|
|
7
|
+
totalWithdrawn: 'Total Withdrawn',
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
actions: {
|
|
11
|
+
addFunds: 'Add Funds',
|
|
12
|
+
withdraw: 'Withdraw',
|
|
13
|
+
continue: 'Continue',
|
|
14
|
+
requestWithdrawal: 'Request Withdrawal',
|
|
15
|
+
cancel: 'Cancel',
|
|
16
|
+
close: 'Close',
|
|
17
|
+
copyAddress: 'Copy Address',
|
|
18
|
+
copied: 'Copied!',
|
|
19
|
+
viewAll: 'View All',
|
|
20
|
+
tryAgain: 'Try Again',
|
|
21
|
+
refreshStatus: 'Refresh Status',
|
|
22
|
+
createNewPayment: 'Create New Payment',
|
|
23
|
+
openInPaymentProvider: 'Open in Payment Provider',
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
paymentStatus: {
|
|
27
|
+
waiting: 'Waiting for payment',
|
|
28
|
+
confirming: 'Confirming',
|
|
29
|
+
completed: 'Completed',
|
|
30
|
+
failed: 'Failed',
|
|
31
|
+
expired: 'Expired',
|
|
32
|
+
paymentExpired: 'Payment Expired',
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
withdrawalStatus: {
|
|
36
|
+
pending: 'Pending Approval',
|
|
37
|
+
approved: 'Approved',
|
|
38
|
+
processing: 'Processing',
|
|
39
|
+
completed: 'Completed',
|
|
40
|
+
rejected: 'Rejected',
|
|
41
|
+
cancelled: 'Cancelled',
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
paymentDescriptions: {
|
|
45
|
+
sendCrypto: 'Send cryptocurrency to complete payment',
|
|
46
|
+
expired: 'This payment has expired',
|
|
47
|
+
completed: 'Payment completed successfully',
|
|
48
|
+
failed: 'Payment failed',
|
|
49
|
+
confirming: 'Confirming your payment',
|
|
50
|
+
createNew: 'Please create a new payment to continue',
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
withdrawalDescriptions: {
|
|
54
|
+
pendingApproval: 'Waiting for admin approval',
|
|
55
|
+
processing: 'Your withdrawal is being processed',
|
|
56
|
+
completed: 'Withdrawal completed successfully',
|
|
57
|
+
rejected: 'Withdrawal was rejected',
|
|
58
|
+
cancelled: 'Withdrawal was cancelled',
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
form: {
|
|
62
|
+
amount: 'Amount',
|
|
63
|
+
amountUsd: 'Amount (USD)',
|
|
64
|
+
currency: 'Currency',
|
|
65
|
+
walletAddress: 'Wallet Address',
|
|
66
|
+
selectCurrency: 'Select currency...',
|
|
67
|
+
enterWalletAddress: 'Enter your wallet address',
|
|
68
|
+
search: 'Search...',
|
|
69
|
+
payWith: 'Pay with',
|
|
70
|
+
withdrawAs: 'Withdraw as',
|
|
71
|
+
amountToSend: 'Amount to send',
|
|
72
|
+
equivalent: 'Equivalent',
|
|
73
|
+
network: 'Network',
|
|
74
|
+
paymentAddress: 'Payment Address',
|
|
75
|
+
transactionHash: 'Transaction Hash',
|
|
76
|
+
paymentId: 'Payment ID',
|
|
77
|
+
orderId: 'Order #',
|
|
78
|
+
created: 'Created',
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
validation: {
|
|
82
|
+
minimumDeposit: 'Minimum $1',
|
|
83
|
+
minimumWithdrawal: 'Minimum $10',
|
|
84
|
+
selectCurrency: 'Select a currency',
|
|
85
|
+
invalidWalletAddress: 'Invalid wallet address',
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
messages: {
|
|
89
|
+
paymentCreated: 'Payment created successfully',
|
|
90
|
+
paymentFailed: 'Failed to create payment',
|
|
91
|
+
withdrawalCreated: 'Withdrawal request created',
|
|
92
|
+
withdrawalFailed: 'Failed to create withdrawal request',
|
|
93
|
+
withdrawalCancelled: 'Withdrawal cancelled',
|
|
94
|
+
addressCopied: 'Address copied to clipboard',
|
|
95
|
+
failedToLoadPayment: 'Failed to load payment',
|
|
96
|
+
failedToCreateWithdrawal: 'Failed to create withdrawal request',
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
sheets: {
|
|
100
|
+
addFundsTitle: 'Add Funds',
|
|
101
|
+
addFundsDescription: 'Add funds to your wallet using cryptocurrency',
|
|
102
|
+
withdrawTitle: 'Withdraw',
|
|
103
|
+
withdrawDescription: 'Withdraw funds to your cryptocurrency wallet',
|
|
104
|
+
paymentTitle: 'Payment Details',
|
|
105
|
+
withdrawalTitle: 'Withdrawal Details',
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
activity: {
|
|
109
|
+
title: 'Recent Activity',
|
|
110
|
+
noActivity: 'No Activity Yet',
|
|
111
|
+
deposit: 'Deposit',
|
|
112
|
+
withdrawal: 'Withdrawal',
|
|
113
|
+
payment: 'Payment',
|
|
114
|
+
transactionsWillAppear: 'Your transactions will appear here',
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
estimate: {
|
|
118
|
+
gettingRate: 'Getting rate...',
|
|
119
|
+
calculating: 'Calculating fees...',
|
|
120
|
+
enterAmountToSee: 'Enter amount to see conversion',
|
|
121
|
+
youWillSend: 'You will send',
|
|
122
|
+
youWillReceive: 'You will receive',
|
|
123
|
+
serviceFee: 'Service fee',
|
|
124
|
+
networkFee: 'Network fee',
|
|
125
|
+
rate: 'Rate',
|
|
126
|
+
minimumAmount: 'Minimum amount',
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
withdraw: {
|
|
130
|
+
insufficientBalance: 'Insufficient balance',
|
|
131
|
+
approvalWarning: 'Withdrawal requests require admin approval. Processing may take 24-48 hours.',
|
|
132
|
+
submitting: 'Submitting...',
|
|
133
|
+
creating: 'Creating...',
|
|
134
|
+
expiresIn: 'Expires in',
|
|
135
|
+
},
|
|
136
|
+
};
|