🤖 test cases for home and navbar component

This commit is contained in:
Mian Muhammad 2022-04-20 15:29:34 +05:00
parent a46057c5ef
commit 436e2ed8df
334 changed files with 35721 additions and 8428 deletions

View File

@ -17,7 +17,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
- Kibo Commerce Demo: https://kibocommerce.vercel.store/ - Kibo Commerce Demo: https://kibocommerce.vercel.store/
- Commerce.js Demo: https://commercejs.vercel.store/ - Commerce.js Demo: https://commercejs.vercel.store/
## Run minimal version locally ## Run minimal version locally
> To run a minimal version of Next.js Commerce you can start with the default local provider `@vercel/commerce-local` that has disabled all features (cart, auth) and use static files for the backend > To run a minimal version of Next.js Commerce you can start with the default local provider `@vercel/commerce-local` that has disabled all features (cart, auth) and use static files for the backend
@ -25,7 +25,8 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
yarn # run this comman in root folder of the mono repo yarn # run this comman in root folder of the mono repo
yarn dev yarn dev
``` ```
> If you encounter any problems while installing and running for the first time, please see the Troubleshoot section
> If you encounter any problems while installing and running for the first time, please see the Troubleshoot section
## Features ## Features

5
cypress.json Normal file
View File

@ -0,0 +1,5 @@
{
"baseUrl": "http://localhost:3000",
"viewportHeight": 1000,
"viewportWidth": 1280
}

View File

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -0,0 +1,18 @@
describe('Header', () => {
beforeEach(() => {
cy.visit('/')
})
it('links to the correct pages', () => {
cy.getBySel('logo').click()
cy.location('pathname').should('eq', '/')
cy.getBySel('nav-link-search').click()
cy.location('pathname').should('eq', '/search')
cy.getBySel('nav-link-home-page').click({ multiple: true })
cy.location('pathname').should('eq', '/search/featured')
})
it('the search bar returns the correct search results', () => {})
})

View File

@ -0,0 +1,29 @@
describe('Home Page', () => {
it('displays all 3 products on the home page', () => {
cy.visit('/')
cy.getBySel('product-tag')
.eq(0)
.within(() => {
cy.getBySel('product-name').should(
'contain',
'New Short Sleeve T-Shirt'
)
cy.getBySel('product-price').should('contain', '$25.00 USD')
})
cy.getBySel('product-tag')
.eq(1)
.within(() => {
cy.getBySel('product-name').should('contain', 'Lightweight Jacket')
cy.getBySel('product-price').should('contain', '$249.99 USD')
})
cy.getBySel('product-tag')
.eq(2)
.within(() => {
cy.getBySel('product-name').should('contain', 'Shirt')
cy.getBySel('product-price').should('contain', '$25.00 USD')
})
})
})

22
cypress/plugins/index.js Normal file
View File

@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@ -0,0 +1,29 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
Cypress.Commands.add('getBySel', (selector, ...args) => {
return cy.get(`[data-test=${selector}]`, ...args)
})

20
cypress/support/index.js Normal file
View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

25442
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,9 +11,12 @@
"dev": "turbo run dev", "dev": "turbo run dev",
"start": "turbo run start", "start": "turbo run start",
"types": "turbo run types", "types": "turbo run types",
"prettier-fix": "prettier --write ." "prettier-fix": "prettier --write .",
"cypress:open": "cypress open",
"cypress:run": "cypress run"
}, },
"devDependencies": { "devDependencies": {
"cypress": "^9.5.4",
"husky": "^7.0.4", "husky": "^7.0.4",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"turbo": "^1.1.2" "turbo": "^1.1.2"

View File

@ -42,7 +42,7 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({
store_hash: config.storeHash, store_hash: config.storeHash,
customer_id: customerId, customer_id: customerId,
channel_id: config.storeChannelId, channel_id: config.storeChannelId,
redirect_to: data.checkout_url.replace(config.storeUrl, ""), redirect_to: data.checkout_url.replace(config.storeUrl, ''),
} }
let token = jwt.sign(payload, config.storeApiClientSecret!, { let token = jwt.sign(payload, config.storeApiClientSecret!, {
algorithm: 'HS256', algorithm: 'HS256',

View File

@ -4,8 +4,14 @@ import type {
HookFetcherContext, HookFetcherContext,
} from '@vercel/commerce/utils/types' } from '@vercel/commerce/utils/types'
import { ValidationError } from '@vercel/commerce/utils/errors' import { ValidationError } from '@vercel/commerce/utils/errors'
import useRemoveItem, { UseRemoveItem } from '@vercel/commerce/cart/use-remove-item' import useRemoveItem, {
import type { Cart, LineItem, RemoveItemHook } from '@vercel/commerce/types/cart' UseRemoveItem,
} from '@vercel/commerce/cart/use-remove-item'
import type {
Cart,
LineItem,
RemoveItemHook,
} from '@vercel/commerce/types/cart'
import useCart from './use-cart' import useCart from './use-cart'
export type RemoveItemFn<T = any> = T extends LineItem export type RemoveItemFn<T = any> = T extends LineItem

View File

@ -5,7 +5,9 @@ import type {
HookFetcherContext, HookFetcherContext,
} from '@vercel/commerce/utils/types' } from '@vercel/commerce/utils/types'
import { ValidationError } from '@vercel/commerce/utils/errors' import { ValidationError } from '@vercel/commerce/utils/errors'
import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item' import useUpdateItem, {
UseUpdateItem,
} from '@vercel/commerce/cart/use-update-item'
import type { LineItem, UpdateItemHook } from '@vercel/commerce/types/cart' import type { LineItem, UpdateItemHook } from '@vercel/commerce/types/cart'
import { handler as removeItemHandler } from './use-remove-item' import { handler as removeItemHandler } from './use-remove-item'
import useCart from './use-cart' import useCart from './use-cart'

View File

@ -1,5 +1,7 @@
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCheckout, { UseCheckout } from '@vercel/commerce/checkout/use-checkout' import useCheckout, {
UseCheckout,
} from '@vercel/commerce/checkout/use-checkout'
export default useCheckout as UseCheckout<typeof handler> export default useCheckout as UseCheckout<typeof handler>

View File

@ -1,4 +1,6 @@
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/address/use-add-item' import useAddItem, {
UseAddItem,
} from '@vercel/commerce/customer/address/use-add-item'
import { MutationHook } from '@vercel/commerce/utils/types' import { MutationHook } from '@vercel/commerce/utils/types'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>

View File

@ -1,4 +1,6 @@
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/card/use-add-item' import useAddItem, {
UseAddItem,
} from '@vercel/commerce/customer/card/use-add-item'
import { MutationHook } from '@vercel/commerce/utils/types' import { MutationHook } from '@vercel/commerce/utils/types'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>

View File

@ -1,5 +1,7 @@
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCustomer, { UseCustomer } from '@vercel/commerce/customer/use-customer' import useCustomer, {
UseCustomer,
} from '@vercel/commerce/customer/use-customer'
import type { CustomerHook } from '../types/customer' import type { CustomerHook } from '../types/customer'
export default useCustomer as UseCustomer<typeof handler> export default useCustomer as UseCustomer<typeof handler>

View File

@ -1,4 +1,7 @@
import { getCommerceProvider, useCommerce as useCoreCommerce } from '@vercel/commerce' import {
getCommerceProvider,
useCommerce as useCoreCommerce,
} from '@vercel/commerce'
import { bigcommerceProvider, BigcommerceProvider } from './provider' import { bigcommerceProvider, BigcommerceProvider } from './provider'
export { bigcommerceProvider } export { bigcommerceProvider }

View File

@ -68,7 +68,10 @@ Then, open [/site/.env.template](/site/.env.template) and add the provider name
Using BigCommerce as an example. The first thing to do is export a `CommerceProvider` component that includes a `provider` object with all the handlers that can be used for hooks: Using BigCommerce as an example. The first thing to do is export a `CommerceProvider` component that includes a `provider` object with all the handlers that can be used for hooks:
```tsx ```tsx
import { getCommerceProvider, useCommerce as useCoreCommerce } from '@vercel/commerce' import {
getCommerceProvider,
useCommerce as useCoreCommerce,
} from '@vercel/commerce'
import { bigcommerceProvider, BigcommerceProvider } from './provider' import { bigcommerceProvider, BigcommerceProvider } from './provider'
export { bigcommerceProvider } export { bigcommerceProvider }
@ -212,25 +215,26 @@ export const handler: MutationHook<AddItemHook> = {
``` ```
## Showing progress and features ## Showing progress and features
When creating a PR for a new provider, include this list in the PR description and mark the progress as you push so we can organize the code review. Not all points are required (but advised) so make sure to keep the list up to date. When creating a PR for a new provider, include this list in the PR description and mark the progress as you push so we can organize the code review. Not all points are required (but advised) so make sure to keep the list up to date.
**Status** **Status**
* [ ] CommerceProvider - [ ] CommerceProvider
* [ ] Schema & TS types - [ ] Schema & TS types
* [ ] API Operations - Get all collections - [ ] API Operations - Get all collections
* [ ] API Operations - Get all pages - [ ] API Operations - Get all pages
* [ ] API Operations - Get all products - [ ] API Operations - Get all products
* [ ] API Operations - Get page - [ ] API Operations - Get page
* [ ] API Operations - Get product - [ ] API Operations - Get product
* [ ] API Operations - Get Shop Info (categories and vendors working — `vendors` query still a WIP PR on Reaction) - [ ] API Operations - Get Shop Info (categories and vendors working — `vendors` query still a WIP PR on Reaction)
* [ ] Hook - Add Item - [ ] Hook - Add Item
* [ ] Hook - Remove Item - [ ] Hook - Remove Item
* [ ] Hook - Update Item - [ ] Hook - Update Item
* [ ] Hook - Get Cart (account-tied carts working, anonymous carts working, cart reconciliation working) - [ ] Hook - Get Cart (account-tied carts working, anonymous carts working, cart reconciliation working)
* [ ] Auth (based on a WIP PR on Reaction - still need to implement refresh tokens) - [ ] Auth (based on a WIP PR on Reaction - still need to implement refresh tokens)
* [ ] Customer information - [ ] Customer information
* [ ] Product attributes - Size, Colors - [ ] Product attributes - Size, Colors
* [ ] Custom checkout - [ ] Custom checkout
* [ ] Typing (in progress) - [ ] Typing (in progress)
* [ ] Tests - [ ] Tests

View File

@ -3,58 +3,60 @@ import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation' import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..' import type { GetAPISchema } from '..'
const cartEndpoint: GetAPISchema<any, CartSchema<any>>['endpoint']['handler'] = const cartEndpoint: GetAPISchema<
async (ctx) => { any,
const { req, res, handlers, config } = ctx CartSchema<any>
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx
if ( if (
!isAllowedOperation(req, res, { !isAllowedOperation(req, res, {
GET: handlers['getCart'], GET: handlers['getCart'],
POST: handlers['addItem'], POST: handlers['addItem'],
PUT: handlers['updateItem'], PUT: handlers['updateItem'],
DELETE: handlers['removeItem'], DELETE: handlers['removeItem'],
}) })
) { ) {
return return
}
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
// Return current cart info
if (req.method === 'GET') {
const body = { cartId }
return await handlers['getCart']({ ...ctx, body })
}
// Create or add an item to the cart
if (req.method === 'POST') {
const body = { ...req.body, cartId }
return await handlers['addItem']({ ...ctx, body })
}
// Update item in cart
if (req.method === 'PUT') {
const body = { ...req.body, cartId }
return await handlers['updateItem']({ ...ctx, body })
}
// Remove an item from the cart
if (req.method === 'DELETE') {
const body = { ...req.body, cartId }
return await handlers['removeItem']({ ...ctx, body })
}
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
} }
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
// Return current cart info
if (req.method === 'GET') {
const body = { cartId }
return await handlers['getCart']({ ...ctx, body })
}
// Create or add an item to the cart
if (req.method === 'POST') {
const body = { ...req.body, cartId }
return await handlers['addItem']({ ...ctx, body })
}
// Update item in cart
if (req.method === 'PUT') {
const body = { ...req.body, cartId }
return await handlers['updateItem']({ ...ctx, body })
}
// Remove an item from the cart
if (req.method === 'DELETE') {
const body = { ...req.body, cartId }
return await handlers['removeItem']({ ...ctx, body })
}
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
}
export default cartEndpoint export default cartEndpoint

View File

@ -3,33 +3,35 @@ import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation' import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..' import type { GetAPISchema } from '..'
const logoutEndpoint: GetAPISchema<any, LogoutSchema>['endpoint']['handler'] = const logoutEndpoint: GetAPISchema<
async (ctx) => { any,
const { req, res, handlers } = ctx LogoutSchema
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers } = ctx
if ( if (
!isAllowedOperation(req, res, { !isAllowedOperation(req, res, {
GET: handlers['logout'], GET: handlers['logout'],
}) })
) { ) {
return return
}
try {
const redirectTo = req.query.redirect_to
const body = typeof redirectTo === 'string' ? { redirectTo } : {}
return await handlers['logout']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
} }
try {
const redirectTo = req.query.redirect_to
const body = typeof redirectTo === 'string' ? { redirectTo } : {}
return await handlers['logout']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
}
export default logoutEndpoint export default logoutEndpoint

View File

@ -3,34 +3,36 @@ import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation' import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..' import type { GetAPISchema } from '..'
const signupEndpoint: GetAPISchema<any, SignupSchema>['endpoint']['handler'] = const signupEndpoint: GetAPISchema<
async (ctx) => { any,
const { req, res, handlers, config } = ctx SignupSchema
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx
if ( if (
!isAllowedOperation(req, res, { !isAllowedOperation(req, res, {
POST: handlers['signup'], POST: handlers['signup'],
}) })
) { ) {
return return
}
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
const body = { ...req.body, cartId }
return await handlers['signup']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
} }
const { cookies } = req
const cartId = cookies[config.cartCookie]
try {
const body = { ...req.body, cartId }
return await handlers['signup']({ ...ctx, body })
} catch (error) {
console.error(error)
const message =
error instanceof CommerceAPIError
? 'An unexpected error ocurred with the Commerce API'
: 'An unexpected error ocurred'
res.status(500).json({ data: null, errors: [{ message }] })
}
}
export default signupEndpoint export default signupEndpoint

