@djangocfg/ext-payments 1.0.21 → 1.0.23

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ext-payments",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "description": "Payments system extension for DjangoCFG",
5
5
  "keywords": [
6
6
  "django",
@@ -51,6 +51,11 @@
51
51
  "types": "./dist/api/hooks.d.ts",
52
52
  "import": "./dist/api/hooks.js",
53
53
  "require": "./dist/api/hooks.cjs"
54
+ },
55
+ "./i18n": {
56
+ "types": "./dist/i18n.d.ts",
57
+ "import": "./dist/i18n.js",
58
+ "require": "./dist/i18n.cjs"
54
59
  }
55
60
  },
56
61
  "files": [
@@ -64,29 +69,33 @@
64
69
  "check": "tsc --noEmit"
65
70
  },
66
71
  "peerDependencies": {
67
- "@djangocfg/api": "^2.1.109",
68
- "@djangocfg/ext-base": "^1.0.16",
69
- "@djangocfg/ui-core": "^2.1.109",
70
- "@djangocfg/ui-nextjs": "^2.1.109",
72
+ "@djangocfg/api": "^2.1.124",
73
+ "@djangocfg/ext-base": "^1.0.18",
74
+ "@djangocfg/i18n": "^2.1.124",
75
+ "@djangocfg/ui-core": "^2.1.124",
76
+ "@djangocfg/ui-nextjs": "^2.1.124",
77
+ "@hookform/resolvers": "^5.2.2",
71
78
  "consola": "^3.4.2",
72
79
  "lucide-react": "^0.545.0",
80
+ "moment": "^2.30.1",
73
81
  "next": "^16",
82
+ "next-intl": "^4",
74
83
  "p-retry": "^7.0.0",
75
84
  "react": "^19",
76
- "swr": "^2.3.7",
77
- "zod": "^4.3.4",
78
- "moment": "^2.30.1",
79
85
  "react-hook-form": "^7.69.0",
80
- "@hookform/resolvers": "^5.2.2"
86
+ "swr": "^2.3.7",
87
+ "zod": "^4.3.4"
81
88
  },
