@hanzo/commerce 4.1.1 → 4.3.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.
@@ -0,0 +1,24 @@
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import { ChevronLeft } from 'lucide-react'
5
+ import { Button } from '@hanzo/ui/primitives'
6
+
7
+ const CloseButton: React.FC<{
8
+ onClose: () => void
9
+ className?: string
10
+ }> = ({
11
+ onClose,
12
+ className=''
13
+ }) => (
14
+ <Button
15
+ variant='ghost'
16
+ size='icon'
17
+ onClick={onClose}
18
+ className={className}
19
+ >
20
+ <ChevronLeft className='w-5 h-5'/>
21
+ </Button>
22
+ )
23
+
24
+ export default CloseButton
@@ -1,10 +1,9 @@
1
1
  'use client'
2
2
 
3
3
  import { useState } from 'react'
4
- import { ChevronLeft } from 'lucide-react'
5
4
  import { observer } from 'mobx-react-lite'
6
5
 
7
- import { Button, Dialog, DialogPortal, Main, Separator, Toaster } from '@hanzo/ui/primitives'
6
+ import { Dialog, DialogPortal, Separator, Toaster } from '@hanzo/ui/primitives'
8
7
  import { cn } from '@hanzo/ui/util'
9
8
  import { useAuth } from '@hanzo/auth/service'
10
9
  import { LoginComponent, AuthWidget } from '@hanzo/auth/components'
@@ -13,6 +12,8 @@ import ShippingInfo from './shipping-info'
13
12
  import ThankYou from './thank-you'
14
13
  import CartPanel from '../cart-panel'
15
14
  import Payment from './payment'
15
+ import CloseButton from './close-button'
16
+ import StepIndicator from './step-indicator'
16
17
 
17
18
  const CheckoutPanel: React.FC<{
18
19
  open: boolean
@@ -24,74 +25,69 @@ const CheckoutPanel: React.FC<{
24
25
 
25
26
  const auth = useAuth()
26
27
 
27
- const [currentStep, setCurrentStep] = useState(1)
28
+ const [step, setStep] = useState<number>(1)
28
29
  const [orderId, setOrderId] = useState<string>()
29
30
 
30
- const step1 = auth.loggedIn ? (
31
- <Payment
32
- orderId={orderId}
33
- setOrderId={setOrderId}
34
- setCurrentStep={setCurrentStep}
35
- />
36
- ) : (
37
- <LoginComponent hideHeader className='max-w-[20rem] mx-auto' inputClassName='border-muted-4'/>
38
- )
39
-
40
- const step2 = (
41
- <ShippingInfo orderId={orderId} setCurrentStep={setCurrentStep}/>
42
- )
43
-
44
- const step3 = <ThankYou/>
31
+ const steps = {
32
+ 1: {
33
+ label: 'Payment',
34
+ element: (
35
+ auth.loggedIn ? (
36
+ <Payment
37
+ orderId={orderId}
38
+ setOrderId={setOrderId}
39
+ setStep={setStep}
40
+ />
41
+ ) : (
42
+ <LoginComponent hideHeader className='max-w-[20rem] mx-auto' inputClassName='border-muted-4'>
43
+ <h6 className='font-nav'>Please login to proceed</h6>
44
+ </LoginComponent >
45
+ )
46
+ )}
47
+ ,
48
+ 2: {
49
+ label: 'Delivery',
50
+ element: (
51
+ <ShippingInfo orderId={orderId} setStep={setStep}/>
52
+ )},
53
+ 3: {
54
+ label: 'Done!',
55
+ element: (
56
+ <ThankYou/>
57
+ )}
58
+ } as Record<number, {label: string, element: JSX.Element}>
45
59
 
46
- const steps = [step1, step2, step3]
60
+ const onClose = () => {
61
+ setStep(1)
62
+ setOpen(false)
63
+ }
47
64
 
48
65
  return (
49
66
  <Dialog open={open}>
50
67
  <DialogPortal>
51
- <div
68
+ <div id='PORTAL_OUTER'
52
69
  className={cn(
53
- 'fixed grid shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0',
54
- '!max-w-none w-full h-full min-h-screen bg-background z-50'
70
+ 'fixed top-0 pt-[44px] md:pt-[80px] shadow-lg ',
71
+ 'animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10',
72
+ 'sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0',
73
+ '!max-w-none w-full h-full min-h-screen bg-transparent backdrop-blur-sm z-50'
55
74
  )}
56
75
  >
57
- <Toaster/>
58
- <div className='absolute flex w-full justify-between pt-2 md:pt-5 px-5'>
59
- <Button
60
- variant='ghost'
61
- size='icon'
62
- onClick={() => {
63
- setCurrentStep(1)
64
- setOpen(false)
65
- }}
66
- className='w-auto h-auto rounded-full bg-level-1 p-2 md:p-4'
67
- >
68
- <ChevronLeft className='w-5 h-5'/>
69
- </Button>
70
- <AuthWidget/>
71
- </div>
72
-
73
- <div className='grid grid-cols-1 md:grid-cols-2 justify-center h-full overflow-y-auto md:overflow-y-hidden'>
74
- <div className='flex items-center justify-center py-2'>
75
- <CartPanel noCheckout className='border-none mt-10 w-full lg:w-1/2'/>
76
+ <div id='GRID' className='grid grid-cols-1 md:grid-cols-2 justify-center h-full overflow-y-auto md:overflow-y-hidden'>
77
+ <div className='bg-background flex flex-row items-start justify-end'>
78
+ <div className='h-full w-full max-w-[750px] relative flex flex-col items-center justify-start px-6 py-8 sm:py-20 '>
79
+ <CloseButton onClose={onClose} className='absolute top-2 left-4 w-auto h-auto rounded-full bg-level-1 hover:bg-level-2 hover:border-muted p-2' />
80
+ <AuthWidget hideLogin className='flex md:hidden absolute top-4 right-4 '/>
81
+ <CartPanel noCheckout className='md:border md:border-muted-2 mt-10 w-full max-w-[550px]'/>
82
+ </div>
76
83
  </div>
77
-
78
- <div className='bg-level-1'>
79
- <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'>
80
- <div className='flex gap-2 mx-auto items-center text-xxs sm:text-base'>
81
- <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' : '')}>
82
- <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Payment</div>
83
- </div>
84
- <Separator className='w-[4rem] sm:w-[6rem]'/>
85
- <div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 2 ? 'bg-foreground text-muted-4' : '')}>
86
- <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Delivery</div>
87
- </div>
88
- <Separator className='w-[4rem] sm:w-[6rem]'/>
89
- <div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 3 ? 'bg-foreground text-muted-4' : '')}>
90
- <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Done!</div>
91
- </div>
92
- </div>
93
- {steps[currentStep - 1]}
94
- </Main>
84
+ <div className='bg-level-1 flex flex-row items-start justify-start'>
85
+ <div className='h-full w-full max-w-[750px] relative flex flex-col gap-8 sm:gap-14 md:overflow-y-auto px-6 pb-6'>
86
+ <Toaster/>
87
+ <AuthWidget hideLogin className='hidden md:flex absolute top-4 right-4 '/>
88
+ <StepIndicator steps={steps} currentStep={step} className='flex gap-2 mx-auto items-center text-xxs sm:text-base' />
89
+ {steps[step].element}
90
+ </div>
95
91
  </div>
