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