@hanzo/commerce 5.1.3 → 6.1.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/{buy-item/select-category-item-card.tsx → buy/_to_deprecate_select-category-item-card.tsx} +22 -32
- package/components/{select-category-item-panel.tsx → buy/_to_deprecate_select-category-item-panel.tsx} +16 -22
- package/components/{buy-item/buy-item-popup.tsx → buy/_unused_buy-popup.tsx_} +4 -4
- package/components/{add-to-cart-widget.tsx → buy/add-to-cart-widget.tsx} +10 -6
- package/components/buy/buy-button.tsx +31 -0
- package/components/buy/buy-card.tsx +267 -0
- package/components/buy/buy-drawer.tsx +89 -0
- package/components/buy/buy-trigger-wrapper.tsx +20 -0
- package/components/{cart-accordian.tsx → cart/cart-accordian.tsx} +2 -1
- package/components/{cart-panel → cart/cart-panel}/cart-line-item.tsx +27 -14
- package/components/{cart-panel → cart/cart-panel}/index.tsx +12 -3
- package/components/{cart-panel → cart/cart-panel}/promo-code.tsx +3 -3
- package/components/{payment-step-form → checkout/payment-step-form}/contact-form.tsx +1 -1
- package/components/{payment-step-form → checkout/payment-step-form}/index.tsx +3 -3
- package/components/{payment-step-form → checkout/payment-step-form}/methods/bank-transfer.tsx +1 -1
- package/components/{payment-step-form → checkout/payment-step-form}/methods/card.tsx +4 -4
- package/components/{payment-step-form → checkout/payment-step-form}/methods/crypto.tsx +3 -3
- package/components/{payment-step-form → checkout/payment-step-form}/methods/index.ts +1 -1
- package/components/{shipping-step-form.tsx → checkout/shipping-step-form.tsx} +4 -4
- package/components/index.ts +20 -18
- package/components/item/item-media.tsx +51 -0
- package/components/item/product-card.tsx +48 -0
- package/components/{facet-values-widget → level-nodes-widget}/index.tsx +20 -19
- package/components/level-nodes-widget/node-image.tsx +31 -0
- package/components/select/carousel-selector.tsx +112 -0
- package/components/select/image-selector.tsx +162 -0
- package/components/select/radio-selector.tsx +125 -0
- package/package.json +3 -2
- package/service/context.tsx +4 -4
- package/service/impls/standalone/actual-line-item.ts +30 -16
- package/service/impls/standalone/index.ts +4 -4
- package/service/impls/standalone/standalone-service.ts +94 -64
- package/types/buy-ui-spec.ts +8 -0
- package/types/category.ts +6 -4
- package/types/commerce-service.ts +21 -16
- package/types/index.ts +3 -2
- package/types/item-selector.ts +34 -6
- package/types/line-item.ts +2 -0
- package/types/product.ts +5 -5
- package/types/tree-node.ts +24 -0
- package/util/buy-ui-conf.ts +34 -0
- package/util/index.ts +14 -11
- package/util/obs-string-mutator.ts +22 -0
- package/util/use-sync-sku-param-w-current-item.ts +5 -5
- package/components/buy-item/buy-item-button-wrapper.tsx +0 -27
- package/components/buy-item/buy-item-button.tsx +0 -34
- package/components/buy-item/buy-item-card.tsx +0 -92
- package/components/buy-item/buy-item-mobile-drawer.tsx +0 -54
- package/components/buy-item/select-category-and-item-widget.tsx +0 -83
- package/components/category-item-radio-selector.tsx +0 -55
- package/components/category-item-scroll-selector.tsx +0 -40
- package/components/facet-values-widget/facet-image.tsx +0 -41
- package/components/item-carousel.tsx +0 -80
- package/components/product-card.tsx +0 -79
- package/types/facet.ts +0 -35
- /package/components/{cart-icon.tsx → cart/cart-icon.tsx} +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icon-row.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/amex.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/diners-club.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/discover.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/jcb.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/mastercard.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/card-icons/visa.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/cc-button.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/btc.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/eth.tsx +0 -0
- /package/components/{payment-step-form → checkout/payment-step-form}/crypto-icons/usdt.tsx +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
import { makeObservable, observable, action } from 'mobx'
|
|
3
|
+
import type { StringMutator } from '../types'
|
|
4
|
+
|
|
5
|
+
class ObsStringMutator implements StringMutator {
|
|
6
|
+
|
|
7
|
+
s: string | null
|
|
8
|
+
constructor(_s: string) {
|
|
9
|
+
this.s = _s
|
|
10
|
+
makeObservable(this, {
|
|
11
|
+
s: observable,
|
|
12
|
+
set: action,
|
|
13
|
+
//get: computed
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
set(v: string | null): void { this.s = v }
|
|
18
|
+
get(): string | null { return this.s }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default ObsStringMutator
|
|
22
|
+
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from 'next-usequerystate'
|
|
10
10
|
|
|
11
11
|
import { useCommerce } from '../service/context'
|
|
12
|
-
import type {
|
|
12
|
+
import type { SelectedPaths } from '../types'
|
|
13
13
|
|
|
14
14
|
const PLEASE_SELECT_FACETS = 'Please select an option from each group.'
|
|
15
15
|
|
|
@@ -32,7 +32,7 @@ const useSyncSkuParamWithCurrentItem = (
|
|
|
32
32
|
useEffect(() => {
|
|
33
33
|
|
|
34
34
|
return reaction(() => ({
|
|
35
|
-
specifiedCat: cmmc.
|
|
35
|
+
specifiedCat: cmmc.selectedCategories.length === 1 ? cmmc.selectedCategories[0] : undefined,
|
|
36
36
|
currentItem: cmmc.currentItem
|
|
37
37
|
}),
|
|
38
38
|
({specifiedCat, currentItem}) => {
|
|
@@ -55,14 +55,14 @@ const useSyncSkuParamWithCurrentItem = (
|
|
|
55
55
|
|
|
56
56
|
const setCurrentCategoryFromSku = (sku: string) => {
|
|
57
57
|
const toks: string[] = sku.split('-')
|
|
58
|
-
const fv:
|
|
59
|
-
// TODO: confirm that extra trailing nonsense tokens won't break
|
|
58
|
+
const fv: SelectedPaths = {}
|
|
59
|
+
// TODO: confirm that extra trailing nonsense tokens won't break selectPaths()
|
|
60
60
|
for (let i = 1; i < categoryLevel; i++) {
|
|
61
61
|
if (i in toks) {
|
|
62
62
|
fv[i] = [toks[i]]
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
cmmc.
|
|
65
|
+
cmmc.selectPaths(fv)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// setCI returns true if it's a recognized sku
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React, {type PropsWithChildren} from 'react'
|
|
3
|
-
|
|
4
|
-
import BuyItemPopup from './buy-item-popup'
|
|
5
|
-
import BuyItemMobileDrawer from './buy-item-mobile-drawer'
|
|
6
|
-
|
|
7
|
-
const BuyItemButtonWrapper: React.FC<{
|
|
8
|
-
skuPath: string
|
|
9
|
-
desktopTrigger: React.ReactNode
|
|
10
|
-
mobileTrigger: React.ReactNode
|
|
11
|
-
popupClx?: string
|
|
12
|
-
}> = ({
|
|
13
|
-
skuPath,
|
|
14
|
-
desktopTrigger,
|
|
15
|
-
mobileTrigger,
|
|
16
|
-
popupClx=''
|
|
17
|
-
}) => (<>
|
|
18
|
-
<BuyItemPopup skuPath={skuPath} popupClx={popupClx}>
|
|
19
|
-
{desktopTrigger}
|
|
20
|
-
</BuyItemPopup>
|
|
21
|
-
<BuyItemMobileDrawer skuPath={skuPath} trigger={mobileTrigger} />
|
|
22
|
-
</>
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export default BuyItemButtonWrapper
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React, {type PropsWithChildren} from 'react'
|
|
3
|
-
|
|
4
|
-
import { type ButtonVariants, type ButtonSizes, Button } from '@hanzo/ui/primitives'
|
|
5
|
-
|
|
6
|
-
import BuyItemPopup from './buy-item-popup'
|
|
7
|
-
import BuyItemMobileDrawer from './buy-item-mobile-drawer'
|
|
8
|
-
import { cn } from '@hanzo/ui/util'
|
|
9
|
-
|
|
10
|
-
const BuyItemButton: React.FC<PropsWithChildren & {
|
|
11
|
-
skuPath: string
|
|
12
|
-
variant? : ButtonVariants
|
|
13
|
-
size?: ButtonSizes
|
|
14
|
-
/* rounded?: ButtonRounded // wait for version bump*/
|
|
15
|
-
className?: string
|
|
16
|
-
popupClx?: string
|
|
17
|
-
}> = ({
|
|
18
|
-
skuPath,
|
|
19
|
-
variant,
|
|
20
|
-
size,
|
|
21
|
-
children,
|
|
22
|
-
className='',
|
|
23
|
-
popupClx=''
|
|
24
|
-
}) => (<>
|
|
25
|
-
<BuyItemPopup skuPath={skuPath} popupClx={popupClx}>
|
|
26
|
-
<Button size={size} variant={variant} className={cn(className, 'hidden md:flex')}>{children}</Button>
|
|
27
|
-
</BuyItemPopup>
|
|
28
|
-
<BuyItemMobileDrawer skuPath={skuPath} trigger={<Button size={size} variant={variant} className={cn(className, 'md:hidden')}>{children}</Button>} />
|
|
29
|
-
</>
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export default BuyItemButton
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React, { useRef, useEffect } from 'react'
|
|
3
|
-
import { autorun } from 'mobx'
|
|
4
|
-
import { observer } from 'mobx-react-lite'
|
|
5
|
-
|
|
6
|
-
import { cn } from '@hanzo/ui/util'
|
|
7
|
-
|
|
8
|
-
import type { FacetsValue } from '../../types'
|
|
9
|
-
import { useCommerce } from '../../service/context'
|
|
10
|
-
import { getFacetValuesMutator } from '../../util'
|
|
11
|
-
import FacetValuesWidget from '../facet-values-widget'
|
|
12
|
-
import SelectCategoryItemCard from './select-category-item-card'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const BuyItemCard: React.FC<{
|
|
16
|
-
skuPath: string
|
|
17
|
-
mobile?: boolean
|
|
18
|
-
className?: string
|
|
19
|
-
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
|
|
20
|
-
}> = observer(({
|
|
21
|
-
skuPath,
|
|
22
|
-
mobile=false,
|
|
23
|
-
className='',
|
|
24
|
-
onQuantityChanged
|
|
25
|
-
}) => {
|
|
26
|
-
|
|
27
|
-
const cmmc = useCommerce()
|
|
28
|
-
const levelRef = useRef<number>(-1)
|
|
29
|
-
|
|
30
|
-
const cat = cmmc.getCategory(skuPath)
|
|
31
|
-
const facets = cat ? undefined : cmmc.getFacetValuesAtSkuPath(skuPath)
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
|
|
35
|
-
const toks = skuPath.split('-')
|
|
36
|
-
levelRef.current = toks.length - 1
|
|
37
|
-
const fsv: FacetsValue = {}
|
|
38
|
-
for (let level = 1; level <= levelRef.current; level++ ) {
|
|
39
|
-
fsv[level] = [toks[level]]
|
|
40
|
-
}
|
|
41
|
-
if (facets) {
|
|
42
|
-
fsv[levelRef.current + 1] = [facets[0].value]
|
|
43
|
-
}
|
|
44
|
-
cmmc.setFacets(fsv)
|
|
45
|
-
|
|
46
|
-
return autorun(() => {
|
|
47
|
-
const cats = cmmc.specifiedCategories
|
|
48
|
-
// Original cat was legit
|
|
49
|
-
if (cat && (cats.length === 0 || cats[0].id !== cat.id)) {
|
|
50
|
-
if (!cmmc.currentItem || cmmc.currentItem.categoryId !== cat.id) {
|
|
51
|
-
cmmc.setCurrentItem(cat.products[0].sku)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
else if (cats.length > 0) {
|
|
55
|
-
if (!cmmc.currentItem || cmmc.currentItem.categoryId !== cats[0].id) {
|
|
56
|
-
cmmc.setCurrentItem(cats[0].products[0].sku)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
}, [cat, facets])
|
|
61
|
-
|
|
62
|
-
const renderFacetTabs = facets && levelRef.current > 0
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<div className={className} >
|
|
66
|
-
{renderFacetTabs && (
|
|
67
|
-
<FacetValuesWidget
|
|
68
|
-
className={cn('grid gap-0 ' + `grid-cols-${facets.length}` + ' self-start ', 'border-b-2 border-level-3 mb-2 -mr-2 -ml-2')}
|
|
69
|
-
isMobile={false}
|
|
70
|
-
mutator={getFacetValuesMutator(levelRef.current + 1, cmmc)}
|
|
71
|
-
itemClx='flex-col h-auto gap-0 pb-1 pt-3 px-3'
|
|
72
|
-
buttonClx={'h-full !rounded-bl-none !rounded-br-none !rounded-tl-lg !rounded-tr-lg ' +
|
|
73
|
-
'!border-r !border-t !border-level-3'}
|
|
74
|
-
facetValues={facets}
|
|
75
|
-
/>
|
|
76
|
-
)}
|
|
77
|
-
{cmmc.specifiedCategories[0] && (
|
|
78
|
-
<SelectCategoryItemCard
|
|
79
|
-
noTitle
|
|
80
|
-
mobile={mobile}
|
|
81
|
-
category={cmmc.specifiedCategories[0]}
|
|
82
|
-
selectedItemRef={cmmc /* ...conveniently. :) */ }
|
|
83
|
-
selectSku={cmmc.setCurrentItem.bind(cmmc)}
|
|
84
|
-
className=''
|
|
85
|
-
onQuantityChanged={onQuantityChanged}
|
|
86
|
-
/>
|
|
87
|
-
)}
|
|
88
|
-
</div >
|
|
89
|
-
)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
export default BuyItemCard
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React, { useState, type ReactNode } from 'react'
|
|
3
|
-
|
|
4
|
-
//import { X as LucideX} from 'lucide-react'
|
|
5
|
-
//import { Sheet, SheetContent, SheetTrigger } from '@hanzo/ui/primitives'
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
Drawer,
|
|
9
|
-
DrawerTrigger,
|
|
10
|
-
DrawerContent,
|
|
11
|
-
} from '@hanzo/ui/primitives'
|
|
12
|
-
|
|
13
|
-
import { cn } from '@hanzo/ui/util'
|
|
14
|
-
|
|
15
|
-
import BuyItemCard from './buy-item-card'
|
|
16
|
-
|
|
17
|
-
const BuyItemMobileDrawer: React.FC<{
|
|
18
|
-
skuPath: string
|
|
19
|
-
trigger: ReactNode
|
|
20
|
-
triggerClx?: string
|
|
21
|
-
drawerClx?: string
|
|
22
|
-
cardClx?: string
|
|
23
|
-
}> = ({
|
|
24
|
-
skuPath,
|
|
25
|
-
trigger,
|
|
26
|
-
triggerClx='',
|
|
27
|
-
drawerClx='',
|
|
28
|
-
cardClx=''
|
|
29
|
-
}) => {
|
|
30
|
-
|
|
31
|
-
const [open, setOpen] = useState<boolean>(false)
|
|
32
|
-
|
|
33
|
-
const onQuantityChanged = (sku: string, oldV: number, newV: number) => {
|
|
34
|
-
if (oldV === 0 && newV === 1) {
|
|
35
|
-
setTimeout(() => {setOpen(false)}, 150)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<Drawer open={open} onOpenChange={setOpen} >
|
|
41
|
-
<DrawerTrigger asChild className={triggerClx}>
|
|
42
|
-
{trigger}
|
|
43
|
-
</DrawerTrigger>
|
|
44
|
-
<DrawerContent
|
|
45
|
-
className={cn('rounded-tl-xl rounded-tr-xl p-0 overflow-hidden', drawerClx)}
|
|
46
|
-
// side="bottom"
|
|
47
|
-
>
|
|
48
|
-
<BuyItemCard skuPath={skuPath} mobile onQuantityChanged={onQuantityChanged} className={cn("w-full relative ", cardClx)}/>
|
|
49
|
-
</DrawerContent>
|
|
50
|
-
</Drawer>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export default BuyItemMobileDrawer
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React, { useEffect } from 'react'
|
|
3
|
-
import { observer } from 'mobx-react-lite'
|
|
4
|
-
|
|
5
|
-
import { ApplyTypography, ListBox } from '@hanzo/ui/primitives'
|
|
6
|
-
import { cn } from '@hanzo/ui/util'
|
|
7
|
-
|
|
8
|
-
import type { FacetValueDesc, FacetsValue, LineItem } from '../../types'
|
|
9
|
-
import { useCommerce } from '../../service/context'
|
|
10
|
-
import { formatCurrencyValue } from '../../util'
|
|
11
|
-
|
|
12
|
-
import FacetTogglesWidget from '../facet-values-widget'
|
|
13
|
-
|
|
14
|
-
const formatItem = (item: LineItem, withQuantity: boolean = false): string => (
|
|
15
|
-
`${item.titleAsOption}, ${formatCurrencyValue(item.price)}${(withQuantity && item.quantity > 0) ? ` (${item.quantity})` : ''}`
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
const SelectCategoryAndItemWidget: React.FC<{
|
|
19
|
-
categoryLevel: number
|
|
20
|
-
parentLevelToken: string
|
|
21
|
-
categoryLevelValues: FacetValueDesc[]
|
|
22
|
-
className?: string
|
|
23
|
-
}> = observer(({
|
|
24
|
-
categoryLevel,
|
|
25
|
-
parentLevelToken,
|
|
26
|
-
categoryLevelValues,
|
|
27
|
-
className=''
|
|
28
|
-
}) => {
|
|
29
|
-
const comm = useCommerce()
|
|
30
|
-
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
const facets: FacetsValue = {}
|
|
33
|
-
facets[categoryLevel - 1] = [parentLevelToken]
|
|
34
|
-
facets[categoryLevel] = [categoryLevelValues[0].value]
|
|
35
|
-
comm.setFacets(facets)
|
|
36
|
-
comm.setCurrentItem(comm.specifiedCategories[0].products[0].sku)
|
|
37
|
-
}, [])
|
|
38
|
-
|
|
39
|
-
const onFacetTokenChanged = (token: string): void => {
|
|
40
|
-
const facets: FacetsValue = {}
|
|
41
|
-
facets[categoryLevel - 1] = [parentLevelToken]
|
|
42
|
-
facets[categoryLevel] = [token]
|
|
43
|
-
comm.setFacets(facets)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const currentFacetToken = (): string | null => {
|
|
47
|
-
if (comm.specifiedCategories.length === 0) return null
|
|
48
|
-
const skuPath = comm.specifiedCategories[0].id
|
|
49
|
-
const skuPathTokens = skuPath.split('-')
|
|
50
|
-
return skuPathTokens.length > 0 ? skuPathTokens[skuPathTokens.length - 1] : null
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div className={cn('flex flex-col justify-start gap-4 items-start pt-3', className)}>
|
|
55
|
-
<FacetTogglesWidget
|
|
56
|
-
facetValues={categoryLevelValues}
|
|
57
|
-
mutator={{
|
|
58
|
-
get: currentFacetToken, // computed's are accessed through their get()
|
|
59
|
-
set: onFacetTokenChanged
|
|
60
|
-
}}
|
|
61
|
-
/>
|
|
62
|
-
{comm.specifiedItems.length === 0 ? (
|
|
63
|
-
<ApplyTypography>
|
|
64
|
-
<h3>No Items</h3>
|
|
65
|
-
</ApplyTypography>
|
|
66
|
-
) : comm.specifiedItems.length === 1 ? (
|
|
67
|
-
<ApplyTypography>
|
|
68
|
-
<h4>{formatItem(comm.specifiedItems[0])}</h4>
|
|
69
|
-
</ApplyTypography>
|
|
70
|
-
) : (
|
|
71
|
-
<ListBox<string>
|
|
72
|
-
values={comm.specifiedItems.map((it) => (it.sku))}
|
|
73
|
-
labels={comm.specifiedItems.map((it) => (formatItem(it)))}
|
|
74
|
-
isEqual={(v1: string, v2: string) => (v1 === v2)}
|
|
75
|
-
value={comm.currentItem?.sku}
|
|
76
|
-
onValueChange={comm.setCurrentItem.bind(comm)}
|
|
77
|
-
/>
|
|
78
|
-
)}
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
export default SelectCategoryAndItemWidget
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { observer } from 'mobx-react-lite'
|
|
4
|
-
|
|
5
|
-
import { Label, RadioGroup, RadioGroupItem } from '@hanzo/ui/primitives'
|
|
6
|
-
import type { ItemSelector, LineItem, Product } from '../types'
|
|
7
|
-
import { formatCurrencyValue } from '../util'
|
|
8
|
-
import { cn } from '@hanzo/ui/util'
|
|
9
|
-
|
|
10
|
-
const CategoryItemRadioSelector: React.FC<ItemSelector & {
|
|
11
|
-
groupClx?: string
|
|
12
|
-
itemClx?: string
|
|
13
|
-
}> = observer(({
|
|
14
|
-
category,
|
|
15
|
-
selectedItemRef: itemRef,
|
|
16
|
-
selectSku,
|
|
17
|
-
groupClx='',
|
|
18
|
-
itemClx='',
|
|
19
|
-
showPrice=true,
|
|
20
|
-
showQuantity=true
|
|
21
|
-
}) => {
|
|
22
|
-
|
|
23
|
-
const Choice: React.FC<{
|
|
24
|
-
item: Product
|
|
25
|
-
className?: string
|
|
26
|
-
}> = ({
|
|
27
|
-
item,
|
|
28
|
-
className=''
|
|
29
|
-
}) => (
|
|
30
|
-
<div className={cn(className, itemClx)}>
|
|
31
|
-
<RadioGroupItem value={item.sku} id={item.sku} />
|
|
32
|
-
<Label htmlFor={item.sku}>{item.titleAsOption + (showPrice ? (', ' + formatCurrencyValue(item.price)) : '')}</Label>
|
|
33
|
-
</div>
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
return ( category.products.length > 1 ? (
|
|
37
|
-
<RadioGroup
|
|
38
|
-
className={cn(showQuantity ? 'table' : '', groupClx)}
|
|
39
|
-
onValueChange={selectSku}
|
|
40
|
-
value={itemRef.item ? itemRef.item.sku : ''}
|
|
41
|
-
>
|
|
42
|
-
{(category.products as LineItem[]).map((item) => (showQuantity ? (
|
|
43
|
-
<div className='table-row' key={item.sku}>
|
|
44
|
-
<Choice item={item} className='table-cell pr-2 align-text-top pb-2'/>
|
|
45
|
-
<div className='table-cell font-semibold text-sm leading-none align-text-top pb-2'>{ item.quantity > 0 ? `(${item.quantity})` : ' '}</div>
|
|
46
|
-
</div>
|
|
47
|
-
) : (
|
|
48
|
-
<Choice item={item} className='mb-2' key={item.sku}/>
|
|
49
|
-
)))}
|
|
50
|
-
</RadioGroup>
|
|
51
|
-
) : (<p>TODO</p>)
|
|
52
|
-
)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
export default CategoryItemRadioSelector
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { observer } from 'mobx-react-lite'
|
|
4
|
-
|
|
5
|
-
import { cn } from '@hanzo/ui/util'
|
|
6
|
-
import { ScrollArea } from '@hanzo/ui/primitives'
|
|
7
|
-
|
|
8
|
-
import type { ItemSelector } from '../types'
|
|
9
|
-
import { formatCurrencyValue } from '../util'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const CategoryItemScrollSelector: React.FC<ItemSelector & {
|
|
13
|
-
outerClx?: string
|
|
14
|
-
itemClx?: string
|
|
15
|
-
}> = observer(({
|
|
16
|
-
category,
|
|
17
|
-
selectedItemRef: iRef,
|
|
18
|
-
selectSku,
|
|
19
|
-
outerClx='',
|
|
20
|
-
itemClx=''
|
|
21
|
-
}) => {
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<ScrollArea className={cn('border border-muted-4 rounded-lg', outerClx)}>
|
|
25
|
-
{category.products.map((prod) => (
|
|
26
|
-
<div key={prod.sku} onClick={() => {selectSku(prod.sku)}}>
|
|
27
|
-
<div className={cn(
|
|
28
|
-
'h-10 border-b flex flex-col justify-center px-4',
|
|
29
|
-
(iRef.item?.sku === prod.sku) ? 'font-semibold text-accent' : 'text-muted',
|
|
30
|
-
itemClx
|
|
31
|
-
)}>
|
|
32
|
-
{prod.titleAsOption + ', ' + formatCurrencyValue(prod.price)}
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
))}
|
|
36
|
-
</ScrollArea>
|
|
37
|
-
)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
export default CategoryItemScrollSelector
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import Image from 'next/image'
|
|
3
|
-
|
|
4
|
-
import type { FacetValueDesc } from '../../types'
|
|
5
|
-
|
|
6
|
-
const ICON_SIZE = 20
|
|
7
|
-
|
|
8
|
-
const FacetImage: React.FC<{
|
|
9
|
-
facetValueDesc: FacetValueDesc
|
|
10
|
-
}> = ({
|
|
11
|
-
facetValueDesc
|
|
12
|
-
}) => {
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
img,
|
|
16
|
-
imgAR: ar,
|
|
17
|
-
label
|
|
18
|
-
} = facetValueDesc
|
|
19
|
-
|
|
20
|
-
if (!img) {
|
|
21
|
-
return null
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// url
|
|
25
|
-
if (typeof img === 'string') {
|
|
26
|
-
return (
|
|
27
|
-
<Image
|
|
28
|
-
src={img as string}
|
|
29
|
-
alt={`Toggle ${label}`}
|
|
30
|
-
className={'block mr-1 '}
|
|
31
|
-
width={ar ? ar * ICON_SIZE : ICON_SIZE}
|
|
32
|
-
height={ICON_SIZE}
|
|
33
|
-
/>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
// ReactNode
|
|
37
|
-
return img as React.ReactNode
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default FacetImage
|
|
41
|
-
|
|
@@ -1,80 +0,0 @@
|
|
|
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,79 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import Image from 'next/image'
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
AspectRatio,
|
|
6
|
-
Card,
|
|
7
|
-
CardContent,
|
|
8
|
-
CardDescription,
|
|
9
|
-
CardFooter,
|
|
10
|
-
CardHeader,
|
|
11
|
-
CardTitle,
|
|
12
|
-
} from '@hanzo/ui/primitives'
|
|
13
|
-
import type { ImageDef } from '@hanzo/ui/types'
|
|
14
|
-
import { cn } from '@hanzo/ui/util'
|
|
15
|
-
|
|
16
|
-
import { formatCurrencyValue } from '../util'
|
|
17
|
-
import type { LineItem } from '../types'
|
|
18
|
-
import { Icons } from './Icons'
|
|
19
|
-
|
|
20
|
-
import AddToCartWidget from './add-to-cart-widget'
|
|
21
|
-
|
|
22
|
-
interface ProductCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
23
|
-
item: LineItem
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const ProductCard: React.FC<ProductCardProps> = ({
|
|
27
|
-
item,
|
|
28
|
-
className,
|
|
29
|
-
...props
|
|
30
|
-
}) => {
|
|
31
|
-
|
|
32
|
-
const ProductImage: React.FC<{className?: string}> = ({className=''}) => (
|
|
33
|
-
<Image
|
|
34
|
-
src={item.img!}
|
|
35
|
-
alt={item.title}
|
|
36
|
-
className={className}
|
|
37
|
-
loading='lazy'
|
|
38
|
-
// width={700}
|
|
39
|
-
// height={700}
|
|
40
|
-
fill
|
|
41
|
-
style={{
|
|
42
|
-
objectFit: 'contain',
|
|
43
|
-
}}
|
|
44
|
-
/>
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<Card
|
|
49
|
-
className={cn('max-h-[360px] lg:min-w-[200px] max-w-[260px] overflow-hidden', className)}
|
|
50
|
-
{...props}
|
|
51
|
-
>
|
|
52
|
-
<CardHeader className='w-full border-b p-6 min-h-[180px] max-h-[240px] relative'>
|
|
53
|
-
{item.img ? (
|
|
54
|
-
<ProductImage className='p-6' />
|
|
55
|
-
) : (
|
|
56
|
-
<div
|
|
57
|
-
aria-label='Placeholder'
|
|
58
|
-
role='img'
|
|
59
|
-
aria-roledescription='placeholder'
|
|
60
|
-
className='flex h-full items-center justify-center bg-background'
|
|
61
|
-
>
|
|
62
|
-
<Icons.barcode className='h-9 w-9 text-muted' aria-hidden='true' />
|
|
63
|
-
</div>
|
|
64
|
-
)}
|
|
65
|
-
</CardHeader>
|
|
66
|
-
<CardContent className='grid gap-2.5 p-4'>
|
|
67
|
-
<CardTitle className='text-sm sm:text-base flex flex-col justify-start items-center line-clap-3'>
|
|
68
|
-
{item.title.split(', ').map((e, i) => (<p key={i}>{e}</p>))}
|
|
69
|
-
<p className='mt-1 font-semibold'>{formatCurrencyValue(item.price)}</p>
|
|
70
|
-
</CardTitle>
|
|
71
|
-
</CardContent>
|
|
72
|
-
<CardFooter className='p-4 flex flex-row justify-center'>
|
|
73
|
-
<AddToCartWidget item={item}/>
|
|
74
|
-
</CardFooter>
|
|
75
|
-
</Card>
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export default ProductCard
|
package/types/facet.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react'
|
|
2
|
-
|
|
3
|
-
interface FacetValueDesc {
|
|
4
|
-
value: string // a token in the sku
|
|
5
|
-
label: string
|
|
6
|
-
img? : string | ReactNode // icon is required
|
|
7
|
-
imgAR? : number // helps with svgs
|
|
8
|
-
sub?: FacetValueDesc[]
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/* *** FOR EXAMPLE **
|
|
12
|
-
{
|
|
13
|
-
1: [ {
|
|
14
|
-
token: 'AG',
|
|
15
|
-
label: 'Silver',
|
|
16
|
-
img: '/assets/img/cart/ui/facets/silver-swatch-200x200.png'
|
|
17
|
-
},
|
|
18
|
-
... more FaceValues describing the "type" level (Silver, Gold)
|
|
19
|
-
],
|
|
20
|
-
2 [
|
|
21
|
-
{
|
|
22
|
-
token: 'B'
|
|
23
|
-
label: 'Minted Bar
|
|
24
|
-
},
|
|
25
|
-
... more FaceValues describing the "form" level (Bar, Coin, )
|
|
26
|
-
]
|
|
27
|
-
}
|
|
28
|
-
*/
|
|
29
|
-
// Which facets tokens are on at each level
|
|
30
|
-
type FacetsValue = Record<number, string[]>
|
|
31
|
-
|
|
32
|
-
export type {
|
|
33
|
-
FacetValueDesc,
|
|
34
|
-
FacetsValue
|
|
35
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|