tf-checkout-react 2.0.0-beta.1 → 2.0.0-beta.2

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,5 +1,5 @@
1
1
  {
2
- "version": "2.0.0-beta.1",
2
+ "version": "2.0.0-beta.2",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -372,7 +372,12 @@ export const EmbeddedCheckout: FC<EmbeddedCheckoutProps> = ({
372
372
  messages,
373
373
  className,
374
374
  }) => (
375
- <CheckoutProvider theme={theme} locale={locale} messages={messages} className={className}>
375
+ <CheckoutProvider
376
+ theme={theme}
377
+ locale={locale}
378
+ messages={messages}
379
+ className={className}
380
+ >
376
381
  <Flow
377
382
  key={eventId}
378
383
  eventId={eventId}
@@ -122,7 +122,7 @@ export const AuthModal: FC<AuthModalProps> = ({
122
122
  if (event.target === event.currentTarget && !auth.isSubmitting) onClose()
123
123
  }
124
124
  const isPasswordReset = auth.view === 'forgot-password'
125
- const title = isPasswordReset ? 'Password reset' : 'Ticket Fairy account'
125
+ const title = isPasswordReset ? t('auth.passwordReset') : t('auth.title')
126
126
 
127
127
  return (
128
128
  <div className={cx('tf-checkout-auth', className)} onMouseDown={closeFromBackdrop}>
@@ -160,7 +160,7 @@ export const AuthModal: FC<AuthModalProps> = ({
160
160
  )}
161
161
  onClick={() => auth.showLogin(auth.suggestedEmail)}
162
162
  >
163
- Login
163
+ {t('auth.logIn')}
164
164
  </button>
165
165
  <button
166
166
  type="button"
@@ -172,7 +172,7 @@ export const AuthModal: FC<AuthModalProps> = ({
172
172
  )}
173
173
  onClick={() => auth.showRegister(auth.suggestedEmail)}
174
174
  >
175
- Sign Up
175
+ {t('auth.signUp')}
176
176
  </button>
177
177
  </div>
178
178
  )}
@@ -47,7 +47,7 @@ export const ForgotPasswordForm: FC<ForgotPasswordFormProps> = ({
47
47
  {successMessage}
48
48
  </div>
49
49
  <Button type="button" block onClick={() => onBackToLogin(email)}>
50
- Back to log in
50
+ {t('auth.backToLogin')}
51
51
  </Button>
52
52
  </div>
53
53
  )
@@ -55,9 +55,7 @@ export const ForgotPasswordForm: FC<ForgotPasswordFormProps> = ({
55
55
 
56
56
  return (
57
57
  <form className="tf-checkout-auth__form" onSubmit={submit} noValidate>
58
- <p className="tf-checkout-auth__intro">
59
- Enter your email and we’ll send you instructions to reset your password.
60
- </p>
58
+ <p className="tf-checkout-auth__intro">{t('auth.forgotIntro')}</p>
61
59
  {error && (
62
60
  <div className="tf-checkout-auth__alert" role="alert">
63
61
  {error}
@@ -79,14 +77,14 @@ export const ForgotPasswordForm: FC<ForgotPasswordFormProps> = ({
79
77
  />
80
78
  </Field>
81
79
  <Button type="submit" block loading={loading}>
82
- Send reset instructions
80
+ {t('auth.sendResetInstructions')}
83
81
  </Button>
84
82
  <button
85
83
  type="button"
86
84
  className="tf-checkout-auth__text-button tf-checkout-auth__back"
87
85
  onClick={() => onBackToLogin(email)}
88
86
  >
89
- Back to log in
87
+ {t('auth.backToLogin')}
90
88
  </button>
91
89
  </form>
92
90
  )
@@ -88,7 +88,7 @@ export const LoginForm: FC<LoginFormProps> = ({
88
88
  />
89
89
  </Field>
90
90
  <Button type="submit" block loading={loading}>
91
- Login
91
+ {t('auth.logIn')}
92
92
  </Button>
93
93
  {allowPasswordReset && onForgotPassword && (
94
94
  <button
@@ -96,7 +96,7 @@ export const LoginForm: FC<LoginFormProps> = ({
96
96
  className="tf-checkout-auth__text-button tf-checkout-auth__forgot"
97
97
  onClick={() => onForgotPassword(email)}
98
98
  >
99
- Forgot password?
99
+ {t('auth.forgotPassword')}
100
100
  </button>
101
101
  )}
102
102
  </form>
@@ -236,7 +236,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({
236
236
  onChange={(event) => setField('stateId', event.target.value)}
237
237
  >
238
238
  <option value="">
239
- {states.length > 0 ? 'Select state / region' : 'Not required'}
239
+ {states.length > 0 ? t('auth.selectState') : t('auth.notRequired')}
240
240
  </option>
241
241
  {states.map((state) => (
242
242
  <option key={state.id} value={state.id}>
@@ -326,7 +326,7 @@ export const RegisterForm: FC<RegisterFormProps> = ({
326
326
  </Field>
327
327
  </FieldRow>
328
328
  <Button type="submit" block loading={loading}>
329
- Create Account
329
+ {t('auth.createAccount')}
330
330
  </Button>
331
331
  </form>
332
332
  )
@@ -28,14 +28,14 @@ export const ResetPasswordForm: FC<ResetPasswordFormProps> = ({
28
28
  const validation = useMemo(() => {
29
29
  if (!submitted) return {}
30
30
  return {
31
- token: token ? '' : 'This password-reset link is missing its token.',
31
+ token: token ? '' : t('auth.missingToken'),
32
32
  password:
33
33
  password.length < 8
34
- ? 'Password must have at least 8 characters.'
34
+ ? t('auth.passwordTooShort')
35
35
  : !/[@$!%*#?&]/.test(password)
36
- ? 'Password must contain at least one special character.'
36
+ ? t('auth.passwordNeedsSpecial')
37
37
  : '',
38
- confirmation: password !== confirmation ? 'Passwords must match.' : '',
38
+ confirmation: password !== confirmation ? t('auth.passwordsMustMatch') : '',
39
39
  }
40
40
  }, [confirmation, password, submitted, token])
41
41
 
@@ -58,11 +58,11 @@ export const ResetPasswordForm: FC<ResetPasswordFormProps> = ({
58
58
  return (
59
59
  <div className="tf-checkout-auth__form">
60
60
  <div className="tf-checkout-auth__success" role="status">
61
- Your password has been changed. You can now log in.
61
+ {t('auth.passwordChanged')}
62
62
  </div>
63
63
  {onBackToLogin && (
64
64
  <Button type="button" block onClick={onBackToLogin}>
65
- Log in
65
+ {t('auth.logIn')}
66
66
  </Button>
67
67
  )}
68
68
  </div>
@@ -105,7 +105,7 @@ export const ResetPasswordForm: FC<ResetPasswordFormProps> = ({
105
105
  />
106
106
  </Field>
107
107
  <Button type="submit" block loading={reset.isPending} disabled={!token}>
108
- Submit
108
+ {t('common.submit')}
109
109
  </Button>
110
110
  </form>
111
111
  )
package/src/configure.ts CHANGED
@@ -5,11 +5,7 @@
5
5
  *
6
6
  * configureCheckout({ env: 'PROD', clientId, clientSecret })
7
7
  */
8
- import {
9
- configure,
10
- type StorageAdapter,
11
- type TtfConfig,
12
- } from 'tf-checkout-shared'
8
+ import { configure, type StorageAdapter, type TtfConfig } from 'tf-checkout-shared'
13
9
 
14
10
  const isBrowser =
15
11
  typeof window !== 'undefined' && typeof window.localStorage !== 'undefined'
package/src/confirmer.ts CHANGED
@@ -17,11 +17,7 @@ import {
17
17
  type StripeCardElement,
18
18
  type StripeElements,
19
19
  } from '@stripe/stripe-js'
20
- import type {
21
- CheckoutBilling,
22
- CheckoutPorts,
23
- PaymentConfirmer,
24
- } from 'tf-checkout-shared'
20
+ import type { CheckoutBilling, CheckoutPorts, PaymentConfirmer } from 'tf-checkout-shared'
25
21
 
26
22
  import type { WebXenditTokenizer } from './xendit'
27
23
 
@@ -21,6 +21,7 @@ import {
21
21
  import { Button } from '../ui/Button'
22
22
  import { Card } from '../ui/Card'
23
23
  import { useCheckoutContext } from '../ui/CheckoutProvider'
24
+ import { useT } from '../ui/I18nProvider'
24
25
  import { CustomFieldGroupFields } from '../ui/CustomFields'
25
26
  import { Loader } from '../ui/Loader'
26
27
  import { PriceLine } from '../ui/PriceLine'
@@ -45,12 +46,16 @@ export const AddOns: FC<AddOnsProps> = ({
45
46
  eventId,
46
47
  onContinue,
47
48
  onSkip,
48
- buttonLabel = 'CONFIRM SELECTION',
49
- eyebrow = 'Get Your Tickets',
50
- heading = 'UPGRADES & ADD-ONS',
49
+ buttonLabel,
50
+ eyebrow,
51
+ heading,
51
52
  className,
52
53
  }) => {
54
+ const t = useT()
53
55
  const checkout = useCheckoutContext()
56
+ const buttonText = buttonLabel ?? t('addons.confirmSelection')
57
+ const eyebrowText = eyebrow ?? t('addons.eyebrow')
58
+ const headingText = heading ?? t('addons.heading')
54
59
  // No onContinue ⇒ combined form (inside <EmbeddedCheckout/>): apply the selection live
55
60
  // (no "confirm" click) — the old checkout applied add-ons at checkout too.
56
61
  const combined = !onContinue
@@ -97,8 +102,8 @@ export const AddOns: FC<AddOnsProps> = ({
97
102
  const hasRequired = (checkout.requiredAddOns ?? []).some((a) => a.required)
98
103
  const head = (
99
104
  <div className="tf-checkout-addons__head">
100
- {eyebrow && <span className="tf-checkout-addons__eyebrow">{eyebrow}</span>}
101
- <span className="tf-checkout-addons__heading">{heading}</span>
105
+ {eyebrowText && <span className="tf-checkout-addons__eyebrow">{eyebrowText}</span>}
106
+ <span className="tf-checkout-addons__heading">{headingText}</span>
102
107
  {onSkip && !hasRequired && (
103
108
  <button type="button" className="tf-checkout-addons__skip" onClick={onSkip}>
104
109
  Skip
@@ -190,7 +195,9 @@ export const AddOns: FC<AddOnsProps> = ({
190
195
  )}
191
196
  </div>
192
197
  {soldOut(a) ? (
193
- <span className="tf-checkout-tickets__soldout">Sold out</span>
198
+ <span className="tf-checkout-tickets__soldout">
199
+ {t('tickets.soldOut')}
200
+ </span>
194
201
  ) : (
195
202
  <QuantitySelect
196
203
  value={n}
@@ -227,7 +234,7 @@ export const AddOns: FC<AddOnsProps> = ({
227
234
  loading={checkout.isBusy}
228
235
  disabled={checkout.isBusy || selectedCount === 0}
229
236
  >
230
- {checkout.isBusy ? 'Updating…' : buttonLabel}
237
+ {checkout.isBusy ? t('addons.updating') : buttonText}
231
238
  </Button>
232
239
  )}
233
240
  {missing.length > 0 && (
@@ -34,6 +34,7 @@ import { PhoneInput, type CountryIso2 } from 'react-international-phone'
34
34
  import { Button } from '../ui/Button'
35
35
  import { Card, SectionHeader } from '../ui/Card'
36
36
  import { useCheckoutContext, useCheckoutValidation } from '../ui/CheckoutProvider'
37
+ import { useT } from '../ui/I18nProvider'
37
38
  import { CustomFieldInput } from '../ui/CustomFields'
38
39
  import { Checkbox, Field, FieldRow, Input } from '../ui/Field'
39
40
  import { BillingAddressFields } from './billing/BillingAddressFields'
@@ -85,22 +86,26 @@ export interface CheckoutPrivacyPolicy {
85
86
  export const BillingInfo: FC<BillingInfoProps> = ({
86
87
  eventId,
87
88
  onContinue,
88
- heading = 'Billing Information',
89
- buttonLabel = 'Continue to payment',
89
+ heading,
90
+ buttonLabel,
90
91
  onLogin,
91
92
  skipWhenProfileComplete = false,
92
93
  requireEmailConfirmation = false,
93
94
  collectTicketHolders = false,
94
95
  collectHolderContact = false,
95
- ttfOptInLabel = 'Keep me updated about future events from The Ticket Fairy',
96
+ ttfOptInLabel,
96
97
  brandOptInLabel,
97
98
  privacyPolicy,
98
99
  showPrivacyError = false,
99
100
  step,
100
101
  className,
101
102
  }) => {
103
+ const t = useT()
102
104
  const checkout = useCheckoutContext()
103
105
  const validation = useCheckoutValidation()
106
+ const headingText = heading ?? t('billing.heading')
107
+ const buttonText = buttonLabel ?? t('billing.continueToPayment')
108
+ const ttfOptInText = ttfOptInLabel ?? t('billing.ttfOptIn')
104
109
  const profile = hooks.useProfile({ retry: false })
105
110
  const event = hooks.useEvent(eventId)
106
111
  // No onContinue ⇒ this is one combined form (inside <EmbeddedCheckout/>): mirror the
@@ -417,7 +422,7 @@ export const BillingInfo: FC<BillingInfoProps> = ({
417
422
  const complete = missingDetails.length === 0
418
423
  const showValidation = validation.attempt > 0
419
424
  const missingSet = new Set(missingDetails)
420
- const fieldError = (key: string, message = 'This field is required.') =>
425
+ const fieldError = (key: string, message = t('billing.fieldRequired')) =>
421
426
  showValidation && missingSet.has(key) ? message : undefined
422
427
  const dobError = showValidation
423
428
  ? missingDetails.find(
@@ -433,7 +438,7 @@ export const BillingInfo: FC<BillingInfoProps> = ({
433
438
  const commit = () => {
434
439
  checkout.setBilling(committedBilling)
435
440
  checkout.setHolders(committedHolders)
436
- if (ttfOptInLabel || brandOptInLabel) checkout.setConsent(consent)
441
+ if (ttfOptInText || brandOptInLabel) checkout.setConsent(consent)
437
442
  }
438
443
 
439
444
  // Combined mode: keep the engine in sync as the form is filled, so <Payment/>
@@ -485,7 +490,7 @@ export const BillingInfo: FC<BillingInfoProps> = ({
485
490
  return (
486
491
  <>
487
492
  <Card className={className}>
488
- <SectionHeader step={step} title={heading} />
493
+ <SectionHeader step={step} title={headingText} />
489
494
 
490
495
  {onLogin && !checkout.isLoggedIn && (
491
496
  <div className="tf-checkout-billing__login">
@@ -506,7 +511,7 @@ export const BillingInfo: FC<BillingInfoProps> = ({
506
511
  invalid={Boolean(fieldError('First name'))}
507
512
  >
508
513
  <Input
509
- placeholder="First name *"
514
+ placeholder={t('billing.firstNamePlaceholder')}
510
515
  value={billing.firstName}
511
516
  required
512
517
  invalid={Boolean(fieldError('First name'))}
@@ -519,7 +524,7 @@ export const BillingInfo: FC<BillingInfoProps> = ({
519
524
  invalid={Boolean(fieldError('Last name'))}
520
525
  >
521
526
  <Input
522
- placeholder="Last name *"
527
+ placeholder={t('billing.lastNamePlaceholder')}
523
528
  value={billing.lastName}
524
529
  required
525
530
  invalid={Boolean(fieldError('Last name'))}
@@ -536,7 +541,7 @@ export const BillingInfo: FC<BillingInfoProps> = ({
536
541
  <Input
537
542
  type="email"
538
543
  autoComplete="email"
539
- placeholder="Email *"
544
+ placeholder={t('billing.emailPlaceholder')}
540
545
  value={billing.email}
541
546
  required
542
547
  invalid={Boolean(fieldError('Valid email address'))}
@@ -548,14 +553,14 @@ export const BillingInfo: FC<BillingInfoProps> = ({
548
553
  <Field
549
554
  error={fieldError(
550
555
  'Matching email confirmation',
551
- 'Email addresses must match.'
556
+ t('billing.emailMustMatch')
552
557
  )}
553
558
  invalid={Boolean(fieldError('Matching email confirmation'))}
554
559
  >
555
560
  <Input
556
561
  type="email"
557
562
  autoComplete="email"
558
- placeholder="Confirm email *"
563
+ placeholder={t('billing.confirmEmailPlaceholder')}
559
564
  value={billing.confirmEmail ?? ''}
560
565
  required
561
566
  invalid={Boolean(fieldError('Matching email confirmation'))}
@@ -570,21 +575,21 @@ export const BillingInfo: FC<BillingInfoProps> = ({
570
575
  billing.confirmEmail &&
571
576
  billing.confirmEmail.trim().toLowerCase() !==
572
577
  billing.email.trim().toLowerCase() && (
573
- <p className="tf-checkout-error-text">The email addresses do not match.</p>
578
+ <p className="tf-checkout-error-text">{t('billing.emailMismatch')}</p>
574
579
  )}
575
580
  {emailTaken && (
576
581
  <p className="tf-checkout-note tf-checkout-billing__email-taken">
577
- This email is already registered.{' '}
582
+ {t('billing.emailRegistered')}{' '}
578
583
  {onLogin ? (
579
584
  <button
580
585
  type="button"
581
586
  className="tf-checkout-billing__login-link"
582
587
  onClick={() => onLogin(billing.email)}
583
588
  >
584
- Log in to autofill your details
589
+ {t('billing.loginAutofill')}
585
590
  </button>
586
591
  ) : (
587
- 'Log in to autofill your details.'
592
+ t('billing.loginAutofillPlain')
588
593
  )}
589
594
  </p>
590
595
  )}
@@ -598,18 +603,18 @@ export const BillingInfo: FC<BillingInfoProps> = ({
598
603
  countries={countries}
599
604
  error={fieldError(
600
605
  f.kind === 'nationality'
601
- ? 'Nationality'
606
+ ? t('billing.nationality')
602
607
  : f.kind === 'gender'
603
- ? 'Gender'
604
- : 'Instagram handle'
608
+ ? t('billing.gender')
609
+ : t('billing.instagramHandle')
605
610
  )}
606
611
  />
607
612
  ))}
608
613
 
609
614
  {gates.showPhone && (
610
615
  <Field
611
- label={`Phone${gates.phoneRequired ? ' *' : ''}`}
612
- error={fieldError('Valid phone number', 'Enter a valid phone number.')}
616
+ label={gates.phoneRequired ? t('billing.phone') : t('billing.phoneOptional')}
617
+ error={fieldError('Valid phone number', t('billing.phoneInvalid'))}
613
618
  invalid={Boolean(fieldError('Valid phone number'))}
614
619
  >
615
620
  <PhoneInput
@@ -628,10 +633,10 @@ export const BillingInfo: FC<BillingInfoProps> = ({
628
633
  disableDialCodePrefill
629
634
  value={billing.phone ?? ''}
630
635
  required={gates.phoneRequired}
631
- placeholder="Phone number"
636
+ placeholder={t('billing.phonePlaceholder')}
632
637
  inputProps={{
633
638
  autoComplete: 'tel',
634
- 'aria-label': 'Phone',
639
+ 'aria-label': t('billing.phoneAria'),
635
640
  'aria-invalid': Boolean(fieldError('Valid phone number')),
636
641
  }}
637
642
  onChange={(phone) => setBilling((current) => ({ ...current, phone }))}
@@ -641,14 +646,16 @@ export const BillingInfo: FC<BillingInfoProps> = ({
641
646
 
642
647
  {gates.showDob && (
643
648
  <Field
644
- label={`Date of birth *${
645
- gates.minimumAge ? ` (must be ${gates.minimumAge}+)` : ''
646
- }`}
649
+ label={
650
+ gates.minimumAge
651
+ ? t('billing.dobWithAge', { age: gates.minimumAge })
652
+ : t('billing.dob')
653
+ }
647
654
  error={
648
655
  dobError === 'Date of birth'
649
- ? 'Date of birth is required.'
656
+ ? t('billing.dobRequired')
650
657
  : dobError
651
- ? `You must be ${gates.minimumAge} or older.`
658
+ ? t('billing.ageMinimum', { age: gates.minimumAge ?? '' })
652
659
  : undefined
653
660
  }
654
661
  invalid={Boolean(dobError)}
@@ -720,24 +727,24 @@ export const BillingInfo: FC<BillingInfoProps> = ({
720
727
  className="tf-checkout-checkbox--consent"
721
728
  label={
722
729
  <>
723
- I have read and agree to {privacyPolicy.brandName}{' '}
730
+ {t('billing.privacyAgree', { brand: privacyPolicy.brandName })}{' '}
724
731
  <a href={privacyPolicy.url} target="_blank" rel="noreferrer">
725
- {privacyPolicy.linkLabel || 'Privacy Policy.'}
732
+ {privacyPolicy.linkLabel || t('billing.privacyLink')}
726
733
  </a>
727
734
  {' *'}
728
735
  </>
729
736
  }
730
737
  />
731
738
  {(showPrivacyError || showValidation) && !consent.ttfOptIn && (
732
- <div className="tf-checkout-consent__error">Required</div>
739
+ <div className="tf-checkout-consent__error">{t('common.required')}</div>
733
740
  )}
734
741
  </div>
735
- ) : ttfOptInLabel ? (
742
+ ) : ttfOptInText ? (
736
743
  <Checkbox
737
744
  checked={consent.ttfOptIn}
738
745
  onChange={(e) => setConsent((c) => ({ ...c, ttfOptIn: e.target.checked }))}
739
746
  className="tf-checkout-checkbox--consent"
740
- label={ttfOptInLabel}
747
+ label={ttfOptInText}
741
748
  />
742
749
  ) : null}
743
750
  {!combined && (
@@ -753,7 +760,7 @@ export const BillingInfo: FC<BillingInfoProps> = ({
753
760
  onContinue?.()
754
761
  }}
755
762
  >
756
- {buttonLabel}
763
+ {buttonText}
757
764
  </Button>
758
765
  </>
759
766
  )}
@@ -14,6 +14,7 @@ import React, { useState, type FC, type ReactNode } from 'react'
14
14
  import { Button } from '../ui/Button'
15
15
  import { Card } from '../ui/Card'
16
16
  import { useCheckoutContext } from '../ui/CheckoutProvider'
17
+ import { useT } from '../ui/I18nProvider'
17
18
  import { cx } from '../ui/cx'
18
19
 
19
20
  export interface ConfirmationProps {
@@ -35,6 +36,7 @@ export const Confirmation: FC<ConfirmationProps> = ({
35
36
  children,
36
37
  className,
37
38
  }) => {
39
+ const t = useT()
38
40
  const checkout = useCheckoutContext()
39
41
  const order = checkout.order
40
42
  const [copied, setCopied] = useState(false)
@@ -78,14 +80,14 @@ export const Confirmation: FC<ConfirmationProps> = ({
78
80
  <Card className={cx('tf-checkout-confirmation', className)}>
79
81
  <span className="tf-checkout-confirmation__check">{pending ? '⏳' : '✓'}</span>
80
82
  <strong className="tf-checkout-confirmation__title">
81
- {pending ? 'Payment pending' : 'Your Tickets are Confirmed!'}
83
+ {pending ? t('confirmation.pending') : t('confirmation.confirmed')}
82
84
  </strong>
83
85
  <p className="tf-checkout-note">
84
86
  {pending
85
- ? 'We’ll email your tickets as soon as the payment settles.'
87
+ ? t('confirmation.pendingBody')
86
88
  : order.alreadyPaid
87
- ? 'This order was already paid — nothing was charged again.'
88
- : 'Your tickets are available in the My Tickets section — please bring them with you to the event.'}
89
+ ? t('confirmation.alreadyPaid')
90
+ : t('confirmation.available')}
89
91
  </p>
90
92
  {order.orderHash && (
91
93
  <p className="tf-checkout-confirmation__ref">Reference: {order.orderHash}</p>
@@ -116,14 +118,14 @@ export const Confirmation: FC<ConfirmationProps> = ({
116
118
  {/* Referral / share — invite friends for cheaper (or free) tickets. */}
117
119
  {!pending && url && (
118
120
  <div className="tf-checkout-confirmation__share">
119
- <strong>Your tickets can be cheaper — or even FREE!</strong>
121
+ <strong>{t('referral.cheaperOrFree')}</strong>
120
122
  <p className="tf-checkout-note">
121
123
  Invite friends with your link. When they buy, you get rewarded.
122
124
  </p>
123
125
  <div className="tf-checkout-confirmation__share-link">
124
126
  <input className="tf-checkout-input" readOnly value={url} />
125
127
  <Button variant="secondary" onClick={copy}>
126
- {copied ? 'Copied!' : 'Copy link'}
128
+ {copied ? t('referral.copied') : t('referral.copyLink')}
127
129
  </Button>
128
130
  </div>
129
131
  <div className="tf-checkout-confirmation__socials">
@@ -4,6 +4,7 @@ import { hooks } from 'tf-checkout-shared'
4
4
 
5
5
  import { Card } from '../ui/Card'
6
6
  import { useCheckoutContext } from '../ui/CheckoutProvider'
7
+ import { useT } from '../ui/I18nProvider'
7
8
 
8
9
  export interface OrderSummaryProps {
9
10
  eventId?: string
@@ -12,6 +13,7 @@ export interface OrderSummaryProps {
12
13
 
13
14
  /** Storefront-compatible order summary shown after the checkout controls. */
14
15
  export const OrderSummary: FC<OrderSummaryProps> = ({ eventId, className }) => {
16
+ const t = useT()
15
17
  const checkout = useCheckoutContext()
16
18
  const event = hooks.useEvent(eventId)
17
19
  const breakdown = checkout.breakdown
@@ -23,13 +25,13 @@ export const OrderSummary: FC<OrderSummaryProps> = ({ eventId, className }) => {
23
25
  <Card className={['tf-checkout-order-summary', className].filter(Boolean).join(' ')}>
24
26
  {event.data?.name && (
25
27
  <div className="tf-checkout-order-summary__group">
26
- <div className="tf-checkout-order-summary__label">Event</div>
28
+ <div className="tf-checkout-order-summary__label">{t('common.event')}</div>
27
29
  <div className="tf-checkout-order-summary__event">{event.data.name}</div>
28
30
  </div>
29
31
  )}
30
32
 
31
33
  <div className="tf-checkout-order-summary__group">
32
- <div className="tf-checkout-order-summary__label">Your Tickets</div>
34
+ <div className="tf-checkout-order-summary__label">{t('tickets.yourTickets')}</div>
33
35
  {breakdown.tickets.map((ticket, index) => {
34
36
  const detail = detailed?.tickets[index]
35
37
  return (
@@ -39,7 +41,7 @@ export const OrderSummary: FC<OrderSummaryProps> = ({ eventId, className }) => {
39
41
  >
40
42
  <div className="tf-checkout-order-summary__ticket">
41
43
  {ticket.quantity} x {ticket.name} - {detail?.unit_price ?? ticket.unit}{' '}
42
- each
44
+ {t('common.each')}
43
45
  </div>
44
46
  {detail?.line_total && detail.line_total !== detail.unit_price && (
45
47
  <div className="tf-checkout-order-summary__fees">
@@ -63,7 +65,7 @@ export const OrderSummary: FC<OrderSummaryProps> = ({ eventId, className }) => {
63
65
  >
64
66
  <div className="tf-checkout-order-summary__ticket">
65
67
  {addOn.quantity} x {addOn.name}
66
- {addOn.show_price ? ` - ${addOn.unit_price} each` : ''}
68
+ {addOn.show_price ? ` - ${addOn.unit_price} ${t('common.each')}` : ''}
67
69
  </div>
68
70
  </div>
69
71
  ))}
@@ -71,7 +73,7 @@ export const OrderSummary: FC<OrderSummaryProps> = ({ eventId, className }) => {
71
73
  )}
72
74
 
73
75
  <div className="tf-checkout-order-summary__total">
74
- <div className="tf-checkout-order-summary__label">Total</div>
76
+ <div className="tf-checkout-order-summary__label">{t('common.total')}</div>
75
77
  <div className="tf-checkout-order-summary__amount">
76
78
  {detailed?.total ??
77
79
  `${breakdown.currencySymbol || `${breakdown.currencyCode} `}${Number(