@hanzo/commerce 4.7.0 → 4.8.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 (29) hide show
  1. package/components/add-to-cart-widget.tsx +2 -2
  2. package/components/cart-panel/index.tsx +12 -15
  3. package/components/checkout-panel/cart-accordian.tsx +14 -6
  4. package/components/checkout-panel/close-button.tsx +3 -3
  5. package/components/checkout-panel/desktop.tsx +46 -0
  6. package/components/checkout-panel/index.tsx +85 -85
  7. package/components/checkout-panel/mobile.tsx +41 -0
  8. package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/index.tsx +2 -1
  9. package/components/checkout-panel/steps/payment/cc-button.tsx +17 -0
  10. package/components/checkout-panel/{payment → steps/payment}/contact-info.tsx +5 -6
  11. package/components/checkout-panel/{payment → steps/payment}/index.tsx +27 -21
  12. package/components/checkout-panel/{payment → steps/payment}/pay-with-bank-transfer.tsx +11 -7
  13. package/components/checkout-panel/steps/payment/pay-with-card.tsx +246 -0
  14. package/components/checkout-panel/{payment → steps/payment}/pay-with-crypto.tsx +7 -7
  15. package/components/checkout-panel/{shipping-info.tsx → steps/shipping-info.tsx} +8 -9
  16. package/components/checkout-panel/{thank-you.tsx → steps/thank-you.tsx} +4 -1
  17. package/components/checkout-panel/steps/types.ts +14 -0
  18. package/package.json +3 -3
  19. package/{components/checkout-panel → util}/countries.ts +1 -1
  20. package/components/checkout-panel/payment/pay-with-card.tsx +0 -222
  21. package/components/checkout-panel/step-indicator.tsx +0 -30
  22. package/components/checkout-panel/step.ts +0 -11
  23. /package/components/checkout-panel/{confirm-order.tsx → _confirm-order.tsx_unused} +0 -0
  24. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/amex.tsx +0 -0
  25. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/diners-club.tsx +0 -0
  26. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/discover.tsx +0 -0
  27. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/jcb.tsx +0 -0
  28. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/mastercard.tsx +0 -0
  29. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/visa.tsx +0 -0
