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

Commerce: Added support for card payments in checkout #59

Merged
merged 11 commits into from
Mar 16, 2024
18 changes: 10 additions & 8 deletions packages/auth/components/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,16 @@ const Login: React.FC<{
</div>
) : (
<div className='flex flex-col gap-4 text-center'>
<div className='mb-4 items-center flex'>
{returnToUrl && (
<Button size='icon' variant='ghost' className='absolute' onClick={() => router.push(returnToUrl)}>
<ArrowLeft/>
</Button>
)}
{!hideHeader && <h2 className='mx-auto'>Login</h2>}
</div>
{!hideHeader && (
<div className='mb-4 items-center flex'>
{returnToUrl && (
<Button size='icon' variant='ghost' className='absolute' onClick={() => router.push(returnToUrl)}>
<ArrowLeft/>
</Button>
)}
<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'/>
<p>- <span className='font-normal font-nav'>or</span> -</p>
Expand Down
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.0",
"version": "2.0.1",
"description": "Library with Firebase authentication.",
"publishConfig": {
"registry": "https://registry.npmjs.org/",
Expand Down
83 changes: 0 additions & 83 deletions packages/commerce/components/checkout/contact-info.tsx

This file was deleted.

82 changes: 17 additions & 65 deletions packages/commerce/components/checkout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,99 +1,51 @@
'use client'

import { useEffect, useState } from 'react'
import { useState } from 'react'
import { ChevronLeft } from 'lucide-react'
import { observer } from 'mobx-react-lite'

import { zodResolver } from '@hookform/resolvers/zod'
import * as z from 'zod'
import { useForm } from 'react-hook-form'

import { Button, Main, Separator, Toaster } from '@hanzo/ui/primitives'
import { cn } from '@hanzo/ui/util'
import { useAuth } from '@hanzo/auth/service'
import { LoginComponent, AuthWidget } from '@hanzo/auth/components'

import ShippingInfo from './shipping-info'
import ThankYou from './thank-you'
import PayWithCrypto from './pay-with-crypto'
import PayByBankTransfer from './pay-by-bank-transfer'
import ContactInfo from './contact-info'
import { Cart } from '..'
import { useCommerce } from '../../service/context'

const contactFormSchema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters.'),
email: z.string().email(),
})
import Payment from './payment'

const Checkout: React.FC<{toggleCheckout: () => void}> = observer(({toggleCheckout}) => {
const auth = useAuth()
const cmmc = useCommerce()

const [currentStep, setCurrentStep] = useState(1)
const [paymentMethod, setPaymentMethod] = useState<'crypto' | 'bank' | undefined>()
const [orderId, setOrderId] = useState<string>()

const contactForm = useForm<z.infer<typeof contactFormSchema>>({
resolver: zodResolver(contactFormSchema),
defaultValues: {
name: auth.user?.displayName ?? '',
email: auth.user?.email ?? '',
},
})

useEffect(() => {
if (auth.loggedIn) {
contactForm.setValue('name', auth.user?.displayName ?? '')
contactForm.setValue('email', auth.user?.email ?? '')
}
}, [auth.loggedIn])

const selectPaymentMethod = async (method: 'crypto' | 'bank') => {
contactForm.handleSubmit(async () => {
if (auth.user) {
if (!!orderId) {
await cmmc.updateOrder(orderId, auth.user.email, method)
} else {
const id = await cmmc.createOrder(auth.user.email, method)
setOrderId(id)
}
}
setPaymentMethod(method)
setCurrentStep(2)
})()
}


const step1 = auth.loggedIn ? (
<ContactInfo form={contactForm} selectPaymentMethod={selectPaymentMethod}/>
<Payment
orderId={orderId}
setOrderId={setOrderId}
setCurrentStep={setCurrentStep}
/>
) : (
<LoginComponent hideHeader className='max-w-[20rem] mx-auto'/>
)

const step2 = paymentMethod === 'crypto' ? (
<PayWithCrypto setCurrentStep={setCurrentStep}/>
) : (
<PayByBankTransfer setCurrentStep={setCurrentStep}/>
)

const step3 = (
<ShippingInfo orderId={orderId} paymentMethod={paymentMethod} setCurrentStep={setCurrentStep}/>
const step2 = (
<ShippingInfo orderId={orderId} setCurrentStep={setCurrentStep}/>
)

const step4 = <ThankYou/>
const step3 = <ThankYou/>

const steps = [step1, step2, step3, step4]
const steps = [step1, step2, step3]

return (
<div className="fixed top-0 left-0 !max-w-none w-full h-full min-h-screen bg-background z-[51]">
<div className="fixed top-0 left-0 !max-w-none w-full h-full min-h-screen bg-background z-50">
<Toaster/>
<Main className='flex flex-col gap-1 h-full'>
<div className='flex justify-between'>
<div className='flex justify-between items-center'>
<Button
variant='ghost'
size='icon'
className='sm:ml-2 sm:mt-2'
onClick={() => {
setCurrentStep(0)
toggleCheckout()
Expand All @@ -109,18 +61,18 @@ const Checkout: React.FC<{toggleCheckout: () => void}> = observer(({toggleChecko
<Cart hideCheckout className='fixed justify-center border-none mt-10 w-1/3 max-w-[40rem]'/>
</div>

<div className='flex flex-col gap-8 sm:gap-14 col-span-5 md:col-span-3 max-w-[30rem] w-full mx-auto overflow-y-auto h-[calc(100%-40px)] sm:h-[calc(100%-48px)] py-4'>
<div className='flex flex-col gap-8 sm:gap-14 col-span-5 md:col-span-3 max-w-[30rem] w-full mx-auto overflow-y-auto h-[calc(100%-40px)] sm:h-[calc(100%-48px)] py-4 px-1'>
<div className='flex gap-2 mx-auto items-center text-xxs sm:text-base'>
<div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 1 ? 'bg-foreground text-muted-4' : '')}>
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Contact</div>
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Payment</div>
</div>
<Separator className='w-[4rem] sm:w-[6rem]'/>
<div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 2 ? 'bg-foreground text-muted-4' : '')}>
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Payment</div>
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Delivery</div>
</div>
<Separator className='w-[4rem] sm:w-[6rem]'/>
<div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 3 ? 'bg-foreground text-muted-4' : '')}>
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Delivery</div>
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Done!</div>
</div>
</div>
{steps[currentStep - 1]}
Expand Down
76 changes: 0 additions & 76 deletions packages/commerce/components/checkout/pay-by-bank-transfer.tsx

This file was deleted.

59 changes: 59 additions & 0 deletions packages/commerce/components/checkout/payment/contact-info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@hanzo/ui/primitives/form'
import { Input } from '@hanzo/ui/primitives'
import type { UseFormReturn } from 'react-hook-form'

const ContactInfo: React.FC<{
form: UseFormReturn<{
name: string
email: string
}, any, {
name: string
email: string
}>,
}> = ({
form,
}) => {
return (
<Form {...form}>
<form className='text-left'>
<div className='flex flex-col sm:flex-row gap-2'>
<FormField
control={form.control}
name='name'
render={({ field }) => (
<FormItem className='space-y-1 w-full'>
<FormLabel>Full 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>
</form>
</Form>
)
}

export default ContactInfo
Loading
Loading