@thorprovider/blocks 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +156 -40
  3. package/package.json +1 -1
  4. package/src/blocks/application/login-forms/login-page.test.tsx +190 -0
  5. package/src/blocks/application/login-forms/login-page.tsx +240 -0
  6. package/src/blocks/application/password-recovery/forgot-password-page-1.tsx +6 -8
  7. package/src/blocks/application/signup-forms/signup-page-1.tsx +127 -129
  8. package/src/blocks/application/verification/verify-1.tsx +2 -2
  9. package/src/blocks/application/viewport-ownership.test.tsx +41 -0
  10. package/src/blocks/e-commerce/category-filters/category-listing-filter-fields.tsx +218 -0
  11. package/src/blocks/e-commerce/category-filters/{category-filter-2.test.tsx → category-listing.test.tsx} +287 -199
  12. package/src/blocks/e-commerce/category-filters/category-listing.tsx +934 -0
  13. package/src/blocks/e-commerce/checkout-forms/checkout-contact-step.tsx +149 -0
  14. package/src/blocks/e-commerce/checkout-forms/{checkout-form-1-country-field.tsx → checkout-country-field.tsx} +10 -8
  15. package/src/blocks/e-commerce/checkout-forms/checkout-order-summary.tsx +230 -0
  16. package/src/blocks/e-commerce/checkout-forms/checkout-payment-step.tsx +100 -0
  17. package/src/blocks/e-commerce/checkout-forms/checkout-progress.tsx +62 -0
  18. package/src/blocks/e-commerce/checkout-forms/checkout-review-step.tsx +59 -0
  19. package/src/blocks/e-commerce/checkout-forms/{checkout-form-1-shipping-step.tsx → checkout-shipping-step.tsx} +65 -41
  20. package/src/blocks/e-commerce/checkout-forms/checkout.test.tsx +478 -0
  21. package/src/blocks/e-commerce/checkout-forms/checkout.tsx +550 -0
  22. package/src/blocks/{marketing/navbars/navbar-4-account-menu.test.tsx → e-commerce/navigation/storefront-navbar-account-menu.test.tsx} +8 -8
  23. package/src/blocks/{marketing/navbars/navbar-4-account-menu.tsx → e-commerce/navigation/storefront-navbar-account-menu.tsx} +3 -3
  24. package/src/blocks/{marketing/navbars/navbar-4-cart-sheet.test.tsx → e-commerce/navigation/storefront-navbar-cart-sheet.test.tsx} +22 -22
  25. package/src/blocks/{marketing/navbars/navbar-4-cart-sheet.tsx → e-commerce/navigation/storefront-navbar-cart-sheet.tsx} +3 -3
  26. package/src/blocks/{marketing/navbars/navbar-4-mobile-nav.tsx → e-commerce/navigation/storefront-navbar-mobile-nav.tsx} +4 -12
  27. package/src/blocks/{marketing/navbars/navbar-4-search-bar.tsx → e-commerce/navigation/storefront-navbar-search-bar.tsx} +3 -3
  28. package/src/blocks/{marketing/navbars/navbar-4.test.tsx → e-commerce/navigation/storefront-navbar.test.tsx} +40 -40
  29. package/src/blocks/{marketing/navbars/navbar-4.tsx → e-commerce/navigation/storefront-navbar.tsx} +17 -17
  30. package/src/blocks/e-commerce/product-list/product-card.tsx +63 -40
  31. package/src/blocks/e-commerce/shopping-carts/shopping-cart.test.tsx +2 -2
  32. package/src/blocks/marketing/footers/footer-section-accessibility.test.tsx +118 -0
  33. package/src/blocks/marketing/footers/footer-section-actions.test.tsx +131 -0
  34. package/src/blocks/marketing/footers/footer-section-data.test.tsx +127 -0
  35. package/src/blocks/marketing/footers/footer-section-native-links.test.tsx +100 -0
  36. package/src/blocks/marketing/footers/footer-section-public-api.test.tsx +11 -0
  37. package/src/blocks/marketing/footers/footer-section.test.tsx +46 -0
  38. package/src/blocks/marketing/footers/footer-section.tsx +532 -0
  39. package/src/blocks/marketing/navbars/navbar-public-api.test.tsx +57 -0
  40. package/src/blocks/marketing/navbars/navbar.mega.test.tsx +149 -0
  41. package/src/blocks/marketing/navbars/navbar.test.tsx +155 -0
  42. package/src/blocks/marketing/navbars/navbar.tsx +735 -0
  43. package/src/blocks/marketing/testimonials/testimonial-section.test.tsx +171 -0
  44. package/src/blocks/marketing/testimonials/testimonial-section.tsx +237 -0
  45. package/src/index.ts +80 -81
  46. package/src/lib/contracts-curated.test.ts +5 -5
  47. package/src/lib/contracts.test.ts +55 -0
  48. package/src/lib/contracts.ts +64 -2
  49. package/tsup.config.ts +1 -1
  50. package/src/blocks/application/login-forms/login-page-1.tsx +0 -246
  51. package/src/blocks/application/login-forms/login-page-2.tsx +0 -207
  52. package/src/blocks/e-commerce/category-filters/category-filter-1.tsx +0 -260
  53. package/src/blocks/e-commerce/category-filters/category-filter-2-filter-panel.tsx +0 -222
  54. package/src/blocks/e-commerce/category-filters/category-filter-2.tsx +0 -921
  55. package/src/blocks/e-commerce/category-filters/category-filter-4-chip-rail.tsx +0 -57
  56. package/src/blocks/e-commerce/category-filters/category-filter-4-drawer.tsx +0 -124
  57. package/src/blocks/e-commerce/category-filters/category-filter-4-fields.tsx +0 -170
  58. package/src/blocks/e-commerce/category-filters/category-filter-4-sidebar.tsx +0 -52
  59. package/src/blocks/e-commerce/category-filters/category-filter-4-sort-sheet.tsx +0 -57
  60. package/src/blocks/e-commerce/category-filters/category-filter-4-state.ts +0 -68
  61. package/src/blocks/e-commerce/category-filters/category-filter-4.tsx +0 -298
  62. package/src/blocks/e-commerce/checkout-forms/card-formatting.test.ts +0 -47
  63. package/src/blocks/e-commerce/checkout-forms/card-formatting.ts +0 -9
  64. package/src/blocks/e-commerce/checkout-forms/checkout-form-1-contact-step.tsx +0 -96
  65. package/src/blocks/e-commerce/checkout-forms/checkout-form-1-order-summary.tsx +0 -252
  66. package/src/blocks/e-commerce/checkout-forms/checkout-form-1-payment-step.tsx +0 -188
  67. package/src/blocks/e-commerce/checkout-forms/checkout-form-1-progress.tsx +0 -36
  68. package/src/blocks/e-commerce/checkout-forms/checkout-form-1.test.tsx +0 -216
  69. package/src/blocks/e-commerce/checkout-forms/checkout-form-1.tsx +0 -338
  70. package/src/blocks/e-commerce/checkout-forms/checkout-form-2-order-summary.tsx +0 -130
  71. package/src/blocks/e-commerce/checkout-forms/checkout-form-2-payment-card.tsx +0 -175
  72. package/src/blocks/e-commerce/checkout-forms/checkout-form-2-shipping-card.tsx +0 -190
  73. package/src/blocks/e-commerce/checkout-forms/checkout-form-2.tsx +0 -312
  74. package/src/blocks/e-commerce/checkout-forms/checkout-form-3-information-step.tsx +0 -91
  75. package/src/blocks/e-commerce/checkout-forms/checkout-form-3-order-summary.tsx +0 -110
  76. package/src/blocks/e-commerce/checkout-forms/checkout-form-3-payment-step.tsx +0 -137
  77. package/src/blocks/e-commerce/checkout-forms/checkout-form-3-progress.tsx +0 -68
  78. package/src/blocks/e-commerce/checkout-forms/checkout-form-3-review-step.tsx +0 -79
  79. package/src/blocks/e-commerce/checkout-forms/checkout-form-3-shipping-step.tsx +0 -172
  80. package/src/blocks/e-commerce/checkout-forms/checkout-form-3.tsx +0 -392
  81. package/src/blocks/marketing/footers/footer-section-1.test.tsx +0 -117
  82. package/src/blocks/marketing/footers/footer-section-1.tsx +0 -178
  83. package/src/blocks/marketing/footers/footer-section-3.tsx +0 -192
  84. package/src/blocks/marketing/footers/footer-section-4.tsx +0 -355
  85. package/src/blocks/marketing/navbars/navbar-1.tsx +0 -267
  86. package/src/blocks/marketing/navbars/navbar-2.tsx +0 -351
  87. package/src/blocks/marketing/navbars/navbar-3.tsx +0 -303
  88. package/src/blocks/marketing/navbars/navbar-4-like-button.tsx +0 -177
  89. package/src/blocks/marketing/navbars/navbar-4-product-grid.tsx +0 -219
  90. package/src/blocks/marketing/testimonials/testimonial-1.tsx +0 -141
  91. package/src/blocks/marketing/testimonials/testimonial-2.tsx +0 -108