96
92
  </div>
97
93
  </div>
@@ -21,11 +21,11 @@ const contactFormSchema = z.object({
21
21
  })
22
22
 
23
23
  const Payment: React.FC<{
24
- setCurrentStep: (currentStep: number) => void
24
+ setStep: (s: number) => void
25
25
  orderId?: string
26
26
  setOrderId: (orderId?: string) => void
27
27
  }> = observer(({
28
- setCurrentStep,
28
+ setStep,
29
29
  orderId,
30
30
  setOrderId
31
31
  }) => {
@@ -89,7 +89,7 @@ const Payment: React.FC<{
89
89
  </TabsList>
90
90
  <TabsContent value='card'>
91
91
  <PayWithCard
92
- setCurrentStep={setCurrentStep}
92
+ setStep={setStep}
93
93
  transactionStatus={transactionStatus}
94
94
  setTransactionStatus={setTransactionStatus}
95
95
  storePaymentInfo={storePaymentInfo}
@@ -98,7 +98,7 @@ const Payment: React.FC<{
98
98
  </TabsContent>
99
99
  <TabsContent value='crypto'>
100
100
  <PayWithCrypto
101
- setCurrentStep={setCurrentStep}
101
+ setStep={setStep}
102
102
  transactionStatus={transactionStatus}
103
103
  setTransactionStatus={setTransactionStatus}
104
104
  storePaymentInfo={storePaymentInfo}
@@ -106,7 +106,7 @@ const Payment: React.FC<{
106
106
  </TabsContent>
107
107
  <TabsContent value='bank'>
108
108
  <PayWithBankTransfer
109
- setCurrentStep={setCurrentStep}
109
+ setStep={setStep}
110
110
  storePaymentInfo={storePaymentInfo}
111
111
  contactForm={contactForm}
112
112
  />
@@ -35,7 +35,7 @@ const InfoField: React.FC<{
35
35
  }
36
36
 
37
37
  const PayWithBankTransfer: React.FC<{
38
- setCurrentStep: (step: number) => void
38
+ setStep: (step: number) => void
39
39
  storePaymentInfo: (paymentInfo: any) => Promise<void>
40
40
  contactForm: UseFormReturn<{
41
41
  name: string
@@ -45,14 +45,14 @@ const PayWithBankTransfer: React.FC<{
45
45
  email: string
46
46
  }>
47
47
  }> = ({
48
- setCurrentStep,
48
+ setStep,
49
49
  storePaymentInfo,
50
50
  contactForm
51
51
  }) => {
52
52
  const payByBankTransfer = async () => {
53
53
  contactForm.handleSubmit(async () => {
54
54
  await storePaymentInfo({paymentMethod: 'bank-transfer'})
55
- setCurrentStep(2)
55
+ setStep(2)
56
56
  })()
57
57
  }
58
58
 
@@ -15,7 +15,7 @@ import PaymentMethods from './payment-methods'
15
15
  import ContactInfo from './contact-info'
16
16
 
17
17
  const PayWithCard: React.FC<{
18
- setCurrentStep: (currentStep: number) => void
18
+ setStep: (currentStep: number) => void
19
19
  transactionStatus: TransactionStatus
20
20
  setTransactionStatus: (status: TransactionStatus) => void
21
21
  storePaymentInfo: (paymentInfo: any) => Promise<void>
@@ -27,7 +27,7 @@ const PayWithCard: React.FC<{
27
27
  email: string
28
28
  }>
29
29
  }> = ({
30
- setCurrentStep,
30
+ setStep,
31
31
  transactionStatus,
32
32
  setTransactionStatus,
33
33
  storePaymentInfo,
@@ -115,7 +115,7 @@ const PayWithCard: React.FC<{
115
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
- <Button onClick={() => setCurrentStep(2)}>Continue</Button>
118
+ <Button onClick={() => setStep(2)}>Continue</Button>
119
119
  </div>
120
120
  ) : transactionStatus === 'confirmed' ? (
121
121
  <h6 className='mx-auto font-nav'>Processing your payment...</h6>
@@ -32,12 +32,12 @@ declare global {
32
32
  }
33
33
 
34
34
  const PayWithCrypto: React.FC<{
35
- setCurrentStep: (currentStep: number) => void
35
+ setStep: (currentStep: number) => void
36
36
  transactionStatus: TransactionStatus
37
37
  setTransactionStatus: (status: TransactionStatus) => void
38
38
  storePaymentInfo: (paymentInfo: any) => Promise<void>
39
39
  }> = observer(({
40
- setCurrentStep,
40
+ setStep,
41
41
  transactionStatus,
42
42
  setTransactionStatus,
43
43
  storePaymentInfo
@@ -151,7 +151,7 @@ const PayWithCrypto: React.FC<{
151
151
  await storePaymentInfo({
152
152
  paymentMethod: 'crypto'
153
153
  })
154
- setCurrentStep(2)
154
+ setStep(2)
155
155
  }
156
156
 
157
157
  const cryptoPriceWidget = !!!(auth.user?.walletAddress) ? (
@@ -40,10 +40,10 @@ const shippingFormSchema = z.object({
40
40
 
41
41
  const ShippingInfo: React.FC<{
42
42
  orderId?: string,
43
- setCurrentStep: (currentStep: number) => void
43
+ setStep: (currentStep: number) => void
44
44
  }> = ({
45
45
  orderId,
46
- setCurrentStep
46
+ setStep
47
47
  }) => {
48
48
  const auth = useAuth()
49
49
  const cmmc = useCommerce()
@@ -66,7 +66,7 @@ const ShippingInfo: React.FC<{
66
66
  if (auth.user && orderId) {
67
67
  await cmmc.updateOrderShippingInfo(orderId, values)
68
68
  }
69
- setCurrentStep(3)
69
+ setStep(3)
70
70
  }
71
71
 
72
72
  return (
@@ -0,0 +1,30 @@
1
+ import React from 'react'
2
+
3
+ import { Separator } from '@hanzo/ui/primitives'
4
+ import { cn } from '@hanzo/ui/util'
5
+
6
+ import type { CheckoutSteps } from './step'
7
+
8
+ const StepIndicator: React.FC<{
9
+ steps: CheckoutSteps
10
+ currentStep: number
11
+ className?: string
12
+ }> = ({
13
+ steps,
14
+ currentStep,
15
+ className=''
16
+ }) => (
17
+ <div className={className}>
18
+ {Object.keys(steps).map((key, index) => (<>
19
+ {index !== 0 && (<Separator className='w-[4rem] sm:w-[6rem]'/>)}
20
+ <div className={cn(
21
+ 'w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center',
22
+ currentStep === parseInt(key) ? 'bg-foreground text-muted-4' : ''
23
+ )}>
24
+ <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>{steps[parseInt(key)].label}</div>
25
+ </div>
26
+ </>))}
27
+ </div>
28
+ )
29
+
30
+ export default StepIndicator
@@ -0,0 +1,11 @@
1
+ interface CheckoutStep {
2
+ label: string
3
+ element: JSX.Element
4
+ }
5
+
6
+ type CheckoutSteps = Record<number, CheckoutStep>
7
+
8
+ export {
9
+ type CheckoutStep,
10
+ type CheckoutSteps
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/commerce",
3
- "version": "4.1.1",
3
+ "version": "4.3.0",
4
4
  "description": "Library with shopping cart components.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -38,7 +38,7 @@
38
38
  "square": "^35.0.0"
39
39
  },
40
40
  "peerDependencies": {
41
- "@hanzo/auth": "^2.0.2",
41
+ "@hanzo/auth": "^2.2.0",
42
42
  "@hanzo/ui": "^3.0.2",
43
43
  "@hookform/resolvers": "^3.3.4",
44
44
  "firebase": "^10.8.0",