diff --git a/.env.template b/.env.template index cf58e89c7..8685fd41d 100644 --- a/.env.template +++ b/.env.template @@ -1,4 +1,4 @@ -# Available providers: local, bigcommerce, shopify, swell, saleor, spree, ordercloud, vendure, kibocommerce, commercejs +# Available providers: bigcommerce, shopify, swell, commercelayer COMMERCE_PROVIDER= BIGCOMMERCE_STOREFRONT_API_URL= diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..1e3e664b2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-msedge", + "request": "launch", + "name": "Launch Edge against localhost", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/framework/commercelayer/.env.template b/framework/commercelayer/.env.template new file mode 100644 index 000000000..46725853b --- /dev/null +++ b/framework/commercelayer/.env.template @@ -0,0 +1,5 @@ +COMMERCE_PROVIDER=commercelayer + +COMMERCELAYER_CLIENT_ID= +COMMERCELAYER_ENDPOINT= +COMMERCELAYER_MARKET_SCOPE= \ No newline at end of file diff --git a/framework/commercelayer/README.md b/framework/commercelayer/README.md new file mode 100644 index 000000000..82789aed5 --- /dev/null +++ b/framework/commercelayer/README.md @@ -0,0 +1,23 @@ +# Commerce Layer Provider + +⚠️ Please note that this provider is still a work in progress. + +Before getting started, you should do the following: + +- Create a Commerce Layer [developer account](https://commercelayer.io). +- Create a new [organization](https://commercelayer.io/docs/data-model/users-and-organizations/) for your business. +- Create an application with `sales_channel` kind. + +Next, copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git): + +```bash +cp framework/commercelayer/.env.template .env.local +``` + +Next, add the application credentials from your organization application dashboard in `.env.local`. + +## Contribute + +Our commitment to Open Source can be found [here](https://vercel.com/oss). + +If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues). \ No newline at end of file diff --git a/framework/commercelayer/api/endpoints/cart/index.ts b/framework/commercelayer/api/endpoints/cart/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/cart/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/catalog/index.ts b/framework/commercelayer/api/endpoints/catalog/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/catalog/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/catalog/products.ts b/framework/commercelayer/api/endpoints/catalog/products.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/catalog/products.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/checkout/index.ts b/framework/commercelayer/api/endpoints/checkout/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/checkout/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/customer/index.ts b/framework/commercelayer/api/endpoints/customer/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/customer/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/login/index.ts b/framework/commercelayer/api/endpoints/login/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/login/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/logout/index.ts b/framework/commercelayer/api/endpoints/logout/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/logout/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/signup/index.ts b/framework/commercelayer/api/endpoints/signup/index.ts new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/signup/index.ts @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/endpoints/wishlist/index.tsx b/framework/commercelayer/api/endpoints/wishlist/index.tsx new file mode 100644 index 000000000..491bf0ac9 --- /dev/null +++ b/framework/commercelayer/api/endpoints/wishlist/index.tsx @@ -0,0 +1 @@ +export default function noopApi(...args: any[]): void {} diff --git a/framework/commercelayer/api/index.ts b/framework/commercelayer/api/index.ts new file mode 100644 index 000000000..9f403d2b3 --- /dev/null +++ b/framework/commercelayer/api/index.ts @@ -0,0 +1,42 @@ +import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api' +import { getCommerceApi as commerceApi } from '@commerce/api' +import createFetcher from './utils/fetch-local' + +import getAllPages from './operations/get-all-pages' +import getPage from './operations/get-page' +import getSiteInfo from './operations/get-site-info' +import getCustomerWishlist from './operations/get-customer-wishlist' +import getAllProductPaths from './operations/get-all-product-paths' +import getAllProducts from './operations/get-all-products' +import getProduct from './operations/get-product' + +export interface LocalConfig extends CommerceAPIConfig {} +const config: LocalConfig = { + commerceUrl: '', + apiToken: '', + cartCookie: '', + customerCookie: '', + cartCookieMaxAge: 2592000, + fetch: createFetcher(() => getCommerceApi().getConfig()), +} + +const operations = { + getAllPages, + getPage, + getSiteInfo, + getCustomerWishlist, + getAllProductPaths, + getAllProducts, + getProduct, +} + +export const provider = { config, operations } + +export type Provider = typeof provider +export type LocalAPI

