Merge branch 'local-provider' into custom-checkout

This commit is contained in:
Tobias Koppers 2021-06-14 21:19:18 +02:00
commit d2688df15f
59 changed files with 11939 additions and 67 deletions

View File

@ -38,19 +38,21 @@ const UserNav: FC<Props> = ({ className }) => {
</Link> </Link>
</li> </li>
)} )}
<li className={s.item}> {process.env.COMMERCE_CUSTOMER_ENABLED && (
{customer ? ( <li className={s.item}>
<DropdownMenu /> {customer ? (
) : ( <DropdownMenu />
<button ) : (
className={s.avatarButton} <button
aria-label="Menu" className={s.avatarButton}
onClick={() => openModal()} aria-label="Menu"
> onClick={() => openModal()}
<Avatar /> >
</button> <Avatar />
)} </button>
</li> )}
</li>
)}
</ul> </ul>
</nav> </nav>
) )

View File

@ -41,9 +41,9 @@ export default function Search({ categories, brands }: SearchPropsType) {
const query = filterQuery({ sort }) const query = filterQuery({ sort })
const { pathname, category, brand } = useSearchMeta(asPath) const { pathname, category, brand } = useSearchMeta(asPath)
const activeCategory = categories.find((cat) => cat.slug === category) const activeCategory = categories.find((cat: any) => cat.slug === category)
const activeBrand = brands.find( const activeBrand = brands.find(
(b) => getSlug(b.node.path) === `brands/${brand}` (b: any) => getSlug(b.node.path) === `brands/${brand}`
)?.node )?.node
const { data } = useSearch({ const { data } = useSearch({
@ -132,7 +132,7 @@ export default function Search({ categories, brands }: SearchPropsType) {
</a> </a>
</Link> </Link>
</li> </li>
{categories.map((cat) => ( {categories.map((cat: any) => (
<li <li
key={cat.path} key={cat.path}
className={cn( className={cn(
@ -233,7 +233,7 @@ export default function Search({ categories, brands }: SearchPropsType) {
</a> </a>
</Link> </Link>
</li> </li>
{brands.flatMap(({ node }) => ( {brands.flatMap(({ node }: { node: any }) => (
<li <li
key={node.path} key={node.path}
className={cn( className={cn(
@ -312,7 +312,6 @@ export default function Search({ categories, brands }: SearchPropsType) {
)} )}
</div> </div>
)} )}
{data ? ( {data ? (
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3"> <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{data.products.map((product: Product) => ( {data.products.map((product: Product) => (
@ -336,7 +335,8 @@ export default function Search({ categories, brands }: SearchPropsType) {
</Skeleton> </Skeleton>
))} ))}
</div> </div>
)} </div> )}{' '}
</div>
{/* Sort */} {/* Sort */}
<div className="col-span-8 lg:col-span-2 order-2 lg:order-none"> <div className="col-span-8 lg:col-span-2 order-2 lg:order-none">

View File

@ -72,9 +72,8 @@ export type APIProvider = {
operations: APIOperations<any> operations: APIOperations<any>
} }
export type CommerceAPI< export type CommerceAPI<P extends APIProvider = APIProvider> =
P extends APIProvider = APIProvider CommerceAPICore<P> & AllOperations<P>
> = CommerceAPICore<P> & AllOperations<P>
export class CommerceAPICore<P extends APIProvider = APIProvider> { export class CommerceAPICore<P extends APIProvider = APIProvider> {
constructor(readonly provider: P) {} constructor(readonly provider: P) {}
@ -134,17 +133,17 @@ export function getEndpoint<
} }
} }
export const createEndpoint = <API extends GetAPISchema<any, any>>( export const createEndpoint =
endpoint: API['endpoint'] <API extends GetAPISchema<any, any>>(endpoint: API['endpoint']) =>
) => <P extends APIProvider>( <P extends APIProvider>(
commerce: CommerceAPI<P>, commerce: CommerceAPI<P>,
context?: Partial<API['endpoint']> & { context?: Partial<API['endpoint']> & {
config?: P['config'] config?: P['config']
options?: API['schema']['endpoint']['options'] options?: API['schema']['endpoint']['options']
}
): NextApiHandler => {
return getEndpoint(commerce, { ...endpoint, ...context })
} }
): NextApiHandler => {
return getEndpoint(commerce, { ...endpoint, ...context })
}
export interface CommerceAPIConfig { export interface CommerceAPIConfig {
locale?: string locale?: string

View File

@ -7,7 +7,7 @@ const fs = require('fs')
const merge = require('deepmerge') const merge = require('deepmerge')
const prettier = require('prettier') const prettier = require('prettier')
const PROVIDERS = ['bigcommerce', 'shopify', 'swell', 'vendure'] const PROVIDERS = ['bigcommerce', 'shopify', 'swell', 'vendure', 'local']
function getProviderName() { function getProviderName() {
return ( return (
@ -18,7 +18,7 @@ function getProviderName() {
? 'shopify' ? 'shopify'
: process.env.NEXT_PUBLIC_SWELL_STORE_ID : process.env.NEXT_PUBLIC_SWELL_STORE_ID
? 'swell' ? 'swell'
: null) : 'local')
) )
} }

View File

@ -165,15 +165,13 @@ export type AddItemHandler<T extends CartTypes = CartTypes> = AddItemHook<T> & {
body: { cartId: string } body: { cartId: string }
} }
export type UpdateItemHandler< export type UpdateItemHandler<T extends CartTypes = CartTypes> =
T extends CartTypes = CartTypes UpdateItemHook<T> & {
> = UpdateItemHook<T> & { data: T['cart']
data: T['cart'] body: { cartId: string }
body: { cartId: string } }
}
export type RemoveItemHandler< export type RemoveItemHandler<T extends CartTypes = CartTypes> =
T extends CartTypes = CartTypes RemoveItemHook<T> & {
> = RemoveItemHook<T> & { body: { cartId: string }
body: { cartId: string } }
}

View File

@ -0,0 +1 @@
COMMERCE_PROVIDER=local

View File

@ -0,0 +1 @@
# Next.js Local Provider

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1,42 @@
import type { APIProvider, CommerceAPIConfig } from '@commerce/api'
import { CommerceAPI, getCommerceApi as commerceApi } from '@commerce/api'
import fetcher 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: fetcher,
}
const operations = {
getAllPages,
getPage,
getSiteInfo,
getCustomerWishlist,
getAllProductPaths,
getAllProducts,
getProduct,
}
export const provider = { config, operations }
export type Provider = typeof provider
export type LocalAPI<P extends Provider = Provider> = CommerceAPI<P | any>
export function getCommerceApi<P extends Provider>(
customProvider: P = provider as any
): LocalAPI<P> {
return commerceApi(customProvider as any)
}

View File

@ -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<LocalConfig>
preview?: boolean
}): Promise<GetAllPagesResult> {
return Promise.resolve({
pages: [],
})
}
return getAllPages
}

View File

@ -0,0 +1,15 @@
import data from '../../data.json'
export type GetAllProductPathsResult = {
products: Array<{ path: string }>
}
export default function getAllProductPathsOperation() {
function getAllProductPaths(): Promise<GetAllProductPathsResult> {
return Promise.resolve({
products: data.products.map(({ path }) => ({ path })),
})
}
return getAllProductPaths
}

View File

@ -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<any>) {
async function getAllProducts<T extends GetAllProductsOperation>({
query = '',
variables,
config,
}: {
query?: string
variables?: T['variables']
config?: Partial<LocalConfig>
preview?: boolean
} = {}): Promise<{ products: Product[] | any[] }> {
return Promise.resolve({
products: data.products,
})
}
return getAllProducts
}

View File

@ -0,0 +1,6 @@
export default function getCustomerWishlistOperation() {
function getCustomerWishlist(): any {
return { wishlist: {} }
}
return getCustomerWishlist
}

View File

@ -0,0 +1,12 @@
export type Page = any
export type GetPageResult = { page?: Page }
export type PageVariables = {
id: number
}
export default function getPageOperation() {
function getPage(): Promise<GetPageResult> {
return Promise.resolve({})
}
return getPage
}

View File

@ -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<any>) {
async function getProduct<T extends GetProductOperation>({
query = '',
variables,
config,
}: {
query?: string
variables?: T['variables']
config?: Partial<LocalConfig>
preview?: boolean
} = {}): Promise<Product | {} | any> {
return Promise.resolve({
product: data.products[0],
})
}
return getProduct
}

View File

@ -0,0 +1,30 @@
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<any>) {
function getSiteInfo({
query,
variables,
config: cfg,
}: {
query?: string
variables?: any
config?: Partial<LocalConfig>
preview?: boolean
} = {}): Promise<GetSiteInfoResult> {
return Promise.resolve({
categories: [],
brands: [],
})
}
return getSiteInfo
}

View File

@ -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'

View File

@ -0,0 +1,36 @@
import { FetcherError } from '@commerce/utils/errors'
import type { GraphQLFetcher } from '@commerce/api'
import { getCommerceApi } from '../index'
import fetch from './fetch'
const fetchGraphqlApi: GraphQLFetcher = async (
query: string,
{ variables, preview } = {},
fetchOptions
) => {
const config = getCommerceApi().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

View File

@ -0,0 +1,3 @@
import zeitFetch from '@vercel/fetch'
export default zeitFetch()

View File

@ -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'

View File

@ -0,0 +1,16 @@
import { MutationHook } from '@commerce/utils/types'
import useLogin, { UseLogin } from '@commerce/auth/use-login'
export default useLogin as UseLogin<typeof handler>
export const handler: MutationHook<any> = {
fetchOptions: {
query: '',
},
async fetcher() {
return null
},
useHook: () => () => {
return async function () {}
},
}

View File

@ -0,0 +1,17 @@
import { MutationHook } from '@commerce/utils/types'
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
export default useLogout as UseLogout<typeof handler>
export const handler: MutationHook<any> = {
fetchOptions: {
query: '',
},
async fetcher() {
return null
},
useHook:
({ fetch }) =>
() =>
async () => {},
}

View File

@ -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<typeof handler>
export const handler: MutationHook<any> = {
fetchOptions: {
query: '',
},
async fetcher() {
return null
},
useHook:
({ fetch }) =>
() =>
() => {},
}

View File

@ -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'

View File

@ -0,0 +1,17 @@
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
import { MutationHook } from '@commerce/utils/types'
export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<any> = {
fetchOptions: {
query: '',
},
async fetcher({ input, options, fetch }) {},
useHook:
({ fetch }) =>
() => {
return async function addItem() {
return {}
}
},
}

View File

@ -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<typeof handler>
export const handler: SWRHook<any> = {
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,
},
}
),
[]
)
},
}

View File

@ -0,0 +1,18 @@
import { MutationHook } from '@commerce/utils/types'
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
export default useRemoveItem as UseRemoveItem<typeof handler>
export const handler: MutationHook<any> = {
fetchOptions: {
query: '',
},
async fetcher({ input, options, fetch }) {},
useHook:
({ fetch }) =>
() => {
return async function removeItem(input) {
return {}
}
},
}

View File

@ -0,0 +1,18 @@
import { MutationHook } from '@commerce/utils/types'
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
export default useUpdateItem as UseUpdateItem<any>
export const handler: MutationHook<any> = {
fetchOptions: {
query: '',
},
async fetcher({ input, options, fetch }) {},
useHook:
({ fetch }) =>
() => {
return async function addItem() {
return {}
}
},
}

View File

@ -0,0 +1,6 @@
{
"provider": "local",
"features": {
"wishlist": false
}
}

View File

@ -0,0 +1 @@
export { default as useCustomer } from './use-customer'

View File

@ -0,0 +1,15 @@
import { SWRHook } from '@commerce/utils/types'
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
export default useCustomer as UseCustomer<typeof handler>
export const handler: SWRHook<any> = {
fetchOptions: {
query: '',
},
async fetcher({ input, options, fetch }) {},
useHook: () => () => {
return async function addItem() {
return {}
}
},
}

241
framework/local/data.json Normal file
View File

@ -0,0 +1,241 @@
{
"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": "<p><span>Show off your love for Next.js and Vercel with this unique,&nbsp;</span><strong>limited edition</strong><span>&nbsp;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!&nbsp;</span><strong>All proceeds will be donated to charity.</strong></p>",
"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
},
{
"url": "/assets/drop-shirt-3.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": "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": "<p><span>Show off your love for Next.js and Vercel with this unique,&nbsp;</span><strong>limited edition</strong><span>&nbsp;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!&nbsp;</span><strong>All proceeds will be donated to charity.</strong></p>",
"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
},
{
"url": "/assets/drop-shirt-3.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": "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": "<p><span>Show off your love for Next.js and Vercel with this unique,&nbsp;</span><strong>limited edition</strong><span>&nbsp;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!&nbsp;</span><strong>All proceeds will be donated to charity.</strong></p>",
"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
},
{
"url": "/assets/drop-shirt-3.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"
}
]
}
]
}
]
}

