Skip to content

Commit

Permalink
ui@3.0.4, auth@2.0.4, cmmc@4.0.0; Cleaner buy flow (#63)
Browse files Browse the repository at this point in the history
* working mobile select drawer and desktop select popup
* adding item to cart closes drawer / popup (but not changing quantity)
* version bumps
  • Loading branch information
artemis-prime authored Mar 18, 2024
1 parent 81073b3 commit cdf5d0a
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 51 deletions.
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hanzo/auth",
"version": "2.0.3",
"version": "2.0.4",
"description": "Library with Firebase authentication.",
"publishConfig": {
"registry": "https://registry.npmjs.org/",
Expand Down
27 changes: 22 additions & 5 deletions packages/commerce/components/add-to-cart-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ const AddToCartWidget: React.FC<{
className?: string
buttonClx?: string
isMobile?: boolean
wide?: boolean
size?: ButtonSizes
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
}> = observer(({
item,
ghost=false,
disabled=false,
className='',
buttonClx='',
size='xs'
size='xs',
onQuantityChanged
}) => {

const iconClx = ghost ? 'h-4 w-4 md:h-3 md:w-3 text-muted-3 hover:text-foreground' : 'h-5 w-7 px-1'
Expand All @@ -37,6 +38,22 @@ const AddToCartWidget: React.FC<{
)
}

const inc = () => {
const old = item.quantity
item.increment()
if (onQuantityChanged) {
onQuantityChanged(item.sku, old, old + 1)
}
}

const dec = () => {
const old = item.quantity
item.decrement()
if (onQuantityChanged) {
onQuantityChanged(item.sku, old, old - 1)
}
}

return ( item.isInCart ? (
<div className={cn('flex flex-row items-stretch justify-center ' + (ghost ? 'bg-transparent rounded-xl' : 'bg-secondary rounded-xl'), className)}>
<Button
Expand All @@ -46,7 +63,7 @@ const AddToCartWidget: React.FC<{
rounded={ghost ? 'full' : 'xl'}
className={cn('px-1 lg:min-w-0 lg:px-2 xs:justify-end', buttonClx)}
key='left'
onClick={item.decrement.bind(item)}
onClick={dec}
>
{(item.quantity > 1) ? (
<Icons.minus className={iconClx} aria-hidden='true'/>
Expand All @@ -61,7 +78,7 @@ const AddToCartWidget: React.FC<{
variant={ghost ? 'ghost' : 'secondary'}
rounded={ghost ? 'full' : 'xl'}
className={cn('px-1 lg:min-w-0 lg:px-2 xs:justify-start', buttonClx)}
onClick={item.increment.bind(item)}
onClick={inc}
key='right'
>
<Icons.plus className={iconClx} aria-hidden='true'/>
Expand All @@ -74,7 +91,7 @@ const AddToCartWidget: React.FC<{
variant='secondary'
rounded='xl'
className={cn(buttonClx, className)}
onClick={item.increment.bind(item)}
onClick={inc}
>
<Icons.plus className='h-5 w-5 mr-1' aria-hidden='true'/>
<span className='mr-1'>Add</span>
Expand Down
10 changes: 8 additions & 2 deletions packages/commerce/components/buy-item/buy-item-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import React, {type PropsWithChildren} from 'react'
import { type ButtonVariants, type ButtonSizes, Button } from '@hanzo/ui/primitives'

import BuyItemPopup from './buy-item-popup'
import BuyItemMobileDrawer from './buy-item-mobile-drawer'
import { cn } from '@hanzo/ui/util'

const BuyItemButton: React.FC<PropsWithChildren & {
skuPath: string
Expand All @@ -19,10 +21,14 @@ const BuyItemButton: React.FC<PropsWithChildren & {
children,
className='',
popupClx=''
}) => (
}) => (<>
<BuyItemPopup skuPath={skuPath} popupClx={popupClx}>
<Button size={size} variant={variant} className={className}>{children}</Button>
<Button size={size} variant={variant} className={cn(className, 'hidden md:flex')}>{children}</Button>
</BuyItemPopup>
<BuyItemMobileDrawer skuPath={skuPath} trigger={<Button size={size} variant={variant} className={cn(className, 'md:hidden')}>{children}</Button>} />
</>
)



export default BuyItemButton
36 changes: 23 additions & 13 deletions packages/commerce/components/buy-item/buy-item-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ import SelectCategoryItemCard from './select-category-item-card'

const BuyItemCard: React.FC<{
skuPath: string
mobile?: boolean
className?: string
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
}> = observer(({
skuPath,
className=''
mobile=false,
className='',
onQuantityChanged
}) => {

const cmmc = useCommerce()
Expand All @@ -28,17 +32,17 @@ const BuyItemCard: React.FC<{

useEffect(() => {

const toks = skuPath.split('-')
levelRef.current = toks.length - 1
const fsv: FacetsValue = {}
for (let level = 1; level <= levelRef.current; level++ ) {
fsv[level] = [toks[level]]
}
if (facets) {
const toks = skuPath.split('-')
const levelSpecified = toks.length - 1
const fsv: FacetsValue = {}
for (let level = 1; level <= levelSpecified; level++ ) {
fsv[level] = [toks[level]]
}
fsv[levelSpecified + 1] = [facets[0].value]
levelRef.current = levelSpecified
cmmc.setFacets(fsv)
fsv[levelRef.current + 1] = [facets[0].value]
}
cmmc.setFacets(fsv)

return autorun(() => {
const cats = cmmc.specifiedCategories
// Original cat was legit
Expand All @@ -55,24 +59,30 @@ const BuyItemCard: React.FC<{
})
}, [cat, facets])

const renderFacetTabs = facets && levelRef.current > 0

return (
<div className={className} >
{facets && levelRef.current > 0 && (
{renderFacetTabs && (
<FacetValuesWidget
className={cn('grid gap-0 ' + `grid-cols-${facets.length}` + ' self-start ', 'border-b mb-2 -mr-2 -ml-2')}
className={cn('grid gap-0 ' + `grid-cols-${facets.length}` + ' self-start ', 'border-b-2 border-level-3 mb-2 -mr-2 -ml-2')}
isMobile={false}
mutator={getFacetValuesMutator(levelRef.current + 1, cmmc)}
itemClx='flex-col h-auto gap-0 pb-1 pt-3 px-3'
buttonClx='h-auto !rounded-bl-none !rounded-br-none !rounded-tl-lg !rounded-tr-lg '
buttonClx={'h-full !rounded-bl-none !rounded-br-none !rounded-tl-lg !rounded-tr-lg ' +
'!border-r !border-t !border-level-3'}
facetValues={facets}
/>
)}
{cmmc.specifiedCategories[0] && (
<SelectCategoryItemCard
noTitle
mobile={mobile}
category={cmmc.specifiedCategories[0]}
selectedItemRef={cmmc /* ...conveniently. :) */ }
selectSku={cmmc.setCurrentItem.bind(cmmc)}
className={!renderFacetTabs && mobile ? 'border-t-2 ' : ''}
onQuantityChanged={onQuantityChanged}
/>
)}
</div >
Expand Down
48 changes: 48 additions & 0 deletions packages/commerce/components/buy-item/buy-item-mobile-drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client'
import React, { useState, type ReactNode } from 'react'

import { X as LucideX} from 'lucide-react'
import { Sheet, SheetContent, SheetTrigger } from '@hanzo/ui/primitives'

import { cn } from '@hanzo/ui/util'

import BuyItemCard from './buy-item-card'

const BuyItemMobileDrawer: React.FC<{
skuPath: string
trigger: ReactNode
triggerClx?: string
drawerClx?: string
cardClx?: string
}> = ({
skuPath,
trigger,
triggerClx='',
drawerClx='',
cardClx=''
}) => {

const [open, setOpen] = useState<boolean>(false)

const onQuantityChanged = (sku: string, oldV: number, newV: number) => {
if (oldV === 0 && newV === 1) {
setTimeout(() => {setOpen(false)}, 150)
}
}

return (
<Sheet open={open} onOpenChange={setOpen} >
<SheetTrigger asChild className={triggerClx}>
{trigger}
</SheetTrigger>
<SheetContent
className={cn('rounded-tl-xl rounded-tr-xl p-0 overflow-hidden border-none', drawerClx)}
side="bottom"
>
<BuyItemCard skuPath={skuPath} mobile onQuantityChanged={onQuantityChanged} className={cn("w-full relative ", cardClx)}/>
</SheetContent>
</Sheet>
)
}

export default BuyItemMobileDrawer
37 changes: 25 additions & 12 deletions packages/commerce/components/buy-item/buy-item-popup.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client'
import React, {type PropsWithChildren} from 'react'
import React, {useState, type PropsWithChildren} from 'react'

import { X } from 'lucide-react'

Expand All @@ -16,23 +16,36 @@ import BuyItemCard from './buy-item-card'

const BuyItemPopup: React.FC<PropsWithChildren & {
skuPath: string
triggerClx?: string
popupClx?: string
cardClx?: string
}> = ({
skuPath,
children,
triggerClx='',
popupClx='',
cardClx='',
}) => (
<Popover>
<PopoverTrigger asChild>
{children}
</PopoverTrigger>
<PopoverContent className={cn('relative flex flex-col p-0 px-4 pb-4 pt-2', popupClx)}>
<PopoverClose className='absolute z-20 right-2 top-2 self-end hover:bg-level-3 text-muted hover:text-accent p-1 rounded-full'><X className='w-5 h-5'/></PopoverClose>
<BuyItemCard skuPath={skuPath} className={cn("w-full relative ", cardClx)}/>
</PopoverContent>
</Popover>
)
}) => {

const [open, setOpen] = useState<boolean>(false)

const onQuantityChanged = (sku: string, oldV: number, newV: number) => {
if (oldV === 0 && newV === 1) {
setTimeout(() => {setOpen(false)}, 150)
}
}

return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild className={triggerClx}>
{children}
</PopoverTrigger>
<PopoverContent className={cn('relative flex flex-col p-0 px-4 pb-4 pt-2', popupClx)}>
<PopoverClose className='absolute z-20 right-2 top-2 self-end hover:bg-level-3 text-muted hover:text-accent p-1 rounded-full'><X className='w-5 h-5'/></PopoverClose>
<BuyItemCard skuPath={skuPath} onQuantityChanged={onQuantityChanged} className={cn("w-full relative ", cardClx)}/>
</PopoverContent>
</Popover>
)
}

export default BuyItemPopup
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
isLoading?: boolean
mobile?: boolean
noTitle?: boolean
onQuantityChanged?: (sku: string, oldV: number, newV: number) => void
}> = /* NOT observer */({
category,
selectedItemRef: selItemRef,
Expand All @@ -24,20 +25,25 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
isLoading = false,
mobile = false,
noTitle = false,
onQuantityChanged,
...props
}) => {

const soleOption = category.products.length === 1

const SelectProductComp: React.FC<{ className?: string }> = ({ className = '' }) => {
const SelectProductComp: React.FC<{ className?: string }> = ({
className = ''
}) => {

const mobilePicker = (mobile || category.products.length > 6)
if (soleOption) {
const item = category.products[0] as LineItem
return (
<p >{item.titleAsOption + ', ' + formatPrice(item.price) + (item.quantity > 0 ? `(${item.quantity})` : '')}</p>
<div className={cn('flex flex-col justify-center items-center ' + (mobilePicker ? 'h-[180px] ' : 'h-auto min-h-24'), className)}>
<p className='text-lg font-semibold'>{item.titleAsOption + ', ' + formatPrice(item.price)}</p>
</div>
)
}
const mobilePicker = (mobile && category.products.length > 6)

return (
<div /* id='CV_AVAIL_AMOUNTS' */ className={cn(
Expand All @@ -52,14 +58,15 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
selectSku={selectSku}
height={180}
itemHeight={30}
outerClx='mb-4'
outerClx='w-full'
/>
) : (
<CategoryItemRadioSelector
category={category}
selectedItemRef={selItemRef}
selectSku={selectSku}
groupClx='mt-2'
showQuantity={false}
itemClx='flex flex-row gap-2.5 items-center'
/>
)}
Expand All @@ -70,7 +77,12 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
const AddToCartComp: React.FC<{ className?: string }> = observer(({ className = '' }) => (
// TODO disable if nothing selected
(selItemRef.item && !isLoading) && (
<AddToCartWidget size='default' item={selItemRef.item} className={cn('lg:min-w-[160px] lg:mx-auto', className)}/>
<AddToCartWidget
size='default'
item={selItemRef.item}
onQuantityChanged={onQuantityChanged}
className={cn('lg:min-w-[160px] lg:mx-auto', className)}
/>
)
))

Expand All @@ -87,15 +99,14 @@ const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & It
return mobile ? (
<div /* id='CV_OUTER' */
className={cn(
'w-full h-[calc(100svh-96px)] max-h-[700px] flex flex-col justify-between ' +
'items-stretch gap-[4vh] mt-[2vh] pb-[6vh]',
'w-full flex flex-col justify-between items-center gap-5 py-pr-6',
className
)}
{...props}
>
{!noTitle && (<TitleArea className='grow pt-3 mb-0' />)}
<SelectProductComp className='mb-[3vh]' />
<AddToCartComp className='w-pr-70 mx-auto' />
<SelectProductComp className='w-pr-65' />
<AddToCartComp className='w-pr-65' />
</div>
) : (
<div className={cn('', className)} {...props}>
Expand Down
Loading

0 comments on commit cdf5d0a

Please sign in to comment.