@@ -1,137 +0,0 @@
1
- 'use client'
2
-
3
- import { CreditCard, Shield } from 'lucide-react'
4
-
5
- import { Field, FieldGroup, FieldLabel, FieldLegend, FieldSet } from '../../../ui/field'
6
- import { Input } from '../../../ui/input'
7
- import { RadioGroup, RadioGroupItem } from '../../../ui/radio-group'
8
-
9
- import type { PaymentMethod } from '@thorprovider/types'
10
- import type { CheckoutForm3ChangeHandler, CheckoutForm3FormData } from './checkout-form-3'
11
-
12
- interface CheckoutForm3PaymentStepProps {
13
- formData: CheckoutForm3FormData
14
- onChange: CheckoutForm3ChangeHandler
15
- paymentMethods: PaymentMethod[]
16
- paymentLabel: string
17
- noMethodsLabel: string
18
- cardNumberLabel: string
19
- expiryLabel: string
20
- cvvLabel: string
21
- cardNameLabel: string
22
- securePaymentLabel: string
23
- }
24
-
25
- export function CheckoutForm3PaymentStep({
26
- formData,
27
- onChange,
28
- paymentMethods,
29
- paymentLabel,
30
- noMethodsLabel,
31
- cardNumberLabel,
32
- expiryLabel,
33
- cvvLabel,
34
- cardNameLabel,
35
- securePaymentLabel,
36
- }: CheckoutForm3PaymentStepProps) {
37
- const selectedMethod = paymentMethods.find(method => method.id === formData.paymentMethod)
38
- const isCard = selectedMethod?.type === 'card'
39
-
40
- return (
41
- <div className='flex flex-col gap-6'>
42
- <FieldSet className='gap-4'>
43
- <FieldLegend className='flex items-center gap-2 text-base font-medium'>
44
- <CreditCard className='size-4' aria-hidden='true' />
45
- {paymentLabel}
46
- </FieldLegend>
47
-
48
- {paymentMethods.length === 0 ? (
49
- <p className='text-muted-foreground text-sm'>{noMethodsLabel}</p>
50
- ) : (
51
- <RadioGroup value={formData.paymentMethod} onValueChange={value => value && onChange('paymentMethod', value)}>
52
- {paymentMethods.map(method => (
53
- <Field key={method.id} orientation='horizontal' className='rounded-lg border p-4'>
54
- <RadioGroupItem value={method.id} id={method.id} />
55
- <FieldLabel htmlFor={method.id} className='flex flex-1 flex-col gap-0.5 font-normal'>
56
- <span className='flex items-center gap-2'>
57
- {method.logo ? (
58
- // eslint-disable-next-line @next/next/no-img-element
59
- <img src={method.logo} alt='' className='size-4 object-contain' />
60
- ) : (
61
- <CreditCard className='size-4' aria-hidden='true' />
62
- )}
63
- {method.name}
64
- </span>
65
- {method.description ? (
66
- <span className='text-muted-foreground text-xs'>{method.description}</span>
67
- ) : null}
68
- </FieldLabel>
69
- </Field>
70
- ))}
71
- </RadioGroup>
72
- )}
73
- </FieldSet>
74
-
75
- {selectedMethod && !isCard ? (
76
- <div className='bg-muted/50 rounded-lg p-4 text-sm'>
77
- <p className='font-medium'>{selectedMethod.name}</p>
78
- {selectedMethod.description ? <p className='text-muted-foreground mt-1'>{selectedMethod.description}</p> : null}
79
- </div>
80
- ) : null}
81
-
82
- {isCard ? (
83
- <div className='rounded-lg border p-4'>
84
- <FieldGroup>
85
- <Field>
86
- <FieldLabel htmlFor='cardNumber'>{cardNumberLabel} *</FieldLabel>
87
- <Input
88
- id='cardNumber'
89
- placeholder='1234 5678 9012 3456'
90
- className='h-9 font-mono'
91
- value={formData.cardNumber}
92
- onChange={event => onChange('cardNumber', event.target.value)}
93
- />
94
- </Field>
95
- <div className='grid gap-4 sm:grid-cols-3'>
96
- <Field className='sm:col-span-2'>
97
- <FieldLabel htmlFor='expiryDate'>{expiryLabel} *</FieldLabel>
98
- <Input
99
- id='expiryDate'
100
- placeholder='MM/AA'
101
- className='h-9 font-mono'
102
- value={formData.expiryDate}
103
- onChange={event => onChange('expiryDate', event.target.value)}
104
- />
105
- </Field>
106
- <Field>
107
- <FieldLabel htmlFor='cvv'>{cvvLabel} *</FieldLabel>
108
- <Input
109
- id='cvv'
110
- placeholder='123'
111
- className='h-9 font-mono'
112
- value={formData.cvv}
113
- onChange={event => onChange('cvv', event.target.value)}
114
- />
115
- </Field>
116
- </div>
117
- <Field>
118
- <FieldLabel htmlFor='cardName'>{cardNameLabel} *</FieldLabel>
119
- <Input
120
- id='cardName'
121
- placeholder='Juan Pérez'
122
- value={formData.cardName}
123
- onChange={event => onChange('cardName', event.target.value)}
124
- className='h-9'
125
- />
126
- </Field>
127
- </FieldGroup>
128
- </div>
129
- ) : null}
130
-
131
- <div className='bg-muted/50 flex items-center gap-2 rounded-lg p-3 text-sm'>
132
- <Shield className='size-4 text-success' aria-hidden='true' />
133
- <span>{securePaymentLabel}</span>
134
- </div>
135
- </div>
136
- )
137
- }
@@ -1,68 +0,0 @@
1
- 'use client'
2
-
3
- import { Check, CreditCard, Package, Truck, User } from 'lucide-react'
4
- import type { LucideIcon } from 'lucide-react'
5
-
6
- import { cn } from '../../../lib/utils'
7
-
8
- import type { CheckoutForm3Step } from './checkout-form-3'
9
-
10
- export const CHECKOUT_FORM_3_STEP_ICONS: LucideIcon[] = [User, Truck, CreditCard, Package]
11
-
12
- interface CheckoutForm3ProgressProps {
13
- steps: CheckoutForm3Step[]
14
- currentStep: number
15
- isStepComplete: (stepNumber: number) => boolean
16
- }
17
-
18
- export function CheckoutForm3Progress({ steps, currentStep, isStepComplete }: CheckoutForm3ProgressProps) {
19
- return (
20
- <div className='mb-8'>
21
- <div className='flex items-center justify-between'>
22
- {steps.map((step, index) => {
23
- const Icon = CHECKOUT_FORM_3_STEP_ICONS[index]
24
- const isActive = step.id === currentStep
25
- const isCompleted = isStepComplete(step.id)
26
-
27
- return (
28
- <div key={step.id} className='flex flex-1 items-center'>
29
- <div className='flex flex-col items-center'>
30
- <div
31
- aria-current={isActive ? 'step' : undefined}
32
- className={cn(
33
- 'flex size-10 items-center justify-center rounded-full border-2 transition-colors',
34
- isCompleted
35
- ? 'border-primary bg-primary text-primary-foreground'
36
- : isActive
37
- ? 'border-primary bg-background text-primary'
38
- : 'border-muted bg-background text-muted-foreground',
39
- )}
40
- >
41
- {isCompleted ? (
42
- <Check className='size-5' aria-hidden='true' />
43
- ) : Icon ? (
44
- <Icon className='size-5' aria-hidden='true' />
45
- ) : null}
46
- </div>
47
- <span
48
- className={cn(
49
- 'mt-2 text-center text-xs font-medium',
50
- isActive ? 'text-primary' : 'text-muted-foreground',
51
- )}
52
- >
53
- {step.name}
54
- </span>
55
- </div>
56
- {index < steps.length - 1 ? (
57
- <div
58
- aria-hidden='true'
59
- className={cn('mx-4 h-px flex-1 transition-colors', isCompleted ? 'bg-primary' : 'bg-muted')}
60
- />
61
- ) : null}
62
- </div>
63
- )
64
- })}
65
- </div>
66
- </div>
67
- )
68
- }
@@ -1,79 +0,0 @@
1
- 'use client'
2
-
3
- import { CheckboxCheck } from './checkbox-check'
4
- import { Field, FieldLabel } from '../../../ui/field'
5
-
6
- import type { PaymentMethod } from '@thorprovider/types'
7
- import type {
8
- CheckoutForm3ChangeHandler,
9
- CheckoutForm3FormData,
10
- CheckoutForm3ShippingMethod,
11
- } from './checkout-form-3'
12
-
13
- interface CheckoutForm3ReviewStepProps {
14
- formData: CheckoutForm3FormData
15
- onChange: CheckoutForm3ChangeHandler
16
- shippingMethods: CheckoutForm3ShippingMethod[]
17
- paymentMethods: PaymentMethod[]
18
- orderReviewLabel: string
19
- contactReviewLabel: string
20
- shipToReviewLabel: string
21
- paymentReviewLabel: string
22
- shippingReviewLabel: string
23
- saveInfoLabel: string
24
- }
25
-
26
- export function CheckoutForm3ReviewStep({
27
- formData,
28
- onChange,
29
- shippingMethods,
30
- paymentMethods,
31
- orderReviewLabel,
32
- contactReviewLabel,
33
- shipToReviewLabel,
34
- paymentReviewLabel,
35
- shippingReviewLabel,
36
- saveInfoLabel,
37
- }: CheckoutForm3ReviewStepProps) {
38
- const shippingName = shippingMethods.find(method => method.id === formData.shippingMethod)?.name
39
- const paymentMethodName = paymentMethods.find(method => method.id === formData.paymentMethod)?.name
40
-
41
- return (
42
- <div className='flex flex-col gap-6'>
43
- <div className='bg-muted/50 rounded-lg p-6'>
44
- <h3 className='mb-4 font-medium'>{orderReviewLabel}</h3>
45
- <dl className='flex flex-col gap-4 text-sm'>
46
- <div>
47
- <dt className='inline font-medium'>{contactReviewLabel}: </dt>
48
- <dd className='inline'>{formData.email}</dd>
49
- </div>
50
- <div>
51
- <dt className='inline font-medium'>{shipToReviewLabel}: </dt>
52
- <dd className='inline'>
53
- {formData.firstName} {formData.lastName}, {formData.address}, {formData.city} {formData.zipCode}
54
- </dd>
55
- </div>
56
- <div>
57
- <dt className='inline font-medium'>{paymentReviewLabel}: </dt>
58
- <dd className='inline'>{paymentMethodName}</dd>
59
- </div>
60
- <div>
61
- <dt className='inline font-medium'>{shippingReviewLabel}: </dt>
62
- <dd className='inline'>{shippingName}</dd>
63
- </div>
64
- </dl>
65
- </div>
66
-
67
- <Field orientation='horizontal'>
68
- <CheckboxCheck
69
- id='saveInfo'
70
- checked={formData.saveInfo}
71
- onCheckedChange={checked => onChange('saveInfo', checked as boolean)}
72
- />
73
- <FieldLabel htmlFor='saveInfo' className='text-sm font-normal'>
74
- {saveInfoLabel}
75
- </FieldLabel>
76
- </Field>
77
- </div>
78
- )
79
- }
@@ -1,172 +0,0 @@
1
- 'use client'
2
-
3
- import { MapPin, Truck } from 'lucide-react'
4
-
5
- import { Field, FieldGroup, FieldLabel, FieldLegend, FieldSet } from '../../../ui/field'
6
- import { Input } from '../../../ui/input'
7
- import { RadioGroup, RadioGroupItem } from '../../../ui/radio-group'
8
- import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '../../../ui/select'
9
- import { Separator } from '../../../ui/separator'
10
-
11
- import type {
12
- CheckoutForm3ChangeHandler,
13
- CheckoutForm3CountryOption,
14
- CheckoutForm3FormData,
15
- CheckoutForm3ShippingMethod,
16
- } from './checkout-form-3'
17
-
18
- interface CheckoutForm3ShippingStepProps {
19
- formData: CheckoutForm3FormData
20
- onChange: CheckoutForm3ChangeHandler
21
- countryOptions: CheckoutForm3CountryOption[]
22
- shippingMethods: CheckoutForm3ShippingMethod[]
23
- shippingAddressLabel: string
24
- addressLabel: string
25
- apartmentLabel: string
26
- cityLabel: string
27
- stateLabel: string
28
- zipCodeLabel: string
29
- countryLabel: string
30
- shippingMethodLabel: string
31
- freeLabel: string
32
- currencySymbol: string
33
- }
34
-
35
- const countryLabelFor = (value: string, options: CheckoutForm3CountryOption[]) =>
36
- options.find(option => option.value === value)?.label ?? value
37
-
38
- export function CheckoutForm3ShippingStep({
39
- formData,
40
- onChange,
41
- countryOptions,
42
- shippingMethods,
43
- shippingAddressLabel,
44
- addressLabel,
45
- apartmentLabel,
46
- cityLabel,
47
- stateLabel,
48
- zipCodeLabel,
49
- countryLabel,
50
- shippingMethodLabel,
51
- freeLabel,
52
- currencySymbol,
53
- }: CheckoutForm3ShippingStepProps) {
54
- return (
55
- <div className='flex flex-col gap-6'>
56
- <div className='flex flex-col gap-4'>
57
- <h3 className='flex items-center gap-2 font-medium'>
58
- <MapPin className='size-4' aria-hidden='true' />
59
- {shippingAddressLabel}
60
- </h3>
61
-
62
- <FieldGroup>
63
- <Field>
64
- <FieldLabel htmlFor='address'>{addressLabel} *</FieldLabel>
65
- <Input
66
- id='address'
67
- placeholder='Calle Principal 123'
68
- value={formData.address}
69
- onChange={event => onChange('address', event.target.value)}
70
- required
71
- className='h-9'
72
- />
73
- </Field>
74
-
75
- <Field>
76
- <FieldLabel htmlFor='apartment'>{apartmentLabel}</FieldLabel>
77
- <Input
78
- id='apartment'
79
- placeholder='Piso 4B'
80
- value={formData.apartment}
81
- onChange={event => onChange('apartment', event.target.value)}
82
- className='h-9'
83
- />
84
- </Field>
85
-
86
- <div className='grid gap-4 sm:grid-cols-3'>
87
- <Field>
88
- <FieldLabel htmlFor='city'>{cityLabel} *</FieldLabel>
89
- <Input
90
- id='city'
91
- placeholder='Madrid'
92
- value={formData.city}
93
- onChange={event => onChange('city', event.target.value)}
94
- required
95
- className='h-9'
96
- />
97
- </Field>
98
- <Field>
99
- <FieldLabel htmlFor='state'>{stateLabel}</FieldLabel>
100
- <Input
101
- id='state'
102
- placeholder='Madrid'
103
- value={formData.state}
104
- onChange={event => onChange('state', event.target.value)}
105
- className='h-9'
106
- />
107
- </Field>
108
- <Field>
109
- <FieldLabel htmlFor='zipCode'>{zipCodeLabel} *</FieldLabel>
110
- <Input
111
- id='zipCode'
112
- placeholder='28001'
113
- value={formData.zipCode}
114
- onChange={event => onChange('zipCode', event.target.value)}
115
- required
116
- className='h-9'
117
- />
118
- </Field>
119
- </div>
120
-
121
- <Field>
122
- <FieldLabel htmlFor='country'>{countryLabel}</FieldLabel>
123
- <Select value={formData.country} onValueChange={value => value && onChange('country', value)}>
124
- <SelectTrigger id='country' className='!h-9 w-full'>
125
- <SelectValue>{countryLabelFor(formData.country, countryOptions)}</SelectValue>
126
- </SelectTrigger>
127
- <SelectContent>
128
- <SelectGroup>
129
- {countryOptions.map(country => (
130
- <SelectItem key={country.value} value={country.value}>
131
- {country.label}
132
- </SelectItem>
133
- ))}
134
- </SelectGroup>
135
- </SelectContent>
136
- </Select>
137
- </Field>
138
- </FieldGroup>
139
- </div>
140
-
141
- <Separator />
142
-
143
- <FieldSet className='gap-4'>
144
- <FieldLegend className='flex items-center gap-2 text-base font-medium'>
145
- <Truck className='size-4' aria-hidden='true' />
146
- {shippingMethodLabel}
147
- </FieldLegend>
148
- <RadioGroup
149
- value={formData.shippingMethod}
150
- onValueChange={value => value && onChange('shippingMethod', value)}
151
- >
152
- {shippingMethods.map(method => (
153
- <div key={method.id} className='flex items-center justify-between rounded-lg border p-4'>
154
- <Field orientation='horizontal' className='w-auto'>
155
- <RadioGroupItem value={method.id} id={method.id} />
156
- <FieldLabel htmlFor={method.id} className='font-normal'>
157
- <div>
158
- <div className='font-medium'>{method.name}</div>
159
- <div className='text-muted-foreground text-sm'>{method.description}</div>
160
- </div>
161
- </FieldLabel>
162
- </Field>
163
- <div className='font-medium'>
164
- {method.cost === 0 ? freeLabel : `${currencySymbol}${method.cost.toFixed(2)}`}
165
- </div>
166
- </div>
167
- ))}
168
- </RadioGroup>
169
- </FieldSet>
170
- </div>
171
- )
172
- }