View File

@ -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
}

32
framework/local/index.tsx Normal file
View File

@ -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<CommerceConfig>) {
return (
<CoreCommerceProvider
provider={localProvider}
config={{ ...localConfig, ...config }}
>
{children}
</CoreCommerceProvider>
)
}
export const useCommerce = () => useCoreCommerce()

View File

@ -0,0 +1,8 @@
const commerce = require('./commerce.config.json')
module.exports = {
commerce,
images: {
domains: ['localhost'],
},
}

View File

@ -0,0 +1,2 @@
export { default as usePrice } from './use-price'
export { default as useSearch } from './use-search'

View File

@ -0,0 +1,2 @@
export * from '@commerce/product/use-price'
export { default } from '@commerce/product/use-price'

View File

@ -0,0 +1,17 @@
import { SWRHook } from '@commerce/utils/types'
import useSearch, { UseSearch } from '@commerce/product/use-search'
export default useSearch as UseSearch<typeof handler>
export const handler: SWRHook<any> = {
fetchOptions: {
query: '',
},
async fetcher({ input, options, fetch }) {},
useHook: () => () => {
return {
data: {
products: [],
},
}
},
}

View File

@ -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 },
}

View File

@ -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

View File

@ -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

View File

@ -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<Wishlist | null, UseWishlistInput> = () => {
return null
}
export function extendHook(
customFetcher: typeof fetcher,
// swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput>
swrOptions?: any
) {
const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => {
return { data: null }
}
useWishlist.extend = extendHook
return useWishlist
}
export default extendHook(fetcher)

