Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmmc: Checkout improv, auth: improved LoginComponent #52

Merged
merged 3 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions packages/auth/components/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ const Login: React.FC<{
redirectUrl?: string,
getStartedUrl?: string,
returnToUrl?: string,
className?: string
className?: string,
hideHeader?: boolean
}> = observer(({
redirectUrl,
getStartedUrl,
returnToUrl,
className
className,
hideHeader
}) => {
const router = useRouter()

Expand Down Expand Up @@ -105,7 +107,7 @@ const Login: React.FC<{
<ArrowLeft/>
</Button>
)}
<h2 className='mx-auto'>Login</h2>
{!hideHeader && <h2 className='mx-auto'>Login</h2>}
</div>
{redirectUrl === 'checkout' && <p>You will be redirected to checkout after login.</p>}
<EmailPasswordForm onSubmit={loginWithEmailPassword} isLoading={isLoading} className='mb-4'/>
Expand Down
37 changes: 17 additions & 20 deletions packages/commerce/components/cart.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
'use client'
import React, { type PropsWithChildren } from 'react'
import { useRouter } from 'next/navigation'
import React, { useState, type PropsWithChildren } from 'react'
import { observer } from 'mobx-react-lite'

import { Button } from '@hanzo/ui/primitives'
import { cn } from '@hanzo/ui/util'
import { useAuth } from '@hanzo/auth/service'

import { useCommerce } from '../service/context'
import { formatPrice } from '../util'

import CartLineItem from './cart-line-item'
import { Checkout } from '.'

const Cart: React.FC<PropsWithChildren & {
className?: string
Expand All @@ -23,31 +22,29 @@ const Cart: React.FC<PropsWithChildren & {
isMobile=false,
hideCheckout=false
}) => {
const [isCheckoutOpen, setIsCheckoutOpen] = useState(false)
const cmmc = useCommerce()
const router = useRouter()
const auth = useAuth()


return (
<div className={cn('border p-4 rounded-lg', className)}>
{children}
<div className='mt-2'>
<div className='mt-2 w-full'>
{!!children && <div className='h-[1px] w-pr-80 mb-4 mx-auto bg-muted-3'/>}
{cmmc.cartItems.length === 0 ? (
<p className='text-center mt-4'>No items in cart</p>
) : (<>
{cmmc.cartItems.map((item, i) => (
<CartLineItem isMobile={isMobile} item={item} key={item.sku} className='mb-2 md:mb-3'/>
))}
<p className='mt-6 text-right border-t pt-1'>TOTAL: <span className='font-semibold'>{cmmc.cartTotal === 0 ? '0' : formatPrice(cmmc.cartTotal)}</span></p>
</>)}
{cmmc.cartItems.length === 0 ? (
<p className='text-center mt-4'>No items in cart</p>
) : (<>
{cmmc.cartItems.map((item, i) => (
<CartLineItem isMobile={isMobile} item={item} key={item.sku} className='mb-2 md:mb-3'/>
))}
<p className='mt-6 text-right border-t pt-1'>TOTAL: <span className='font-semibold'>{cmmc.cartTotal === 0 ? '0' : formatPrice(cmmc.cartTotal)}</span></p>
</>)}
</div>
{cmmc.cartItems.length > 0 && !hideCheckout && (
{!hideCheckout && (
<>
{!(auth && auth.loggedIn) ? (
<Button size='sm' variant='secondary' rounded='lg' className='mt-12 mx-auto' onClick={() => router.push('/login?redirectUrl=checkout')}>Login to checkout</Button>
) : (
<Button size='lg' variant='secondary' rounded='lg' className='mt-12 mx-auto' onClick={() => router.push('/checkout')}>Checkout</Button>
{cmmc.cartItems.length > 0 && (
<Button size='lg' variant='secondary' rounded='lg' className='mt-12 mx-auto' onClick={() => setIsCheckoutOpen(!isCheckoutOpen)}>Checkout</Button>
)}
{isCheckoutOpen && <Checkout toggleCheckout={() => setIsCheckoutOpen(!isCheckoutOpen)}/>}
</>
)}
</div>
Expand Down
142 changes: 142 additions & 0 deletions packages/commerce/components/checkout/confirm-order.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
'use client'

import { ApplyTypography, Button } from '@hanzo/ui/primitives'
import { BookUser, Lock, User } from 'lucide-react'
import { EnhHeadingBlockComponent, type EnhHeadingBlock } from '@hanzo/ui/blocks'
import type { UseFormReturn } from 'react-hook-form'

const ConfirmOrder: React.FC<{
paymentForm: UseFormReturn<{
name: string;
email: string;
cardName: string;
cardNumber: string;
cardExpiryDate: string;
cardCvc: string;
}, any, {
name: string;
email: string;
cardName: string;
cardNumber: string;
cardExpiryDate: string;
cardCvc: string;
}>,
shippingForm: UseFormReturn<{
firstName: string;
lastName: string;
addressLine1: string;
zipCode: string;
city: string;
country: string;
addressLine2?: string | undefined;
state?: string | undefined;
}, any, {
firstName: string;
lastName: string;
addressLine1: string;
zipCode: string;
city: string;
country: string;
addressLine2?: string | undefined;
state?: string | undefined;
}>,
setStep: (step: number) => void,
onPayClick: () => void
}> = ({
paymentForm,
shippingForm,
setStep,
onPayClick
}) => {

const paymentFormValues = paymentForm.getValues()
const shippingFormValues = shippingForm.getValues()

return (
<div className='flex flex-col gap-6'>
<div className='flex gap-4 items-center'>
<User />
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: `Contact Details`, level: 4 },
} as EnhHeadingBlock}/>
</div>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Contact name', level: 6 },
byline: { text: paymentFormValues.name, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Contact email', level: 6 },
byline: { text: paymentFormValues.email, level: 0 },
} as EnhHeadingBlock}/>

<div className='flex gap-4 items-center'>
<ApplyTypography>
<h4 className='flex gap-4 items-center'><Lock /> Payment Info</h4>
<p>Guaranteed safe & secure checkout.</p>
</ApplyTypography>
</div>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Name on card', level: 6 },
byline: { text: paymentFormValues.cardName, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Card number', level: 6 },
byline: { text: paymentFormValues.cardNumber, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Card expiry date', level: 6 },
byline: { text: paymentFormValues.cardExpiryDate, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Card CVC', level: 6 },
byline: { text: paymentFormValues.cardCvc, level: 0 },
} as EnhHeadingBlock}/>

<div className='flex gap-4 items-center'>
<BookUser />
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: `Shipping address`, level: 4 },
} as EnhHeadingBlock}/>
</div>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'First name', level: 6 },
byline: { text: shippingFormValues.firstName, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Last name', level: 6 },
byline: { text: shippingFormValues.lastName, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Address line 1', level: 6 },
byline: { text: shippingFormValues.addressLine1, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Address line 2 (optional)', level: 6 },
byline: { text: shippingFormValues.addressLine2, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Zip code', level: 6 },
byline: { text: shippingFormValues.zipCode, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'City', level: 6 },
byline: { text: shippingFormValues.city, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'State (optional)', level: 6 },
byline: { text: shippingFormValues.state, level: 0 },
} as EnhHeadingBlock}/>
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: 'Country', level: 6 },
byline: { text: shippingFormValues.country, level: 0 },
} as EnhHeadingBlock}/>

<div className='flex justify-between mt-8'>
<Button variant='outline' onClick={() => setStep(1)}>Back</Button>
<Button onClick={onPayClick}>Pay</Button>
</div>
</div>
)
}

