mirror of
https://github.com/vercel/commerce.git
synced 2025-07-25 11:11:24 +00:00
Custom Checkout Progress
This commit is contained in:
@@ -1,6 +1,31 @@
|
||||
.root {
|
||||
@apply flex flex-col py-4;
|
||||
}
|
||||
|
||||
.root:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@apply flex p-1 border-accent-3 border items-center justify-center w-12 text-accent-7;
|
||||
transition-property: border-color, background, color, transform, box-shadow;
|
||||
transition-duration: 0.15s;
|
||||
transition-timing-function: ease;
|
||||
}
|
||||
|
||||
.actions:hover {
|
||||
@apply bg-accent-1 border-accent-4 text-accent-9;
|
||||
transition: border-color;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.actions:focus {
|
||||
@apply bg-accent-2 outline-none;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
appearance: textfield;
|
||||
@apply w-8 border-accents-2 border mx-3 rounded text-center text-sm text-black;
|
||||
@apply w-8 border-accent-2 border mx-3 rounded text-center text-sm text-black;
|
||||
}
|
||||
|
||||
.quantity::-webkit-outer-spin-button,
|
||||
|
@@ -3,7 +3,7 @@ import cn from 'classnames'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import s from './CartItem.module.css'
|
||||
import { Trash, Plus, Minus } from '@components/icons'
|
||||
import { Trash, Plus, Minus, Cross } from '@components/icons'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import type { LineItem } from '@framework/types'
|
||||
import usePrice from '@framework/product/use-price'
|
||||
@@ -20,8 +20,10 @@ type ItemOption = {
|
||||
const CartItem = ({
|
||||
item,
|
||||
currencyCode,
|
||||
noEdit = false,
|
||||
...rest
|
||||
}: {
|
||||
noEdit?: boolean
|
||||
item: LineItem
|
||||
currencyCode: string
|
||||
}) => {
|
||||
@@ -49,6 +51,7 @@ const CartItem = ({
|
||||
setQuantity(Number(e.target.value))
|
||||
}
|
||||
}
|
||||
|
||||
const handleBlur = () => {
|
||||
const val = Number(quantity)
|
||||
|
||||
@@ -56,6 +59,7 @@ const CartItem = ({
|
||||
updateQuantity(val)
|
||||
}
|
||||
}
|
||||
|
||||
const increaseQuantity = (n = 1) => {
|
||||
const val = Number(quantity) + n
|
||||
|
||||
@@ -64,6 +68,7 @@ const CartItem = ({
|
||||
updateQuantity(val)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemove = async () => {
|
||||
setRemoving(true)
|
||||
|
||||
@@ -87,75 +92,100 @@ const CartItem = ({
|
||||
|
||||
return (
|
||||
<li
|
||||
className={cn('flex flex-row space-x-8 py-8', {
|
||||
'opacity-75 pointer-events-none': removing,
|
||||
className={cn(s.root, {
|
||||
'opacity-50 pointer-events-none': removing,
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
<div className="w-16 h-16 bg-violet relative overflow-hidden cursor-pointer">
|
||||
<Link href={`/product/${item.path}`}>
|
||||
<Image
|
||||
onClick={() => closeSidebarIfPresent()}
|
||||
className={s.productImage}
|
||||
width={150}
|
||||
height={150}
|
||||
src={item.variant.image!.url}
|
||||
alt={item.variant.image!.altText}
|
||||
unoptimized
|
||||
/>
|
||||
</Link>
|
||||
<div className="flex flex-row space-x-4 py-4">
|
||||
<div className="w-16 h-16 bg-violet relative overflow-hidden cursor-pointer">
|
||||
<Link href={`/product/${item.path}`}>
|
||||
<Image
|
||||
onClick={() => closeSidebarIfPresent()}
|
||||
className={s.productImage}
|
||||
width={150}
|
||||
height={150}
|
||||
src={item.variant.image!.url}
|
||||
alt={item.variant.image!.altText}
|
||||
unoptimized
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col text-base">
|
||||
<Link href={`/product/${item.path}`}>
|
||||
<span
|
||||
className="font-medium cursor-pointer leading-6"
|
||||
onClick={() => closeSidebarIfPresent()}
|
||||
>
|
||||
{item.name}
|
||||
</span>
|
||||
</Link>
|
||||
{options && options.length > 0 ? (
|
||||
<div className="">
|
||||
{options.map((option: ItemOption, i: number) => (
|
||||
<div
|
||||
key={`${item.id}-${option.name}`}
|
||||
className="text-sm font-semibold text-accent-7 inline-flex items-center justify-center"
|
||||
>
|
||||
{option.name}
|
||||
{option.name === 'Color' ? (
|
||||
<span
|
||||
className="mx-2 rounded-full bg-transparent border w-5 h-5 p-1 text-accent-9 inline-flex items-center justify-center overflow-hidden"
|
||||
style={{
|
||||
backgroundColor: `${option.value}`,
|
||||
}}
|
||||
></span>
|
||||
) : (
|
||||
<span className="mx-2 rounded-full bg-transparent border h-5 p-1 text-accent-9 inline-flex items-center justify-center overflow-hidden">
|
||||
{option.value}
|
||||
</span>
|
||||
)}
|
||||
{i === options.length - 1 ? '' : <span className="mr-3" />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-col justify-between space-y-2 text-sm">
|
||||
<span>{price}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col text-base">
|
||||
<Link href={`/product/${item.path}`}>
|
||||
<span
|
||||
className="font-bold text-lg cursor-pointer leading-6"
|
||||
onClick={() => closeSidebarIfPresent()}
|
||||
>
|
||||
{item.name}
|
||||
</span>
|
||||
</Link>
|
||||
{options && options.length > 0 ? (
|
||||
<div className="">
|
||||
{options.map((option: ItemOption, i: number) => (
|
||||
<span
|
||||
key={`${item.id}-${option.name}`}
|
||||
className="text-sm font-semibold text-accents-7"
|
||||
>
|
||||
{option.value}
|
||||
{i === options.length - 1 ? '' : ', '}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-center mt-3">
|
||||
<button type="button" onClick={() => increaseQuantity(-1)}>
|
||||
<Minus width={18} height={18} />
|
||||
{!noEdit ? (
|
||||
<div className="flex flex-row h-9">
|
||||
<button className={s.actions} onClick={handleRemove}>
|
||||
<Cross width={20} height={20} />
|
||||
</button>
|
||||
<label>
|
||||
<label className="w-full border-accent-3 border ml-2">
|
||||
<input
|
||||
type="number"
|
||||
max={99}
|
||||
min={0}
|
||||
className={s.quantity}
|
||||
className="bg-transparent px-4 w-full h-full focus:outline-none"
|
||||
value={quantity}
|
||||
onChange={handleQuantity}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</label>
|
||||
<button type="button" onClick={() => increaseQuantity(1)}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => increaseQuantity(-1)}
|
||||
className={s.actions}
|
||||
style={{ marginLeft: '-1px' }}
|
||||
>
|
||||
<Minus width={18} height={18} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => increaseQuantity(1)}
|
||||
className={cn(s.actions)}
|
||||
style={{ marginLeft: '-1px' }}
|
||||
>
|
||||
<Plus width={18} height={18} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-between space-y-2 text-base">
|
||||
<span>{price}</span>
|
||||
<button
|
||||
className="flex justify-end outline-none"
|
||||
onClick={handleRemove}
|
||||
>
|
||||
<Trash />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div>x{quantity}</div>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
.root {
|
||||
@apply h-full flex flex-col;
|
||||
@apply h-full flex flex-col relative w-full relative;
|
||||
}
|
||||
|
||||
.root.empty {
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { FC } from 'react'
|
||||
import cn from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import CartItem from '../CartItem'
|
||||
import { FC } from 'react'
|
||||
import s from './CartSidebarView.module.css'
|
||||
import CartItem from '../CartItem'
|
||||
import { Button } from '@components/ui'
|
||||
import { UserNav } from '@components/common'
|
||||
import { useUI } from '@components/ui/context'
|
||||
@@ -11,7 +11,7 @@ import useCart from '@framework/cart/use-cart'
|
||||
import usePrice from '@framework/product/use-price'
|
||||
|
||||
const CartSidebarView: FC = () => {
|
||||
const { closeSidebar } = useUI()
|
||||
const { closeSidebar, setSidebarView } = useUI()
|
||||
const { data, isLoading, isEmpty } = useCart()
|
||||
|
||||
const { price: subTotal } = usePrice(
|
||||
@@ -27,6 +27,7 @@ const CartSidebarView: FC = () => {
|
||||
}
|
||||
)
|
||||
const handleClose = () => closeSidebar()
|
||||
const goToCheckout = () => setSidebarView('CHECKOUT_VIEW')
|
||||
|
||||
const error = null
|
||||
const success = null
|
||||
@@ -43,9 +44,12 @@ const CartSidebarView: FC = () => {
|
||||
<button
|
||||
onClick={handleClose}
|
||||
aria-label="Close panel"
|
||||
className="hover:text-gray-500 transition ease-in-out duration-150"
|
||||
className="hover:text-gray-500 transition ease-in-out duration-150 flex items-center focus:outline-none"
|
||||
>
|
||||
<Cross className="h-6 w-6" />
|
||||
<span className="ml-2 text-accent-7 text-xs hover:text-gray-500">
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
@@ -62,7 +66,7 @@ const CartSidebarView: FC = () => {
|
||||
<h2 className="pt-6 text-2xl font-bold tracking-wide text-center">
|
||||
Your cart is empty
|
||||
</h2>
|
||||
<p className="text-accents-3 px-10 text-center pt-2">
|
||||
<p className="text-accent-3 px-10 text-center pt-2">
|
||||
Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.
|
||||
</p>
|
||||
</div>
|
||||
@@ -90,13 +94,13 @@ const CartSidebarView: FC = () => {
|
||||
<div className="px-4 sm:px-6 flex-1">
|
||||
<Link href="/cart">
|
||||
<h2
|
||||
className="pt-1 pb-4 text-2xl leading-7 font-bold text-base tracking-wide cursor-pointer inline-block"
|
||||
className="pt-1 pb-8 text-2xl font-semibold tracking-wide cursor-pointer inline-block"
|
||||
onClick={handleClose}
|
||||
>
|
||||
My Cart
|
||||
</h2>
|
||||
</Link>
|
||||
<ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3">
|
||||
<ul className="py-4 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accent-3 border-accent-3">
|
||||
{data!.lineItems.map((item: any) => (
|
||||
<CartItem
|
||||
key={item.id}
|
||||
@@ -107,30 +111,41 @@ const CartSidebarView: FC = () => {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0 px-4 py-5 sm:px-6">
|
||||
<div className="border-t border-accents-3">
|
||||
<ul className="py-3">
|
||||
<li className="flex justify-between py-1">
|
||||
<span>Subtotal</span>
|
||||
<span>{subTotal}</span>
|
||||
</li>
|
||||
<li className="flex justify-between py-1">
|
||||
<span>Taxes</span>
|
||||
<span>Calculated at checkout</span>
|
||||
</li>
|
||||
<li className="flex justify-between py-1">
|
||||
<span>Estimated Shipping</span>
|
||||
<span className="font-bold tracking-wide">FREE</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex justify-between border-t border-accents-3 py-3 font-bold mb-10">
|
||||
<span>Total</span>
|
||||
<span>{total}</span>
|
||||
</div>
|
||||
<div className="flex-shrink-0 px-6 py-6 sm:px-6 sticky z-20 bottom-0 w-full right-0 left-0 bg-accent-0 shadow-outline-normal text-sm">
|
||||
<ul className="pb-2">
|
||||
<li className="flex justify-between py-1">
|
||||
<span>Subtotal</span>
|
||||
<span>{subTotal}</span>
|
||||
</li>
|
||||
<li className="flex justify-between py-1">
|
||||
<span>Taxes</span>
|
||||
<span>Calculated at checkout</span>
|
||||
</li>
|
||||
<li className="flex justify-between py-1">
|
||||
<span>Shipping</span>
|
||||
<span className="font-bold tracking-wide">FREE</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex justify-between border-t border-accent-3 py-3 font-bold mb-2">
|
||||
<span>Total</span>
|
||||
<span>{total}</span>
|
||||
</div>
|
||||
<div>
|
||||
{process.env.COMMERCE_CHECKOUT_ENABLED ? (
|
||||
<Button
|
||||
Component="a"
|
||||
width="100%"
|
||||
variant="ghost"
|
||||
onClick={goToCheckout}
|
||||
>
|
||||
Continue to Checkout
|
||||
</Button>
|
||||
) : (
|
||||
<Button href="/checkout" Component="a" width="100%">
|
||||
Proceed to Checkout
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<Button href="/checkout" Component="a" width="100%">
|
||||
Proceed to Checkout
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
Reference in New Issue
Block a user