View File

@ -18,16 +18,18 @@ export const handler: MutationHook<LogoutHook> = {
}) })
return null return null
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCustomer() ({ fetch }) =>
() => {
const { mutate } = useCustomer()
return useCallback( return useCallback(
async function logout() { async function logout() {
const data = await fetch() const data = await fetch()
await mutate(null, false) await mutate(null, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

11033
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,7 @@
"dot-object": "^2.1.4", "dot-object": "^2.1.4",
"email-validator": "^2.0.4", "email-validator": "^2.0.4",
"immutability-helper": "^3.1.1", "immutability-helper": "^3.1.1",
"isomorphic-unfetch": "^3.1.0",
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"keen-slider": "^5.4.1", "keen-slider": "^5.4.1",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",

View File

@ -8,6 +8,7 @@ import { Text } from '@components/ui'
import { Layout } from '@components/common' import { Layout } from '@components/common'
import getSlug from '@lib/get-slug' import getSlug from '@lib/get-slug'
import { missingLocaleInPages } from '@lib/usage-warns' import { missingLocaleInPages } from '@lib/usage-warns'
import type { Page } from '@commerce/types/page'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
@ -22,7 +23,9 @@ export async function getStaticProps({
const { categories } = await siteInfoPromise const { categories } = await siteInfoPromise
const path = params?.pages.join('/') const path = params?.pages.join('/')
const slug = locale ? `${locale}/${path}` : path const slug = locale ? `${locale}/${path}` : path
const pageItem = pages.find((p) => (p.url ? getSlug(p.url) === slug : false)) const pageItem = pages.find((p: Page) =>
p.url ? getSlug(p.url) === slug : false
)
const data = const data =
pageItem && pageItem &&
(await commerce.getPage({ (await commerce.getPage({
@ -45,7 +48,7 @@ export async function getStaticProps({
export async function getStaticPaths({ locales }: GetStaticPathsContext) { export async function getStaticPaths({ locales }: GetStaticPathsContext) {
const config = { locales } const config = { locales }
const { pages } = await commerce.getAllPages({ config }) const { pages }: { pages: Page[] } = await commerce.getAllPages({ config })
const [invalidPaths, log] = missingLocaleInPages() const [invalidPaths, log] = missingLocaleInPages()
const paths = pages const paths = pages
.map((page) => page.url) .map((page) => page.url)

View File

@ -12,6 +12,13 @@ export async function getStaticProps({
locale, locale,
locales, locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
// Disabling page if Feature is not available
if (!process.env.COMMERCE_CART_ENABLED) {
return {
notFound: true,
}
}
const config = { locale, locales } const config = { locale, locales }
const pagesPromise = commerce.getAllPages({ config, preview }) const pagesPromise = commerce.getAllPages({ config, preview })
const siteInfoPromise = commerce.getSiteInfo({ config, preview }) const siteInfoPromise = commerce.getSiteInfo({ config, preview })
@ -79,7 +86,7 @@ export default function Cart() {
<Text variant="pageHeading">My Cart</Text> <Text variant="pageHeading">My Cart</Text>
<Text variant="sectionHeading">Review your Order</Text> <Text variant="sectionHeading">Review your Order</Text>
<ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accent-2 border-b border-accent-2"> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accent-2 border-b border-accent-2">
{data!.lineItems.map((item) => ( {data!.lineItems.map((item: any) => (
<CartItem <CartItem
key={item.id} key={item.id}
item={item} item={item}

View File

@ -39,7 +39,7 @@ export default function Home({
return ( return (
<> <>
<Grid variant="filled"> <Grid variant="filled">
{products.slice(0, 3).map((product, i) => ( {products.slice(0, 3).map((product: any, i: number) => (
<ProductCard <ProductCard
key={product.id} key={product.id}
product={product} product={product}
@ -51,7 +51,7 @@ export default function Home({
))} ))}
</Grid> </Grid>
<Marquee variant="secondary"> <Marquee variant="secondary">
{products.slice(0, 3).map((product, i) => ( {products.slice(0, 3).map((product: any, i: number) => (
<ProductCard key={product.id} product={product} variant="slim" /> <ProductCard key={product.id} product={product} variant="slim" />
))} ))}
</Marquee> </Marquee>
@ -60,7 +60,7 @@ export default function Home({
description="Cupcake ipsum dolor sit amet lemon drops pastry cotton candy. Sweet carrot cake macaroon bonbon croissant fruitcake jujubes macaroon oat cake. Soufflé bonbon caramels jelly beans. Tiramisu sweet roll cheesecake pie carrot cake. " description="Cupcake ipsum dolor sit amet lemon drops pastry cotton candy. Sweet carrot cake macaroon bonbon croissant fruitcake jujubes macaroon oat cake. Soufflé bonbon caramels jelly beans. Tiramisu sweet roll cheesecake pie carrot cake. "
/> />
<Grid layout="B" variant="filled"> <Grid layout="B" variant="filled">
{products.slice(0, 3).map((product, i) => ( {products.slice(0, 3).map((product: any, i: number) => (
<ProductCard <ProductCard
key={product.id} key={product.id}
product={product} product={product}
@ -72,7 +72,7 @@ export default function Home({
))} ))}
</Grid> </Grid>
<Marquee> <Marquee>
{products.slice(3).map((product, i) => ( {products.slice(3).map((product: any, i: number) => (
<ProductCard key={product.id} product={product} variant="slim" /> <ProductCard key={product.id} product={product} variant="slim" />
))} ))}
</Marquee> </Marquee>

View File

@ -55,12 +55,12 @@ export async function getStaticPaths({ locales }: GetStaticPathsContext) {
paths: locales paths: locales
? locales.reduce<string[]>((arr, locale) => { ? locales.reduce<string[]>((arr, locale) => {
// Add a product path for every locale // Add a product path for every locale
products.forEach((product) => { products.forEach((product: any) => {
arr.push(`/${locale}/product${product.path}`) arr.push(`/${locale}/product${product.path}`)
}) })
return arr return arr
}, []) }, [])
: products.map((product) => `/product${product.path}`), : products.map((product: any) => `/product${product.path}`),
fallback: 'blocking', fallback: 'blocking',
} }
} }

View File

@ -22,8 +22,8 @@
"@components/*": ["components/*"], "@components/*": ["components/*"],
"@commerce": ["framework/commerce"], "@commerce": ["framework/commerce"],
"@commerce/*": ["framework/commerce/*"], "@commerce/*": ["framework/commerce/*"],
"@framework": ["framework/shopify"], "@framework": ["framework/local"],
"@framework/*": ["framework/shopify/*"] "@framework/*": ["framework/local/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],

View File

@ -3804,6 +3804,14 @@ isomorphic-form-data@2.0.0:
dependencies: dependencies:
form-data "^2.3.2" form-data "^2.3.2"
isomorphic-unfetch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f"
integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==
dependencies:
node-fetch "^2.6.1"
unfetch "^4.2.0"
isomorphic-ws@4.0.1: isomorphic-ws@4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc"
@ -6648,6 +6656,11 @@ unc-path-regex@^0.1.2:
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo=
unfetch@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
uniq@^1.0.1: uniq@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"