View File

@ -1,7 +1,9 @@
import { useCallback } from 'react' import { useCallback } from 'react'
import type { MutationHook } from '@vercel/commerce/utils/types' import type { MutationHook } from '@vercel/commerce/utils/types'
import type { RemoveItemHook } from '@vercel/commerce/types/cart' import type { RemoveItemHook } from '@vercel/commerce/types/cart'
import useRemoveItem, { UseRemoveItem } from '@vercel/commerce/cart/use-remove-item' import useRemoveItem, {
UseRemoveItem,
} from '@vercel/commerce/cart/use-remove-item'
import type { CommercejsCart } from '../types/cart' import type { CommercejsCart } from '../types/cart'
import { normalizeCart } from '../utils/normalize-cart' import { normalizeCart } from '../utils/normalize-cart'
import useCart from './use-cart' import useCart from './use-cart'

View File

@ -6,7 +6,9 @@ import type {
import { ValidationError } from '@vercel/commerce/utils/errors' import { ValidationError } from '@vercel/commerce/utils/errors'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import { useCallback } from 'react' import { useCallback } from 'react'
import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item' import useUpdateItem, {
UseUpdateItem,
} from '@vercel/commerce/cart/use-update-item'
import type { CommercejsCart } from '../types/cart' import type { CommercejsCart } from '../types/cart'
import { normalizeCart } from '../utils/normalize-cart' import { normalizeCart } from '../utils/normalize-cart'
import useCart from './use-cart' import useCart from './use-cart'

View File

@ -2,7 +2,9 @@ import type { GetCheckoutHook } from '@vercel/commerce/types/checkout'
import { useMemo } from 'react' import { useMemo } from 'react'
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCheckout, { UseCheckout } from '@vercel/commerce/checkout/use-checkout' import useCheckout, {
UseCheckout,
} from '@vercel/commerce/checkout/use-checkout'
import useSubmitCheckout from './use-submit-checkout' import useSubmitCheckout from './use-submit-checkout'
import { useCheckoutContext } from '@components/checkout/context' import { useCheckoutContext } from '@components/checkout/context'

View File

@ -1,7 +1,9 @@
import type { AddItemHook } from '@vercel/commerce/types/customer/address' import type { AddItemHook } from '@vercel/commerce/types/customer/address'
import type { MutationHook } from '@vercel/commerce/utils/types' import type { MutationHook } from '@vercel/commerce/utils/types'
import { useCallback } from 'react' import { useCallback } from 'react'
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/address/use-add-item' import useAddItem, {
UseAddItem,
} from '@vercel/commerce/customer/address/use-add-item'
import { useCheckoutContext } from '@components/checkout/context' import { useCheckoutContext } from '@components/checkout/context'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>

View File

@ -1,7 +1,9 @@
import type { AddItemHook } from '@vercel/commerce/types/customer/card' import type { AddItemHook } from '@vercel/commerce/types/customer/card'
import type { MutationHook } from '@vercel/commerce/utils/types' import type { MutationHook } from '@vercel/commerce/utils/types'
import { useCallback } from 'react' import { useCallback } from 'react'
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/card/use-add-item' import useAddItem, {
UseAddItem,
} from '@vercel/commerce/customer/card/use-add-item'
import { useCheckoutContext } from '@components/checkout/context' import { useCheckoutContext } from '@components/checkout/context'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>

View File

@ -1,7 +1,9 @@
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import { decode } from 'jsonwebtoken' import { decode } from 'jsonwebtoken'
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCustomer, { UseCustomer } from '@vercel/commerce/customer/use-customer' import useCustomer, {
UseCustomer,
} from '@vercel/commerce/customer/use-customer'
import { CUSTOMER_COOKIE, API_URL } from '../constants' import { CUSTOMER_COOKIE, API_URL } from '../constants'
import type { CustomerHook } from '../types/customer' import type { CustomerHook } from '../types/customer'

View File

@ -1,5 +1,8 @@
import { commercejsProvider, CommercejsProvider } from './provider' import { commercejsProvider, CommercejsProvider } from './provider'
import { getCommerceProvider, useCommerce as useCoreCommerce } from '@vercel/commerce' import {
getCommerceProvider,
useCommerce as useCoreCommerce,
} from '@vercel/commerce'
export { commercejsProvider } export { commercejsProvider }
export type { CommercejsProvider } export type { CommercejsProvider }

View File

