@hanzo/commerce 4.6.0 → 4.7.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.
- package/components/cart-panel/index.tsx +4 -0
- package/components/cart-panel/products-carousel.tsx +60 -0
- package/components/checkout-panel/cart-accordian.tsx +39 -0
- package/components/checkout-panel/index.tsx +18 -29
- package/components/checkout-panel/payment/contact-info.tsx +3 -5
- package/components/checkout-panel/payment/index.tsx +6 -6
- package/components/checkout-panel/payment/pay-with-bank-transfer.tsx +3 -3
- package/components/checkout-panel/payment/pay-with-card.tsx +21 -16
- package/components/checkout-panel/payment/pay-with-crypto.tsx +0 -12
- package/components/checkout-panel/payment/payment-methods/index.tsx +1 -1
- package/components/checkout-panel/shipping-info.tsx +7 -13
- package/package.json +5 -3
- package/service/impls/standalone/actual-line-item.ts +7 -1
- package/types/product.ts +4 -0
|
@@ -10,11 +10,13 @@ import { formatPrice } from '../../util'
|
|
|
10
10
|
|
|
11
11
|
import CartLineItem from './cart-line-item'
|
|
12
12
|
import { sendFBEvent, sendGAEvent } from '../../util/analytics'
|
|
13
|
+
import ProductsCarousel from './products-carousel'
|
|
13
14
|
|
|
14
15
|
const CartPanel: React.FC<PropsWithChildren & {
|
|
15
16
|
className?: string
|
|
16
17
|
isMobile?: boolean,
|
|
17
18
|
noCheckout?: boolean
|
|
19
|
+
showProductsCarousel?: boolean
|
|
18
20
|
onCheckoutOpen?: () => void
|
|
19
21
|
}> = observer(({
|
|
20
22
|
/** Children is the heading area. */
|
|
@@ -22,6 +24,7 @@ const CartPanel: React.FC<PropsWithChildren & {
|
|
|
22
24
|
className='',
|
|
23
25
|
isMobile=false,
|
|
24
26
|
noCheckout=false,
|
|
27
|
+
showProductsCarousel=false,
|
|
25
28
|
onCheckoutOpen,
|
|
26
29
|
}) => {
|
|
27
30
|
/* TODO: onCheckoutOpen is a hackish way fix a bug with multiple dialog opened at the same time.
|
|
@@ -60,6 +63,7 @@ const CartPanel: React.FC<PropsWithChildren & {
|
|
|
60
63
|
return (
|
|
61
64
|
<div className={cn('border p-4 rounded-lg', className)}>
|
|
62
65
|
{children}
|
|
66
|
+
{showProductsCarousel && <ProductsCarousel items={cmmc.cartItems}/>}
|
|
63
67
|
<div className='mt-2 w-full'>
|
|
64
68
|
{cmmc.cartEmpty ? (
|
|
65
69
|
<p className='text-center mt-4'>No items in cart</p>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import Spline from '@splinetool/react-spline'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Carousel,
|
|
5
|
+
CarouselContent,
|
|
6
|
+
CarouselItem,
|
|
7
|
+
CarouselPrevious,
|
|
8
|
+
CarouselNext
|
|
9
|
+
} from '@hanzo/ui/primitives'
|
|
10
|
+
import {
|
|
11
|
+
VideoBlockComponent,
|
|
12
|
+
ImageBlockComponent,
|
|
13
|
+
type ImageBlock,
|
|
14
|
+
type Block,
|
|
15
|
+
type VideoBlock
|
|
16
|
+
} from '@hanzo/ui/blocks'
|
|
17
|
+
|
|
18
|
+
import type { LineItem } from '../../types'
|
|
19
|
+
|
|
20
|
+
// Carousel content hierarchy: 3D > MP4 > Image
|
|
21
|
+
const ProductsCarousel: React.FC<{
|
|
22
|
+
items: LineItem[]
|
|
23
|
+
}> = ({
|
|
24
|
+
items,
|
|
25
|
+
}) => (
|
|
26
|
+
<Carousel options={{ loop: true }} className='w-full max-w-sm mx-auto px-2' >
|
|
27
|
+
<CarouselContent>
|
|
28
|
+
{items.map(({title, img, video, animation}, index) => (
|
|
29
|
+
<CarouselItem key={index}>
|
|
30
|
+
<div className='flex aspect-square items-center justify-center p-6'>
|
|
31
|
+
{animation ? (
|
|
32
|
+
<Spline
|
|
33
|
+
scene={animation}
|
|
34
|
+
className='!aspect-[12/10] pointer-events-none !w-auto !h-auto'
|
|
35
|
+
/>
|
|
36
|
+
) : video ? (
|
|
37
|
+
<VideoBlockComponent
|
|
38
|
+
block={{blockType: 'video', ...video} satisfies VideoBlock as Block}
|
|
39
|
+
/>
|
|
40
|
+
) : (
|
|
41
|
+
<ImageBlockComponent
|
|
42
|
+
block={{
|
|
43
|
+
blockType: 'image',
|
|
44
|
+
src: img ?? '',
|
|
45
|
+
alt: title + ' image',
|
|
46
|
+
dim: { w: 250, h: 250 }
|
|
47
|
+
} satisfies ImageBlock as Block}
|
|
48
|
+
className='m-auto'
|
|
49
|
+
/>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
</CarouselItem>
|
|
53
|
+
))}
|
|
54
|
+
</CarouselContent>
|
|
55
|
+
<CarouselPrevious />
|
|
56
|
+
<CarouselNext />
|
|
57
|
+
</Carousel>
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
export default ProductsCarousel
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { observer } from 'mobx-react-lite'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Accordion,
|
|
7
|
+
AccordionContent,
|
|
8
|
+
AccordionItem,
|
|
9
|
+
AccordionTrigger,
|
|
10
|
+
} from '@hanzo/ui/primitives'
|
|
11
|
+
|
|
12
|
+
import { formatPrice, useCommerce } from '../..'
|
|
13
|
+
|
|
14
|
+
import CartPanel from '../cart-panel'
|
|
15
|
+
import BagIcon from './icons/bag-icon'
|
|
16
|
+
|
|
17
|
+
const CartAccordian: React.FC<{className?: string}> = observer(({
|
|
18
|
+
className=''
|
|
19
|
+
}) => {
|
|
20
|
+
|
|
21
|
+
const cmmc = useCommerce()
|
|
22
|
+
return (
|
|
23
|
+
<Accordion type="single" collapsible className={className}>
|
|
24
|
+
<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>
|
|
29
|
+
</div>
|
|
30
|
+
</AccordionTrigger>
|
|
31
|
+
<AccordionContent>
|
|
32
|
+
<CartPanel noCheckout className='border-none w-full'/>
|
|
33
|
+
</AccordionContent>
|
|
34
|
+
</AccordionItem>
|
|
35
|
+
</Accordion>
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export default CartAccordian
|
|
@@ -4,24 +4,22 @@ import { useState } from 'react'
|
|
|
4
4
|
import { observer } from 'mobx-react-lite'
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
Accordion,
|
|
8
|
-
AccordionContent,
|
|
9
|
-
AccordionItem,
|
|
10
|
-
AccordionTrigger,
|
|
11
7
|
Dialog,
|
|
12
8
|
DialogPortal,
|
|
9
|
+
ScrollArea,
|
|
13
10
|
} from '@hanzo/ui/primitives'
|
|
14
11
|
import { cn } from '@hanzo/ui/util'
|
|
15
12
|
import { AuthWidget } from '@hanzo/auth/components'
|
|
16
13
|
|
|
14
|
+
import { useCommerce } from '../..'
|
|
15
|
+
|
|
17
16
|
import ShippingInfo from './shipping-info'
|
|
18
17
|
import ThankYou from './thank-you'
|
|
19
18
|
import CartPanel from '../cart-panel'
|
|
20
19
|
import Payment from './payment'
|
|
21
20
|
import CloseButton from './close-button'
|
|
22
21
|
import StepIndicator from './step-indicator'
|
|
23
|
-
import
|
|
24
|
-
import { formatPrice, useCommerce } from '../..'
|
|
22
|
+
import CartAccordian from './cart-accordian'
|
|
25
23
|
|
|
26
24
|
const CheckoutPanel: React.FC<{
|
|
27
25
|
open: boolean
|
|
@@ -72,7 +70,7 @@ const CheckoutPanel: React.FC<{
|
|
|
72
70
|
return (
|
|
73
71
|
<Dialog open={open}>
|
|
74
72
|
<DialogPortal>
|
|
75
|
-
<div id='PORTAL_OUTER'
|
|
73
|
+
<div /* id='PORTAL_OUTER' */
|
|
76
74
|
className={cn(
|
|
77
75
|
'fixed top-0 shadow-lg ',
|
|
78
76
|
'animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10',
|
|
@@ -80,35 +78,26 @@ const CheckoutPanel: React.FC<{
|
|
|
80
78
|
'!max-w-none w-full h-full min-h-screen bg-transparent backdrop-blur-sm z-50'
|
|
81
79
|
)}
|
|
82
80
|
>
|
|
83
|
-
<div id='GRID' className='
|
|
84
|
-
<div className='bg-background flex flex-row items-start justify-end'>
|
|
85
|
-
<
|
|
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'>
|
|
86
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' />
|
|
87
85
|
<AuthWidget hideLogin className='flex md:hidden absolute top-3 right-3'/>
|
|
88
|
-
<CartPanel
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<h5 className='text-sm sm:text-xl truncate'>Order Summary {formatPrice(cmmc.cartTotal)}</h5>
|
|
96
|
-
</div>
|
|
97
|
-
</AccordionTrigger>
|
|
98
|
-
<AccordionContent>
|
|
99
|
-
<CartPanel noCheckout className='border-none w-full'/>
|
|
100
|
-
</AccordionContent>
|
|
101
|
-
</AccordionItem>
|
|
102
|
-
</Accordion>
|
|
103
|
-
</div>
|
|
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>
|
|
104
93
|
</div>
|
|
105
|
-
<
|
|
106
|
-
<div className='h-full w-full max-w-[750px] relative flex flex-col gap-8 sm:gap-14 px-8 pb-6 pt-6 md:pt-14'>
|
|
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'>
|
|
107
96
|
<AuthWidget hideLogin className='hidden md:flex absolute top-4 right-4 '/>
|
|
108
97
|
<StepIndicator steps={steps} currentStep={step} className='flex gap-2 mx-auto items-center text-xxs sm:text-base' />
|
|
109
98
|
{steps[step].element}
|
|
110
99
|
</div>
|
|
111
|
-
</
|
|
100
|
+
</ScrollArea>
|
|
112
101
|
</div>
|
|
113
102
|
</div>
|
|
114
103
|
</DialogPortal>
|
|
@@ -23,15 +23,14 @@ const ContactInfo: React.FC<{
|
|
|
23
23
|
return (
|
|
24
24
|
<Form {...form}>
|
|
25
25
|
<form className='text-left'>
|
|
26
|
-
<div className='flex
|
|
26
|
+
<div className='flex gap-1 sm:gap-2'>
|
|
27
27
|
<FormField
|
|
28
28
|
control={form.control}
|
|
29
29
|
name='name'
|
|
30
30
|
render={({ field }) => (
|
|
31
31
|
<FormItem className='space-y-1 w-full'>
|
|
32
|
-
<FormLabel>Full name</FormLabel>
|
|
33
32
|
<FormControl>
|
|
34
|
-
<Input {...field} className='border-muted-4'/>
|
|
33
|
+
<Input {...field} className='border-muted-4' placeholder='Full name'/>
|
|
35
34
|
</FormControl>
|
|
36
35
|
<FormMessage />
|
|
37
36
|
</FormItem>
|
|
@@ -42,9 +41,8 @@ const ContactInfo: React.FC<{
|
|
|
42
41
|
name='email'
|
|
43
42
|
render={({ field }) => (
|
|
44
43
|
<FormItem className='space-y-1 w-full'>
|
|
45
|
-
<FormLabel>Email</FormLabel>
|
|
46
44
|
<FormControl>
|
|
47
|
-
<Input {...field} className='border-muted-4'/>
|
|
45
|
+
<Input {...field} className='border-muted-4' placeholder='Email'/>
|
|
48
46
|
</FormControl>
|
|
49
47
|
<FormMessage />
|
|
50
48
|
</FormItem>
|
|
@@ -86,24 +86,24 @@ const Payment: React.FC<{
|
|
|
86
86
|
<TabsList className='grid w-full grid-cols-3 mx-auto bg-level-2 h-auto'>
|
|
87
87
|
<TabsTrigger
|
|
88
88
|
value='card'
|
|
89
|
-
className='whitespace-normal h-full text-
|
|
89
|
+
className='whitespace-normal h-full text-xs sm:text-base px-1'
|
|
90
90
|
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
|
|
91
91
|
>
|
|
92
|
-
|
|
92
|
+
Pay with card
|
|
93
93
|
</TabsTrigger>
|
|
94
94
|
<TabsTrigger
|
|
95
95
|
value='crypto'
|
|
96
|
-
className='whitespace-normal h-full text-
|
|
96
|
+
className='whitespace-normal h-full text-xs sm:text-base px-1'
|
|
97
97
|
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
|
|
98
98
|
>
|
|
99
|
-
|
|
99
|
+
Pay with Wallet
|
|
100
100
|
</TabsTrigger>
|
|
101
101
|
<TabsTrigger
|
|
102
102
|
value='bank'
|
|
103
|
-
className='whitespace-normal h-full text-
|
|
103
|
+
className='whitespace-normal h-full text-xs sm:text-base px-1'
|
|
104
104
|
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
|
|
105
105
|
>
|
|
106
|
-
|
|
106
|
+
Pay with Wire
|
|
107
107
|
</TabsTrigger>
|
|
108
108
|
</TabsList>
|
|
109
109
|
<TabsContent value='card'>
|
|
@@ -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'/>
|
|
@@ -57,7 +57,7 @@ const PayWithBankTransfer: React.FC<{
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
|
-
<div className='flex flex-col gap-
|
|
60
|
+
<div className='flex flex-col gap-2 mt-6'>
|
|
61
61
|
<ContactInfo form={contactForm}/>
|
|
62
62
|
<Tabs defaultValue="usd" className='w-full mx-auto max-w-[50rem]'>
|
|
63
63
|
<TabsList className="grid w-full grid-cols-2 max-w-[15rem] mx-auto bg-level-2">
|
|
@@ -6,6 +6,7 @@ import type { UseFormReturn } from 'react-hook-form'
|
|
|
6
6
|
import { ApplePay, GooglePay, CreditCard, PaymentForm } from 'react-square-web-payments-sdk'
|
|
7
7
|
|
|
8
8
|
import { ApplyTypography, Button } from '@hanzo/ui/primitives'
|
|
9
|
+
import { cn } from '@hanzo/ui/util'
|
|
9
10
|
|
|
10
11
|
import { processSquareCardPayment } from '../../../util'
|
|
11
12
|
import { useCommerce } from '../../../service/context'
|
|
@@ -133,7 +134,7 @@ const PayWithCard: React.FC<{
|
|
|
133
134
|
*/
|
|
134
135
|
locationId={process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID}
|
|
135
136
|
>
|
|
136
|
-
<ApplyTypography className='flex flex-col
|
|
137
|
+
<ApplyTypography className='flex flex-col mt-6'>
|
|
137
138
|
{transactionStatus === 'paid' ? (
|
|
138
139
|
<h6 className='mx-auto font-nav'>Processing your payment...</h6>
|
|
139
140
|
) : transactionStatus === 'confirmed' ? (
|
|
@@ -147,13 +148,11 @@ const PayWithCard: React.FC<{
|
|
|
147
148
|
<GooglePay/>
|
|
148
149
|
<ApplePay/>
|
|
149
150
|
|
|
150
|
-
<div className='flex gap-2 whitespace-nowrap items-center my-
|
|
151
|
+
<div className='flex gap-2 whitespace-nowrap items-center my-1 sm:my-2 text-xs text-foreground/60'>
|
|
151
152
|
<hr className='bg-foreground/60 w-full border'/> or pay with card <hr className='bg-foreground/60 w-full border'/>
|
|
152
153
|
</div>
|
|
153
154
|
|
|
154
155
|
<ContactInfo form={contactForm}/>
|
|
155
|
-
|
|
156
|
-
<PaymentMethods/>
|
|
157
156
|
|
|
158
157
|
{/* Imitates hanzo/ui Button and Input styles, I was unable to render the
|
|
159
158
|
hanzo/ui button outright and keeping the submit form functionality*/}
|
|
@@ -184,6 +183,8 @@ const PayWithCard: React.FC<{
|
|
|
184
183
|
input: {
|
|
185
184
|
backgroundColor: '#1f1f1f',
|
|
186
185
|
color: '#FFFFFF',
|
|
186
|
+
fontSize: '15px',
|
|
187
|
+
fontFamily: 'helvetica neue, sans-serif',
|
|
187
188
|
},
|
|
188
189
|
'input::placeholder': {
|
|
189
190
|
color: '#999999',
|
|
@@ -192,18 +193,22 @@ const PayWithCard: React.FC<{
|
|
|
192
193
|
color: '#ff1600',
|
|
193
194
|
},
|
|
194
195
|
}}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
render={(Button: any) => (<>
|
|
197
|
+
<PaymentMethods/>
|
|
198
|
+
<Button className={cn(
|
|
199
|
+
'items-center justify-center font-medium transition-colors focus-visible:outline-none focus-visible:ring-2',
|
|
200
|
+
'focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
|
|
201
|
+
'!bg-primary !text-primary-fg hover:!bg-primary-hover font-nav whitespace-nowrap not-typography h-10 py-2 px-4',
|
|
202
|
+
'!text-sm rounded-md lg:min-w-[220px] sm:min-w-[220px] flex'
|
|
203
|
+
)}
|
|
204
|
+
>
|
|
205
|
+
Pay
|
|
206
|
+
</Button>
|
|
207
|
+
</>)}
|
|
208
|
+
>
|
|
209
|
+
|
|
210
|
+
</CreditCard>
|
|
211
|
+
|
|
207
212
|
{transactionStatus === 'error' && (
|
|
208
213
|
<p className='mx-auto text-destructive'>There was an error processing your payment.</p>
|
|
209
214
|
)}
|
|
@@ -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
|
|
@@ -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>
|
|
@@ -8,7 +8,7 @@ import Jcb from './jcb'
|
|
|
8
8
|
|
|
9
9
|
const PaymentMethods: React.FC = () => {
|
|
10
10
|
return (
|
|
11
|
-
<div className='flex gap-1 items-center'>
|
|
11
|
+
<div className='flex gap-1 items-center text-muted-1'>
|
|
12
12
|
<LockKeyhole className='w-4 h-4'/>
|
|
13
13
|
<span className='hidden sm:flex text-sm'>Secure payments with</span>
|
|
14
14
|
<Amex className='w-9 h-5'/>
|
|
@@ -84,9 +84,8 @@ const ShippingInfo: React.FC<{
|
|
|
84
84
|
name='addressLine1'
|
|
85
85
|
render={({ field }) => (
|
|
86
86
|
<FormItem className='space-y-1 w-full'>
|
|
87
|
-
<FormLabel>Address line 1</FormLabel>
|
|
88
87
|
<FormControl>
|
|
89
|
-
<Input {...field} className='border-muted-4'/>
|
|
88
|
+
<Input {...field} className='border-muted-4' placeholder='Address line 1'/>
|
|
90
89
|
</FormControl>
|
|
91
90
|
<FormMessage />
|
|
92
91
|
</FormItem>
|
|
@@ -97,9 +96,8 @@ const ShippingInfo: React.FC<{
|
|
|
97
96
|
name='addressLine2'
|
|
98
97
|
render={({ field }) => (
|
|
99
98
|
<FormItem className='space-y-1 w-full'>
|
|
100
|
-
<FormLabel>Address line 2 (optional)</FormLabel>
|
|
101
99
|
<FormControl>
|
|
102
|
-
<Input {...field} className='border-muted-4'/>
|
|
100
|
+
<Input {...field} className='border-muted-4' placeholder='Address line 2 (optional)'/>
|
|
103
101
|
</FormControl>
|
|
104
102
|
<FormMessage />
|
|
105
103
|
</FormItem>
|
|
@@ -111,9 +109,8 @@ const ShippingInfo: React.FC<{
|
|
|
111
109
|
name='zipCode'
|
|
112
110
|
render={({ field }) => (
|
|
113
111
|
<FormItem className='space-y-1 w-full'>
|
|
114
|
-
<FormLabel>Zip code</FormLabel>
|
|
115
112
|
<FormControl>
|
|
116
|
-
<Input {...field} className='border-muted-4'/>
|
|
113
|
+
<Input {...field} className='border-muted-4' placeholder='Zip code'/>
|
|
117
114
|
</FormControl>
|
|
118
115
|
<FormMessage />
|
|
119
116
|
</FormItem>
|
|
@@ -124,9 +121,8 @@ const ShippingInfo: React.FC<{
|
|
|
124
121
|
name='city'
|
|
125
122
|
render={({ field }) => (
|
|
126
123
|
<FormItem className='space-y-1 w-full'>
|
|
127
|
-
<FormLabel>City</FormLabel>
|
|
128
124
|
<FormControl>
|
|
129
|
-
<Input {...field} className='border-muted-4'/>
|
|
125
|
+
<Input {...field} className='border-muted-4' placeholder='City'/>
|
|
130
126
|
</FormControl>
|
|
131
127
|
<FormMessage />
|
|
132
128
|
</FormItem>
|
|
@@ -139,9 +135,8 @@ const ShippingInfo: React.FC<{
|
|
|
139
135
|
name='state'
|
|
140
136
|
render={({ field }) => (
|
|
141
137
|
<FormItem className='space-y-1 w-full'>
|
|
142
|
-
<FormLabel>State (Optional)</FormLabel>
|
|
143
138
|
<FormControl>
|
|
144
|
-
<Input {...field} className='border-muted-4'/>
|
|
139
|
+
<Input {...field} className='border-muted-4' placeholder='State (optional)'/>
|
|
145
140
|
</FormControl>
|
|
146
141
|
<FormMessage />
|
|
147
142
|
</FormItem>
|
|
@@ -152,11 +147,10 @@ const ShippingInfo: React.FC<{
|
|
|
152
147
|
name='country'
|
|
153
148
|
render={({ field }) => (
|
|
154
149
|
<FormItem className='space-y-1 w-full'>
|
|
155
|
-
<FormLabel>Country</FormLabel>
|
|
156
150
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
157
151
|
<FormControl>
|
|
158
|
-
<SelectTrigger>
|
|
159
|
-
<SelectValue placeholder='
|
|
152
|
+
<SelectTrigger className='bg-level-1 border-muted-4'>
|
|
153
|
+
<SelectValue placeholder='Country' />
|
|
160
154
|
</SelectTrigger>
|
|
161
155
|
</FormControl>
|
|
162
156
|
<SelectContent>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/commerce",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.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.
|
|
41
|
-
"@hanzo/ui": "^3.0.
|
|
42
|
+
"@hanzo/auth": "^2.3.1",
|
|
43
|
+
"@hanzo/ui": "^3.0.9",
|
|
42
44
|
"@hookform/resolvers": "^3.3.4",
|
|
43
45
|
"firebase": "^10.8.0",
|
|
44
46
|
"lucide-react": "^0.307.0",
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
observable,
|
|
6
6
|
} from 'mobx'
|
|
7
7
|
|
|
8
|
+
import type { VideoDef } from '@hanzo/ui/types'
|
|
9
|
+
|
|
8
10
|
import type { Product, LineItem } from '../../../types'
|
|
9
11
|
|
|
10
12
|
interface ActualLineItemSnapshot {
|
|
@@ -30,6 +32,8 @@ class ActualLineItem
|
|
|
30
32
|
desc?: string
|
|
31
33
|
price: number
|
|
32
34
|
img?: string
|
|
35
|
+
video?: VideoDef
|
|
36
|
+
animation?: string
|
|
33
37
|
timeAdded: number = 0 // timeAdded of being added to cart
|
|
34
38
|
|
|
35
39
|
constructor(prod: Product, snap?: ActualLineItemSnapshot) {
|
|
@@ -41,10 +45,12 @@ class ActualLineItem
|
|
|
41
45
|
this.desc = prod.desc
|
|
42
46
|
this.price = prod.price
|
|
43
47
|
this.img = prod.img
|
|
48
|
+
this.video = prod.video
|
|
49
|
+
this.animation = prod.animation
|
|
44
50
|
|
|
45
51
|
if (snap) {
|
|
46
52
|
this.qu = snap.quantity
|
|
47
|
-
this.timeAdded = snap.
|
|
53
|
+
this.timeAdded = snap.timeAdded
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
makeObservable(this, {
|
package/types/product.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { VideoDef } from '@hanzo/ui/types'
|
|
2
|
+
|
|
1
3
|
interface Product {
|
|
2
4
|
id: string // DB index // not a logical aspect of our domain. may not be necessary at all
|
|
3
5
|
sku: string // human visible on orders etc.
|
|
@@ -8,6 +10,8 @@ interface Product {
|
|
|
8
10
|
desc?: string
|
|
9
11
|
price: number
|
|
10
12
|
img?: string // if undefined: (category's img exists) ? (use it) : (use generic placeholder)
|
|
13
|
+
animation?: string // spline scene url
|
|
14
|
+
video?: VideoDef
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
export {
|