@@ -29,7 +29,7 @@ const AddToCartWidget: React.FC<{
29
29
  }) => {
30
30
 
31
31
  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'
32
- const digitClx = ghost ? 'px-2 md:px-0.5' : 'sm:px-2 font-bold '
32
+ const digitClx = ghost ? 'px-2 md:px-0.5 text-foreground ' : 'sm:px-2 font-bold text-primary-fg '
33
33
 
34
34
  if (disabled) {
35
35
  return (
@@ -116,7 +116,7 @@ const AddToCartWidget: React.FC<{
116
116
  <Icons.trash className={iconClx} aria-hidden='true'/>
117
117
  )}
118
118
  </Button>
119
- <div className={'text-sm flex items-center cursor-default xs:px-2 text-primary-fg ' + digitClx} >{item.quantity}</div>
119
+ <div className={'text-sm flex items-center cursor-default xs:px-2 ' + digitClx} >{item.quantity}</div>
120
120
  <Button
121
121
  aria-label={'Add another ' + item.title + ' to the cart'}
122
122
  size={size}
@@ -15,27 +15,24 @@ import ProductsCarousel from './products-carousel'
15
15
  const CartPanel: React.FC<PropsWithChildren & {
16
16
  className?: string
17
17
  isMobile?: boolean,
18
- noCheckout?: boolean
19
- showProductsCarousel?: boolean
20
- onCheckoutOpen?: () => void
18
+ showCarousel?: boolean
19
+ /** if not provided, checkout button will not be shown */
20
+ handleCheckout?: () => void
21
21
  }> = observer(({
22
22
  /** Children is the heading area. */
23
23
  children,
24
24
  className='',
25
25
  isMobile=false,
26
- noCheckout=false,
27
- showProductsCarousel=false,
28
- onCheckoutOpen,
26
+ showCarousel=false,
27
+ handleCheckout,
29
28
  }) => {
30
- /* TODO: onCheckoutOpen is a hackish way fix a bug with multiple dialog opened at the same time.
31
- * Needs refactor with context or so.
32
- **/
29
+
33
30
  const cmmc = useCommerce()
34
31
  if (!cmmc) {
35
32
  return <div />
36
33
  }
37
34
 
38
- const showCheckout = () => {
35
+ const _handleCheckout = () => {
39
36
  sendGAEvent('begin_checkout', {
40
37
  currency: 'USD',
41
38
  value: cmmc.cartTotal,
@@ -57,13 +54,13 @@ const CartPanel: React.FC<PropsWithChildren & {
57
54
  value: cmmc.cartTotal,
58
55
  currency: 'USD',
59
56
  })
60
- onCheckoutOpen && onCheckoutOpen()
57
+ handleCheckout && handleCheckout()
61
58
  }
62
59
 
63
60
  return (
64
61
  <div className={cn('border p-4 rounded-lg', className)}>
65
62
  {children}
66
- {showProductsCarousel && <ProductsCarousel items={cmmc.cartItems}/>}
63
+ {showCarousel && <ProductsCarousel items={cmmc.cartItems}/>}
67
64
  <div className='mt-2 w-full'>
68
65
  {cmmc.cartEmpty ? (
69
66
  <p className='text-center mt-4'>No items in cart</p>
@@ -74,13 +71,13 @@ const CartPanel: React.FC<PropsWithChildren & {
74
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>
75
72
  </>)}
76
73
  </div>
77
- {!noCheckout && (<>
74
+ {handleCheckout && (<>
78
75
  {!cmmc.cartEmpty && (
79
76
  <Button
80
77
  variant='primary'
81
78
  rounded='lg'
82
- className='mt-12 mx-auto w-full'
83
- onClick={showCheckout}
79
+ className='mt-12 mx-auto w-full sm:max-w-[220px]'
80
+ onClick={_handleCheckout}
84
81
  >
85
82
  Checkout
86
83
  </Button>
@@ -13,6 +13,7 @@ import { formatPrice, useCommerce } from '../..'
13
13
 
14
14
  import CartPanel from '../cart-panel'
15
15
  import BagIcon from './icons/bag-icon'
16
+ import { ChevronRight } from 'lucide-react'
16
17
 
17
18
  const CartAccordian: React.FC<{className?: string}> = observer(({
18
19
  className=''
@@ -22,14 +23,21 @@ const CartAccordian: React.FC<{className?: string}> = observer(({
22
23
  return (
23
24
  <Accordion type="single" collapsible className={className}>
24
25
  <AccordionItem value="cart" className='w-full border-b-0'>
25
- <AccordionTrigger className='!no-underline py-1'>
26
- <div className='flex gap-4 items-center'>
27
- <BagIcon className='w-4 h-4 sm:w-6 sm:h-6'/>
28
- <h5 className='text-sm sm:text-xl truncate'>Order Summary {formatPrice(cmmc.cartTotal)}</h5>
26
+ <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'/>
29
+ <h5 className='text-sm sm:text-xl grow'>
30
+ <span className='group-data-[state=open]:hidden' >Order Total:</span>
31
+ <span className='group-data-[state=closed]:hidden' >Your Order</span>
32
+ </h5>
33
+ </div>
34
+ <div className='flex gap-1 items-center'>
35
+ <h5 className='text-sm sm:text-xl grow truncate'>{formatPrice(cmmc.cartTotal)}</h5>
36
+ <ChevronRight className="h-5 w-5 -mr-2 shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-90" />
29
37
  </div>
30
38
  </AccordionTrigger>
31
- <AccordionContent>
32
- <CartPanel noCheckout className='border-none w-full'/>
39
+ <AccordionContent className='data-[state=open]:mb-4'>
40
+ <CartPanel className='w-full'/>
33
41
  </AccordionContent>
34
42
  </AccordionItem>
35
43
  </Accordion>
@@ -5,16 +5,16 @@ import { ChevronLeft } from 'lucide-react'
5
5
  import { Button } from '@hanzo/ui/primitives'
6
6
 
7
7
  const CloseButton: React.FC<{
8
- onClose: () => void
8
+ close: () => void
9
9
  className?: string
10
10
  }> = ({
11
- onClose,
11
+ close,
12
12
  className=''
13
13
  }) => (
14
14
  <Button
15
15
  variant='ghost'
16
16
  size='icon'
17
- onClick={onClose}
17
+ onClick={close}
18
18
  className={'group ' + className}
19
19
  >
20
20
  <ChevronLeft className='w-5 h-5 group-hover:scale-110 transition-scale transition-duration-300'/>
@@ -0,0 +1,46 @@
1
+ 'use client'
2
+ import React, { type PropsWithChildren } from 'react'
3
+
4
+ import { ScrollArea, StepIndicator } from '@hanzo/ui/primitives'
5
+ import { AuthWidget } from '@hanzo/auth/components'
6
+
7
+ import CartPanel from '../cart-panel'
8
+ import CloseButton from './close-button'
9
+
10
+ const DesktopPanel: React.FC<PropsWithChildren & {
11
+ index: number
12
+ stepNames: string[]
13
+ close:() => void
14
+ className?: string
15
+ }> = ({
16
+ index,
17
+ stepNames,
18
+ close,
19
+ className='',
20
+ children
21
+ }) => (
22
+ <div /* id='DT-GRID' */ className={className}>
23
+ <div className='w-full bg-background flex flex-row items-start justify-end'>
24
+ <ScrollArea className='h-full w-full max-w-[750px] relative flex flex-col items-center justify-start px-6 pt-12 pb-9'>
25
+ <CloseButton close={close} className='absolute top-3 left-3 w-auto h-auto rounded-full bg-level-1 hover:bg-level-2 hover:border-muted p-2' />
26
+ <CartPanel showCarousel className='flex border-none mt-10 w-full max-w-[550px] flex-col' />
27
+ </ScrollArea>
28
+ </div>
29
+ <ScrollArea className='w-full h-full bg-level-1 flex flex-row items-start justify-start overflow-y-auto'>
30
+ <div className='h-full w-full max-w-[750px] relative flex flex-col items-center gap-6 px-8 pb-6 pt-0'>
31
+ <AuthWidget noLogin className='hidden md:flex absolute top-4 right-4 '/>
32
+ <div className='bg-level-1 sticky h-[90px] pb-2 w-full top-0 flex justify-center items-end'>
33
+ <StepIndicator
34
+ dotSizeRem={1.5}
35
+ steps={stepNames}
36
+ currentStep={index}
37
+ className='gap-2 text-base w-pr-70 '
38
+ />
39
+ </div>
40
+ {children}
41
+ </div>
42
+ </ScrollArea>
43
+ </div>
44
+ )
45
+
46
+ export default DesktopPanel
@@ -1,32 +1,42 @@
1
1
  'use client'
2
+ import React, { useState } from 'react'
2
3
 
3
- import { useState } from 'react'
4
- import { observer } from 'mobx-react-lite'
5
-
6
- import {
7
- Dialog,
8
- DialogPortal,
9
- ScrollArea,
10
- } from '@hanzo/ui/primitives'
11
- import { cn } from '@hanzo/ui/util'
12
- import { AuthWidget } from '@hanzo/auth/components'
4
+ import { capitalize } from '@hanzo/ui/util'
13
5
 
14
6
  import { useCommerce } from '../..'
15
7
 
16
- import ShippingInfo from './shipping-info'
17
- import ThankYou from './thank-you'
18
- import CartPanel from '../cart-panel'
19
- import Payment from './payment'
20
- import CloseButton from './close-button'
21
- import StepIndicator from './step-indicator'
22
- import CartAccordian from './cart-accordian'
8
+ import type { CheckoutStep } from './steps/types'
9
+ import ShippingInfo from './steps/shipping-info'
10
+ import ThankYou from './steps/thank-you'
11
+ import Payment from './steps/payment'
12
+
13
+ const STEPS = [
14
+ {
15
+ name: 'payment',
16
+ Comp: Payment
17
+ },
18
+ {
19
+ name: 'delivery',
20
+ Comp: ShippingInfo
21
+ },
22
+ {
23
+ name: 'done',
24
+ label: 'Done!',
25
+ Comp: ThankYou
26
+ }
27
+ ] satisfies CheckoutStep[]
28
+
29
+ const STEP_NAMES = STEPS.map((s) => (s.label ? s.label : capitalize(s.name)))
30
+
31
+ import DesktopCP from './desktop'
32
+ import MobileCP from './mobile'
23
33
 
24
34
  const CheckoutPanel: React.FC<{
25
- open: boolean
26
- onCheckoutClose: () => void
27
- }> = observer(({
28
- open,
29
- onCheckoutClose
35
+ close: () => void
36
+ className?: string
37
+ }> = ({
38
+ close,
39
+ className=''
30
40
  }) => {
31
41
 
32
42
  const cmmc = useCommerce()
@@ -35,74 +45,64 @@ const CheckoutPanel: React.FC<{
35
45
  if (!cmmc) {
36
46
  return <></>
37
47
  }
38
-
39
- const [step, setStep] = useState<number>(1)
40
- const [orderId, setOrderId] = useState<string>()
48
+ const [stepIndex, setStepIndex] = useState<number>(0)
49
+ const [orderId, setOrderId] = useState<string | undefined>(undefined)
50
+
51
+ // Step.name or 'first' or 'next' or 'last'
52
+ const setStep = (name: string): void => {
41
53
 
42
- const steps = {
43
- 1: {
44
- label: 'Payment',
45
- element: (
46
- <Payment
47
- orderId={orderId}
48
- setOrderId={setOrderId}
49
- setStep={setStep}
50
- />
51
- )}
52
- ,
53
- 2: {
54
- label: 'Delivery',
55
- element: (
56
- <ShippingInfo orderId={orderId} setStep={setStep}/>
57
- )},
58
- 3: {
59
- label: 'Done!',
60
- element: (
61
- <ThankYou/>
62
- )}
63
- } as Record<number, {label: string, element: JSX.Element}>
54
+ if (name === 'first') {
55
+ setStepIndex(0)
56
+ }
57
+ else if (name === 'last') {
58
+ setStepIndex(STEPS.length - 1)
59
+ }
60
+ else if (name === 'next') {
61
+ if (stepIndex < STEPS.length - 2) {
62
+ setStepIndex(stepIndex + 1)
63
+ }
64
+ else {
65
+ throw new Error('CheckoutPanel.setStep(): Attempting to advance past last step!')
66
+ }
67
+ }
68
+ else {
69
+ const indexFound = STEPS.findIndex((el) => (el.name === name))
70
+ if (indexFound !== -1) {
71
+ setStepIndex(indexFound)
72
+ }
73
+ else {
74
+ throw new Error('CheckoutPanel.setStep(): Step named ' + name + ' not found!')
75
+ }
76
+ }
77
+ }
64
78
 
65
- const onClose = () => {
66
- setStep(1)
67
- onCheckoutClose()
79
+ const _close = () => {
80
+ setStep('first')
81
+ close()
68
82
  }
69
83
 
84
+ const StepToRender = STEPS[stepIndex].Comp
85
+
70
86
  return (
71
- <Dialog open={open}>
72
- <DialogPortal>
73
- <div /* id='PORTAL_OUTER' */
74
- className={cn(
75
- 'fixed top-0 shadow-lg ',
76
- 'animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10',
77
- 'sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0',
78
- '!max-w-none w-full h-full min-h-screen bg-transparent backdrop-blur-sm z-50'
79
- )}
80
- >
81
- <div /* id='GRID' */ className='flex flex-col md:flex-row justify-center h-full overflow-y-auto md:overflow-y-hidden'>
82
- <div className='w-full bg-background flex flex-row items-start justify-end'>
83
- <ScrollArea className='h-full w-full max-w-[750px] relative flex flex-col items-center justify-start px-6 pt-12 pb-0 md:pb-9'>
84
- <CloseButton onClose={onClose} className='absolute top-3 left-3 w-auto h-auto rounded-full bg-level-1 hover:bg-level-2 hover:border-muted p-2' />
85
- <AuthWidget hideLogin className='flex md:hidden absolute top-3 right-3'/>
86
- <CartPanel
87
- className='hidden md:flex border-none mt-10 w-full max-w-[550px] flex-col'
88
- noCheckout
89
- showProductsCarousel
90
- />
91
- <CartAccordian className='md:hidden flex items-center justify-center py-2 w-full' />
92
- </ScrollArea>
93
- </div>
94
- <ScrollArea className='w-full h-full bg-level-1 flex flex-row items-start justify-start md:overflow-y-auto'>
95
- <div className='h-full w-full max-w-[750px] relative flex flex-col gap-8 sm:gap-14 px-4 md:px-8 pb-6 pt-6 md:pt-14'>
96
- <AuthWidget hideLogin className='hidden md:flex absolute top-4 right-4 '/>
97
- <StepIndicator steps={steps} currentStep={step} className='flex gap-2 mx-auto items-center text-xxs sm:text-base' />
98
- {steps[step].element}
99
- </div>
100
- </ScrollArea>
101
- </div>
102
- </div>
103
- </DialogPortal>
104
- </Dialog>
87
+ <div /* id='CHECKOUT_PANEL' */ className={className} >
88
+ <DesktopCP
89
+ className='hidden md:flex h-full flex-row justify-center overflow-y-hidden'
90
+ close={_close}
91
+ index={stepIndex}
92
+ stepNames={STEP_NAMES}
93
+ >
94
+ <StepToRender onDone={() => {setStep('next')}} orderId={orderId} setOrderId={setOrderId}/>
95
+ </DesktopCP>
96
+ <MobileCP
97
+ className='md:hidden h-full overflow-y-auto'
98
+ close={_close}
99
+ index={stepIndex}
100
+ stepNames={STEP_NAMES}
101
+ >
102
+ <StepToRender onDone={() => {setStep('next')}} orderId={orderId} setOrderId={setOrderId}/>
103
+ </MobileCP>
104
+ </div>
105
105
  )
106
- })
106
+ }
107
107
 
108
108
  export default CheckoutPanel
@@ -0,0 +1,41 @@
1
+ 'use client'
2
+ import React, { type PropsWithChildren } from 'react'
3
+
4
+ import { StepIndicator } from '@hanzo/ui/primitives'
5
+ import { cn } from '@hanzo/ui/util'
6
+ import { AuthWidget } from '@hanzo/auth/components'
7
+
8
+ import CloseButton from './close-button'
9
+ import CartAccordian from './cart-accordian'
10
+
11
+ const MobilePanel: React.FC<PropsWithChildren & {
12
+ index: number
13
+ stepNames: string[]
14
+ close:() => void
15
+ className?: string
16
+ }> = ({
17
+ index,
18
+ stepNames,
19
+ close,
20
+ className='',
21
+ children
22
+ }) => (
23
+
24
+ <div /* id='MOBILE_GRID' */ className={cn('bg-background flex flex-col justify-start px-4', className)}>
25
+ <div className='sticky top-0 w-full flex flex-row justify-between items-start bg-background'>
26
+ <CloseButton close={close} className='rounded-full bg-background hover:bg-background p-1 -ml-3' />
27
+ <StepIndicator
28
+ dotSizeRem={1}
29
+ steps={stepNames}
30
+ currentStep={index}
31
+ className='text-xs font-semibold w-pr-70 mt-3'
32
+ />
33
+ {/* Need wrapper div since 'noLogin' returns null if no logged in user */}
34
+ <div className='w-10 h-10 flex items-center justify-center'><AuthWidget noLogin className=''/></div>
35
+ </div>
36
+ <CartAccordian className='flex items-center justify-center py-2 mt-2 w-full' />
37
+ {children}
38
+ </div>
39
+ )
40
+
41
+ export default MobilePanel
@@ -6,11 +6,12 @@ import Visa from './visa'
6
6
  import DinersClub from './diners-club'
7
7
  import Jcb from './jcb'
8
8
 
9
+ // <span className='hidden sm:flex text-sm'>Secure payments with</span>
10
+
9
11
  const PaymentMethods: React.FC = () => {
10
12
  return (
11
13
  <div className='flex gap-1 items-center text-muted-1'>
12
14
  <LockKeyhole className='w-4 h-4'/>
13
- <span className='hidden sm:flex text-sm'>Secure payments with</span>
14
15
  <Amex className='w-9 h-5'/>
15
16
  <Discover className='w-9 h-5'/>
16
17
  <Mastercard className='w-9 h-5'/>
@@ -0,0 +1,17 @@
1
+ import { Button } from '@hanzo/ui/primitives'
2
+
3
+ import PaymentMethods from './cards'
4
+ import type { PropsWithChildren } from 'react'
5
+
6
+ const CCButton: React.FC<PropsWithChildren> = ({
7
+ children
8
+ }) => (
9
+ <Button variant='outline' size='lg' className='flex items-center' >
10
+ <div className='font-sans text-base mr-2 text-muted'>CC</div>
11
+ <PaymentMethods />
12
+ {children}
13
+ </Button>
14
+ )
15
+
16
+
17
+ export default CCButton
@@ -3,7 +3,6 @@ import {
3
3
  FormControl,
4
4
  FormField,
5
5
  FormItem,
6
- FormLabel,
7
6
  FormMessage,
8
7
  } from '@hanzo/ui/primitives/form'
9
8
  import { Input } from '@hanzo/ui/primitives'
@@ -23,16 +22,16 @@ const ContactInfo: React.FC<{
23
22
  return (
24
23
  <Form {...form}>
25
24
  <form className='text-left'>
26
- <div className='flex gap-1 sm:gap-2'>
25
+ <div className='flex flex-col gap-1 sm:gap-2'>
27
26
  <FormField
28
27
  control={form.control}
29
28
  name='name'
30
29
  render={({ field }) => (
31
- <FormItem className='space-y-1 w-full'>
30
+ <FormItem className='w-full'>
32
31
  <FormControl>
33
32
  <Input {...field} className='border-muted-4' placeholder='Full name'/>
34
33
  </FormControl>
35
- <FormMessage />
34
+ <FormMessage className='py-0 space-y-0 !m-0'/>
36
35
  </FormItem>
37
36
  )}
38
37
  />
@@ -40,11 +39,11 @@ const ContactInfo: React.FC<{
40
39
  control={form.control}
41
40
  name='email'
42
41
  render={({ field }) => (
43
- <FormItem className='space-y-1 w-full'>
42
+ <FormItem className='w-full'>
44
43
  <FormControl>
45
44
  <Input {...field} className='border-muted-4' placeholder='Email'/>
46
45
  </FormControl>
47
- <FormMessage />
46
+ <FormMessage className='py-0 space-y-0 !m-0'/>
48
47
  </FormItem>
49
48
  )}
50
49
  />
@@ -12,21 +12,20 @@ import { useAuth } from '@hanzo/auth/service'
12
12
  import PayWithCrypto from './pay-with-crypto'
13
13
  import PayWithBankTransfer from './pay-with-bank-transfer'
14
14
  import PayWithCard from './pay-with-card'
15
- import { useCommerce } from '../../../service/context'
16
- import type { TransactionStatus } from '../../../types'
17
- import { sendFBEvent, sendGAEvent } from '../../../util/analytics'
15
+
16
+ import { useCommerce } from '../../../../service/context'
17
+ import type { TransactionStatus } from '../../../../types'
18
+ import { sendFBEvent, sendGAEvent } from '../../../../util/analytics'
19
+
20
+ import type { StepComponentProps } from '../types'
18
21
 
19
22
  const contactFormSchema = z.object({
20
23
  name: z.string().min(1, 'Enter your full name.'),
21
24
  email: z.string().email(),
22
25
  })
23
26
 
24
- const Payment: React.FC<{
25
- setStep: (s: number) => void
26
- orderId?: string
27
- setOrderId: (orderId?: string) => void
28
- }> = observer(({
29
- setStep,
27
+ const Payment: React.FC<StepComponentProps> = observer(({
28
+ onDone,
30
29
  orderId,
31
30
  setOrderId
32
31
  }) => {
@@ -51,7 +50,7 @@ const Payment: React.FC<{
51
50
 
52
51
  const storePaymentInfo = async (paymentInfo: any) => {
53
52
  const {name, email} = contactForm.getValues()
54
- let id
53
+ let id: string | undefined = undefined
55
54
  if (!orderId) {
56
55
  id = await cmmc.createOrder(email, name)
57
56
  setOrderId(id)
@@ -81,34 +80,41 @@ const Payment: React.FC<{
81
80
  }
82
81
  }
83
82
 
83
+ const groupClx = 'grid w-full grid-cols-3 mx-auto ' +
84
+ 'p-0 h-auto overflow-hidden ' +
85
+ 'border-2 bg-background border-level-2 md:bg-level-1 md:border-level-3 '
86
+
87
+ const tabClx = 'whitespace-normal h-full text-xs sm:text-base px-1 text-muted ' +
88
+ 'data-[state=active]:text-accent data-[state=active]:bg-level-2 md:data-[state=active]:bg-level-3'
89
+
84
90
  return (
85
- <Tabs defaultValue='card' className='w-full mx-auto max-w-[50rem]'>
86
- <TabsList className='grid w-full grid-cols-3 mx-auto bg-level-2 h-auto'>
91
+ <Tabs defaultValue='card' className='w-full sm:max-w-[500px] sm:mx-auto'>
92
+ <TabsList className={groupClx}>
87
93
  <TabsTrigger
88
94
  value='card'
89
- className='whitespace-normal h-full text-xs sm:text-base px-1'
95
+ className={tabClx}
90
96
  disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
91
97
  >
92
- Pay with card
98
+ Card
93
99
  </TabsTrigger>
94
100
  <TabsTrigger
95
101
  value='crypto'
96
- className='whitespace-normal h-full text-xs sm:text-base px-1'
102
+ className={tabClx}
97
103
  disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
98
104
  >
99
- Pay with Wallet
105
+ Wallet
100
106
  </TabsTrigger>
101
107
  <TabsTrigger
102
108
  value='bank'
103
- className='whitespace-normal h-full text-xs sm:text-base px-1'
109
+ className={tabClx}
104
110
  disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
105
111
  >
106
- Pay with Wire
112
+ Bank Wire
107
113
  </TabsTrigger>
108
114
  </TabsList>
109
115
  <TabsContent value='card'>
110
116
  <PayWithCard
111
- setStep={setStep}
117
+ onDone={onDone}
112
118
  transactionStatus={transactionStatus}
113
119
  setTransactionStatus={setTransactionStatus}
114
120
  storePaymentInfo={storePaymentInfo}
@@ -117,7 +123,7 @@ const Payment: React.FC<{
117
123
  </TabsContent>
118
124
  <TabsContent value='crypto'>
119
125
  <PayWithCrypto
120
- setStep={setStep}
126
+ onDone={onDone}
121
127
  transactionStatus={transactionStatus}
122
128
  setTransactionStatus={setTransactionStatus}
123
129
  storePaymentInfo={storePaymentInfo}
@@ -126,7 +132,7 @@ const Payment: React.FC<{
126
132
  </TabsContent>
127
133
  <TabsContent value='bank'>
128
134
  <PayWithBankTransfer
129
- setStep={setStep}
135
+ onDone={onDone}
130
136
  storePaymentInfo={storePaymentInfo}
131
137
  contactForm={contactForm}
132
138
  />
@@ -35,7 +35,7 @@ const InfoField: React.FC<{
35
35
  }
36
36
 
37
37
  const PayWithBankTransfer: React.FC<{
38
- setStep: (step: number) => void
38
+ onDone: () => void
39
39
  storePaymentInfo: (paymentInfo: any) => Promise<void>
40
40
  contactForm: UseFormReturn<{
41
41
  name: string
@@ -45,24 +45,28 @@ const PayWithBankTransfer: React.FC<{
45
45
  email: string
46
46
  }>
47
47
  }> = ({
48
- setStep,
48
+ onDone,
49
49
  storePaymentInfo,
50
50
  contactForm
51
51
  }) => {
52
+
52
53
  const payByBankTransfer = async () => {
53
- contactForm.handleSubmit(async () => {
54
+ contactForm.handleSubmit( async () => {
54
55
  await storePaymentInfo({paymentMethod: 'bank-transfer'})
55
- setStep(2)
56
+ onDone()
56
57
  })()
57
58
  }
58
59
 
60
+ const groupClx = 'border-2 bg-background border-level-2 md:bg-level-1 md:border-level-3 p-0 h-auto overflow-hidden rounded-md'
61
+ const tabClx = 'text-muted data-[state=active]:text-accent data-[state=active]:bg-level-2 md:data-[state=active]:bg-level-3'
62
+
59
63
  return (
60
64
  <div className='flex flex-col gap-2 mt-6'>
61
65
  <ContactInfo form={contactForm}/>
62
66
  <Tabs defaultValue="usd" className='w-full mx-auto max-w-[50rem]'>
63
- <TabsList className="grid w-full grid-cols-2 max-w-[15rem] mx-auto bg-level-2">
64
- <TabsTrigger value="usd">USD</TabsTrigger>
65
- <TabsTrigger value="eur">EUR/GBP</TabsTrigger>
67
+ <TabsList className={"grid w-full grid-cols-2 max-w-[15rem] mx-auto " + groupClx}>
68
+ <TabsTrigger className={tabClx} value="usd">USD</TabsTrigger>
69
+ <TabsTrigger className={tabClx} value="eur">EUR/GBP</TabsTrigger>
66
70
  </TabsList>
67
71
  <TabsContent value="usd">
68
72
  <div className='flex flex-col gap-4 w-full'>