Skip to content

Commit

Permalink
cmmc: mobile checkout condensed; desktop checkout has prod carousel; …
Browse files Browse the repository at this point in the history
…minor cleanups (#73)

cmmc:
* Style fixes for checkout (as requested)
* added products carousel to cart panel on desktop
* added video and animation for Product, 
* fixed scroll for cart on checkout
ui:
* VideoDef  
* added comment to animation prop
* [email protected], [email protected], cmmc:4.7.0; minor cleanups
---------
Co-authored-by: artem ash <[email protected]>
  • Loading branch information
erikrakuscek and artemis-prime authored Mar 21, 2024
1 parent 7da85b5 commit 7d8d161
Show file tree
Hide file tree
Showing 25 changed files with 279 additions and 146 deletions.
35 changes: 15 additions & 20 deletions packages/auth/components/auth-widget.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client'
import React from 'react'

import { observer } from 'mobx-react-lite'

import {
Expand Down Expand Up @@ -45,26 +44,22 @@ const AuthWidget: React.FC<{
// button regardless of status.
if (!auth.loggedIn) {

return (
!hideLogin ? (
(triggerLogin) ? (
<Button
variant='primary'
className='h-8 !text-[13px]/[13px] w-fit !min-w-0'
onClick={triggerLogin}
>
Login
</Button>
) : (
<LinkElement
def={{href: '/login', title: 'Login', variant: 'primary'} satisfies LinkDef}
className='h-8 !text-[13px]/[13px] w-fit !min-w-0'
/>
)
return (hideLogin ? null : (
(triggerLogin) ? (
<Button
variant='primary'
className='h-8 !text-[13px]/[13px] w-fit !min-w-0'
onClick={triggerLogin}
>
Login
</Button>
) : (
<div/>
<LinkElement
def={{href: '/login', title: 'Login', variant: 'primary'} satisfies LinkDef}
className='h-8 !text-[13px]/[13px] w-fit !min-w-0'
/>
)
)
))
}

return (
Expand Down Expand Up @@ -103,4 +98,4 @@ const AuthWidget: React.FC<{
)
})

export default AuthWidget
export default AuthWidget
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.3.0",
"version": "2.3.1",
"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.6",
"@hanzo/ui": "^3.0.9",
"@hookform/resolvers": "^3.3.4",
"firebase": "^10.8.0",
"lucide-react": "^0.344.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/commerce/components/cart-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@ import { formatPrice } from '../../util'

import CartLineItem from './cart-line-item'
import { sendFBEvent, sendGAEvent } from '../../util/analytics'
import ProductsCarousel from './products-carousel'

const CartPanel: React.FC<PropsWithChildren & {
className?: string
isMobile?: boolean,
noCheckout?: boolean
showProductsCarousel?: boolean
onCheckoutOpen?: () => void
}> = observer(({
/** Children is the heading area. */
children,
className='',
isMobile=false,
noCheckout=false,
showProductsCarousel=false,
onCheckoutOpen,
}) => {
/* TODO: onCheckoutOpen is a hackish way fix a bug with multiple dialog opened at the same time.
Expand Down Expand Up @@ -60,6 +63,7 @@ const CartPanel: React.FC<PropsWithChildren & {
return (
<div className={cn('border p-4 rounded-lg', className)}>
{children}
{showProductsCarousel && <ProductsCarousel items={cmmc.cartItems}/>}
<div className='mt-2 w-full'>
{cmmc.cartEmpty ? (
<p className='text-center mt-4'>No items in cart</p>
Expand Down
60 changes: 60 additions & 0 deletions packages/commerce/components/cart-panel/products-carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Spline from '@splinetool/react-spline'

import {
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext
} from '@hanzo/ui/primitives'
import {
VideoBlockComponent,
ImageBlockComponent,
type ImageBlock,
type Block,
type VideoBlock
} from '@hanzo/ui/blocks'

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

// Carousel content hierarchy: 3D > MP4 > Image
const ProductsCarousel: React.FC<{
items: LineItem[]
}> = ({
items,
}) => (
<Carousel options={{ loop: true }} className='w-full max-w-sm mx-auto px-2' >
<CarouselContent>
{items.map(({title, img, video, animation}, index) => (
<CarouselItem key={index}>
<div className='flex aspect-square items-center justify-center p-6'>
{animation ? (
<Spline
scene={animation}
className='!aspect-[12/10] pointer-events-none !w-auto !h-auto'
/>
) : video ? (
<VideoBlockComponent
block={{blockType: 'video', ...video} satisfies VideoBlock as Block}
/>
) : (
<ImageBlockComponent
block={{
blockType: 'image',
src: img ?? '',
alt: title + ' image',
dim: { w: 250, h: 250 }
} satisfies ImageBlock as Block}
className='m-auto'
/>
)}
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
)

export default ProductsCarousel
39 changes: 39 additions & 0 deletions packages/commerce/components/checkout-panel/cart-accordian.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client'
import React from 'react'
import { observer } from 'mobx-react-lite'

import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@hanzo/ui/primitives'

import { formatPrice, useCommerce } from '../..'

import CartPanel from '../cart-panel'
import BagIcon from './icons/bag-icon'

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

const cmmc = useCommerce()
return (
<Accordion type="single" collapsible className={className}>
<AccordionItem value="cart" className='w-full border-b-0'>
<AccordionTrigger className='!no-underline py-1'>
<div className='flex gap-4 items-center'>
<BagIcon className='w-4 h-4 sm:w-6 sm:h-6'/>
<h5 className='text-sm sm:text-xl truncate'>Order Summary {formatPrice(cmmc.cartTotal)}</h5>
</div>
</AccordionTrigger>
<AccordionContent>
<CartPanel noCheckout className='border-none w-full'/>
</AccordionContent>
</AccordionItem>
</Accordion>
)
})

export default CartAccordian
47 changes: 18 additions & 29 deletions packages/commerce/components/checkout-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,22 @@ import { useState } from 'react'
import { observer } from 'mobx-react-lite'

import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
Dialog,
DialogPortal,
ScrollArea,
} from '@hanzo/ui/primitives'
import { cn } from '@hanzo/ui/util'
import { AuthWidget } from '@hanzo/auth/components'

import { useCommerce } from '../..'

import ShippingInfo from './shipping-info'
import ThankYou from './thank-you'
import CartPanel from '../cart-panel'
import Payment from './payment'
import CloseButton from './close-button'
import StepIndicator from './step-indicator'
import BagIcon from './icons/bag-icon'
import { formatPrice, useCommerce } from '../..'
import CartAccordian from './cart-accordian'

const CheckoutPanel: React.FC<{
open: boolean
Expand Down Expand Up @@ -72,43 +70,34 @@ const CheckoutPanel: React.FC<{
return (
<Dialog open={open}>
<DialogPortal>
<div id='PORTAL_OUTER'
<div /* id='PORTAL_OUTER' */
className={cn(
'fixed top-0 shadow-lg ',
'animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10',
'sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0',
'!max-w-none w-full h-full min-h-screen bg-transparent backdrop-blur-sm z-50'
)}
>
<div id='GRID' className='grid grid-cols-1 md:grid-cols-2 justify-center h-full overflow-y-auto md:overflow-y-hidden'>
<div className='bg-background flex flex-row items-start justify-end'>
<div className='h-full w-full max-w-[750px] relative flex flex-col items-center justify-start px-6 pt-10 pb-0 md:pb-9'>
<div /* id='GRID' */ className='flex flex-col md:flex-row justify-center h-full overflow-y-auto md:overflow-y-hidden'>
<div className='w-full bg-background flex flex-row items-start justify-end'>
<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'>
<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' />
<AuthWidget hideLogin className='flex md:hidden absolute top-3 right-3'/>
<CartPanel noCheckout className='border border-muted-2 mt-10 w-full max-w-[550px] hidden md:flex'/>

<Accordion type="single" collapsible className='flex items-center justify-center py-2 w-full md:hidden'>
<AccordionItem value="cart" className='w-full border-b-0'>
<AccordionTrigger className='!no-underline'>
<div className='flex gap-4 items-center'>
<BagIcon className='w-4 h-4 sm:w-6 sm:h-6'/>
<h5 className='text-sm sm:text-xl truncate'>Order Summary {formatPrice(cmmc.cartTotal)}</h5>
</div>
</AccordionTrigger>
<AccordionContent>
<CartPanel noCheckout className='border-none w-full'/>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<CartPanel
className='hidden md:flex border-none mt-10 w-full max-w-[550px] flex-col'
noCheckout
showProductsCarousel
/>
<CartAccordian className='md:hidden flex items-center justify-center py-2 w-full' />
</ScrollArea>
</div>
<div className='bg-level-1 flex flex-row items-start justify-start md:overflow-y-auto'>
<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'>
<ScrollArea className='w-full h-full bg-level-1 flex flex-row items-start justify-start md:overflow-y-auto'>
<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'>
<AuthWidget hideLogin className='hidden md:flex absolute top-4 right-4 '/>
<StepIndicator steps={steps} currentStep={step} className='flex gap-2 mx-auto items-center text-xxs sm:text-base' />
{steps[step].element}
</div>
</div>
</ScrollArea>
</div>
</div>
</DialogPortal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ const ContactInfo: React.FC<{
return (
<Form {...form}>
<form className='text-left'>
<div className='flex flex-col sm:flex-row gap-2'>
<div className='flex gap-1 sm:gap-2'>
<FormField
control={form.control}
name='name'
render={({ field }) => (
<FormItem className='space-y-1 w-full'>
<FormLabel>Full name</FormLabel>
<FormControl>
<Input {...field} className='border-muted-4'/>
<Input {...field} className='border-muted-4' placeholder='Full name'/>
</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -42,9 +41,8 @@ const ContactInfo: React.FC<{
name='email'
render={({ field }) => (
<FormItem className='space-y-1 w-full'>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} className='border-muted-4'/>
<Input {...field} className='border-muted-4' placeholder='Email'/>
</FormControl>
<FormMessage />
</FormItem>
Expand Down
12 changes: 6 additions & 6 deletions packages/commerce/components/checkout-panel/payment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,24 @@ const Payment: React.FC<{
<TabsList className='grid w-full grid-cols-3 mx-auto bg-level-2 h-auto'>
<TabsTrigger
value='card'
className='whitespace-normal h-full text-sm sm:text-base'
className='whitespace-normal h-full text-xs sm:text-base px-1'
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
>
PAY WITH CARD
Pay with card
</TabsTrigger>
<TabsTrigger
value='crypto'
className='whitespace-normal h-full text-sm sm:text-base'
className='whitespace-normal h-full text-xs sm:text-base px-1'
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
>
PAY WITH CRYPTO
Pay with Wallet
</TabsTrigger>
<TabsTrigger
value='bank'
className='whitespace-normal h-full text-sm sm:text-base'
className='whitespace-normal h-full text-xs sm:text-base px-1'
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
>
BANK TRANSFER
Pay with Wire
</TabsTrigger>
</TabsList>
<TabsContent value='card'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ const InfoField: React.FC<{
}) => {
const copyToClipboard = (label: string, text: string) => {
navigator.clipboard.writeText(text)
toast(`${label} copied to clipboard`)
toast(`${label} copied to clipboard.`)
}

return (
<div className='flex flex-col gap-1'>
<p className='text-xs'>{label}</p>
<div className='flex items-center justify-between text-lg border rounded-lg py-2 px-4'>
<div className='flex items-center justify-between sm:text-lg border rounded-lg py-2 px-4'>
{value}
<Button variant='ghost' size='icon' onClick={() => copyToClipboard(label, copyValue)}>
<Copy className='h-4 w-4'/>
Expand Down Expand Up @@ -57,7 +57,7 @@ const PayWithBankTransfer: React.FC<{
}

return (
<div className='flex flex-col gap-6 mt-6'>
<div className='flex flex-col gap-2 mt-6'>
<ContactInfo form={contactForm}/>
<Tabs defaultValue="usd" className='w-full mx-auto max-w-[50rem]'>
<TabsList className="grid w-full grid-cols-2 max-w-[15rem] mx-auto bg-level-2">
Expand Down
Loading

0 comments on commit 7d8d161

Please sign in to comment.