@hanzo/commerce 4.8.0 → 5.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 (37) hide show
  1. package/components/add-to-cart-widget.tsx +0 -1
  2. package/components/{checkout-panel/cart-accordian.tsx → cart-accordian.tsx} +18 -9
  3. package/components/cart-panel/cart-line-item.tsx +22 -28
  4. package/components/cart-panel/index.tsx +57 -33
  5. package/components/index.ts +4 -1
  6. package/components/item-carousel.tsx +80 -0
  7. package/components/{checkout-panel/steps/payment/cards/index.tsx → payment-step-form/card-icon-row.tsx} +6 -6
  8. package/components/{checkout-panel/steps/payment → payment-step-form}/cc-button.tsx +1 -1
  9. package/components/payment-step-form/contact-form.tsx +49 -0
  10. package/components/{checkout-panel/steps/payment → payment-step-form}/index.tsx +27 -54
  11. package/components/{checkout-panel/steps/payment/pay-with-bank-transfer.tsx → payment-step-form/methods/bank-transfer.tsx} +17 -17
  12. package/components/{checkout-panel/steps/payment/pay-with-card.tsx → payment-step-form/methods/card.tsx} +9 -21
  13. package/components/{checkout-panel/steps/payment/pay-with-crypto.tsx → payment-step-form/methods/crypto.tsx} +8 -22
  14. package/components/payment-step-form/methods/index.ts +23 -0
  15. package/components/{checkout-panel/steps/shipping-info.tsx → shipping-step-form.tsx} +6 -8
  16. package/package.json +2 -2
  17. package/types/checkout.ts +43 -1
  18. package/types/index.ts +1 -1
  19. package/components/cart-panel/products-carousel.tsx +0 -60
  20. package/components/checkout-panel/_confirm-order.tsx_unused +0 -143
  21. package/components/checkout-panel/close-button.tsx +0 -24
  22. package/components/checkout-panel/desktop.tsx +0 -46
  23. package/components/checkout-panel/icons/bag-icon.tsx +0 -10
  24. package/components/checkout-panel/index.tsx +0 -108
  25. package/components/checkout-panel/mobile.tsx +0 -41
  26. package/components/checkout-panel/steps/payment/contact-info.tsx +0 -56
  27. package/components/checkout-panel/steps/thank-you.tsx +0 -18
  28. package/components/checkout-panel/steps/types.ts +0 -14
  29. /package/components/{checkout-panel/steps/payment/cards → payment-step-form/card-icons}/amex.tsx +0 -0
  30. /package/components/{checkout-panel/steps/payment/cards → payment-step-form/card-icons}/diners-club.tsx +0 -0
  31. /package/components/{checkout-panel/steps/payment/cards → payment-step-form/card-icons}/discover.tsx +0 -0
  32. /package/components/{checkout-panel/steps/payment/cards → payment-step-form/card-icons}/jcb.tsx +0 -0
  33. /package/components/{checkout-panel/steps/payment/cards → payment-step-form/card-icons}/mastercard.tsx +0 -0
  34. /package/components/{checkout-panel/steps/payment/cards → payment-step-form/card-icons}/visa.tsx +0 -0
  35. /package/components/{checkout-panel/icons → payment-step-form/crypto-icons}/btc.tsx +0 -0
  36. /package/components/{checkout-panel/icons → payment-step-form/crypto-icons}/eth.tsx +0 -0
  37. /package/components/{checkout-panel/icons → payment-step-form/crypto-icons}/usdt.tsx +0 -0
