@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.
- package/CHANGELOG.md +48 -0
- package/README.md +156 -40
- package/package.json +1 -1
- package/src/blocks/application/login-forms/login-page.test.tsx +190 -0
- package/src/blocks/application/login-forms/login-page.tsx +240 -0
- package/src/blocks/application/password-recovery/forgot-password-page-1.tsx +6 -8
- package/src/blocks/application/signup-forms/signup-page-1.tsx +127 -129
- package/src/blocks/application/verification/verify-1.tsx +2 -2
- package/src/blocks/application/viewport-ownership.test.tsx +41 -0
- package/src/blocks/e-commerce/category-filters/category-listing-filter-fields.tsx +218 -0
- package/src/blocks/e-commerce/category-filters/{category-filter-2.test.tsx → category-listing.test.tsx} +287 -199
- package/src/blocks/e-commerce/category-filters/category-listing.tsx +934 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-contact-step.tsx +149 -0
- package/src/blocks/e-commerce/checkout-forms/{checkout-form-1-country-field.tsx → checkout-country-field.tsx} +10 -8
- package/src/blocks/e-commerce/checkout-forms/checkout-order-summary.tsx +230 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-payment-step.tsx +100 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-progress.tsx +62 -0
- package/src/blocks/e-commerce/checkout-forms/checkout-review-step.tsx +59 -0
- package/src/blocks/e-commerce/checkout-forms/{checkout-form-1-shipping-step.tsx → checkout-shipping-step.tsx} +65 -41
- package/src/blocks/e-commerce/checkout-forms/checkout.test.tsx +478 -0
- package/src/blocks/e-commerce/checkout-forms/checkout.tsx +550 -0
- package/src/blocks/{marketing/navbars/navbar-4-account-menu.test.tsx → e-commerce/navigation/storefront-navbar-account-menu.test.tsx} +8 -8
- package/src/blocks/{marketing/navbars/navbar-4-account-menu.tsx → e-commerce/navigation/storefront-navbar-account-menu.tsx} +3 -3
- package/src/blocks/{marketing/navbars/navbar-4-cart-sheet.test.tsx → e-commerce/navigation/storefront-navbar-cart-sheet.test.tsx} +22 -22
- package/src/blocks/{marketing/navbars/navbar-4-cart-sheet.tsx → e-commerce/navigation/storefront-navbar-cart-sheet.tsx} +3 -3
- package/src/blocks/{marketing/navbars/navbar-4-mobile-nav.tsx → e-commerce/navigation/storefront-navbar-mobile-nav.tsx} +4 -12
- package/src/blocks/{marketing/navbars/navbar-4-search-bar.tsx → e-commerce/navigation/storefront-navbar-search-bar.tsx} +3 -3
- package/src/blocks/{marketing/navbars/navbar-4.test.tsx → e-commerce/navigation/storefront-navbar.test.tsx} +40 -40
- package/src/blocks/{marketing/navbars/navbar-4.tsx → e-commerce/navigation/storefront-navbar.tsx} +17 -17
- package/src/blocks/e-commerce/product-list/product-card.tsx +63 -40
- package/src/blocks/e-commerce/shopping-carts/shopping-cart.test.tsx +2 -2
- package/src/blocks/marketing/footers/footer-section-accessibility.test.tsx +118 -0
- package/src/blocks/marketing/footers/footer-section-actions.test.tsx +131 -0
- package/src/blocks/marketing/footers/footer-section-data.test.tsx +127 -0
- package/src/blocks/marketing/footers/footer-section-native-links.test.tsx +100 -0
- package/src/blocks/marketing/footers/footer-section-public-api.test.tsx +11 -0
- package/src/blocks/marketing/footers/footer-section.test.tsx +46 -0
- package/src/blocks/marketing/footers/footer-section.tsx +532 -0
- package/src/blocks/marketing/navbars/navbar-public-api.test.tsx +57 -0
- package/src/blocks/marketing/navbars/navbar.mega.test.tsx +149 -0
- package/src/blocks/marketing/navbars/navbar.test.tsx +155 -0
- package/src/blocks/marketing/navbars/navbar.tsx +735 -0
- package/src/blocks/marketing/testimonials/testimonial-section.test.tsx +171 -0
- package/src/blocks/marketing/testimonials/testimonial-section.tsx +237 -0
- package/src/index.ts +80 -81
- package/src/lib/contracts-curated.test.ts +5 -5
- package/src/lib/contracts.test.ts +55 -0
- package/src/lib/contracts.ts +64 -2
- package/tsup.config.ts +1 -1
- package/src/blocks/application/login-forms/login-page-1.tsx +0 -246
- package/src/blocks/application/login-forms/login-page-2.tsx +0 -207
- package/src/blocks/e-commerce/category-filters/category-filter-1.tsx +0 -260
- package/src/blocks/e-commerce/category-filters/category-filter-2-filter-panel.tsx +0 -222
- package/src/blocks/e-commerce/category-filters/category-filter-2.tsx +0 -921
- package/src/blocks/e-commerce/category-filters/category-filter-4-chip-rail.tsx +0 -57
- package/src/blocks/e-commerce/category-filters/category-filter-4-drawer.tsx +0 -124
- package/src/blocks/e-commerce/category-filters/category-filter-4-fields.tsx +0 -170
- package/src/blocks/e-commerce/category-filters/category-filter-4-sidebar.tsx +0 -52
- package/src/blocks/e-commerce/category-filters/category-filter-4-sort-sheet.tsx +0 -57
- package/src/blocks/e-commerce/category-filters/category-filter-4-state.ts +0 -68
- package/src/blocks/e-commerce/category-filters/category-filter-4.tsx +0 -298
- package/src/blocks/e-commerce/checkout-forms/card-formatting.test.ts +0 -47
- package/src/blocks/e-commerce/checkout-forms/card-formatting.ts +0 -9
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-contact-step.tsx +0 -96
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-order-summary.tsx +0 -252
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-payment-step.tsx +0 -188
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1-progress.tsx +0 -36
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1.test.tsx +0 -216
- package/src/blocks/e-commerce/checkout-forms/checkout-form-1.tsx +0 -338
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2-order-summary.tsx +0 -130
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2-payment-card.tsx +0 -175
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2-shipping-card.tsx +0 -190
- package/src/blocks/e-commerce/checkout-forms/checkout-form-2.tsx +0 -312
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-information-step.tsx +0 -91
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-order-summary.tsx +0 -110
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-payment-step.tsx +0 -137
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-progress.tsx +0 -68
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-review-step.tsx +0 -79
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3-shipping-step.tsx +0 -172
- package/src/blocks/e-commerce/checkout-forms/checkout-form-3.tsx +0 -392
- package/src/blocks/marketing/footers/footer-section-1.test.tsx +0 -117
- package/src/blocks/marketing/footers/footer-section-1.tsx +0 -178
- package/src/blocks/marketing/footers/footer-section-3.tsx +0 -192
- package/src/blocks/marketing/footers/footer-section-4.tsx +0 -355
- package/src/blocks/marketing/navbars/navbar-1.tsx +0 -267
- package/src/blocks/marketing/navbars/navbar-2.tsx +0 -351
- package/src/blocks/marketing/navbars/navbar-3.tsx +0 -303
- package/src/blocks/marketing/navbars/navbar-4-like-button.tsx +0 -177
- package/src/blocks/marketing/navbars/navbar-4-product-grid.tsx +0 -219
- package/src/blocks/marketing/testimonials/testimonial-1.tsx +0 -141
- 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
|
-
}
|