@ -24,12 +24,12 @@ KIBO_AUTH_URL='https://home.mozu.com'
- `KIBO_CLIENT_ID` - Unique Application (Client) ID of your Application - `KIBO_CLIENT_ID` - Unique Application (Client) ID of your Application
- `KIBO_SHARED_SECRET` - Secret API key used to authenticate application/client id. - `KIBO_SHARED_SECRET` - Secret API key used to authenticate application/client id.
Your Kibo Client ID and Shared Secret can be found from your [Kibo eCommerce Dev Center](https://mozu.com/login) Your Kibo Client ID and Shared Secret can be found from your [Kibo eCommerce Dev Center](https://mozu.com/login)
Visit [Kibo documentation](https://apidocs.kibong-perf.com/?spec=graphql#auth) for more details on API authentication Visit [Kibo documentation](https://apidocs.kibong-perf.com/?spec=graphql#auth) for more details on API authentication
Based on the config, this integration will handle Authenticating your application against the Kibo API using the Kibo Client ID and Kibo Shared Secret. Based on the config, this integration will handle Authenticating your application against the Kibo API using the Kibo Client ID and Kibo Shared Secret.
## Contribute ## Contribute
Our commitment to Open Source can be found [here](https://vercel.com/oss). Our commitment to Open Source can be found [here](https://vercel.com/oss).

View File

@ -1,21 +1,21 @@
{ {
"schema": { "schema": {
"https://t17194-s21127.dev10.kubedev.kibo-dev.com/graphql": {} "https://t17194-s21127.dev10.kubedev.kibo-dev.com/graphql": {}
}, },
"generates": { "generates": {
"./schema.d.ts": { "./schema.d.ts": {
"plugins": ["typescript", "typescript-operations"], "plugins": ["typescript", "typescript-operations"],
"config": { "config": {
"scalars": { "scalars": {
"ID": "string" "ID": "string"
}
} }
},
"./schema.graphql": {
"plugins": ["schema-ast"]
} }
}, },
"hooks": { "./schema.graphql": {
"afterAllFileWrite": ["prettier --write"] "plugins": ["schema-ast"]
} }
},
"hooks": {
"afterAllFileWrite": ["prettier --write"]
} }
}

View File

@ -2,10 +2,12 @@ export type Maybe<T> = T | null
export type Exact<T extends { [key: string]: unknown }> = { export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K] [K in keyof T]: T[K]
} }
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
{ [SubKey in K]?: Maybe<T[SubKey]> } [SubKey in K]?: Maybe<T[SubKey]>
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & }
{ [SubKey in K]: Maybe<T[SubKey]> } export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]: Maybe<T[SubKey]>
}
/** All built-in and custom scalars, mapped to their actual values */ /** All built-in and custom scalars, mapped to their actual values */
export type Scalars = { export type Scalars = {
ID: string ID: string

View File

@ -74,7 +74,7 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({
if (!cookieHandler.getAccessToken()) { if (!cookieHandler.getAccessToken()) {
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken() let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken()
accessToken = anonymousShopperTokenResponse.accessToken; accessToken = anonymousShopperTokenResponse.accessToken
} else { } else {
accessToken = cookieHandler.getAccessToken() accessToken = cookieHandler.getAccessToken()
} }

View File

@ -16,7 +16,8 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({
let accessToken = null let accessToken = null
if (!cookieHandler.getAccessToken()) { if (!cookieHandler.getAccessToken()) {
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken() let anonymousShopperTokenResponse =
await cookieHandler.getAnonymousToken()
const response = anonymousShopperTokenResponse.response const response = anonymousShopperTokenResponse.response
accessToken = anonymousShopperTokenResponse.accessToken accessToken = anonymousShopperTokenResponse.accessToken
cookieHandler.setAnonymousShopperCookie(response) cookieHandler.setAnonymousShopperCookie(response)

View File

@ -1,8 +1,8 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api' import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import cartEndpoint from '@vercel/commerce/api/endpoints/cart' import cartEndpoint from '@vercel/commerce/api/endpoints/cart'
import type { KiboCommerceAPI } from '../..' import type { KiboCommerceAPI } from '../..'
import getCart from './get-cart'; import getCart from './get-cart'
import addItem from './add-item'; import addItem from './add-item'
import updateItem from './update-item' import updateItem from './update-item'
import removeItem from './remove-item' import removeItem from './remove-item'

View File

@ -2,16 +2,16 @@ import { Product } from '@vercel/commerce/types/product'
import { ProductsEndpoint } from '.' import { ProductsEndpoint } from '.'
import productSearchQuery from '../../../queries/product-search-query' import productSearchQuery from '../../../queries/product-search-query'
import { buildProductSearchVars } from '../../../../lib/product-search-vars' import { buildProductSearchVars } from '../../../../lib/product-search-vars'
import {normalizeProduct} from '../../../../lib/normalize' import { normalizeProduct } from '../../../../lib/normalize'
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
res, res,
body: { search, categoryId, brandId, sort }, body: { search, categoryId, brandId, sort },
config, config,
}) => { }) => {
const pageSize = 100; const pageSize = 100
const filters = {}; const filters = {}
const startIndex = 0; const startIndex = 0
const variables = buildProductSearchVars({ const variables = buildProductSearchVars({
categoryCode: categoryId, categoryCode: categoryId,
pageSize, pageSize,
@ -20,12 +20,14 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
filters, filters,
startIndex, startIndex,
}) })
const {data} = await config.fetch(productSearchQuery, { variables }); const { data } = await config.fetch(productSearchQuery, { variables })
const found = data?.products?.items?.length > 0 ? true : false; const found = data?.products?.items?.length > 0 ? true : false
let productsResponse= data?.products?.items.map((item: any) =>normalizeProduct(item,config)); let productsResponse = data?.products?.items.map((item: any) =>
const products: Product[] = found ? productsResponse : []; normalizeProduct(item, config)
)
const products: Product[] = found ? productsResponse : []
res.status(200).json({ data: { products, found } }); res.status(200).json({ data: { products, found } })
} }
export default getProducts export default getProducts

View File

@ -3,34 +3,31 @@ import type { CustomerEndpoint } from '.'
import { getCustomerAccountQuery } from '../../queries/get-customer-account-query' import { getCustomerAccountQuery } from '../../queries/get-customer-account-query'
import { normalizeCustomer } from '../../../lib/normalize' import { normalizeCustomer } from '../../../lib/normalize'
const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = async ({ const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] =
req, async ({ req, res, config }) => {
res, const cookieHandler = new CookieHandler(config, req, res)
config, let accessToken = cookieHandler.getAccessToken()
}) => {
const cookieHandler = new CookieHandler(config, req, res)
let accessToken = cookieHandler.getAccessToken();
if (!cookieHandler.isShopperCookieAnonymous()) { if (!cookieHandler.isShopperCookieAnonymous()) {
const { data } = await config.fetch(getCustomerAccountQuery, undefined, { const { data } = await config.fetch(getCustomerAccountQuery, undefined, {
headers: { headers: {
'x-vol-user-claims': accessToken, 'x-vol-user-claims': accessToken,
}, },
})
const customer = normalizeCustomer(data?.customerAccount)
if (!customer.id) {
return res.status(400).json({
data: null,
errors: [{ message: 'Customer not found', code: 'not_found' }],
}) })
const customer = normalizeCustomer(data?.customerAccount)
if (!customer.id) {
return res.status(400).json({
data: null,
errors: [{ message: 'Customer not found', code: 'not_found' }],
})
}
return res.status(200).json({ data: { customer } })
} }
return res.status(200).json({ data: { customer } }) res.status(200).json({ data: null })
} }
res.status(200).json({ data: null })
}
export default getLoggedInCustomer export default getLoggedInCustomer

View File

@ -15,6 +15,4 @@ const loginApi = createEndpoint<LoginAPI>({
handlers, handlers,
}) })
export default loginApi; export default loginApi

View File

@ -1,7 +1,7 @@
import { FetcherError } from '@vercel/commerce/utils/errors' import { FetcherError } from '@vercel/commerce/utils/errors'
import type { LoginEndpoint } from '.' import type { LoginEndpoint } from '.'
import { loginMutation } from '../../mutations/login-mutation' import { loginMutation } from '../../mutations/login-mutation'
import { prepareSetCookie } from '../../../lib/prepare-set-cookie'; import { prepareSetCookie } from '../../../lib/prepare-set-cookie'
import { setCookies } from '../../../lib/set-cookie' import { setCookies } from '../../../lib/set-cookie'
import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date' import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date'
@ -14,7 +14,6 @@ const login: LoginEndpoint['handlers']['login'] = async ({
config, config,
commerce, commerce,
}) => { }) => {
if (!(email && password)) { if (!(email && password)) {
return res.status(400).json({ return res.status(400).json({
data: null, data: null,
@ -22,23 +21,23 @@ const login: LoginEndpoint['handlers']['login'] = async ({
}) })
} }
let response; let response
try { try {
const variables = { loginInput: { username: email, password } }
const variables = { loginInput : { username: email, password }}; response = await config.fetch(loginMutation, { variables })
response = await config.fetch(loginMutation, { variables }) const { account: token } = response.data
const { account: token } = response.data;
// Set Cookie // Set Cookie
const cookieExpirationDate = getCookieExpirationDate(config.customerCookieMaxAgeInDays) const cookieExpirationDate = getCookieExpirationDate(
config.customerCookieMaxAgeInDays
)
const authCookie = prepareSetCookie( const authCookie = prepareSetCookie(
config.customerCookie, config.customerCookie,
JSON.stringify(token), JSON.stringify(token),
token.accessTokenExpiration ? { expires: cookieExpirationDate }: {}, token.accessTokenExpiration ? { expires: cookieExpirationDate } : {}
) )
setCookies(res, [authCookie]) setCookies(res, [authCookie])
} catch (error) { } catch (error) {
// Check if the email and password didn't match an existing account // Check if the email and password didn't match an existing account
if ( if (

View File

@ -1,6 +1,6 @@
import type { LogoutEndpoint } from '.' import type { LogoutEndpoint } from '.'
import {prepareSetCookie} from '../../../lib/prepare-set-cookie'; import { prepareSetCookie } from '../../../lib/prepare-set-cookie'
import {setCookies} from '../../../lib/set-cookie' import { setCookies } from '../../../lib/set-cookie'
const logout: LogoutEndpoint['handlers']['logout'] = async ({ const logout: LogoutEndpoint['handlers']['logout'] = async ({
res, res,
@ -8,8 +8,11 @@ const logout: LogoutEndpoint['handlers']['logout'] = async ({
config, config,
}) => { }) => {
// Remove the cookie // Remove the cookie
const authCookie = prepareSetCookie(config.customerCookie,'',{ maxAge: -1, path: '/' }) const authCookie = prepareSetCookie(config.customerCookie, '', {
setCookies(res, [authCookie]) maxAge: -1,
path: '/',
})
setCookies(res, [authCookie])
// Only allow redirects to a relative URL // Only allow redirects to a relative URL
if (redirectTo?.startsWith('/')) { if (redirectTo?.startsWith('/')) {

View File

@ -1,7 +1,10 @@
import { FetcherError } from '@vercel/commerce/utils/errors' import { FetcherError } from '@vercel/commerce/utils/errors'
import type { SignupEndpoint } from '.' import type { SignupEndpoint } from '.'
import { registerUserMutation, registerUserLoginMutation } from '../../mutations/signup-mutation' import {
import { prepareSetCookie } from '../../../lib/prepare-set-cookie'; registerUserMutation,
registerUserLoginMutation,
} from '../../mutations/signup-mutation'
import { prepareSetCookie } from '../../../lib/prepare-set-cookie'
import { setCookies } from '../../../lib/set-cookie' import { setCookies } from '../../../lib/set-cookie'
import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date' import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date'
@ -14,7 +17,6 @@ const signup: SignupEndpoint['handlers']['signup'] = async ({
config, config,
commerce, commerce,
}) => { }) => {
if (!(email && password)) { if (!(email && password)) {
return res.status(400).json({ return res.status(400).json({
data: null, data: null,
@ -22,48 +24,52 @@ const signup: SignupEndpoint['handlers']['signup'] = async ({
}) })
} }
let response; let response
try { try {
// Register user // Register user
const registerUserVariables = { const registerUserVariables = {
customerAccountInput: { customerAccountInput: {
emailAddress: email, emailAddress: email,
firstName: firstName, firstName: firstName,
lastName: lastName, lastName: lastName,
acceptsMarketing: true, acceptsMarketing: true,
id: 0 id: 0,
} },
} }
const registerUserResponse = await config.fetch(registerUserMutation, { variables: registerUserVariables}) const registerUserResponse = await config.fetch(registerUserMutation, {
const accountId = registerUserResponse.data?.account?.id; variables: registerUserVariables,
})
const accountId = registerUserResponse.data?.account?.id
// Login user // Login user
const registerUserLoginVairables = { const registerUserLoginVairables = {
accountId: accountId, accountId: accountId,
customerLoginInfoInput: { customerLoginInfoInput: {
emailAddress: email, emailAddress: email,
username: email, username: email,
password: password, password: password,
isImport: false isImport: false,
} },
} }
response = await config.fetch(registerUserLoginMutation, { variables: registerUserLoginVairables}) response = await config.fetch(registerUserLoginMutation, {
const { account: token } = response.data; variables: registerUserLoginVairables,
})
const { account: token } = response.data
// Set Cookie // Set Cookie
const cookieExpirationDate = getCookieExpirationDate(config.customerCookieMaxAgeInDays) const cookieExpirationDate = getCookieExpirationDate(
config.customerCookieMaxAgeInDays
)
const authCookie = prepareSetCookie( const authCookie = prepareSetCookie(
config.customerCookie, config.customerCookie,
JSON.stringify(token), JSON.stringify(token),
token.accessTokenExpiration ? { expires: cookieExpirationDate }: {}, token.accessTokenExpiration ? { expires: cookieExpirationDate } : {}
) )
setCookies(res, [authCookie]) setCookies(res, [authCookie])
} catch (error) { } catch (error) {
// Check if the email and password didn't match an existing account // Check if the email and password didn't match an existing account
if ( if (

View File

@ -11,7 +11,7 @@ const buildAddToWishlistVariables = ({
productId, productId,
variantId, variantId,
productResponse, productResponse,
wishlist wishlist,
}: { }: {
productId: string productId: string
variantId: string variantId: string
@ -23,7 +23,7 @@ const buildAddToWishlistVariables = ({
const selectedOptions = product.variations?.find( const selectedOptions = product.variations?.find(
(v: any) => v.productCode === variantId (v: any) => v.productCode === variantId
).options ).options
const quantity=1 const quantity = 1
let options: any[] = [] let options: any[] = []
selectedOptions?.forEach((each: any) => { selectedOptions?.forEach((each: any) => {
product?.options product?.options
@ -47,8 +47,8 @@ const buildAddToWishlistVariables = ({
productCode: productId, productCode: productId,
variationProductCode: variantId ? variantId : null, variationProductCode: variantId ? variantId : null,
options, options,
}
}, },
},
} }
} }
@ -58,8 +58,10 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
config, config,
commerce, commerce,
}) => { }) => {
const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null; const token = customerToken
const accessToken = token ? JSON.parse(token).accessToken : null; ? Buffer.from(customerToken, 'base64').toString('ascii')
: null
const accessToken = token ? JSON.parse(token).accessToken : null
let result: { data?: any } = {} let result: { data?: any } = {}
let wishlist: any let wishlist: any
@ -70,8 +72,9 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
}) })
} }
const customerId = customerToken && (await getCustomerId({ customerToken, config })) const customerId =
const wishlistName= config.defaultWishlistName customerToken && (await getCustomerId({ customerToken, config }))
const wishlistName = config.defaultWishlistName
if (!customerId) { if (!customerId) {
return res.status(400).json({ return res.status(400).json({
@ -84,16 +87,21 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
variables: { customerId, wishlistName }, variables: { customerId, wishlistName },
config, config,
}) })
wishlist= wishlistResponse?.wishlist wishlist = wishlistResponse?.wishlist
if(Object.keys(wishlist).length === 0) { if (Object.keys(wishlist).length === 0) {
const createWishlistResponse= await config.fetch(createWishlist, {variables: { const createWishlistResponse = await config.fetch(
wishlistInput: { createWishlist,
customerAccountId: customerId, {
name: wishlistName variables: {
} wishlistInput: {
} customerAccountId: customerId,
}, {headers: { 'x-vol-user-claims': accessToken } }) name: wishlistName,
wishlist= createWishlistResponse?.data?.createWishlist },
},
},
{ headers: { 'x-vol-user-claims': accessToken } }
)
wishlist = createWishlistResponse?.data?.createWishlist
} }
const productResponse = await config.fetch(getProductQuery, { const productResponse = await config.fetch(getProductQuery, {
@ -103,20 +111,31 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
const addItemToWishlistResponse = await config.fetch( const addItemToWishlistResponse = await config.fetch(
addItemToWishlistMutation, addItemToWishlistMutation,
{ {
variables: buildAddToWishlistVariables({ ...item, productResponse, wishlist }), variables: buildAddToWishlistVariables({
...item,
productResponse,
wishlist,
}),
}, },
{ headers: { 'x-vol-user-claims': accessToken } } { headers: { 'x-vol-user-claims': accessToken } }
) )
if(addItemToWishlistResponse?.data?.createWishlistItem){ if (addItemToWishlistResponse?.data?.createWishlistItem) {
const wishlistResponse= await commerce.getCustomerWishlist({ const wishlistResponse = await commerce.getCustomerWishlist({
variables: { customerId, wishlistName }, variables: { customerId, wishlistName },
config, config,
}) })
wishlist= wishlistResponse?.wishlist wishlist = wishlistResponse?.wishlist
}
result = {
data: {
...wishlist,
items: wishlist?.items?.map((item: any) =>
normalizeWishlistItem(item, config)
),
},
} }
result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config))} }
res.status(200).json({ data: result?.data }) res.status(200).json({ data: result?.data })
} }

View File

@ -11,8 +11,9 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
}) => { }) => {
let result: { data?: any } = {} let result: { data?: any } = {}
if (customerToken) { if (customerToken) {
const customerId = customerToken && (await getCustomerId({ customerToken, config })) const customerId =
const wishlistName= config.defaultWishlistName customerToken && (await getCustomerId({ customerToken, config }))
const wishlistName = config.defaultWishlistName
if (!customerId) { if (!customerId) {
// If the customerToken is invalid, then this request is too // If the customerToken is invalid, then this request is too
return res.status(404).json({ return res.status(404).json({
@ -26,7 +27,14 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
config, config,
}) })
result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config, includeProducts))} } result = {
data: {
...wishlist,
items: wishlist?.items?.map((item: any) =>
normalizeWishlistItem(item, config, includeProducts)
),
},
}
} }
res.status(200).json({ data: result?.data ?? null }) res.status(200).json({ data: result?.data ?? null })

View File

@ -10,50 +10,58 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
config, config,
commerce, commerce,
}) => { }) => {
const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null; const token = customerToken
const accessToken = token ? JSON.parse(token).accessToken : null; ? Buffer.from(customerToken, 'base64').toString('ascii')
: null
const accessToken = token ? JSON.parse(token).accessToken : null
let result: { data?: any } = {} let result: { data?: any } = {}
let wishlist: any let wishlist: any
const customerId = customerToken && (await getCustomerId({ customerToken, config })) const customerId =
const wishlistName= config.defaultWishlistName customerToken && (await getCustomerId({ customerToken, config }))
const wishlistName = config.defaultWishlistName
const wishlistResponse = await commerce.getCustomerWishlist({ const wishlistResponse = await commerce.getCustomerWishlist({
variables: { customerId, wishlistName }, variables: { customerId, wishlistName },
config, config,
}) })
wishlist= wishlistResponse?.wishlist wishlist = wishlistResponse?.wishlist
if (!wishlist || !itemId) { if (!wishlist || !itemId) {
return res.status(400).json({ return res.status(400).json({
data: null, data: null,
errors: [{ message: 'Invalid request' }], errors: [{ message: 'Invalid request' }],
}) })
} }
const removedItem = wishlist?.items?.find( const removedItem = wishlist?.items?.find((item: any) => {
(item:any) => { return item.product.productCode === itemId
return item.product.productCode === itemId; })
}
);
const removeItemFromWishlistResponse = await config.fetch( const removeItemFromWishlistResponse = await config.fetch(
removeItemFromWishlistMutation, removeItemFromWishlistMutation,
{ {
variables: { variables: {
wishlistId: wishlist?.id, wishlistId: wishlist?.id,
wishlistItemId: removedItem?.id wishlistItemId: removedItem?.id,
}, },
}, },
{ headers: { 'x-vol-user-claims': accessToken } } { headers: { 'x-vol-user-claims': accessToken } }
) )
if(removeItemFromWishlistResponse?.data?.deleteWishlistItem){ if (removeItemFromWishlistResponse?.data?.deleteWishlistItem) {
const wishlistResponse= await commerce.getCustomerWishlist({ const wishlistResponse = await commerce.getCustomerWishlist({
variables: { customerId, wishlistName }, variables: { customerId, wishlistName },
config, config,
}) })
wishlist= wishlistResponse?.wishlist wishlist = wishlistResponse?.wishlist
}
result = {
data: {
...wishlist,
items: wishlist?.items?.map((item: any) =>
normalizeWishlistItem(item, config)
),
},
} }
result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config))} }
res.status(200).json({ data: result?.data }) res.status(200).json({ data: result?.data })
} }

View File

@ -1,5 +1,5 @@
import { productDetails } from '../fragments/productDetails' import { productDetails } from '../fragments/productDetails'
export const cartItemDetails = /*GraphQL*/` export const cartItemDetails = /*GraphQL*/ `
fragment cartItemDetails on CartItem { fragment cartItemDetails on CartItem {
id id
product { product {
@ -8,4 +8,4 @@ fragment cartItemDetails on CartItem {
quantity quantity
} }
${productDetails} ${productDetails}
`; `

View File

@ -1,11 +1,12 @@
export const CategoryInfo = /* GraphQL */` export const CategoryInfo = /* GraphQL */ `
fragment categoryInfo on PrCategory { fragment categoryInfo on PrCategory {
categoryId categoryId
categoryCode categoryCode
isDisplayed isDisplayed
content { content {
name name
slug slug
description description
} }
}`; }
`

View File

@ -1,98 +1,104 @@
export const productPrices = /* GraphQL */` export const productPrices = /* GraphQL */ `
fragment productPrices on Product { fragment productPrices on Product {
price { price {
price
salePrice
}
priceRange {
lower {
price price
salePrice salePrice
} }
priceRange { upper {
lower { price, salePrice} price
upper { price, salePrice } salePrice
} }
} }
`; }
export const productAttributes = /* GraphQL */` `
fragment productAttributes on Product { export const productAttributes = /* GraphQL */ `
properties { fragment productAttributes on Product {
attributeFQN properties {
attributeDetail { attributeFQN
name attributeDetail {
} name
isHidden }
values { isHidden
value values {
stringValue value
} stringValue
} }
}
`;
export const productContent = /* GraphQL */`
fragment productContent on Product {
content {
productFullDescription
productShortDescription
seoFriendlyUrl
productName
productImages {
imageUrl
imageLabel
mediaType
} }
} }
} `
`; export const productContent = /* GraphQL */ `
export const productOptions = /* GraphQL */` fragment productContent on Product {
fragment productOptions on Product { content {
options { productFullDescription
attributeFQN productShortDescription
attributeDetail { seoFriendlyUrl
name productName
} productImages {
isProductImageGroupSelector imageUrl
isRequired imageLabel
isMultiValue mediaType
values { }
value
isSelected
deltaPrice
stringValue
} }
} }
} `
`; export const productOptions = /* GraphQL */ `
export const productInfo = /* GraphQL */` fragment productOptions on Product {
fragment productInfo on Product { options {
productCode attributeFQN
productUsage attributeDetail {
name
}
isProductImageGroupSelector
isRequired
isMultiValue
values {
value
isSelected
deltaPrice
stringValue
}
}
}
`
export const productInfo = /* GraphQL */ `
fragment productInfo on Product {
productCode
productUsage
purchasableState { purchasableState {
isPurchasable isPurchasable
} }
variations { variations {
productCode, productCode
options { options {
__typename __typename
attributeFQN attributeFQN
value value
} }
} }
categories { categories {
categoryCode categoryCode
categoryId categoryId
content { content {
name name
slug slug
} }
} }
...productPrices ...productPrices
...productAttributes ...productAttributes
...productContent ...productContent
...productOptions ...productOptions
} }
${productPrices} ${productPrices}
${productAttributes} ${productAttributes}
${productContent} ${productContent}
${productOptions} ${productOptions}
`; `

View File

@ -25,6 +25,6 @@ export const productDetails = /* GraphQL */ `
} }
categories { categories {
id id
} }
} }
` `

View File

@ -1,32 +1,33 @@
import { productInfo } from './product'; import { productInfo } from './product'
export const searchFacets = /* GraphQL */` export const searchFacets = /* GraphQL */ `
fragment searchFacets on Facet { fragment searchFacets on Facet {
label label
field field
values { values {
label label
value value
isApplied isApplied
filterValue filterValue
isDisplayed isDisplayed
count count
} }
}`; }
`
export const searchResults = /* GraphQL */` export const searchResults = /* GraphQL */ `
fragment searchResults on ProductSearchResult { fragment searchResults on ProductSearchResult {
totalCount totalCount
pageSize pageSize
pageCount pageCount
startIndex startIndex
items { items {
...productInfo ...productInfo
} }
facets { facets {
...searchFacets ...searchFacets
} }
} }
${searchFacets} ${searchFacets}
${productInfo} ${productInfo}
`; `

View File

@ -15,10 +15,10 @@ export interface KiboCommerceConfig extends CommerceAPIConfig {
apiHost?: string apiHost?: string
clientId?: string clientId?: string
sharedSecret?: string sharedSecret?: string
customerCookieMaxAgeInDays: number, customerCookieMaxAgeInDays: number
currencyCode: string, currencyCode: string
documentListName: string, documentListName: string
defaultWishlistName: string, defaultWishlistName: string
authUrl?: string authUrl?: string
} }
@ -37,7 +37,7 @@ const config: KiboCommerceConfig = {
sharedSecret: process.env.KIBO_SHARED_SECRET || '', sharedSecret: process.env.KIBO_SHARED_SECRET || '',
customerCookieMaxAgeInDays: 30, customerCookieMaxAgeInDays: 30,
currencyCode: 'USD', currencyCode: 'USD',
defaultWishlistName: 'My Wishlist' defaultWishlistName: 'My Wishlist',
} }
const operations = { const operations = {
@ -55,7 +55,7 @@ export const provider = { config, operations }
export type KiboCommerceProvider = typeof provider export type KiboCommerceProvider = typeof provider
export type KiboCommerceAPI< export type KiboCommerceAPI<
P extends KiboCommerceProvider = KiboCommerceProvider P extends KiboCommerceProvider = KiboCommerceProvider
> = CommerceAPI<P | any> > = CommerceAPI<P | any>
export function getCommerceApi<P extends KiboCommerceProvider>( export function getCommerceApi<P extends KiboCommerceProvider>(
customProvider: P = provider as any customProvider: P = provider as any

View File

@ -1,5 +1,5 @@
import {productDetails} from '../fragments/productDetails' import { productDetails } from '../fragments/productDetails'
const addItemToWishlistMutation = /* GraphQL */` const addItemToWishlistMutation = /* GraphQL */ `
mutation createWishlistItem( mutation createWishlistItem(
$wishlistId: String! $wishlistId: String!
$wishlistItemInput: WishlistItemInput $wishlistItemInput: WishlistItemInput
@ -15,7 +15,7 @@ const addItemToWishlistMutation = /* GraphQL */`
} }
} }
} }
${productDetails} ${productDetails}
`; `
export default addItemToWishlistMutation; export default addItemToWishlistMutation

