@hanzo/commerce 4.6.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 (32) hide show
  1. package/components/add-to-cart-widget.tsx +2 -2
  2. package/components/cart-panel/index.tsx +13 -12
  3. package/components/cart-panel/products-carousel.tsx +60 -0
  4. package/components/checkout-panel/cart-accordian.tsx +47 -0
  5. package/components/checkout-panel/close-button.tsx +3 -3
  6. package/components/checkout-panel/desktop.tsx +46 -0
  7. package/components/checkout-panel/index.tsx +86 -97
  8. package/components/checkout-panel/mobile.tsx +41 -0
  9. package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/index.tsx +3 -2
  10. package/components/checkout-panel/steps/payment/cc-button.tsx +17 -0
  11. package/components/checkout-panel/{payment → steps/payment}/contact-info.tsx +7 -10
  12. package/components/checkout-panel/{payment → steps/payment}/index.tsx +27 -21
  13. package/components/checkout-panel/{payment → steps/payment}/pay-with-bank-transfer.tsx +14 -10
  14. package/components/checkout-panel/steps/payment/pay-with-card.tsx +246 -0
  15. package/components/checkout-panel/{payment → steps/payment}/pay-with-crypto.tsx +7 -19
  16. package/components/checkout-panel/{shipping-info.tsx → steps/shipping-info.tsx} +15 -22
  17. package/components/checkout-panel/{thank-you.tsx → steps/thank-you.tsx} +4 -1
  18. package/components/checkout-panel/steps/types.ts +14 -0
  19. package/package.json +5 -3
  20. package/service/impls/standalone/actual-line-item.ts +7 -1
  21. package/types/product.ts +4 -0
  22. package/{components/checkout-panel → util}/countries.ts +1 -1
  23. package/components/checkout-panel/payment/pay-with-card.tsx +0 -217
  24. package/components/checkout-panel/step-indicator.tsx +0 -30
  25. package/components/checkout-panel/step.ts +0 -11
  26. /package/components/checkout-panel/{confirm-order.tsx → _confirm-order.tsx_unused} +0 -0
  27. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/amex.tsx +0 -0
  28. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/diners-club.tsx +0 -0
  29. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/discover.tsx +0 -0
  30. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/jcb.tsx +0 -0
  31. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/mastercard.tsx +0 -0
  32. /package/components/checkout-panel/{payment/payment-methods → steps/payment/cards}/visa.tsx +0 -0
@@ -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-sm sm:text-base'
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-sm sm:text-base'
102
+ className={tabClx}
97
103
  disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
98
104
  >
99
- PAY WITH CRYPTO
105
+ Wallet
100
106
  </TabsTrigger>
101
107
  <TabsTrigger
102
108
  value='bank'
103
- className='whitespace-normal h-full text-sm sm:text-base'
109
+ className={tabClx}
104
110
  disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
105
111
  >
106
- BANK TRANSFER
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
  />
