4
0
forked from crowetic/commerce

Shopify Provier Updates (#212)

* changes

* Adding shopify commit

* Changed to query page by id

* Fixed page query, Changed use-search GraphQl query

* Update use-search.tsx

* remove unused util

* Changed cookie expiration

* Update tsconfig.json

Co-authored-by: okbel <curciobel@gmail.com>
This commit is contained in:
cond0r 2021-03-01 16:47:30 +02:00 committed by GitHub
parent 751011767a
commit 641ce0aa64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 164 additions and 175 deletions

View File

@ -3,4 +3,4 @@ BIGCOMMERCE_STOREFRONT_API_TOKEN=
BIGCOMMERCE_STORE_API_URL=
BIGCOMMERCE_STORE_API_TOKEN=
BIGCOMMERCE_STORE_API_CLIENT_ID=
BIGCOMMERCE_CHANNEL_ID=
BIGCOMMERCE_CHANNEL_ID=

View File

@ -5,6 +5,7 @@ import {
API_TOKEN,
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
SHOPIFY_COOKIE_EXPIRE,
} from '../const'
if (!API_URL) {
@ -43,10 +44,11 @@ export class Config {
}
const config = new Config({
locale: 'en-US',
commerceUrl: API_URL,
apiToken: API_TOKEN!,
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
cartCookieMaxAge: 60 * 60 * 24 * 30,
cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE,
fetch: fetchGraphqlApi,
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
})

View File

@ -1,6 +1,7 @@
import {
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CHECKOUT_URL_COOKIE,
SHOPIFY_COOKIE_EXPIRE,
} from '../../const'
import checkoutCreateMutation from '../../utils/mutations/checkout-create'
@ -15,8 +16,11 @@ export const checkoutCreate = async (fetch: any) => {
const checkoutId = checkout?.id
if (checkoutId) {
Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId)
Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl)
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

View File

@ -25,12 +25,13 @@ const getAllPages = async (options?: {
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { locale } = config
const { data } = await config.fetch(getAllPagesQuery, { variables })
const pages = data.pages?.edges?.map(
({ node: { title: name, handle, ...node } }: PageEdge) => ({
...node,
url: `/${handle}`,
url: `/${locale}/${handle}`,
name,
})
)

View File

@ -3,33 +3,32 @@ import getPageQuery from '../utils/queries/get-page-query'
import { Page } from './get-all-pages'
type Variables = {
slug: string
id: string
}
type ReturnType = {
page: Page
}
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
const getPage = async (options: {
variables: Variables
config: ShopifyConfig
preview?: boolean
}): Promise<ReturnType> => {
}): Promise<GetPageResult> => {
let { config, variables } = options ?? {}
config = getConfig(config)
const { locale } = config
const { data } = await config.fetch(getPageQuery, {
variables,
})
const { pageByHandle: page } = data
const page = data.node
return {
page: page
? {
...page,
name: page.title,
url: page?.handle,
url: `/${locale}/${page.handle}`,
}
: null,
}

View File

@ -6,6 +6,8 @@ export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken'
export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
export const SHOPIFY_COOKIE_EXPIRE = 30
export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json`
export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN

View File

@ -4,6 +4,7 @@ import useSearch, { UseSearch } from '@commerce/product/use-search'
import { ProductEdge } from '../schema'
import {
getAllProductsQuery,
getCollectionProductsQuery,
getSearchVariables,
normalizeProduct,
} from '../utils'
@ -14,8 +15,8 @@ export default useSearch as UseSearch<typeof handler>
export type SearchProductsInput = {
search?: string
categoryId?: number
brandId?: number
categoryId?: string
brandId?: string
sort?: string
}
@ -23,6 +24,7 @@ export type SearchProductsData = {
products: Product[]
found: boolean
}
export const handler: SWRHook<
SearchProductsData,
SearchProductsInput,
@ -32,18 +34,30 @@ export const handler: SWRHook<
query: getAllProductsQuery,
},
async fetcher({ input, options, fetch }) {
const resp = await fetch({
query: options?.query,
const { categoryId, brandId } = input
const data = await fetch({
query: categoryId ? getCollectionProductsQuery : options.query,
method: options?.method,
variables: getSearchVariables(input),
})
const edges = resp.products?.edges
let edges
if (categoryId) {
edges = data.node?.products?.edges ?? []
if (brandId) {
edges = edges.filter(
({ node: { vendor } }: ProductEdge) => vendor === brandId
)
}
} else {
edges = data.products?.edges ?? []
}
return {
products: edges?.map(({ node: p }: ProductEdge) =>
// TODO: Fix this product type
normalizeProduct(p as any)
),
found: !!edges?.length,
products: edges.map(({ node }: ProductEdge) => normalizeProduct(node)),
found: !!edges.length,
}
},
useHook: ({ useData }) => (input = {}) => {

View File

@ -1,12 +1,21 @@
import Cookies from 'js-cookie'
import { SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const'
import Cookies, { CookieAttributes } from 'js-cookie'
import { SHOPIFY_COOKIE_EXPIRE, SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const'
export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE)
export const setCustomerToken = (token: string | null, options?: any) => {
export const setCustomerToken = (
token: string | null,
options?: CookieAttributes
) => {
if (!token) {
Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE)
} else {
Cookies.set(SHOPIFY_CUSTOMER_TOKEN_COOKIE, token, options)
Cookies.set(
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
token,
options ?? {
expires: SHOPIFY_COOKIE_EXPIRE,
}
)
}
}

View File

@ -17,8 +17,8 @@ const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
return (
data.collections?.edges?.map(
({ node: { title: name, handle } }: CollectionEdge) => ({
entityId: handle,
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({
entityId,
name,
path: `/${handle}`,
})

View File

@ -2,9 +2,9 @@ import getSortVariables from './get-sort-variables'
import type { SearchProductsInput } from '../product/use-search'
export const getSearchVariables = ({
categoryId,
brandId,
search,
categoryId,
sort,
}: SearchProductsInput) => {
let query = ''
@ -13,17 +13,14 @@ export const getSearchVariables = ({
query += `product_type:${search} OR title:${search} OR tag:${search}`
}
if (categoryId) {
query += `tag:${categoryId}`
}
if (brandId) {
query += `${categoryId ? ' AND ' : ''}vendor:${brandId}`
query += `${search ? ' AND ' : ''}vendor:${brandId}`
}
return {
categoryId,
query,
...getSortVariables(sort),
...getSortVariables(sort, !!categoryId),
}
}

View File

@ -1,4 +1,4 @@
const getSortVariables = (sort?: string) => {
const getSortVariables = (sort?: string, isCategory = false) => {
let output = {}
switch (sort) {
case 'price-asc':
@ -21,7 +21,7 @@ const getSortVariables = (sort?: string) => {
break
case 'latest-desc':
output = {
sortKey: 'CREATED_AT',
sortKey: isCategory ? 'CREATED' : 'CREATED_AT',
reverse: true,
}
break

View File

@ -1,3 +1,5 @@
import { Product } from '@commerce/types'
import {
Product as ShopifyProduct,
Checkout,
@ -5,8 +7,8 @@ import {
SelectedOption,
ImageConnection,
ProductVariantConnection,
ProductOption,
MoneyV2,
ProductOption,
} from '../schema'
import type { Cart, LineItem } from '../types'
@ -19,18 +21,26 @@ const money = ({ amount, currencyCode }: MoneyV2) => {
}
const normalizeProductOption = ({
id,
name: displayName,
values,
...rest
}: ProductOption) => {
return {
__typename: 'MultipleChoiceOption',
id,
displayName,
values: values.map((value) => ({
label: value,
hexColors: displayName === 'Color' ? [value] : null,
})),
...rest,
values: values.map((value) => {
let output: any = {
label: value,
}
if (displayName === 'Color') {
output = {
...output,
hexColors: [value],
}
}
return output
}),
}
}
@ -41,19 +51,28 @@ const normalizeProductImages = ({ edges }: ImageConnection) =>
}))
const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
return edges?.map(({ node: { id, selectedOptions } }) => ({
id,
options: selectedOptions.map(({ name, value }: SelectedOption) =>
normalizeProductOption({
id,
name,
values: [value],
})
),
}))
return edges?.map(
({
node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 },
}) => ({
id,
name: title,
sku: sku ?? id,
price: +priceV2.amount,
listPrice: +compareAtPriceV2?.amount,
requiresShipping: true,
options: selectedOptions.map(({ name, value }: SelectedOption) =>
normalizeProductOption({
id,
name,
values: [value],
})
),
})
)
}
export function normalizeProduct(productNode: ShopifyProduct): any {
export function normalizeProduct(productNode: ShopifyProduct): Product {
const {
id,
title: name,
@ -95,8 +114,8 @@ export function normalizeCart(checkout: Checkout): Cart {
},
taxesIncluded: checkout.taxesIncluded,
lineItems: checkout.lineItems?.edges.map(normalizeLineItem),
lineItemsSubtotalPrice: checkout.subtotalPriceV2?.amount,
subtotalPrice: checkout.subtotalPriceV2?.amount,
lineItemsSubtotalPrice: +checkout.subtotalPriceV2?.amount,
subtotalPrice: +checkout.subtotalPriceV2?.amount,
totalPrice: checkout.totalPriceV2?.amount,
discounts: [],
}

View File

@ -1,3 +1,38 @@
export const productConnection = `
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
title
vendor
handle
description
priceRange {
minVariantPrice {
amount
currencyCode
}
}
images(first: 1) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
originalSrc
altText
width
height
}
}
}
}
}`
export const productsFragment = `
products(
first: $first
@ -5,39 +40,7 @@ products(
reverse: $reverse
query: $query
) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
title
vendor
handle
description
priceRange {
minVariantPrice {
amount
currencyCode
}
}
images(first: 1) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
originalSrc
altText
width
height
}
}
}
}
}
${productConnection}
}
`

View File

@ -1,16 +1,23 @@
import { productsFragment } from './get-all-products-query'
import { productConnection } from './get-all-products-query'
const getCollectionProductsQuery = /* GraphQL */ `
query getProductsFromCollection(
$categoryHandle: String!
$categoryId: ID!
$first: Int = 250
$query: String = ""
$sortKey: ProductSortKeys = RELEVANCE
$sortKey: ProductCollectionSortKeys = RELEVANCE
$reverse: Boolean = false
) {
collectionByHandle(handle: $categoryHandle)
{
${productsFragment}
node(id: $categoryId) {
id
... on Collection {
products(
first: $first
sortKey: $sortKey
reverse: $reverse
) {
${productConnection}
}
}
}
}
`

View File

@ -1,12 +1,13 @@
export const getPageQuery = /* GraphQL */ `
query getPageBySlug($slug: String!) {
pageByHandle(handle: $slug) {
query($id: ID!) {
node(id: $id) {
id
title
handle
body
bodySummary
url
... on Page {
title
handle
body
bodySummary
}
}
}
`

View File

@ -32,6 +32,7 @@ const getProductQuery = /* GraphQL */ `
node {
id
title
sku
selectedOptions {
name
value

View File

@ -1,68 +0,0 @@
// TODO: Fix the types in this file
// import { Product, Image } from '../types'
type Product = any
type Image = any
export default function toCommerceProducts(products: Product[]) {
return products.map((product: Product) => {
return {
id: product.id,
entityId: product.id,
name: product.title,
slug: product.handle,
title: product.title,
vendor: product.vendor,
description: product.descriptionHtml,
path: `/${product.handle}`,
price: {
value: +product.variants[0].price,
currencyCode: 'USD', // TODO
},
images: product.images.map((image: Image) => {
return {
url: image.src,
}
}),
// TODO: Fix the variant type
variants: product.variants.map((variant: any) => {
return {
id: variant.id,
// TODO: Fix the selectedOption type
options: variant.selectedOptions.map((selectedOption: any) => {
return {
__typename: 'MultipleChoiceOption',
displayName: selectedOption.name,
values: [
{
node: {
id: variant.id,
label: selectedOption.value,
},
},
],
}
}),
}
}),
// TODO: Fix the option type
productOptions: product.options.map((option: any) => {
return {
__typename: 'MultipleChoiceOption',
displayName: option.name,
// TODO: Fix the value type
values: option.values.map((value: any) => {
return {
node: {
entityId: 1,
label: value.value,
hexColors: [value.value],
},
}
}),
}
}),
options: [],
}
})
}

View File

@ -2,9 +2,7 @@
// Shopify doesn't have a wishlist
import { HookFetcher } from '@commerce/utils/types'
import useCommerceWishlist from '@commerce/wishlist/use-wishlist'
import { Product } from '../schema'
import useCustomer from '../customer/use-customer'
const defaultOpts = {}