= CommerceAPI

+ +export function getCommerceApi

( + customProvider: P = provider as any +): LocalAPI

{ + return commerceApi(customProvider as any) +} diff --git a/framework/commercelayer/api/operations/get-all-pages.ts b/framework/commercelayer/api/operations/get-all-pages.ts new file mode 100644 index 000000000..b258fe70a --- /dev/null +++ b/framework/commercelayer/api/operations/get-all-pages.ts @@ -0,0 +1,19 @@ +export type Page = { url: string } +export type GetAllPagesResult = { pages: Page[] } +import type { LocalConfig } from '../index' + +export default function getAllPagesOperation() { + function getAllPages({ + config, + preview, + }: { + url?: string + config?: Partial + preview?: boolean + }): Promise { + return Promise.resolve({ + pages: [], + }) + } + return getAllPages +} diff --git a/framework/commercelayer/api/operations/get-all-product-paths.ts b/framework/commercelayer/api/operations/get-all-product-paths.ts new file mode 100644 index 000000000..fff24e791 --- /dev/null +++ b/framework/commercelayer/api/operations/get-all-product-paths.ts @@ -0,0 +1,15 @@ +import data from '../../data.json' + +export type GetAllProductPathsResult = { + products: Array<{ path: string }> +} + +export default function getAllProductPathsOperation() { + function getAllProductPaths(): Promise { + return Promise.resolve({ + products: data.products.map(({ path }) => ({ path })), + }) + } + + return getAllProductPaths +} diff --git a/framework/commercelayer/api/operations/get-all-products.ts b/framework/commercelayer/api/operations/get-all-products.ts new file mode 100644 index 000000000..21a04559d --- /dev/null +++ b/framework/commercelayer/api/operations/get-all-products.ts @@ -0,0 +1,25 @@ +import { Product } from '@commerce/types/product' +import { GetAllProductsOperation } from '@commerce/types/product' +import type { OperationContext } from '@commerce/api/operations' +import type { LocalConfig, Provider } from '../index' +import data from '../../data.json' + +export default function getAllProductsOperation({ + commerce, +}: OperationContext) { + async function getAllProducts({ + query = '', + variables, + config, + }: { + query?: string + variables?: T['variables'] + config?: Partial + preview?: boolean + } = {}): Promise<{ products: Product[] | any[] }> { + return { + products: data.products, + } + } + return getAllProducts +} diff --git a/framework/commercelayer/api/operations/get-customer-wishlist.ts b/framework/commercelayer/api/operations/get-customer-wishlist.ts new file mode 100644 index 000000000..8c34b9e87 --- /dev/null +++ b/framework/commercelayer/api/operations/get-customer-wishlist.ts @@ -0,0 +1,6 @@ +export default function getCustomerWishlistOperation() { + function getCustomerWishlist(): any { + return { wishlist: {} } + } + return getCustomerWishlist +} diff --git a/framework/commercelayer/api/operations/get-page.ts b/framework/commercelayer/api/operations/get-page.ts new file mode 100644 index 000000000..b0cfdf58f --- /dev/null +++ b/framework/commercelayer/api/operations/get-page.ts @@ -0,0 +1,13 @@ +export type Page = any +export type GetPageResult = { page?: Page } + +export type PageVariables = { + id: number +} + +export default function getPageOperation() { + function getPage(): Promise { + return Promise.resolve({}) + } + return getPage +} diff --git a/framework/commercelayer/api/operations/get-product.ts b/framework/commercelayer/api/operations/get-product.ts new file mode 100644 index 000000000..690b1aab9 --- /dev/null +++ b/framework/commercelayer/api/operations/get-product.ts @@ -0,0 +1,26 @@ +import type { LocalConfig } from '../index' +import { Product } from '@commerce/types/product' +import { GetProductOperation } from '@commerce/types/product' +import data from '../../data.json' +import type { OperationContext } from '@commerce/api/operations' + +export default function getProductOperation({ + commerce, +}: OperationContext) { + async function getProduct({ + query = '', + variables, + config, + }: { + query?: string + variables?: T['variables'] + config?: Partial + preview?: boolean + } = {}): Promise { + return { + product: data.products.find(({ slug }) => slug === variables!.slug), + } + } + + return getProduct +} diff --git a/framework/commercelayer/api/operations/get-site-info.ts b/framework/commercelayer/api/operations/get-site-info.ts new file mode 100644 index 000000000..d43ed8359 --- /dev/null +++ b/framework/commercelayer/api/operations/get-site-info.ts @@ -0,0 +1,43 @@ +import { OperationContext } from '@commerce/api/operations' +import { Category } from '@commerce/types/site' +import { LocalConfig } from '../index' + +export type GetSiteInfoResult< + T extends { categories: any[]; brands: any[] } = { + categories: Category[] + brands: any[] + } +> = T + +export default function getSiteInfoOperation({}: OperationContext) { + function getSiteInfo({ + query, + variables, + config: cfg, + }: { + query?: string + variables?: any + config?: Partial + preview?: boolean + } = {}): Promise { + return Promise.resolve({ + categories: [ + { + id: 'new-arrivals', + name: 'New Arrivals', + slug: 'new-arrivals', + path: '/new-arrivals', + }, + { + id: 'featured', + name: 'Featured', + slug: 'featured', + path: '/featured', + }, + ], + brands: [], + }) + } + + return getSiteInfo +} diff --git a/framework/commercelayer/api/operations/index.ts b/framework/commercelayer/api/operations/index.ts new file mode 100644 index 000000000..086fdf83a --- /dev/null +++ b/framework/commercelayer/api/operations/index.ts @@ -0,0 +1,6 @@ +export { default as getPage } from './get-page' +export { default as getSiteInfo } from './get-site-info' +export { default as getAllPages } from './get-all-pages' +export { default as getProduct } from './get-product' +export { default as getAllProducts } from './get-all-products' +export { default as getAllProductPaths } from './get-all-product-paths' diff --git a/framework/commercelayer/api/utils/fetch-local.ts b/framework/commercelayer/api/utils/fetch-local.ts new file mode 100644 index 000000000..aa85cf27b --- /dev/null +++ b/framework/commercelayer/api/utils/fetch-local.ts @@ -0,0 +1,34 @@ +import { FetcherError } from '@commerce/utils/errors' +import type { GraphQLFetcher } from '@commerce/api' +import type { LocalConfig } from '../index' +import fetch from './fetch' + +const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher = + (getConfig) => + async (query: string, { variables, preview } = {}, fetchOptions) => { + const config = getConfig() + const res = await fetch(config.commerceUrl, { + ...fetchOptions, + method: 'POST', + headers: { + ...fetchOptions?.headers, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables, + }), + }) + + const json = await res.json() + if (json.errors) { + throw new FetcherError({ + errors: json.errors ?? [{ message: 'Failed to fetch for API' }], + status: res.status, + }) + } + + return { data: json.data, res } + } + +export default fetchGraphqlApi diff --git a/framework/commercelayer/api/utils/fetch.ts b/framework/commercelayer/api/utils/fetch.ts new file mode 100644 index 000000000..9d9fff3ed --- /dev/null +++ b/framework/commercelayer/api/utils/fetch.ts @@ -0,0 +1,3 @@ +import zeitFetch from '@vercel/fetch' + +export default zeitFetch() diff --git a/framework/commercelayer/auth/index.ts b/framework/commercelayer/auth/index.ts new file mode 100644 index 000000000..36e757a89 --- /dev/null +++ b/framework/commercelayer/auth/index.ts @@ -0,0 +1,3 @@ +export { default as useLogin } from './use-login' +export { default as useLogout } from './use-logout' +export { default as useSignup } from './use-signup' diff --git a/framework/commercelayer/auth/use-login.tsx b/framework/commercelayer/auth/use-login.tsx new file mode 100644 index 000000000..28351dc7f --- /dev/null +++ b/framework/commercelayer/auth/use-login.tsx @@ -0,0 +1,16 @@ +import { MutationHook } from '@commerce/utils/types' +import useLogin, { UseLogin } from '@commerce/auth/use-login' + +export default useLogin as UseLogin + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return null + }, + useHook: () => () => { + return async function () {} + }, +} diff --git a/framework/commercelayer/auth/use-logout.tsx b/framework/commercelayer/auth/use-logout.tsx new file mode 100644 index 000000000..9b3fc3e44 --- /dev/null +++ b/framework/commercelayer/auth/use-logout.tsx @@ -0,0 +1,17 @@ +import { MutationHook } from '@commerce/utils/types' +import useLogout, { UseLogout } from '@commerce/auth/use-logout' + +export default useLogout as UseLogout + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return null + }, + useHook: + ({ fetch }) => + () => + async () => {}, +} diff --git a/framework/commercelayer/auth/use-signup.tsx b/framework/commercelayer/auth/use-signup.tsx new file mode 100644 index 000000000..e9ad13458 --- /dev/null +++ b/framework/commercelayer/auth/use-signup.tsx @@ -0,0 +1,19 @@ +import { useCallback } from 'react' +import useCustomer from '../customer/use-customer' +import { MutationHook } from '@commerce/utils/types' +import useSignup, { UseSignup } from '@commerce/auth/use-signup' + +export default useSignup as UseSignup + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return null + }, + useHook: + ({ fetch }) => + () => + () => {}, +} diff --git a/framework/commercelayer/cart/index.ts b/framework/commercelayer/cart/index.ts new file mode 100644 index 000000000..3b8ba990e --- /dev/null +++ b/framework/commercelayer/cart/index.ts @@ -0,0 +1,4 @@ +export { default as useCart } from './use-cart' +export { default as useAddItem } from './use-add-item' +export { default as useRemoveItem } from './use-remove-item' +export { default as useUpdateItem } from './use-update-item' diff --git a/framework/commercelayer/cart/use-add-item.tsx b/framework/commercelayer/cart/use-add-item.tsx new file mode 100644 index 000000000..7f3d1061f --- /dev/null +++ b/framework/commercelayer/cart/use-add-item.tsx @@ -0,0 +1,17 @@ +import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' +import { MutationHook } from '@commerce/utils/types' + +export default useAddItem as UseAddItem +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => { + return async function addItem() { + return {} + } + }, +} diff --git a/framework/commercelayer/cart/use-cart.tsx b/framework/commercelayer/cart/use-cart.tsx new file mode 100644 index 000000000..b3e509a21 --- /dev/null +++ b/framework/commercelayer/cart/use-cart.tsx @@ -0,0 +1,42 @@ +import { useMemo } from 'react' +import { SWRHook } from '@commerce/utils/types' +import useCart, { UseCart } from '@commerce/cart/use-cart' + +export default useCart as UseCart + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher() { + return { + id: '', + createdAt: '', + currency: { code: '' }, + taxesIncluded: '', + lineItems: [], + lineItemsSubtotalPrice: '', + subtotalPrice: 0, + totalPrice: 0, + } + }, + useHook: + ({ useData }) => + (input) => { + return useMemo( + () => + Object.create( + {}, + { + isEmpty: { + get() { + return true + }, + enumerable: true, + }, + } + ), + [] + ) + }, +} diff --git a/framework/commercelayer/cart/use-remove-item.tsx b/framework/commercelayer/cart/use-remove-item.tsx new file mode 100644 index 000000000..b4ed583b8 --- /dev/null +++ b/framework/commercelayer/cart/use-remove-item.tsx @@ -0,0 +1,18 @@ +import { MutationHook } from '@commerce/utils/types' +import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item' + +export default useRemoveItem as UseRemoveItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => { + return async function removeItem(input) { + return {} + } + }, +} diff --git a/framework/commercelayer/cart/use-update-item.tsx b/framework/commercelayer/cart/use-update-item.tsx new file mode 100644 index 000000000..06d703f70 --- /dev/null +++ b/framework/commercelayer/cart/use-update-item.tsx @@ -0,0 +1,18 @@ +import { MutationHook } from '@commerce/utils/types' +import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item' + +export default useUpdateItem as UseUpdateItem + +export const handler: MutationHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: + ({ fetch }) => + () => { + return async function addItem() { + return {} + } + }, +} diff --git a/framework/commercelayer/commerce.config.json b/framework/commercelayer/commerce.config.json new file mode 100644 index 000000000..261211527 --- /dev/null +++ b/framework/commercelayer/commerce.config.json @@ -0,0 +1,9 @@ +{ + "provider": "local", + "features": { + "wishlist": false, + "cart": false, + "search": false, + "customerAuth": false + } +} diff --git a/framework/commercelayer/customer/index.ts b/framework/commercelayer/customer/index.ts new file mode 100644 index 000000000..6c903ecc5 --- /dev/null +++ b/framework/commercelayer/customer/index.ts @@ -0,0 +1 @@ +export { default as useCustomer } from './use-customer' diff --git a/framework/commercelayer/customer/use-customer.tsx b/framework/commercelayer/customer/use-customer.tsx new file mode 100644 index 000000000..41757cd0d --- /dev/null +++ b/framework/commercelayer/customer/use-customer.tsx @@ -0,0 +1,15 @@ +import { SWRHook } from '@commerce/utils/types' +import useCustomer, { UseCustomer } from '@commerce/customer/use-customer' + +export default useCustomer as UseCustomer +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: () => () => { + return async function addItem() { + return {} + } + }, +} diff --git a/framework/commercelayer/data.json b/framework/commercelayer/data.json new file mode 100644 index 000000000..18c8ee718 --- /dev/null +++ b/framework/commercelayer/data.json @@ -0,0 +1,235 @@ +{ + "products": [ + { + "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjA=", + "name": "New Short Sleeve T-Shirt", + "vendor": "Next.js", + "path": "/new-short-sleeve-t-shirt", + "slug": "new-short-sleeve-t-shirt", + "price": { "value": 25, "currencyCode": "USD" }, + "descriptionHtml": "