@@ -18,13 +18,13 @@ const InfoField: React.FC<{
18
18
  }) => {
19
19
  const copyToClipboard = (label: string, text: string) => {
20
20
  navigator.clipboard.writeText(text)
21
- toast(`${label} copied to clipboard`)
21
+ toast(`${label} copied to clipboard.`)
22
22
  }
23
23
 
24
24
  return (
25
25
  <div className='flex flex-col gap-1'>
26
26
  <p className='text-xs'>{label}</p>
27
- <div className='flex items-center justify-between text-lg border rounded-lg py-2 px-4'>
27
+ <div className='flex items-center justify-between sm:text-lg border rounded-lg py-2 px-4'>
28
28
  {value}
29
29
  <Button variant='ghost' size='icon' onClick={() => copyToClipboard(label, copyValue)}>
30
30
  <Copy className='h-4 w-4'/>
@@ -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
- <div className='flex flex-col gap-6 mt-6'>
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'>
@@ -0,0 +1,246 @@
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import type { UseFormReturn } from 'react-hook-form'
5
+ // @ts-ignore
6
+ import { ApplePay, GooglePay, CreditCard, PaymentForm } from 'react-square-web-payments-sdk'
7
+
8
+ import { ChevronRight } from 'lucide-react'
9
+
10
+ import {
11
+ Accordion,
12
+ AccordionContent,
13
+ AccordionItem,
14
+ AccordionTrigger,
15
+ ApplyTypography,
16
+ Button,
17
+ buttonVariants
18
+ } from '@hanzo/ui/primitives'
19
+
20
+ import { cn } from '@hanzo/ui/util'
21
+
22
+ import { processSquareCardPayment } from '../../../../util'
23
+ import { useCommerce } from '../../../../service/context'
24
+ import type { TransactionStatus } from '../../../../types'
25
+
26
+ import ContactInfo from './contact-info'
27
+ import { sendFBEvent, sendGAEvent } from '../../../../util/analytics'
28
+ import PaymentMethods from './cards'
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
+ }> = ({
43
+ onDone,
44
+ transactionStatus,
45
+ setTransactionStatus,
46
+ storePaymentInfo,
47
+ contactForm,
48
+ }) => {
49
+ const cmmc = useCommerce()
50
+
51
+ const cardTokenizeResponseReceived = async (
52
+ token: any,
53
+ verifiedBuyer: any
54
+ ) => {
55
+ contactForm.handleSubmit(async () => {
56
+ setTransactionStatus('paid')
57
+ const res = await processSquareCardPayment(token.token, cmmc.cartTotal, verifiedBuyer.token)
58
+ if (res) {
59
+ console.log(token)
60
+ await storePaymentInfo({paymentMethod: token.details.method ?? null, processed: res})
61
+ setTransactionStatus('confirmed')
62
+ sendGAEvent('purchase', {
63
+ transaction_id: res.payment?.id,
64
+ value: res.payment?.amountMoney?.amount,
65
+ currency: res.payment?.amountMoney?.currency,
66
+ items: cmmc.cartItems.map((item) => ({
67
+ item_id: item.sku,
68
+ item_name: item.title,
69
+ item_category: item.categoryId,
70
+ price: item.price,
71
+ quantity: item.quantity
72
+ })),
73
+ })
74
+ sendFBEvent('Purchase', {
75
+ content_ids: cmmc.cartItems.map((item) => item.sku),
76
+ contents: cmmc.cartItems.map(item => ({
77
+ id: item.sku,
78
+ quantity: item.quantity
79
+ })),
80
+ num_items: cmmc.cartItems.length,
81
+ value: cmmc.cartTotal,
82
+ currency: 'USD',
83
+ })
84
+ } else {
85
+ setTransactionStatus('error')
86
+ }
87
+ })()
88
+ }
89
+
90
+ const createVerificationDetails = () => {
91
+ const {name, email} = contactForm.getValues()
92
+ return {
93
+ amount: cmmc.cartTotal.toFixed(2),
94
+ billingContact: {
95
+ givenName: name,
96
+ email,
97
+ },
98
+ currencyCode: 'USD',
99
+ intent: 'CHARGE',
100
+ }
101
+ }
102
+
103
+ const createPaymentRequest= () => ({
104
+ countryCode: "US",
105
+ currencyCode: "USD",
106
+ lineItems: cmmc.cartItems.map(item => ({
107
+ amount: item.price.toFixed(2),
108
+ label: item.title,
109
+ id: item.sku,
110
+ })),
111
+ requestBillingContact: false,
112
+ requestShippingContact: false,
113
+ total: {
114
+ amount: cmmc.cartTotal.toFixed(2),
115
+ label: "Total",
116
+ },
117
+ })
118
+
119
+ return (
120
+ <PaymentForm
121
+ /**
122
+ * Identifies the calling form with a verified application ID generated from
123
+ * the Square Application Dashboard.
124
+ */
125
+ applicationId={process.env.NEXT_PUBLIC_SQUARE_APPLICATION_ID}
126
+ /**
127
+ * Invoked when payment form receives the result of a tokenize generation
128
+ * request. The result will be a valid credit card or wallet token, or an error.
129
+ */
130
+ cardTokenizeResponseReceived={cardTokenizeResponseReceived}
131
+ /**
132
+ * This function enable the Strong Customer Authentication (SCA) flow
133
+ *
134
+ * We strongly recommend use this function to verify the buyer and reduce
135
+ * the chance of fraudulent transactions.
136
+ */
137
+ createVerificationDetails={createVerificationDetails}
138
+ /**
139
+ * This function is required for digital wallets (Apple Pay, Google Pay)
140
+ */
141
+ createPaymentRequest={createPaymentRequest}
142
+ /**
143
+ * Identifies the location of the merchant that is taking the payment.
144
+ * Obtained from the Square Application Dashboard - Locations tab.
145
+ */
146
+ locationId={process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID}
147
+ >
148
+ <ApplyTypography className='flex flex-col mt-6 gap-1'>
149
+ {transactionStatus === 'paid' ? (
150
+ <h6 className='mx-auto font-nav'>Processing your payment...</h6>
151
+ ) : transactionStatus === 'confirmed' ? (
152
+ <div className='flex flex-col gap-4'>
153
+ <h5 className='mx-auto font-nav'>Payment confirmed!</h5>
154
+ <p className='mx-auto'>Thank you for your purchase.</p>
155
+ <Button onClick={onDone}>Continue</Button>
156
+ </div>
157
+ ) : (
158
+ <>
159
+ <GooglePay/>
160
+ <ApplePay/>
161
+
162
+ <div className='flex gap-2 whitespace-nowrap items-center my-0.5 sm:my-1 text-xs text-muted'>
163
+ <hr className='grow border'/><div className='shrink-0 mx-1'>or</div><hr className='grow border'/>
164
+ </div>
165
+
166
+ <Accordion type="single" collapsible className={''}>
167
+ <AccordionItem value="cart" className='w-full border-b-0 data-[state=open]:border data-[state=open]:px-3 rounded '>
168
+ <AccordionTrigger className={cn(
169
+ buttonVariants({variant: 'outline', size: 'lg'}),
170
+ '!no-underline group flex justify-between ',
171
+ 'bg-transparent hover:bg-transparent border-muted-2 hover:border-muted-2',
172
+ 'data-[state=open]:border-none data-[state=open]:py-0 px-4 data-[state=open]:px-1'
173
+ )}>
174
+ <div className='font-sans text-base mr-2 text-muted'>CC</div>
175
+ <PaymentMethods />
176
+ <div>
177
+ <ChevronRight className="h-5 w-5 -mr-2 text-muted shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-90 hidden group-data-[state=open]:block" />
178
+ </div>
179
+ </AccordionTrigger>
180
+ <AccordionContent className='data-[state=open]:mb-4 data-[state=open]:mt-1'>
181
+ <ContactInfo form={contactForm}/>
182
+ {/* Imitates hanzo/ui Button and Input styles, I was unable to render the
183
+ hanzo/ui button outright and keeping the submit form functionality*/}
184
+ <CreditCard
185
+ style={{
186
+ '.input-container': {
187
+ borderColor: '#404040',
188
+ borderRadius: '6px',
189
+ },
190
+ '.input-container.is-focus': {
191
+ borderColor: '#ffffff',
192
+ },
193
+ '.input-container.is-error': {
194
+ borderColor: '#ff1600',
195
+ },
196
+ '.message-text': {
197
+ color: '#999999',
198
+ },
199
+ '.message-icon': {
200
+ color: '#999999',
201
+ },
202
+ '.message-text.is-error': {
203
+ color: '#ff1600',
204
+ },
205
+ '.message-icon.is-error': {
206
+ color: '#ff1600',
207
+ },
208
+ input: {
209
+ backgroundColor: '#1f1f1f',
210
+ color: '#FFFFFF',
211
+ fontSize: '15px',
212
+ fontFamily: 'helvetica neue, sans-serif',
213
+ },
214
+ 'input::placeholder': {
215
+ color: '#999999',
216
+ },
217
+ 'input.is-error': {
218
+ color: '#ff1600',
219
+ },
220
+ }}
221
+ render={(Button: any) => (
222
+ <Button className={cn(
223
+ 'items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2',
224
+ 'focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
225
+ '!bg-primary !text-primary-fg hover:!bg-primary-hover font-nav whitespace-nowrap not-typography h-10 py-2 px-4',
226
+ '!text-sm rounded-md lg:min-w-[220px] sm:min-w-[220px] flex'
227
+ )}>
228
+ Pay
229
+ </Button>
230
+ )}
231
+ />
232
+ {transactionStatus === 'error' && (
233
+ <p className='mx-auto text-destructive'>There was an error processing your payment.</p>
234
+ )}
235
+ </AccordionContent>
236
+ </AccordionItem>
237
+ </Accordion>
238
+
239
+ </>
240
+ )}
241
+ </ApplyTypography>
242
+ </PaymentForm>
243
+ )
244
+ }
245
+
246
+ export default PayWithCard
@@ -20,12 +20,12 @@ import {
20
20
 
21
21
  import { useAuth } from '@hanzo/auth/service'
22
22
 
23
- import Eth from '../icons/eth'
24
- import { useCommerce } from '../../../service/context'
25
- import type { TransactionStatus } from '../../../types'
23
+ import Eth from '../../icons/eth'
24
+ import { useCommerce } from '../../../../service/context'
25
+ import type { TransactionStatus } from '../../../../types'
26
26
  import ContactInfo from './contact-info'
27
27
  import type { UseFormReturn } from 'react-hook-form'
28
- import { sendFBEvent, sendGAEvent } from '../../../util/analytics'
28
+ import { sendFBEvent, sendGAEvent } from '../../../../util/analytics'
29
29
 
30
30
  declare global {
31
31
  interface Window{
@@ -34,7 +34,7 @@ declare global {
34
34
  }
35
35
 
36
36
  const PayWithCrypto: React.FC<{
37
- setStep: (currentStep: number) => void
37
+ onDone: () => void
38
38
  transactionStatus: TransactionStatus
39
39
  setTransactionStatus: (status: TransactionStatus) => void
40
40
  storePaymentInfo: (paymentInfo: any) => Promise<void>
@@ -46,7 +46,7 @@ const PayWithCrypto: React.FC<{
46
46
  email: string
47
47
  }>
48
48
  }> = observer(({
49
- setStep,
49
+ onDone,
50
50
  transactionStatus,
51
51
  setTransactionStatus,
52
52
  storePaymentInfo,
@@ -57,7 +57,6 @@ const PayWithCrypto: React.FC<{
57
57
  const [loadingPrice, setLoadingPrice] = useState(false)
58
58
  //const [selectedToken, setSelectedToken] = useState('eth')
59
59
  const [amount, setAmount] = useState<number>()
60
- const [availableAmount, setAvailableAmount] = useState<number>()
61
60
  const [provider, setProvider] = useState<ethers.BrowserProvider>()
62
61
 
63
62
  //const selectedToken = 'eth'
@@ -68,11 +67,6 @@ const PayWithCrypto: React.FC<{
68
67
  return autorun(() => {
69
68
  const newProvider = new ethers.BrowserProvider(window.ethereum)
70
69
  setProvider(newProvider)
71
- if (auth.user?.walletAddress) {
72
- newProvider.getBalance(auth.user?.walletAddress).then((balance: any) => {
73
- setAvailableAmount(Number(balance)/(10**18))
74
- })
75
- }
76
70
  })
77
71
  }, [])
78
72
 
@@ -109,11 +103,6 @@ const PayWithCrypto: React.FC<{
109
103
  })
110
104
  const newProvider = new ethers.BrowserProvider(window.ethereum)
111
105
  setProvider(newProvider)
112
- if (auth.user?.walletAddress) {
113
- newProvider.getBalance(auth.user?.walletAddress).then((balance: any) => {
114
- setAvailableAmount(Number(balance)/(10**18))
115
- })
116
- }
117
106
  } catch (err) {
118
107
  toast('Please switch your wallet to the Ethereum network.')
119
108
  return
@@ -193,7 +182,7 @@ const PayWithCrypto: React.FC<{
193
182
  await storePaymentInfo({
194
183
  paymentMethod: 'crypto'
195
184
  })
196
- setStep(2)
185
+ onDone()
197
186
  }
198
187
 
199
188
  return (
@@ -225,7 +214,6 @@ const PayWithCrypto: React.FC<{
225
214
  </div>
226
215
  </div>
227
216
  </div>
228
- <div>Available funds in your wallet: {availableAmount} ETH</div>
229
217
 
230
218
  {transactionStatus === 'error' ? (
231
219
  <h4 className='text-destructive'>There was an error while confirming the transaction.</h4>
@@ -21,10 +21,12 @@ import {
21
21
  SelectValue
22
22
  } from '@hanzo/ui/primitives'
23
23
 
24
- import { useCommerce } from '../..'
24
+ import { useCommerce } from '../../..'
25
25
 
26
- import { countries } from './countries'
27
- import { sendGAEvent } from '../../util/analytics'
26
+ import countries from '../../../util/countries'
27
+ import { sendGAEvent } from '../../../util/analytics'
28
+
29
+ import type { StepComponentProps } from './types'
28
30
 
29
31
  const shippingFormSchema = z.object({
30
32
  addressLine1: z.string().min(2, 'Address must be at least 2 characters.'),
@@ -35,12 +37,9 @@ const shippingFormSchema = z.object({
35
37
  country: z.string().min(2, 'Country is invalid.'),
36
38
  })
37
39
 
38
- const ShippingInfo: React.FC<{
39
- orderId?: string,
40
- setStep: (currentStep: number) => void
41
- }> = ({
40
+ const ShippingInfo: React.FC<StepComponentProps> = ({
42
41
  orderId,
43
- setStep
42
+ onDone
44
43
  }) => {
45
44
  const cmmc = useCommerce()
46
45
 
@@ -72,7 +71,7 @@ const ShippingInfo: React.FC<{
72
71
  value: cmmc.cartTotal,
73
72
  currency: 'USD',
74
73
  })
75
- setStep(3)
74
+ onDone()
76
75
  }
77
76
 
78
77
  return (
@@ -84,9 +83,8 @@ const ShippingInfo: React.FC<{
84
83
  name='addressLine1'
85
84
  render={({ field }) => (
86
85
  <FormItem className='space-y-1 w-full'>
87
- <FormLabel>Address line 1</FormLabel>
88
86
  <FormControl>
89
- <Input {...field} className='border-muted-4'/>
87
+ <Input {...field} className='border-muted-4' placeholder='Address line 1'/>
90
88
  </FormControl>
91
89
  <FormMessage />
92
90
  </FormItem>
@@ -97,9 +95,8 @@ const ShippingInfo: React.FC<{
97
95
  name='addressLine2'
98
96
  render={({ field }) => (
99
97
  <FormItem className='space-y-1 w-full'>
100
- <FormLabel>Address line 2 (optional)</FormLabel>
101
98
  <FormControl>
102
- <Input {...field} className='border-muted-4'/>
99
+ <Input {...field} className='border-muted-4' placeholder='Address line 2 (optional)'/>
103
100
  </FormControl>
104
101
  <FormMessage />
105
102
  </FormItem>
@@ -111,9 +108,8 @@ const ShippingInfo: React.FC<{
111
108
  name='zipCode'
112
109
  render={({ field }) => (
113
110
  <FormItem className='space-y-1 w-full'>
114
- <FormLabel>Zip code</FormLabel>
115
111
  <FormControl>
116
- <Input {...field} className='border-muted-4'/>
112
+ <Input {...field} className='border-muted-4' placeholder='Zip code'/>
117
113
  </FormControl>
118
114
  <FormMessage />
119
115
  </FormItem>
@@ -124,9 +120,8 @@ const ShippingInfo: React.FC<{
124
120
  name='city'
125
121
  render={({ field }) => (
126
122
  <FormItem className='space-y-1 w-full'>
127
- <FormLabel>City</FormLabel>
128
123
  <FormControl>
129
- <Input {...field} className='border-muted-4'/>
124
+ <Input {...field} className='border-muted-4' placeholder='City'/>
130
125
  </FormControl>
131
126
  <FormMessage />
132
127
  </FormItem>
@@ -139,9 +134,8 @@ const ShippingInfo: React.FC<{
139
134
  name='state'
140
135
  render={({ field }) => (
141
136
  <FormItem className='space-y-1 w-full'>
142
- <FormLabel>State (Optional)</FormLabel>
143
137
  <FormControl>
144
- <Input {...field} className='border-muted-4'/>
138
+ <Input {...field} className='border-muted-4' placeholder='State (optional)'/>
145
139
  </FormControl>
146
140
  <FormMessage />
147
141
  </FormItem>
@@ -152,11 +146,10 @@ const ShippingInfo: React.FC<{
152
146
  name='country'
153
147
  render={({ field }) => (
154
148
  <FormItem className='space-y-1 w-full'>
155
- <FormLabel>Country</FormLabel>
156
149
  <Select onValueChange={field.onChange} defaultValue={field.value}>
157
150
  <FormControl>
158
- <SelectTrigger>
159
- <SelectValue placeholder='Select a country' />
151
+ <SelectTrigger className='bg-level-1 border-muted-4'>
152
+ <SelectValue placeholder='Country' />
160
153
  </SelectTrigger>
161
154
  </FormControl>
162
155
  <SelectContent>
@@ -1,8 +1,11 @@
1
1
  import React from 'react'
2
2
  import Link from 'next/link'
3
+
3
4
  import { ApplyTypography } from '@hanzo/ui/primitives'
4
5
 
5
- const ThankYou: React.FC = () => (
6
+ import type { StepComponentProps } from './types'
7
+
8
+ const ThankYou: React.FC<StepComponentProps> = ({}) => (
6
9
  <ApplyTypography className='flex flex-col gap-4 text-center mt-10'>
7
10
  <h3>Thank you for your order!</h3>
8
11
  <h6>Once your payment has been confirmed we will send additional information to your email.</h6>
@@ -0,0 +1,14 @@
1
+ import type { ComponentType } from 'react'
2
+
3
+ export interface StepComponentProps {
4
+ onDone: () => void
5
+ orderId: string | undefined
6
+ setOrderId: (orderId: string | undefined) => void
7
+ }
8
+
9
+ export interface CheckoutStep {
10
+ name: string
11
+ label?: string
12
+ Comp: ComponentType<StepComponentProps>
13
+ }
14
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/commerce",
3
- "version": "4.6.0",
3
+ "version": "4.8.0",
4
4
  "description": "Library with shopping cart components.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -31,14 +31,16 @@
31
31
  "./types": "./types/index.ts"
32
32
  },
33
33
  "dependencies": {
34
+ "@splinetool/react-spline": "^2.2.6",
35
+ "@splinetool/runtime": "^1.0.75",
34
36
  "ethers": "^6.11.1",
35
37
  "next-usequerystate": "^1.17.0",
36
38
  "react-square-web-payments-sdk": "^3.2.1",
37
39
  "square": "^35.0.0"
38
40
  },
39
41
  "peerDependencies": {
40
- "@hanzo/auth": "^2.3.0",
41
- "@hanzo/ui": "^3.0.7",
42
+ "@hanzo/auth": "^2.3.2",
43
+ "@hanzo/ui": "^3.0.10",
42
44
  "@hookform/resolvers": "^3.3.4",
43
45
  "firebase": "^10.8.0",
44
46
  "lucide-react": "^0.307.0",