From d1d9e8c4343e70d72df6e00ec95f6ea4efbb7c54 Mon Sep 17 00:00:00 2001 From: Catalin Pinte Date: Mon, 30 Jan 2023 17:50:25 +0200 Subject: [PATCH] Fix auth & wishlist (#918) * Fix auth & wishlist * Revert files * Update signup.ts * Update signup.ts * Requested changes * Revert fetch options --- .../customer/get-logged-in-customer.ts | 4 ++- .../src/api/endpoints/login/login.ts | 14 ++++---- .../src/api/endpoints/signup/signup.ts | 32 +++++++++---------- .../src/api/endpoints/wishlist/add-item.ts | 7 ++-- .../api/endpoints/wishlist/get-wishlist.ts | 7 ++-- .../src/api/endpoints/wishlist/remove-item.ts | 12 ++++--- .../api/operations/get-customer-wishlist.ts | 32 ++++++++++--------- .../bigcommerce/src/api/operations/login.ts | 21 ++++-------- .../src/api/utils/fetch-graphql-api.ts | 9 +++--- .../src/api/utils/get-customer-id.ts | 4 ++- packages/bigcommerce/src/api/utils/types.ts | 12 +++++++ packages/bigcommerce/src/lib/normalize.ts | 15 +++++++++ packages/commerce/src/api/index.ts | 8 ++++- packages/commerce/src/schemas/whishlist.ts | 10 +++--- packages/commerce/src/types/login.ts | 2 +- .../src/api/utils/fetch-graphql-api.ts | 13 +++++--- .../src/api/utils/get-customer-id.ts | 4 ++- .../src/api/operations/get-all-pages.ts | 4 ++- .../src/api/operations/get-all-products.ts | 8 +++-- .../saleor/src/api/operations/get-page.ts | 4 ++- .../saleor/src/api/operations/get-product.ts | 4 ++- .../saleor/src/api/utils/fetch-graphql-api.ts | 9 +++--- packages/sfcc/src/api/utils/fetch-local.ts | 13 +++++--- .../src/api/operations/get-all-pages.ts | 4 ++- .../src/api/operations/get-all-products.ts | 4 ++- .../shopify/src/api/operations/get-page.ts | 4 ++- .../shopify/src/api/operations/get-product.ts | 4 ++- .../src/api/utils/fetch-graphql-api.ts | 9 +++--- packages/shopify/src/utils/get-categories.ts | 4 ++- .../src/api/utils/fetch-graphql-api.ts | 9 +++--- site/components/auth/LoginView.tsx | 7 ++-- site/components/auth/SignUpView.tsx | 4 +-- .../CustomerMenuContent.module.css | 4 +-- .../CustomerMenuContent.tsx | 8 +---- .../common/UserNav/UserNav.module.css | 11 ++++--- site/components/common/UserNav/UserNav.tsx | 15 +++------ .../WishlistButton/WishlistButton.tsx | 2 +- .../wishlist/WishlistCard/WishlistCard.tsx | 1 + site/pages/wishlist.tsx | 11 ++++--- 39 files changed, 203 insertions(+), 146 deletions(-) diff --git a/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts b/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts index 37d440666..02de28b23 100644 --- a/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts +++ b/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts @@ -34,7 +34,9 @@ const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = getLoggedInCustomerQuery, undefined, { - 'Set-Cookie': `${config.customerCookie}=${token}`, + headers: { + cookie: `${config.customerCookie}=${token}`, + }, } ) const { customer } = data diff --git a/packages/bigcommerce/src/api/endpoints/login/login.ts b/packages/bigcommerce/src/api/endpoints/login/login.ts index 3ad1419aa..dda5f6bf7 100644 --- a/packages/bigcommerce/src/api/endpoints/login/login.ts +++ b/packages/bigcommerce/src/api/endpoints/login/login.ts @@ -11,12 +11,12 @@ const login: LoginEndpoint['handlers']['login'] = async ({ commerce, }) => { try { - const res = new Response() - await commerce.login({ variables: { email, password }, config, res }) - return { - status: res.status, - headers: res.headers, - } + const response = await commerce.login({ + variables: { email, password }, + config, + }) + + return response } catch (error) { // Check if the email and password didn't match an existing account if (error instanceof FetcherError) { @@ -24,7 +24,7 @@ const login: LoginEndpoint['handlers']['login'] = async ({ invalidCredentials.test(error.message) ? 'Cannot find an account that matches the provided credentials' : error.message, - { status: error.status || 401 } + { status: 401 } ) } else { throw error diff --git a/packages/bigcommerce/src/api/endpoints/signup/signup.ts b/packages/bigcommerce/src/api/endpoints/signup/signup.ts index 94499f96a..aa591e5de 100644 --- a/packages/bigcommerce/src/api/endpoints/signup/signup.ts +++ b/packages/bigcommerce/src/api/endpoints/signup/signup.ts @@ -1,6 +1,5 @@ import type { SignupEndpoint } from '.' import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' - import { BigcommerceApiError } from '../../utils/errors' const signup: SignupEndpoint['handlers']['signup'] = async ({ @@ -22,28 +21,27 @@ const signup: SignupEndpoint['handlers']['signup'] = async ({ }, ]), }) + + // Login the customer right after creating it + const response = await commerce.login({ + variables: { email, password }, + config, + }) + + return response } catch (error) { - if (error instanceof BigcommerceApiError && error.status === 422) { - const hasEmailError = '0.email' in error.data?.errors - // If there's an error with the email, it most likely means it's duplicated - if (hasEmailError) { - throw new CommerceAPIError('Email already in use', { + // Display all validation errors from BigCommerce in a single error message + if (error instanceof BigcommerceApiError && error.status >= 400) { + const message = Object.values(error.data.errors).join('
') + if (message) { + throw new CommerceAPIError(message, { status: 400, - code: 'duplicated_email', + code: 'invalid_request', }) } - } else { - throw error } - } - const res = new Response() - - // Login the customer right after creating it - await commerce.login({ variables: { email, password }, res, config }) - - return { - headers: res.headers, + throw error } } diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts index cc1cdf951..128390ca3 100644 --- a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts +++ b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts @@ -1,6 +1,7 @@ import { parseWishlistItem } from '../../utils/parse-item' import getCustomerId from '../../utils/get-customer-id' import type { WishlistEndpoint } from '.' +import { normalizeWishlist } from '../../../lib/normalize' const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ body: { customerToken, item }, @@ -31,7 +32,7 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ }), }) return { - data, + data: normalizeWishlist(data), } } @@ -47,7 +48,9 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ ) // Returns Wishlist - return { data } + return { + data: normalizeWishlist(data), + } } export default addItem diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts b/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts index c0f57fb92..325adce54 100644 --- a/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts +++ b/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts @@ -1,5 +1,4 @@ import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' -import type { Wishlist } from '@vercel/commerce/types/wishlist' import type { WishlistEndpoint } from '.' import getCustomerId from '../../utils/get-customer-id' @@ -9,8 +8,6 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({ config, commerce, }) => { - let result: { data?: Wishlist } = {} - if (customerToken) { const customerId = customerToken && (await getCustomerId({ customerToken, config })) @@ -25,10 +22,10 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({ config, }) - result = { data: wishlist } + return { data: wishlist } } - return { data: result.data ?? null } + return { data: null } } export default getWishlist diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts b/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts index 36b0a09db..4a79728a2 100644 --- a/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts +++ b/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts @@ -1,7 +1,9 @@ -import type { Wishlist } from '@vercel/commerce/types/wishlist' -import getCustomerId from '../../utils/get-customer-id' import type { WishlistEndpoint } from '.' +import type { BCWishlist } from '../../utils/types' + +import getCustomerId from '../../utils/get-customer-id' import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' +import { normalizeWishlist } from '../../../lib/normalize' // Return wishlist info const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({ @@ -11,6 +13,7 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({ }) => { const customerId = customerToken && (await getCustomerId({ customerToken, config })) + const { wishlist } = (customerId && (await commerce.getCustomerWishlist({ @@ -23,13 +26,12 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({ throw new CommerceAPIError('Wishlist not found', { status: 400 }) } - const result = await config.storeApiFetch<{ data: Wishlist } | null>( + const result = await config.storeApiFetch<{ data: BCWishlist } | null>( `/v3/wishlists/${wishlist.id}/items/${itemId}`, { method: 'DELETE' } ) - const data = result?.data ?? null - return { data } + return { data: result?.data ? normalizeWishlist(result.data) : null } } export default removeItem diff --git a/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts b/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts index cdfd05acf..c7ff1697f 100644 --- a/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts +++ b/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts @@ -2,13 +2,11 @@ import type { OperationContext, OperationOptions, } from '@vercel/commerce/api/operations' -import type { - GetCustomerWishlistOperation, - Wishlist, -} from '@vercel/commerce/types/wishlist' -import type { RecursivePartial, RecursiveRequired } from '../utils/types' +import type { GetCustomerWishlistOperation } from '@vercel/commerce/types/wishlist' +import type { RecursivePartial, BCWishlist } from '../utils/types' import { BigcommerceConfig, Provider } from '..' -import getAllProducts, { ProductEdge } from './get-all-products' +import { ProductEdge } from './get-all-products' +import { normalizeWishlist } from '../../lib/normalize' export default function getCustomerWishlistOperation({ commerce, @@ -41,18 +39,22 @@ export default function getCustomerWishlistOperation({ }): Promise { config = commerce.getConfig(config) - const { data = [] } = await config.storeApiFetch< - RecursivePartial<{ data: Wishlist[] }> - >(`/v3/wishlists?customer_id=${variables.customerId}`) + const { data = [] } = await config.storeApiFetch<{ data: BCWishlist[] }>( + `/v3/wishlists?customer_id=${variables.customerId}` + ) const wishlist = data[0] if (includeProducts && wishlist?.items?.length) { - const ids = wishlist.items - ?.map((item) => (item?.productId ? String(item?.productId) : null)) - .filter((id): id is string => !!id) + const ids = [] - if (ids?.length) { + for (const wishlistItem of wishlist.items) { + if (wishlistItem.product_id) { + ids.push(String(wishlistItem.product_id)) + } + } + + if (ids.length) { const graphqlData = await commerce.getAllProducts({ variables: { first: 50, ids }, config, @@ -66,7 +68,7 @@ export default function getCustomerWishlistOperation({ }, {}) // Populate the wishlist items with the graphql products wishlist.items.forEach((item) => { - const product = item && productsById[Number(item.productId)] + const product = item && productsById[Number(item.product_id)] if (item && product) { // @ts-ignore Fix this type when the wishlist type is properly defined item.product = product @@ -75,7 +77,7 @@ export default function getCustomerWishlistOperation({ } } - return { wishlist: wishlist as RecursiveRequired } + return { wishlist: wishlist && normalizeWishlist(wishlist) } } return getCustomerWishlist diff --git a/packages/bigcommerce/src/api/operations/login.ts b/packages/bigcommerce/src/api/operations/login.ts index dad3b48b1..da63e21f0 100644 --- a/packages/bigcommerce/src/api/operations/login.ts +++ b/packages/bigcommerce/src/api/operations/login.ts @@ -5,7 +5,6 @@ import type { import type { LoginOperation } from '@vercel/commerce/types/login' import type { LoginMutation } from '../../../schema' import type { RecursivePartial } from '../utils/types' -import concatHeader from '../utils/concat-cookie' import type { BigcommerceConfig, Provider } from '..' export const loginMutation = /* GraphQL */ ` @@ -22,26 +21,23 @@ export default function loginOperation({ async function login(opts: { variables: T['variables'] config?: BigcommerceConfig - res: Response }): Promise async function login( opts: { variables: T['variables'] config?: BigcommerceConfig - res: Response } & OperationOptions ): Promise async function login({ query = loginMutation, variables, - res: response, config, }: { query?: string variables: T['variables'] - res: Response + config?: BigcommerceConfig }): Promise { config = commerce.getConfig(config) @@ -50,6 +46,9 @@ export default function loginOperation({ query, { variables } ) + + const headers = new Headers() + // Bigcommerce returns a Set-Cookie header with the auth cookie let cookie = res.headers.get('Set-Cookie') @@ -63,19 +62,13 @@ export default function loginOperation({ cookie = cookie.replace(/; SameSite=none/gi, '; SameSite=lax') } - const prevCookie = response.headers.get('Set-Cookie') - const newCookie = concatHeader(prevCookie, cookie) - - if (newCookie) { - res.headers.set( - 'Set-Cookie', - String(Array.isArray(newCookie) ? newCookie.join(',') : newCookie) - ) - } + headers.set('Set-Cookie', cookie) } return { result: data.login?.result, + headers, + status: res.status, } } diff --git a/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts b/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts index 9072b2432..e860cb714 100644 --- a/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts +++ b/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts @@ -1,5 +1,5 @@ import { FetcherError } from '@vercel/commerce/utils/errors' -import type { GraphQLFetcher } from '@vercel/commerce/api' +import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api' import type { BigcommerceConfig } from '../index' const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher = @@ -7,19 +7,20 @@ const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher = async ( query: string, { variables, preview } = {}, - options: { headers?: HeadersInit } = {} + options?: FetchOptions ): Promise => { // log.warn(query) const config = getConfig() const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { - method: 'POST', + method: options?.method || 'POST', headers: { Authorization: `Bearer ${config.apiToken}`, - ...options.headers, + ...options?.headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ + ...options?.body, query, variables, }), diff --git a/packages/bigcommerce/src/api/utils/get-customer-id.ts b/packages/bigcommerce/src/api/utils/get-customer-id.ts index e66bb5d30..7344e017b 100644 --- a/packages/bigcommerce/src/api/utils/get-customer-id.ts +++ b/packages/bigcommerce/src/api/utils/get-customer-id.ts @@ -20,7 +20,9 @@ async function getCustomerId({ getCustomerIdQuery, undefined, { - 'Set-Cookie': `${config.customerCookie}=${customerToken}`, + headers: { + cookie: `${config.customerCookie}=${customerToken}`, + }, } ) diff --git a/packages/bigcommerce/src/api/utils/types.ts b/packages/bigcommerce/src/api/utils/types.ts index 56f9c1728..9a75cda55 100644 --- a/packages/bigcommerce/src/api/utils/types.ts +++ b/packages/bigcommerce/src/api/utils/types.ts @@ -5,3 +5,15 @@ export type RecursivePartial = { export type RecursiveRequired = { [P in keyof T]-?: RecursiveRequired } + +export interface BCWishlist { + id: number + items: { + id: number + customer_id: number + is_public: boolean + product_id: number + variant_id: number + }[] + token: string +} diff --git a/packages/bigcommerce/src/lib/normalize.ts b/packages/bigcommerce/src/lib/normalize.ts index e83394c91..885694122 100644 --- a/packages/bigcommerce/src/lib/normalize.ts +++ b/packages/bigcommerce/src/lib/normalize.ts @@ -5,8 +5,10 @@ import type { Category, Brand } from '@vercel/commerce/types/site' import type { BigcommerceCart, BCCategory, BCBrand } from '../types' import type { ProductNode } from '../api/operations/get-all-products' import type { definitions } from '../api/definitions/store-content' +import type { BCWishlist } from '../api/utils/types' import getSlug from './get-slug' +import { Wishlist } from '@vercel/commerce/types/wishlist' function normalizeProductOption(productOption: any) { const { @@ -137,3 +139,16 @@ export function normalizeBrand(brand: BCBrand): Brand { path: `/${slug}`, } } + +export function normalizeWishlist(wishlist: BCWishlist): Wishlist { + return { + id: String(wishlist.id), + token: wishlist.token, + items: wishlist.items.map((item: any) => ({ + id: String(item.id), + productId: String(item.product_id), + variantId: String(item.variant_id), + product: item.product, + })), + } +} diff --git a/packages/commerce/src/api/index.ts b/packages/commerce/src/api/index.ts index 7cd52eb94..dc4aeda40 100644 --- a/packages/commerce/src/api/index.ts +++ b/packages/commerce/src/api/index.ts @@ -73,6 +73,12 @@ export type EndpointHandlers< > } +export type FetchOptions = { + method?: string + body?: Body + headers?: HeadersInit +} + export type APIProvider = { config: CommerceAPIConfig operations: APIOperations @@ -165,7 +171,7 @@ export interface CommerceAPIConfig { fetch( query: string, queryData?: CommerceAPIFetchOptions, - headers?: HeadersInit + options?: FetchOptions ): Promise> } diff --git a/packages/commerce/src/schemas/whishlist.ts b/packages/commerce/src/schemas/whishlist.ts index f1b406235..58c9a2865 100644 --- a/packages/commerce/src/schemas/whishlist.ts +++ b/packages/commerce/src/schemas/whishlist.ts @@ -5,7 +5,7 @@ export const wishlistSchemaItem = z.object({ id: z.string(), productId: z.string(), variantId: z.string(), - product: productSchema, + product: productSchema.optional(), }) export const wishlistSchema = z.object({ @@ -15,7 +15,7 @@ export const wishlistSchema = z.object({ }) export const getWishlistBodySchema = z.object({ - customerAccessToken: z.string(), + customerToken: z.string().optional(), includeProducts: z.boolean(), }) @@ -25,17 +25,17 @@ export const wishlistItemBodySchema = z.object({ }) export const addItemBodySchema = z.object({ - cartId: z.string().optional(), + customerToken: z.string(), item: wishlistItemBodySchema, }) export const updateItemBodySchema = z.object({ - cartId: z.string(), + customerToken: z.string(), itemId: z.string(), item: wishlistItemBodySchema, }) export const removeItemBodySchema = z.object({ - cartId: z.string(), + customerToken: z.string(), itemId: z.string(), }) diff --git a/packages/commerce/src/types/login.ts b/packages/commerce/src/types/login.ts index e032b1827..1fb06101a 100644 --- a/packages/commerce/src/types/login.ts +++ b/packages/commerce/src/types/login.ts @@ -26,6 +26,6 @@ export type LoginSchema = { } export type LoginOperation = { - data: { result?: string } + data: { result?: string; status?: number; headers?: Headers } variables: unknown } diff --git a/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts b/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts index 245e59971..9f07ef210 100644 --- a/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts +++ b/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts @@ -1,5 +1,5 @@ import { FetcherError } from '@vercel/commerce/utils/errors' -import type { GraphQLFetcher } from '@vercel/commerce/api' +import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api' import type { KiboCommerceConfig } from '../index' import { APIAuthenticationHelper } from './api-auth-helper' @@ -8,18 +8,23 @@ const fetchGraphqlApi: ( getConfig: () => KiboCommerceConfig ) => GraphQLFetcher = (getConfig) => - async (query: string, { variables, preview } = {}, headers?: HeadersInit) => { + async ( + query: string, + { variables, preview } = {}, + options?: FetchOptions + ) => { const config = getConfig() const authHelper = new APIAuthenticationHelper(config) const apiToken = await authHelper.getAccessToken() const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { - method: 'POST', + method: options?.method || 'POST', headers: { - ...headers, + ...options?.headers, Authorization: `Bearer ${apiToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ + ...options?.body, query, variables, }), diff --git a/packages/kibocommerce/src/api/utils/get-customer-id.ts b/packages/kibocommerce/src/api/utils/get-customer-id.ts index 1d93ade4f..f96000976 100644 --- a/packages/kibocommerce/src/api/utils/get-customer-id.ts +++ b/packages/kibocommerce/src/api/utils/get-customer-id.ts @@ -13,7 +13,9 @@ async function getCustomerId({ : null const accessToken = token ? JSON.parse(token).accessToken : null const { data } = await config.fetch(getCustomerAccountQuery, undefined, { - 'x-vol-user-claims': accessToken, + headers: { + 'x-vol-user-claims': accessToken, + }, }) return data?.customerAccount?.id diff --git a/packages/saleor/src/api/operations/get-all-pages.ts b/packages/saleor/src/api/operations/get-all-pages.ts index 0da523a88..e53cc8f31 100644 --- a/packages/saleor/src/api/operations/get-all-pages.ts +++ b/packages/saleor/src/api/operations/get-all-pages.ts @@ -30,7 +30,9 @@ export default function getAllPagesOperation({ { variables }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/saleor/src/api/operations/get-all-products.ts b/packages/saleor/src/api/operations/get-all-products.ts index 9a92e2f7a..1be61b4e0 100644 --- a/packages/saleor/src/api/operations/get-all-products.ts +++ b/packages/saleor/src/api/operations/get-all-products.ts @@ -38,9 +38,11 @@ export default function getAllProductsOperation({ query, { variables }, { - ...(locale && { - 'Accept-Language': locale, - }), + headers: { + ...(locale && { + 'Accept-Language': locale, + }), + }, } ) diff --git a/packages/saleor/src/api/operations/get-page.ts b/packages/saleor/src/api/operations/get-page.ts index 9d260713d..6fa427691 100644 --- a/packages/saleor/src/api/operations/get-page.ts +++ b/packages/saleor/src/api/operations/get-page.ts @@ -28,7 +28,9 @@ export default function getPageOperation({ { variables }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/saleor/src/api/operations/get-product.ts b/packages/saleor/src/api/operations/get-product.ts index c9bdf364b..cc2bf9a7a 100644 --- a/packages/saleor/src/api/operations/get-product.ts +++ b/packages/saleor/src/api/operations/get-product.ts @@ -32,7 +32,9 @@ export default function getProductOperation({ { variables }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/saleor/src/api/utils/fetch-graphql-api.ts b/packages/saleor/src/api/utils/fetch-graphql-api.ts index 45f5326d5..ba2183797 100644 --- a/packages/saleor/src/api/utils/fetch-graphql-api.ts +++ b/packages/saleor/src/api/utils/fetch-graphql-api.ts @@ -1,4 +1,4 @@ -import type { GraphQLFetcher } from '@vercel/commerce/api' +import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api' import { API_URL } from '../../const' import { getError } from '../../utils/handle-fetch-response' @@ -7,20 +7,21 @@ import { getToken } from '../../utils/index' const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, - headers?: HeadersInit + options?: FetchOptions ) => { const token = getToken() const res = await fetch(API_URL!, { - method: 'POST', + method: options?.method || 'POST', headers: { ...(token && { Authorization: `Bearer ${token}`, }), - ...headers, + ...options?.headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ + ...options?.body, query, variables, }), diff --git a/packages/sfcc/src/api/utils/fetch-local.ts b/packages/sfcc/src/api/utils/fetch-local.ts index 58e801517..89c1288d8 100644 --- a/packages/sfcc/src/api/utils/fetch-local.ts +++ b/packages/sfcc/src/api/utils/fetch-local.ts @@ -1,18 +1,23 @@ import { FetcherError } from '@vercel/commerce/utils/errors' -import type { GraphQLFetcher } from '@vercel/commerce/api' +import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api' import type { SFCCConfig } from '../index' const fetchGraphqlApi: (getConfig: () => SFCCConfig) => GraphQLFetcher = (getConfig) => - async (query: string, { variables, preview } = {}, headers?: HeadersInit) => { + async ( + query: string, + { variables, preview } = {}, + options?: FetchOptions + ) => { const config = getConfig() const res = await fetch(config.commerceUrl, { - method: 'POST', + method: options?.method || 'POST', headers: { - ...headers, + ...options?.headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ + ...options?.body, query, variables, }), diff --git a/packages/shopify/src/api/operations/get-all-pages.ts b/packages/shopify/src/api/operations/get-all-pages.ts index f8cde77f8..55c1d7d69 100644 --- a/packages/shopify/src/api/operations/get-all-pages.ts +++ b/packages/shopify/src/api/operations/get-all-pages.ts @@ -51,7 +51,9 @@ export default function getAllPagesOperation({ }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/shopify/src/api/operations/get-all-products.ts b/packages/shopify/src/api/operations/get-all-products.ts index 6ae4885ed..fcab20128 100644 --- a/packages/shopify/src/api/operations/get-all-products.ts +++ b/packages/shopify/src/api/operations/get-all-products.ts @@ -49,7 +49,9 @@ export default function getAllProductsOperation({ { variables }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/shopify/src/api/operations/get-page.ts b/packages/shopify/src/api/operations/get-page.ts index a98cae79b..e6ba19ce5 100644 --- a/packages/shopify/src/api/operations/get-page.ts +++ b/packages/shopify/src/api/operations/get-page.ts @@ -50,7 +50,9 @@ export default function getPageOperation({ }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/shopify/src/api/operations/get-product.ts b/packages/shopify/src/api/operations/get-product.ts index e8aa28120..76a3c865f 100644 --- a/packages/shopify/src/api/operations/get-product.ts +++ b/packages/shopify/src/api/operations/get-product.ts @@ -48,7 +48,9 @@ export default function getProductOperation({ }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/shopify/src/api/utils/fetch-graphql-api.ts b/packages/shopify/src/api/utils/fetch-graphql-api.ts index 1eac16ef1..39d6b3eb0 100644 --- a/packages/shopify/src/api/utils/fetch-graphql-api.ts +++ b/packages/shopify/src/api/utils/fetch-graphql-api.ts @@ -1,4 +1,4 @@ -import type { GraphQLFetcher } from '@vercel/commerce/api' +import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api' import { API_URL, API_TOKEN } from '../../const' import { getError } from '../../utils/handle-fetch-response' @@ -6,17 +6,18 @@ import { getError } from '../../utils/handle-fetch-response' const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, - headers?: HeadersInit + options?: FetchOptions ) => { try { const res = await fetch(API_URL, { - method: 'POST', + method: options?.method || 'POST', headers: { 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - ...headers, + ...options?.headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ + ...options?.body, query, variables, }), diff --git a/packages/shopify/src/utils/get-categories.ts b/packages/shopify/src/utils/get-categories.ts index e21592ee1..2bae3c3b5 100644 --- a/packages/shopify/src/utils/get-categories.ts +++ b/packages/shopify/src/utils/get-categories.ts @@ -17,7 +17,9 @@ const getCategories = async ({ }, { ...(locale && { - 'Accept-Language': locale, + headers: { + 'Accept-Language': locale, + }, }), } ) diff --git a/packages/vendure/src/api/utils/fetch-graphql-api.ts b/packages/vendure/src/api/utils/fetch-graphql-api.ts index f8377f7ae..61183ada3 100644 --- a/packages/vendure/src/api/utils/fetch-graphql-api.ts +++ b/packages/vendure/src/api/utils/fetch-graphql-api.ts @@ -1,21 +1,22 @@ import { FetcherError } from '@vercel/commerce/utils/errors' -import type { GraphQLFetcher } from '@vercel/commerce/api' +import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api' import { getCommerceApi } from '../' const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, - headers?: HeadersInit + options?: FetchOptions ) => { const config = getCommerceApi().getConfig() const res = await fetch(config.commerceUrl, { - method: 'POST', + method: options?.method || 'POST', headers: { - ...headers, + ...options?.headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ + ...options?.body, query, variables, }), diff --git a/site/components/auth/LoginView.tsx b/site/components/auth/LoginView.tsx index c7d399f94..c30f71834 100644 --- a/site/components/auth/LoginView.tsx +++ b/site/components/auth/LoginView.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState, useCallback } from 'react' +import { useEffect, useState, useCallback } from 'react' import { Logo, Button, Input } from '@components/ui' import useLogin from '@framework/auth/use-login' import { useUI } from '@components/ui/context' @@ -31,7 +31,6 @@ const LoginView: React.FC = () => { email, password, }) - setLoading(false) closeModal() } catch ({ errors }) { if (errors instanceof Array) { @@ -39,15 +38,15 @@ const LoginView: React.FC = () => { } else { setMessage('Unexpected error') } - setLoading(false) setDisabled(false) + } finally { + setLoading(false) } } const handleValidation = useCallback(() => { // Test for Alphanumeric password const validPassword = /^(?=.*[a-zA-Z])(?=.*[0-9])/.test(password) - // Unable to send form unless fields are valid. if (dirty) { setDisabled(!validate(email) || password.length < 7 || !validPassword) diff --git a/site/components/auth/SignUpView.tsx b/site/components/auth/SignUpView.tsx index fa71bf95d..cc6c629be 100644 --- a/site/components/auth/SignUpView.tsx +++ b/site/components/auth/SignUpView.tsx @@ -38,7 +38,6 @@ const SignUpView: FC = () => { lastName, password, }) - setLoading(false) closeModal() } catch ({ errors }) { console.error(errors) @@ -47,8 +46,9 @@ const SignUpView: FC = () => { } else { setMessage('Unexpected error') } - setLoading(false) setDisabled(false) + } finally { + setLoading(false) } } diff --git a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css index 93a183a2b..4cdec856f 100644 --- a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css +++ b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css @@ -4,7 +4,6 @@ z-index: 10; height: 100vh; min-width: 100vw; - transition: none; } @media screen(lg) { @@ -18,8 +17,7 @@ .link { @apply text-primary flex cursor-pointer px-6 py-3 transition ease-in-out duration-150 leading-6 - font-medium items-center capitalize w-full box-border - outline-0; + font-medium items-center capitalize w-full box-border outline-0; } .link:hover { diff --git a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx index 992d17178..83516da21 100644 --- a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx +++ b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx @@ -35,13 +35,7 @@ export default function CustomerMenuContent() { } return ( - + {LINKS.map(({ name, href }) => ( button { + @apply cursor-pointer transition ease-in-out duration-100 outline-none text-primary; +} + +.item > button:hover { + @apply text-accent-6 transition scale-110 outline-none; } .item:first-child { diff --git a/site/components/common/UserNav/UserNav.tsx b/site/components/common/UserNav/UserNav.tsx index 7441b854a..ff63c878b 100644 --- a/site/components/common/UserNav/UserNav.tsx +++ b/site/components/common/UserNav/UserNav.tsx @@ -23,13 +23,8 @@ const UserNav: React.FC<{ }> = ({ className }) => { const { data } = useCart() const { data: isCustomerLoggedIn } = useCustomer() - const { - toggleSidebar, - closeSidebarIfPresent, - openModal, - setSidebarView, - openSidebar, - } = useUI() + const { closeSidebarIfPresent, openModal, setSidebarView, openSidebar } = + useUI() const itemsCount = data?.lineItems?.reduce(countItem, 0) ?? 0 const DropdownTrigger = isCustomerLoggedIn @@ -59,10 +54,10 @@ const UserNav: React.FC<{ )} {process.env.COMMERCE_WISHLIST_ENABLED && (
  • - - + +
  • )} diff --git a/site/components/wishlist/WishlistButton/WishlistButton.tsx b/site/components/wishlist/WishlistButton/WishlistButton.tsx index 0bd5c188a..72767f843 100644 --- a/site/components/wishlist/WishlistButton/WishlistButton.tsx +++ b/site/components/wishlist/WishlistButton/WishlistButton.tsx @@ -30,7 +30,7 @@ const WishlistButton: FC = ({ // @ts-ignore Wishlist is not always enabled const itemInWishlist = data?.items?.find( // @ts-ignore Wishlist is not always enabled - (item) => item.product_id === productId && item.variant_id === variant.id + (item) => item.productId === productId && item.variantId === variant.id ) const handleWishlistChange = async (e: any) => { diff --git a/site/components/wishlist/WishlistCard/WishlistCard.tsx b/site/components/wishlist/WishlistCard/WishlistCard.tsx index 1d0cd08d9..07114dd6a 100644 --- a/site/components/wishlist/WishlistCard/WishlistCard.tsx +++ b/site/components/wishlist/WishlistCard/WishlistCard.tsx @@ -19,6 +19,7 @@ const WishlistCard: React.FC<{ item: WishlistItem }> = ({ item }) => { const product: Product = item.product + const { price } = usePrice({ amount: product.price?.value, baseAmount: product.price?.retailPrice, diff --git a/site/pages/wishlist.tsx b/site/pages/wishlist.tsx index 94f7a7049..c8e2ec5f5 100644 --- a/site/pages/wishlist.tsx +++ b/site/pages/wishlist.tsx @@ -35,9 +35,10 @@ export async function getStaticProps({ } export default function Wishlist() { - const { data: customer } = useCustomer() // @ts-ignore Shopify - Fix this types - const { data, isLoading, isEmpty } = useWishlist({ includeProducts: true }) + const { data, isLoading, isEmpty } = useWishlist({ + includeProducts: true, + }) return ( @@ -45,10 +46,10 @@ export default function Wishlist() { My Wishlist
    {isLoading ? ( -
    - {rangeMap(12, (i) => ( +
    + {rangeMap(4, (i) => ( -
    +
    ))}