Show off your love for Next.js and Vercel with this unique, limited edition t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! All proceeds will be donated to charity.

", + "images": [ + { + "url": "/assets/drop-shirt-0.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/drop-shirt-1.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/drop-shirt-2.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + } + ], + "variants": [ + { + "id": "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjAss=", + "options": [ + { + "__typename": "MultipleChoiceOption", + "id": "asd", + "displayName": "Size", + "values": [ + { + "label": "XL" + } + ] + } + ] + } + ], + "options": [ + { + "id": "option-color", + "displayName": "Color", + "values": [ + { + "label": "color", + "hexColors": ["#222"] + } + ] + }, + { + "id": "option-size", + "displayName": "Size", + "values": [ + { + "label": "S" + }, + { + "label": "M" + }, + { + "label": "L" + } + ] + } + ] + }, + { + "id": "Z2lkOi8vc2hvcGlmeS9Qcm9ksdWN0LzU0NDczMjUwMjQ0MjA=", + "name": "Lightweight Jacket", + "vendor": "Next.js", + "path": "/lightweight-jacket", + "slug": "lightweight-jacket", + "price": { "value": 249.99, "currencyCode": "USD" }, + "descriptionHtml": "

Show off your love for Next.js and Vercel with this unique, limited edition t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! All proceeds will be donated to charity.

", + "images": [ + { + "url": "/assets/lightweight-jacket-0.png", + "altText": "Lightweight Jacket", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/lightweight-jacket-1.png", + "altText": "Lightweight Jacket", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/lightweight-jacket-2.png", + "altText": "Lightweight Jacket", + "width": 1000, + "height": 1000 + } + ], + "variants": [ + { + "id": "Z2lkOid8vc2hvcGlmeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjAss=", + "options": [ + { + "__typename": "MultipleChoiceOption", + "id": "asd", + "displayName": "Size", + "values": [ + { + "label": "XL" + } + ] + } + ] + } + ], + "options": [ + { + "id": "option-color", + "displayName": "Color", + "values": [ + { + "label": "color", + "hexColors": ["#222"] + } + ] + }, + { + "id": "option-size", + "displayName": "Size", + "values": [ + { + "label": "S" + }, + { + "label": "M" + }, + { + "label": "L" + } + ] + } + ] + }, + { + "id": "Z2lkOis8vc2hvcGlmsddeS9Qcm9kdWN0LzU0NDczMjUwMjQ0MjA=", + "name": "Shirt", + "vendor": "Next.js", + "path": "/shirt", + "slug": "shirt", + "price": { "value": 25, "currencyCode": "USD" }, + "descriptionHtml": "

Show off your love for Next.js and Vercel with this unique, limited edition t-shirt. This design is part of a limited run, numbered drop at the June 2021 Next.js Conf. It features a unique, handcrafted triangle design. Get it while supplies last – only 200 of these shirts will be made! All proceeds will be donated to charity.

", + "images": [ + { + "url": "/assets/t-shirt-0.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/t-shirt-1.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/t-shirt-2.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/t-shirt-3.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + }, + { + "url": "/assets/t-shirt-4.png", + "altText": "Shirt", + "width": 1000, + "height": 1000 + } + ], + "variants": [ + { + "id": "Z2lkOi8vc2hvcGlmeS9Qcms9kdWN0LzU0NDczMjUwMjQ0MjAss=", + "options": [ + { + "__typename": "MultipleChoiceOption", + "id": "asd", + "displayName": "Size", + "values": [ + { + "label": "XL" + } + ] + } + ] + } + ], + "options": [ + { + "id": "option-color", + "displayName": "Color", + "values": [ + { + "label": "color", + "hexColors": ["#222"] + } + ] + }, + { + "id": "option-size", + "displayName": "Size", + "values": [ + { + "label": "S" + }, + { + "label": "M" + }, + { + "label": "L" + } + ] + } + ] + } + ] +} diff --git a/framework/commercelayer/fetcher.ts b/framework/commercelayer/fetcher.ts new file mode 100644 index 000000000..69943d1df --- /dev/null +++ b/framework/commercelayer/fetcher.ts @@ -0,0 +1,11 @@ +import { Fetcher } from '@commerce/utils/types' + +export const fetcher: Fetcher = async () => { + console.log('FETCHER') + const res = await fetch('./data.json') + if (res.ok) { + const { data } = await res.json() + return data + } + throw res +} diff --git a/framework/commercelayer/index.tsx b/framework/commercelayer/index.tsx new file mode 100644 index 000000000..2ec304f63 --- /dev/null +++ b/framework/commercelayer/index.tsx @@ -0,0 +1,32 @@ +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) { + return ( + + {children} + + ) +} + +export const useCommerce = () => useCoreCommerce() diff --git a/framework/commercelayer/next.config.js b/framework/commercelayer/next.config.js new file mode 100644 index 000000000..ce46b706f --- /dev/null +++ b/framework/commercelayer/next.config.js @@ -0,0 +1,8 @@ +const commerce = require('./commerce.config.json') + +module.exports = { + commerce, + images: { + domains: ['localhost'], + }, +} diff --git a/framework/commercelayer/product/index.ts b/framework/commercelayer/product/index.ts new file mode 100644 index 000000000..426a3edcd --- /dev/null +++ b/framework/commercelayer/product/index.ts @@ -0,0 +1,2 @@ +export { default as usePrice } from './use-price' +export { default as useSearch } from './use-search' diff --git a/framework/commercelayer/product/use-price.tsx b/framework/commercelayer/product/use-price.tsx new file mode 100644 index 000000000..0174faf5e --- /dev/null +++ b/framework/commercelayer/product/use-price.tsx @@ -0,0 +1,2 @@ +export * from '@commerce/product/use-price' +export { default } from '@commerce/product/use-price' diff --git a/framework/commercelayer/product/use-search.tsx b/framework/commercelayer/product/use-search.tsx new file mode 100644 index 000000000..30e699537 --- /dev/null +++ b/framework/commercelayer/product/use-search.tsx @@ -0,0 +1,17 @@ +import { SWRHook } from '@commerce/utils/types' +import useSearch, { UseSearch } from '@commerce/product/use-search' +export default useSearch as UseSearch + +export const handler: SWRHook = { + fetchOptions: { + query: '', + }, + async fetcher({ input, options, fetch }) {}, + useHook: () => () => { + return { + data: { + products: [], + }, + } + }, +} diff --git a/framework/commercelayer/provider.ts b/framework/commercelayer/provider.ts new file mode 100644 index 000000000..e6a2b0a21 --- /dev/null +++ b/framework/commercelayer/provider.ts @@ -0,0 +1,21 @@ +import { fetcher } from './fetcher' +import { handler as useCart } from './cart/use-cart' +import { handler as useAddItem } from './cart/use-add-item' +import { handler as useUpdateItem } from './cart/use-update-item' +import { handler as useRemoveItem } from './cart/use-remove-item' +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' +import { handler as useLogin } from './auth/use-login' +import { handler as useLogout } from './auth/use-logout' +import { handler as useSignup } from './auth/use-signup' + +export type Provider = typeof localProvider +export const localProvider = { + locale: 'en-us', + cartCookie: 'session', + fetcher: fetcher, + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, + customer: { useCustomer }, + products: { useSearch }, + auth: { useLogin, useLogout, useSignup }, +} diff --git a/framework/commercelayer/wishlist/use-add-item.tsx b/framework/commercelayer/wishlist/use-add-item.tsx new file mode 100644 index 000000000..75f067c3a --- /dev/null +++ b/framework/commercelayer/wishlist/use-add-item.tsx @@ -0,0 +1,13 @@ +import { useCallback } from 'react' + +export function emptyHook() { + const useEmptyHook = async (options = {}) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/commercelayer/wishlist/use-remove-item.tsx b/framework/commercelayer/wishlist/use-remove-item.tsx new file mode 100644 index 000000000..a2d3a8a05 --- /dev/null +++ b/framework/commercelayer/wishlist/use-remove-item.tsx @@ -0,0 +1,17 @@ +import { useCallback } from 'react' + +type Options = { + includeProducts?: boolean +} + +export function emptyHook(options?: Options) { + const useEmptyHook = async ({ id }: { id: string | number }) => { + return useCallback(async function () { + return Promise.resolve() + }, []) + } + + return useEmptyHook +} + +export default emptyHook diff --git a/framework/commercelayer/wishlist/use-wishlist.tsx b/framework/commercelayer/wishlist/use-wishlist.tsx new file mode 100644 index 000000000..9fe0e758f --- /dev/null +++ b/framework/commercelayer/wishlist/use-wishlist.tsx @@ -0,0 +1,43 @@ +import { HookFetcher } from '@commerce/utils/types' +import type { Product } from '@commerce/types/product' + +const defaultOpts = {} + +export type Wishlist = { + items: [ + { + product_id: number + variant_id: number + id: number + product: Product + } + ] +} + +export interface UseWishlistOptions { + includeProducts?: boolean +} + +export interface UseWishlistInput extends UseWishlistOptions { + customerId?: number +} + +export const fetcher: HookFetcher = () => { + return null +} + +export function extendHook( + customFetcher: typeof fetcher, + // swrOptions?: SwrOptions + swrOptions?: any +) { + const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => { + return { data: null } + } + + useWishlist.extend = extendHook + + return useWishlist +} + +export default extendHook(fetcher)