diff --git a/.env.template b/.env.template index 73a8a6e3b..c5543e4b1 100644 --- a/.env.template +++ b/.env.template @@ -1,5 +1,18 @@ +# Available providers: bigcommerce, shopify, swell +COMMERCE_PROVIDER= + BIGCOMMERCE_STOREFRONT_API_URL= BIGCOMMERCE_STOREFRONT_API_TOKEN= BIGCOMMERCE_STORE_API_URL= BIGCOMMERCE_STORE_API_TOKEN= -BIGCOMMERCE_STORE_API_CLIENT_ID= \ No newline at end of file +BIGCOMMERCE_STORE_API_CLIENT_ID= +BIGCOMMERCE_CHANNEL_ID= + +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= + +NEXT_PUBLIC_SWELL_STORE_ID= +NEXT_PUBLIC_SWELL_PUBLIC_KEY= + +NEXT_PUBLIC_SALEOR_API_URL= +NEXT_PUBLIC_SALEOR_CHANNEL= diff --git a/.gitignore b/.gitignore index bcbf6047a..22f1bf4f3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ out/ # misc .DS_Store *.pem +.idea # debug npm-debug.log* @@ -25,6 +26,7 @@ yarn-debug.log* yarn-error.log* # local env files +.env .env.local .env.development.local .env.test.local diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..91990a859 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,14 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "useTabs": false, + "overrides": [ + { + "files": ["framework/saleor/**/*"], + "options": { + "printWidth": 120 + } + } + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..c83e26348 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["esbenp.prettier-vscode"] +} diff --git a/README.md b/README.md index e73a9747e..b0cd2b2d3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,11 @@ Start right now at [nextjs.org/commerce](https://nextjs.org/commerce) Demo live at: [demo.vercel.store](https://demo.vercel.store/) -This project is currently under development. +- Shopify Demo: https://shopify.vercel.store/ +- Swell Demo: https://swell.vercel.store/ +- BigCommerce Demo: https://bigcommerce.vercel.store/ +- Vendure Demo: https://vendure.vercel.store +- Saleor Demo: https://saleor.vercel.store/ ## Features @@ -21,23 +25,88 @@ This project is currently under development. - Integrations - Integrate seamlessly with the most common ecommerce platforms. - Dark Mode Support -## Work in progress -We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) - ## Integrations -Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support all major ecommerce backends. +Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor and Vendure. We plan to support all major ecommerce backends. -## Goals +## Considerations -* **Next.js Commerce** should have a completely data **agnostic** UI -* **Aware of schema**: should ship with the right data schemas and types. -* All providers should return the right data types and schemas to blend correctly with Next.js Commerce. -* `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._ +- `framework/commerce` contains all types, helpers and functions to be used as base to build a new **provider**. +- **Providers** live under `framework`'s root folder and they will extend Next.js Commerce types and functionality (`framework/commerce`). +- We have a **Features API** to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programatically. +- Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in case of BigCommerce, the images CDN and additional API routes. +- **Providers don't depend on anything that's specific to the application they're used in**. They only depend on `framework/commerce`, on their own framework folder and on some dependencies included in `package.json` -There is a `framework` folder in the root folder that will contain multiple ecommerce providers. +## Configuration -Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) we will also have to build a feature API to disable/enable features in the UI. +### How to change providers + +Open `.env.local` and change the value of `COMMERCE_PROVIDER` to the provider you would like to use, then set the environment variables for that provider (use `.env.template` as the base). + +The setup for Shopify would look like this for example: + +``` +COMMERCE_PROVIDER=shopify +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxx +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=xxxxxxx.myshopify.com +``` + +And check that the `tsconfig.json` resolves to the chosen provider: + +``` + "@framework": ["framework/shopify"], + "@framework/*": ["framework/shopify/*"] +``` + +That's it! + +### Features + +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) + +- Open `commerce.config.json` +- You'll see a config file like this: + ```json + { + "features": { + "wishlist": false, + "customCheckout": true + } + } + ``` +- Turn wishlist on by setting wishlist to true. +- Run the app and the wishlist functionality should be back on. + +### How to create a new provider + +Follow our docs for [Adding a new Commerce Provider](framework/commerce/new-provider.md). + +If you succeeded building a provider, submit a PR with a valid demo and we'll review it asap. + +## Contribute + +Our commitment to Open Source can be found [here](https://vercel.com/oss). + +1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. +2. Create a new branch `git checkout -b MY_BRANCH_NAME` +3. Install yarn: `npm install -g yarn` +4. Install the dependencies: `yarn` +5. Duplicate `.env.template` and rename it to `.env.local` +6. Add proper store values to `.env.local` +7. Run `yarn dev` to build and watch for code changes + +## Work in progress + +We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) People actively working on this project: @okbel & @lfades. @@ -57,6 +126,7 @@ BIGCOMMERCE_STOREFRONT_API_TOKEN=<> BIGCOMMERCE_STORE_API_URL=<> BIGCOMMERCE_STORE_API_TOKEN=<> BIGCOMMERCE_STORE_API_CLIENT_ID=<> +BIGCOMMERCE_CHANNEL_ID=<> ``` If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials. @@ -77,22 +147,3 @@ After Email confirmation, Checkout should be manually enabled through BigCommerc
BigCommerce team has been notified and they plan to add more detailed about this subject. - -## Contribute - -Our commitment to Open Source can be found [here](https://vercel.com/oss). - -1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. -2. Create a new branch `git checkout -b MY_BRANCH_NAME` -3. Install yarn: `npm install -g yarn` -4. Install the dependencies: `yarn` -5. Duplicate `.env.template` and rename it to `.env.local`. -6. Add proper store values to `.env.local`. -7. Run `yarn dev` to build and watch for code changes -8. The development branch is `development` (this is the branch pull requests should be made against). - On a release, `develop` branch is rebased into `master`. - - - - - diff --git a/assets/base.css b/assets/base.css index 781ebe0ac..00081f459 100644 --- a/assets/base.css +++ b/assets/base.css @@ -3,7 +3,6 @@ --primary-2: #f1f3f5; --secondary: #000000; --secondary-2: #111; - --selection: var(--cyan); --text-base: #000000; @@ -13,28 +12,31 @@ --hover: rgba(0, 0, 0, 0.075); --hover-1: rgba(0, 0, 0, 0.15); --hover-2: rgba(0, 0, 0, 0.25); - --cyan: #22b8cf; --green: #37b679; --red: #da3c3c; - --pink: #e64980; --purple: #f81ce5; - --blue: #0070f3; - --violet-light: #7048e8; - --violet: #5f3dc4; + --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; - --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; --font-sans: -apple-system, system-ui, BlinkMacSystemFont, 'Helvetica Neue', 'Helvetica', sans-serif; } @@ -53,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; } *, @@ -89,6 +91,7 @@ body { -moz-osx-font-smoothing: grayscale; background-color: var(--primary); color: var(--text-primary); + overscroll-behavior-x: none; } body { @@ -102,8 +105,6 @@ a { } .animated { - -webkit-animation-duration: 1s; - animation-duration: 1s; -webkit-animation-duration: 1s; animation-duration: 1s; -webkit-animation-fill-mode: both; diff --git a/codegen.bigcommerce.json b/codegen.bigcommerce.json new file mode 100644 index 000000000..1f14e88ac --- /dev/null +++ b/codegen.bigcommerce.json @@ -0,0 +1,27 @@ +{ + "schema": { + "https://buybutton.store/graphql": { + "headers": { + "Authorization": "Bearer xzy" + } + } + }, + "documents": [ + { + "./framework/bigcommerce/api/**/*.ts": { + "noRequire": true + } + } + ], + "generates": { + "./framework/bigcommerce/schema.d.ts": { + "plugins": ["typescript", "typescript-operations"] + }, + "./framework/bigcommerce/schema.graphql": { + "plugins": ["schema-ast"] + } + }, + "hooks": { + "afterAllFileWrite": ["prettier --write"] + } +} diff --git a/codegen.json b/codegen.json index 1f14e88ac..d9a5c09ce 100644 --- a/codegen.json +++ b/codegen.json @@ -1,23 +1,29 @@ { "schema": { - "https://buybutton.store/graphql": { - "headers": { - "Authorization": "Bearer xzy" - } - } + "https://master.staging.saleor.cloud/graphql/": {} }, "documents": [ { - "./framework/bigcommerce/api/**/*.ts": { + "./framework/saleor/utils/queries/get-all-products-query.ts": { + "noRequire": true + } + }, + { + "./framework/saleor/utils/queries/get-all-products-paths-query.ts": { + "noRequire": true + } + }, + { + "./framework/saleor/utils/queries/get-products.ts": { "noRequire": true } } ], "generates": { - "./framework/bigcommerce/schema.d.ts": { + "./framework/saleor/schema.d.ts": { "plugins": ["typescript", "typescript-operations"] }, - "./framework/bigcommerce/schema.graphql": { + "./framework/saleor/schema.graphql": { "plugins": ["schema-ast"] } }, diff --git a/commerce.config.json b/commerce.config.json new file mode 100644 index 000000000..06b985504 --- /dev/null +++ b/commerce.config.json @@ -0,0 +1,6 @@ +{ + "features": { + "wishlist": false, + "customCheckout": false + } +} 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.extended_sale_price, - baseAmount: item.extended_list_price, + amount: item.variant.price * item.quantity, + baseAmount: item.variant.listPrice * item.quantity, currencyCode, }) - const updateItem = useUpdateItem(item) - const removeItem = useRemoveItem() - const [quantity, setQuantity] = useState(item.quantity) - const [removing, setRemoving] = useState(false) - const updateQuantity = async (val: number) => { + + const handleChange = async ({ + target: { value }, + }: ChangeEvent) => { + setQuantity(Number(value)) + await updateItem({ quantity: Number(value) }) + } + + const increaseQuantity = async (n = 1) => { + const val = Number(quantity) + n + setQuantity(val) await updateItem({ quantity: val }) } - const handleQuantity = (e: ChangeEvent) => { - const val = Number(e.target.value) - if (Number.isInteger(val) && val >= 0) { - setQuantity(e.target.value) - } - } - 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({ id: item.id }) + await removeItem(item) } catch (error) { setRemoving(false) } } + // TODO: Add a type for this + const options = (item as any).options + useEffect(() => { // Reset the quantity state if the item quantity changes if (item.quantity !== Number(quantity)) { @@ -70,55 +74,76 @@ const CartItem = ({ return (
  • -
    - Product Image -
    -
    - {/** TODO: Replace this. No `path` found at Cart */} - - - {item.name} - - - -
    - -
    -
    - {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 eb76c5da5..128b928a8 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -1,62 +1,45 @@ -import { FC } from 'react' import cn from 'classnames' -import { UserNav } from '@components/common' -import { Button } from '@components/ui' -import { Bag, Cross, Check } from '@components/icons' -import { useUI } from '@components/ui/context' -import useCart from '@framework/cart/use-cart' -import usePrice from '@framework/use-price' -import CartItem from '../CartItem' +import Link from 'next/link' +import { FC } from 'react' import s from './CartSidebarView.module.css' +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 { data, isEmpty } = useCart() + const { closeSidebar, setSidebarView } = useUI() + const { data, isLoading, isEmpty } = useCart() + const { price: subTotal } = usePrice( data && { - amount: data.base_amount, + amount: Number(data.subtotalPrice), currencyCode: data.currency.code, } ) const { price: total } = usePrice( data && { - amount: data.cart_amount, + amount: Number(data.totalPrice), currencyCode: data.currency.code, } ) const handleClose = () => closeSidebar() - - const items = data?.line_items.physical_items ?? [] + const goToCheckout = () => setSidebarView('CHECKOUT_VIEW') const error = null const success = null return ( -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - - {isEmpty ? ( + {isLoading || isEmpty ? (
    @@ -64,7 +47,7 @@ const CartSidebarView: FC = () => {

    Your cart is empty

    -

    +

    Biscuit oat cake wafer icing ice cream tiramisu pudding cupcake.

    @@ -90,48 +73,56 @@ const CartSidebarView: FC = () => { ) : ( <>
    -

    - My Cart -

    -
      - {items.map((item: any) => ( + + + 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 6ea72abb2..663538450 100644 --- a/components/common/Avatar/Avatar.tsx +++ b/components/common/Avatar/Avatar.tsx @@ -1,6 +1,5 @@ -import cn from 'classnames' -import { FC, useState, useMemo, useRef, useEffect } from 'react' -import { getRandomPairOfColors } from '@lib/colors' +import { FC, useRef, useEffect } from 'react' +import { useUserAvatar } from '@lib/hooks/useUserAvatar' interface Props { className?: string @@ -8,19 +7,14 @@ interface Props { } const Avatar: FC = ({}) => { - const [bg] = useState(useMemo(() => getRandomPairOfColors, [])) let ref = useRef() as React.MutableRefObject - - useEffect(() => { - if (ref && ref.current) { - ref.current.style.backgroundImage = `linear-gradient(140deg, ${bg[0]}, ${bg[1]} 100%)` - } - }, [bg]) + let { userAvatar } = useUserAvatar() return (
    {/* Add an image - We're generating a gradient as placeholder */}
    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 627530121..04b80404e 100644 --- a/components/common/Footer/Footer.tsx +++ b/components/common/Footer/Footer.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import cn from 'classnames' import Link from 'next/link' import { useRouter } from 'next/router' -import type { Page } from '@framework/api/operations/get-all-pages' +import type { Page } from '@commerce/types/page' import getSlug from '@lib/get-slug' import { Github, Vercel } from '@components/icons' import { Logo, Container } from '@components/ui' @@ -15,79 +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 (