@@ -15,7 +15,6 @@ const AddToCartWidget: React.FC<{
15
15
  disabled?: boolean
16
16
  className?: string
17
17
  buttonClx?: string
18
- isMobile?: boolean
19
18
  size?: ButtonSizes
20
19
  onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
21
20
  }> = observer(({
@@ -2,6 +2,8 @@
2
2
  import React from 'react'
3
3
  import { observer } from 'mobx-react-lite'
4
4
 
5
+ import { ChevronRight } from 'lucide-react'
6
+
5
7
  import {
6
8
  Accordion,
7
9
  AccordionContent,
@@ -9,23 +11,24 @@ import {
9
11
  AccordionTrigger,
10
12
  } from '@hanzo/ui/primitives'
11
13
 
12
- import { formatPrice, useCommerce } from '../..'
14
+ import { formatPrice, useCommerce } from '..'
13
15
 
14
- import CartPanel from '../cart-panel'
15
- import BagIcon from './icons/bag-icon'
16
- import { ChevronRight } from 'lucide-react'
16
+ import CartPanel from './cart-panel'
17
17
 
18
- const CartAccordian: React.FC<{className?: string}> = observer(({
18
+ const CartAccordian: React.FC<{
19
+ icon?: React.ReactNode
20
+ className?: string
21
+ }> = observer(({
22
+ icon,
19
23
  className=''
20
24
  }) => {
21
-
22
25
  const cmmc = useCommerce()
23
26
  return (
24
27
  <Accordion type="single" collapsible className={className}>
25
28
  <AccordionItem value="cart" className='w-full border-b-0'>
26
29
  <AccordionTrigger className='!no-underline group flex justify-between'>
27
- <div className='flex gap-1 items-center'>
28
- <BagIcon className='w-4 h-4 relative -top-0.5 sm:w-6 shrink-0 sm:h-6'/>
30
+ <div className='flex gap-0 items-center'>
31
+ {icon}
29
32
  <h5 className='text-sm sm:text-xl grow'>
30
33
  <span className='group-data-[state=open]:hidden' >Order Total:</span>
31
34
  <span className='group-data-[state=closed]:hidden' >Your Order</span>
@@ -37,7 +40,13 @@ const CartAccordian: React.FC<{className?: string}> = observer(({
37
40
  </div>
38
41
  </AccordionTrigger>
39
42
  <AccordionContent className='data-[state=open]:mb-4'>
40
- <CartPanel className='w-full'/>
43
+ <CartPanel
44
+ className='w-full '
45
+ scrollAfter={4}
46
+ scrollHeightClx='h-[350px]'
47
+ itemClx='mt-2'
48
+ totalClx='sticky px-1 pr-2 border-t -bottom-[1px] bg-background'
49
+ />
41
50
  </AccordionContent>
42
51
  </AccordionItem>
43
52
  </Accordion>
@@ -1,15 +1,14 @@
1
1
  import React from 'react'
2
- import { observer } from 'mobx-react-lite'
3
2
  import Image from 'next/image'
3
+ import { observer } from 'mobx-react-lite'
4
4
 
5
5
  import { cn } from '@hanzo/ui/util'
6
6
 
7
7
  import type { LineItem } from '../../types'
8
8
  import { formatPrice } from '../../util'
9
-
10
9
  import AddToCartWidget from '../add-to-cart-widget'
11
10
 
12
- const IMG_SIZE=40
11
+ const DEF_IMG_SIZE=40
13
12
 
14
13
  const renderTitle = (t: string): React.ReactNode => {
15
14
  const toks = t.split(', ')
@@ -22,36 +21,31 @@ const renderTitle = (t: string): React.ReactNode => {
22
21
  const CartLineItem: React.FC<{
23
22
  item: LineItem
24
23
  className?: string
25
- isMobile?: boolean
24
+ imgSizePx?: number
26
25
  }> = observer(({
27
26
  item,
28
27
  className='',
29
- isMobile=false
30
- }) => {
31
-
32
- const imgSize = (isMobile) ? IMG_SIZE * 0.65 : IMG_SIZE
33
-
34
- return (
35
- <div className={cn('flex flex-col justify-start items-start text-sm font-sans', className)}>
36
- <div className='flex flex-row justify-between items-center gap-2'>
37
- {!!item.img ? (
38
- <Image src={item.img} alt={item.title + ' image'} height={imgSize} width={imgSize} className=''/>
39
- ) : (
40
- <div style={{height: imgSize, width: imgSize}}/ >
41
- )}
42
- <div className='grow leading-tight'>{renderTitle(item.title)}</div>
28
+ imgSizePx=DEF_IMG_SIZE
29
+ }) => (
30
+ <div className={cn('flex flex-col justify-start items-start text-sm font-sans', className)}>
31
+ <div className='flex flex-row justify-between items-center gap-2'>
32
+ {!!item.img ? (
33
+ <Image src={item.img} alt={item.title + ' image'} height={imgSizePx} width={imgSizePx} className=''/>
34
+ ) : ( // placeholder so things align
35
+ <div style={{height: imgSizePx, width: imgSizePx}}/ >
36
+ )}
37
+ <div className='grow leading-tight'>{renderTitle(item.title)}</div>
38
+ </div>
39
+ <div className='flex flex-row items-center justify-between w-full'>
40
+ <div className='flex flex-row items-center'>
41
+ <AddToCartWidget className='' item={item} size='xs' buttonClx='h-8 md:h-6' ghost/>
42
+ {item.quantity > 1 && (<span className='pl-2.5'>{'@' + formatPrice(item.price)}</span>)}
43
43
  </div>
44
- <div className='flex flex-row items-center justify-between w-full'>
45
- <div className='flex flex-row items-center'>
46
- <AddToCartWidget isMobile={isMobile} className='' item={item} size='xs' buttonClx='h-8 md:h-6' ghost/>
47
- {item.quantity > 1 && (<span className='pl-2.5'>{'@' + formatPrice(item.price)}</span>)}
48
- </div>
49
- <div className='flex flex-row items-center justify-end'>
50
- {formatPrice(item.price * item.quantity)}
51
- </div>
44
+ <div className='flex flex-row items-center justify-end'>
45
+ {formatPrice(item.price * item.quantity)}
52
46
  </div>
53
47
  </div>
54
- )
55
- })
48
+ </div>
49
+ ))
56
50
 
57
51
  export default CartLineItem
@@ -2,28 +2,40 @@
2
2
  import React, { type PropsWithChildren } from 'react'
3
3
  import { observer } from 'mobx-react-lite'
4
4
 
5
- import { Button } from '@hanzo/ui/primitives'
5
+ import { Button, ScrollArea } from '@hanzo/ui/primitives'
6
6
  import { cn } from '@hanzo/ui/util'
7
7
 
8
8
  import { useCommerce } from '../../service/context'
9
9
  import { formatPrice } from '../../util'
10
+ import { sendFBEvent, sendGAEvent } from '../../util/analytics'
10
11
 
11
12
  import CartLineItem from './cart-line-item'
12
- import { sendFBEvent, sendGAEvent } from '../../util/analytics'
13
- import ProductsCarousel from './products-carousel'
14
13
 
15
14
  const CartPanel: React.FC<PropsWithChildren & {
15
+ /** fix size and scroll after 'scrollAfter items in teh cart. */
16
+ scrollAfter?: number
17
+ scrollHeightClx?: string
18
+ imgSizePx?: number
16
19
  className?: string
17
- isMobile?: boolean,
18
- showCarousel?: boolean
19
- /** if not provided, checkout button will not be shown */
20
+ itemClx?: string
21
+ listClx?: string
22
+ noItemsClx?: string
23
+ totalClx?: string
24
+ buttonClx?: string
25
+ /** if not provided, 'checkout' button will be rendered */
20
26
  handleCheckout?: () => void
21
27
  }> = observer(({
22
- /** Children is the heading area. */
28
+ /** If provided, 'children' is rendered above the items. eg, a heading. */
23
29
  children,
30
+ scrollAfter=5,
31
+ scrollHeightClx='h-[70vh]',
32
+ imgSizePx=40,
24
33
  className='',
25
- isMobile=false,
26
- showCarousel=false,
34
+ itemClx='',
35
+ listClx='',
36
+ noItemsClx='',
37
+ totalClx='',
38
+ buttonClx='',
27
39
  handleCheckout,
28
40
  }) => {
29
41
 
@@ -57,32 +69,44 @@ const CartPanel: React.FC<PropsWithChildren & {
57
69
  handleCheckout && handleCheckout()
58
70
  }
59
71
 
72
+ const Main: React.FC = () => (<>
73
+ {cmmc.cartEmpty ? (
74
+ <p className={cn('text-center mt-4', noItemsClx)}>No items in cart</p>
75
+ ) : (<>
76
+ {cmmc.cartItems.map((item) => (
77
+ <CartLineItem imgSizePx={imgSizePx} item={item} key={`mobile-${item.sku}`} className={cn('mb-2', itemClx)}/>
78
+ ))}
79
+ </>)}
80
+ <p className={cn('mt-6 text-right border-t pt-1', totalClx)}>
81
+ <span>TOTAL:&nbsp;</span>
82
+ <span className='font-semibold'>{cmmc.cartTotal === 0 ? '0' : formatPrice(cmmc.cartTotal)}</span>
83
+ </p>
84
+ </>)
85
+
86
+ const scrolling = (): boolean => (cmmc.cartItems.length > scrollAfter)
87
+
60
88
  return (
61
- <div className={cn('border p-4 rounded-lg', className)}>
89
+ <div className={cn('border p-4 rounded-lg flex flex-col', className, scrolling() ? scrollHeightClx : 'h-auto')}>
62
90
  {children}
63
- {showCarousel && <ProductsCarousel items={cmmc.cartItems}/>}
64
- <div className='mt-2 w-full'>
65
- {cmmc.cartEmpty ? (
66
- <p className='text-center mt-4'>No items in cart</p>
67
- ) : (<>
68
- {cmmc.cartItems.map((item, i) => (
69
- <CartLineItem isMobile={isMobile} item={item} key={item.sku} className='mb-2 md:mb-3'/>
70
- ))}
71
- <p className='mt-6 text-right border-t pt-1'>TOTAL: <span className='font-semibold'>{cmmc.cartTotal === 0 ? '0' : formatPrice(cmmc.cartTotal)}</span></p>
72
- </>)}
73
- </div>
74
- {handleCheckout && (<>
75
- {!cmmc.cartEmpty && (
76
- <Button
77
- variant='primary'
78
- rounded='lg'
79
- className='mt-12 mx-auto w-full sm:max-w-[220px]'
80
- onClick={_handleCheckout}
81
- >
82
- Checkout
83
- </Button>
84
- )}
85
- </>)}
91
+ {scrolling() ? (
92
+ <ScrollArea className={cn('mt-2 w-full shrink py-0', listClx)}>
93
+ <Main />
94
+ </ScrollArea>
95
+ ) : (
96
+ <div className={cn('mt-2 w-full', listClx)}>
97
+ <Main />
98
+ </div>
99
+ )}
100
+ {handleCheckout && !cmmc.cartEmpty && (
101
+ <Button
102
+ onClick={_handleCheckout}
103
+ variant='primary'
104
+ rounded='lg'
105
+ className={cn('mt-12 mx-auto w-full sm:max-w-[220px]', buttonClx)}
106
+ >
107
+ Checkout
108
+ </Button>
109
+ )}
86
110
  </div>
87
111
  )
88
112
  })
@@ -3,13 +3,16 @@ export { default as BuyItemButtonWrapper } from './buy-item/buy-item-button-wrap
3
3
  export { default as BuyItemButton } from './buy-item/buy-item-button'
4
4
  export { default as BuyItemCard } from './buy-item/buy-item-card'
5
5
  export { default as BuyItemPopup } from './buy-item/buy-item-popup'
6
+ export { default as CartAccordian } from './cart-accordian'
6
7
  export { default as CartPanel } from './cart-panel'
7
- export { default as CheckoutPanel } from './checkout-panel'
8
8
  export { default as CategoryItemIOSWheelSelector } from './category-item-scroll-selector'
9
9
  export { default as CategoryItemRadioSelector } from './category-item-radio-selector'
10
10
  export { default as FacetValuesWidget } from './facet-values-widget'
11
+ export { default as ItemCarousel } from './item-carousel'
12
+ export { default as PaymentStepForm } from './payment-step-form'
11
13
  export { default as ProductCard } from './product-card'
12
14
  export { default as SelectCategoryAndItemWidget } from './buy-item/select-category-and-item-widget'
13
15
  export { default as SelectCategoryItemCard } from './buy-item/select-category-item-card'
14
16
  export { default as SelectCategoryItemPanel } from './select-category-item-panel'
17
+ export { default as ShippingStepForm } from './shipping-step-form'
15
18
  export { Icons } from './Icons'
@@ -0,0 +1,80 @@
1
+ import React from 'react'
2
+
3
+ import Spline from '@splinetool/react-spline'
4
+
5
+ import { cn } from '@hanzo/ui/util'
6
+
7
+ import {
8
+ type CarouselOptionsType,
9
+ Carousel,
10
+ CarouselContent,
11
+ CarouselItem,
12
+ CarouselPrevious,
13
+ CarouselNext
14
+ } from '@hanzo/ui/primitives'
15
+
16
+ import {
17
+ VideoBlockComponent,
18
+ ImageBlockComponent,
19
+ type ImageBlock,
20
+ type Block,
21
+ type VideoBlock
22
+ } from '@hanzo/ui/blocks'
23
+
24
+ import type { LineItem } from '../types'
25
+ import type { Dimensions } from '@hanzo/ui/types'
26
+
27
+ // Order of precedence of visuals: 3D > MP4 > Image
28
+ const ItemCarousel: React.FC<{
29
+ items: LineItem[]
30
+ constrainTo: Dimensions
31
+ options?: CarouselOptionsType
32
+ className?: string
33
+ itemClx?: string
34
+ }> = ({
35
+ items,
36
+ options,
37
+ className='',
38
+ constrainTo,
39
+ itemClx=''
40
+ }) => (
41
+ // className: 'w-full max-w-sm mx-auto
42
+ // itemClx: 'flex aspect-square items-center justify-center '
43
+ <Carousel options={options} className={cn('px-2', className)} >
44
+ <CarouselContent>
45
+ {items.map(({title, img, video, animation}, index) => (
46
+ <CarouselItem key={index} className={cn('p-2 flex flex-row justify-center items-center', itemClx)}>
47
+ {animation ? (
48
+ <Spline
49
+ scene={animation}
50
+ className='pointer-events-none' // !aspect-[12/10]
51
+ style={{
52
+ width: (6/5 * (typeof constrainTo.h === 'number' ? constrainTo.h as number : parseInt(constrainTo.h as string)) ),
53
+ height: constrainTo.h
54
+ }}
55
+ />
56
+ ) : video ? (
57
+ <VideoBlockComponent
58
+ constraint={constrainTo}
59
+ block={{blockType: 'video', ...video} satisfies VideoBlock as Block}
60
+ />
61
+ ) : (
62
+ <ImageBlockComponent
63
+ block={{
64
+ blockType: 'image',
65
+ src: img ?? '',
66
+ alt: title + ' image',
67
+ dim: constrainTo
68
+ } satisfies ImageBlock as Block}
69
+ className='m-auto'
70
+ />
71
+ )}
72
+ </CarouselItem>
73
+ ))}
74
+ </CarouselContent>
75
+ <CarouselPrevious className='left-1'/>
76
+ <CarouselNext className='right-1'/>
77
+ </Carousel>
78
+ )
79
+
80
+ export default ItemCarousel
@@ -1,10 +1,10 @@
1
1
  import { LockKeyhole } from 'lucide-react'
2
- import Amex from './amex'
3
- import Discover from './discover'
4
- import Mastercard from './mastercard'
5
- import Visa from './visa'
6
- import DinersClub from './diners-club'
7
- import Jcb from './jcb'
2
+ import Amex from './card-icons/amex'
3
+ import Discover from './card-icons/discover'
4
+ import Mastercard from './card-icons/mastercard'
5
+ import Visa from './card-icons/visa'
6
+ import DinersClub from './card-icons/diners-club'
7
+ import Jcb from './card-icons/jcb'
8
8
 
9
9
  // <span className='hidden sm:flex text-sm'>Secure payments with</span>
10
10
 
@@ -1,6 +1,6 @@
1
1
  import { Button } from '@hanzo/ui/primitives'
2
2
 
3
- import PaymentMethods from './cards'
3
+ import PaymentMethods from './card-icon-row'
4
4
  import type { PropsWithChildren } from 'react'
5
5
 
6
6
  const CCButton: React.FC<PropsWithChildren> = ({
@@ -0,0 +1,49 @@
1
+ import {
2
+ Form,
3
+ FormControl,
4
+ FormField,
5
+ FormItem,
6
+ FormMessage,
7
+ } from '@hanzo/ui/primitives/form'
8
+ import { Input } from '@hanzo/ui/primitives'
9
+
10
+ import type { ContactFormType } from '../../types'
11
+
12
+ const ContactForm: React.FC<{
13
+ form: ContactFormType,
14
+ }> = ({
15
+ form,
16
+ }) => (
17
+ <Form {...form}>
18
+ <form className='text-left'>
19
+ <div className='flex flex-col gap-1 sm:gap-2'>
20
+ <FormField
21
+ control={form.control}
22
+ name='name'
23
+ render={({ field }) => (
24
+ <FormItem className='w-full'>
25
+ <FormControl>
26
+ <Input {...field} className='border-muted-4' placeholder='Full name'/>
27
+ </FormControl>
28
+ <FormMessage className='py-0 space-y-0 !m-0'/>
29
+ </FormItem>
30
+ )}
31
+ />
32
+ <FormField
33
+ control={form.control}
34
+ name='email'
35
+ render={({ field }) => (
36
+ <FormItem className='w-full'>
37
+ <FormControl>
38
+ <Input {...field} className='border-muted-4' placeholder='Email'/>
39
+ </FormControl>
40
+ <FormMessage className='py-0 space-y-0 !m-0'/>
41
+ </FormItem>
42
+ )}
43
+ />
44
+ </div>
45
+ </form>
46
+ </Form>
47
+ )
48
+
49
+ export default ContactForm
@@ -1,6 +1,5 @@
1
1
  'use client'
2
-
3
- import { useEffect, useState } from 'react'
2
+ import React, { useEffect, useState } from 'react'
4
3
  import { observer } from 'mobx-react-lite'
5
4
 
6
5
  import { zodResolver } from '@hookform/resolvers/zod'
@@ -9,22 +8,19 @@ import { useForm } from 'react-hook-form'
9
8
 
10
9
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@hanzo/ui/primitives'
11
10
  import { useAuth } from '@hanzo/auth/service'
12
- import PayWithCrypto from './pay-with-crypto'
13
- import PayWithBankTransfer from './pay-with-bank-transfer'
14
- import PayWithCard from './pay-with-card'
15
11
 
16
- import { useCommerce } from '../../../../service/context'
17
- import type { TransactionStatus } from '../../../../types'
18
- import { sendFBEvent, sendGAEvent } from '../../../../util/analytics'
12
+ import { useCommerce } from '../../service/context'
13
+ import { sendFBEvent, sendGAEvent } from '../../util/analytics'
14
+ import type { CheckoutStepComponentProps, TransactionStatus } from '../../types'
19
15
 
20
- import type { StepComponentProps } from '../types'
16
+ import METHODS from './methods'
21
17
 
22
18
  const contactFormSchema = z.object({
23
19
  name: z.string().min(1, 'Enter your full name.'),
24
20
  email: z.string().email(),
25
21
  })
26
22
 
27
- const Payment: React.FC<StepComponentProps> = observer(({
23
+ const PaymentStepForm: React.FC<CheckoutStepComponentProps> = observer(({
28
24
  onDone,
29
25
  orderId,
30
26
  setOrderId
@@ -87,58 +83,35 @@ const Payment: React.FC<StepComponentProps> = observer(({
87
83
  const tabClx = 'whitespace-normal h-full text-xs sm:text-base px-1 text-muted ' +
88
84
  'data-[state=active]:text-accent data-[state=active]:bg-level-2 md:data-[state=active]:bg-level-3'
89
85
 
86
+ const disabled = transactionStatus === 'paid' || transactionStatus === 'confirmed'
87
+
90
88
  return (
91
- <Tabs defaultValue='card' className='w-full sm:max-w-[500px] sm:mx-auto'>
89
+ <Tabs defaultValue='card' className='w-full'>
92
90
  <TabsList className={groupClx}>
91
+ {METHODS.map(({ label, value }) => (
93
92
  <TabsTrigger
94
- value='card'
95
- className={tabClx}
96
- disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
97
- >
98
- Card
99
- </TabsTrigger>
100
- <TabsTrigger
101
- value='crypto'
102
- className={tabClx}
103
- disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
104
- >
105
- Wallet
106
- </TabsTrigger>
107
- <TabsTrigger
108
- value='bank'
93
+ value={value}
109
94
  className={tabClx}
110
- disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
95
+ disabled={disabled}
96
+ key={`tabs-${value}`}
111
97
  >
112
- Bank Wire
98
+ {label}
113
99
  </TabsTrigger>
100
+ ))}
114
101
  </TabsList>
115
- <TabsContent value='card'>
116
- <PayWithCard
117
- onDone={onDone}
118
- transactionStatus={transactionStatus}
119
- setTransactionStatus={setTransactionStatus}
120
- storePaymentInfo={storePaymentInfo}
121
- contactForm={contactForm}
122
- />
123
- </TabsContent>
124
- <TabsContent value='crypto'>
125
- <PayWithCrypto
126
- onDone={onDone}
127
- transactionStatus={transactionStatus}
128
- setTransactionStatus={setTransactionStatus}
129
- storePaymentInfo={storePaymentInfo}
130
- contactForm={contactForm}
131
- />
132
- </TabsContent>
133
- <TabsContent value='bank'>
134
- <PayWithBankTransfer
135
- onDone={onDone}
136
- storePaymentInfo={storePaymentInfo}
137
- contactForm={contactForm}
138
- />
139
- </TabsContent>
102
+ {METHODS.map(({Comp: PaymentMethodComp, value}) => (
103
+ <TabsContent value={value} key={`content-${value}`}>
104
+ <PaymentMethodComp
105
+ onDone={onDone}
106
+ transactionStatus={transactionStatus}
107
+ setTransactionStatus={setTransactionStatus}
108
+ storePaymentInfo={storePaymentInfo}
109
+ contactForm={contactForm}
110
+ />
111
+ </TabsContent>
112
+ ))}
140
113
  </Tabs>
141
114
  )
142
115
  })
143
116
 
144
- export default Payment
117
+ export default PaymentStepForm
@@ -1,21 +1,31 @@
1
1
  'use client'
2
2
 
3
- import type { ReactNode } from 'react'
3
+ import React from 'react'
4
4
  import { Copy } from 'lucide-react'
5
- import type { UseFormReturn } from 'react-hook-form'
6
5
 
7
- import { Button, Tabs, TabsContent, TabsList, TabsTrigger, toast } from '@hanzo/ui/primitives'
8
- import ContactInfo from './contact-info'
6
+ import {
7
+ Button,
8
+ Tabs,
9
+ TabsContent,
10
+ TabsList,
11
+ TabsTrigger,
12
+ toast
13
+ } from '@hanzo/ui/primitives'
14
+
15
+ import type { PaymentMethodComponentProps } from '../../../types'
16
+
17
+ import ContactForm from '../contact-form'
9
18
 
10
19
  const InfoField: React.FC<{
11
20
  label: string,
12
- value: ReactNode,
21
+ value: React.ReactNode,
13
22
  copyValue: string
14
23
  }> = ({
15
24
  label,
16
25
  value,
17
26
  copyValue
18
27
  }) => {
28
+
19
29
  const copyToClipboard = (label: string, text: string) => {
20
30
  navigator.clipboard.writeText(text)
21
31
  toast(`${label} copied to clipboard.`)
@@ -34,17 +44,7 @@ const InfoField: React.FC<{
34
44
  )
35
45
  }
36
46
 
37
- const PayWithBankTransfer: React.FC<{
38
- onDone: () => void
39
- storePaymentInfo: (paymentInfo: any) => Promise<void>
40
- contactForm: UseFormReturn<{
41
- name: string
42
- email: string
43
- }, any, {
44
- name: string
45
- email: string
46
- }>
47
- }> = ({
47
+ const PayWithBankTransfer: React.FC<PaymentMethodComponentProps> = ({
48
48
  onDone,
49
49
  storePaymentInfo,
50
50
  contactForm
@@ -62,7 +62,7 @@ const PayWithBankTransfer: React.FC<{
62
62
 
63
63
  return (
64
64
  <div className='flex flex-col gap-2 mt-6'>
65
- <ContactInfo form={contactForm}/>
65
+ <ContactForm form={contactForm}/>
66
66
  <Tabs defaultValue="usd" className='w-full mx-auto max-w-[50rem]'>
67
67
  <TabsList className={"grid w-full grid-cols-2 max-w-[15rem] mx-auto " + groupClx}>
68
68
  <TabsTrigger className={tabClx} value="usd">USD</TabsTrigger>
@@ -19,27 +19,15 @@ import {
19
19
 
20
20
  import { cn } from '@hanzo/ui/util'
21
21
 
22
- import { processSquareCardPayment } from '../../../../util'
23
- import { useCommerce } from '../../../../service/context'
24
- import type { TransactionStatus } from '../../../../types'
22
+ import { useCommerce } from '../../../service/context'
23
+ import { processSquareCardPayment } from '../../../util'
24
+ import type { PaymentMethodComponentProps } from '../../../types'
25
+ import { sendFBEvent, sendGAEvent } from '../../../util/analytics'
25
26
 
26
- import ContactInfo from './contact-info'
27
- import { sendFBEvent, sendGAEvent } from '../../../../util/analytics'
28
- import PaymentMethods from './cards'
27
+ import ContactInfo from '../contact-form'
28
+ import PaymentMethods from '../card-icon-row'
29
29
 
30
- const PayWithCard: React.FC<{
31
- onDone: () => void
32
- transactionStatus: TransactionStatus
33
- setTransactionStatus: (status: TransactionStatus) => void
34
- storePaymentInfo: (paymentInfo: any) => Promise<void>
35
- contactForm: UseFormReturn<{
36
- name: string
37
- email: string
38
- }, any, {
39
- name: string
40
- email: string
41
- }>
42
- }> = ({
30
+ const PayWithCard: React.FC<PaymentMethodComponentProps> = ({
43
31
  onDone,
44
32
  transactionStatus,
45
33
  setTransactionStatus,
@@ -206,7 +194,7 @@ const PayWithCard: React.FC<{
206
194
  color: '#ff1600',
207
195
  },
208
196
  input: {
209
- backgroundColor: '#1f1f1f',
197
+ backgroundColor: 'transparent',
210
198
  color: '#FFFFFF',
211
199
  fontSize: '15px',
212
200
  fontFamily: 'helvetica neue, sans-serif',
@@ -243,4 +231,4 @@ const PayWithCard: React.FC<{
243
231
  )
244
232
  }
245
233
 
246
- export default PayWithCard
234
+ export default PayWithCard