@hanzo/commerce 3.0.0 → 4.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/components/add-to-cart-widget.tsx +22 -5
- package/components/buy-item/buy-item-button.tsx +8 -2
- package/components/buy-item/buy-item-card.tsx +23 -13
- package/components/buy-item/buy-item-mobile-drawer.tsx +48 -0
- package/components/buy-item/buy-item-popup.tsx +25 -12
- package/components/buy-item/select-category-item-card.tsx +20 -9
- package/components/cart-panel/index.tsx +0 -1
- package/components/category-item-ios-wheel-selector.tsx +4 -2
- package/components/checkout-panel/index.tsx +23 -22
- package/components/checkout-panel/payment/contact-info.tsx +2 -2
- package/components/checkout-panel/payment/pay-with-card.tsx +10 -14
- package/components/checkout-panel/payment/pay-with-crypto.tsx +27 -21
- package/components/checkout-panel/shipping-info.tsx +7 -7
- package/package.json +1 -1
- package/types/commerce-service.ts +2 -2
|
@@ -15,15 +15,16 @@ const AddToCartWidget: React.FC<{
|
|
|
15
15
|
className?: string
|
|
16
16
|
buttonClx?: string
|
|
17
17
|
isMobile?: boolean
|
|
18
|
-
wide?: boolean
|
|
19
18
|
size?: ButtonSizes
|
|
19
|
+
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
20
20
|
}> = observer(({
|
|
21
21
|
item,
|
|
22
22
|
ghost=false,
|
|
23
23
|
disabled=false,
|
|
24
24
|
className='',
|
|
25
25
|
buttonClx='',
|
|
26
|
-
size='xs'
|
|
26
|
+
size='xs',
|
|
27
|
+
onQuantityChanged
|
|
27
28
|
}) => {
|
|
28
29
|
|
|
29
30
|
const iconClx = ghost ? 'h-4 w-4 md:h-3 md:w-3 text-muted-3 hover:text-foreground' : 'h-5 w-7 px-1'
|
|
@@ -37,6 +38,22 @@ const AddToCartWidget: React.FC<{
|
|
|
37
38
|
)
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
const inc = () => {
|
|
42
|
+
const old = item.quantity
|
|
43
|
+
item.increment()
|
|
44
|
+
if (onQuantityChanged) {
|
|
45
|
+
onQuantityChanged(item.sku, old, old + 1)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const dec = () => {
|
|
50
|
+
const old = item.quantity
|
|
51
|
+
item.decrement()
|
|
52
|
+
if (onQuantityChanged) {
|
|
53
|
+
onQuantityChanged(item.sku, old, old - 1)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
40
57
|
return ( item.isInCart ? (
|
|
41
58
|
<div className={cn('flex flex-row items-stretch justify-center ' + (ghost ? 'bg-transparent rounded-xl' : 'bg-secondary rounded-xl'), className)}>
|
|
42
59
|
<Button
|
|
@@ -46,7 +63,7 @@ const AddToCartWidget: React.FC<{
|
|
|
46
63
|
rounded={ghost ? 'full' : 'xl'}
|
|
47
64
|
className={cn('px-1 lg:min-w-0 lg:px-2 xs:justify-end', buttonClx)}
|
|
48
65
|
key='left'
|
|
49
|
-
onClick={
|
|
66
|
+
onClick={dec}
|
|
50
67
|
>
|
|
51
68
|
{(item.quantity > 1) ? (
|
|
52
69
|
<Icons.minus className={iconClx} aria-hidden='true'/>
|
|
@@ -61,7 +78,7 @@ const AddToCartWidget: React.FC<{
|
|
|
61
78
|
variant={ghost ? 'ghost' : 'secondary'}
|
|
62
79
|
rounded={ghost ? 'full' : 'xl'}
|
|
63
80
|
className={cn('px-1 lg:min-w-0 lg:px-2 xs:justify-start', buttonClx)}
|
|
64
|
-
onClick={
|
|
81
|
+
onClick={inc}
|
|
65
82
|
key='right'
|
|
66
83
|
>
|
|
67
84
|
<Icons.plus className={iconClx} aria-hidden='true'/>
|
|
@@ -74,7 +91,7 @@ const AddToCartWidget: React.FC<{
|
|
|
74
91
|
variant='secondary'
|
|
75
92
|
rounded='xl'
|
|
76
93
|
className={cn(buttonClx, className)}
|
|
77
|
-
onClick={
|
|
94
|
+
onClick={inc}
|
|
78
95
|
>
|
|
79
96
|
<Icons.plus className='h-5 w-5 mr-1' aria-hidden='true'/>
|
|
80
97
|
<span className='mr-1'>Add</span>
|
|
@@ -4,6 +4,8 @@ import React, {type PropsWithChildren} from 'react'
|
|
|
4
4
|
import { type ButtonVariants, type ButtonSizes, Button } from '@hanzo/ui/primitives'
|
|
5
5
|
|
|
6
6
|
import BuyItemPopup from './buy-item-popup'
|
|
7
|
+
import BuyItemMobileDrawer from './buy-item-mobile-drawer'
|
|
8
|
+
import { cn } from '@hanzo/ui/util'
|
|
7
9
|
|
|
8
10
|
const BuyItemButton: React.FC<PropsWithChildren & {
|
|
9
11
|
skuPath: string
|
|
@@ -19,10 +21,14 @@ const BuyItemButton: React.FC<PropsWithChildren & {
|
|
|
19
21
|
children,
|
|
20
22
|
className='',
|
|
21
23
|
popupClx=''
|
|
22
|
-
}) => (
|
|
24
|
+
}) => (<>
|
|
23
25
|
<BuyItemPopup skuPath={skuPath} popupClx={popupClx}>
|
|
24
|
-
<Button size={size} variant={variant} className={className}>{children}</Button>
|
|
26
|
+
<Button size={size} variant={variant} className={cn(className, 'hidden md:flex')}>{children}</Button>
|
|
25
27
|
</BuyItemPopup>
|
|
28
|
+
<BuyItemMobileDrawer skuPath={skuPath} trigger={<Button size={size} variant={variant} className={cn(className, 'md:hidden')}>{children}</Button>} />
|
|
29
|
+
</>
|
|
26
30
|
)
|
|
27
31
|
|
|
32
|
+
|
|
33
|
+
|
|
28
34
|
export default BuyItemButton
|
|
@@ -14,10 +14,14 @@ import SelectCategoryItemCard from './select-category-item-card'
|
|
|
14
14
|
|
|
15
15
|
const BuyItemCard: React.FC<{
|
|
16
16
|
skuPath: string
|
|
17
|
+
mobile?: boolean
|
|
17
18
|
className?: string
|
|
19
|
+
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
18
20
|
}> = observer(({
|
|
19
21
|
skuPath,
|
|
20
|
-
|
|
22
|
+
mobile=false,
|
|
23
|
+
className='',
|
|
24
|
+
onQuantityChanged
|
|
21
25
|
}) => {
|
|
22
26
|
|
|
23
27
|
const cmmc = useCommerce()
|
|
@@ -28,17 +32,17 @@ const BuyItemCard: React.FC<{
|
|
|
28
32
|
|
|
29
33
|
useEffect(() => {
|
|
30
34
|
|
|
35
|
+
const toks = skuPath.split('-')
|
|
36
|
+
levelRef.current = toks.length - 1
|
|
37
|
+
const fsv: FacetsValue = {}
|
|
38
|
+
for (let level = 1; level <= levelRef.current; level++ ) {
|
|
39
|
+
fsv[level] = [toks[level]]
|
|
40
|
+
}
|
|
31
41
|
if (facets) {
|
|
32
|
-
|
|
33
|
-
const levelSpecified = toks.length - 1
|
|
34
|
-
const fsv: FacetsValue = {}
|
|
35
|
-
for (let level = 1; level <= levelSpecified; level++ ) {
|
|
36
|
-
fsv[level] = [toks[level]]
|
|
37
|
-
}
|
|
38
|
-
fsv[levelSpecified + 1] = [facets[0].value]
|
|
39
|
-
levelRef.current = levelSpecified
|
|
40
|
-
cmmc.setFacets(fsv)
|
|
42
|
+
fsv[levelRef.current + 1] = [facets[0].value]
|
|
41
43
|
}
|
|
44
|
+
cmmc.setFacets(fsv)
|
|
45
|
+
|
|
42
46
|
return autorun(() => {
|
|
43
47
|
const cats = cmmc.specifiedCategories
|
|
44
48
|
// Original cat was legit
|
|
@@ -55,24 +59,30 @@ const BuyItemCard: React.FC<{
|
|
|
55
59
|
})
|
|
56
60
|
}, [cat, facets])
|
|
57
61
|
|
|
62
|
+
const renderFacetTabs = facets && levelRef.current > 0
|
|
63
|
+
|
|
58
64
|
return (
|
|
59
65
|
<div className={className} >
|
|
60
|
-
{
|
|
66
|
+
{renderFacetTabs && (
|
|
61
67
|
<FacetValuesWidget
|
|
62
|
-
className={cn('grid gap-0 ' + `grid-cols-${facets.length}` + ' self-start ', 'border-b mb-2 -mr-2 -ml-2')}
|
|
68
|
+
className={cn('grid gap-0 ' + `grid-cols-${facets.length}` + ' self-start ', 'border-b-2 border-level-3 mb-2 -mr-2 -ml-2')}
|
|
63
69
|
isMobile={false}
|
|
64
70
|
mutator={getFacetValuesMutator(levelRef.current + 1, cmmc)}
|
|
65
71
|
itemClx='flex-col h-auto gap-0 pb-1 pt-3 px-3'
|
|
66
|
-
buttonClx='h-
|
|
72
|
+
buttonClx={'h-full !rounded-bl-none !rounded-br-none !rounded-tl-lg !rounded-tr-lg ' +
|
|
73
|
+
'!border-r !border-t !border-level-3'}
|
|
67
74
|
facetValues={facets}
|
|
68
75
|
/>
|
|
69
76
|
)}
|
|
70
77
|
{cmmc.specifiedCategories[0] && (
|
|
71
78
|
<SelectCategoryItemCard
|
|
72
79
|
noTitle
|
|
80
|
+
mobile={mobile}
|
|
73
81
|
category={cmmc.specifiedCategories[0]}
|
|
74
82
|
selectedItemRef={cmmc /* ...conveniently. :) */ }
|
|
75
83
|
selectSku={cmmc.setCurrentItem.bind(cmmc)}
|
|
84
|
+
className={!renderFacetTabs && mobile ? 'border-t-2 ' : ''}
|
|
85
|
+
onQuantityChanged={onQuantityChanged}
|
|
76
86
|
/>
|
|
77
87
|
)}
|
|
78
88
|
</div >
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useState, type ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import { X as LucideX} from 'lucide-react'
|
|
5
|
+
import { Sheet, SheetContent, SheetTrigger } from '@hanzo/ui/primitives'
|
|
6
|
+
|
|
7
|
+
import { cn } from '@hanzo/ui/util'
|
|
8
|
+
|
|
9
|
+
import BuyItemCard from './buy-item-card'
|
|
10
|
+
|
|
11
|
+
const BuyItemMobileDrawer: React.FC<{
|
|
12
|
+
skuPath: string
|
|
13
|
+
trigger: ReactNode
|
|
14
|
+
triggerClx?: string
|
|
15
|
+
drawerClx?: string
|
|
16
|
+
cardClx?: string
|
|
17
|
+
}> = ({
|
|
18
|
+
skuPath,
|
|
19
|
+
trigger,
|
|
20
|
+
triggerClx='',
|
|
21
|
+
drawerClx='',
|
|
22
|
+
cardClx=''
|
|
23
|
+
}) => {
|
|
24
|
+
|
|
25
|
+
const [open, setOpen] = useState<boolean>(false)
|
|
26
|
+
|
|
27
|
+
const onQuantityChanged = (sku: string, oldV: number, newV: number) => {
|
|
28
|
+
if (oldV === 0 && newV === 1) {
|
|
29
|
+
setTimeout(() => {setOpen(false)}, 150)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Sheet open={open} onOpenChange={setOpen} >
|
|
35
|
+
<SheetTrigger asChild className={triggerClx}>
|
|
36
|
+
{trigger}
|
|
37
|
+
</SheetTrigger>
|
|
38
|
+
<SheetContent
|
|
39
|
+
className={cn('rounded-tl-xl rounded-tr-xl p-0 overflow-hidden border-none', drawerClx)}
|
|
40
|
+
side="bottom"
|
|
41
|
+
>
|
|
42
|
+
<BuyItemCard skuPath={skuPath} mobile onQuantityChanged={onQuantityChanged} className={cn("w-full relative ", cardClx)}/>
|
|
43
|
+
</SheetContent>
|
|
44
|
+
</Sheet>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default BuyItemMobileDrawer
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import React, {type PropsWithChildren} from 'react'
|
|
2
|
+
import React, {useState, type PropsWithChildren} from 'react'
|
|
3
3
|
|
|
4
4
|
import { X } from 'lucide-react'
|
|
5
5
|
|
|
@@ -16,23 +16,36 @@ import BuyItemCard from './buy-item-card'
|
|
|
16
16
|
|
|
17
17
|
const BuyItemPopup: React.FC<PropsWithChildren & {
|
|
18
18
|
skuPath: string
|
|
19
|
+
triggerClx?: string
|
|
19
20
|
popupClx?: string
|
|
20
21
|
cardClx?: string
|
|
21
22
|
}> = ({
|
|
22
23
|
skuPath,
|
|
23
24
|
children,
|
|
25
|
+
triggerClx='',
|
|
24
26
|
popupClx='',
|
|
25
27
|
cardClx='',
|
|
26
|
-
}) =>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
}) => {
|
|
29
|
+
|
|
30
|
+
const [open, setOpen] = useState<boolean>(false)
|
|
31
|
+
|
|
32
|
+
const onQuantityChanged = (sku: string, oldV: number, newV: number) => {
|
|
33
|
+
if (oldV === 0 && newV === 1) {
|
|
34
|
+
setTimeout(() => {setOpen(false)}, 150)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
40
|
+
<PopoverTrigger asChild className={triggerClx}>
|
|
41
|
+
{children}
|
|
42
|
+
</PopoverTrigger>
|
|
43
|
+
<PopoverContent className={cn('relative flex flex-col p-0 px-4 pb-4 pt-2', popupClx)}>
|
|
44
|
+
<PopoverClose className='absolute z-20 right-2 top-2 self-end hover:bg-level-3 text-muted hover:text-accent p-1 rounded-full'><X className='w-5 h-5'/></PopoverClose>
|
|
45
|
+
<BuyItemCard skuPath={skuPath} onQuantityChanged={onQuantityChanged} className={cn("w-full relative ", cardClx)}/>
|
|
46
|
+
</PopoverContent>
|
|
47
|
+
</Popover>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
37
50
|
|
|
38
51
|
export default BuyItemPopup
|
|
@@ -16,6 +16,7 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
|
|
|
16
16
|
isLoading?: boolean
|
|
17
17
|
mobile?: boolean
|
|
18
18
|
noTitle?: boolean
|
|
19
|
+
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
19
20
|
}> = /* NOT observer */({
|
|
20
21
|
category,
|
|
21
22
|
selectedItemRef: selItemRef,
|
|
@@ -24,20 +25,25 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
|
|
|
24
25
|
isLoading = false,
|
|
25
26
|
mobile = false,
|
|
26
27
|
noTitle = false,
|
|
28
|
+
onQuantityChanged,
|
|
27
29
|
...props
|
|
28
30
|
}) => {
|
|
29
31
|
|
|
30
32
|
const soleOption = category.products.length === 1
|
|
31
33
|
|
|
32
|
-
const SelectProductComp: React.FC<{ className?: string }> = ({
|
|
34
|
+
const SelectProductComp: React.FC<{ className?: string }> = ({
|
|
35
|
+
className = ''
|
|
36
|
+
}) => {
|
|
33
37
|
|
|
38
|
+
const mobilePicker = (mobile || category.products.length > 6)
|
|
34
39
|
if (soleOption) {
|
|
35
40
|
const item = category.products[0] as LineItem
|
|
36
41
|
return (
|
|
37
|
-
<
|
|
42
|
+
<div className={cn('flex flex-col justify-center items-center ' + (mobilePicker ? 'h-[180px] ' : 'h-auto min-h-24'), className)}>
|
|
43
|
+
<p className='text-lg font-semibold'>{item.titleAsOption + ', ' + formatPrice(item.price)}</p>
|
|
44
|
+
</div>
|
|
38
45
|
)
|
|
39
46
|
}
|
|
40
|
-
const mobilePicker = (mobile && category.products.length > 6)
|
|
41
47
|
|
|
42
48
|
return (
|
|
43
49
|
<div /* id='CV_AVAIL_AMOUNTS' */ className={cn(
|
|
@@ -52,7 +58,7 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
|
|
|
52
58
|
selectSku={selectSku}
|
|
53
59
|
height={180}
|
|
54
60
|
itemHeight={30}
|
|
55
|
-
outerClx='
|
|
61
|
+
outerClx='w-full'
|
|
56
62
|
/>
|
|
57
63
|
) : (
|
|
58
64
|
<CategoryItemRadioSelector
|
|
@@ -60,6 +66,7 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
|
|
|
60
66
|
selectedItemRef={selItemRef}
|
|
61
67
|
selectSku={selectSku}
|
|
62
68
|
groupClx='mt-2'
|
|
69
|
+
showQuantity={false}
|
|
63
70
|
itemClx='flex flex-row gap-2.5 items-center'
|
|
64
71
|
/>
|
|
65
72
|
)}
|
|
@@ -70,7 +77,12 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
|
|
|
70
77
|
const AddToCartComp: React.FC<{ className?: string }> = observer(({ className = '' }) => (
|
|
71
78
|
// TODO disable if nothing selected
|
|
72
79
|
(selItemRef.item && !isLoading) && (
|
|
73
|
-
<AddToCartWidget
|
|
80
|
+
<AddToCartWidget
|
|
81
|
+
size='default'
|
|
82
|
+
item={selItemRef.item}
|
|
83
|
+
onQuantityChanged={onQuantityChanged}
|
|
84
|
+
className={cn('lg:min-w-[160px] lg:mx-auto', className)}
|
|
85
|
+
/>
|
|
74
86
|
)
|
|
75
87
|
))
|
|
76
88
|
|
|
@@ -87,15 +99,14 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
|
|
|
87
99
|
return mobile ? (
|
|
88
100
|
<div /* id='CV_OUTER' */
|
|
89
101
|
className={cn(
|
|
90
|
-
'w-full
|
|
91
|
-
'items-stretch gap-[4vh] mt-[2vh] pb-[6vh]',
|
|
102
|
+
'w-full flex flex-col justify-between items-center gap-5 py-pr-6',
|
|
92
103
|
className
|
|
93
104
|
)}
|
|
94
105
|
{...props}
|
|
95
106
|
>
|
|
96
107
|
{!noTitle && (<TitleArea className='grow pt-3 mb-0' />)}
|
|
97
|
-
<SelectProductComp className='
|
|
98
|
-
<AddToCartComp className='w-pr-
|
|
108
|
+
<SelectProductComp className='w-pr-65' />
|
|
109
|
+
<AddToCartComp className='w-pr-65' />
|
|
99
110
|
</div>
|
|
100
111
|
) : (
|
|
101
112
|
<div className={cn('', className)} {...props}>
|
|
@@ -30,7 +30,6 @@ const CartPanel: React.FC<PropsWithChildren & {
|
|
|
30
30
|
<div className={cn('border p-4 rounded-lg', className)}>
|
|
31
31
|
{children}
|
|
32
32
|
<div className='mt-2 w-full'>
|
|
33
|
-
{!!children && <div className='h-[1px] w-pr-80 mb-4 mx-auto bg-muted-3'/>}
|
|
34
33
|
{cmmc.cartEmpty ? (
|
|
35
34
|
<p className='text-center mt-4'>No items in cart</p>
|
|
36
35
|
) : (<>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import Picker from 'react-mobile-picker'
|
|
4
|
+
import { observer } from 'mobx-react-lite'
|
|
4
5
|
|
|
5
6
|
import type { ItemSelector } from '../types'
|
|
6
7
|
import { formatPrice } from '../util'
|
|
@@ -16,7 +17,7 @@ const CategoryItemIOSWheelSelector: React.FC<ItemSelector & {
|
|
|
16
17
|
itemHeight?: number
|
|
17
18
|
outerClx?: string
|
|
18
19
|
itemClx?: string
|
|
19
|
-
}> = ({
|
|
20
|
+
}> = observer(({
|
|
20
21
|
category,
|
|
21
22
|
selectedItemRef: iRef,
|
|
22
23
|
selectSku,
|
|
@@ -28,6 +29,7 @@ const CategoryItemIOSWheelSelector: React.FC<ItemSelector & {
|
|
|
28
29
|
|
|
29
30
|
// @ts-ignore
|
|
30
31
|
const onChange = (val, ignore) => {
|
|
32
|
+
console.log("WHEEL: ", val.item)
|
|
31
33
|
selectSku(val.item)
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -55,6 +57,6 @@ const CategoryItemIOSWheelSelector: React.FC<ItemSelector & {
|
|
|
55
57
|
</Picker>
|
|
56
58
|
</div>
|
|
57
59
|
)
|
|
58
|
-
}
|
|
60
|
+
})
|
|
59
61
|
|
|
60
62
|
export default CategoryItemIOSWheelSelector
|
|
@@ -32,7 +32,7 @@ const CheckoutPanel: React.FC<{
|
|
|
32
32
|
setCurrentStep={setCurrentStep}
|
|
33
33
|
/>
|
|
34
34
|
) : (
|
|
35
|
-
<LoginComponent hideHeader className='max-w-[20rem] mx-auto'/>
|
|
35
|
+
<LoginComponent hideHeader className='max-w-[20rem] mx-auto' inputClassName='border-muted-4'/>
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
const step2 = (
|
|
@@ -46,27 +46,28 @@ const CheckoutPanel: React.FC<{
|
|
|
46
46
|
return (
|
|
47
47
|
<div className="fixed top-0 left-0 !max-w-none w-full h-full min-h-screen bg-background z-50">
|
|
48
48
|
<Toaster/>
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
49
|
+
<div className='absolute flex w-full justify-between pt-2 md:pt-5 px-2 md:px-5'>
|
|
50
|
+
<Button
|
|
51
|
+
variant='ghost'
|
|
52
|
+
size='icon'
|
|
53
|
+
onClick={() => {
|
|
54
|
+
setCurrentStep(0)
|
|
55
|
+
close()
|
|
56
|
+
}}
|
|
57
|
+
className='w-auto h-auto rounded-full bg-level-1 p-2 md:p-4'
|
|
58
|
+
>
|
|
59
|
+
<ChevronLeft className='w-5 h-5'/>
|
|
60
|
+
</Button>
|
|
61
|
+
<AuthWidget/>
|
|
62
|
+
</div>
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
<div className='grid grid-cols-1 md:grid-cols-2 justify-center h-full overflow-y-auto md:overflow-y-hidden'>
|
|
65
|
+
<div className='flex items-center justify-center py-2'>
|
|
66
|
+
<CartPanel noCheckout className='border-none mt-10 w-full lg:w-1/2'/>
|
|
67
|
+
</div>
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
<div className='bg-level-1'>
|
|
70
|
+
<Main className='flex flex-col h-full md:h-[calc(100vh-48px)] gap-8 sm:gap-14 max-w-[30rem] w-full mx-auto md:overflow-y-auto px-2 pb-6 md:mt-12'>
|
|
70
71
|
<div className='flex gap-2 mx-auto items-center text-xxs sm:text-base'>
|
|
71
72
|
<div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 1 ? 'bg-foreground text-muted-4' : '')}>
|
|
72
73
|
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Payment</div>
|
|
@@ -81,9 +82,9 @@ const CheckoutPanel: React.FC<{
|
|
|
81
82
|
</div>
|
|
82
83
|
</div>
|
|
83
84
|
{steps[currentStep - 1]}
|
|
84
|
-
</
|
|
85
|
+
</Main>
|
|
85
86
|
</div>
|
|
86
|
-
</
|
|
87
|
+
</div>
|
|
87
88
|
</div>
|
|
88
89
|
)
|
|
89
90
|
})
|
|
@@ -31,7 +31,7 @@ const ContactInfo: React.FC<{
|
|
|
31
31
|
<FormItem className='space-y-1 w-full'>
|
|
32
32
|
<FormLabel>Full name</FormLabel>
|
|
33
33
|
<FormControl>
|
|
34
|
-
<Input {...field} />
|
|
34
|
+
<Input {...field} className='border-muted-4'/>
|
|
35
35
|
</FormControl>
|
|
36
36
|
<FormMessage />
|
|
37
37
|
</FormItem>
|
|
@@ -44,7 +44,7 @@ const ContactInfo: React.FC<{
|
|
|
44
44
|
<FormItem className='space-y-1 w-full'>
|
|
45
45
|
<FormLabel>Email</FormLabel>
|
|
46
46
|
<FormControl>
|
|
47
|
-
<Input {...field} />
|
|
47
|
+
<Input {...field} className='border-muted-4'/>
|
|
48
48
|
</FormControl>
|
|
49
49
|
<FormMessage />
|
|
50
50
|
</FormItem>
|
|
@@ -110,17 +110,15 @@ const PayWithCard: React.FC<{
|
|
|
110
110
|
*/
|
|
111
111
|
locationId={process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID}
|
|
112
112
|
>
|
|
113
|
-
<
|
|
114
|
-
{transactionStatus === '
|
|
115
|
-
<
|
|
113
|
+
<ApplyTypography className='flex flex-col gap-2 mt-6'>
|
|
114
|
+
{transactionStatus === 'paid' ? (
|
|
115
|
+
<div className='flex flex-col gap-4'>
|
|
116
116
|
<h5 className='mx-auto font-nav'>Payment confirmed!</h5>
|
|
117
117
|
<p className='mx-auto'>Thank you for your purchase.</p>
|
|
118
118
|
<Button onClick={() => setCurrentStep(2)}>Continue</Button>
|
|
119
|
-
</
|
|
120
|
-
) : transactionStatus === '
|
|
121
|
-
|
|
122
|
-
<h5 className='mx-auto font-nav'>Processing your payment...</h5>
|
|
123
|
-
</ApplyTypography>
|
|
119
|
+
</div>
|
|
120
|
+
) : transactionStatus === 'confirmed' ? (
|
|
121
|
+
<h6 className='mx-auto font-nav'>Processing your payment...</h6>
|
|
124
122
|
) : (
|
|
125
123
|
<>
|
|
126
124
|
<GooglePay/>
|
|
@@ -139,7 +137,7 @@ const PayWithCard: React.FC<{
|
|
|
139
137
|
<CreditCard
|
|
140
138
|
style={{
|
|
141
139
|
'.input-container': {
|
|
142
|
-
borderColor: '#
|
|
140
|
+
borderColor: '#404040',
|
|
143
141
|
borderRadius: '6px',
|
|
144
142
|
},
|
|
145
143
|
'.input-container.is-focus': {
|
|
@@ -161,7 +159,7 @@ const PayWithCard: React.FC<{
|
|
|
161
159
|
color: '#ff1600',
|
|
162
160
|
},
|
|
163
161
|
input: {
|
|
164
|
-
backgroundColor: '#
|
|
162
|
+
backgroundColor: '#1f1f1f',
|
|
165
163
|
color: '#FFFFFF',
|
|
166
164
|
},
|
|
167
165
|
'input::placeholder': {
|
|
@@ -184,13 +182,11 @@ const PayWithCard: React.FC<{
|
|
|
184
182
|
}}
|
|
185
183
|
/>
|
|
186
184
|
{transactionStatus === 'error' && (
|
|
187
|
-
<
|
|
188
|
-
<p className='mx-auto text-destructive'>There was an error processing your payment.</p>
|
|
189
|
-
</ApplyTypography>
|
|
185
|
+
<p className='mx-auto text-destructive'>There was an error processing your payment.</p>
|
|
190
186
|
)}
|
|
191
187
|
</>
|
|
192
188
|
)}
|
|
193
|
-
</
|
|
189
|
+
</ApplyTypography>
|
|
194
190
|
</PaymentForm>
|
|
195
191
|
)
|
|
196
192
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use client'
|
|
2
|
+
|
|
2
3
|
import React, { useEffect, useState } from 'react'
|
|
3
4
|
import { autorun } from 'mobx'
|
|
4
5
|
import { observer } from 'mobx-react-lite'
|
|
@@ -23,7 +24,6 @@ import { Ethereum as EthIconFromAuth } from '@hanzo/auth/icons'
|
|
|
23
24
|
import Eth from '../icons/eth'
|
|
24
25
|
import { useCommerce } from '../../../service/context'
|
|
25
26
|
import type { TransactionStatus } from '../../../types'
|
|
26
|
-
import { formatPrice } from '../../../util'
|
|
27
27
|
|
|
28
28
|
declare global {
|
|
29
29
|
interface Window{
|
|
@@ -165,28 +165,34 @@ const PayWithCrypto: React.FC<{
|
|
|
165
165
|
</Button>
|
|
166
166
|
</div>
|
|
167
167
|
) : (
|
|
168
|
-
<div className='flex flex-col gap-2 w-full
|
|
169
|
-
<div
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
168
|
+
<div className='flex flex-col gap-2 w-full'>
|
|
169
|
+
<div className='flex gap-2 grid grid-cols-3'>
|
|
170
|
+
<Select onValueChange={(token) => {/*ONLY ETH setSelectedToken(token) */}} defaultValue='eth'>
|
|
171
|
+
<SelectTrigger>
|
|
172
|
+
<SelectValue defaultValue='eth' className='border-muted-4'/>
|
|
173
|
+
</SelectTrigger>
|
|
174
|
+
<SelectContent>
|
|
175
|
+
<SelectGroup>
|
|
176
|
+
<SelectItem value='eth'><div className='flex items-center gap-2'><Eth height={14}/>ETH</div></SelectItem>
|
|
177
|
+
{/* <SelectItem value='btc' ><div className='flex items-center gap-2'><Btc height={14}/>BTC</div></SelectItem>
|
|
178
|
+
<SelectItem value='usdt' ><div className='flex items-center gap-2'><Usdt height={14}/>USDT</div></SelectItem> */}
|
|
179
|
+
</SelectGroup>
|
|
180
|
+
</SelectContent>
|
|
181
|
+
</Select>
|
|
182
|
+
<div className='col-span-2'>
|
|
183
|
+
<Input
|
|
184
|
+
value={amount ? amount/(10**18) : amount}
|
|
185
|
+
contentEditable={false}
|
|
186
|
+
className='border-muted-4'
|
|
187
|
+
/>
|
|
188
|
+
<div className='relative flex items-center gap-2 -top-[32px] justify-end px-2 py-1 rounded-lg bg-muted-4 w-fit text-xs float-right mr-3'>
|
|
189
|
+
<Eth height={10}/>
|
|
190
|
+
ETH
|
|
191
|
+
</div>
|
|
188
192
|
</div>
|
|
189
193
|
</div>
|
|
194
|
+
<div>Available funds in your wallet: {availableAmount} ETH</div>
|
|
195
|
+
|
|
190
196
|
{transactionStatus === 'error' ? (
|
|
191
197
|
<h4 className='text-destructive'>There was an error while confirming the transaction.</h4>
|
|
192
198
|
) : transactionStatus === 'paid' ? (
|
|
@@ -81,7 +81,7 @@ const ShippingInfo: React.FC<{
|
|
|
81
81
|
<FormItem className='space-y-1 w-full'>
|
|
82
82
|
<FormLabel>First name</FormLabel>
|
|
83
83
|
<FormControl>
|
|
84
|
-
<Input {...field} />
|
|
84
|
+
<Input {...field} className='border-muted-4'/>
|
|
85
85
|
</FormControl>
|
|
86
86
|
<FormMessage />
|
|
87
87
|
</FormItem>
|
|
@@ -94,7 +94,7 @@ const ShippingInfo: React.FC<{
|
|
|
94
94
|
<FormItem className='space-y-1 w-full'>
|
|
95
95
|
<FormLabel>Last name</FormLabel>
|
|
96
96
|
<FormControl>
|
|
97
|
-
<Input {...field} />
|
|
97
|
+
<Input {...field} className='border-muted-4'/>
|
|
98
98
|
</FormControl>
|
|
99
99
|
<FormMessage />
|
|
100
100
|
</FormItem>
|
|
@@ -109,7 +109,7 @@ const ShippingInfo: React.FC<{
|
|
|
109
109
|
<FormItem className='space-y-1 w-full'>
|
|
110
110
|
<FormLabel>Address line 1</FormLabel>
|
|
111
111
|
<FormControl>
|
|
112
|
-
<Input {...field} />
|
|
112
|
+
<Input {...field} className='border-muted-4'/>
|
|
113
113
|
</FormControl>
|
|
114
114
|
<FormMessage />
|
|
115
115
|
</FormItem>
|
|
@@ -122,7 +122,7 @@ const ShippingInfo: React.FC<{
|
|
|
122
122
|
<FormItem className='space-y-1 w-full'>
|
|
123
123
|
<FormLabel>Address line 2 (optional)</FormLabel>
|
|
124
124
|
<FormControl>
|
|
125
|
-
<Input {...field} />
|
|
125
|
+
<Input {...field} className='border-muted-4'/>
|
|
126
126
|
</FormControl>
|
|
127
127
|
<FormMessage />
|
|
128
128
|
</FormItem>
|
|
@@ -137,7 +137,7 @@ const ShippingInfo: React.FC<{
|
|
|
137
137
|
<FormItem className='space-y-1 w-full'>
|
|
138
138
|
<FormLabel>Zip code</FormLabel>
|
|
139
139
|
<FormControl>
|
|
140
|
-
<Input {...field} />
|
|
140
|
+
<Input {...field} className='border-muted-4'/>
|
|
141
141
|
</FormControl>
|
|
142
142
|
<FormMessage />
|
|
143
143
|
</FormItem>
|
|
@@ -150,7 +150,7 @@ const ShippingInfo: React.FC<{
|
|
|
150
150
|
<FormItem className='space-y-1 w-full'>
|
|
151
151
|
<FormLabel>City</FormLabel>
|
|
152
152
|
<FormControl>
|
|
153
|
-
<Input {...field} />
|
|
153
|
+
<Input {...field} className='border-muted-4'/>
|
|
154
154
|
</FormControl>
|
|
155
155
|
<FormMessage />
|
|
156
156
|
</FormItem>
|
|
@@ -165,7 +165,7 @@ const ShippingInfo: React.FC<{
|
|
|
165
165
|
<FormItem className='space-y-1 w-full'>
|
|
166
166
|
<FormLabel>State (Optional)</FormLabel>
|
|
167
167
|
<FormControl>
|
|
168
|
-
<Input {...field} />
|
|
168
|
+
<Input {...field} className='border-muted-4'/>
|
|
169
169
|
</FormControl>
|
|
170
170
|
<FormMessage />
|
|
171
171
|
</FormItem>
|
package/package.json
CHANGED
|
@@ -6,9 +6,9 @@ interface CommerceService extends ObsLineItemRef {
|
|
|
6
6
|
|
|
7
7
|
/** Items in cart */
|
|
8
8
|
get cartItems(): LineItem[]
|
|
9
|
-
/** Total of all quantities of all
|
|
9
|
+
/** Total of all quantities of all items in cart */
|
|
10
10
|
get cartQuantity(): number
|
|
11
|
-
/** Total of all prices * quantities of
|
|
11
|
+
/** Total of all prices * quantities of items in cart */
|
|
12
12
|
get cartTotal(): number
|
|
13
13
|
|
|
14
14
|
get cartEmpty(): boolean
|