mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 23:16:59 +00:00
Merge branch 'main' of github.com:vercel/commerce into outgrow-reaction-commerce-provider
Signed-off-by: Loan Laux <llaux@trellis.co>
This commit is contained in:
commit
eced304c25
14
.gitignore
vendored
14
.gitignore
vendored
@ -2,18 +2,19 @@
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
/.pnp
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
coverage
|
||||
|
||||
# next.js
|
||||
.next/
|
||||
out/
|
||||
.next
|
||||
out
|
||||
|
||||
# production
|
||||
/build
|
||||
build
|
||||
dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
@ -34,3 +35,6 @@ yarn-error.log*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# Turborepo
|
||||
.turbo
|
||||
|
@ -1,3 +1,5 @@
|
||||
# Every package defines its prettier config
|
||||
node_modules
|
||||
dist
|
||||
.next
|
||||
public
|
||||
public
|
||||
|
10
.prettierrc
10
.prettierrc
@ -2,13 +2,5 @@
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["framework/saleor/**/*"],
|
||||
"options": {
|
||||
"printWidth": 120
|
||||
}
|
||||
}
|
||||
]
|
||||
"useTabs": false
|
||||
}
|
||||
|
7
.vscode/extensions.json
vendored
7
.vscode/extensions.json
vendored
@ -1,3 +1,8 @@
|
||||
{
|
||||
"recommendations": ["esbenp.prettier-vscode"]
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode",
|
||||
"csstools.postcss",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"ms-vscode.vscode-typescript-next"
|
||||
]
|
||||
}
|
||||
|
83
README.md
83
README.md
@ -1,4 +1,4 @@
|
||||
[](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites.&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT)
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites.&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT,oac_9HSKtXld74NG0srzdxSiBGty&skippable-integrations=1&root-directory=site&build-command=cd%20..%20%26%26%20yarn%20build)
|
||||
|
||||
# Next.js Commerce
|
||||
|
||||
@ -12,6 +12,10 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
|
||||
- BigCommerce Demo: https://bigcommerce.vercel.store/
|
||||
- Vendure Demo: https://vendure.vercel.store
|
||||
- Saleor Demo: https://saleor.vercel.store/
|
||||
- Ordercloud Demo: https://ordercloud.vercel.store/
|
||||
- Spree Demo: https://spree.vercel.store/
|
||||
- Kibo Commerce Demo: https://kibocommerce.vercel.store/
|
||||
- Commerce.js Demo: https://commercejs.vercel.store/
|
||||
|
||||
## Features
|
||||
|
||||
@ -27,42 +31,32 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
|
||||
|
||||
## Integrations
|
||||
|
||||
Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor and Vendure. We plan to support all major ecommerce backends.
|
||||
Next.js Commerce integrates out-of-the-box with BigCommerce, Shopify, Swell, Saleor, Vendure, Spree and Commerce.js. We plan to support all major ecommerce backends.
|
||||
|
||||
## Considerations
|
||||
|
||||
- `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`).
|
||||
- `packages/commerce` contains all types, helpers and functions to be used as base to build a new **provider**.
|
||||
- **Providers** live under `packages`'s root folder and they will extend Next.js Commerce types and functionality (`packages/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`
|
||||
|
||||
## Configuration
|
||||
|
||||
### 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).
|
||||
Open `site/.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 `site/.env.template` as the base).
|
||||
|
||||
The setup for Shopify would look like this for example:
|
||||
|
||||
```
|
||||
COMMERCE_PROVIDER=shopify
|
||||
COMMERCE_PROVIDER=@vercel/commerce-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`
|
||||
Every provider defines the features that it supports under `packages/{provider}/src/commerce.config.json`
|
||||
|
||||
#### Features Available
|
||||
|
||||
@ -79,7 +73,7 @@ For example: Turning `cart` off will disable Cart capabilities.
|
||||
|
||||
> 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`
|
||||
- Open `site/commerce.config.json`
|
||||
- You'll see a config file like this:
|
||||
```json
|
||||
{
|
||||
@ -94,7 +88,7 @@ For example: Turning `cart` off will disable Cart capabilities.
|
||||
|
||||
### How to create a new provider
|
||||
|
||||
Follow our docs for [Adding a new Commerce Provider](framework/commerce/new-provider.md).
|
||||
Follow our docs for [Adding a new Commerce Provider](packages/commerce/new-provider.md).
|
||||
|
||||
If you succeeded building a provider, submit a PR with a valid demo and we'll review it asap.
|
||||
|
||||
@ -104,17 +98,17 @@ 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
|
||||
3. Install the dependencies: `yarn`
|
||||
4. Duplicate `site/.env.template` and rename it to `site/.env.local`
|
||||
5. Add proper store values to `site/.env.local`
|
||||
6. Run `cd site` and `yarn dev` to build and watch for code changes
|
||||
7. Run `yarn turbo run build` to check the build after your 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.
|
||||
People actively working on this project: @okbel, @lfades, @dominiksipowicz, @gbibeaul.
|
||||
|
||||
## Troubleshoot
|
||||
|
||||
@ -141,7 +135,7 @@ If your project was started with a "Deploy with Vercel" button, you can use Verc
|
||||
2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link`
|
||||
3. Download your environment variables: `vercel env pull .env.local`
|
||||
|
||||
Next, you're free to customize the starter. More updates coming soon. Stay tuned.
|
||||
Next, you're free to customize the starter. More updates coming soon. Stay tuned..
|
||||
|
||||
</details>
|
||||
|
||||
@ -153,3 +147,40 @@ After Email confirmation, Checkout should be manually enabled through BigCommerc
|
||||
<br>
|
||||
BigCommerce team has been notified and they plan to add more details about this subject.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>When run locally I get `Error: Cannot find module '...@vercel/commerce/dist/config'`</summary>
|
||||
|
||||
```bash
|
||||
commerce/site
|
||||
❯ yarn dev
|
||||
yarn run v1.22.17
|
||||
$ next dev
|
||||
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
|
||||
info - Loaded env from /commerce/site/.env.local
|
||||
error - Failed to load next.config.js, see more info here https://nextjs.org/docs/messages/next-config-error
|
||||
Error: Cannot find module '/Users/dom/work/vercel/commerce/node_modules/@vercel/commerce/dist/config.cjs'
|
||||
at createEsmNotFoundErr (node:internal/modules/cjs/loader:960:15)
|
||||
at finalizeEsmResolution (node:internal/modules/cjs/loader:953:15)
|
||||
at resolveExports (node:internal/modules/cjs/loader:482:14)
|
||||
at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
|
||||
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
|
||||
at Function.mod._resolveFilename (/Users/dom/work/vercel/commerce/node_modules/next/dist/build/webpack/require-hook.js:179:28)
|
||||
at Function.Module._load (node:internal/modules/cjs/loader:778:27)
|
||||
at Module.require (node:internal/modules/cjs/loader:1005:19)
|
||||
at require (node:internal/modules/cjs/helpers:102:18)
|
||||
at Object.<anonymous> (/Users/dom/work/vercel/commerce/site/commerce-config.js:9:14) {
|
||||
code: 'MODULE_NOT_FOUND',
|
||||
path: '/Users/dom/work/vercel/commerce/node_modules/@vercel/commerce/package.json'
|
||||
}
|
||||
error Command failed with exit code 1.
|
||||
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
|
||||
```
|
||||
|
||||
The error usually occurs when running yarn dev inside of the `/site/` folder after installing a fresh repository.
|
||||
|
||||
In order to fix this, run `yarn dev` in the monorepo root folder first.
|
||||
|
||||
> Using `yarn dev` from the root is recommended for developing, which will run watch mode on all packages.
|
||||
|
||||
</details>
|
||||
|
@ -1,184 +0,0 @@
|
||||
import { FC } from 'react'
|
||||
import cn from 'classnames'
|
||||
import {
|
||||
CardNumberElement,
|
||||
CardExpiryElement,
|
||||
CardCvcElement,
|
||||
useStripe,
|
||||
useElements
|
||||
} from '@stripe/react-stripe-js'
|
||||
import { Button, Text } from '@components/ui'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import s from './PaymentMethodView.module.css'
|
||||
import SidebarLayout from '@components/common/SidebarLayout'
|
||||
import countries from '@lib/countries'
|
||||
|
||||
const PaymentMethodView: FC = () => {
|
||||
const { paymentMethodDetails, setPaymentMethodDetails, setSidebarView } = useUI()
|
||||
const { address } = paymentMethodDetails
|
||||
const stripe = useStripe()
|
||||
const elements = useElements()
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!stripe || !elements) {
|
||||
return
|
||||
}
|
||||
|
||||
const card = elements.getElement(CardNumberElement)
|
||||
|
||||
const { error, paymentMethod } = await stripe.createPaymentMethod({
|
||||
type: 'card',
|
||||
card
|
||||
})
|
||||
|
||||
if (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
setPaymentMethodDetails({ address, paymentMethod })
|
||||
setSidebarView('CHECKOUT_VIEW')
|
||||
}
|
||||
|
||||
const updateAddressData = ({ target }: any) => setPaymentMethodDetails({
|
||||
...paymentMethodDetails,
|
||||
address: {
|
||||
...paymentMethodDetails.address,
|
||||
[target.name]: target.value,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<SidebarLayout handleBack={handleSubmit}>
|
||||
<div className="px-4 sm:px-6 flex-1">
|
||||
<Text variant="sectionHeading"> Payment Method</Text>
|
||||
<div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Cardholder Name</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='cardholderName'
|
||||
onChange={updateAddressData}
|
||||
value={address.cardholderName}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-3 grid-flow-row grid-cols-12">
|
||||
<div className={cn(s.fieldset, 'col-span-7')}>
|
||||
<label className={s.label}>Card Number</label>
|
||||
<CardNumberElement className={s.input} />
|
||||
</div>
|
||||
<div className={cn(s.fieldset, 'col-span-3')}>
|
||||
<label className={s.label}>Expires</label>
|
||||
<CardExpiryElement className={s.input} placeholder='MM/YY' />
|
||||
</div>
|
||||
<div className={cn(s.fieldset, 'col-span-2')}>
|
||||
<label className={s.label}>CVC</label>
|
||||
<CardCvcElement className={s.input} />
|
||||
</div>
|
||||
</div>
|
||||
<hr className="border-accent-2 my-6" />
|
||||
<div className="grid gap-3 grid-flow-row grid-cols-12">
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>First Name</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='firstName'
|
||||
onChange={updateAddressData}
|
||||
value={address.firstName}
|
||||
/>
|
||||
</div>
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>Last Name</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='lastName'
|
||||
onChange={updateAddressData}
|
||||
value={address.lastName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Company (Optional)</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='company'
|
||||
onChange={updateAddressData}
|
||||
value={address.company}
|
||||
/>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Street and House Number</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='addressLine1'
|
||||
onChange={updateAddressData}
|
||||
value={address.addressLine1}
|
||||
/>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Apartment, Suite, Etc. (Optional)</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='addressLine2'
|
||||
onChange={updateAddressData}
|
||||
value={address.addressLine2}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-3 grid-flow-row grid-cols-12">
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>Postal Code</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='postalCode'
|
||||
onChange={updateAddressData}
|
||||
value={address.postalCode}
|
||||
/>
|
||||
</div>
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>City</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='city'
|
||||
onChange={updateAddressData}
|
||||
value={address.city}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Region</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='region'
|
||||
onChange={updateAddressData}
|
||||
value={address.region}
|
||||
/>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Country</label>
|
||||
<select
|
||||
className={s.select}
|
||||
name='country'
|
||||
onChange={updateAddressData}
|
||||
value={address.country}
|
||||
>
|
||||
{countries.map((country) => (
|
||||
<option
|
||||
key={country.code}
|
||||
value={country.code}
|
||||
>
|
||||
{country.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sticky z-20 bottom-0 w-full right-0 left-0 py-12 bg-accent-0 border-t border-accent-2 px-6">
|
||||
<Button Component="a" width="100%" variant="ghost">
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
</SidebarLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default PaymentMethodView
|
@ -1,207 +0,0 @@
|
||||
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'
|
||||
import countries from '@lib/countries'
|
||||
import useAddShippingAddress from '@framework/cart/use-add-shipping-address'
|
||||
|
||||
const PaymentMethodView: FC = () => {
|
||||
const addShippingAddress = useAddShippingAddress()
|
||||
const {
|
||||
paymentMethodDetails,
|
||||
setShippingAddress,
|
||||
setSidebarView,
|
||||
setUseBillingAddressForShipping,
|
||||
shippingAddress,
|
||||
useBillingAddressForShipping,
|
||||
} = useUI()
|
||||
|
||||
const handleUseBillingAddressForShipping = (event) => {
|
||||
setUseBillingAddressForShipping(event.target.value === 'true')
|
||||
|
||||
if (event.target.value === 'true') {
|
||||
setShippingAddress({
|
||||
firstName: paymentMethodDetails.address?.firstName,
|
||||
lastName: paymentMethodDetails.address?.lastName,
|
||||
company: paymentMethodDetails.address?.company,
|
||||
addressLine1: paymentMethodDetails.address?.addressLine1,
|
||||
addressLine2: paymentMethodDetails.address?.addressLine2,
|
||||
postalCode: paymentMethodDetails.address?.postalCode,
|
||||
city: paymentMethodDetails.address?.city,
|
||||
country: paymentMethodDetails.address?.country,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const updateAddressData = ({ target }: any) => setShippingAddress({
|
||||
...shippingAddress,
|
||||
[target.name]: target.value,
|
||||
})
|
||||
|
||||
return (
|
||||
<SidebarLayout handleBack={async () => {
|
||||
// add shipping address to cart
|
||||
await addShippingAddress({ address: shippingAddress })
|
||||
setSidebarView('CHECKOUT_VIEW')
|
||||
}}>
|
||||
<div className="px-4 sm:px-6 flex-1">
|
||||
<h2 className="pt-1 pb-8 text-2xl font-semibold tracking-wide cursor-pointer inline-block">
|
||||
Shipping
|
||||
</h2>
|
||||
<div>
|
||||
<div className="flex flex-row my-3 items-center">
|
||||
<input
|
||||
className={s.radio}
|
||||
type="radio"
|
||||
id="useBillingAddressForShippingTrue"
|
||||
name="useBillingAddressForShipping"
|
||||
value="true"
|
||||
onChange={handleUseBillingAddressForShipping}
|
||||
checked={useBillingAddressForShipping === true}
|
||||
/>
|
||||
<label htmlFor="useBillingAddressForShippingTrue" className="ml-3 text-sm">Same as billing address</label>
|
||||
</div>
|
||||
<div className="flex flex-row my-3 items-center">
|
||||
<input
|
||||
className={s.radio}
|
||||
type="radio"
|
||||
id="useBillingAddressForShippingFalse"
|
||||
name="useBillingAddressForShipping"
|
||||
value="false"
|
||||
onChange={handleUseBillingAddressForShipping}
|
||||
checked={useBillingAddressForShipping === false}
|
||||
/>
|
||||
<label htmlFor="useBillingAddressForShippingFalse" className="ml-3 text-sm">
|
||||
Use a different shipping address
|
||||
</label>
|
||||
</div>
|
||||
<hr className="border-accent-2 my-6" />
|
||||
<div className="grid gap-3 grid-flow-row grid-cols-12">
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>First Name</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='firstName'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.firstName}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>Last Name</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='lastName'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.lastName}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Phone Number</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='phone'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.phone}
|
||||
disabled={useBillingAddressForShipping}
|
||||
type='tel'
|
||||
/>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Company (Optional)</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='company'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.company}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Street and House Number</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='addressLine1'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.addressLine1}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Apartment, Suite, Etc. (Optional)</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='addressLine2'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.addressLine2}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-3 grid-flow-row grid-cols-12">
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>Postal Code</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='postalCode'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.postalCode}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
<div className={cn(s.fieldset, 'col-span-6')}>
|
||||
<label className={s.label}>City</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='city'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.city}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Region</label>
|
||||
<input
|
||||
className={s.input}
|
||||
name='region'
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.region}
|
||||
disabled={useBillingAddressForShipping}
|
||||
/>
|
||||
</div>
|
||||
<div className={s.fieldset}>
|
||||
<label className={s.label}>Country</label>
|
||||
<select
|
||||
className={s.select}
|
||||
name="country"
|
||||
onChange={updateAddressData}
|
||||
value={shippingAddress.country}
|
||||
disabled={useBillingAddressForShipping}
|
||||
|
||||
>
|
||||
{countries.map((country) => (
|
||||
<option
|
||||
key={country.code}
|
||||
value={country.code}
|
||||
>
|
||||
{country.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sticky z-20 bottom-0 w-full right-0 left-0 py-12 bg-accent-0 border-t border-accent-2 px-6">
|
||||
<Button Component="a" width="100%" variant="ghost">
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
</SidebarLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default PaymentMethodView
|
@ -1,7 +0,0 @@
|
||||
.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 {
|
||||
@apply flex text-left;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { FC } from 'react'
|
||||
import NextHead from 'next/head'
|
||||
import { DefaultSeo } from 'next-seo'
|
||||
import config from '@config/seo.json'
|
||||
|
||||
const Head: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<DefaultSeo {...config} />
|
||||
<NextHead>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="manifest" href="/site.webmanifest" key="site-manifest" />
|
||||
</NextHead>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Head
|
@ -1,63 +0,0 @@
|
||||
import { FC, InputHTMLAttributes, useEffect, useMemo } from 'react'
|
||||
import cn from 'classnames'
|
||||
import s from './Searchbar.module.css'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
id?: string
|
||||
}
|
||||
|
||||
const Searchbar: FC<Props> = ({ className, id = 'search' }) => {
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
router.prefetch('/search')
|
||||
}, [])
|
||||
|
||||
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
const q = e.currentTarget.value
|
||||
|
||||
router.push(
|
||||
{
|
||||
pathname: `/search`,
|
||||
query: q ? { q } : {},
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
<div className={cn(s.root, className)}>
|
||||
<label className="hidden" htmlFor={id}>
|
||||
Search
|
||||
</label>
|
||||
<input
|
||||
id={id}
|
||||
className={s.input}
|
||||
placeholder="Search for products..."
|
||||
defaultValue={router.query.q}
|
||||
onKeyUp={handleKeyUp}
|
||||
/>
|
||||
<div className={s.iconContainer}>
|
||||
<svg className={s.icon} fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
export default Searchbar
|
@ -1,50 +0,0 @@
|
||||
import { Swatch } from '@components/product'
|
||||
import type { ProductOption } from '@commerce/types/product'
|
||||
import { SelectedOptions } from '../helpers'
|
||||
import React from 'react'
|
||||
interface ProductOptionsProps {
|
||||
options: ProductOption[]
|
||||
selectedOptions: SelectedOptions
|
||||
setSelectedOptions: React.Dispatch<React.SetStateAction<SelectedOptions>>
|
||||
}
|
||||
|
||||
const ProductOptions: React.FC<ProductOptionsProps> = React.memo(
|
||||
({ options, selectedOptions, setSelectedOptions }) => {
|
||||
return (
|
||||
<div>
|
||||
{options.map((opt) => (
|
||||
<div className="pb-4" key={opt.displayName}>
|
||||
<h2 className="uppercase font-medium text-sm tracking-wide">
|
||||
{opt.displayName}
|
||||
</h2>
|
||||
<div className="flex flex-row py-4">
|
||||
{opt.values.map((v, i: number) => {
|
||||
const active = selectedOptions[opt.displayName.toLowerCase()]
|
||||
return (
|
||||
<Swatch
|
||||
key={`${opt.id}-${i}`}
|
||||
active={v.label.toLowerCase() === active}
|
||||
variant={opt.displayName}
|
||||
color={v.hexColors ? v.hexColors[0] : ''}
|
||||
label={v.label}
|
||||
onClick={() => {
|
||||
setSelectedOptions((selectedOptions) => {
|
||||
return {
|
||||
...selectedOptions,
|
||||
[opt.displayName.toLowerCase()]:
|
||||
v.label.toLowerCase(),
|
||||
}
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default ProductOptions
|
@ -1,31 +0,0 @@
|
||||
import cn from 'classnames'
|
||||
import React from 'react'
|
||||
import s from './ProductSliderControl.module.css'
|
||||
import { ArrowLeft, ArrowRight } from '@components/icons'
|
||||
|
||||
interface ProductSliderControl {
|
||||
onPrev: React.MouseEventHandler<HTMLButtonElement>
|
||||
onNext: React.MouseEventHandler<HTMLButtonElement>
|
||||
}
|
||||
|
||||
const ProductSliderControl: React.FC<ProductSliderControl> = React.memo(
|
||||
({ onPrev, onNext }) => (
|
||||
<div className={s.control}>
|
||||
<button
|
||||
className={cn(s.leftControl)}
|
||||
onClick={onPrev}
|
||||
aria-label="Previous Product Image"
|
||||
>
|
||||
<ArrowLeft />
|
||||
</button>
|
||||
<button
|
||||
className={cn(s.rightControl)}
|
||||
onClick={onNext}
|
||||
aria-label="Next Product Image"
|
||||
>
|
||||
<ArrowRight />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
export default ProductSliderControl
|
@ -1,62 +0,0 @@
|
||||
import cn from 'classnames'
|
||||
import React from 'react'
|
||||
import s from './Swatch.module.css'
|
||||
import { Check } from '@components/icons'
|
||||
import Button, { ButtonProps } from '@components/ui/Button'
|
||||
import { isDark } from '@lib/colors'
|
||||
interface SwatchProps {
|
||||
active?: boolean
|
||||
children?: any
|
||||
className?: string
|
||||
variant?: 'size' | 'color' | string
|
||||
color?: string
|
||||
label?: string | null
|
||||
}
|
||||
|
||||
const Swatch: React.FC<Omit<ButtonProps, 'variant'> & SwatchProps> = React.memo(
|
||||
({
|
||||
active,
|
||||
className,
|
||||
color = '',
|
||||
label = null,
|
||||
variant = 'size',
|
||||
...props
|
||||
}) => {
|
||||
variant = variant?.toLowerCase()
|
||||
|
||||
if (label) {
|
||||
label = label?.toLowerCase()
|
||||
}
|
||||
|
||||
const swatchClassName = cn(
|
||||
s.swatch,
|
||||
{
|
||||
[s.color]: color,
|
||||
[s.active]: active,
|
||||
[s.size]: variant === 'size',
|
||||
[s.dark]: color ? isDark(color) : false,
|
||||
[s.textLabel]: !color && label && label.length > 3,
|
||||
},
|
||||
className
|
||||
)
|
||||
|
||||
return (
|
||||
<Button
|
||||
aria-label="Variant Swatch"
|
||||
className={swatchClassName}
|
||||
{...(label && color && { title: label })}
|
||||
style={color ? { backgroundColor: color } : {}}
|
||||
{...props}
|
||||
>
|
||||
{color && active && (
|
||||
<span>
|
||||
<Check />
|
||||
</span>
|
||||
)}
|
||||
{!color ? label : null}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default Swatch
|
@ -1,27 +0,0 @@
|
||||
import React, { FC } from 'react'
|
||||
import rangeMap from '@lib/range-map'
|
||||
import { Star } from '@components/icons'
|
||||
import cn from 'classnames'
|
||||
|
||||
export interface RatingProps {
|
||||
value: number
|
||||
}
|
||||
|
||||
const Quantity: React.FC<RatingProps> = React.memo(({ value = 5 }) => {
|
||||
return (
|
||||
<div className="flex flex-row py-6 text-accent-9">
|
||||
{rangeMap(5, (i) => (
|
||||
<span
|
||||
key={`star_${i}`}
|
||||
className={cn('inline-block ml-1 ', {
|
||||
'text-accent-5': i >= Math.floor(value),
|
||||
})}
|
||||
>
|
||||
<Star />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default Quantity
|
@ -1,46 +0,0 @@
|
||||
import { FC, useEffect, useRef } from 'react'
|
||||
import s from './Sidebar.module.css'
|
||||
import cn from 'classnames'
|
||||
import {
|
||||
disableBodyScroll,
|
||||
enableBodyScroll,
|
||||
clearAllBodyScrollLocks,
|
||||
} from 'body-scroll-lock'
|
||||
|
||||
interface SidebarProps {
|
||||
children: any
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const Sidebar: FC<SidebarProps> = ({ children, onClose }) => {
|
||||
const ref = useRef() as React.MutableRefObject<HTMLDivElement>
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
disableBodyScroll(ref.current, { reserveScrollBarGap: true })
|
||||
}
|
||||
return () => {
|
||||
if (ref && ref.current) {
|
||||
enableBodyScroll(ref.current)
|
||||
}
|
||||
clearAllBodyScrollLocks()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={cn(s.root)}>
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<div className={s.backdrop} onClick={onClose} />
|
||||
<section className="absolute inset-y-0 right-0 max-w-full flex outline-none pl-10">
|
||||
<div className="h-full w-full md:w-screen md:max-w-md">
|
||||
<div className={s.sidebar} ref={ref}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Sidebar
|
@ -1,15 +0,0 @@
|
||||
.body {
|
||||
@apply text-base leading-7 max-w-6xl mx-auto;
|
||||
}
|
||||
|
||||
.heading {
|
||||
@apply text-5xl pt-1 pb-2 font-semibold tracking-wide cursor-pointer mb-2;
|
||||
}
|
||||
|
||||
.pageHeading {
|
||||
@apply pt-1 pb-4 text-2xl leading-7 font-bold tracking-wide;
|
||||
}
|
||||
|
||||
.sectionHeading {
|
||||
@apply pt-1 pb-2 text-2xl font-bold tracking-wide cursor-pointer mb-2;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import {
|
||||
CommerceConfig,
|
||||
CommerceProvider as CoreCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@commerce'
|
||||
import { bigcommerceProvider } from './provider'
|
||||
import type { BigcommerceProvider } from './provider'
|
||||
|
||||
export { bigcommerceProvider }
|
||||
export type { BigcommerceProvider }
|
||||
|
||||
export const bigcommerceConfig: CommerceConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'bc_cartId',
|
||||
}
|
||||
|
||||
export type BigcommerceConfig = Partial<CommerceConfig>
|
||||
|
||||
export type BigcommerceProps = {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
} & BigcommerceConfig
|
||||
|
||||
export function CommerceProvider({ children, ...config }: BigcommerceProps) {
|
||||
return (
|
||||
<CoreCommerceProvider
|
||||
provider={bigcommerceProvider}
|
||||
config={{ ...bigcommerceConfig, ...config }}
|
||||
>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useCommerce = () => useCoreCommerce<BigcommerceProvider>()
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/checkout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/common'
|
@ -1,5 +0,0 @@
|
||||
import * as Core from '@commerce/types/customer'
|
||||
|
||||
export * from '@commerce/types/customer'
|
||||
|
||||
export type CustomerSchema = Core.CustomerSchema
|
@ -1,8 +0,0 @@
|
||||
import * as Core from '@commerce/types/login'
|
||||
import type { LoginMutationVariables } from '../schema'
|
||||
|
||||
export * from '@commerce/types/login'
|
||||
|
||||
export type LoginOperation = Core.LoginOperation & {
|
||||
variables: LoginMutationVariables
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/logout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/product'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/signup'
|
@ -1,89 +0,0 @@
|
||||
/**
|
||||
* This file is expected to be used in next.config.js only
|
||||
*/
|
||||
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const merge = require('deepmerge')
|
||||
const prettier = require('prettier')
|
||||
|
||||
const PROVIDERS = [
|
||||
'bigcommerce',
|
||||
'shopify',
|
||||
'swell',
|
||||
'vendure',
|
||||
'reactioncommerce',
|
||||
'saleor',
|
||||
'local',
|
||||
]
|
||||
|
||||
function getProviderName() {
|
||||
return (
|
||||
process.env.COMMERCE_PROVIDER ||
|
||||
(process.env.BIGCOMMERCE_STOREFRONT_API_URL
|
||||
? 'bigcommerce'
|
||||
: process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
|
||||
? 'shopify'
|
||||
: process.env.NEXT_PUBLIC_SWELL_STORE_ID
|
||||
? 'swell'
|
||||
: 'local')
|
||||
)
|
||||
}
|
||||
|
||||
function withCommerceConfig(nextConfig = {}) {
|
||||
const commerce = nextConfig.commerce || {}
|
||||
const name = commerce.provider || getProviderName()
|
||||
|
||||
if (!name) {
|
||||
throw new Error(
|
||||
`The commerce provider is missing, please add a valid provider name or its environment variables`
|
||||
)
|
||||
}
|
||||
if (!PROVIDERS.includes(name)) {
|
||||
throw new Error(
|
||||
`The commerce provider "${name}" can't be found, please use one of "${PROVIDERS.join(
|
||||
', '
|
||||
)}"`
|
||||
)
|
||||
}
|
||||
|
||||
const commerceNextConfig = require(path.join('../', name, 'next.config'))
|
||||
const config = merge(nextConfig, commerceNextConfig)
|
||||
|
||||
config.env = config.env || {}
|
||||
|
||||
Object.entries(config.commerce.features).forEach(([k, v]) => {
|
||||
if (v) config.env[`COMMERCE_${k.toUpperCase()}_ENABLED`] = true
|
||||
})
|
||||
|
||||
// Update paths in `tsconfig.json` to point to the selected provider
|
||||
if (config.commerce.updateTSConfig !== false) {
|
||||
const tsconfigPath = path.join(process.cwd(), 'tsconfig.json')
|
||||
const tsconfig = require(tsconfigPath)
|
||||
|
||||
tsconfig.compilerOptions.paths['@framework'] = [`framework/${name}`]
|
||||
tsconfig.compilerOptions.paths['@framework/*'] = [`framework/${name}/*`]
|
||||
|
||||
// When running for production it may be useful to exclude the other providers
|
||||
// from TS checking
|
||||
if (process.env.VERCEL) {
|
||||
const exclude = tsconfig.exclude.filter(
|
||||
(item) => !item.startsWith('framework/')
|
||||
)
|
||||
|
||||
tsconfig.exclude = PROVIDERS.reduce((exclude, current) => {
|
||||
if (current !== name) exclude.push(`framework/${current}`)
|
||||
return exclude
|
||||
}, exclude)
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
tsconfigPath,
|
||||
prettier.format(JSON.stringify(tsconfig), { parser: 'json' })
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = { withCommerceConfig, getProviderName }
|
@ -1,10 +0,0 @@
|
||||
export type CheckoutSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
handlers: {
|
||||
checkout: {
|
||||
data: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
import { localProvider } from './provider'
|
||||
import {
|
||||
CommerceConfig,
|
||||
CommerceProvider as CoreCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@commerce'
|
||||
|
||||
export const localConfig: CommerceConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'session',
|
||||
}
|
||||
|
||||
export function CommerceProvider({
|
||||
children,
|
||||
...config
|
||||
}: {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
} & Partial<CommerceConfig>) {
|
||||
return (
|
||||
<CoreCommerceProvider
|
||||
provider={localProvider}
|
||||
config={{ ...localConfig, ...config }}
|
||||
>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useCommerce = () => useCoreCommerce()
|
@ -1,2 +0,0 @@
|
||||
export * from '@commerce/product/use-price'
|
||||
export { default } from '@commerce/product/use-price'
|
@ -1,2 +0,0 @@
|
||||
export * from '@commerce/product/use-price'
|
||||
export { default } from '@commerce/product/use-price'
|
@ -1 +0,0 @@
|
||||
export default function (_commerce: any) {}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"provider": "saleor",
|
||||
"features": {
|
||||
"wishlist": false,
|
||||
"customCheckout": true
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import { CommerceConfig, CommerceProvider as CoreCommerceProvider, useCommerce as useCoreCommerce } from '@commerce'
|
||||
|
||||
import { saleorProvider, SaleorProvider } from './provider'
|
||||
import * as Const from './const'
|
||||
|
||||
export { saleorProvider }
|
||||
export type { SaleorProvider }
|
||||
|
||||
export const saleorConfig: CommerceConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: Const.CHECKOUT_ID_COOKIE,
|
||||
}
|
||||
|
||||
export type SaleorConfig = Partial<CommerceConfig>
|
||||
|
||||
export type SaleorProps = {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
} & SaleorConfig
|
||||
|
||||
export function CommerceProvider({ children, ...config }: SaleorProps) {
|
||||
return (
|
||||
<CoreCommerceProvider provider={saleorProvider} config={{ ...saleorConfig, ...config }}>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useCommerce = () => useCoreCommerce()
|
@ -1,2 +0,0 @@
|
||||
export * from '@commerce/product/use-price'
|
||||
export { default } from '@commerce/product/use-price'
|
@ -1 +0,0 @@
|
||||
export default function (_commerce: any) {}
|
@ -1,40 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import {
|
||||
CommerceConfig,
|
||||
CommerceProvider as CoreCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@commerce'
|
||||
|
||||
import { shopifyProvider } from './provider'
|
||||
import type { ShopifyProvider } from './provider'
|
||||
import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const'
|
||||
|
||||
export { shopifyProvider }
|
||||
export type { ShopifyProvider }
|
||||
|
||||
export const shopifyConfig: CommerceConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
}
|
||||
|
||||
export type ShopifyConfig = Partial<CommerceConfig>
|
||||
|
||||
export type ShopifyProps = {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
} & ShopifyConfig
|
||||
|
||||
export function CommerceProvider({ children, ...config }: ShopifyProps) {
|
||||
return (
|
||||
<CoreCommerceProvider
|
||||
provider={shopifyProvider}
|
||||
config={{ ...shopifyConfig, ...config }}
|
||||
>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useCommerce = () => useCoreCommerce<ShopifyProvider>()
|
@ -1,2 +0,0 @@
|
||||
export * from '@commerce/product/use-price'
|
||||
export { default } from '@commerce/product/use-price'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/checkout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/common'
|
@ -1,5 +0,0 @@
|
||||
import * as Core from '@commerce/types/customer'
|
||||
|
||||
export * from '@commerce/types/customer'
|
||||
|
||||
export type CustomerSchema = Core.CustomerSchema
|
@ -1,8 +0,0 @@
|
||||
import * as Core from '@commerce/types/login'
|
||||
import type { CustomerAccessTokenCreateInput } from '../schema'
|
||||
|
||||
export * from '@commerce/types/login'
|
||||
|
||||
export type LoginOperation = Core.LoginOperation & {
|
||||
variables: CustomerAccessTokenCreateInput
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/logout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/product'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/signup'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/site'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/wishlist'
|
@ -1,33 +0,0 @@
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
import {
|
||||
SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
SHOPIFY_CHECKOUT_URL_COOKIE,
|
||||
SHOPIFY_COOKIE_EXPIRE,
|
||||
} from '../const'
|
||||
|
||||
import checkoutCreateMutation from './mutations/checkout-create'
|
||||
import { CheckoutCreatePayload } from '../schema'
|
||||
|
||||
export const checkoutCreate = async (
|
||||
fetch: any
|
||||
): Promise<CheckoutCreatePayload> => {
|
||||
const data = await fetch({
|
||||
query: checkoutCreateMutation,
|
||||
})
|
||||
|
||||
const checkout = data.checkoutCreate?.checkout
|
||||
const checkoutId = checkout?.id
|
||||
|
||||
if (checkoutId) {
|
||||
const options = {
|
||||
expires: SHOPIFY_COOKIE_EXPIRE,
|
||||
}
|
||||
Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options)
|
||||
Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout.webUrl, options)
|
||||
}
|
||||
|
||||
return checkout
|
||||
}
|
||||
|
||||
export default checkoutCreate
|
@ -1 +0,0 @@
|
||||
export default function (_commerce: any) {}
|
@ -1,47 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import swell from 'swell-js'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import {
|
||||
CommerceConfig,
|
||||
CommerceProvider as CoreCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@commerce'
|
||||
|
||||
import { swellProvider, SwellProvider } from './provider'
|
||||
import {
|
||||
SWELL_CHECKOUT_ID_COOKIE,
|
||||
SWELL_STORE_ID,
|
||||
SWELL_PUBLIC_KEY,
|
||||
} from './const'
|
||||
swell.init(SWELL_STORE_ID, SWELL_PUBLIC_KEY)
|
||||
|
||||
export { swellProvider }
|
||||
export type { SwellProvider }
|
||||
|
||||
export const swellConfig: any = {
|
||||
locale: 'en-us',
|
||||
cartCookie: SWELL_CHECKOUT_ID_COOKIE,
|
||||
swell,
|
||||
}
|
||||
|
||||
export type SwellConfig = Partial<CommerceConfig>
|
||||
|
||||
export type SwellProps = {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
} & SwellConfig
|
||||
|
||||
export function CommerceProvider({ children, ...config }: SwellProps) {
|
||||
return (
|
||||
<CoreCommerceProvider
|
||||
// TODO: Fix this type
|
||||
provider={swellProvider as any}
|
||||
config={{ ...swellConfig, ...config }}
|
||||
>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useCommerce = () => useCoreCommerce()
|
@ -1,2 +0,0 @@
|
||||
export * from '@commerce/product/use-price'
|
||||
export { default } from '@commerce/product/use-price'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/cart'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/checkout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/common'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/customer'
|
@ -1,11 +0,0 @@
|
||||
import * as Core from '@commerce/types/login'
|
||||
import { LoginBody, LoginTypes } from '@commerce/types/login'
|
||||
|
||||
export * from '@commerce/types/login'
|
||||
|
||||
export type LoginHook<T extends LoginTypes = LoginTypes> = {
|
||||
data: null
|
||||
actionInput: LoginBody
|
||||
fetcherInput: LoginBody
|
||||
body: T['body']
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/logout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/page'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/product'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/signup'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/site'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/wishlist'
|
@ -1,33 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import { ReactNode } from 'react'
|
||||
import {
|
||||
CommerceConfig,
|
||||
CommerceProvider as CoreCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@commerce'
|
||||
import { vendureProvider } from './provider'
|
||||
|
||||
export const vendureConfig: CommerceConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: 'session',
|
||||
}
|
||||
|
||||
export type VendureConfig = Partial<CommerceConfig>
|
||||
|
||||
export type VendureProps = {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
} & VendureConfig
|
||||
|
||||
export function CommerceProvider({ children, ...config }: VendureProps) {
|
||||
return (
|
||||
<CoreCommerceProvider
|
||||
provider={vendureProvider}
|
||||
config={{ ...vendureConfig, ...config }}
|
||||
>
|
||||
{children}
|
||||
</CoreCommerceProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useCommerce = () => useCoreCommerce()
|
@ -1,2 +0,0 @@
|
||||
export * from '@commerce/product/use-price'
|
||||
export { default } from '@commerce/product/use-price'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/cart'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/checkout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/common'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/customer'
|
@ -1,12 +0,0 @@
|
||||
import * as Core from '@commerce/types/login'
|
||||
import type { LoginMutationVariables } from '../schema'
|
||||
import { LoginBody, LoginTypes } from '@commerce/types/login'
|
||||
|
||||
export * from '@commerce/types/login'
|
||||
|
||||
export type LoginHook<T extends LoginTypes = LoginTypes> = {
|
||||
data: null
|
||||
actionInput: LoginBody
|
||||
fetcherInput: LoginBody
|
||||
body: T['body']
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/logout'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/page'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/product'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/signup'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/site'
|
@ -1 +0,0 @@
|
||||
export * from '@commerce/types/wishlist'
|
3
next-env.d.ts
vendored
3
next-env.d.ts
vendored
@ -1,3 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
128
package.json
128
package.json
@ -1,121 +1,27 @@
|
||||
{
|
||||
"name": "nextjs-commerce",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "NODE_OPTIONS='--inspect' next dev -p 4000",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"analyze": "BUNDLE_ANALYZE=both yarn build",
|
||||
"prettier-fix": "prettier --write .",
|
||||
"find:unused": "npx next-unused",
|
||||
"generate": "graphql-codegen",
|
||||
"generate:reactioncommerce": "graphql-codegen --config framework/reactioncommerce/codegen.json",
|
||||
"generate:shopify": "DOTENV_CONFIG_PATH=./.env.local graphql-codegen -r dotenv/config --config framework/shopify/codegen.json",
|
||||
"generate:vendure": "graphql-codegen --config framework/vendure/codegen.json",
|
||||
"generate:definitions": "node framework/bigcommerce/scripts/generate-definitions.js"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"name": "commerce",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-spring/web": "^9.2.1",
|
||||
"@stripe/react-stripe-js": "^1.4.1",
|
||||
"@stripe/stripe-js": "^1.16.0",
|
||||
"@vercel/fetch": "^6.1.0",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"body-scroll-lock": "^3.1.5",
|
||||
"classnames": "^2.3.1",
|
||||
"cookie": "^0.4.1",
|
||||
"email-validator": "^2.0.4",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"js-cookie": "^2.2.1",
|
||||
"keen-slider": "^5.5.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.random": "^3.2.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"next": "^11.0.0",
|
||||
"next-seo": "^4.26.0",
|
||||
"next-themes": "^0.0.14",
|
||||
"postcss": "^8.3.5",
|
||||
"postcss-nesting": "^8.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-fast-marquee": "^1.1.4",
|
||||
"react-merge-refs": "^1.1.0",
|
||||
"react-use-measure": "^2.0.4",
|
||||
"swell-js": "^4.0.0-next.0",
|
||||
"swr": "^0.5.6",
|
||||
"tabbable": "^5.2.0",
|
||||
"tailwindcss": "^2.2.2",
|
||||
"uuidv4": "^6.2.10"
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"site",
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "turbo run build --scope=next-commerce --include-dependencies --no-deps",
|
||||
"dev": "turbo run dev",
|
||||
"start": "turbo run start",
|
||||
"types": "turbo run types",
|
||||
"prettier-fix": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "^1.21.5",
|
||||
"@graphql-codegen/schema-ast": "^1.18.3",
|
||||
"@graphql-codegen/typescript": "^1.22.2",
|
||||
"@graphql-codegen/typescript-operations": "^1.18.1",
|
||||
"@next/bundle-analyzer": "^10.2.3",
|
||||
"@types/body-scroll-lock": "^2.6.1",
|
||||
"@types/cookie": "^0.4.0",
|
||||
"@types/js-cookie": "^2.2.6",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/lodash.random": "^3.2.6",
|
||||
"@types/lodash.throttle": "^4.1.6",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/react": "^17.0.8",
|
||||
"deepmerge": "^4.2.2",
|
||||
"graphql": "^15.5.1",
|
||||
"husky": "^6.0.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
"postcss-flexbugs-fixes": "^5.0.2",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"prettier": "^2.3.0",
|
||||
"typescript": "4.3.4"
|
||||
"husky": "^7.0.4",
|
||||
"prettier": "^2.5.1",
|
||||
"turbo": "^1.1.2"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
"pre-commit": "turbo run lint"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
],
|
||||
"**/*.{md,mdx,json}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"next-unused": {
|
||||
"alias": {
|
||||
"@lib/*": [
|
||||
"lib/*"
|
||||
],
|
||||
"@assets/*": [
|
||||
"assets/*"
|
||||
],
|
||||
"@config/*": [
|
||||
"config/*"
|
||||
],
|
||||
"@components/*": [
|
||||
"components/*"
|
||||
],
|
||||
"@utils/*": [
|
||||
"utils/*"
|
||||
]
|
||||
},
|
||||
"debug": true,
|
||||
"include": [
|
||||
"components",
|
||||
"lib",
|
||||
"pages"
|
||||
],
|
||||
"exclude": [],
|
||||
"entrypoints": [
|
||||
"pages"
|
||||
]
|
||||
}
|
||||
"packageManager": "yarn@1.22.17"
|
||||
}
|
||||
|
2
packages/bigcommerce/.prettierignore
Normal file
2
packages/bigcommerce/.prettierignore
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
6
packages/bigcommerce/.prettierrc
Normal file
6
packages/bigcommerce/.prettierrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
@ -9,7 +9,7 @@ With the deploy button below you'll be able to have a [BigCommerce](https://www.
|
||||
If you already have a BigCommerce account and want to use your current store, then copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git):
|
||||
|
||||
```bash
|
||||
cp framework/bigcommerce/.env.template .env.local
|
||||
cp packages/bigcommerce/.env.template .env.local
|
||||
```
|
||||
|
||||
Then, set the environment variables in `.env.local` to match the ones from your store.
|
@ -8,16 +8,16 @@
|
||||
},
|
||||
"documents": [
|
||||
{
|
||||
"./framework/bigcommerce/api/**/*.ts": {
|
||||
"./src/api/**/*.ts": {
|
||||
"noRequire": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"generates": {
|
||||
"./framework/bigcommerce/schema.d.ts": {
|
||||
"./schema.d.ts": {
|
||||
"plugins": ["typescript", "typescript-operations"]
|
||||
},
|
||||
"./framework/bigcommerce/schema.graphql": {
|
||||
"./schema.graphql": {
|
||||
"plugins": ["schema-ast"]
|
||||
}
|
||||
},
|
86
packages/bigcommerce/package.json
Normal file
86
packages/bigcommerce/package.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"name": "@vercel/commerce-bigcommerce",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"release": "taskr release",
|
||||
"build": "taskr build",
|
||||
"dev": "taskr",
|
||||
"types": "tsc --emitDeclarationOnly",
|
||||
"generate:definitions": "node scripts/generate-definitions.js"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./*": [
|
||||
"./dist/*.js",
|
||||
"./dist/*/index.js"
|
||||
],
|
||||
"./next.config": "./dist/next.config.cjs"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"src/*",
|
||||
"src/*/index"
|
||||
],
|
||||
"next.config": [
|
||||
"dist/next.config.d.cts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"publishConfig": {
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"dist/*.d.ts",
|
||||
"dist/*/index.d.ts"
|
||||
],
|
||||
"config": [
|
||||
"dist/next.config.d.cts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/fetch": "^6.1.1",
|
||||
"cookie": "^0.4.1",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"uuidv4": "^6.2.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^12",
|
||||
"react": "^17",
|
||||
"react-dom": "^17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@taskr/clear": "^1.1.0",
|
||||
"@taskr/esnext": "^1.1.0",
|
||||
"@taskr/watch": "^1.1.0",
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/jsonwebtoken": "^8.5.7",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/node": "^17.0.8",
|
||||
"@types/react": "^17.0.38",
|
||||
"lint-staged": "^12.1.7",
|
||||
"next": "^12.0.8",
|
||||
"prettier": "^2.5.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"taskr": "^1.1.0",
|
||||
"taskr-swc": "^0.0.1",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,jsx,ts,tsx,json}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
@ -27,11 +27,11 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({
|
||||
}
|
||||
const { data } = cartId
|
||||
? await config.storeApiFetch(
|
||||
`/v3/carts/${cartId}/items?include=line_items.physical_items.options`,
|
||||
`/v3/carts/${cartId}/items?include=line_items.physical_items.options,line_items.digital_items.options`,
|
||||
options
|
||||
)
|
||||
: await config.storeApiFetch(
|
||||
'/v3/carts?include=line_items.physical_items.options',
|
||||
'/v3/carts?include=line_items.physical_items.options,line_items.digital_items.options',
|
||||
options
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({
|
||||
if (cartId) {
|
||||
try {
|
||||
result = await config.storeApiFetch(
|
||||
`/v3/carts/${cartId}?include=line_items.physical_items.options`
|
||||
`/v3/carts/${cartId}?include=line_items.physical_items.options,line_items.digital_items.options`
|
||||
)
|
||||
} catch (error) {
|
||||
if (error instanceof BigcommerceApiError && error.status === 404) {
|
@ -1,5 +1,5 @@
|
||||
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||
import cartEndpoint from '@commerce/api/endpoints/cart'
|
||||
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
|
||||
import cartEndpoint from '@vercel/commerce/api/endpoints/cart'
|
||||
import type { CartSchema } from '../../../types/cart'
|
||||
import type { BigcommerceAPI } from '../..'
|
||||
import getCart from './get-cart'
|
@ -1,4 +1,4 @@
|
||||
import { Product } from '@commerce/types/product'
|
||||
import { Product } from '@vercel/commerce/types/product'
|
||||
import { ProductsEndpoint } from '.'
|
||||
|
||||
const SORT: { [key: string]: string | undefined } = {
|
@ -1,5 +1,5 @@
|
||||
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||
import productsEndpoint from '@commerce/api/endpoints/catalog/products'
|
||||
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
|
||||
import productsEndpoint from '@vercel/commerce/api/endpoints/catalog/products'
|
||||
import type { ProductsSchema } from '../../../../types/product'
|
||||
import type { BigcommerceAPI } from '../../..'
|
||||
import getProducts from './get-products'
|
@ -5,7 +5,7 @@ import { uuid } from 'uuidv4'
|
||||
|
||||
const fullCheckout = true
|
||||
|
||||
const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
|
||||
const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({
|
||||
req,
|
||||
res,
|
||||
config,
|
||||
@ -42,7 +42,7 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
|
||||
store_hash: config.storeHash,
|
||||
customer_id: customerId,
|
||||
channel_id: config.storeChannelId,
|
||||
redirect_to: data.checkout_url,
|
||||
redirect_to: data.checkout_url.replace(config.storeUrl, ""),
|
||||
}
|
||||
let token = jwt.sign(payload, config.storeApiClientSecret!, {
|
||||
algorithm: 'HS256',
|
||||
@ -87,4 +87,4 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
|
||||
res.end()
|
||||
}
|
||||
|
||||
export default checkout
|
||||
export default getCheckout
|
@ -1,14 +1,14 @@
|
||||
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||
import checkoutEndpoint from '@commerce/api/endpoints/checkout'
|
||||
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
|
||||
import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout'
|
||||
import type { CheckoutSchema } from '../../../types/checkout'
|
||||
import type { BigcommerceAPI } from '../..'
|
||||
import checkout from './checkout'
|
||||
import getCheckout from './get-checkout'
|
||||
|
||||
export type CheckoutAPI = GetAPISchema<BigcommerceAPI, CheckoutSchema>
|
||||
|
||||
export type CheckoutEndpoint = CheckoutAPI['endpoint']
|
||||
|
||||
export const handlers: CheckoutEndpoint['handlers'] = { checkout }
|
||||
export const handlers: CheckoutEndpoint['handlers'] = { getCheckout }
|
||||
|
||||
const checkoutApi = createEndpoint<CheckoutAPI>({
|
||||
handler: checkoutEndpoint,
|
@ -1,4 +1,4 @@
|
||||
import type { GetLoggedInCustomerQuery } from '../../../schema'
|
||||
import type { GetLoggedInCustomerQuery } from '../../../../schema'
|
||||
import type { CustomerEndpoint } from '.'
|
||||
|
||||
export const getLoggedInCustomerQuery = /* GraphQL */ `
|
@ -1,5 +1,5 @@
|
||||
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||
import customerEndpoint from '@commerce/api/endpoints/customer'
|
||||
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
|
||||
import customerEndpoint from '@vercel/commerce/api/endpoints/customer'
|
||||
import type { CustomerSchema } from '../../../types/customer'
|
||||
import type { BigcommerceAPI } from '../..'
|
||||
import getLoggedInCustomer from './get-logged-in-customer'
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user