mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
Merge branch 'master' of github.com:okbel/e-comm-example
This commit is contained in:
@@ -70,8 +70,8 @@ const LoginView: FC<Props> = () => {
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
<Input placeholder="Email" onChange={setEmail} type="email" />
|
||||
<Input placeholder="Password" onChange={setPassword} type="password" />
|
||||
<Input type="email" placeholder="Email" onChange={setEmail} />
|
||||
<Input type="password" placeholder="Password" onChange={setPassword} />
|
||||
|
||||
<Button
|
||||
variant="slim"
|
||||
|
@@ -69,8 +69,8 @@ const SignUpView: FC<Props> = () => {
|
||||
)}
|
||||
<Input placeholder="First Name" onChange={setFirstName} />
|
||||
<Input placeholder="Last Name" onChange={setLastName} />
|
||||
<Input placeholder="Email" onChange={setEmail} type="email" />
|
||||
<Input placeholder="Password" onChange={setPassword} type="password" />
|
||||
<Input type="email" placeholder="Email" onChange={setEmail} />
|
||||
<Input type="password" placeholder="Password" onChange={setPassword} />
|
||||
<span className="text-accents-8">
|
||||
<span className="inline-block align-middle ">
|
||||
<Info width="15" height="15" />
|
||||
|
@@ -0,0 +1,23 @@
|
||||
.root {
|
||||
@apply py-12 flex flex-col w-full px-6;
|
||||
|
||||
@screen md {
|
||||
@apply flex-row;
|
||||
}
|
||||
}
|
||||
|
||||
.asideWrapper {
|
||||
@apply pr-3 w-full relative;
|
||||
|
||||
@screen md {
|
||||
@apply w-48;
|
||||
}
|
||||
}
|
||||
|
||||
.aside {
|
||||
@apply flex flex-row w-full justify-around mb-12;
|
||||
|
||||
@screen md {
|
||||
@apply mb-0 block sticky top-32;
|
||||
}
|
||||
}
|
66
components/core/HomeAllProductsGrid/HomeAllProductsGrid.tsx
Normal file
66
components/core/HomeAllProductsGrid/HomeAllProductsGrid.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { getCategoryPath, getDesignerPath } from '@utils/search'
|
||||
import { Grid } from '@components/ui'
|
||||
import { ProductCard } from '@components/product'
|
||||
import s from './HomeAllProductsGrid.module.css'
|
||||
|
||||
interface Props {
|
||||
categories?: any
|
||||
brands?: any
|
||||
newestProducts?: any
|
||||
}
|
||||
|
||||
const Head: FC<Props> = ({ categories, brands, newestProducts }) => {
|
||||
return (
|
||||
<div className={s.root}>
|
||||
<div className={s.asideWrapper}>
|
||||
<div className={s.aside}>
|
||||
<ul className="mb-10">
|
||||
<li className="py-1 text-base font-bold tracking-wide">
|
||||
<Link href={getCategoryPath('')}>
|
||||
<a>All Categories</a>
|
||||
</Link>
|
||||
</li>
|
||||
{categories.map((cat: any) => (
|
||||
<li key={cat.path} className="py-1 text-accents-8">
|
||||
<Link href={getCategoryPath(cat.path)}>
|
||||
<a>{cat.name}</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul className="">
|
||||
<li className="py-1 text-base font-bold tracking-wide">
|
||||
<Link href={getDesignerPath('')}>
|
||||
<a>All Designers</a>
|
||||
</Link>
|
||||
</li>
|
||||
{brands.flatMap(({ node }: any) => (
|
||||
<li key={node.path} className="py-1 text-accents-8">
|
||||
<Link href={getDesignerPath(node.path)}>
|
||||
<a>{node.name}</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Grid layout="normal">
|
||||
{newestProducts.map(({ node }: any) => (
|
||||
<ProductCard
|
||||
key={node.path}
|
||||
product={node}
|
||||
variant="simple"
|
||||
imgWidth={480}
|
||||
imgHeight={480}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Head
|
1
components/core/HomeAllProductsGrid/index.ts
Normal file
1
components/core/HomeAllProductsGrid/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './HomeAllProductsGrid'
|
@@ -119,7 +119,7 @@
|
||||
}
|
||||
|
||||
.wishlistButton {
|
||||
@apply w-10 h-10 flex ml-auto flex items-center justify-center bg-primary text-primary font-semibold text-xs leading-6 cursor-pointer;
|
||||
@apply w-10 h-10 flex ml-auto flex items-center justify-center bg-primary text-primary font-semibold text-xs leading-6 cursor-pointer z-10;
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
|
@@ -1,15 +1,14 @@
|
||||
import React, { FC, ReactNode, Component } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import cn from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import type { ProductNode } from '@lib/bigcommerce/api/operations/get-all-products'
|
||||
import usePrice from '@lib/bigcommerce/use-price'
|
||||
import { Heart } from '@components/icons'
|
||||
import { EnhancedImage } from '@components/core'
|
||||
import s from './ProductCard.module.css'
|
||||
import WishlistButton from '@components/wishlist/WishlistButton'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: ReactNode[] | Component[] | any[]
|
||||
product: ProductNode
|
||||
variant?: 'slim' | 'simple'
|
||||
imgWidth: number | string
|
||||
@@ -25,7 +24,7 @@ const ProductCard: FC<Props> = ({
|
||||
imgHeight,
|
||||
priority,
|
||||
}) => {
|
||||
const src = p.images.edges?.[0]?.node.urlOriginal!
|
||||
const src = p.images.edges?.[0]?.node?.urlOriginal!
|
||||
const { price } = usePrice({
|
||||
amount: p.prices?.price?.value,
|
||||
baseAmount: p.prices?.retailPrice?.value,
|
||||
@@ -53,7 +52,7 @@ const ProductCard: FC<Props> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Link href={`product${p.path}`}>
|
||||
<Link href={`/product${p.path}`}>
|
||||
<a
|
||||
className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}
|
||||
>
|
||||
@@ -65,9 +64,11 @@ const ProductCard: FC<Props> = ({
|
||||
</h3>
|
||||
<span className={s.productPrice}>{price}</span>
|
||||
</div>
|
||||
<div className={s.wishlistButton}>
|
||||
<Heart />
|
||||
</div>
|
||||
<WishlistButton
|
||||
className={s.wishlistButton}
|
||||
productId={p.entityId}
|
||||
variant={p.variants.edges?.[0]!}
|
||||
/>
|
||||
</div>
|
||||
<div className={cn(s.imageContainer)}>
|
||||
<EnhancedImage
|
||||
|
@@ -48,7 +48,8 @@
|
||||
@apply hidden;
|
||||
|
||||
@screen sm {
|
||||
@apply block absolute bottom-6 left-1/2 -translate-x-1/2 transform;
|
||||
@apply block absolute bottom-6 left-1/2;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
|
74
components/wishlist/WishlistButton/WishlistButton.tsx
Normal file
74
components/wishlist/WishlistButton/WishlistButton.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React, { FC, useState } from 'react'
|
||||
import cn from 'classnames'
|
||||
import type { ProductNode } from '@lib/bigcommerce/api/operations/get-all-products'
|
||||
import useAddItem from '@lib/bigcommerce/wishlist/use-add-item'
|
||||
import useRemoveItem from '@lib/bigcommerce/wishlist/use-remove-item'
|
||||
import useWishlist from '@lib/bigcommerce/wishlist/use-wishlist'
|
||||
import useCustomer from '@lib/bigcommerce/use-customer'
|
||||
import { Heart } from '@components/icons'
|
||||
import { useUI } from '@components/ui/context'
|
||||
|
||||
type Props = {
|
||||
productId: number
|
||||
variant: NonNullable<ProductNode['variants']['edges']>[0]
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement>
|
||||
|
||||
const WishlistButton: FC<Props> = ({
|
||||
productId,
|
||||
variant,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
const addItem = useAddItem()
|
||||
const removeItem = useRemoveItem()
|
||||
const { data } = useWishlist()
|
||||
const { data: customer } = useCustomer()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const { openModal, setModalView } = useUI()
|
||||
const itemInWishlist = data?.items?.find(
|
||||
(item) =>
|
||||
item.product_id === productId &&
|
||||
item.variant_id === variant?.node.entityId
|
||||
)
|
||||
|
||||
const handleWishlistChange = async (e: any) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (loading) return
|
||||
|
||||
// A login is required before adding an item to the wishlist
|
||||
if (!customer) {
|
||||
setModalView('LOGIN_VIEW')
|
||||
return openModal()
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
if (itemInWishlist) {
|
||||
await removeItem({ id: itemInWishlist.id! })
|
||||
} else {
|
||||
await addItem({
|
||||
productId,
|
||||
variantId: variant?.node.entityId!,
|
||||
})
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
} catch (err) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={cn({ 'opacity-50': loading }, className)}
|
||||
onClick={handleWishlistChange}
|
||||
>
|
||||
<Heart fill={itemInWishlist ? 'var(--pink)' : 'none'} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default WishlistButton
|
1
components/wishlist/WishlistButton/index.ts
Normal file
1
components/wishlist/WishlistButton/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './WishlistButton'
|
Reference in New Issue
Block a user