This commit is contained in:
Luis Alvarez
2020-10-27 04:48:49 -05:00
112 changed files with 311 additions and 8396 deletions

View File

@@ -1,9 +1,7 @@
import { FC, useEffect, useState, useCallback } from 'react'
import { validate } from 'email-validator'
import { Info } from '@components/icons'
import { useUI } from '@components/ui/context'
import { Logo, Button, Input } from '@components/ui'
import useSignup from '@lib/bigcommerce/use-signup'
interface Props {}
@@ -15,27 +13,15 @@ const ForgotPassword: FC<Props> = () => {
const [dirty, setDirty] = useState(false)
const [disabled, setDisabled] = useState(false)
const signup = useSignup()
const { setModalView, closeModal } = useUI()
const handleSignup = async () => {
const handleResetPassword = async (e: React.SyntheticEvent<EventTarget>) => {
e.preventDefault()
if (!dirty && !disabled) {
setDirty(true)
handleValidation()
}
// try {
// setLoading(true)
// setMessage('')
// await signup({
// email,
// })
// setLoading(false)
// closeModal()
// } catch ({ errors }) {
// setMessage(errors[0].message)
// setLoading(false)
// }
}
const handleValidation = useCallback(() => {
@@ -50,7 +36,10 @@ const ForgotPassword: FC<Props> = () => {
}, [handleValidation])
return (
<div className="w-80 flex flex-col justify-between p-3">
<form
onSubmit={handleResetPassword}
className="w-80 flex flex-col justify-between p-3"
>
<div className="flex justify-center pb-12 ">
<Logo width="64px" height="64px" />
</div>
@@ -63,7 +52,7 @@ const ForgotPassword: FC<Props> = () => {
<div className="pt-2 w-full flex flex-col">
<Button
variant="slim"
onClick={() => handleSignup()}
type="submit"
loading={loading}
disabled={disabled}
>
@@ -82,7 +71,7 @@ const ForgotPassword: FC<Props> = () => {
</a>
</span>
</div>
</div>
</form>
)
}

View File

@@ -1,6 +1,6 @@
import { FC, useEffect, useState, useCallback } from 'react'
import { Logo, Modal, Button, Input } from '@components/ui'
import useLogin from '@lib/bigcommerce/use-login'
import useLogin from '@bigcommerce/storefront-data-hooks/use-login'
import { useUI } from '@components/ui/context'
import { validate } from 'email-validator'
@@ -18,7 +18,9 @@ const LoginView: FC<Props> = () => {
const login = useLogin()
const handleLogin = async () => {
const handleLogin = async (e: React.SyntheticEvent<EventTarget>) => {
e.preventDefault()
if (!dirty && !disabled) {
setDirty(true)
handleValidation()
@@ -54,7 +56,10 @@ const LoginView: FC<Props> = () => {
}, [handleValidation])
return (
<div className="w-80 flex flex-col justify-between p-3">
<form
onSubmit={handleLogin}
className="w-80 flex flex-col justify-between p-3"
>
<div className="flex justify-center pb-12 ">
<Logo width="64px" height="64px" />
</div>
@@ -75,7 +80,7 @@ const LoginView: FC<Props> = () => {
<Button
variant="slim"
onClick={() => handleLogin()}
type="submit"
loading={loading}
disabled={disabled}
>
@@ -92,7 +97,7 @@ const LoginView: FC<Props> = () => {
</a>
</div>
</div>
</div>
</form>
)
}

View File

@@ -3,7 +3,7 @@ import { validate } from 'email-validator'
import { Info } from '@components/icons'
import { useUI } from '@components/ui/context'
import { Logo, Button, Input } from '@components/ui'
import useSignup from '@lib/bigcommerce/use-signup'
import useSignup from '@bigcommerce/storefront-data-hooks/use-signup'
interface Props {}
@@ -21,7 +21,9 @@ const SignUpView: FC<Props> = () => {
const signup = useSignup()
const { setModalView, closeModal } = useUI()
const handleSignup = async () => {
const handleSignup = async (e: React.SyntheticEvent<EventTarget>) => {
e.preventDefault()
if (!dirty && !disabled) {
setDirty(true)
handleValidation()
@@ -59,7 +61,10 @@ const SignUpView: FC<Props> = () => {
}, [handleValidation])
return (
<div className="w-80 flex flex-col justify-between p-3">
<form
onSubmit={handleSignup}
className="w-80 flex flex-col justify-between p-3"
>
<div className="flex justify-center pb-12 ">
<Logo width="64px" height="64px" />
</div>
@@ -83,7 +88,7 @@ const SignUpView: FC<Props> = () => {
<div className="pt-2 w-full flex flex-col">
<Button
variant="slim"
onClick={() => handleSignup()}
type="submit"
loading={loading}
disabled={disabled}
>
@@ -102,7 +107,7 @@ const SignUpView: FC<Props> = () => {
</a>
</span>
</div>
</div>
</form>
)
}

View File

@@ -3,9 +3,9 @@ import cn from 'classnames'
import Image from 'next/image'
import Link from 'next/link'
import { Trash, Plus, Minus } from '@components/icons'
import usePrice from '@lib/bigcommerce/use-price'
import useUpdateItem from '@lib/bigcommerce/cart/use-update-item'
import useRemoveItem from '@lib/bigcommerce/cart/use-remove-item'
import usePrice from '@bigcommerce/storefront-data-hooks/use-price'
import useUpdateItem from '@bigcommerce/storefront-data-hooks/cart/use-update-item'
import useRemoveItem from '@bigcommerce/storefront-data-hooks/cart/use-remove-item'
import s from './CartItem.module.css'
const CartItem = ({

View File

@@ -2,10 +2,10 @@ import { FC } from 'react'
import cn from 'classnames'
import { UserNav } from '@components/core'
import { Button } from '@components/ui'
import { ArrowLeft, Bag, Cross, Check } from '@components/icons'
import { Bag, Cross, Check } from '@components/icons'
import { useUI } from '@components/ui/context'
import useCart from '@lib/bigcommerce/cart/use-cart'
import usePrice from '@lib/bigcommerce/use-price'
import useCart from '@bigcommerce/storefront-data-hooks/cart/use-cart'
import usePrice from '@bigcommerce/storefront-data-hooks/use-price'
import CartItem from '../CartItem'
import s from './CartSidebarView.module.css'
@@ -47,11 +47,11 @@ const CartSidebarView: FC = () => {
aria-label="Close panel"
className="hover:text-gray-500 transition ease-in-out duration-150"
>
<ArrowLeft className="h-6 w-6" />
<Cross className="h-6 w-6" />
</button>
</div>
<div className="space-y-1">
<UserNav />
<UserNav className="" />
</div>
</div>
</header>

View File

@@ -0,0 +1,9 @@
.link {
& > svg {
@apply transform duration-75 ease-linear;
}
&:hover > svg {
@apply scale-110;
}
}

View File

@@ -2,12 +2,12 @@ import { FC } from 'react'
import cn from 'classnames'
import Link from 'next/link'
import { useRouter } from 'next/router'
import type { Page } from '@lib/bigcommerce/api/operations/get-all-pages'
import type { Page } from '@bigcommerce/storefront-data-hooks/api/operations/get-all-pages'
import getSlug from '@utils/get-slug'
import { Github } from '@components/icons'
import { Logo, Container } from '@components/ui'
import { I18nWidget } from '@components/core'
import s from './Footer.module.css'
interface Props {
className?: string
children?: any
@@ -83,7 +83,9 @@ const Footer: FC<Props> = ({ className, pages }) => {
</div>
<div className="col-span-1 lg:col-span-6 flex items-start lg:justify-end text-primary">
<div className="flex space-x-6 items-center h-10">
<Github />
<a href="https://github.com/vercel/commerce" className={s.link}>
<Github />
</a>
<I18nWidget />
</div>
</div>

View File

@@ -4,20 +4,20 @@
@screen md {
@apply flex-row;
}
}
.asideWrapper {
@apply pr-3 w-full relative;
& .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;
@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;
}
}
}

View File

@@ -6,21 +6,52 @@ import { Menu } from '@headlessui/react'
import { DoubleChevron } from '@components/icons'
import s from './I18nWidget.module.css'
const LOCALES_MAP: Record<string, string> = {
es: 'Español',
'en-US': 'English',
interface LOCALE_DATA {
name: string
img: {
filename: string
alt: string
}
}
const LOCALES_MAP: Record<string, LOCALE_DATA> = {
es: {
name: 'Español',
img: {
filename: 'flag-es-co.svg',
alt: 'Bandera Colombiana',
},
},
'en-US': {
name: 'English',
img: {
filename: 'flag-en-us.svg',
alt: 'US Flag',
},
},
}
const I18nWidget: FC = () => {
const { locale, locales, defaultLocale = 'en-US' } = useRouter()
const {
locale,
locales,
defaultLocale = 'en-US',
asPath: currentPath,
} = useRouter()
const options = locales?.filter((val) => val !== locale)
const currentLocale = locale || defaultLocale
return (
<nav className={s.root}>
<Menu>
<Menu.Button className={s.button} aria-label="Language selector">
<img className="mr-2" src="/flag-us.png" alt="US Flag" />
<span className="mr-2">{LOCALES_MAP[locale || defaultLocale]}</span>
<img
className="block mr-2 w-5"
src={`/${LOCALES_MAP[currentLocale].img.filename}`}
alt={LOCALES_MAP[currentLocale].img.alt}
/>
<span className="mr-2">{LOCALES_MAP[currentLocale].name}</span>
{options && (
<span>
<DoubleChevron />
@@ -33,9 +64,9 @@ const I18nWidget: FC = () => {
{options.map((locale) => (
<Menu.Item key={locale}>
{({ active }) => (
<Link href="/" locale={locale}>
<Link href={currentPath} locale={locale}>
<a className={cn(s.item, { [s.active]: active })}>
{LOCALES_MAP[locale]}
{LOCALES_MAP[locale].name}
</a>
</Link>
)}

View File

@@ -1,8 +1,8 @@
import { FC, useCallback, useEffect, useState } from 'react'
import cn from 'classnames'
import { useRouter } from 'next/router'
import type { Page } from '@lib/bigcommerce/api/operations/get-all-pages'
import { CommerceProvider } from '@lib/bigcommerce'
import type { Page } from '@bigcommerce/storefront-data-hooks/api/operations/get-all-pages'
import { CommerceProvider } from '@bigcommerce/storefront-data-hooks'
import { CartSidebarView } from '@components/cart'
import { Container, Sidebar, Button, Modal, Toast } from '@components/ui'
import { Navbar, Featurebar, Footer } from '@components/core'

View File

@@ -4,8 +4,9 @@ import { useTheme } from 'next-themes'
import cn from 'classnames'
import s from './DropdownMenu.module.css'
import { Moon, Sun } from '@components/icons'
import { useUI } from '@components/ui/context'
import { Menu, Transition } from '@headlessui/react'
import useLogout from '@lib/bigcommerce/use-logout'
import useLogout from '@bigcommerce/storefront-data-hooks/use-logout'
import { useRouter } from 'next/router'
interface DropdownMenuProps {
@@ -32,6 +33,8 @@ const DropdownMenu: FC<DropdownMenuProps> = ({ open = false }) => {
const logout = useLogout()
const { pathname } = useRouter()
const { closeSidebarIfPresent } = useUI()
return (
<Transition
show={open}
@@ -51,6 +54,7 @@ const DropdownMenu: FC<DropdownMenuProps> = ({ open = false }) => {
className={cn(s.link, {
[s.active]: pathname === href,
})}
onClick={closeSidebarIfPresent}
>
{name}
</a>

View File

@@ -1,14 +1,15 @@
import { FC } from 'react'
import Link from 'next/link'
import cn from 'classnames'
import s from './UserNav.module.css'
import { FC } from 'react'
import useCart from '@bigcommerce/storefront-data-hooks/cart/use-cart'
import useCustomer from '@bigcommerce/storefront-data-hooks/use-customer'
import { Menu } from '@headlessui/react'
import { Heart, Bag } from '@components/icons'
import { Avatar } from '@components/core'
import { useUI } from '@components/ui/context'
import DropdownMenu from './DropdownMenu'
import { Menu } from '@headlessui/react'
import useCart from '@lib/bigcommerce/cart/use-cart'
import useCustomer from '@lib/bigcommerce/use-customer'
import s from './UserNav.module.css'
interface Props {
className?: string
}
@@ -21,22 +22,19 @@ const UserNav: FC<Props> = ({ className, children, ...props }) => {
const { data } = useCart()
const { data: customer } = useCustomer()
const { openSidebar, closeSidebar, displaySidebar, openModal } = useUI()
const { toggleSidebar, closeSidebarIfPresent, openModal } = useUI()
const itemsCount = Object.values(data?.line_items ?? {}).reduce(countItems, 0)
return (
<nav className={cn(s.root, className)}>
<div className={s.mainContainer}>
<ul className={s.list}>
<li
className={s.item}
onClick={(e) => (displaySidebar ? closeSidebar() : openSidebar())}
>
<li className={s.item} onClick={toggleSidebar}>
<Bag />
{itemsCount > 0 && <span className={s.bagCount}>{itemsCount}</span>}
</li>
<li className={s.item}>
<Link href="/wishlist">
<a>
<a onClick={closeSidebarIfPresent}>
<Heart />
</a>
</Link>

View File

@@ -1,5 +1,6 @@
.root {
@apply relative w-full box-border overflow-hidden bg-no-repeat bg-center bg-cover transition ease-linear cursor-pointer;
@apply relative max-h-full w-full box-border overflow-hidden bg-no-repeat bg-center bg-cover transition ease-linear cursor-pointer;
height: 100% !important;
&:hover {
& .squareBg:before {
@@ -119,17 +120,19 @@
}
.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 z-10;
@apply w-10 h-10 flex ml-auto items-center justify-center bg-primary text-primary font-semibold text-xs leading-6 cursor-pointer;
}
.imageContainer {
@apply absolute z-10 inset-0 flex items-center justify-center;
& > div {
@apply h-full;
& > div {
@apply h-full;
padding-bottom: 0 !important;
height: 100%;
margin: 0 auto;
}
}
}
.product-image {
height: 120% !important;
top: -10% !important;
}

View File

@@ -1,8 +1,8 @@
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 type { ProductNode } from '@bigcommerce/storefront-data-hooks/api/operations/get-all-products'
import usePrice from '@bigcommerce/storefront-data-hooks/use-price'
import { EnhancedImage } from '@components/core'
import s from './ProductCard.module.css'
import WishlistButton from '@components/wishlist/WishlistButton'
@@ -31,56 +31,56 @@ const ProductCard: FC<Props> = ({
currencyCode: p.prices?.price?.currencyCode!,
})
if (variant === 'slim') {
return (
<div className="relative overflow-hidden box-border">
<div className="absolute inset-0 flex items-center justify-end mr-8 z-20">
<span className="bg-black text-white inline-block p-3 font-bold text-xl break-words">
{p.name}
</span>
</div>
<EnhancedImage
src={p.images.edges?.[0]?.node.urlOriginal!}
alt={p.images.edges?.[0]?.node.altText || 'Product Image'}
width={imgWidth}
height={imgHeight}
priority={priority}
quality="90"
/>
</div>
)
}
return (
<Link href={`/product${p.path}`}>
<a
className={cn(s.root, { [s.simple]: variant === 'simple' }, className)}
>
<div className={s.squareBg} />
<div className="flex flex-row justify-between box-border w-full z-20 absolute">
<div className="absolute top-0 left-0 pr-16 max-w-full">
<h3 className={s.productTitle}>
<span>{p.name}</span>
</h3>
<span className={s.productPrice}>{price}</span>
{variant === 'slim' ? (
<div className="relative overflow-hidden box-border">
<div className="absolute inset-0 flex items-center justify-end mr-8 z-20">
<span className="bg-black text-white inline-block p-3 font-bold text-xl break-words">
{p.name}
</span>
</div>
<EnhancedImage
src={p.images.edges?.[0]?.node.urlOriginal!}
alt={p.images.edges?.[0]?.node.altText || 'Product Image'}
width={imgWidth}
height={imgHeight}
priority={priority}
quality="90"
/>
</div>
<WishlistButton
className={s.wishlistButton}
productId={p.entityId}
variant={p.variants.edges?.[0]!}
/>
</div>
<div className={cn(s.imageContainer)}>
<EnhancedImage
alt={p.name}
className={cn('w-full object-cover', s['product-image'])}
src={src}
width={imgWidth}
height={imgHeight}
priority={priority}
quality="90"
/>
</div>
) : (
<>
<div className={s.squareBg} />
<div className="flex flex-row justify-between box-border w-full z-20 absolute">
<div className="absolute top-0 left-0 pr-16 max-w-full">
<h3 className={s.productTitle}>
<span>{p.name}</span>
</h3>
<span className={s.productPrice}>{price}</span>
</div>
<WishlistButton
className={s.wishlistButton}
productId={p.entityId}
variant={p.variants.edges?.[0]!}
/>
</div>
<div className={s.imageContainer}>
<EnhancedImage
alt={p.name}
className={cn('w-full object-cover', s['product-image'])}
src={src}
width={imgWidth}
height={imgHeight}
priority={priority}
quality="90"
/>
</div>
</>
)}
</a>
</Link>
)

View File

@@ -9,8 +9,8 @@ import { Swatch, ProductSlider } from '@components/product'
import { Button, Container } from '@components/ui'
import { HTMLContent } from '@components/core'
import useAddItem from '@lib/bigcommerce/cart/use-add-item'
import type { ProductNode } from '@lib/bigcommerce/api/operations/get-product'
import useAddItem from '@bigcommerce/storefront-data-hooks/cart/use-add-item'
import type { ProductNode } from '@bigcommerce/storefront-data-hooks/api/operations/get-product'
import {
getCurrentVariant,
getProductOptions,

View File

@@ -1,4 +1,4 @@
import type { ProductNode } from '@lib/bigcommerce/api/operations/get-product'
import type { ProductNode } from '@bigcommerce/storefront-data-hooks/api/operations/get-product'
export type SelectedOptions = {
size: string | null

View File

@@ -131,6 +131,12 @@ export const UIProvider: FC = (props) => {
const openSidebar = () => dispatch({ type: 'OPEN_SIDEBAR' })
const closeSidebar = () => dispatch({ type: 'CLOSE_SIDEBAR' })
const toggleSidebar = () =>
state.displaySidebar
? dispatch({ type: 'CLOSE_SIDEBAR' })
: dispatch({ type: 'OPEN_SIDEBAR' })
const closeSidebarIfPresent = () =>
state.displaySidebar && dispatch({ type: 'CLOSE_SIDEBAR' })
const openDropdown = () => dispatch({ type: 'OPEN_DROPDOWN' })
const closeDropdown = () => dispatch({ type: 'CLOSE_DROPDOWN' })
@@ -149,6 +155,8 @@ export const UIProvider: FC = (props) => {
...state,
openSidebar,
closeSidebar,
toggleSidebar,
closeSidebarIfPresent,
openDropdown,
closeDropdown,
openModal,

View File

@@ -1,10 +1,10 @@
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 type { ProductNode } from '@bigcommerce/storefront-data-hooks/api/operations/get-all-products'
import useAddItem from '@bigcommerce/storefront-data-hooks/wishlist/use-add-item'
import useRemoveItem from '@bigcommerce/storefront-data-hooks/wishlist/use-remove-item'
import useWishlist from '@bigcommerce/storefront-data-hooks/wishlist/use-wishlist'
import useCustomer from '@bigcommerce/storefront-data-hooks/use-customer'
import { Heart } from '@components/icons'
import { useUI } from '@components/ui/context'
@@ -62,9 +62,10 @@ const WishlistButton: FC<Props> = ({
return (
<button
{...props}
aria-label="Add to wishlist"
className={cn({ 'opacity-50': loading }, className)}
onClick={handleWishlistChange}
{...props}
>
<Heart fill={itemInWishlist ? 'var(--pink)' : 'none'} />
</button>