82
89
  "devDependencies": {
83
- "@djangocfg/api": "^2.1.109",
84
- "@djangocfg/ext-base": "^1.0.16",
85
- "@djangocfg/typescript-config": "^2.1.109",
86
- "@djangocfg/ui-nextjs": "^2.1.109",
90
+ "@djangocfg/api": "^2.1.124",
91
+ "@djangocfg/ext-base": "^1.0.18",
92
+ "@djangocfg/i18n": "^2.1.124",
93
+ "@djangocfg/typescript-config": "^2.1.124",
94
+ "@djangocfg/ui-nextjs": "^2.1.124",
87
95
  "@types/node": "^24.7.2",
88
96
  "@types/react": "^19.0.0",
89
97
  "consola": "^3.4.2",
98
+ "next-intl": "^4.1.0",
90
99
  "p-retry": "^7.0.0",
91
100
  "swr": "^2.3.7",
92
101
  "tsup": "^8.5.0",
@@ -6,8 +6,10 @@
6
6
 
7
7
  'use client';
8
8
 
9
+ import { useMemo } from 'react';
9
10
  import { History, ChevronRight } from 'lucide-react';
10
11
 
12
+ import { usePaymentsT } from '../i18n';
11
13
  import { Button, Skeleton } from '@djangocfg/ui-core';
12
14
  import { cn } from '@djangocfg/ui-core/lib';
13
15
 
@@ -27,8 +29,17 @@ export function ActivityList({
27
29
  limit = 10,
28
30
  className,
29
31
  }: ActivityListProps) {
32
+ const pt = usePaymentsT();
30
33
  const { activity, isLoadingActivity, hasMoreActivity } = useWallet();
31
34
 
35
+ // Prepare labels before render
36
+ const labels = useMemo(() => ({
37
+ title: pt('activity.title'),
38
+ noActivity: pt('activity.noActivity'),
39
+ transactionsWillAppear: pt('activity.transactionsWillAppear'),
40
+ viewAll: pt('actions.viewAll'),
41
+ }), [pt]);
42
+
32
43
  const displayedActivity = limit ? activity.slice(0, limit) : activity;
33
44
 
34
45
  if (isLoadingActivity) {
@@ -57,9 +68,9 @@ export function ActivityList({
57
68
  <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-muted mb-4">
58
69
  <History className="h-8 w-8 text-muted-foreground" />
59
70
  </div>
60
- <h3 className="font-semibold mb-1">No Activity Yet</h3>
71
+ <h3 className="font-semibold mb-1">{labels.noActivity}</h3>
61
72
  <p className="text-sm text-muted-foreground">
62
- Your transactions will appear here
73
+ {labels.transactionsWillAppear}
63
74
  </p>
64
75
  </div>
65
76
  );
@@ -69,10 +80,10 @@ export function ActivityList({
69
80
  <div className={cn('pt-6', className)}>
70
81
  {/* Header */}
71
82
  <div className="flex items-center justify-between px-4 py-2">
72
- <h2 className="font-semibold text-lg">Recent Activity</h2>
83
+ <h2 className="font-semibold text-lg">{labels.title}</h2>
73
84
  {hasMoreActivity && onViewAll && (
74
85
  <Button variant="ghost" size="sm" onClick={onViewAll} className="text-primary">
75
- View All
86
+ {labels.viewAll}
76
87
  <ChevronRight className="h-4 w-4 ml-1" />
77
88
  </Button>
78
89
  )}
@@ -13,6 +13,7 @@ import { useForm } from 'react-hook-form';
13
13
  import { z } from 'zod';
14
14
  import { zodResolver } from '@hookform/resolvers/zod';
15
15
 
16
+ import { usePaymentsT } from '../i18n';
16
17
  import {
17
18
  Alert,
18
19
  AlertDescription,
@@ -73,10 +74,29 @@ interface AddFundsSaved {
73
74
  }
74
75
 
75
76
  export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetProps) {
77
+ const pt = usePaymentsT();
76
78
  const { currencies, isLoadingCurrencies, addFunds } = useWallet();
77
79
  const [isSubmitting, setIsSubmitting] = useState(false);
78
80
  const [error, setError] = useState<string | null>(null);
79
81
 
82
+ // Prepare labels before render
83
+ const labels = useMemo(() => ({
84
+ title: pt('sheets.addFundsTitle'),
85
+ description: pt('sheets.addFundsDescription'),
86
+ amountUsd: pt('form.amountUsd'),
87
+ payWith: pt('form.payWith'),
88
+ amount: pt('form.amount'),
89
+ serviceFee: pt('estimate.serviceFee'),
90
+ rate: pt('estimate.rate'),
91
+ youWillSend: pt('estimate.youWillSend'),
92
+ youWillReceive: pt('estimate.youWillReceive'),
93
+ gettingRate: pt('estimate.gettingRate'),
94
+ enterAmountToSee: pt('estimate.enterAmountToSee'),
95
+ minimumAmount: pt('estimate.minimumAmount'),
96
+ continue: pt('actions.continue'),
97
+ creating: pt('withdraw.creating'),
98
+ }), [pt]);
99
+
80
100
  // Remember last used currency and amount
81
101
  const [saved, setSaved] = useLocalStorage<AddFundsSaved>(STORAGE_KEY, {
82
102
  currency: '',
@@ -183,9 +203,9 @@ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetPr
183
203
  <ResponsiveSheet open={open} onOpenChange={handleOpenChange}>
184
204
  <ResponsiveSheetContent className="sm:max-w-md">
185
205
  <ResponsiveSheetHeader>
186
- <ResponsiveSheetTitle>Add Funds</ResponsiveSheetTitle>
206
+ <ResponsiveSheetTitle>{labels.title}</ResponsiveSheetTitle>
187
207
  <ResponsiveSheetDescription>
188
- Add funds to your wallet using cryptocurrency
208
+ {labels.description}
189
209
  </ResponsiveSheetDescription>
190
210
  </ResponsiveSheetHeader>
191
211
 
@@ -197,7 +217,7 @@ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetPr
197
217
  name="amount"
198
218
  render={({ field }) => (
199
219
  <FormItem>
200
- <FormLabel>Amount (USD)</FormLabel>
220
+ <FormLabel>{labels.amountUsd}</FormLabel>
201
221
  <FormControl>
202
222
  <div className="relative">
203
223
  <span className="absolute left-4 top-1/2 -translate-y-1/2 text-muted-foreground text-lg">
@@ -225,7 +245,7 @@ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetPr
225
245
  name="currency"
226
246
  render={({ field }) => (
227
247
  <FormItem>
228
- <FormLabel>Pay with</FormLabel>
248
+ <FormLabel>{labels.payWith}</FormLabel>
229
249
  <FormControl>
230
250
  <CurrencyCombobox
231
251
  options={currencyOptions}
@@ -245,24 +265,24 @@ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetPr
245
265
  {isLoadingEstimate ? (
246
266
  <div className="flex items-center justify-center py-2">
247
267
  <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
248
- <span className="ml-2 text-sm text-muted-foreground">Getting rate...</span>
268
+ <span className="ml-2 text-sm text-muted-foreground">{labels.gettingRate}</span>
249
269
  </div>
250
270
  ) : displayData ? (
251
271
  <>
252
272
  {/* Amount breakdown */}
253
273
  <div className="flex items-center justify-between text-sm">
254
- <span className="text-muted-foreground">Amount</span>
274
+ <span className="text-muted-foreground">{labels.amount}</span>
255
275
  <span>${formatUsdAmount(displayData.amountToReceive)}</span>
256
276
  </div>
257
277
  <div className="flex items-center justify-between text-sm">
258
- <span className="text-muted-foreground">Service fee ({displayData.serviceFeePercent}%)</span>
278
+ <span className="text-muted-foreground">{labels.serviceFee} ({displayData.serviceFeePercent}%)</span>
259
279
  <span>+${formatUsdAmount(displayData.serviceFeeUsd)}</span>
260
280
  </div>
261
281
 
262
282
  {/* Rate (for non-stablecoins) */}
263
283
  {displayData.showRate && (
264
284
  <div className="flex items-center justify-between text-sm text-muted-foreground">
265
- <span>Rate</span>
285
+ <span>{labels.rate}</span>
266
286
  <span>1 {displayData.token} = ${displayData.rate}</span>
267
287
  </div>
268
288
  )}
@@ -270,7 +290,7 @@ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetPr
270
290
  {/* You will send */}
271
291
  <div className="border-t pt-2 mt-2">
272
292
  <div className="flex items-center justify-between">
273
- <span className="font-medium">You will send</span>
293
+ <span className="font-medium">{labels.youWillSend}</span>
274
294
  <div className="text-right">
275
295
  <div className="flex items-center gap-2 justify-end">
276
296
  <TokenIcon symbol={displayData.token} size={20} />
@@ -287,20 +307,20 @@ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetPr
287
307
 
288
308
  {/* You will receive */}
289
309
  <div className="flex items-center justify-between text-sm pt-2">
290
- <span className="text-muted-foreground">You will receive</span>
310
+ <span className="text-muted-foreground">{labels.youWillReceive}</span>
291
311
  <span className="font-medium">${formatUsdAmount(displayData.amountToReceive)}</span>
292
312
  </div>
293
313
 
294
314
  {/* Minimum warning */}
295
315
  {displayData.belowMinimum && displayData.minAmount && (
296
316
  <div className="text-sm text-destructive mt-2 pt-2 border-t border-destructive/20">
297
- Minimum amount: ${displayData.minAmount}
317
+ {labels.minimumAmount}: ${displayData.minAmount}
298
318
  </div>
299
319
  )}
300
320
  </>
301
321
  ) : (
302
322
  <div className="text-center text-sm text-muted-foreground py-2">
303
- Enter amount to see conversion
323
+ {labels.enterAmountToSee}
304
324
  </div>
305
325
  )}
306
326
  </div>
@@ -328,10 +348,10 @@ export function AddFundsSheet({ open, onOpenChange, onSuccess }: AddFundsSheetPr
328
348
  {isSubmitting ? (
329
349
  <>
330
350
  <RefreshCw className="h-5 w-5 mr-2 animate-spin" />
331
- Creating...
351
+ {labels.creating}
332
352
  </>
333
353
  ) : (
334
- 'Continue'
354
+ labels.continue
335
355
  )}
336
356
  </Button>
337
357
  </form>
@@ -6,8 +6,10 @@
6
6
 
7
7
  'use client';
8
8
 
9
+ import { useMemo } from 'react';
9
10
  import { Plus, ArrowUpRight, RefreshCw } from 'lucide-react';
10
11
 
12
+ import { usePaymentsT } from '../i18n';
11
13
  import { Button, Skeleton } from '@djangocfg/ui-core';
12
14
  import { cn } from '@djangocfg/ui-core/lib';
13
15
 
@@ -20,8 +22,18 @@ interface BalanceHeroProps {
20
22
  }
21
23
 
22
24
  export function BalanceHero({ onAddFunds, onWithdraw, className }: BalanceHeroProps) {
25
+ const pt = usePaymentsT();
23
26
  const { balance, balanceAmount, isLoadingBalance, refreshWallet } = useWallet();
24
27
 
28
+ // Prepare labels before render
29
+ const labels = useMemo(() => ({
30
+ available: pt('balance.available'),
31
+ totalDeposited: pt('balance.totalDeposited'),
32
+ totalWithdrawn: pt('balance.totalWithdrawn'),
33
+ addFunds: pt('actions.addFunds'),
34
+ withdraw: pt('actions.withdraw'),
35
+ }), [pt]);
36
+
25
37
  const formattedBalance = new Intl.NumberFormat('en-US', {
26
38
  style: 'currency',
27
39
  currency: 'USD',
@@ -43,7 +55,7 @@ export function BalanceHero({ onAddFunds, onWithdraw, className }: BalanceHeroPr
43
55
  <h1 className="text-5xl font-bold tracking-tight tabular-nums">
44
56
  {formattedBalance}
45
57
  </h1>
46
- <p className="text-muted-foreground mt-1">Available Balance</p>
58
+ <p className="text-muted-foreground mt-1">{labels.available}</p>
47
59
  </>
48
60
  )}
49
61
  </div>
@@ -56,7 +68,7 @@ export function BalanceHero({ onAddFunds, onWithdraw, className }: BalanceHeroPr
56
68
  className="rounded-full px-6"
57
69
  >
58
70
  <Plus className="h-5 w-5 mr-2" />
59
- Add Funds
71
+ {labels.addFunds}
60
72
  </Button>
61
73
 
62
74
  <Button
@@ -66,7 +78,7 @@ export function BalanceHero({ onAddFunds, onWithdraw, className }: BalanceHeroPr
66
78
  className="rounded-full px-6"
67
79
  >
68
80
  <ArrowUpRight className="h-5 w-5 mr-2" />
69
- Withdraw
81
+ {labels.withdraw}
70
82
  </Button>
71
83
 
72
84
  <Button
@@ -86,14 +98,14 @@ export function BalanceHero({ onAddFunds, onWithdraw, className }: BalanceHeroPr
86
98
  <span className="block font-medium text-foreground">
87
99
  ${parseFloat(balance.total_deposited || '0').toFixed(2)}
88
100
  </span>
89
- <span>Total Deposited</span>
101
+ <span>{labels.totalDeposited}</span>
90
102
  </div>
91
103
  <div className="h-8 w-px bg-border" />
92
104
  <div className="text-center">
93
105
  <span className="block font-medium text-foreground">
94
106
  ${parseFloat(balance.total_withdrawn || '0').toFixed(2)}
95
107
  </span>
96
- <span>Total Withdrawn</span>
108
+ <span>{labels.totalWithdrawn}</span>
97
109
  </div>
98
110
  </div>
99
111
  )}
@@ -4,6 +4,9 @@
4
4
  * Currency Combobox with TokenIcon
5
5
  */
6
6
 
7
+ import { useMemo } from 'react';
8
+
9
+ import { usePaymentsT } from '../i18n';
7
10
  import { Combobox, TokenIcon } from '@djangocfg/ui-core';
8
11
  import type { CurrencyOption } from '../types';
9
12
 
@@ -21,15 +24,22 @@ export function CurrencyCombobox({
21
24
  value,
22
25
  onChange,
23
26
  disabled,
24
- placeholder = 'Select currency...',
27
+ placeholder,
25
28
  }: CurrencyComboboxProps) {
29
+ const pt = usePaymentsT();
30
+
31
+ const labels = useMemo(() => ({
32
+ selectCurrency: pt('form.selectCurrency'),
33
+ search: pt('form.search'),
34
+ }), [pt]);
35
+
26
36
  return (
27
37
  <Combobox
28
38
  options={options}
29
39
  value={value}
30
40
  onValueChange={onChange}
31
- placeholder={placeholder}
32
- searchPlaceholder="Search..."
41
+ placeholder={placeholder || labels.selectCurrency}
42
+ searchPlaceholder={labels.search}
33
43
  disabled={disabled}
34
44
  className="h-14"
35
45
  renderOption={(option) => (
@@ -19,6 +19,7 @@ import {
19
19
  import moment from 'moment';
20
20
  import useSWR from 'swr';
21
21
 
22
+ import { usePaymentsT } from '../i18n';
22
23
  import {
23
24
  Button,
24
25
  CopyButton,
@@ -50,37 +51,32 @@ interface PaymentSheetProps {
50
51
  // Status Config
51
52
  // ─────────────────────────────────────────────────────────────────────────────
52
53
 
53
- const statusConfig: Record<string, { icon: any; color: string; bg: string; label: string; animate?: boolean }> = {
54
+ const statusConfig: Record<string, { icon: any; color: string; bg: string; animate?: boolean }> = {
54
55
  pending: {
55
56
  icon: Clock,
56
57
  color: 'text-yellow-500',
57
58
  bg: 'bg-yellow-500/10',
58
- label: 'Waiting for payment',
59
59
  },
60
60
  confirming: {
61
61
  icon: RefreshCw,
62
62
  color: 'text-blue-500',
63
63
  bg: 'bg-blue-500/10',
64
- label: 'Confirming',
65
64
  animate: true,
66
65
  },
67
66
  completed: {
68
67
  icon: CheckCircle2,
69
68
  color: 'text-green-500',
70
69
  bg: 'bg-green-500/10',
71
- label: 'Completed',
72
70
  },
73
71
  failed: {
74
72
  icon: XCircle,
75
73
  color: 'text-red-500',
76
74
  bg: 'bg-red-500/10',
77
- label: 'Failed',
78
75
  },
79
76
  expired: {
80
77
  icon: AlertCircle,
81
78
  color: 'text-muted-foreground',
82
79
  bg: 'bg-muted',
83
- label: 'Expired',
84
80
  },
85
81
  };
86
82
 
@@ -89,9 +85,47 @@ const statusConfig: Record<string, { icon: any; color: string; bg: string; label
89
85
  // ─────────────────────────────────────────────────────────────────────────────
90
86
 
91
87
  export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: PaymentSheetProps) {
88
+ const pt = usePaymentsT();
92
89
  const { getPaymentDetails } = useWallet();
93
90
  const [timeLeft, setTimeLeft] = useState<string>('');
94
91
 
92
+ // Prepare labels before render
93
+ const labels = useMemo(() => ({
94
+ title: pt('sheets.paymentTitle'),
95
+ // Status labels
96
+ waiting: pt('paymentStatus.waiting'),
97
+ confirming: pt('paymentStatus.confirming'),
98
+ completed: pt('paymentStatus.completed'),
99
+ failed: pt('paymentStatus.failed'),
100
+ expired: pt('paymentStatus.expired'),
101
+ paymentExpired: pt('paymentStatus.paymentExpired'),
102
+ // Descriptions
103
+ sendCrypto: pt('paymentDescriptions.sendCrypto'),
104
+ expiredDesc: pt('paymentDescriptions.expired'),
105
+ completedDesc: pt('paymentDescriptions.completed'),
106
+ failedDesc: pt('paymentDescriptions.failed'),
107
+ confirmingDesc: pt('paymentDescriptions.confirming'),
108
+ createNew: pt('paymentDescriptions.createNew'),
109
+ // Form
110
+ amountToSend: pt('form.amountToSend'),
111
+ equivalent: pt('form.equivalent'),
112
+ network: pt('form.network'),
113
+ paymentAddress: pt('form.paymentAddress'),
114
+ transactionHash: pt('form.transactionHash'),
115
+ paymentId: pt('form.paymentId'),
116
+ orderId: pt('form.orderId'),
117
+ created: pt('form.created'),
118
+ // Actions
119
+ tryAgain: pt('actions.tryAgain'),
120
+ refreshStatus: pt('actions.refreshStatus'),
121
+ createNewPayment: pt('actions.createNewPayment'),
122
+ openInPaymentProvider: pt('actions.openInPaymentProvider'),
123
+ // Withdraw
124
+ expiresIn: pt('withdraw.expiresIn'),
125
+ // Messages
126
+ failedToLoadPayment: pt('messages.failedToLoadPayment'),
127
+ }), [pt]);
128
+
95
129
  // Fetch payment details when sheet is open
96
130
  const { data: payment, isLoading, error, mutate } = useSWR<PaymentDetail>(
97
131
  open && paymentId ? ['payment-details', paymentId] : null,
@@ -145,24 +179,33 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
145
179
  const isConfirming = status === 'confirming';
146
180
  const canPay = isPending && !isExpired;
147
181
 
182
+ // Status label from translations
183
+ const statusLabels: Record<string, string> = {
184
+ pending: labels.waiting,
185
+ confirming: labels.confirming,
186
+ completed: labels.completed,
187
+ failed: labels.failed,
188
+ expired: labels.expired,
189
+ };
190
+
148
191
  // Description text
149
192
  let description = '';
150
- if (canPay) description = 'Send cryptocurrency to complete payment';
151
- else if (isExpired) description = 'This payment has expired';
152
- else if (isCompleted) description = 'Payment completed successfully';
153
- else if (isFailed) description = 'Payment failed';
154
- else if (isConfirming) description = 'Confirming your payment';
193
+ if (canPay) description = labels.sendCrypto;
194
+ else if (isExpired) description = labels.expiredDesc;
195
+ else if (isCompleted) description = labels.completedDesc;
196
+ else if (isFailed) description = labels.failedDesc;
197
+ else if (isConfirming) description = labels.confirmingDesc;
155
198
 
156
199
  // Status badge
157
200
  const statusBadge = {
158
201
  bg: isExpired ? 'bg-muted' : config.bg,
159
202
  iconColor: isExpired ? 'text-muted-foreground' : config.color,
160
203
  iconAnimate: config.animate,
161
- label: isExpired ? 'Payment Expired' : config.label,
204
+ label: isExpired ? labels.paymentExpired : statusLabels[status],
162
205
  subtitle: canPay && timeLeft
163
- ? `Expires in ${timeLeft}`
206
+ ? `${labels.expiresIn} ${timeLeft}`
164
207
  : isExpired
165
- ? 'Please create a new payment to continue'
208
+ ? labels.createNew
166
209
  : null,
167
210
  };
168
211
 
@@ -192,7 +235,7 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
192
235
  amountUsd,
193
236
  createdAt,
194
237
  };
195
- }, [payment, timeLeft]);
238
+ }, [payment, timeLeft, labels]);
196
239
 
197
240
  const { config, canPay, isExpired, description, statusBadge, qrCodeUrl, amountUsd, createdAt } = displayData;
198
241
  const StatusIcon = config.icon;
@@ -201,7 +244,7 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
201
244
  <ResponsiveSheet open={open} onOpenChange={onOpenChange}>
202
245
  <ResponsiveSheetContent className="sm:max-w-lg">
203
246
  <ResponsiveSheetHeader>
204
- <ResponsiveSheetTitle>Payment Details</ResponsiveSheetTitle>
247
+ <ResponsiveSheetTitle>{labels.title}</ResponsiveSheetTitle>
205
248
  <ResponsiveSheetDescription>{description}</ResponsiveSheetDescription>
206
249
  </ResponsiveSheetHeader>
207
250
 
@@ -219,8 +262,8 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
219
262
  {error && (
220
263
  <div className="flex flex-col items-center justify-center py-12">
221
264
  <XCircle className="h-12 w-12 text-destructive mb-4" />
222
- <p className="text-sm text-muted-foreground mb-4">Failed to load payment</p>
223
- <Button onClick={() => mutate()}>Try Again</Button>
265
+ <p className="text-sm text-muted-foreground mb-4">{labels.failedToLoadPayment}</p>
266
+ <Button onClick={() => mutate()}>{labels.tryAgain}</Button>
224
267
  </div>
225
268
  )}
226
269
 
@@ -240,7 +283,7 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
240
283
  {/* Amount */}
241
284
  <div className="bg-muted rounded-xl p-4 space-y-3">
242
285
  <div className="flex items-center justify-between">
243
- <span className="text-muted-foreground">Amount to send</span>
286
+ <span className="text-muted-foreground">{labels.amountToSend}</span>
244
287
  <div className="flex items-center gap-2">
245
288
  <TokenIcon symbol={payment.currency_code} size={24} />
246
289
  <span className="font-mono font-bold text-lg">
@@ -249,12 +292,12 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
249
292
  </div>
250
293
  </div>
251
294
  <div className="flex items-center justify-between text-sm">
252
- <span className="text-muted-foreground">Equivalent</span>
295
+ <span className="text-muted-foreground">{labels.equivalent}</span>
253
296
  <span className="font-semibold">{amountUsd}</span>
254
297
  </div>
255
298
  {payment.currency_network && (
256
299
  <div className="flex items-center justify-between text-sm pt-2 border-t">
257
- <span className="text-muted-foreground">Network</span>
300
+ <span className="text-muted-foreground">{labels.network}</span>
258
301
  <span className="font-medium">{payment.currency_network}</span>
259
302
  </div>
260
303
  )}
@@ -270,7 +313,7 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
270
313
  {/* Payment Address */}
271
314
  {payment.pay_address && canPay && (
272
315
  <div className="space-y-2">
273
- <label className="text-sm font-medium">Payment Address</label>
316
+ <label className="text-sm font-medium">{labels.paymentAddress}</label>
274
317
  <div className="flex items-center gap-2">
275
318
  <div className="flex-1 p-3 bg-muted rounded-xl font-mono text-sm break-all">
276
319
  {payment.pay_address}
@@ -290,14 +333,14 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
290
333
  onCreateNew();
291
334
  }}
292
335
  >
293
- Create New Payment
336
+ {labels.createNewPayment}
294
337
  </Button>
295
338
  )}
296
339
 
297
340
  {/* Transaction Hash (if completed) */}
298
341
  {payment.transaction_hash && (
299
342
  <div className="space-y-2">
300
- <label className="text-sm font-medium">Transaction Hash</label>
343
+ <label className="text-sm font-medium">{labels.transactionHash}</label>
301
344
  <div className="p-3 bg-muted rounded-xl font-mono text-sm break-all">
302
345
  {payment.transaction_hash}
303
346
  </div>
@@ -312,24 +355,24 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
312
355
  onClick={() => window.open(payment.payment_url!, '_blank')}
313
356
  >
314
357
  <ExternalLink className="h-4 w-4 mr-2" />
315
- Open in Payment Provider
358
+ {labels.openInPaymentProvider}
316
359
  </Button>
317
360
  )}
318
361
 
319
362
  {/* Metadata */}
320
363
  <div className="space-y-2 text-xs text-muted-foreground pt-4 border-t">
321
364
  <div className="flex justify-between">
322
- <span>Payment ID</span>
365
+ <span>{labels.paymentId}</span>
323
366
  <span className="font-mono">{payment.id}</span>
324
367
  </div>
325
368
  {payment.internal_payment_id && (
326
369
  <div className="flex justify-between">
327
- <span>Order #</span>
370
+ <span>{labels.orderId}</span>
328
371
  <span className="font-mono">{payment.internal_payment_id}</span>
329
372
  </div>
330
373
  )}
331
374
  <div className="flex justify-between">
332
- <span>Created</span>
375
+ <span>{labels.created}</span>
333
376
  <span>{createdAt}</span>
334
377
  </div>
335
378
  </div>
@@ -341,7 +384,7 @@ export function PaymentSheet({ paymentId, open, onOpenChange, onCreateNew }: Pay
341
384
  onClick={() => mutate()}
342
385
  >
343
386
  <RefreshCw className="h-4 w-4 mr-2" />
344
- Refresh Status
387
+ {labels.refreshStatus}
345
388
  </Button>
346
389
  </div>
347
390
  )}