diff --git a/README.md b/README.md index 189192216..7c69eb09d 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ That's it! Every provider defines the features that it supports under `framework/{provider}/commerce.config.json` +#### Features Available + +- wishlist +- customCheckout + #### How to turn Features on and off > NOTE: The selected provider should support the feature that you are toggling. (This means that you can't turn wishlist on if the provider doesn't support this functionality out the box) @@ -73,7 +78,8 @@ Every provider defines the features that it supports under `framework/{provider} ```json { "features": { - "wishlist": false + "wishlist": false, + "customCheckout": true } } ``` diff --git a/assets/base.css b/assets/base.css index e63ea1aa4..00081f459 100644 --- a/assets/base.css +++ b/assets/base.css @@ -15,21 +15,28 @@ --cyan: #22b8cf; --green: #37b679; --red: #da3c3c; - --pink: #e64980; --purple: #f81ce5; --blue: #0070f3; - --violet: #5f3dc4; - --violet-light: #7048e8; - --accents-0: #f8f9fa; - --accents-1: #f1f3f5; - --accents-2: #e9ecef; - --accents-3: #dee2e6; - --accents-4: #ced4da; - --accents-5: #adb5bd; - --accents-6: #868e96; - --accents-7: #495057; - --accents-8: #343a40; - --accents-9: #212529; + + --pink: #ff0080; + --pink-light: #ff379c; + + --magenta: #eb367f; + + --violet: #7928ca; + --violet-dark: #4c2889; + + --accent-0: #fff; + --accent-1: #fafafa; + --accent-2: #eaeaea; + --accent-3: #999999; + --accent-4: #888888; + --accent-5: #666666; + --accent-6: #444444; + --accent-7: #333333; + --accent-8: #111111; + --accent-9: #000; + --font-sans: -apple-system, system-ui, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica', sans-serif; } @@ -48,16 +55,16 @@ --text-primary: white; --text-secondary: black; - --accents-0: #212529; - --accents-1: #343a40; - --accents-2: #495057; - --accents-3: #868e96; - --accents-4: #adb5bd; - --accents-5: #ced4da; - --accents-6: #dee2e6; - --accents-7: #e9ecef; - --accents-8: #f1f3f5; - --accents-9: #f8f9fa; + --accent-9: #fff; + --accent-8: #fafafa; + --accent-7: #eaeaea; + --accent-6: #999999; + --accent-5: #888888; + --accent-4: #666666; + --accent-3: #444444; + --accent-2: #333333; + --accent-1: #111111; + --accent-0: #000; } *, @@ -84,6 +91,7 @@ body { -moz-osx-font-smoothing: grayscale; background-color: var(--primary); color: var(--text-primary); + overscroll-behavior-x: none; } body { diff --git a/components/auth/ForgotPassword.tsx b/components/auth/ForgotPassword.tsx index 597ee328e..dbac371c7 100644 --- a/components/auth/ForgotPassword.tsx +++ b/components/auth/ForgotPassword.tsx @@ -61,7 +61,7 @@ const ForgotPassword: FC = () => { - Do you have an account? + Do you have an account? {` `} = () => { Log In
- Don't have an account? + Don't have an account? {` `} = () => { - + {' '} @@ -97,7 +97,7 @@ const SignUpView: FC = () => {
- Do you have an account? + Do you have an account? {` `} { const { closeSidebarIfPresent } = useUI() + const [removing, setRemoving] = useState(false) + const [quantity, setQuantity] = useState(item.quantity) + const removeItem = useRemoveItem() + const updateItem = useUpdateItem({ item }) const { price } = usePrice({ amount: item.variant.price * item.quantity, @@ -33,48 +40,28 @@ const CartItem = ({ currencyCode, }) - const updateItem = useUpdateItem({ item }) - const removeItem = useRemoveItem() - const [quantity, setQuantity] = useState(item.quantity) - const [removing, setRemoving] = useState(false) + const handleChange = async ({ + target: { value }, + }: ChangeEvent) => { + setQuantity(Number(value)) + await updateItem({ quantity: Number(value) }) + } - const updateQuantity = async (val: number) => { + const increaseQuantity = async (n = 1) => { + const val = Number(quantity) + n + setQuantity(val) await updateItem({ quantity: val }) } - const handleQuantity = (e: ChangeEvent) => { - const val = !e.target.value ? '' : Number(e.target.value) - - if (!val || (Number.isInteger(val) && val >= 0)) { - setQuantity(val) - } - } - const handleBlur = () => { - const val = Number(quantity) - - if (val !== item.quantity) { - updateQuantity(val) - } - } - const increaseQuantity = (n = 1) => { - const val = Number(quantity) + n - - if (Number.isInteger(val) && val >= 0) { - setQuantity(val) - updateQuantity(val) - } - } const handleRemove = async () => { setRemoving(true) - try { - // If this action succeeds then there's no need to do `setRemoving(true)` - // because the component will be removed from the view await removeItem(item) } catch (error) { setRemoving(false) } } + // TODO: Add a type for this const options = (item as any).options @@ -87,79 +74,76 @@ const CartItem = ({ return (
  • -
    - - closeSidebarIfPresent()} - className={s.productImage} - width={150} - height={150} - src={item.variant.image!.url} - alt={item.variant.image!.altText} - unoptimized - /> - -
    -
    - - closeSidebarIfPresent()} - > -
    +
    + + closeSidebarIfPresent()} + className={s.productImage} + width={150} + height={150} + src={item.variant.image!.url} + alt={item.variant.image!.altText} + unoptimized + /> + +
    +
    + + closeSidebarIfPresent()} > {item.name} + + + {options && options.length > 0 && ( +
    + {options.map((option: ItemOption, i: number) => ( +
    + {option.name} + {option.name === 'Color' ? ( + + ) : ( + + {option.value} + + )} + {i === options.length - 1 ? '' : } +
    + ))}
    - {item.variant ? {item.variant.name} : ""} - - - {options && options.length > 0 ? ( -
    - {options.map((option: ItemOption, i: number) => ( - - {option.value} - {i === options.length - 1 ? '' : ', '} - - ))} -
    - ) : null} -
    - - - + )} + {variant === 'display' && ( +
    {quantity}x
    + )} +
    +
    + {price}
    -
    - {price} - -
    + {variant === 'default' && ( + increaseQuantity(1)} + decrease={() => increaseQuantity(-1)} + /> + )}
  • ) } diff --git a/components/cart/CartSidebarView/CartSidebarView.module.css b/components/cart/CartSidebarView/CartSidebarView.module.css index 9b94021ad..c9ffbed50 100644 --- a/components/cart/CartSidebarView/CartSidebarView.module.css +++ b/components/cart/CartSidebarView/CartSidebarView.module.css @@ -1,15 +1,11 @@ .root { - @apply h-full flex flex-col; + min-height: 100vh; } .root.empty { @apply bg-secondary text-secondary; } -.root.success { - @apply bg-green text-white; -} - -.root.error { - @apply bg-red text-white; +.lineItemsList { + @apply py-4 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accent-2 border-accent-2; } diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 326390327..128b928a8 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -1,17 +1,17 @@ -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 { Button } from '@components/ui' -import { UserNav } from '@components/common' +import CartItem from '../CartItem' +import { Button, Text } from '@components/ui' import { useUI } from '@components/ui/context' import { Bag, Cross, Check } from '@components/icons' import useCart from '@framework/cart/use-cart' import usePrice from '@framework/product/use-price' +import SidebarLayout from '@components/common/SidebarLayout' const CartSidebarView: FC = () => { - const { closeSidebar } = useUI() + const { closeSidebar, setSidebarView } = useUI() const { data, isLoading, isEmpty } = useCart() const { price: subTotal } = usePrice( @@ -27,33 +27,18 @@ const CartSidebarView: FC = () => { } ) const handleClose = () => closeSidebar() + const goToCheckout = () => setSidebarView('CHECKOUT_VIEW') const error = null const success = null return ( -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - {isLoading || isEmpty ? (
    @@ -62,7 +47,7 @@ const CartSidebarView: FC = () => {

    Your cart is empty

    -

    +

    Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.

    @@ -89,14 +74,11 @@ const CartSidebarView: FC = () => { <>
    -

    + My Cart -

    + -
      +
        {data!.lineItems.map((item: any) => ( {
    -
    -
    -
      -
    • - Subtotal - {subTotal} -
    • -
    • - Taxes - Calculated at checkout -
    • -
    • - Estimated Shipping - FREE -
    • -
    -
    - Total - {total} -
    +
    +
      +
    • + Subtotal + {subTotal} +
    • +
    • + Taxes + Calculated at checkout +
    • +
    • + Shipping + FREE +
    • +
    +
    + Total + {total} +
    +
    + {process.env.COMMERCE_CUSTOMCHECKOUT_ENABLED ? ( + + ) : ( + + )}
    -
    )} -
    + ) } diff --git a/components/checkout/CheckoutSidebarView/CheckoutSidebarView.module.css b/components/checkout/CheckoutSidebarView/CheckoutSidebarView.module.css new file mode 100644 index 000000000..34c1b487c --- /dev/null +++ b/components/checkout/CheckoutSidebarView/CheckoutSidebarView.module.css @@ -0,0 +1,7 @@ +.root { + min-height: calc(100vh - 322px); +} + +.lineItemsList { + @apply py-4 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accent-2 border-accent-2; +} diff --git a/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx b/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx new file mode 100644 index 000000000..fb562e7af --- /dev/null +++ b/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx @@ -0,0 +1,89 @@ +import cn from 'classnames' +import Link from 'next/link' +import { FC } from 'react' +import CartItem from '@components/cart/CartItem' +import { Button, Text } from '@components/ui' +import { useUI } from '@components/ui/context' +import useCart from '@framework/cart/use-cart' +import usePrice from '@framework/product/use-price' +import ShippingWidget from '../ShippingWidget' +import PaymentWidget from '../PaymentWidget' +import SidebarLayout from '@components/common/SidebarLayout' +import s from './CheckoutSidebarView.module.css' + +const CheckoutSidebarView: FC = () => { + const { setSidebarView } = useUI() + const { data } = useCart() + + const { price: subTotal } = usePrice( + data && { + amount: Number(data.subtotalPrice), + currencyCode: data.currency.code, + } + ) + const { price: total } = usePrice( + data && { + amount: Number(data.totalPrice), + currencyCode: data.currency.code, + } + ) + + return ( + setSidebarView('CART_VIEW')} + > +
    + + Checkout + + + setSidebarView('PAYMENT_VIEW')} /> + setSidebarView('SHIPPING_VIEW')} /> + +
      + {data!.lineItems.map((item: any) => ( + + ))} +
    +
    + +
    +
      +
    • + Subtotal + {subTotal} +
    • +
    • + Taxes + Calculated at checkout +
    • +
    • + Shipping + FREE +
    • +
    +
    + Total + {total} +
    +
    + {/* Once data is correcly filled */} + {/* */} + +
    +
    +
    + ) +} + +export default CheckoutSidebarView diff --git a/components/checkout/CheckoutSidebarView/index.ts b/components/checkout/CheckoutSidebarView/index.ts new file mode 100644 index 000000000..168bc58f4 --- /dev/null +++ b/components/checkout/CheckoutSidebarView/index.ts @@ -0,0 +1 @@ +export { default } from './CheckoutSidebarView' diff --git a/components/checkout/PaymentMethodView/PaymentMethodView.module.css b/components/checkout/PaymentMethodView/PaymentMethodView.module.css new file mode 100644 index 000000000..f3880c72c --- /dev/null +++ b/components/checkout/PaymentMethodView/PaymentMethodView.module.css @@ -0,0 +1,17 @@ +.fieldset { + @apply flex flex-col my-3; +} + +.fieldset .label { + @apply text-accent-7 uppercase text-xs font-medium mb-2; +} + +.fieldset .input, +.fieldset .select { + @apply p-2 border border-accent-2 w-full text-sm font-normal; +} + +.fieldset .input:focus, +.fieldset .select:focus { + @apply outline-none shadow-outline-normal; +} diff --git a/components/checkout/PaymentMethodView/PaymentMethodView.tsx b/components/checkout/PaymentMethodView/PaymentMethodView.tsx new file mode 100644 index 000000000..a5f6f4b51 --- /dev/null +++ b/components/checkout/PaymentMethodView/PaymentMethodView.tsx @@ -0,0 +1,84 @@ +import { FC } from 'react' +import cn from 'classnames' +import { Button, Text } from '@components/ui' +import { useUI } from '@components/ui/context' +import s from './PaymentMethodView.module.css' +import SidebarLayout from '@components/common/SidebarLayout' + +const PaymentMethodView: FC = () => { + const { setSidebarView } = useUI() + + return ( + setSidebarView('CHECKOUT_VIEW')}> +
    + Payment Method +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    + ) +} + +export default PaymentMethodView diff --git a/components/checkout/PaymentMethodView/index.ts b/components/checkout/PaymentMethodView/index.ts new file mode 100644 index 000000000..951b3c318 --- /dev/null +++ b/components/checkout/PaymentMethodView/index.ts @@ -0,0 +1 @@ +export { default } from './PaymentMethodView' diff --git a/components/checkout/PaymentWidget/PaymentWidget.module.css b/components/checkout/PaymentWidget/PaymentWidget.module.css new file mode 100644 index 000000000..38dcab0c0 --- /dev/null +++ b/components/checkout/PaymentWidget/PaymentWidget.module.css @@ -0,0 +1,4 @@ +.root { + @apply border border-accent-2 px-6 py-5 mb-4 text-center + flex items-center cursor-pointer hover:border-accent-4; +} diff --git a/components/checkout/PaymentWidget/PaymentWidget.tsx b/components/checkout/PaymentWidget/PaymentWidget.tsx new file mode 100644 index 000000000..e1892934e --- /dev/null +++ b/components/checkout/PaymentWidget/PaymentWidget.tsx @@ -0,0 +1,29 @@ +import { FC } from 'react' +import s from './PaymentWidget.module.css' +import { ChevronRight, CreditCard } from '@components/icons' + +interface ComponentProps { + onClick?: () => any +} + +const PaymentWidget: FC = ({ onClick }) => { + /* Shipping Address + Only available with checkout set to true - + This means that the provider does offer checkout functionality. */ + return ( +
    +
    + + + Add Payment Method + + {/* VISA #### #### #### 2345 */} +
    +
    + +
    +
    + ) +} + +export default PaymentWidget diff --git a/components/checkout/PaymentWidget/index.ts b/components/checkout/PaymentWidget/index.ts new file mode 100644 index 000000000..18cadea57 --- /dev/null +++ b/components/checkout/PaymentWidget/index.ts @@ -0,0 +1 @@ +export { default } from './PaymentWidget' diff --git a/components/checkout/ShippingView/ShippingView.module.css b/components/checkout/ShippingView/ShippingView.module.css new file mode 100644 index 000000000..157d3174e --- /dev/null +++ b/components/checkout/ShippingView/ShippingView.module.css @@ -0,0 +1,21 @@ +.fieldset { + @apply flex flex-col my-3; +} + +.fieldset .label { + @apply text-accent-7 uppercase text-xs font-medium mb-2; +} + +.fieldset .input, +.fieldset .select { + @apply p-2 border border-accent-2 w-full text-sm font-normal; +} + +.fieldset .input:focus, +.fieldset .select:focus { + @apply outline-none shadow-outline-normal; +} + +.radio { + @apply bg-black; +} diff --git a/components/checkout/ShippingView/ShippingView.tsx b/components/checkout/ShippingView/ShippingView.tsx new file mode 100644 index 000000000..1d03a2aac --- /dev/null +++ b/components/checkout/ShippingView/ShippingView.tsx @@ -0,0 +1,78 @@ +import { FC } from 'react' +import cn from 'classnames' +import s from './ShippingView.module.css' +import Button from '@components/ui/Button' +import { useUI } from '@components/ui/context' +import SidebarLayout from '@components/common/SidebarLayout' + +const PaymentMethodView: FC = () => { + const { setSidebarView } = useUI() + + return ( + setSidebarView('CHECKOUT_VIEW')}> +
    +

    + Shipping +

    +
    +
    + + Same as billing address +
    +
    + + + Use a different shipping address + +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    + ) +} + +export default PaymentMethodView diff --git a/components/checkout/ShippingView/index.ts b/components/checkout/ShippingView/index.ts new file mode 100644 index 000000000..428e7e4fe --- /dev/null +++ b/components/checkout/ShippingView/index.ts @@ -0,0 +1 @@ +export { default } from './ShippingView' diff --git a/components/checkout/ShippingWidget/ShippingWidget.module.css b/components/checkout/ShippingWidget/ShippingWidget.module.css new file mode 100644 index 000000000..38dcab0c0 --- /dev/null +++ b/components/checkout/ShippingWidget/ShippingWidget.module.css @@ -0,0 +1,4 @@ +.root { + @apply border border-accent-2 px-6 py-5 mb-4 text-center + flex items-center cursor-pointer hover:border-accent-4; +} diff --git a/components/checkout/ShippingWidget/ShippingWidget.tsx b/components/checkout/ShippingWidget/ShippingWidget.tsx new file mode 100644 index 000000000..b072178b0 --- /dev/null +++ b/components/checkout/ShippingWidget/ShippingWidget.tsx @@ -0,0 +1,33 @@ +import { FC } from 'react' +import s from './ShippingWidget.module.css' +import { ChevronRight, MapPin } from '@components/icons' +import cn from 'classnames' + +interface ComponentProps { + onClick?: () => any +} + +const ShippingWidget: FC = ({ onClick }) => { + /* Shipping Address + Only available with checkout set to true - + This means that the provider does offer checkout functionality. */ + return ( +
    +
    + + + Add Shipping Address + + {/* + 1046 Kearny Street.
    + San Franssisco, California +
    */} +
    +
    + +
    +
    + ) +} + +export default ShippingWidget diff --git a/components/checkout/ShippingWidget/index.ts b/components/checkout/ShippingWidget/index.ts new file mode 100644 index 000000000..88e6dca4b --- /dev/null +++ b/components/checkout/ShippingWidget/index.ts @@ -0,0 +1 @@ +export { default } from './ShippingWidget' diff --git a/components/common/Avatar/Avatar.tsx b/components/common/Avatar/Avatar.tsx index f78aa1d01..663538450 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -14,7 +14,7 @@ const Avatar: FC = ({}) => {
    {/* Add an image - We're generating a gradient as placeholder */}
    diff --git a/components/common/FeatureBar/FeatureBar.module.css b/components/common/FeatureBar/FeatureBar.module.css index 419fd4b08..a3cb61cd2 100644 --- a/components/common/FeatureBar/FeatureBar.module.css +++ b/components/common/FeatureBar/FeatureBar.module.css @@ -1,9 +1,7 @@ .root { @apply text-center p-6 bg-primary text-sm flex-row justify-center items-center font-medium fixed bottom-0 w-full z-30 transition-all duration-300 ease-out; -} -@screen md { - .root { + @screen md { @apply flex text-left; } } diff --git a/components/common/Footer/Footer.module.css b/components/common/Footer/Footer.module.css index 259318ecf..2ba492086 100644 --- a/components/common/Footer/Footer.module.css +++ b/components/common/Footer/Footer.module.css @@ -1,3 +1,7 @@ +.root { + @apply border-t border-accent-2; +} + .link { & > svg { @apply transform duration-75 ease-linear; diff --git a/components/common/Footer/Footer.tsx b/components/common/Footer/Footer.tsx index 4190815ec..04b80404e 100644 --- a/components/common/Footer/Footer.tsx +++ b/components/common/Footer/Footer.tsx @@ -15,65 +15,50 @@ interface Props { pages?: Page[] } -const LEGAL_PAGES = ['terms-of-use', 'shipping-returns', 'privacy-policy'] +const links = [ + { + name: 'Home', + url: '/', + }, +] const Footer: FC = ({ className, pages }) => { - const { sitePages, legalPages } = usePages(pages) - const rootClassName = cn(className) + const { sitePages } = usePages(pages) + const rootClassName = cn(s.root, className) return (