export default ConfirmOrder
89 changes: 89 additions & 0 deletions packages/commerce/components/checkout/contact-info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use client'

import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@hanzo/ui/primitives/form'
import { Input, Button } from '@hanzo/ui/primitives'
import { User } from 'lucide-react'
import { EnhHeadingBlockComponent, type EnhHeadingBlock } from '@hanzo/ui/blocks'
import type { UseFormReturn } from 'react-hook-form'

const ContactInfo: React.FC<{
form: UseFormReturn<{
name: string;
email: string;
}, any, {
name: string;
email: string;
}>,
selectPaymentMethod: (method: 'crypto' | 'bank') => Promise<void>,
}> = ({
form,
selectPaymentMethod,
}) => {
return (
<Form {...form}>
<form className='text-left'>
<div className='flex flex-col gap-4'>
<div className='flex gap-4 items-center'>
<User />
<EnhHeadingBlockComponent block={{blockType: 'enh-heading',
heading: { text: `Contact Info`, level: 4 },
} as EnhHeadingBlock}/>
</div>
<div className='flex flex-col sm:flex-row gap-4'>
<FormField
control={form.control}
name='name'
render={({ field }) => (
<FormItem className='space-y-1 w-full'>
<FormLabel>Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='email'
render={({ field }) => (
<FormItem className='space-y-1 w-full'>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className='flex flex-col sm:flex-row gap-4 items-center'>
<Button
type='button'
onClick={() => selectPaymentMethod('crypto')}
className='mx-auto w-full'
>
Pay with crypto
</Button>
<Button
type='button'
onClick={() => selectPaymentMethod('bank')}
className='mx-auto w-full'
>
Bank transfer
</Button>
</div>
</div>
</form>
</Form>
)
}

export default ContactInfo
Loading
Loading