@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.
@@ -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={item.decrement.bind(item)}
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={item.increment.bind(item)}
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={item.increment.bind(item)}
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
- className=''
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
- const toks = skuPath.split('-')
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
- {facets && levelRef.current > 0 && (
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-auto !rounded-bl-none !rounded-br-none !rounded-tl-lg !rounded-tr-lg '
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
- <Popover>
28
- <PopoverTrigger asChild>
29
- {children}
30
- </PopoverTrigger>
31
- <PopoverContent className={cn('relative flex flex-col p-0 px-4 pb-4 pt-2', popupClx)}>
32
- <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>
33
- <BuyItemCard skuPath={skuPath} className={cn("w-full relative ", cardClx)}/>
34
- </PopoverContent>
35
- </Popover>
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 }> = ({ className = '' }) => {
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
- <p >{item.titleAsOption + ', ' + formatPrice(item.price) + (item.quantity > 0 ? `(${item.quantity})` : '')}</p>
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='mb-4'
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 size='default' item={selItemRef.item} className={cn('lg:min-w-[160px] lg:mx-auto', className)}/>
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 h-[calc(100svh-96px)] max-h-[700px] flex flex-col justify-between ' +
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='mb-[3vh]' />
98
- <AddToCartComp className='w-pr-70 mx-auto' />
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
- <Main className='flex flex-col gap-1 h-full'>
50
- <div className='flex justify-between items-center'>
51
- <Button
52
- variant='ghost'
53
- size='icon'
54
- onClick={() => {
55
- setCurrentStep(0)
56
- close()
57
- }}
58
- >
59
- <ChevronLeft/>
60
- </Button>
61
- <AuthWidget/>
62
- </div>
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
- <div className='grid grid-cols-5 justify-center gap-8 h-full'>
65
- <div className='col-span-2 hidden md:flex'>
66
- <CartPanel noCheckout className='fixed justify-center border-none mt-10 w-1/3 max-w-[40rem]'/>
67
- </div>
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
- <div className='flex flex-col gap-8 sm:gap-14 col-span-5 md:col-span-3 max-w-[30rem] w-full mx-auto overflow-y-auto h-[calc(100%-40px)] sm:h-[calc(100%-48px)] py-4 px-1'>
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
- </div>
85
+ </Main>
85
86
  </div>
86
- </Main>
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
- <div className='flex flex-col gap-2 mt-6'>
114
- {transactionStatus === 'confirmed' ? (
115
- <ApplyTypography className='flex flex-col gap-4'>
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
- </ApplyTypography>
120
- ) : transactionStatus === 'paid' ? (
121
- <ApplyTypography className='flex flex-col gap-4'>
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: '#2D2D2D',
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: '#000000',
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
- <ApplyTypography>
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
- </div>
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 mx-auto max-w-[20rem]'>
169
- <div>Cart value: {formatPrice(c.cartTotal)}</div>
170
- <Select onValueChange={(token) => {/*ONLY ETH setSelectedToken(token) */}} defaultValue='eth'>
171
- <SelectTrigger>
172
- <SelectValue defaultValue='eth' />
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>Available funds in your wallet: {availableAmount} ETH</div>
183
- <div>
184
- <Input value={amount ? amount/(10**18) : amount} contentEditable={false}/>
185
- <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'>
186
- <Eth height={10}/>
187
- ETH
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/commerce",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Library with shopping cart components.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -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 products in cart */
9
+ /** Total of all quantities of all items in cart */
10
10
  get cartQuantity(): number
11
- /** Total of all prices * quantities of products in cart */
11
+ /** Total of all prices * quantities of items in cart */
12
12
  get cartTotal(): number
13
13
 
14
14
  get cartEmpty(): boolean