Vendure provider (#223)

* Minimal list/detail views working with Vendure

* Implement useCart/useAddItem

* Implement useUpdateItem & useRemoveItem

* Implement useSearch

* Add operations codegen, tidy up

* Dummy checkout page

* Implement auth/customer hooks

* Use env var for Shop API url

* Add some documentation

* Improve error handling

* Optimize preview image size

* Fix accidental change

* Update Vendure provider to latest changes

* Vendure provider: split out gql operations, remove unused files

* Update Vendure provider readme

* Add local next.config to Vendure provider, update docs

* Update to use demo server

* Fix build errors

* Use proxy for vendure api

* Simplify instructions in Vendure readme

* Refactor Vendure checkout api handler

* Improve image quality
This commit is contained in:
Michael Bromley
2021-05-27 23:06:56 +02:00
committed by GitHub
parent 8fb6c7b206
commit da4371090d
71 changed files with 8593 additions and 51 deletions

View File

@@ -0,0 +1,48 @@
import type {
GetAllProductPathsQuery,
GetAllProductPathsQueryVariables,
} from '../schema'
import { getConfig, VendureConfig } from '../api'
import { getAllProductPathsQuery } from '../lib/queries/get-all-product-paths-query'
export type GetAllProductPathsResult = {
products: Array<{ node: { path: string } }>
}
async function getAllProductPaths(opts?: {
variables?: GetAllProductPathsQueryVariables
config?: VendureConfig
}): Promise<GetAllProductPathsResult>
async function getAllProductPaths<
T extends { products: any[] },
V = any
>(opts: {
query: string
variables?: V
config?: VendureConfig
}): Promise<GetAllProductPathsResult>
async function getAllProductPaths({
query = getAllProductPathsQuery,
variables,
config,
}: {
query?: string
variables?: GetAllProductPathsQueryVariables
config?: VendureConfig
} = {}): Promise<GetAllProductPathsResult> {
config = getConfig(config)
// RecursivePartial forces the method to check for every prop in the data, which is
// required in case there's a custom `query`
const { data } = await config.fetch<GetAllProductPathsQuery>(query, {
variables,
})
const products = data.products.items
return {
products: products.map((p) => ({ node: { path: `/${p.slug}` } })),
}
}
export default getAllProductPaths

View File

@@ -0,0 +1,39 @@
import { Product } from '@commerce/types'
import { getConfig, VendureConfig } from '../api'
import { GetAllProductsQuery } from '../schema'
import { normalizeSearchResult } from '../lib/normalize'
import { getAllProductsQuery } from '../lib/queries/get-all-products-query'
export type ProductVariables = { first?: number }
async function getAllProducts(opts?: {
variables?: ProductVariables
config?: VendureConfig
preview?: boolean
}): Promise<{ products: Product[] }>
async function getAllProducts({
query = getAllProductsQuery,
variables: { ...vars } = {},
config,
}: {
query?: string
variables?: ProductVariables
config?: VendureConfig
preview?: boolean
} = {}): Promise<{ products: Product[] | any[] }> {
config = getConfig(config)
const variables = {
input: {
take: vars.first,
groupByProduct: true,
},
}
const { data } = await config.fetch<GetAllProductsQuery>(query, { variables })
return {
products: data.search.items.map((item) => normalizeSearchResult(item)),
}
}
export default getAllProducts

View File

@@ -0,0 +1,57 @@
import { Product } from '@commerce/types'
import { getConfig, VendureConfig } from '../api'
import { GetProductQuery } from '../schema'
import { getProductQuery } from '../lib/queries/get-product-query'
async function getProduct({
query = getProductQuery,
variables,
config,
}: {
query?: string
variables: { slug: string }
config?: VendureConfig
preview?: boolean
}): Promise<Product | {} | any> {
config = getConfig(config)
const locale = config.locale
const { data } = await config.fetch<GetProductQuery>(query, { variables })
const product = data.product
if (product) {
return {
product: {
id: product.id,
name: product.name,
description: product.description,
slug: product.slug,
images: product.assets.map((a) => ({
url: a.preview,
alt: a.name,
})),
variants: product.variants.map((v) => ({
id: v.id,
options: v.options.map((o) => ({
id: o.id,
displayName: o.name,
values: o.group.options.map((_o) => ({ label: _o.name })),
})),
})),
price: {
value: product.variants[0].priceWithTax / 100,
currencyCode: product.variants[0].currencyCode,
},
options: product.optionGroups.map((og) => ({
id: og.id,
displayName: og.name,
values: og.options.map((o) => ({ label: o.name })),
})),
} as Product,
}
}
return {}
}
export default getProduct

View File

@@ -0,0 +1,4 @@
export { default as usePrice } from './use-price'
export { default as useSearch } from './use-search'
export { default as getProduct } from './get-product'
export { default as getAllProducts } from './get-all-products'

View File

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

View File

@@ -0,0 +1,65 @@
import { SWRHook } from '@commerce/utils/types'
import useSearch, { UseSearch } from '@commerce/product/use-search'
import { Product } from '@commerce/types'
import { SearchQuery, SearchQueryVariables } from '../schema'
import { normalizeSearchResult } from '../lib/normalize'
import { searchQuery } from '../lib/queries/search-query'
export default useSearch as UseSearch<typeof handler>
export type SearchProductsInput = {
search?: string
categoryId?: string
brandId?: string
sort?: string
}
export type SearchProductsData = {
products: Product[]
found: boolean
}
export const handler: SWRHook<
SearchProductsData,
SearchProductsInput,
SearchProductsInput
> = {
fetchOptions: {
query: searchQuery,
},
async fetcher({ input, options, fetch }) {
const { categoryId, brandId } = input
const variables: SearchQueryVariables = {
input: {
term: input.search,
collectionId: input.categoryId,
groupByProduct: true,
// TODO: what is the "sort" value?
},
}
const { search } = await fetch<SearchQuery>({
query: searchQuery,
variables,
})
return {
found: search.totalItems > 0,
products: search.items.map((item) => normalizeSearchResult(item)) ?? [],
}
},
useHook: ({ useData }) => (input = {}) => {
return useData({
input: [
['search', input.search],
['categoryId', input.categoryId],
['brandId', input.brandId],
['sort', input.sort],
],
swrOptions: {
revalidateOnFocus: false,
...input.swrOptions,
},
})
},
}