Skip to content

Commit

Permalink
[email protected], [email protected], [email protected]; Cmmc: simplifying buy flow; core - …
Browse files Browse the repository at this point in the history
…enh facets system (#60)

* [email protected], [email protected], [email protected]
Cmmc:
FacetValueDesc is now nested
enh: AddToCartWidget: hover and sizing
enh: radio selector now (optionally) shows quantities
impl: SelectCategoryItemCard
SelectCategoryItemCard; widget / card / panel naming
Category has optional parentTitle
renamed several key components according to new "widget" / "card" / "panel" system
impl and moved BuyItem<Button|Card|Pupop> to here (from client project)
Final styling and layout for buy popup
minor tree cleanup
changed mobx-utils to peerDep
minor changes to service function names
  • Loading branch information
artemis-prime authored Mar 16, 2024
1 parent 4c57bc5 commit 6ccc6c8
Show file tree
Hide file tree
Showing 63 changed files with 821 additions and 452 deletions.
4 changes: 2 additions & 2 deletions packages/auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hanzo/auth",
"version": "2.0.1",
"version": "2.0.2",
"description": "Library with Firebase authentication.",
"publishConfig": {
"registry": "https://registry.npmjs.org/",
Expand Down Expand Up @@ -31,7 +31,7 @@
"firebase-admin": "^12.0.0"
},
"peerDependencies": {
"@hanzo/ui": "^3.0.0",
"@hanzo/ui": "^3.0.2",
"@hookform/resolvers": "^3.3.4",
"firebase": "^10.8.0",
"lucide-react": "^0.344.0",
Expand Down

This file was deleted.

9 changes: 0 additions & 9 deletions packages/commerce/blocks/def/commerce-cat-and-item-block.ts

This file was deleted.

6 changes: 3 additions & 3 deletions packages/commerce/components/add-to-cart-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const AddToCartWidget: React.FC<{
size='xs'
}) => {

const iconClx = ghost ? 'h-4 w-4 md:h-3 md:w-3 text-muted-3 hover:text-foreground' : 'h-5 w-5 mr-1'
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'
const digitClx = ghost ? 'px-2 md:px-0.5' : 'sm:px-2 font-bold '

if (disabled) {
Expand All @@ -44,7 +44,7 @@ const AddToCartWidget: React.FC<{
size={size}
variant={ghost ? 'ghost' : 'secondary'}
rounded={ghost ? 'full' : 'xl'}
className={cn('px-1 lg:min-w-0 lg:px-2 xs:grow xs:justify-end', buttonClx)}
className={cn('px-1 lg:min-w-0 lg:px-2 xs:justify-end', buttonClx)}
key='left'
onClick={item.decrement.bind(item)}
>
Expand All @@ -60,7 +60,7 @@ const AddToCartWidget: React.FC<{
size={size}
variant={ghost ? 'ghost' : 'secondary'}
rounded={ghost ? 'full' : 'xl'}
className={cn('px-1 lg:min-w-0 lg:px-2 xs:grow xs:justify-start', buttonClx)}
className={cn('px-1 lg:min-w-0 lg:px-2 xs:justify-start', buttonClx)}
onClick={item.increment.bind(item)}
key='right'
>
Expand Down
28 changes: 28 additions & 0 deletions packages/commerce/components/buy-item/buy-item-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client'
import React, {type PropsWithChildren} from 'react'

import { type ButtonVariants, type ButtonSizes, Button } from '@hanzo/ui/primitives'

import BuyItemPopup from './buy-item-popup'

const BuyItemButton: React.FC<PropsWithChildren & {
skuPath: string
variant? : ButtonVariants
size?: ButtonSizes
/* rounded?: ButtonRounded // wait for version bump*/
className?: string
popupClx?: string
}> = ({
skuPath,
variant,
size,
children,
className='',
popupClx=''
}) => (
<BuyItemPopup skuPath={skuPath} popupClx={popupClx}>
<Button size={size} variant={variant} className={className}>{children}</Button>
</BuyItemPopup>
)

export default BuyItemButton
82 changes: 82 additions & 0 deletions packages/commerce/components/buy-item/buy-item-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use client'
import React, { useRef, useEffect } from 'react'
import { autorun } from 'mobx'
import { observer } from 'mobx-react-lite'

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

import type { FacetsValue } from '../../types'
import { useCommerce } from '../../service/context'
import { getFacetValuesMutator } from '../../util'
import FacetValuesWidget from '../facet-values-widget'
import SelectCategoryItemCard from './select-category-item-card'


const BuyItemCard: React.FC<{
skuPath: string
className?: string
}> = observer(({
skuPath,
className=''
}) => {

const cmmc = useCommerce()
const levelRef = useRef<number>(-1)

const cat = cmmc.getCategory(skuPath)
const facets = cat ? undefined : cmmc.getFacetValuesAtSkuPath(skuPath)

useEffect(() => {

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)
}
return autorun(() => {
const cats = cmmc.specifiedCategories
// Original cat was legit
if (cat && (cats.length === 0 || cats[0].id !== cat.id)) {
if (!cmmc.currentItem || cmmc.currentItem.categoryId !== cat.id) {
cmmc.setCurrentItem(cat.products[0].sku)
}
}
else if (cats.length > 0) {
if (!cmmc.currentItem || cmmc.currentItem.categoryId !== cats[0].id) {
cmmc.setCurrentItem(cats[0].products[0].sku)
}
}
})
}, [cat, facets])

return (
<div className={className} >
{facets && levelRef.current > 0 && (
<FacetValuesWidget
className={cn('grid gap-0 ' + `grid-cols-${facets.length}` + ' self-start ', 'border-b 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 '
facetValues={facets}
/>
)}
{cmmc.specifiedCategories[0] && (
<SelectCategoryItemCard
noTitle
category={cmmc.specifiedCategories[0]}
selectedItemRef={cmmc /* ...conveniently. :) */ }
selectSku={cmmc.setCurrentItem.bind(cmmc)}
/>
)}
</div >
)
})

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

import { X } from 'lucide-react'

import {
Popover,
PopoverContent,
PopoverTrigger,
PopoverClose
} from "@hanzo/ui/primitives"

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

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

const BuyItemPopup: React.FC<PropsWithChildren & {
skuPath: string
popupClx?: string
cardClx?: string
}> = ({
skuPath,
children,
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>
)

export default BuyItemPopup
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { observer } from 'mobx-react-lite'
import { ApplyTypography, ListBox } from '@hanzo/ui/primitives'
import { cn } from '@hanzo/ui/util'

import type { FacetValueDesc, FacetsValue, LineItem } from '../types'
import { useCommerce } from '../service/context'
import { formatPrice } from '../util'
import type { FacetValueDesc, FacetsValue, LineItem } from '../../types'
import { useCommerce } from '../../service/context'
import { formatPrice } from '../../util'

import FacetTogglesWidget from './facet-toggles-widget'
import FacetTogglesWidget from '../facet-values-widget'

const formatItem = (item: LineItem, withQuantity: boolean = false): string => (
`${item.titleAsOption}, ${formatPrice(item.price)}${(withQuantity && item.quantity > 0) ? ` (${item.quantity})` : ''}`
Expand Down
111 changes: 111 additions & 0 deletions packages/commerce/components/buy-item/select-category-item-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use client'
import React from 'react'
import { observer } from 'mobx-react-lite'

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

import type { ItemSelector, LineItem } from '../../types'

import AddToCartWidget from '../add-to-cart-widget'
import CategoryItemRadioSelector from '../category-item-radio-selector'
import CategoryItemIOSWheelSelector from '../category-item-ios-wheel-selector'
import { formatPrice } from '../../util'

const SelectCategoryItemCard: React.FC<React.HTMLAttributes<HTMLDivElement> & ItemSelector & {
isLoading?: boolean
mobile?: boolean
noTitle?: boolean
}> = /* NOT observer */({
category,
selectedItemRef: selItemRef,
selectSku,
className,
isLoading = false,
mobile = false,
noTitle = false,
...props
}) => {

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

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

if (soleOption) {
const item = category.products[0] as LineItem
return (
<p >{item.titleAsOption + ', ' + formatPrice(item.price) + (item.quantity > 0 ? `(${item.quantity})` : '')}</p>
)
}
const mobilePicker = (mobile && category.products.length > 6)

return (
<div /* id='CV_AVAIL_AMOUNTS' */ className={cn(
'sm:w-pr-80 sm:mx-auto md:w-full flex flex-col justify-start items-center',
className
)}>
{!noTitle && (<div className={'h-[1px] bg-muted-3 ' + (mobilePicker ? 'w-pr-55' : 'w-pr-60') } /> )}
{mobilePicker ? (
<CategoryItemIOSWheelSelector
category={category}
selectedItemRef={selItemRef}
selectSku={selectSku}
height={180}
itemHeight={30}
outerClx='mb-4'
/>
) : (
<CategoryItemRadioSelector
category={category}
selectedItemRef={selItemRef}
selectSku={selectSku}
groupClx='mt-2'
itemClx='flex flex-row gap-2.5 items-center'
/>
)}
</div>
)
}

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)}/>
)
))

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

isLoading ? (<Skeleton className={'h-8 w-full ' + className} />) : (

<div className={cn('text-center flex flex-col justify-start items-center', className)}>
<p className='font-nav text-center'>{category.title}</p>
</div>

)))

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]',
className
)}
{...props}
>
{!noTitle && (<TitleArea className='grow pt-3 mb-0' />)}
<SelectProductComp className='mb-[3vh]' />
<AddToCartComp className='w-pr-70 mx-auto' />
</div>
) : (
<div className={cn('', className)} {...props}>
{!noTitle && (<TitleArea className='' />)}
<div className='flex flex-col justify-start items-center gap-4'>
<SelectProductComp />
<AddToCartComp className='' />
</div>
</div>
)
}

export default SelectCategoryItemCard
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import Image from 'next/image'

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

import type { LineItem } from '../types'
import AddToCartWidget from './add-to-cart-widget'
import { formatPrice } from '../util'
import type { LineItem } from '../../types'
import { formatPrice } from '../../util'

import AddToCartWidget from '../add-to-cart-widget'

const IMG_SIZE=40

Expand Down
Loading

0 comments on commit 6ccc6c8

Please sign in to comment.