View File

@ -1,4 +1,4 @@
const createWishlist = /*GraphQL*/` const createWishlist = /*GraphQL*/ `
mutation createWishlist($wishlistInput:WishlistInput!) { mutation createWishlist($wishlistInput:WishlistInput!) {
createWishlist(wishlistInput:$wishlistInput){ createWishlist(wishlistInput:$wishlistInput){
id id
@ -6,6 +6,6 @@ mutation createWishlist($wishlistInput:WishlistInput!) {
customerAccountId customerAccountId
} }
} }
`; `
export default createWishlist; export default createWishlist

View File

@ -1,7 +1,6 @@
export const loginMutation = /* GraphQL */ `
export const loginMutation = /* GraphQL */` mutation login($loginInput: CustomerUserAuthInfoInput!) {
mutation login($loginInput:CustomerUserAuthInfoInput!) { account: createCustomerAuthTicket(customerUserAuthInfoInput: $loginInput) {
account:createCustomerAuthTicket(customerUserAuthInfoInput:$loginInput) {
accessToken accessToken
userId userId
refreshToken refreshToken
@ -17,4 +16,3 @@ mutation login($loginInput:CustomerUserAuthInfoInput!) {
} }
} }
` `

View File

@ -1,9 +1,9 @@
/* /*
* Delete cart based on current user session * Delete cart based on current user session
*/ */
const removeItemFromCartMutation = /*GraphQL*/` const removeItemFromCartMutation = /*GraphQL*/ `
mutation deleteCartItem($id: String!) { mutation deleteCartItem($id: String!) {
deleteCurrentCartItem(cartItemId:$id) deleteCurrentCartItem(cartItemId:$id)
}`; }`
export default removeItemFromCartMutation; export default removeItemFromCartMutation

View File

@ -1,8 +1,7 @@
const removeItemFromWishlistMutation = /* GraphQL */` const removeItemFromWishlistMutation = /* GraphQL */ `
mutation deletewishlistitem($wishlistId: String!, $wishlistItemId: String!) { mutation deletewishlistitem($wishlistId: String!, $wishlistItemId: String!) {
deleteWishlistItem(wishlistId: $wishlistId, wishlistItemId:$wishlistItemId) deleteWishlistItem(wishlistId: $wishlistId, wishlistItemId: $wishlistItemId)
} }
`; `
export default removeItemFromWishlistMutation;
export default removeItemFromWishlistMutation

View File

@ -1,41 +1,46 @@
const registerUserMutation = /* GraphQL */ `
const registerUserMutation = /* GraphQL */` mutation registerUser($customerAccountInput: CustomerAccountInput!) {
mutation registerUser($customerAccountInput: CustomerAccountInput!) { account: createCustomerAccount(
account:createCustomerAccount(customerAccountInput:$customerAccountInput) { customerAccountInput: $customerAccountInput
emailAddress ) {
userName emailAddress
firstName userName
lastName firstName
localeCode lastName
userId localeCode
id userId
isAnonymous id
attributes { isAnonymous
values attributes {
fullyQualifiedName values
} fullyQualifiedName
}
} }
}`; }
`
const registerUserLoginMutation = /* GraphQL */` const registerUserLoginMutation = /* GraphQL */ `
mutation registerUserLogin($accountId: Int!, $customerLoginInfoInput: CustomerLoginInfoInput!) { mutation registerUserLogin(
account:createCustomerAccountLogin(accountId:$accountId, customerLoginInfoInput:$customerLoginInfoInput) { $accountId: Int!
$customerLoginInfoInput: CustomerLoginInfoInput!
) {
account: createCustomerAccountLogin(
accountId: $accountId
customerLoginInfoInput: $customerLoginInfoInput
) {
accessToken accessToken
accessTokenExpiration accessTokenExpiration
refreshToken refreshToken
refreshTokenExpiration refreshTokenExpiration
userId userId
customerAccount { customerAccount {
id id
emailAddress emailAddress
firstName firstName
userName userName
} }
}
} }
}`; `
export {
registerUserMutation,
registerUserLoginMutation
};
export { registerUserMutation, registerUserLoginMutation }

View File

@ -1,9 +1,9 @@
const updateCartItemQuantityMutation = /*GraphQL*/` const updateCartItemQuantityMutation = /*GraphQL*/ `
mutation updateCartItemQuantity($itemId:String!, $quantity: Int!){ mutation updateCartItemQuantity($itemId:String!, $quantity: Int!){
updateCurrentCartItemQuantity(cartItemId:$itemId, quantity:$quantity){ updateCurrentCartItemQuantity(cartItemId:$itemId, quantity:$quantity){
id id
quantity quantity
} }
}`; }`
export default updateCartItemQuantityMutation; export default updateCartItemQuantityMutation

View File

@ -1,17 +1,14 @@
import type { OperationContext } from '@vercel/commerce/api/operations' import type { OperationContext } from '@vercel/commerce/api/operations'
import type { KiboCommerceConfig } from '../index' import type { KiboCommerceConfig } from '../index'
import { getAllPagesQuery } from '../queries/get-all-pages-query' import { getAllPagesQuery } from '../queries/get-all-pages-query'
import { GetPagesQueryParams } from "../../types/page"; import { GetPagesQueryParams } from '../../types/page'
import { normalizePage } from '../../lib/normalize' import { normalizePage } from '../../lib/normalize'
export type GetAllPagesResult< export type GetAllPagesResult<T extends { pages: any[] } = { pages: any[] }> = T
T extends { pages: any[] } = { pages: any[] }
> = T
export default function getAllPagesOperation({ export default function getAllPagesOperation({
commerce, commerce,
}: OperationContext<any>) { }: OperationContext<any>) {
async function getAllPages({ async function getAllPages({
query = getAllPagesQuery, query = getAllPagesQuery,
config, config,
@ -25,11 +22,11 @@ export default function getAllPagesOperation({
} = {}): Promise<GetAllPagesResult> { } = {}): Promise<GetAllPagesResult> {
const cfg = commerce.getConfig(config) const cfg = commerce.getConfig(config)
variables = { variables = {
documentListName: cfg.documentListName documentListName: cfg.documentListName,
} }
const { data } = await cfg.fetch(query, { variables }); const { data } = await cfg.fetch(query, { variables })
const pages = data.documentListDocuments.items.map(normalizePage); const pages = data.documentListDocuments.items.map(normalizePage)
return { pages } return { pages }
} }

View File

@ -1,24 +1,31 @@
import { KiboCommerceConfig } from '../index' import { KiboCommerceConfig } from '../index'
import { getAllProductsQuery } from '../queries/get-all-products-query'; import { getAllProductsQuery } from '../queries/get-all-products-query'
import { normalizeProduct } from '../../lib/normalize' import { normalizeProduct } from '../../lib/normalize'
export type GetAllProductPathsResult = { export type GetAllProductPathsResult = {
products: Array<{ path: string }> products: Array<{ path: string }>
} }
export default function getAllProductPathsOperation({commerce,}: any) { export default function getAllProductPathsOperation({ commerce }: any) {
async function getAllProductPaths({ config }: {config?: KiboCommerceConfig } = {}): Promise<GetAllProductPathsResult> { async function getAllProductPaths({
config,
}: { config?: KiboCommerceConfig } = {}): Promise<GetAllProductPathsResult> {
const cfg = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const productVariables = {startIndex: 0, pageSize: 100}; const productVariables = { startIndex: 0, pageSize: 100 }
const { data } = await cfg.fetch(getAllProductsQuery, { variables: productVariables }); const { data } = await cfg.fetch(getAllProductsQuery, {
variables: productVariables,
})
const normalizedProducts = data.products.items ? data.products.items.map( (item:any) => normalizeProduct(item, cfg)) : []; const normalizedProducts = data.products.items
const products = normalizedProducts.map((product: any) => ({ path: product.path })) ? data.products.items.map((item: any) => normalizeProduct(item, cfg))
: []
const products = normalizedProducts.map((product: any) => ({
path: product.path,
}))
return Promise.resolve({ return Promise.resolve({
products: products products: products,
}) })
} }

View File

@ -2,7 +2,7 @@ import { Product } from '@vercel/commerce/types/product'
import { GetAllProductsOperation } from '@vercel/commerce/types/product' import { GetAllProductsOperation } from '@vercel/commerce/types/product'
import type { OperationContext } from '@vercel/commerce/api/operations' import type { OperationContext } from '@vercel/commerce/api/operations'
import type { KiboCommerceConfig } from '../index' import type { KiboCommerceConfig } from '../index'
import { getAllProductsQuery } from '../queries/get-all-products-query'; import { getAllProductsQuery } from '../queries/get-all-products-query'
import { normalizeProduct } from '../../lib/normalize' import { normalizeProduct } from '../../lib/normalize'
export default function getAllProductsOperation({ export default function getAllProductsOperation({
@ -18,11 +18,12 @@ export default function getAllProductsOperation({
config?: Partial<KiboCommerceConfig> config?: Partial<KiboCommerceConfig>
preview?: boolean preview?: boolean
} = {}): Promise<{ products: Product[] | any[] }> { } = {}): Promise<{ products: Product[] | any[] }> {
const cfg = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const { data } = await cfg.fetch(query); const { data } = await cfg.fetch(query)
let normalizedProducts = data.products.items ? data.products.items.map( (item:any) => normalizeProduct(item, cfg)) : []; let normalizedProducts = data.products.items
? data.products.items.map((item: any) => normalizeProduct(item, cfg))
: []
return { return {
products: normalizedProducts, products: normalizedProducts,

View File

@ -9,7 +9,7 @@ import type {
// import type { RecursivePartial, RecursiveRequired } from '../utils/types' // import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import { KiboCommerceConfig } from '..' import { KiboCommerceConfig } from '..'
// import getAllProducts, { ProductEdge } from './get-all-products' // import getAllProducts, { ProductEdge } from './get-all-products'
import {getCustomerWishlistQuery} from '../queries/get-customer-wishlist-query' import { getCustomerWishlistQuery } from '../queries/get-customer-wishlist-query'
export default function getCustomerWishlistOperation({ export default function getCustomerWishlistOperation({
commerce, commerce,
@ -40,14 +40,15 @@ export default function getCustomerWishlistOperation({
config?: KiboCommerceConfig config?: KiboCommerceConfig
includeProducts?: boolean includeProducts?: boolean
}): Promise<T['data']> { }): Promise<T['data']> {
let customerWishlist ={} let customerWishlist = {}
try { try {
config = commerce.getConfig(config) config = commerce.getConfig(config)
const result= await config?.fetch(getCustomerWishlistQuery,{variables}) const result = await config?.fetch(getCustomerWishlistQuery, {
customerWishlist= result?.data?.customerWishlist; variables,
} catch(e) { })
customerWishlist= {} customerWishlist = result?.data?.customerWishlist
} catch (e) {
customerWishlist = {}
} }
return { wishlist: customerWishlist as any } return { wishlist: customerWishlist as any }

View File

@ -1,15 +1,11 @@
import type { import type { OperationContext } from '@vercel/commerce/api/operations'
OperationContext,
} from '@vercel/commerce/api/operations'
import type { KiboCommerceConfig, KiboCommerceProvider } from '..' import type { KiboCommerceConfig, KiboCommerceProvider } from '..'
import { normalizePage } from '../../lib/normalize' import { normalizePage } from '../../lib/normalize'
import { getPageQuery } from '../queries/get-page-query' import { getPageQuery } from '../queries/get-page-query'
import type { Page, GetPageQueryParams } from "../../types/page"; import type { Page, GetPageQueryParams } from '../../types/page'
import type { Document } from '../../../schema' import type { Document } from '../../../schema'
export default function getPageOperation({ export default function getPageOperation({ commerce }: OperationContext<any>) {
commerce,
}: OperationContext<any>) {
async function getPage<T extends Page>({ async function getPage<T extends Page>({
url, url,
variables, variables,
@ -24,11 +20,14 @@ export default function getPageOperation({
// RecursivePartial forces the method to check for every prop in the data, which is // RecursivePartial forces the method to check for every prop in the data, which is
// required in case there's a custom `url` // required in case there's a custom `url`
const cfg = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const pageVariables = { documentListName: cfg.documentListName, filter: `id eq ${variables.id}` } const pageVariables = {
documentListName: cfg.documentListName,
filter: `id eq ${variables.id}`,
}
const { data } = await cfg.fetch(getPageQuery, { variables: pageVariables }) const { data } = await cfg.fetch(getPageQuery, { variables: pageVariables })
const firstPage = data.documentListDocuments.items?.[0]; const firstPage = data.documentListDocuments.items?.[0]
const page = firstPage as Document const page = firstPage as Document
if (preview || page?.properties?.is_visible) { if (preview || page?.properties?.is_visible) {
return { page: normalizePage(page as any) } return { page: normalizePage(page as any) }

View File

@ -8,7 +8,6 @@ import { normalizeProduct } from '../../lib/normalize'
export default function getProductOperation({ export default function getProductOperation({
commerce, commerce,
}: OperationContext<any>) { }: OperationContext<any>) {
async function getProduct<T extends GetProductOperation>({ async function getProduct<T extends GetProductOperation>({
query = getProductQuery, query = getProductQuery,
variables, variables,
@ -19,15 +18,15 @@ export default function getProductOperation({
config?: Partial<KiboCommerceConfig> config?: Partial<KiboCommerceConfig>
preview?: boolean preview?: boolean
} = {}): Promise<Product | {} | any> { } = {}): Promise<Product | {} | any> {
const productVariables = { productCode: variables?.slug} const productVariables = { productCode: variables?.slug }
const cfg = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const { data } = await cfg.fetch(query, { variables: productVariables }); const { data } = await cfg.fetch(query, { variables: productVariables })
const normalizedProduct = normalizeProduct(data.product, cfg) const normalizedProduct = normalizeProduct(data.product, cfg)
return { return {
product: normalizedProduct product: normalizedProduct,
} }
} }

View File

@ -1,7 +1,7 @@
import { OperationContext } from '@vercel/commerce/api/operations' import { OperationContext } from '@vercel/commerce/api/operations'
import { Category } from '@vercel/commerce/types/site' import { Category } from '@vercel/commerce/types/site'
import { KiboCommerceConfig } from '../index' import { KiboCommerceConfig } from '../index'
import {categoryTreeQuery} from '../queries/get-categories-tree-query' import { categoryTreeQuery } from '../queries/get-categories-tree-query'
import { normalizeCategory } from '../../lib/normalize' import { normalizeCategory } from '../../lib/normalize'
export type GetSiteInfoResult< export type GetSiteInfoResult<
@ -11,9 +11,11 @@ export type GetSiteInfoResult<
} }
> = T > = T
export default function getSiteInfoOperation({commerce}: OperationContext<any>) { export default function getSiteInfoOperation({
commerce,
}: OperationContext<any>) {
async function getSiteInfo({ async function getSiteInfo({
query= categoryTreeQuery, query = categoryTreeQuery,
variables, variables,
config, config,
}: { }: {
@ -23,8 +25,8 @@ export default function getSiteInfoOperation({commerce}: OperationContext<any>)
preview?: boolean preview?: boolean
} = {}): Promise<GetSiteInfoResult> { } = {}): Promise<GetSiteInfoResult> {
const cfg = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const { data } = await cfg.fetch(query); const { data } = await cfg.fetch(query)
const categories= data.categories.items.map(normalizeCategory); const categories = data.categories.items.map(normalizeCategory)
return Promise.resolve({ return Promise.resolve({
categories: categories ?? [], categories: categories ?? [],
brands: [], brands: [],

View File

@ -1,6 +1,6 @@
export const getAllPagesQuery = /* GraphQL */` export const getAllPagesQuery = /* GraphQL */ `
query($documentListName: String!) { query ($documentListName: String!) {
documentListDocuments(documentListName:$documentListName){ documentListDocuments(documentListName: $documentListName) {
items { items {
id id
name name
@ -8,4 +8,5 @@ query($documentListName: String!) {
properties properties
} }
} }
}`; }
`

View File

@ -1,21 +1,13 @@
import { productInfo } from '../fragments/product'; import { productInfo } from '../fragments/product'
export const getAllProductsQuery = /* GraphQL */` export const getAllProductsQuery = /* GraphQL */ `
${productInfo} ${productInfo}
query products( query products($filter: String, $startIndex: Int, $pageSize: Int) {
$filter: String products(filter: $filter, startIndex: $startIndex, pageSize: $pageSize) {
$startIndex: Int items {
$pageSize: Int ...productInfo
) { }
products(
filter: $filter
startIndex: $startIndex
pageSize: $pageSize
) {
items {
...productInfo
} }
} }
} `
`

View File

@ -1,6 +1,6 @@
import { productDetails } from '../fragments/productDetails' import { productDetails } from '../fragments/productDetails'
export const getCartQuery = /* GraphQL */` export const getCartQuery = /* GraphQL */ `
query cart { query cart {
currentCart { currentCart {
id id
userId userId
@ -8,7 +8,7 @@ query cart {
impact impact
discount { discount {
id id
name name
} }
couponCode couponCode
} }
@ -18,15 +18,15 @@ query cart {
items { items {
id id
subtotal subtotal
unitPrice{ unitPrice {
extendedAmount extendedAmount
} }
product { product {
...productDetails ...productDetails
}
quantity
} }
quantity
} }
} }
${productDetails} }
${productDetails}
` `

View File

@ -1,29 +1,30 @@
import { CategoryInfo } from '../fragments/category' import { CategoryInfo } from '../fragments/category'
export const categoryTreeQuery = /* GraphQL */` export const categoryTreeQuery = /* GraphQL */ `
query GetCategoryTree { query GetCategoryTree {
categories: categoriesTree { categories: categoriesTree {
items { items {
...categoryInfo ...categoryInfo
childrenCategories { childrenCategories {
...categoryInfo
childrenCategories {
...categoryInfo ...categoryInfo
childrenCategories { childrenCategories {
...categoryInfo
childrenCategories {
...categoryInfo ...categoryInfo
childrenCategories { childrenCategories {
...categoryInfo
childrenCategories {
...categoryInfo ...categoryInfo
childrenCategories { }
...categoryInfo
childrenCategories {
...categoryInfo
childrenCategories {
...categoryInfo
}
}
}
} }
}
} }
}
} }
} }
} }
} }
${CategoryInfo}`; ${CategoryInfo}
`

View File

@ -1,12 +1,12 @@
export const getCustomerAccountQuery = /* GraphQL */` export const getCustomerAccountQuery = /* GraphQL */ `
query getUser { query getUser {
customerAccount:getCurrentAccount { customerAccount: getCurrentAccount {
id id
firstName firstName
lastName lastName
emailAddress emailAddress
userName userName
isAnonymous isAnonymous
} }
} }
` `

View File

@ -1,25 +1,28 @@
import {productDetails} from '../fragments/productDetails' import { productDetails } from '../fragments/productDetails'
export const getCustomerWishlistQuery= /* GraphQL */` export const getCustomerWishlistQuery = /* GraphQL */ `
query wishlist($customerId: Int!, $wishlistName: String!) { query wishlist($customerId: Int!, $wishlistName: String!) {
customerWishlist(customerAccountId:$customerId ,wishlistName: $wishlistName){ customerWishlist(
customerAccountId: $customerId
wishlistName: $wishlistName
) {
customerAccountId customerAccountId
name name
id id
userId userId
items { items {
id id
quantity quantity
total total
subtotal subtotal
unitPrice{ unitPrice {
extendedAmount extendedAmount
} }
quantity quantity
product { product {
...productDetails ...productDetails
}
} }
}
} }
} }
${productDetails} ${productDetails}
` `

View File

@ -1,14 +1,17 @@
export const getPageQuery = /* GraphQL */` export const getPageQuery = /* GraphQL */ `
query($documentListName: String!, $filter: String!) { query ($documentListName: String!, $filter: String!) {
documentListDocuments(documentListName: $documentListName, filter: $filter){ documentListDocuments(
startIndex documentListName: $documentListName
totalCount filter: $filter
items { ) {
id startIndex
name totalCount
listFQN items {
properties id
} name
} listFQN
properties
}
} }
`; }
`

View File

@ -1,15 +1,11 @@
import { productInfo } from '../fragments/product'; import { productInfo } from '../fragments/product'
export const getProductQuery = /* GraphQL */` export const getProductQuery = /* GraphQL */ `
${productInfo} ${productInfo}
query product( query product($productCode: String!) {
$productCode: String! product(productCode: $productCode) {
) {
product(
productCode: $productCode
) {
...productInfo ...productInfo
} }
} }
` `

View File

@ -1,20 +1,27 @@
import { searchResults } from '../fragments/search' import { searchResults } from '../fragments/search'
const query = /* GraphQL */` const query = /* GraphQL */ `
query ProductSearch($query:String, $startIndex:Int, query ProductSearch(
$pageSize:Int, $sortBy:String, $filter:String,$facetTemplate:String,$facetValueFilter:String ) { $query: String
products:productSearch ( $startIndex: Int
query:$query, $pageSize: Int
startIndex: $startIndex, $sortBy: String
pageSize:$pageSize, $filter: String
sortBy: $sortBy, $facetTemplate: String
filter:$filter, $facetValueFilter: String
facetTemplate:$facetTemplate, ) {
facetValueFilter:$facetValueFilter products: productSearch(
) { query: $query
...searchResults startIndex: $startIndex
} pageSize: $pageSize
sortBy: $sortBy
filter: $filter
facetTemplate: $facetTemplate
facetValueFilter: $facetValueFilter
) {
...searchResults
} }
${searchResults} }
`; ${searchResults}
export default query; `
export default query

View File

@ -41,8 +41,8 @@ export class APIAuthenticationHelper {
this._clientId = clientId this._clientId = clientId
this._sharedSecret = sharedSecret this._sharedSecret = sharedSecret
this._authUrl = authUrl this._authUrl = authUrl
if(!authTicketCache) { if (!authTicketCache) {
this._authTicketCache = new RuntimeMemCache(); this._authTicketCache = new RuntimeMemCache()
} }
} }
private _buildFetchOptions(body: any = {}): FetchOptions { private _buildFetchOptions(body: any = {}): FetchOptions {

View File

@ -6,8 +6,8 @@ import { NextApiRequest } from 'next'
import getAnonymousShopperToken from './get-anonymous-shopper-token' import getAnonymousShopperToken from './get-anonymous-shopper-token'
const parseCookie = (cookieValue?: any) => { const parseCookie = (cookieValue?: any) => {
return cookieValue return cookieValue
? JSON.parse(Buffer.from(cookieValue, 'base64').toString('ascii')) ? JSON.parse(Buffer.from(cookieValue, 'base64').toString('ascii'))
: null : null
} }
export default class CookieHandler { export default class CookieHandler {
@ -37,8 +37,8 @@ export default class CookieHandler {
isShopperCookieAnonymous() { isShopperCookieAnonymous() {
const customerCookieKey = this.config.customerCookie const customerCookieKey = this.config.customerCookie
const shopperCookie = this.request.cookies[customerCookieKey] const shopperCookie = this.request.cookies[customerCookieKey]
const shopperSession = parseCookie(shopperCookie); const shopperSession = parseCookie(shopperCookie)
const isAnonymous = shopperSession?.customerAccount ? false : true const isAnonymous = shopperSession?.customerAccount ? false : true
return isAnonymous return isAnonymous
} }
setAnonymousShopperCookie(anonymousShopperTokenResponse: any) { setAnonymousShopperCookie(anonymousShopperTokenResponse: any) {

View File

@ -2,42 +2,46 @@ import { FetcherError } from '@vercel/commerce/utils/errors'
import type { GraphQLFetcher } from '@vercel/commerce/api' import type { GraphQLFetcher } from '@vercel/commerce/api'
import type { KiboCommerceConfig } from '../index' import type { KiboCommerceConfig } from '../index'
import fetch from './fetch' import fetch from './fetch'
import { APIAuthenticationHelper } from './api-auth-helper'; import { APIAuthenticationHelper } from './api-auth-helper'
const fetchGraphqlApi: ( const fetchGraphqlApi: (
getConfig: () => KiboCommerceConfig getConfig: () => KiboCommerceConfig
) => GraphQLFetcher = (getConfig) => async ( ) => GraphQLFetcher =
query: string, (getConfig) =>
{ variables, preview } = {}, async (query: string, { variables, preview } = {}, fetchOptions) => {
fetchOptions const config = getConfig()
) => { const authHelper = new APIAuthenticationHelper(config)
const config = getConfig() const apiToken = await authHelper.getAccessToken()
const authHelper = new APIAuthenticationHelper(config); const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
const apiToken = await authHelper.getAccessToken(); ...fetchOptions,
const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { method: 'POST',
...fetchOptions, headers: {
method: 'POST', ...fetchOptions?.headers,
headers: { Authorization: `Bearer ${apiToken}`,
...fetchOptions?.headers, 'Content-Type': 'application/json',
Authorization: `Bearer ${apiToken}`, },
'Content-Type': 'application/json', body: JSON.stringify({
}, query,
body: JSON.stringify({ variables,
query, }),
variables,
}),
})
const json = await res.json()
if (json.errors) {
console.warn(`Kibo API Request Correlation ID: ${res.headers.get('x-vol-correlation')}`);
throw new FetcherError({
errors: json.errors ?? [{ message: 'Failed to fetch KiboCommerce API' }],
status: res.status,
}) })
const json = await res.json()
if (json.errors) {
console.warn(
`Kibo API Request Correlation ID: ${res.headers.get(
'x-vol-correlation'
)}`
)
throw new FetcherError({
errors: json.errors ?? [
{ message: 'Failed to fetch KiboCommerce API' },
],
status: res.status,
})
}
return { data: json.data, res }
} }
return { data: json.data, res }
}
export default fetchGraphqlApi export default fetchGraphqlApi

View File

@ -3,12 +3,14 @@ import type { GraphQLFetcher } from '@vercel/commerce/api'
import type { KiboCommerceConfig } from '../index' import type { KiboCommerceConfig } from '../index'
import fetch from './fetch' import fetch from './fetch'
const fetchGraphqlApi: (getConfig: () => KiboCommerceConfig) => GraphQLFetcher = const fetchGraphqlApi: (
getConfig: () => KiboCommerceConfig
) => GraphQLFetcher =
(getConfig) => (getConfig) =>
async (query: string, { variables, preview } = {}, fetchOptions) => { async (query: string, { variables, preview } = {}, fetchOptions) => {
const config = getConfig() const config = getConfig()
const res = await fetch(config.commerceUrl, { const res = await fetch(config.commerceUrl, {
//const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { //const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
...fetchOptions, ...fetchOptions,
method: 'POST', method: 'POST',
headers: { headers: {
@ -25,7 +27,9 @@ const fetchGraphqlApi: (getConfig: () => KiboCommerceConfig) => GraphQLFetcher =
const json = await res.json() const json = await res.json()
if (json.errors) { if (json.errors) {
throw new FetcherError({ throw new FetcherError({
errors: json.errors ?? [{ message: 'Failed to fetch KiboCommerce API' }], errors: json.errors ?? [
{ message: 'Failed to fetch KiboCommerce API' },
],
status: res.status, status: res.status,
}) })
} }

View File

@ -8,17 +8,15 @@ async function getCustomerId({
customerToken: string customerToken: string
config: KiboCommerceConfig config: KiboCommerceConfig
}): Promise<string | undefined> { }): Promise<string | undefined> {
const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null; const token = customerToken
const accessToken = token ? JSON.parse(token).accessToken : null; ? Buffer.from(customerToken, 'base64').toString('ascii')
const { data } = await config.fetch( : null
getCustomerAccountQuery, const accessToken = token ? JSON.parse(token).accessToken : null
undefined, const { data } = await config.fetch(getCustomerAccountQuery, undefined, {
{ headers: {
headers: { 'x-vol-user-claims': accessToken,
'x-vol-user-claims': accessToken, },
}, })
}
)
return data?.customerAccount?.id return data?.customerAccount?.id
} }

View File

@ -12,18 +12,20 @@ export const handler: MutationHook<LogoutHook> = {
url: '/api/logout', url: '/api/logout',
method: 'GET', method: 'GET',
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCustomer() ({ fetch }) =>
const { mutate: mutateCart } = useCart() () => {
const { mutate } = useCustomer()
const { mutate: mutateCart } = useCart()
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)
await mutateCart(null, false) await mutateCart(null, false)
return data return data
}, },
[fetch, mutate, mutateCart] [fetch, mutate, mutateCart]
) )
}, },
} }

View File

@ -29,16 +29,18 @@ export const handler: MutationHook<AddItemHook> = {
return data return data
}, },
useHook: ({ fetch }) => () => { useHook:
const { mutate } = useCart() ({ fetch }) =>
() => {
const { mutate } = useCart()
return useCallback( return useCallback(
async function addItem(input) { async function addItem(input) {
const data = await fetch({ input }) const data = await fetch({ input })
await mutate(data, false) await mutate(data, false)
return data return data
}, },
[fetch, mutate] [fetch, mutate]
) )
}, },
} }

View File

@ -12,22 +12,24 @@ export const handler: SWRHook<any> = {
async fetcher({ options, fetch }) { async fetcher({ options, fetch }) {
return await fetch({ ...options }) return await fetch({ ...options })
}, },
useHook: ({ useData }) => (input) => { useHook:
const response = useData({ ({ useData }) =>
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, (input) => {
}) const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo( return useMemo(
() => () =>
Object.create(response, { Object.create(response, {
isEmpty: { isEmpty: {
get() { get() {
return (response.data?.lineItems.length ?? 0) <= 0 return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
}, },
enumerable: true, }),
}, [response]
}), )
[response] },
)
},
} }

View File

@ -4,8 +4,14 @@ import type {
HookFetcherContext, HookFetcherContext,
} from '@vercel/commerce/utils/types' } from '@vercel/commerce/utils/types'
import { ValidationError } from '@vercel/commerce/utils/errors' import { ValidationError } from '@vercel/commerce/utils/errors'
import useRemoveItem, { UseRemoveItem } from '@vercel/commerce/cart/use-remove-item' import useRemoveItem, {
import type { Cart, LineItem, RemoveItemHook } from '@vercel/commerce/types/cart' UseRemoveItem,
} from '@vercel/commerce/cart/use-remove-item'
import type {
Cart,
LineItem,
RemoveItemHook,
} from '@vercel/commerce/types/cart'
import useCart from './use-cart' import useCart from './use-cart'
export type RemoveItemFn<T = any> = T extends LineItem export type RemoveItemFn<T = any> = T extends LineItem
@ -30,27 +36,25 @@ export const handler = {
}: HookFetcherContext<RemoveItemHook>) { }: HookFetcherContext<RemoveItemHook>) {
return await fetch({ ...options, body: { itemId } }) return await fetch({ ...options, body: { itemId } })
}, },
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<RemoveItemHook>) =>
>( <T extends LineItem | undefined = undefined>(ctx: { item?: T } = {}) => {
ctx: { item?: T } = {} const { item } = ctx
) => { const { mutate } = useCart()
const { item } = ctx const removeItem: RemoveItemFn<LineItem> = async (input) => {
const { mutate } = useCart() const itemId = input?.id ?? item?.id
const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id
if (!itemId) { if (!itemId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
}) })
}
const data = await fetch({ input: { itemId } })
await mutate(data, false)
return data
} }
const data = await fetch({ input: { itemId } }) return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
await mutate(data, false) },
return data
}
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
},
} }

View File

@ -5,7 +5,9 @@ import type {
HookFetcherContext, HookFetcherContext,
} from '@vercel/commerce/utils/types' } from '@vercel/commerce/utils/types'
import { ValidationError } from '@vercel/commerce/utils/errors' import { ValidationError } from '@vercel/commerce/utils/errors'
import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item' import useUpdateItem, {
UseUpdateItem,
} from '@vercel/commerce/cart/use-update-item'
import type { LineItem, UpdateItemHook } from '@vercel/commerce/types/cart' import type { LineItem, UpdateItemHook } from '@vercel/commerce/types/cart'
import { handler as removeItemHandler } from './use-remove-item' import { handler as removeItemHandler } from './use-remove-item'
import useCart from './use-cart' import useCart from './use-cart'
@ -46,39 +48,39 @@ export const handler = {
body: { itemId, item }, body: { itemId, item },
}) })
}, },
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<UpdateItemHook>) =>
>( <T extends LineItem | undefined = undefined>(
ctx: { ctx: {
item?: T item?: T
wait?: number wait?: number
} = {} } = {}
) => { ) => {
const { item } = ctx const { item } = ctx
const { mutate } = useCart() as any const { mutate } = useCart() as any
return useCallback( return useCallback(
debounce(async (input: UpdateItemActionInput<T>) => { debounce(async (input: UpdateItemActionInput<T>) => {
const itemId = input.id ?? item?.id const itemId = input.id ?? item?.id
const productId = input.productId ?? item?.productId const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId const variantId = input.productId ?? item?.variantId
if (!itemId || !productId || !variantId) { if (!itemId || !productId || !variantId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
})
}
const data = await fetch({
input: {
itemId,
item: { productId, variantId, quantity: input.quantity },
},
}) })
} await mutate(data, false)
return data
const data = await fetch({ }, ctx.wait ?? 500),
input: { [fetch, mutate]
itemId, )
item: { productId, variantId, quantity: input.quantity }, },
},
})
await mutate(data, false)
return data
}, ctx.wait ?? 500),
[fetch, mutate]
)
},
} }

View File

@ -1,5 +1,7 @@
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCheckout, { UseCheckout } from '@vercel/commerce/checkout/use-checkout' import useCheckout, {
UseCheckout,
} from '@vercel/commerce/checkout/use-checkout'
export default useCheckout as UseCheckout<typeof handler> export default useCheckout as UseCheckout<typeof handler>

View File

@ -6,4 +6,4 @@
"search": true, "search": true,
"customerAuth": true "customerAuth": true
} }
} }

View File

@ -1,4 +1,6 @@
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/address/use-add-item' import useAddItem, {
UseAddItem,
} from '@vercel/commerce/customer/address/use-add-item'
import { MutationHook } from '@vercel/commerce/utils/types' import { MutationHook } from '@vercel/commerce/utils/types'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>

View File

@ -1,4 +1,6 @@
import useAddItem, { UseAddItem } from '@vercel/commerce/customer/card/use-add-item' import useAddItem, {
UseAddItem,
} from '@vercel/commerce/customer/card/use-add-item'
import { MutationHook } from '@vercel/commerce/utils/types' import { MutationHook } from '@vercel/commerce/utils/types'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>

View File

@ -1,5 +1,7 @@
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCustomer, { UseCustomer } from '@vercel/commerce/customer/use-customer' import useCustomer, {
UseCustomer,
} from '@vercel/commerce/customer/use-customer'
import type { CustomerHook } from '../types/customer' import type { CustomerHook } from '../types/customer'
export default useCustomer as UseCustomer<typeof handler> export default useCustomer as UseCustomer<typeof handler>
@ -13,12 +15,14 @@ export const handler: SWRHook<CustomerHook> = {
const data = await fetch(options) const data = await fetch(options)
return data?.customer ?? null return data?.customer ?? null
}, },
useHook: ({ useData }) => (input) => { useHook:
return useData({ ({ useData }) =>
swrOptions: { (input) => {
revalidateOnFocus: false, return useData({
...input?.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input?.swrOptions,
}, },
})
},
} }

View File

@ -1,4 +1,7 @@
import { getCommerceProvider, useCommerce as useCoreCommerce } from '@vercel/commerce' import {
getCommerceProvider,
useCommerce as useCoreCommerce,
} from '@vercel/commerce'
import { kiboCommerceProvider, KibocommerceProvider } from './provider' import { kiboCommerceProvider, KibocommerceProvider } from './provider'
export { kiboCommerceProvider } export { kiboCommerceProvider }

View File

@ -1,8 +1,10 @@
export function getCookieExpirationDate(maxAgeInDays: number){ export function getCookieExpirationDate(maxAgeInDays: number) {
const today = new Date(); const today = new Date()
const expirationDate = new Date(); const expirationDate = new Date()
const cookieExpirationDate = new Date ( expirationDate.setDate(today.getDate() + maxAgeInDays) )
return cookieExpirationDate; const cookieExpirationDate = new Date(
} expirationDate.setDate(today.getDate() + maxAgeInDays)
)
return cookieExpirationDate
}

View File

@ -1,5 +1,5 @@
import type { PrCategory, CustomerAccountInput, Document } from '../../schema' import type { PrCategory, CustomerAccountInput, Document } from '../../schema'
import { Page } from '../types/page'; import { Page } from '../types/page'
import { Customer } from '../types/customer' import { Customer } from '../types/customer'
export function normalizeProduct(productNode: any, config: any): any { export function normalizeProduct(productNode: any, config: any): any {
@ -57,7 +57,7 @@ export function normalizePage(page: Document): Page {
url: page.properties.url, url: page.properties.url,
body: page.properties.body, body: page.properties.body,
is_visible: page.properties.is_visible, is_visible: page.properties.is_visible,
sort_order: page.properties.sort_order sort_order: page.properties.sort_order,
} }
} }
@ -91,7 +91,7 @@ export function normalizeCustomer(customer: CustomerAccountInput): Customer {
lastName: customer.lastName, lastName: customer.lastName,
email: customer.emailAddress, email: customer.emailAddress,
userName: customer.userName, userName: customer.userName,
isAnonymous: customer.isAnonymous isAnonymous: customer.isAnonymous,
} }
} }
@ -133,7 +133,7 @@ export function normalizeCategory(category: PrCategory): any {
export function normalizeWishlistItem( export function normalizeWishlistItem(
item: any, item: any,
config: any, config: any,
includeProducts=false includeProducts = false
): any { ): any {
if (includeProducts) { if (includeProducts) {
return { return {

View File

@ -1,15 +1,19 @@
export function prepareSetCookie(name: string, value: string, options: any = {}): string { export function prepareSetCookie(
name: string,
value: string,
options: any = {}
): string {
const encodedValue = Buffer.from(value).toString('base64') const encodedValue = Buffer.from(value).toString('base64')
const cookieValue = [`${name}=${encodedValue}`]; const cookieValue = [`${name}=${encodedValue}`]
if (options.maxAge) { if (options.maxAge) {
cookieValue.push(`Max-Age=${options.maxAge}`); cookieValue.push(`Max-Age=${options.maxAge}`)
} }
if (options.expires && !options.maxAge) { if (options.expires && !options.maxAge) {
cookieValue.push(`Expires=${options.expires.toUTCString()}`); cookieValue.push(`Expires=${options.expires.toUTCString()}`)
} }
const cookie = cookieValue.join('; ') const cookie = cookieValue.join('; ')
return cookie return cookie
} }

View File

@ -1,9 +1,9 @@
function getFacetValueFilter(categoryCode: string, filters = []) { function getFacetValueFilter(categoryCode: string, filters = []) {
let facetValueFilter = ''; let facetValueFilter = ''
if (categoryCode) { if (categoryCode) {
facetValueFilter = `categoryCode:${categoryCode},`; facetValueFilter = `categoryCode:${categoryCode},`
} }
return facetValueFilter + filters.join(','); return facetValueFilter + filters.join(',')
} }
export const buildProductSearchVars = ({ export const buildProductSearchVars = ({
@ -14,33 +14,38 @@ export const buildProductSearchVars = ({
sort = '', sort = '',
search = '', search = '',
}) => { }) => {
let facetTemplate = ''; let facetTemplate = ''
let filter = ''; let filter = ''
let sortBy; let sortBy
if (categoryCode) { if (categoryCode) {
facetTemplate = `categoryCode:${categoryCode}`; facetTemplate = `categoryCode:${categoryCode}`
filter = `categoryCode req ${categoryCode}`; filter = `categoryCode req ${categoryCode}`
} }
const facetFilterList = Object.keys(filters).filter(k => filters[k].length).reduce((accum, k): any => { const facetFilterList = Object.keys(filters)
return [...accum, ...filters[k].map((facetValue: any) => `Tenant~${k}:${facetValue}`)]; .filter((k) => filters[k].length)
}, []); .reduce((accum, k): any => {
return [
...accum,
...filters[k].map((facetValue: any) => `Tenant~${k}:${facetValue}`),
]
}, [])
const facetValueFilter = getFacetValueFilter(categoryCode, facetFilterList); const facetValueFilter = getFacetValueFilter(categoryCode, facetFilterList)
switch(sort) { switch (sort) {
case 'latest-desc': case 'latest-desc':
sortBy= 'createDate desc'; sortBy = 'createDate desc'
break; break
case 'price-asc': case 'price-asc':
sortBy= 'price asc'; sortBy = 'price asc'
break; break
case 'price-desc': case 'price-desc':
sortBy= 'price desc'; sortBy = 'price desc'
break; break
case 'trending-desc': case 'trending-desc':
default: default:
sortBy= ''; sortBy = ''
break; break
} }
return { return {
@ -50,6 +55,6 @@ export const buildProductSearchVars = ({
sortBy, sortBy,
filter: filter, filter: filter,
facetTemplate, facetTemplate,
facetValueFilter facetValueFilter,
} }
} }

View File

@ -1,3 +1,3 @@
export function setCookies(res: any, cookies: string[]): void { export function setCookies(res: any, cookies: string[]): void {
res.setHeader('Set-Cookie', cookies); res.setHeader('Set-Cookie', cookies)
} }

View File

@ -4,9 +4,13 @@ module.exports = {
commerce, commerce,
serverRuntimeConfig: { serverRuntimeConfig: {
// Will only be available on the server side // Will only be available on the server side
kiboAuthTicket: null kiboAuthTicket: null,
}, },
images: { images: {
domains: ['d1slj7rdbjyb5l.cloudfront.net', 'cdn-tp1.mozu.com', 'cdn-sb.mozu.com'], domains: [
'd1slj7rdbjyb5l.cloudfront.net',
'cdn-tp1.mozu.com',
'cdn-sb.mozu.com',
],
}, },
} }

View File

@ -23,15 +23,17 @@ export const handler: SWRHook<any> = {
method: options.method, method: options.method,
}) })
}, },
useHook: ({ useData }) => (input) => { useHook:
return useData({ ({ useData }) =>
input: [ (input) => {
['search', input.search], return useData({
['categoryId', input.categoryId], input: [
['brandId', input.brandId], ['search', input.search],
['sort', input.sort], ['categoryId', input.categoryId],
], ['brandId', input.brandId],
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, ['sort', input.sort],
}) ],
}, swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
},
} }

View File

@ -9,7 +9,7 @@ import { handler as useLogin } from './auth/use-login'
import { handler as useLogout } from './auth/use-logout' import { handler as useLogout } from './auth/use-logout'
import { handler as useSignup } from './auth/use-signup' import { handler as useSignup } from './auth/use-signup'
import { handler as useWishlist } from './wishlist/use-wishlist' import { handler as useWishlist } from './wishlist/use-wishlist'
import { handler as useWishlistAddItem } from './wishlist/use-add-item' import { handler as useWishlistAddItem } from './wishlist/use-add-item'
import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item' import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item'
export const kiboCommerceProvider = { export const kiboCommerceProvider = {

View File

@ -2,26 +2,26 @@ import * as Core from '@vercel/commerce/types/customer'
export type Maybe<T> = T | null export type Maybe<T> = T | null
export * from '@vercel/commerce/types/customer' export * from '@vercel/commerce/types/customer'
export type Scalars = { export type Scalars = {
ID: string ID: string
String: string String: string
Boolean: boolean Boolean: boolean
Int: number Int: number
Float: number Float: number
/** The `AnyScalar` type allows any scalar value by examining the input and passing the serialize, parseValue, and parseLiteral operations to their respective types. */ /** The `AnyScalar` type allows any scalar value by examining the input and passing the serialize, parseValue, and parseLiteral operations to their respective types. */
AnyScalar: any AnyScalar: any
/** DateTime custom scalar type */ /** DateTime custom scalar type */
DateTime: any DateTime: any
/** Object custom scalar type */ /** Object custom scalar type */
Object: any Object: any
} }
export type Customer = { export type Customer = {
id: Scalars['Int'], id: Scalars['Int']
firstName?: Maybe<Scalars['String']>, firstName?: Maybe<Scalars['String']>
lastName?: Maybe<Scalars['String']>, lastName?: Maybe<Scalars['String']>
email?: Maybe<Scalars['String']>, email?: Maybe<Scalars['String']>
userName?: Maybe<Scalars['String']>, userName?: Maybe<Scalars['String']>
isAnonymous?: Maybe<Scalars['Boolean']> isAnonymous?: Maybe<Scalars['Boolean']>
} }
export type CustomerSchema = Core.CustomerSchema export type CustomerSchema = Core.CustomerSchema

View File

@ -1,17 +1,17 @@
import * as Core from '@vercel/commerce/types/page' import * as Core from '@vercel/commerce/types/page'
export type Maybe<T> = T | null export type Maybe<T> = T | null
export type Scalars = { export type Scalars = {
ID: string ID: string
String: string String: string
Boolean: boolean Boolean: boolean
Int: number Int: number
Float: number Float: number
/** The `AnyScalar` type allows any scalar value by examining the input and passing the serialize, parseValue, and parseLiteral operations to their respective types. */ /** The `AnyScalar` type allows any scalar value by examining the input and passing the serialize, parseValue, and parseLiteral operations to their respective types. */
AnyScalar: any AnyScalar: any
/** DateTime custom scalar type */ /** DateTime custom scalar type */
DateTime: any DateTime: any
/** Object custom scalar type */ /** Object custom scalar type */
Object: any Object: any
} }
export * from '@vercel/commerce/types/page' export * from '@vercel/commerce/types/page'
@ -19,16 +19,16 @@ export * from '@vercel/commerce/types/page'
export type Page = Core.Page export type Page = Core.Page
export type PageTypes = { export type PageTypes = {
page: Page page: Page
} }
export type GetPagesQueryParams = { export type GetPagesQueryParams = {
documentListName: Maybe<Scalars["String"]> documentListName: Maybe<Scalars['String']>
} }
export type GetPageQueryParams = { export type GetPageQueryParams = {
id: Maybe<Scalars["String"]> id: Maybe<Scalars['String']>
documentListName: Maybe<Scalars["String"]> documentListName: Maybe<Scalars['String']>
} }
export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes> export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes>

View File

@ -1,6 +1,8 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useWishlist, { UseWishlist } from '@vercel/commerce/wishlist/use-wishlist' import useWishlist, {
UseWishlist,
} from '@vercel/commerce/wishlist/use-wishlist'
import type { GetWishlistHook } from '@vercel/commerce/types/wishlist' import type { GetWishlistHook } from '@vercel/commerce/types/wishlist'
import useCustomer from '../customer/use-customer' import useCustomer from '../customer/use-customer'
@ -11,42 +13,44 @@ export const handler: SWRHook<any> = {
url: '/api/wishlist', url: '/api/wishlist',
method: 'GET', method: 'GET',
}, },
fetcher({ input: { customerId, includeProducts}, options, fetch }) { fetcher({ input: { customerId, includeProducts }, options, fetch }) {
if (!customerId) return null if (!customerId) return null
// Use a dummy base as we only care about the relative path // Use a dummy base as we only care about the relative path
const url = new URL(options.url!, 'http://a') const url = new URL(options.url!, 'http://a')
if (includeProducts) url.searchParams.set('products', '1') if (includeProducts) url.searchParams.set('products', '1')
if(customerId) url.searchParams.set('customerId', customerId) if (customerId) url.searchParams.set('customerId', customerId)
return fetch({ return fetch({
url: url.pathname + url.search, url: url.pathname + url.search,
method: options.method, method: options.method,
}) })
}, },
useHook: ({ useData }) => (input) => { useHook:
const { data: customer } = useCustomer() ({ useData }) =>
const response = useData({ (input) => {
input: [ const { data: customer } = useCustomer()
['customerId', customer?.id], const response = useData({
['includeProducts', input?.includeProducts], input: [
], ['customerId', customer?.id],
swrOptions: { ['includeProducts', input?.includeProducts],
revalidateOnFocus: false, ],
...input?.swrOptions, swrOptions: {
}, revalidateOnFocus: false,
}) ...input?.swrOptions,
return useMemo( },
() => })
Object.create(response, { return useMemo(
isEmpty: { () =>
get() { Object.create(response, {
return (response.data?.items?.length || 0) <= 0 isEmpty: {
get() {
return (response.data?.items?.length || 0) <= 0
},
enumerable: true,
}, },
enumerable: true, }),
}, [response]
}), )
[response] },
)
},
} }

Some files were not shown because too many files have changed in this diff Show More