forked from crowetic/commerce
feat(provider): Implement Kibo Commerce provider (#575)
* Icky 161 folder and env setup (#1) * folder and env setup * codegen.json headers removed Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> * Feature/icky 194 (#5) * folder and env setup * codegen.json headers removed * use-cart code flow updated * use-cart code flow updated * Implemented get-cart functionality * removed unused file * getAnonymousShopperToken function added * normalization mapping updated * PR points resolved * Anonymous shopper token query added * getAnonymousShopperToken function added * Anonymous shopper token query added Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> * Icky 175 (#3) * folder and env setup * codegen.json headers removed * icky-175-get-site-info * PR comments resolved * update category Id Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> * Icky-169-LogIn (#4) * Update README.md * Initial Commit * Commited Keys * GraphQL Changes * GraphQL Query * Final Changes * Changed login.ts * Made changes in login.ts * Final Changes * Refactored login.ts * SignUp Initial Checkin * logout Initial * Customer Account Initial Commit * Logout - deleted cookie * Reverted ReadMe and UserNav file * Final Changes * Resolved comments * Resolved comments 1 * Resolved comments 2 * Resolved comments 3 * Resolved comments 4 Co-authored-by: SushantJadhav <Sushant.Jadhav@kibocommerce.com> * ICKY-166-getProducts-and-getProduct (#6) * GetProduct Initial Commit * Passed productCode as Slug to get-product * Moved currencyCode in Config file * Icky 173 (#9) * Initial commit related to getAllPages * Initial Changes * Making documentListName configurable * fixing dynamic page rendering and adding typescript code Co-authored-by: amolnadagonde <amol.nadagonde@kibocommerce.com> Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com> * Feature/icky 176 (#8) * GetProduct Initial Commit * addItemToCart function implemneted * Add Item to cart functionality implemented * ICKY-166-getProducts-and-getProduct (#6) * GetProduct Initial Commit * Passed productCode as Slug to get-product * Moved currencyCode in Config file * Icky 173 (#9) * Initial commit related to getAllPages * Initial Changes * Making documentListName configurable * fixing dynamic page rendering and adding typescript code Co-authored-by: amolnadagonde <amol.nadagonde@kibocommerce.com> Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com> * addItemToCart function implemneted Conflicts resolved * Add Item to cart functionality implemented * booleans removed from query * cart size option enabled * updated addItem for with and without variants products Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com> Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> Co-authored-by: kibo-sushant <89385472+kibo-sushant@users.noreply.github.com> Co-authored-by: kibo-kevinwatts <85258296+kibo-kevinwatts@users.noreply.github.com> Co-authored-by: amolnadagonde <amol.nadagonde@kibocommerce.com> * Removed types from schema.d.ts (#11) * Final Changes (#16) * Icky 177 (#13) * addItemToCart function implemneted Conflicts resolved * Add Item to cart functionality implemented * remove Item from cart implemented * removed unused code Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> * Icky 178 - Update Cart Quantity implemented (#14) * update cart quantity implemented * add item to cart bug fixed Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> * Icky 182 (#12) * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * changes in product-search-vars * remove unwanted boolean * Feature/icky 179 (#17) * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * remove extra spaces * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * changes in product-search-vars * remove unwanted boolean * initial commit * updated Provider * usewishlist/getwishlist * changes in provider.ts * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * usewishlist/getwishlist * updated Provider * changes in provider.ts * normalize wishlistitem * changes in get-customer-account * remove unwanted code * resolve empty wishlist case * resolve pr comments Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com> * token encoding and decoding fixed (#19) Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> * Feature/icky- 180 & 181 (#20) * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * usewishlist/getwishlist * changes in provider.ts * changes in get-customer-account * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * initial commit * useSearch hook * revert pages and component changes * remove extra spacing in search.tsx * changes in catalog/products and product-search-vars * usewishlist/getwishlist * changes in provider.ts * remove unwanted code * initial commit * resolve pr comments * changes in add-item * remove wishlist fragment * remove item from wishlist * changes in normalize.ts * changes in additemtowishlist mutation * resove pr comments * Feature/icky 291 (#22) * Kibo API authentication helper handling oauth token generation / refresh and making auth ticket available to process via next server runtime config * Update .env template with placeholder for Kibo Auth Url * resolve ICKY-275 (#24) * Fix/icky 276 (#21) * remove Item from cart implemented * update cart quantity implemented * removed unused code * ICKY 176 and 263 implemented * ICKY 263 removed * PR points resolved Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> * ICKY-263 (#23) * ICKY-263 * resolve pr comments * resolve pr comments for customer typescript * docs: update kibo commerce readme with env details (#26) * resolve icky-264 (#25) * chore: remove extra field from .env.template * chore: remove extra .vscode launch json file * chore: cleanup test message from kibocommerce fork * chore(docs): remove extra field from .env template and .env related docs * chore: remove test data json file * chore: delete yarn.lock * refactor: remove unused fetch from kibo config, remove unused CommerceProvider * refactor: rename queries and util modules for consistency * chore: add checkout related api noop handlers and hooks * chore: revert modified core files * chore(config): add kibo production sandbox cdn to image domains config * fix: page normalizer and query for static pages * chore: remove commented code and unnecessary imports * fix(module paths): switch framework alias for relative path Co-authored-by: kibo-chandradeeptalaha <89371824+kibo-chandradeeptalaha@users.noreply.github.com> Co-authored-by: Chandradeepta <43542673+Chandradeepta@users.noreply.github.com> Co-authored-by: kibo-geetanshuchhabra <89399259+kibo-geetanshuchhabra@users.noreply.github.com> Co-authored-by: kibo-sushant <89385472+kibo-sushant@users.noreply.github.com> Co-authored-by: SushantJadhav <Sushant.Jadhav@kibocommerce.com> Co-authored-by: amolnadagonde <amol.nadagonde@kibocommerce.com> Co-authored-by: kibo-sushant <sushant.jadhav@blueconchtech.com> Co-authored-by: kibo-amolnadagonde <75060520+kibo-amolnadagonde@users.noreply.github.com>
This commit is contained in:
parent
d77d000431
commit
9a4772cdb4
@ -27,3 +27,10 @@ NEXT_PUBLIC_VENDURE_LOCAL_URL=
|
|||||||
ORDERCLOUD_CLIENT_ID=
|
ORDERCLOUD_CLIENT_ID=
|
||||||
ORDERCLOUD_CLIENT_SECRET=
|
ORDERCLOUD_CLIENT_SECRET=
|
||||||
STRIPE_SECRET=
|
STRIPE_SECRET=
|
||||||
|
|
||||||
|
KIBO_API_URL=
|
||||||
|
KIBO_CLIENT_ID=
|
||||||
|
KIBO_SHARED_SECRET=
|
||||||
|
KIBO_CART_COOKIE=
|
||||||
|
KIBO_CUSTOMER_COOKIE=
|
||||||
|
KIBO_API_HOST=
|
||||||
|
@ -155,4 +155,4 @@ After Email confirmation, Checkout should be manually enabled through BigCommerc
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
BigCommerce team has been notified and they plan to add more details about this subject.
|
BigCommerce team has been notified and they plan to add more details about this subject.
|
||||||
</details>
|
</details>
|
@ -15,7 +15,8 @@ const PROVIDERS = [
|
|||||||
'swell',
|
'swell',
|
||||||
'vendure',
|
'vendure',
|
||||||
'ordercloud',
|
'ordercloud',
|
||||||
'spree',
|
'kibocommerce',
|
||||||
|
'spree'
|
||||||
]
|
]
|
||||||
|
|
||||||
function getProviderName() {
|
function getProviderName() {
|
||||||
|
7
framework/kibocommerce/.env.template
Normal file
7
framework/kibocommerce/.env.template
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
COMMERCE_PROVIDER=kibocommerce
|
||||||
|
KIBO_API_URL=
|
||||||
|
KIBO_CART_COOKIE=
|
||||||
|
KIBO_CUSTOMER_COOKIE=
|
||||||
|
KIBO_CLIENT_ID=
|
||||||
|
KIBO_SHARED_SECRET=
|
||||||
|
KIBO_AUTH_URL=
|
37
framework/kibocommerce/README.md
Normal file
37
framework/kibocommerce/README.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Kibo Commerce Provider
|
||||||
|
|
||||||
|
If you already have a Kibo Commerce account and want to use your current store, then copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp framework/kibocommerce/.env.template .env.local
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, set the environment variables in `.env.local` to match the ones from your store.
|
||||||
|
|
||||||
|
```
|
||||||
|
COMMERCE_PROVIDER='kibocommerce'
|
||||||
|
KIBO_API_URL= 'https://t1234-s1234.sandbox.mozu.com/graphql'
|
||||||
|
KIBO_CART_COOKIE='kibo_cart'
|
||||||
|
KIBO_CUSTOMER_COOKIE='kibo_customer'
|
||||||
|
KIBO_CLIENT_ID='KIBO.APP.1.0.0.Release'
|
||||||
|
KIBO_SHARED_SECRET='12345secret'
|
||||||
|
KIBO_AUTH_URL='https://home.mozu.com'
|
||||||
|
```
|
||||||
|
|
||||||
|
- `KIBO_API_URL` - link to your Kibo Commerce GraphQL API instance.
|
||||||
|
- `KIBO_CART_COOKIE` - configurable cookie name for cart.
|
||||||
|
- `KIBO_CUSTOMER_COOKIE` - configurable cookie name for shopper identifier/authentication cookie
|
||||||
|
- `KIBO_CLIENT_ID` - Unique Application (Client) ID of your Application
|
||||||
|
- `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)
|
||||||
|
|
||||||
|
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.
|
||||||
|
## Contribute
|
||||||
|
|
||||||
|
Our commitment to Open Source can be found [here](https://vercel.com/oss).
|
||||||
|
|
||||||
|
If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues).
|
102
framework/kibocommerce/api/endpoints/cart/add-item.ts
Normal file
102
framework/kibocommerce/api/endpoints/cart/add-item.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { Product } from './../../../schema.d'
|
||||||
|
import { normalizeCart } from '../../../lib/normalize'
|
||||||
|
import type { CartEndpoint } from '.'
|
||||||
|
import addToCurrentCartMutation from '../../../api/mutations/addToCart-mutation'
|
||||||
|
|
||||||
|
import { getProductQuery } from '../../../api/queries/get-product-query'
|
||||||
|
import { getCartQuery } from '../../../api/queries/get-cart-query'
|
||||||
|
import CookieHandler from '../../../api/utils/cookie-handler'
|
||||||
|
|
||||||
|
const buildAddToCartVariables = ({
|
||||||
|
productId,
|
||||||
|
variantId,
|
||||||
|
quantity = 1,
|
||||||
|
productResponse,
|
||||||
|
}: {
|
||||||
|
productId: string
|
||||||
|
variantId: string
|
||||||
|
quantity: number
|
||||||
|
productResponse: any
|
||||||
|
}) => {
|
||||||
|
const { product } = productResponse.data
|
||||||
|
|
||||||
|
const selectedOptions = product.variations?.find(
|
||||||
|
(v: any) => v.productCode === variantId
|
||||||
|
).options
|
||||||
|
|
||||||
|
let options: any[] = []
|
||||||
|
selectedOptions?.forEach((each: any) => {
|
||||||
|
product?.options
|
||||||
|
.filter((option: any) => {
|
||||||
|
return option.attributeFQN == each.attributeFQN
|
||||||
|
})
|
||||||
|
.forEach((po: any) => {
|
||||||
|
options.push({
|
||||||
|
attributeFQN: po.attributeFQN,
|
||||||
|
name: po.attributeDetail.name,
|
||||||
|
value: po.values?.find((v: any) => v.value == each.value).value,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
productToAdd: {
|
||||||
|
product: {
|
||||||
|
productCode: productId,
|
||||||
|
variationProductCode: variantId ? variantId : null,
|
||||||
|
options,
|
||||||
|
},
|
||||||
|
quantity,
|
||||||
|
fulfillmentMethod: 'Ship',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addItem: CartEndpoint['handlers']['addItem'] = async ({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
body: { cartId, item },
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
if (!item) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Missing item' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!item.quantity) item.quantity = 1
|
||||||
|
|
||||||
|
const productResponse = await config.fetch(getProductQuery, {
|
||||||
|
variables: { productCode: item?.productId },
|
||||||
|
})
|
||||||
|
|
||||||
|
const cookieHandler = new CookieHandler(config, req, res)
|
||||||
|
let accessToken = null
|
||||||
|
|
||||||
|
if (!cookieHandler.getAccessToken()) {
|
||||||
|
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken()
|
||||||
|
accessToken = anonymousShopperTokenResponse.accessToken;
|
||||||
|
} else {
|
||||||
|
accessToken = cookieHandler.getAccessToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
const addToCartResponse = await config.fetch(
|
||||||
|
addToCurrentCartMutation,
|
||||||
|
{
|
||||||
|
variables: buildAddToCartVariables({ ...item, productResponse }),
|
||||||
|
},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
let currentCart = null
|
||||||
|
if (addToCartResponse.data.addItemToCurrentCart) {
|
||||||
|
let result = await config.fetch(
|
||||||
|
getCartQuery,
|
||||||
|
{},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
currentCart = result?.data?.currentCart
|
||||||
|
}
|
||||||
|
res.status(200).json({ data: normalizeCart(currentCart) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default addItem
|
41
framework/kibocommerce/api/endpoints/cart/get-cart.ts
Normal file
41
framework/kibocommerce/api/endpoints/cart/get-cart.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import CookieHandler from '../../../api/utils/cookie-handler'
|
||||||
|
import { normalizeCart } from '../../../lib/normalize'
|
||||||
|
import { Cart } from '../../../schema'
|
||||||
|
import type { CartEndpoint } from '.'
|
||||||
|
import { getCartQuery } from '../../queries/get-cart-query'
|
||||||
|
|
||||||
|
const getCart: CartEndpoint['handlers']['getCart'] = async ({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
body: { cartId },
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
let currentCart: Cart = {}
|
||||||
|
try {
|
||||||
|
const cookieHandler = new CookieHandler(config, req, res)
|
||||||
|
let accessToken = null
|
||||||
|
|
||||||
|
if (!cookieHandler.getAccessToken()) {
|
||||||
|
let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken()
|
||||||
|
const response = anonymousShopperTokenResponse.response
|
||||||
|
accessToken = anonymousShopperTokenResponse.accessToken
|
||||||
|
cookieHandler.setAnonymousShopperCookie(response)
|
||||||
|
} else {
|
||||||
|
accessToken = cookieHandler.getAccessToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = await config.fetch(
|
||||||
|
getCartQuery,
|
||||||
|
{},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
currentCart = result?.data?.currentCart
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
res.status(200).json({
|
||||||
|
data: currentCart ? normalizeCart(currentCart) : null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getCart
|
25
framework/kibocommerce/api/endpoints/cart/index.ts
Normal file
25
framework/kibocommerce/api/endpoints/cart/index.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||||
|
import cartEndpoint from '@commerce/api/endpoints/cart'
|
||||||
|
import type { KiboCommerceAPI } from '../..'
|
||||||
|
import getCart from './get-cart';
|
||||||
|
import addItem from './add-item';
|
||||||
|
import updateItem from './update-item'
|
||||||
|
import removeItem from './remove-item'
|
||||||
|
|
||||||
|
export type CartAPI = GetAPISchema<KiboCommerceAPI, any>
|
||||||
|
|
||||||
|
export type CartEndpoint = CartAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers: CartEndpoint['handlers'] = {
|
||||||
|
getCart,
|
||||||
|
addItem,
|
||||||
|
updateItem,
|
||||||
|
removeItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
const cartApi = createEndpoint<CartAPI>({
|
||||||
|
handler: cartEndpoint,
|
||||||
|
handlers,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default cartApi
|
45
framework/kibocommerce/api/endpoints/cart/remove-item.ts
Normal file
45
framework/kibocommerce/api/endpoints/cart/remove-item.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { normalizeCart } from '../../../lib/normalize'
|
||||||
|
import type { CartEndpoint } from '.'
|
||||||
|
import removeItemFromCartMutation from '../../../api/mutations/removeItemFromCart-mutation'
|
||||||
|
import { getCartQuery } from '../../../api/queries/get-cart-query'
|
||||||
|
|
||||||
|
const removeItem: CartEndpoint['handlers']['removeItem'] = async ({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
body: { cartId, itemId },
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
if (!itemId) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Invalid request' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const encodedToken = req.cookies[config.customerCookie]
|
||||||
|
const token = encodedToken
|
||||||
|
? Buffer.from(encodedToken, 'base64').toString('ascii')
|
||||||
|
: null
|
||||||
|
|
||||||
|
const accessToken = token ? JSON.parse(token).accessToken : null
|
||||||
|
|
||||||
|
const removeItemResponse = await config.fetch(
|
||||||
|
removeItemFromCartMutation,
|
||||||
|
{
|
||||||
|
variables: { id: itemId },
|
||||||
|
},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
|
||||||
|
let currentCart = null
|
||||||
|
if (removeItemResponse.data.deleteCurrentCartItem) {
|
||||||
|
let result = await config.fetch(
|
||||||
|
getCartQuery,
|
||||||
|
{},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
currentCart = result?.data?.currentCart
|
||||||
|
}
|
||||||
|
res.status(200).json({ data: normalizeCart(currentCart) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default removeItem
|
45
framework/kibocommerce/api/endpoints/cart/update-item.ts
Normal file
45
framework/kibocommerce/api/endpoints/cart/update-item.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { normalizeCart } from '../../../lib/normalize'
|
||||||
|
import type { CartEndpoint } from '.'
|
||||||
|
import { getCartQuery } from '../../../api/queries/get-cart-query'
|
||||||
|
import updateCartItemQuantityMutation from '../../../api/mutations/updateCartItemQuantity-mutation'
|
||||||
|
|
||||||
|
const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
body: { cartId, itemId, item },
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
if (!itemId || !item) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Invalid request' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const encodedToken = req.cookies[config.customerCookie]
|
||||||
|
const token = encodedToken
|
||||||
|
? Buffer.from(encodedToken, 'base64').toString('ascii')
|
||||||
|
: null
|
||||||
|
|
||||||
|
const accessToken = token ? JSON.parse(token).accessToken : null
|
||||||
|
|
||||||
|
const updateItemResponse = await config.fetch(
|
||||||
|
updateCartItemQuantityMutation,
|
||||||
|
{
|
||||||
|
variables: { itemId: itemId, quantity: item.quantity },
|
||||||
|
},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
|
||||||
|
let currentCart = null
|
||||||
|
if (updateItemResponse.data) {
|
||||||
|
let result = await config.fetch(
|
||||||
|
getCartQuery,
|
||||||
|
{},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
currentCart = result?.data?.currentCart
|
||||||
|
}
|
||||||
|
res.status(200).json({ data: normalizeCart(currentCart) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default updateItem
|
@ -0,0 +1,17 @@
|
|||||||
|
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||||
|
import productsEndpoint from '@commerce/api/endpoints/catalog/products'
|
||||||
|
import type { KiboCommerceAPI } from '../../..'
|
||||||
|
import getProducts from '../products/products'
|
||||||
|
|
||||||
|
export type ProductsAPI = GetAPISchema<KiboCommerceAPI, any>
|
||||||
|
|
||||||
|
export type ProductsEndpoint = ProductsAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers: ProductsEndpoint['handlers'] = { getProducts }
|
||||||
|
|
||||||
|
const productsApi = createEndpoint<ProductsAPI>({
|
||||||
|
handler: productsEndpoint,
|
||||||
|
handlers,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default productsApi
|
@ -0,0 +1,31 @@
|
|||||||
|
import { Product } from '@commerce/types/product'
|
||||||
|
import { ProductsEndpoint } from '.'
|
||||||
|
import productSearchQuery from '../../../queries/product-search-query'
|
||||||
|
import { buildProductSearchVars } from '../../../../lib/product-search-vars'
|
||||||
|
import {normalizeProduct} from '../../../../lib/normalize'
|
||||||
|
|
||||||
|
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
||||||
|
res,
|
||||||
|
body: { search, categoryId, brandId, sort },
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
const pageSize = 100;
|
||||||
|
const filters = {};
|
||||||
|
const startIndex = 0;
|
||||||
|
const variables = buildProductSearchVars({
|
||||||
|
categoryCode: categoryId,
|
||||||
|
pageSize,
|
||||||
|
search,
|
||||||
|
sort,
|
||||||
|
filters,
|
||||||
|
startIndex,
|
||||||
|
})
|
||||||
|
const {data} = await config.fetch(productSearchQuery, { variables });
|
||||||
|
const found = data?.products?.items?.length > 0 ? true : false;
|
||||||
|
let productsResponse= data?.products?.items.map((item: any) =>normalizeProduct(item,config));
|
||||||
|
const products: Product[] = found ? productsResponse : [];
|
||||||
|
|
||||||
|
res.status(200).json({ data: { products, found } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getProducts
|
1
framework/kibocommerce/api/endpoints/checkout/index.ts
Normal file
1
framework/kibocommerce/api/endpoints/checkout/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/kibocommerce/api/endpoints/customer/address.ts
Normal file
1
framework/kibocommerce/api/endpoints/customer/address.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/kibocommerce/api/endpoints/customer/card.ts
Normal file
1
framework/kibocommerce/api/endpoints/customer/card.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
36
framework/kibocommerce/api/endpoints/customer/customer.ts
Normal file
36
framework/kibocommerce/api/endpoints/customer/customer.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import CookieHandler from '../../../api/utils/cookie-handler'
|
||||||
|
import type { CustomerEndpoint } from '.'
|
||||||
|
import { getCustomerAccountQuery } from '../../queries/get-customer-account-query'
|
||||||
|
import { normalizeCustomer } from '../../../lib/normalize'
|
||||||
|
|
||||||
|
const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = async ({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
const cookieHandler = new CookieHandler(config, req, res)
|
||||||
|
let accessToken = cookieHandler.getAccessToken();
|
||||||
|
|
||||||
|
if (cookieHandler.getAccessToken()) {
|
||||||
|
const { data } = await config.fetch(getCustomerAccountQuery, undefined, {
|
||||||
|
headers: {
|
||||||
|
'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' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({ data: { customer } })
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({ data: null })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getLoggedInCustomer
|
18
framework/kibocommerce/api/endpoints/customer/index.ts
Normal file
18
framework/kibocommerce/api/endpoints/customer/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||||
|
import customerEndpoint from '@commerce/api/endpoints/customer'
|
||||||
|
import type { CustomerSchema } from '../../../types/customer'
|
||||||
|
import type { KiboCommerceAPI } from '../..'
|
||||||
|
import getLoggedInCustomer from './customer'
|
||||||
|
|
||||||
|
export type CustomerAPI = GetAPISchema<KiboCommerceAPI, CustomerSchema>
|
||||||
|
|
||||||
|
export type CustomerEndpoint = CustomerAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers: CustomerEndpoint['handlers'] = { getLoggedInCustomer }
|
||||||
|
|
||||||
|
const customerApi = createEndpoint<CustomerAPI>({
|
||||||
|
handler: customerEndpoint,
|
||||||
|
handlers,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default customerApi
|
20
framework/kibocommerce/api/endpoints/login/index.ts
Normal file
20
framework/kibocommerce/api/endpoints/login/index.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||||
|
import loginEndpoint from '@commerce/api/endpoints/login'
|
||||||
|
import type { LoginSchema } from '../../../types/login'
|
||||||
|
import type { KiboCommerceAPI } from '../..'
|
||||||
|
import login from './login'
|
||||||
|
|
||||||
|
export type LoginAPI = GetAPISchema<KiboCommerceAPI, LoginSchema>
|
||||||
|
|
||||||
|
export type LoginEndpoint = LoginAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers: LoginEndpoint['handlers'] = { login }
|
||||||
|
|
||||||
|
const loginApi = createEndpoint<LoginAPI>({
|
||||||
|
handler: loginEndpoint,
|
||||||
|
handlers,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default loginApi;
|
||||||
|
|
||||||
|
|
66
framework/kibocommerce/api/endpoints/login/login.ts
Normal file
66
framework/kibocommerce/api/endpoints/login/login.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { FetcherError } from '@commerce/utils/errors'
|
||||||
|
import type { LoginEndpoint } from '.'
|
||||||
|
import { loginMutation } from '../../mutations/login-mutation'
|
||||||
|
import { prepareSetCookie } from '../../../lib/prepare-set-cookie';
|
||||||
|
import { setCookies } from '../../../lib/set-cookie'
|
||||||
|
import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date'
|
||||||
|
|
||||||
|
const invalidCredentials = /invalid credentials/i
|
||||||
|
|
||||||
|
const login: LoginEndpoint['handlers']['login'] = async ({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
body: { email, password },
|
||||||
|
config,
|
||||||
|
commerce,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
if (!(email && password)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Invalid request' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
|
||||||
|
const variables = { loginInput : { username: email, password }};
|
||||||
|
response = await config.fetch(loginMutation, { variables })
|
||||||
|
const { account: token } = response.data;
|
||||||
|
|
||||||
|
// Set Cookie
|
||||||
|
const cookieExpirationDate = getCookieExpirationDate(config.customerCookieMaxAgeInDays)
|
||||||
|
|
||||||
|
const authCookie = prepareSetCookie(
|
||||||
|
config.customerCookie,
|
||||||
|
JSON.stringify(token),
|
||||||
|
token.accessTokenExpiration ? { expires: cookieExpirationDate }: {},
|
||||||
|
)
|
||||||
|
setCookies(res, [authCookie])
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Check if the email and password didn't match an existing account
|
||||||
|
if (
|
||||||
|
error instanceof FetcherError &&
|
||||||
|
invalidCredentials.test(error.message)
|
||||||
|
) {
|
||||||
|
return res.status(401).json({
|
||||||
|
data: null,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
message:
|
||||||
|
'Cannot find an account that matches the provided credentials',
|
||||||
|
code: 'invalid_credentials',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({ data: response })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default login
|
18
framework/kibocommerce/api/endpoints/logout/index.ts
Normal file
18
framework/kibocommerce/api/endpoints/logout/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||||
|
import logoutEndpoint from '@commerce/api/endpoints/logout'
|
||||||
|
import type { LogoutSchema } from '../../../types/logout'
|
||||||
|
import type { KiboCommerceAPI } from '../..'
|
||||||
|
import logout from './logout'
|
||||||
|
|
||||||
|
export type LogoutAPI = GetAPISchema<KiboCommerceAPI, LogoutSchema>
|
||||||
|
|
||||||
|
export type LogoutEndpoint = LogoutAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers: LogoutEndpoint['handlers'] = { logout }
|
||||||
|
|
||||||
|
const logoutApi = createEndpoint<LogoutAPI>({
|
||||||
|
handler: logoutEndpoint,
|
||||||
|
handlers,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default logoutApi
|
22
framework/kibocommerce/api/endpoints/logout/logout.ts
Normal file
22
framework/kibocommerce/api/endpoints/logout/logout.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import type { LogoutEndpoint } from '.'
|
||||||
|
import {prepareSetCookie} from '../../../lib/prepare-set-cookie';
|
||||||
|
import {setCookies} from '../../../lib/set-cookie'
|
||||||
|
|
||||||
|
const logout: LogoutEndpoint['handlers']['logout'] = async ({
|
||||||
|
res,
|
||||||
|
body: { redirectTo },
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
// Remove the cookie
|
||||||
|
const authCookie = prepareSetCookie(config.customerCookie,'',{ maxAge: -1, path: '/' })
|
||||||
|
setCookies(res, [authCookie])
|
||||||
|
|
||||||
|
// Only allow redirects to a relative URL
|
||||||
|
if (redirectTo?.startsWith('/')) {
|
||||||
|
res.redirect(redirectTo)
|
||||||
|
} else {
|
||||||
|
res.status(200).json({ data: null })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default logout
|
18
framework/kibocommerce/api/endpoints/signup/index.ts
Normal file
18
framework/kibocommerce/api/endpoints/signup/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||||
|
import signupEndpoint from '@commerce/api/endpoints/signup'
|
||||||
|
import type { SignupSchema } from '../../../types/signup'
|
||||||
|
import type { KiboCommerceAPI } from '../..'
|
||||||
|
import signup from './signup'
|
||||||
|
|
||||||
|
export type SignupAPI = GetAPISchema<KiboCommerceAPI, SignupSchema>
|
||||||
|
|
||||||
|
export type SignupEndpoint = SignupAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers: SignupEndpoint['handlers'] = { signup }
|
||||||
|
|
||||||
|
const singupApi = createEndpoint<SignupAPI>({
|
||||||
|
handler: signupEndpoint,
|
||||||
|
handlers,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default singupApi
|
91
framework/kibocommerce/api/endpoints/signup/signup.ts
Normal file
91
framework/kibocommerce/api/endpoints/signup/signup.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { FetcherError } from '@commerce/utils/errors'
|
||||||
|
import type { SignupEndpoint } from '.'
|
||||||
|
import { registerUserMutation, registerUserLoginMutation } from '../../mutations/signup-mutation'
|
||||||
|
import { prepareSetCookie } from '../../../lib/prepare-set-cookie';
|
||||||
|
import { setCookies } from '../../../lib/set-cookie'
|
||||||
|
import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date'
|
||||||
|
|
||||||
|
const invalidCredentials = /invalid credentials/i
|
||||||
|
|
||||||
|
const signup: SignupEndpoint['handlers']['signup'] = async ({
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
body: { email, password, firstName, lastName },
|
||||||
|
config,
|
||||||
|
commerce,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
if (!(email && password)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Invalid request' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Register user
|
||||||
|
const registerUserVariables = {
|
||||||
|
customerAccountInput: {
|
||||||
|
emailAddress: email,
|
||||||
|
firstName: firstName,
|
||||||
|
lastName: lastName,
|
||||||
|
acceptsMarketing: true,
|
||||||
|
id: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const registerUserResponse = await config.fetch(registerUserMutation, { variables: registerUserVariables})
|
||||||
|
const accountId = registerUserResponse.data?.account?.id;
|
||||||
|
|
||||||
|
// Login user
|
||||||
|
const registerUserLoginVairables = {
|
||||||
|
accountId: accountId,
|
||||||
|
customerLoginInfoInput: {
|
||||||
|
emailAddress: email,
|
||||||
|
username: email,
|
||||||
|
password: password,
|
||||||
|
isImport: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await config.fetch(registerUserLoginMutation, { variables: registerUserLoginVairables})
|
||||||
|
const { account: token } = response.data;
|
||||||
|
|
||||||
|
// Set Cookie
|
||||||
|
const cookieExpirationDate = getCookieExpirationDate(config.customerCookieMaxAgeInDays)
|
||||||
|
|
||||||
|
const authCookie = prepareSetCookie(
|
||||||
|
config.customerCookie,
|
||||||
|
JSON.stringify(token),
|
||||||
|
token.accessTokenExpiration ? { expires: cookieExpirationDate }: {},
|
||||||
|
)
|
||||||
|
|
||||||
|
setCookies(res, [authCookie])
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Check if the email and password didn't match an existing account
|
||||||
|
if (
|
||||||
|
error instanceof FetcherError &&
|
||||||
|
invalidCredentials.test(error.message)
|
||||||
|
) {
|
||||||
|
return res.status(401).json({
|
||||||
|
data: null,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
message:
|
||||||
|
'Cannot find an account that matches the provided credentials',
|
||||||
|
code: 'invalid_credentials',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({ data: response })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default signup
|
124
framework/kibocommerce/api/endpoints/wishlist/add-item.ts
Normal file
124
framework/kibocommerce/api/endpoints/wishlist/add-item.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import getCustomerWishlist from '../../operations/get-customer-wishlist'
|
||||||
|
import getCustomerId from '../../utils/get-customer-id'
|
||||||
|
import type { WishlistEndpoint } from '.'
|
||||||
|
import { normalizeWishlistItem } from '../../../lib/normalize'
|
||||||
|
import { getProductQuery } from '../../../api/queries/get-product-query'
|
||||||
|
import addItemToWishlistMutation from '../../mutations/addItemToWishlist-mutation'
|
||||||
|
import createWishlist from '../../mutations/create-wishlist-mutation'
|
||||||
|
|
||||||
|
// Return wishlist info
|
||||||
|
const buildAddToWishlistVariables = ({
|
||||||
|
productId,
|
||||||
|
variantId,
|
||||||
|
productResponse,
|
||||||
|
wishlist
|
||||||
|
}: {
|
||||||
|
productId: string
|
||||||
|
variantId: string
|
||||||
|
productResponse: any
|
||||||
|
wishlist: any
|
||||||
|
}) => {
|
||||||
|
const { product } = productResponse.data
|
||||||
|
|
||||||
|
const selectedOptions = product.variations?.find(
|
||||||
|
(v: any) => v.productCode === variantId
|
||||||
|
).options
|
||||||
|
const quantity=1
|
||||||
|
let options: any[] = []
|
||||||
|
selectedOptions?.forEach((each: any) => {
|
||||||
|
product?.options
|
||||||
|
.filter((option: any) => {
|
||||||
|
return option.attributeFQN == each.attributeFQN
|
||||||
|
})
|
||||||
|
.forEach((po: any) => {
|
||||||
|
options.push({
|
||||||
|
attributeFQN: po.attributeFQN,
|
||||||
|
name: po.attributeDetail.name,
|
||||||
|
value: po.values?.find((v: any) => v.value == each.value).value,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
wishlistId: wishlist?.id,
|
||||||
|
wishlistItemInput: {
|
||||||
|
quantity,
|
||||||
|
product: {
|
||||||
|
productCode: productId,
|
||||||
|
variationProductCode: variantId ? variantId : null,
|
||||||
|
options,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
|
||||||
|
res,
|
||||||
|
body: { customerToken, item },
|
||||||
|
config,
|
||||||
|
commerce,
|
||||||
|
}) => {
|
||||||
|
const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null;
|
||||||
|
const accessToken = token ? JSON.parse(token).accessToken : null;
|
||||||
|
let result: { data?: any } = {}
|
||||||
|
let wishlist: any
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Missing item' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const customerId = customerToken && (await getCustomerId({ customerToken, config }))
|
||||||
|
const wishlistName= config.defaultWishlistName
|
||||||
|
|
||||||
|
if (!customerId) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Invalid request' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const wishlistResponse = await commerce.getCustomerWishlist({
|
||||||
|
variables: { customerId, wishlistName },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
wishlist= wishlistResponse?.wishlist
|
||||||
|
if(Object.keys(wishlist).length === 0) {
|
||||||
|
const createWishlistResponse= await config.fetch(createWishlist, {variables: {
|
||||||
|
wishlistInput: {
|
||||||
|
customerAccountId: customerId,
|
||||||
|
name: wishlistName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {headers: { 'x-vol-user-claims': accessToken } })
|
||||||
|
wishlist= createWishlistResponse?.data?.createWishlist
|
||||||
|
}
|
||||||
|
|
||||||
|
const productResponse = await config.fetch(getProductQuery, {
|
||||||
|
variables: { productCode: item?.productId },
|
||||||
|
})
|
||||||
|
|
||||||
|
const addItemToWishlistResponse = await config.fetch(
|
||||||
|
addItemToWishlistMutation,
|
||||||
|
{
|
||||||
|
variables: buildAddToWishlistVariables({ ...item, productResponse, wishlist }),
|
||||||
|
},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
|
||||||
|
if(addItemToWishlistResponse?.data?.createWishlistItem){
|
||||||
|
const wishlistResponse= await commerce.getCustomerWishlist({
|
||||||
|
variables: { customerId, wishlistName },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
wishlist= wishlistResponse?.wishlist
|
||||||
|
}
|
||||||
|
|
||||||
|
result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config))} }
|
||||||
|
|
||||||
|
res.status(200).json({ data: result?.data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default addItem
|
@ -0,0 +1,35 @@
|
|||||||
|
import type { WishlistEndpoint } from '.'
|
||||||
|
import getCustomerId from '../../utils/get-customer-id'
|
||||||
|
import { normalizeWishlistItem } from '../../../lib/normalize'
|
||||||
|
|
||||||
|
// Return wishlist info
|
||||||
|
const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
|
||||||
|
res,
|
||||||
|
body: { customerToken, includeProducts },
|
||||||
|
config,
|
||||||
|
commerce,
|
||||||
|
}) => {
|
||||||
|
let result: { data?: any } = {}
|
||||||
|
if (customerToken) {
|
||||||
|
const customerId = customerToken && (await getCustomerId({ customerToken, config }))
|
||||||
|
const wishlistName= config.defaultWishlistName
|
||||||
|
if (!customerId) {
|
||||||
|
// If the customerToken is invalid, then this request is too
|
||||||
|
return res.status(404).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Wishlist not found' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const { wishlist } = await commerce.getCustomerWishlist({
|
||||||
|
variables: { customerId, wishlistName },
|
||||||
|
includeProducts,
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
|
||||||
|
result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config, includeProducts))} }
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({ data: result?.data ?? null })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getWishlist
|
23
framework/kibocommerce/api/endpoints/wishlist/index.ts
Normal file
23
framework/kibocommerce/api/endpoints/wishlist/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { GetAPISchema, createEndpoint } from '@commerce/api'
|
||||||
|
import wishlistEndpoint from '@commerce/api/endpoints/wishlist'
|
||||||
|
import type { KiboCommerceAPI } from '../..'
|
||||||
|
import getWishlist from './get-wishlist'
|
||||||
|
import addItem from './add-item'
|
||||||
|
import removeItem from './remove-item'
|
||||||
|
|
||||||
|
export type WishlistAPI = GetAPISchema<KiboCommerceAPI, any>
|
||||||
|
|
||||||
|
export type WishlistEndpoint = WishlistAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers: WishlistEndpoint['handlers'] = {
|
||||||
|
getWishlist,
|
||||||
|
addItem,
|
||||||
|
removeItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
const wishlistApi = createEndpoint<WishlistAPI>({
|
||||||
|
handler: wishlistEndpoint,
|
||||||
|
handlers,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default wishlistApi
|
60
framework/kibocommerce/api/endpoints/wishlist/remove-item.ts
Normal file
60
framework/kibocommerce/api/endpoints/wishlist/remove-item.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import getCustomerId from '../../utils/get-customer-id'
|
||||||
|
import type { WishlistEndpoint } from '.'
|
||||||
|
import { normalizeWishlistItem } from '../../../lib/normalize'
|
||||||
|
import removeItemFromWishlistMutation from '../../mutations/removeItemFromWishlist-mutation'
|
||||||
|
|
||||||
|
// Return wishlist info
|
||||||
|
const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
|
||||||
|
res,
|
||||||
|
body: { customerToken, itemId },
|
||||||
|
config,
|
||||||
|
commerce,
|
||||||
|
}) => {
|
||||||
|
const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null;
|
||||||
|
const accessToken = token ? JSON.parse(token).accessToken : null;
|
||||||
|
let result: { data?: any } = {}
|
||||||
|
let wishlist: any
|
||||||
|
|
||||||
|
const customerId = customerToken && (await getCustomerId({ customerToken, config }))
|
||||||
|
const wishlistName= config.defaultWishlistName
|
||||||
|
const wishlistResponse = await commerce.getCustomerWishlist({
|
||||||
|
variables: { customerId, wishlistName },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
wishlist= wishlistResponse?.wishlist
|
||||||
|
|
||||||
|
if (!wishlist || !itemId) {
|
||||||
|
return res.status(400).json({
|
||||||
|
data: null,
|
||||||
|
errors: [{ message: 'Invalid request' }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const removedItem = wishlist?.items?.find(
|
||||||
|
(item:any) => {
|
||||||
|
return item.product.productCode === itemId;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeItemFromWishlistResponse = await config.fetch(
|
||||||
|
removeItemFromWishlistMutation,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
wishlistId: wishlist?.id,
|
||||||
|
wishlistItemId: removedItem?.id
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ headers: { 'x-vol-user-claims': accessToken } }
|
||||||
|
)
|
||||||
|
|
||||||
|
if(removeItemFromWishlistResponse?.data?.deleteWishlistItem){
|
||||||
|
const wishlistResponse= await commerce.getCustomerWishlist({
|
||||||
|
variables: { customerId, wishlistName },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
wishlist= wishlistResponse?.wishlist
|
||||||
|
}
|
||||||
|
result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config))} }
|
||||||
|
res.status(200).json({ data: result?.data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default removeItem
|
11
framework/kibocommerce/api/fragments/cartItemDetails.ts
Normal file
11
framework/kibocommerce/api/fragments/cartItemDetails.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { productDetails } from '../fragments/productDetails'
|
||||||
|
export const cartItemDetails = /*GraphQL*/`
|
||||||
|
fragment cartItemDetails on CartItem {
|
||||||
|
id
|
||||||
|
product {
|
||||||
|
...productDetails
|
||||||
|
}
|
||||||
|
quantity
|
||||||
|
}
|
||||||
|
${productDetails}
|
||||||
|
`;
|
11
framework/kibocommerce/api/fragments/category.ts
Normal file
11
framework/kibocommerce/api/fragments/category.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const CategoryInfo = /* GraphQL */`
|
||||||
|
fragment categoryInfo on PrCategory {
|
||||||
|
categoryId
|
||||||
|
categoryCode
|
||||||
|
isDisplayed
|
||||||
|
content {
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}`;
|
98
framework/kibocommerce/api/fragments/product.ts
Normal file
98
framework/kibocommerce/api/fragments/product.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
export const productPrices = /* GraphQL */`
|
||||||
|
fragment productPrices on Product {
|
||||||
|
price {
|
||||||
|
price
|
||||||
|
salePrice
|
||||||
|
}
|
||||||
|
priceRange {
|
||||||
|
lower { price, salePrice}
|
||||||
|
upper { price, salePrice }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const productAttributes = /* GraphQL */`
|
||||||
|
fragment productAttributes on Product {
|
||||||
|
properties {
|
||||||
|
attributeFQN
|
||||||
|
attributeDetail {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
isHidden
|
||||||
|
values {
|
||||||
|
value
|
||||||
|
stringValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const productContent = /* GraphQL */`
|
||||||
|
fragment productContent on Product {
|
||||||
|
content {
|
||||||
|
productFullDescription
|
||||||
|
productShortDescription
|
||||||
|
seoFriendlyUrl
|
||||||
|
productName
|
||||||
|
productImages {
|
||||||
|
imageUrl
|
||||||
|
imageLabel
|
||||||
|
mediaType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const productOptions = /* GraphQL */`
|
||||||
|
fragment productOptions on Product {
|
||||||
|
options {
|
||||||
|
attributeFQN
|
||||||
|
attributeDetail {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
isProductImageGroupSelector
|
||||||
|
isRequired
|
||||||
|
isMultiValue
|
||||||
|
values {
|
||||||
|
value
|
||||||
|
isSelected
|
||||||
|
deltaPrice
|
||||||
|
stringValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const productInfo = /* GraphQL */`
|
||||||
|
fragment productInfo on Product {
|
||||||
|
productCode
|
||||||
|
productUsage
|
||||||
|
|
||||||
|
purchasableState {
|
||||||
|
isPurchasable
|
||||||
|
}
|
||||||
|
|
||||||
|
variations {
|
||||||
|
productCode,
|
||||||
|
options {
|
||||||
|
__typename
|
||||||
|
attributeFQN
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
categories {
|
||||||
|
categoryCode
|
||||||
|
categoryId
|
||||||
|
content {
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
...productPrices
|
||||||
|
...productAttributes
|
||||||
|
...productContent
|
||||||
|
...productOptions
|
||||||
|
}
|
||||||
|
${productPrices}
|
||||||
|
${productAttributes}
|
||||||
|
${productContent}
|
||||||
|
${productOptions}
|
||||||
|
`;
|
30
framework/kibocommerce/api/fragments/productDetails.ts
Normal file
30
framework/kibocommerce/api/fragments/productDetails.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export const productDetails = /* GraphQL */ `
|
||||||
|
fragment productDetails on CrProduct {
|
||||||
|
productCode
|
||||||
|
name
|
||||||
|
description
|
||||||
|
imageUrl
|
||||||
|
imageAlternateText
|
||||||
|
sku
|
||||||
|
variationProductCode
|
||||||
|
price {
|
||||||
|
price
|
||||||
|
salePrice
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
attributeFQN
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
properties {
|
||||||
|
attributeFQN
|
||||||
|
name
|
||||||
|
values {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
categories {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
32
framework/kibocommerce/api/fragments/search.ts
Normal file
32
framework/kibocommerce/api/fragments/search.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { productInfo } from './product';
|
||||||
|
|
||||||
|
export const searchFacets = /* GraphQL */`
|
||||||
|
fragment searchFacets on Facet {
|
||||||
|
label
|
||||||
|
field
|
||||||
|
values {
|
||||||
|
label
|
||||||
|
value
|
||||||
|
isApplied
|
||||||
|
filterValue
|
||||||
|
isDisplayed
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const searchResults = /* GraphQL */`
|
||||||
|
fragment searchResults on ProductSearchResult {
|
||||||
|
totalCount
|
||||||
|
pageSize
|
||||||
|
pageCount
|
||||||
|
startIndex
|
||||||
|
items {
|
||||||
|
...productInfo
|
||||||
|
}
|
||||||
|
facets {
|
||||||
|
...searchFacets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${searchFacets}
|
||||||
|
${productInfo}
|
||||||
|
`;
|
64
framework/kibocommerce/api/index.ts
Normal file
64
framework/kibocommerce/api/index.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api'
|
||||||
|
import { getCommerceApi as commerceApi } from '@commerce/api'
|
||||||
|
import createFetchGraphqlApi from './utils/fetch-graphql-api'
|
||||||
|
|
||||||
|
import getAllPages from './operations/get-all-pages'
|
||||||
|
import getPage from './operations/get-page'
|
||||||
|
import getSiteInfo from './operations/get-site-info'
|
||||||
|
import getCustomerWishlist from './operations/get-customer-wishlist'
|
||||||
|
import getAllProductPaths from './operations/get-all-product-paths'
|
||||||
|
import getAllProducts from './operations/get-all-products'
|
||||||
|
import getProduct from './operations/get-product'
|
||||||
|
import type { RequestInit } from '@vercel/fetch'
|
||||||
|
|
||||||
|
export interface KiboCommerceConfig extends CommerceAPIConfig {
|
||||||
|
apiHost?: string
|
||||||
|
clientId?: string
|
||||||
|
sharedSecret?: string
|
||||||
|
customerCookieMaxAgeInDays: number,
|
||||||
|
currencyCode: string,
|
||||||
|
documentListName: string,
|
||||||
|
defaultWishlistName: string,
|
||||||
|
authUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const config: KiboCommerceConfig = {
|
||||||
|
commerceUrl: process.env.KIBO_API_URL || '',
|
||||||
|
apiToken: process.env.KIBO_API_TOKEN || '',
|
||||||
|
cartCookie: process.env.KIBO_CART_COOKIE || '',
|
||||||
|
customerCookie: process.env.KIBO_CUSTOMER_COOKIE || '',
|
||||||
|
cartCookieMaxAge: 2592000,
|
||||||
|
documentListName: 'siteSnippets@mozu',
|
||||||
|
fetch: createFetchGraphqlApi(() => getCommerceApi().getConfig()),
|
||||||
|
authUrl: process.env.KIBO_AUTH_URL || '',
|
||||||
|
// REST API
|
||||||
|
apiHost: process.env.KIBO_API_HOST || '',
|
||||||
|
clientId: process.env.KIBO_CLIENT_ID || '',
|
||||||
|
sharedSecret: process.env.KIBO_SHARED_SECRET || '',
|
||||||
|
customerCookieMaxAgeInDays: 30,
|
||||||
|
currencyCode: 'USD',
|
||||||
|
defaultWishlistName: 'My Wishlist'
|
||||||
|
}
|
||||||
|
|
||||||
|
const operations = {
|
||||||
|
getAllPages,
|
||||||
|
getPage,
|
||||||
|
getSiteInfo,
|
||||||
|
getCustomerWishlist,
|
||||||
|
getAllProductPaths,
|
||||||
|
getAllProducts,
|
||||||
|
getProduct,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const provider = { config, operations }
|
||||||
|
|
||||||
|
export type KiboCommerceProvider = typeof provider
|
||||||
|
export type KiboCommerceAPI<
|
||||||
|
P extends KiboCommerceProvider = KiboCommerceProvider
|
||||||
|
> = CommerceAPI<P | any>
|
||||||
|
|
||||||
|
export function getCommerceApi<P extends KiboCommerceProvider>(
|
||||||
|
customProvider: P = provider as any
|
||||||
|
): KiboCommerceAPI<P> {
|
||||||
|
return commerceApi(customProvider as any)
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import {productDetails} from '../fragments/productDetails'
|
||||||
|
const addItemToWishlistMutation = /* GraphQL */`
|
||||||
|
mutation createWishlistItem(
|
||||||
|
$wishlistId: String!
|
||||||
|
$wishlistItemInput: WishlistItemInput
|
||||||
|
) {
|
||||||
|
createWishlistItem(
|
||||||
|
wishlistId: $wishlistId
|
||||||
|
wishlistItemInput: $wishlistItemInput
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
product {
|
||||||
|
...productDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${productDetails}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default addItemToWishlistMutation;
|
12
framework/kibocommerce/api/mutations/addToCart-mutation.ts
Normal file
12
framework/kibocommerce/api/mutations/addToCart-mutation.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { cartItemDetails } from './../fragments/cartItemDetails'
|
||||||
|
|
||||||
|
const addToCurrentCartMutation = /*GraphQL*/ `
|
||||||
|
${cartItemDetails}
|
||||||
|
|
||||||
|
mutation addToCart($productToAdd:CartItemInput!){
|
||||||
|
addItemToCurrentCart(cartItemInput: $productToAdd) {
|
||||||
|
...cartItemDetails
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
export default addToCurrentCartMutation
|
@ -0,0 +1,11 @@
|
|||||||
|
const createWishlist = /*GraphQL*/`
|
||||||
|
mutation createWishlist($wishlistInput:WishlistInput!) {
|
||||||
|
createWishlist(wishlistInput:$wishlistInput){
|
||||||
|
id
|
||||||
|
name
|
||||||
|
customerAccountId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default createWishlist;
|
20
framework/kibocommerce/api/mutations/login-mutation.ts
Normal file
20
framework/kibocommerce/api/mutations/login-mutation.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
export const loginMutation = /* GraphQL */`
|
||||||
|
mutation login($loginInput:CustomerUserAuthInfoInput!) {
|
||||||
|
account:createCustomerAuthTicket(customerUserAuthInfoInput:$loginInput) {
|
||||||
|
accessToken
|
||||||
|
userId
|
||||||
|
refreshToken
|
||||||
|
refreshTokenExpiration
|
||||||
|
accessTokenExpiration
|
||||||
|
customerAccount {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
emailAddress
|
||||||
|
userName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Delete cart based on current user session
|
||||||
|
*/
|
||||||
|
const removeItemFromCartMutation = /*GraphQL*/`
|
||||||
|
mutation deleteCartItem($id: String!) {
|
||||||
|
deleteCurrentCartItem(cartItemId:$id)
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export default removeItemFromCartMutation;
|
@ -0,0 +1,8 @@
|
|||||||
|
const removeItemFromWishlistMutation = /* GraphQL */`
|
||||||
|
mutation deletewishlistitem($wishlistId: String!, $wishlistItemId: String!) {
|
||||||
|
deleteWishlistItem(wishlistId: $wishlistId, wishlistItemId:$wishlistItemId)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default removeItemFromWishlistMutation;
|
||||||
|
|
41
framework/kibocommerce/api/mutations/signup-mutation.ts
Normal file
41
framework/kibocommerce/api/mutations/signup-mutation.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
const registerUserMutation = /* GraphQL */`
|
||||||
|
mutation registerUser($customerAccountInput: CustomerAccountInput!) {
|
||||||
|
account:createCustomerAccount(customerAccountInput:$customerAccountInput) {
|
||||||
|
emailAddress
|
||||||
|
userName
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
localeCode
|
||||||
|
userId
|
||||||
|
id
|
||||||
|
isAnonymous
|
||||||
|
attributes {
|
||||||
|
values
|
||||||
|
fullyQualifiedName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const registerUserLoginMutation = /* GraphQL */`
|
||||||
|
mutation registerUserLogin($accountId: Int!, $customerLoginInfoInput: CustomerLoginInfoInput!) {
|
||||||
|
account:createCustomerAccountLogin(accountId:$accountId, customerLoginInfoInput:$customerLoginInfoInput) {
|
||||||
|
accessToken
|
||||||
|
accessTokenExpiration
|
||||||
|
refreshToken
|
||||||
|
refreshTokenExpiration
|
||||||
|
userId
|
||||||
|
customerAccount {
|
||||||
|
id
|
||||||
|
emailAddress
|
||||||
|
firstName
|
||||||
|
userName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export {
|
||||||
|
registerUserMutation,
|
||||||
|
registerUserLoginMutation
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
const updateCartItemQuantityMutation = /*GraphQL*/`
|
||||||
|
mutation updateCartItemQuantity($itemId:String!, $quantity: Int!){
|
||||||
|
updateCurrentCartItemQuantity(cartItemId:$itemId, quantity:$quantity){
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export default updateCartItemQuantityMutation;
|
38
framework/kibocommerce/api/operations/get-all-pages.ts
Normal file
38
framework/kibocommerce/api/operations/get-all-pages.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { OperationContext } from '@commerce/api/operations'
|
||||||
|
import type { KiboCommerceConfig } from '../index'
|
||||||
|
import { getAllPagesQuery } from '../queries/get-all-pages-query'
|
||||||
|
import { GetPagesQueryParams } from "../../types/page";
|
||||||
|
import { normalizePage } from '../../lib/normalize'
|
||||||
|
|
||||||
|
export type GetAllPagesResult<
|
||||||
|
T extends { pages: any[] } = { pages: any[] }
|
||||||
|
> = T
|
||||||
|
|
||||||
|
export default function getAllPagesOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<any>) {
|
||||||
|
|
||||||
|
async function getAllPages({
|
||||||
|
query = getAllPagesQuery,
|
||||||
|
config,
|
||||||
|
variables,
|
||||||
|
}: {
|
||||||
|
url?: string
|
||||||
|
config?: Partial<KiboCommerceConfig>
|
||||||
|
variables?: GetPagesQueryParams
|
||||||
|
preview?: boolean
|
||||||
|
query?: string
|
||||||
|
} = {}): Promise<GetAllPagesResult> {
|
||||||
|
const cfg = commerce.getConfig(config)
|
||||||
|
variables = {
|
||||||
|
documentListName: cfg.documentListName
|
||||||
|
}
|
||||||
|
const { data } = await cfg.fetch(query, { variables });
|
||||||
|
|
||||||
|
const pages = data.documentListDocuments.items.map(normalizePage);
|
||||||
|
|
||||||
|
return { pages }
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllPages
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { KiboCommerceConfig } from '../index'
|
||||||
|
import { getAllProductsQuery } from '../queries/get-all-products-query';
|
||||||
|
import { normalizeProduct } from '../../lib/normalize'
|
||||||
|
|
||||||
|
export type GetAllProductPathsResult = {
|
||||||
|
products: Array<{ path: string }>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getAllProductPathsOperation({commerce,}: any) {
|
||||||
|
async function getAllProductPaths({ config }: {config?: KiboCommerceConfig } = {}): Promise<GetAllProductPathsResult> {
|
||||||
|
|
||||||
|
const cfg = commerce.getConfig(config)
|
||||||
|
|
||||||
|
const productVariables = {startIndex: 0, pageSize: 100};
|
||||||
|
const { data } = await cfg.fetch(getAllProductsQuery, { variables: productVariables });
|
||||||
|
|
||||||
|
const normalizedProducts = data.products.items ? data.products.items.map( (item:any) => normalizeProduct(item, cfg)) : [];
|
||||||
|
const products = normalizedProducts.map((product: any) => ({ path: product.path }))
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
products: products
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllProductPaths
|
||||||
|
}
|
32
framework/kibocommerce/api/operations/get-all-products.ts
Normal file
32
framework/kibocommerce/api/operations/get-all-products.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Product } from '@commerce/types/product'
|
||||||
|
import { GetAllProductsOperation } from '@commerce/types/product'
|
||||||
|
import type { OperationContext } from '@commerce/api/operations'
|
||||||
|
import type { KiboCommerceConfig } from '../index'
|
||||||
|
import { getAllProductsQuery } from '../queries/get-all-products-query';
|
||||||
|
import { normalizeProduct } from '../../lib/normalize'
|
||||||
|
|
||||||
|
export default function getAllProductsOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<any>) {
|
||||||
|
async function getAllProducts<T extends GetAllProductsOperation>({
|
||||||
|
query = getAllProductsQuery,
|
||||||
|
variables,
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: Partial<KiboCommerceConfig>
|
||||||
|
preview?: boolean
|
||||||
|
} = {}): Promise<{ products: Product[] | any[] }> {
|
||||||
|
|
||||||
|
const cfg = commerce.getConfig(config)
|
||||||
|
const { data } = await cfg.fetch(query);
|
||||||
|
|
||||||
|
let normalizedProducts = data.products.items ? data.products.items.map( (item:any) => normalizeProduct(item, cfg)) : [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
products: normalizedProducts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getAllProducts
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
import type {
|
||||||
|
OperationContext,
|
||||||
|
OperationOptions,
|
||||||
|
} from '@commerce/api/operations'
|
||||||
|
import type {
|
||||||
|
GetCustomerWishlistOperation,
|
||||||
|
Wishlist,
|
||||||
|
} from '@commerce/types/wishlist'
|
||||||
|
// import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||||
|
import { KiboCommerceConfig } from '..'
|
||||||
|
// import getAllProducts, { ProductEdge } from './get-all-products'
|
||||||
|
import {getCustomerWishlistQuery} from '../queries/get-customer-wishlist-query'
|
||||||
|
|
||||||
|
export default function getCustomerWishlistOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<any>) {
|
||||||
|
async function getCustomerWishlist<
|
||||||
|
T extends GetCustomerWishlistOperation
|
||||||
|
>(opts: {
|
||||||
|
variables: T['variables']
|
||||||
|
config?: KiboCommerceConfig
|
||||||
|
includeProducts?: boolean
|
||||||
|
}): Promise<T['data']>
|
||||||
|
|
||||||
|
async function getCustomerWishlist<T extends GetCustomerWishlistOperation>(
|
||||||
|
opts: {
|
||||||
|
variables: T['variables']
|
||||||
|
config?: KiboCommerceConfig
|
||||||
|
includeProducts?: boolean
|
||||||
|
} & OperationOptions
|
||||||
|
): Promise<T['data']>
|
||||||
|
|
||||||
|
async function getCustomerWishlist<T extends GetCustomerWishlistOperation>({
|
||||||
|
config,
|
||||||
|
variables,
|
||||||
|
includeProducts,
|
||||||
|
}: {
|
||||||
|
url?: string
|
||||||
|
variables: T['variables']
|
||||||
|
config?: KiboCommerceConfig
|
||||||
|
includeProducts?: boolean
|
||||||
|
}): Promise<T['data']> {
|
||||||
|
let customerWishlist ={}
|
||||||
|
try {
|
||||||
|
|
||||||
|
config = commerce.getConfig(config)
|
||||||
|
const result= await config?.fetch(getCustomerWishlistQuery,{variables})
|
||||||
|
customerWishlist= result?.data?.customerWishlist;
|
||||||
|
} catch(e) {
|
||||||
|
customerWishlist= {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { wishlist: customerWishlist as any }
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCustomerWishlist
|
||||||
|
}
|
40
framework/kibocommerce/api/operations/get-page.ts
Normal file
40
framework/kibocommerce/api/operations/get-page.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import type {
|
||||||
|
OperationContext,
|
||||||
|
} from '@commerce/api/operations'
|
||||||
|
import type { KiboCommerceConfig, KiboCommerceProvider } from '..'
|
||||||
|
import { normalizePage } from '../../lib/normalize'
|
||||||
|
import { getPageQuery } from '../queries/get-page-query'
|
||||||
|
import type { Page, GetPageQueryParams } from "../../types/page";
|
||||||
|
import type { Document } from '../../schema'
|
||||||
|
|
||||||
|
export default function getPageOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<any>) {
|
||||||
|
async function getPage<T extends Page>({
|
||||||
|
url,
|
||||||
|
variables,
|
||||||
|
config,
|
||||||
|
preview,
|
||||||
|
}: {
|
||||||
|
url?: string
|
||||||
|
variables: GetPageQueryParams
|
||||||
|
config?: Partial<KiboCommerceConfig>
|
||||||
|
preview?: boolean
|
||||||
|
}): Promise<any> {
|
||||||
|
// RecursivePartial forces the method to check for every prop in the data, which is
|
||||||
|
// required in case there's a custom `url`
|
||||||
|
const cfg = commerce.getConfig(config)
|
||||||
|
const pageVariables = { documentListName: cfg.documentListName, filter: `id eq ${variables.id}` }
|
||||||
|
|
||||||
|
const { data } = await cfg.fetch(getPageQuery, { variables: pageVariables })
|
||||||
|
|
||||||
|
const firstPage = data.documentListDocuments.items?.[0];
|
||||||
|
const page = firstPage as Document
|
||||||
|
if (preview || page?.properties?.is_visible) {
|
||||||
|
return { page: normalizePage(page as any) }
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getPage
|
||||||
|
}
|
35
framework/kibocommerce/api/operations/get-product.ts
Normal file
35
framework/kibocommerce/api/operations/get-product.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import type { KiboCommerceConfig } from '../index'
|
||||||
|
import { Product } from '@commerce/types/product'
|
||||||
|
import { GetProductOperation } from '@commerce/types/product'
|
||||||
|
import type { OperationContext } from '@commerce/api/operations'
|
||||||
|
import { getProductQuery } from '../queries/get-product-query'
|
||||||
|
import { normalizeProduct } from '../../lib/normalize'
|
||||||
|
|
||||||
|
export default function getProductOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<any>) {
|
||||||
|
|
||||||
|
async function getProduct<T extends GetProductOperation>({
|
||||||
|
query = getProductQuery,
|
||||||
|
variables,
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: Partial<KiboCommerceConfig>
|
||||||
|
preview?: boolean
|
||||||
|
} = {}): Promise<Product | {} | any> {
|
||||||
|
const productVariables = { productCode: variables?.slug}
|
||||||
|
|
||||||
|
const cfg = commerce.getConfig(config)
|
||||||
|
const { data } = await cfg.fetch(query, { variables: productVariables });
|
||||||
|
|
||||||
|
const normalizedProduct = normalizeProduct(data.product, cfg)
|
||||||
|
|
||||||
|
return {
|
||||||
|
product: normalizedProduct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getProduct
|
||||||
|
}
|
35
framework/kibocommerce/api/operations/get-site-info.ts
Normal file
35
framework/kibocommerce/api/operations/get-site-info.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { OperationContext } from '@commerce/api/operations'
|
||||||
|
import { Category } from '@commerce/types/site'
|
||||||
|
import { KiboCommerceConfig } from '../index'
|
||||||
|
import {categoryTreeQuery} from '../queries/get-categories-tree-query'
|
||||||
|
import { normalizeCategory } from '../../lib/normalize'
|
||||||
|
|
||||||
|
export type GetSiteInfoResult<
|
||||||
|
T extends { categories: any[]; brands: any[] } = {
|
||||||
|
categories: Category[]
|
||||||
|
brands: any[]
|
||||||
|
}
|
||||||
|
> = T
|
||||||
|
|
||||||
|
export default function getSiteInfoOperation({commerce}: OperationContext<any>) {
|
||||||
|
async function getSiteInfo({
|
||||||
|
query= categoryTreeQuery,
|
||||||
|
variables,
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: any
|
||||||
|
config?: Partial<KiboCommerceConfig>
|
||||||
|
preview?: boolean
|
||||||
|
} = {}): Promise<GetSiteInfoResult> {
|
||||||
|
const cfg = commerce.getConfig(config)
|
||||||
|
const { data } = await cfg.fetch(query);
|
||||||
|
const categories= data.categories.items.map(normalizeCategory);
|
||||||
|
return Promise.resolve({
|
||||||
|
categories: categories ?? [],
|
||||||
|
brands: [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSiteInfo
|
||||||
|
}
|
6
framework/kibocommerce/api/operations/index.ts
Normal file
6
framework/kibocommerce/api/operations/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export { default as getPage } from './get-page'
|
||||||
|
export { default as getSiteInfo } from './get-site-info'
|
||||||
|
export { default as getAllPages } from './get-all-pages'
|
||||||
|
export { default as getProduct } from './get-product'
|
||||||
|
export { default as getAllProducts } from './get-all-products'
|
||||||
|
export { default as getAllProductPaths } from './get-all-product-paths'
|
11
framework/kibocommerce/api/queries/get-all-pages-query.ts
Normal file
11
framework/kibocommerce/api/queries/get-all-pages-query.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const getAllPagesQuery = /* GraphQL */`
|
||||||
|
query($documentListName: String!) {
|
||||||
|
documentListDocuments(documentListName:$documentListName){
|
||||||
|
items {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
listFQN
|
||||||
|
properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
21
framework/kibocommerce/api/queries/get-all-products-query.ts
Normal file
21
framework/kibocommerce/api/queries/get-all-products-query.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { productInfo } from '../fragments/product';
|
||||||
|
|
||||||
|
export const getAllProductsQuery = /* GraphQL */`
|
||||||
|
${productInfo}
|
||||||
|
|
||||||
|
query products(
|
||||||
|
$filter: String
|
||||||
|
$startIndex: Int
|
||||||
|
$pageSize: Int
|
||||||
|
) {
|
||||||
|
products(
|
||||||
|
filter: $filter
|
||||||
|
startIndex: $startIndex
|
||||||
|
pageSize: $pageSize
|
||||||
|
) {
|
||||||
|
items {
|
||||||
|
...productInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@ -0,0 +1,11 @@
|
|||||||
|
export const getAnonymousShopperTokenQuery = /* GraphQL */ `
|
||||||
|
query {
|
||||||
|
getAnonymousShopperToken {
|
||||||
|
accessToken
|
||||||
|
accessTokenExpiration
|
||||||
|
refreshToken
|
||||||
|
refreshTokenExpiration
|
||||||
|
jwtAccessToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
32
framework/kibocommerce/api/queries/get-cart-query.ts
Normal file
32
framework/kibocommerce/api/queries/get-cart-query.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { productDetails } from '../fragments/productDetails'
|
||||||
|
export const getCartQuery = /* GraphQL */`
|
||||||
|
query cart {
|
||||||
|
currentCart {
|
||||||
|
id
|
||||||
|
userId
|
||||||
|
orderDiscounts {
|
||||||
|
impact
|
||||||
|
discount {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
couponCode
|
||||||
|
}
|
||||||
|
subtotal
|
||||||
|
shippingTotal
|
||||||
|
total
|
||||||
|
items {
|
||||||
|
id
|
||||||
|
subtotal
|
||||||
|
unitPrice{
|
||||||
|
extendedAmount
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
...productDetails
|
||||||
|
}
|
||||||
|
quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${productDetails}
|
||||||
|
`
|
@ -0,0 +1,29 @@
|
|||||||
|
import { CategoryInfo } from '../fragments/category'
|
||||||
|
|
||||||
|
export const categoryTreeQuery = /* GraphQL */`
|
||||||
|
query GetCategoryTree {
|
||||||
|
categories: categoriesTree {
|
||||||
|
items {
|
||||||
|
...categoryInfo
|
||||||
|
childrenCategories {
|
||||||
|
...categoryInfo
|
||||||
|
childrenCategories {
|
||||||
|
...categoryInfo
|
||||||
|
childrenCategories {
|
||||||
|
...categoryInfo
|
||||||
|
childrenCategories {
|
||||||
|
...categoryInfo
|
||||||
|
childrenCategories {
|
||||||
|
...categoryInfo
|
||||||
|
childrenCategories {
|
||||||
|
...categoryInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${CategoryInfo}`;
|
@ -0,0 +1,12 @@
|
|||||||
|
export const getCustomerAccountQuery = /* GraphQL */`
|
||||||
|
query getUser {
|
||||||
|
customerAccount:getCurrentAccount {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
emailAddress
|
||||||
|
userName
|
||||||
|
isAnonymous
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@ -0,0 +1,25 @@
|
|||||||
|
import {productDetails} from '../fragments/productDetails'
|
||||||
|
export const getCustomerWishlistQuery= /* GraphQL */`
|
||||||
|
query wishlist($customerId: Int!, $wishlistName: String!) {
|
||||||
|
customerWishlist(customerAccountId:$customerId ,wishlistName: $wishlistName){
|
||||||
|
customerAccountId
|
||||||
|
name
|
||||||
|
id
|
||||||
|
userId
|
||||||
|
items {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
total
|
||||||
|
subtotal
|
||||||
|
unitPrice{
|
||||||
|
extendedAmount
|
||||||
|
}
|
||||||
|
quantity
|
||||||
|
product {
|
||||||
|
...productDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${productDetails}
|
||||||
|
`
|
14
framework/kibocommerce/api/queries/get-page-query.ts
Normal file
14
framework/kibocommerce/api/queries/get-page-query.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export const getPageQuery = /* GraphQL */`
|
||||||
|
query($documentListName: String!, $filter: String!) {
|
||||||
|
documentListDocuments(documentListName: $documentListName, filter: $filter){
|
||||||
|
startIndex
|
||||||
|
totalCount
|
||||||
|
items {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
listFQN
|
||||||
|
properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
15
framework/kibocommerce/api/queries/get-product-query.ts
Normal file
15
framework/kibocommerce/api/queries/get-product-query.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { productInfo } from '../fragments/product';
|
||||||
|
|
||||||
|
export const getProductQuery = /* GraphQL */`
|
||||||
|
${productInfo}
|
||||||
|
|
||||||
|
query product(
|
||||||
|
$productCode: String!
|
||||||
|
) {
|
||||||
|
product(
|
||||||
|
productCode: $productCode
|
||||||
|
) {
|
||||||
|
...productInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
20
framework/kibocommerce/api/queries/product-search-query.ts
Normal file
20
framework/kibocommerce/api/queries/product-search-query.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { searchResults } from '../fragments/search'
|
||||||
|
|
||||||
|
const query = /* GraphQL */`
|
||||||
|
query ProductSearch($query:String, $startIndex:Int,
|
||||||
|
$pageSize:Int, $sortBy:String, $filter:String,$facetTemplate:String,$facetValueFilter:String ) {
|
||||||
|
products:productSearch (
|
||||||
|
query:$query,
|
||||||
|
startIndex: $startIndex,
|
||||||
|
pageSize:$pageSize,
|
||||||
|
sortBy: $sortBy,
|
||||||
|
filter:$filter,
|
||||||
|
facetTemplate:$facetTemplate,
|
||||||
|
facetValueFilter:$facetValueFilter
|
||||||
|
) {
|
||||||
|
...searchResults
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${searchResults}
|
||||||
|
`;
|
||||||
|
export default query;
|
110
framework/kibocommerce/api/utils/api-auth-helper.ts
Normal file
110
framework/kibocommerce/api/utils/api-auth-helper.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import getNextConfig from 'next/config'
|
||||||
|
import type { KiboCommerceConfig } from '../index'
|
||||||
|
import type { FetchOptions } from '@vercel/fetch'
|
||||||
|
import fetch from './fetch'
|
||||||
|
|
||||||
|
interface AppAuthTicket {
|
||||||
|
access_token: string
|
||||||
|
token_type: string
|
||||||
|
expires_in: number
|
||||||
|
expires_at: number
|
||||||
|
refresh_token: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuthTicketCache {
|
||||||
|
getAuthTicket: () => Promise<AppAuthTicket>
|
||||||
|
setAuthTicket: (kiboAuthTicket: AppAuthTicket) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
class RuntimeMemCache implements AuthTicketCache {
|
||||||
|
constructor() {}
|
||||||
|
async getAuthTicket() {
|
||||||
|
const { serverRuntimeConfig } = getNextConfig()
|
||||||
|
return serverRuntimeConfig.kiboAuthTicket
|
||||||
|
}
|
||||||
|
setAuthTicket(kiboAuthTicket: AppAuthTicket) {
|
||||||
|
const { serverRuntimeConfig } = getNextConfig()
|
||||||
|
serverRuntimeConfig.kiboAuthTicket = kiboAuthTicket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class APIAuthenticationHelper {
|
||||||
|
private _clientId: string
|
||||||
|
private _sharedSecret: string
|
||||||
|
private _authUrl: string
|
||||||
|
private _authTicketCache!: AuthTicketCache
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
{ clientId = '', sharedSecret = '', authUrl = '' }: KiboCommerceConfig,
|
||||||
|
authTicketCache?: AuthTicketCache
|
||||||
|
) {
|
||||||
|
this._clientId = clientId
|
||||||
|
this._sharedSecret = sharedSecret
|
||||||
|
this._authUrl = authUrl
|
||||||
|
if(!authTicketCache) {
|
||||||
|
this._authTicketCache = new RuntimeMemCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _buildFetchOptions(body: any = {}): FetchOptions {
|
||||||
|
return {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _calculateTicketExpiration(kiboAuthTicket: AppAuthTicket) {
|
||||||
|
//calculate how many milliseconds until auth expires
|
||||||
|
const millisecsUntilExpiration = kiboAuthTicket.expires_in * 1000
|
||||||
|
kiboAuthTicket.expires_at = Date.now() + millisecsUntilExpiration
|
||||||
|
|
||||||
|
return kiboAuthTicket
|
||||||
|
}
|
||||||
|
public async authenticate(): Promise<AppAuthTicket> {
|
||||||
|
// create oauth fetch options
|
||||||
|
const options = this._buildFetchOptions({
|
||||||
|
client_id: this._clientId,
|
||||||
|
client_secret: this._sharedSecret,
|
||||||
|
grant_type: 'client_credentials',
|
||||||
|
})
|
||||||
|
// perform authentication
|
||||||
|
const authTicket = await fetch(
|
||||||
|
`${this._authUrl}/api/platform/applications/authtickets/oauth`,
|
||||||
|
options
|
||||||
|
).then((response) => response.json())
|
||||||
|
// set expiration time in ms on auth ticket
|
||||||
|
this._calculateTicketExpiration(authTicket)
|
||||||
|
// set authentication ticket on next server runtime object
|
||||||
|
this._authTicketCache.setAuthTicket(authTicket)
|
||||||
|
|
||||||
|
return authTicket
|
||||||
|
}
|
||||||
|
public async refreshTicket(kiboAuthTicket: AppAuthTicket) {
|
||||||
|
// create oauth refresh fetch options
|
||||||
|
const options = this._buildFetchOptions({
|
||||||
|
refreshToken: kiboAuthTicket?.refresh_token,
|
||||||
|
})
|
||||||
|
// perform auth ticket refresh
|
||||||
|
const refreshedTicket = await fetch(
|
||||||
|
`${this._authUrl}/api/platform/applications/authtickets/refresh-ticket`,
|
||||||
|
options
|
||||||
|
).then((response) => response.json())
|
||||||
|
|
||||||
|
return refreshedTicket
|
||||||
|
}
|
||||||
|
public async getAccessToken(): Promise<string> {
|
||||||
|
// get current Kibo API auth ticket
|
||||||
|
let authTicket = await this._authTicketCache.getAuthTicket()
|
||||||
|
|
||||||
|
// if no current ticket, perform auth
|
||||||
|
// or if ticket expired, refresh auth
|
||||||
|
if (!authTicket) {
|
||||||
|
authTicket = await this.authenticate()
|
||||||
|
} else if (authTicket.expires_at < Date.now()) {
|
||||||
|
authTicket = await this.refreshTicket(authTicket)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authTicket.access_token
|
||||||
|
}
|
||||||
|
}
|
52
framework/kibocommerce/api/utils/cookie-handler.ts
Normal file
52
framework/kibocommerce/api/utils/cookie-handler.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { KiboCommerceConfig } from './../index'
|
||||||
|
import { getCookieExpirationDate } from '../../lib/get-cookie-expiration-date'
|
||||||
|
import { prepareSetCookie } from '../../lib/prepare-set-cookie'
|
||||||
|
import { setCookies } from '../../lib/set-cookie'
|
||||||
|
import { NextApiRequest } from 'next'
|
||||||
|
import getAnonymousShopperToken from './get-anonymous-shopper-token'
|
||||||
|
|
||||||
|
export default class CookieHandler {
|
||||||
|
config: KiboCommerceConfig
|
||||||
|
request: NextApiRequest
|
||||||
|
response: any
|
||||||
|
accessToken: any
|
||||||
|
constructor(config: any, req: NextApiRequest, res: any) {
|
||||||
|
this.config = config
|
||||||
|
this.request = req
|
||||||
|
this.response = res
|
||||||
|
const encodedToken = req.cookies[config.customerCookie]
|
||||||
|
const token = encodedToken
|
||||||
|
? JSON.parse(Buffer.from(encodedToken, 'base64').toString('ascii'))
|
||||||
|
: null
|
||||||
|
this.accessToken = token ? token.accessToken : null
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAnonymousToken() {
|
||||||
|
const response: any = await getAnonymousShopperToken({
|
||||||
|
config: this.config,
|
||||||
|
})
|
||||||
|
let anonymousAccessToken = response?.accessToken
|
||||||
|
return {
|
||||||
|
response,
|
||||||
|
accessToken: anonymousAccessToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAnonymousShopperCookie(anonymousShopperTokenResponse: any) {
|
||||||
|
const cookieExpirationDate = getCookieExpirationDate(
|
||||||
|
this.config.customerCookieMaxAgeInDays
|
||||||
|
)
|
||||||
|
|
||||||
|
const authCookie = prepareSetCookie(
|
||||||
|
this.config.customerCookie,
|
||||||
|
JSON.stringify(anonymousShopperTokenResponse),
|
||||||
|
anonymousShopperTokenResponse?.accessTokenExpiration
|
||||||
|
? { expires: cookieExpirationDate }
|
||||||
|
: {}
|
||||||
|
)
|
||||||
|
setCookies(this.response, [authCookie])
|
||||||
|
}
|
||||||
|
getAccessToken() {
|
||||||
|
return this.accessToken
|
||||||
|
}
|
||||||
|
}
|
43
framework/kibocommerce/api/utils/fetch-graphql-api.ts
Normal file
43
framework/kibocommerce/api/utils/fetch-graphql-api.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { FetcherError } from '@commerce/utils/errors'
|
||||||
|
import type { GraphQLFetcher } from '@commerce/api'
|
||||||
|
import type { KiboCommerceConfig } from '../index'
|
||||||
|
import fetch from './fetch'
|
||||||
|
import { APIAuthenticationHelper } from './api-auth-helper';
|
||||||
|
|
||||||
|
const fetchGraphqlApi: (
|
||||||
|
getConfig: () => KiboCommerceConfig
|
||||||
|
) => GraphQLFetcher = (getConfig) => async (
|
||||||
|
query: string,
|
||||||
|
{ variables, preview } = {},
|
||||||
|
fetchOptions
|
||||||
|
) => {
|
||||||
|
const config = getConfig()
|
||||||
|
const authHelper = new APIAuthenticationHelper(config);
|
||||||
|
const apiToken = await authHelper.getAccessToken();
|
||||||
|
const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
|
||||||
|
...fetchOptions,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
...fetchOptions?.headers,
|
||||||
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: json.data, res }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fetchGraphqlApi
|
36
framework/kibocommerce/api/utils/fetch-local.ts
Normal file
36
framework/kibocommerce/api/utils/fetch-local.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { FetcherError } from '@commerce/utils/errors'
|
||||||
|
import type { GraphQLFetcher } from '@commerce/api'
|
||||||
|
import type { KiboCommerceConfig } from '../index'
|
||||||
|
import fetch from './fetch'
|
||||||
|
|
||||||
|
const fetchGraphqlApi: (getConfig: () => KiboCommerceConfig) => GraphQLFetcher =
|
||||||
|
(getConfig) =>
|
||||||
|
async (query: string, { variables, preview } = {}, fetchOptions) => {
|
||||||
|
const config = getConfig()
|
||||||
|
const res = await fetch(config.commerceUrl, {
|
||||||
|
//const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
|
||||||
|
...fetchOptions,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${config.apiToken}`,
|
||||||
|
...fetchOptions?.headers,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
query,
|
||||||
|
variables,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const json = await res.json()
|
||||||
|
if (json.errors) {
|
||||||
|
throw new FetcherError({
|
||||||
|
errors: json.errors ?? [{ message: 'Failed to fetch KiboCommerce API' }],
|
||||||
|
status: res.status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: json.data, res }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fetchGraphqlApi
|
3
framework/kibocommerce/api/utils/fetch.ts
Normal file
3
framework/kibocommerce/api/utils/fetch.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import zeitFetch from '@vercel/fetch'
|
||||||
|
|
||||||
|
export default zeitFetch()
|
@ -0,0 +1,13 @@
|
|||||||
|
import type { KiboCommerceConfig } from '../'
|
||||||
|
import { getAnonymousShopperTokenQuery } from '../queries/get-anonymous-shopper-token-query'
|
||||||
|
|
||||||
|
async function getAnonymousShopperToken({
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
config: KiboCommerceConfig
|
||||||
|
}): Promise<string | undefined> {
|
||||||
|
const { data } = await config.fetch(getAnonymousShopperTokenQuery)
|
||||||
|
return data?.getAnonymousShopperToken
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getAnonymousShopperToken
|
26
framework/kibocommerce/api/utils/get-customer-id.ts
Normal file
26
framework/kibocommerce/api/utils/get-customer-id.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import type { KiboCommerceConfig } from '..'
|
||||||
|
import { getCustomerAccountQuery } from '../queries/get-customer-account-query'
|
||||||
|
|
||||||
|
async function getCustomerId({
|
||||||
|
customerToken,
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
customerToken: string
|
||||||
|
config: KiboCommerceConfig
|
||||||
|
}): Promise<string | undefined> {
|
||||||
|
const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null;
|
||||||
|
const accessToken = token ? JSON.parse(token).accessToken : null;
|
||||||
|
const { data } = await config.fetch(
|
||||||
|
getCustomerAccountQuery,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'x-vol-user-claims': accessToken,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return data?.customerAccount?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getCustomerId
|
3
framework/kibocommerce/auth/index.ts
Normal file
3
framework/kibocommerce/auth/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { default as useLogin } from './use-login'
|
||||||
|
export { default as useLogout } from './use-logout'
|
||||||
|
export { default as useSignup } from './use-signup'
|
42
framework/kibocommerce/auth/use-login.tsx
Normal file
42
framework/kibocommerce/auth/use-login.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useLogin, { UseLogin } from '@commerce/auth/use-login'
|
||||||
|
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import { CommerceError } from '@commerce/utils/errors'
|
||||||
|
import type { LoginHook } from '../types/login'
|
||||||
|
import useCustomer from '../customer/use-customer'
|
||||||
|
import useCart from '../cart/use-cart'
|
||||||
|
export default useLogin as UseLogin<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<LoginHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
url: '/api/login',
|
||||||
|
method: 'POST'
|
||||||
|
},
|
||||||
|
async fetcher({ input: { email, password }, options, fetch }) {
|
||||||
|
if (!(email && password)) {
|
||||||
|
throw new CommerceError({
|
||||||
|
message:
|
||||||
|
'An email and password are required to login',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch({
|
||||||
|
...options,
|
||||||
|
body: { email, password },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
useHook: ({ fetch }) => () => {
|
||||||
|
const { revalidate } = useCustomer()
|
||||||
|
const {revalidate: revalidateCart} = useCart()
|
||||||
|
return useCallback(
|
||||||
|
async function login(input) {
|
||||||
|
const data = await fetch({ input })
|
||||||
|
await revalidate()
|
||||||
|
await revalidateCart()
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
[fetch, revalidate, revalidateCart]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
29
framework/kibocommerce/auth/use-logout.tsx
Normal file
29
framework/kibocommerce/auth/use-logout.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import type { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
|
||||||
|
import type { LogoutHook } from '../types/logout'
|
||||||
|
import useCustomer from '../customer/use-customer'
|
||||||
|
import useCart from '../cart/use-cart'
|
||||||
|
|
||||||
|
export default useLogout as UseLogout<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<LogoutHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
url: '/api/logout',
|
||||||
|
method: 'GET',
|
||||||
|
},
|
||||||
|
useHook: ({ fetch }) => () => {
|
||||||
|
const { mutate } = useCustomer()
|
||||||
|
const { mutate: mutateCart } = useCart()
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
async function logout() {
|
||||||
|
const data = await fetch()
|
||||||
|
await mutate(null, false)
|
||||||
|
await mutateCart(null, false)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
[fetch, mutate, mutateCart]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
44
framework/kibocommerce/auth/use-signup.tsx
Normal file
44
framework/kibocommerce/auth/use-signup.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import type { MutationHook } from '@commerce/utils/types'
|
||||||
|
import { CommerceError } from '@commerce/utils/errors'
|
||||||
|
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
|
||||||
|
import type { SignupHook } from '../types/signup'
|
||||||
|
import useCustomer from '../customer/use-customer'
|
||||||
|
|
||||||
|
export default useSignup as UseSignup<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<SignupHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
url: '/api/signup',
|
||||||
|
method: 'POST',
|
||||||
|
},
|
||||||
|
async fetcher({
|
||||||
|
input: { firstName, lastName, email, password },
|
||||||
|
options,
|
||||||
|
fetch,
|
||||||
|
}) {
|
||||||
|
if (!(firstName && lastName && email && password)) {
|
||||||
|
throw new CommerceError({
|
||||||
|
message:
|
||||||
|
'A first name, last name, email and password are required to signup',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch({
|
||||||
|
...options,
|
||||||
|
body: { firstName, lastName, email, password },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
useHook: ({ fetch }) => () => {
|
||||||
|
const { revalidate } = useCustomer()
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
async function signup(input) {
|
||||||
|
const data = await fetch({ input })
|
||||||
|
await revalidate()
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
[fetch, revalidate]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
4
framework/kibocommerce/cart/index.ts
Normal file
4
framework/kibocommerce/cart/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { default as useCart } from './use-cart'
|
||||||
|
export { default as useAddItem } from './use-add-item'
|
||||||
|
export { default as useRemoveItem } from './use-remove-item'
|
||||||
|
export { default as useUpdateItem } from './use-update-item'
|
44
framework/kibocommerce/cart/use-add-item.tsx
Normal file
44
framework/kibocommerce/cart/use-add-item.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import type { MutationHook } from '@commerce/utils/types'
|
||||||
|
import { CommerceError } from '@commerce/utils/errors'
|
||||||
|
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
|
||||||
|
import type { AddItemHook } from '@commerce/types/cart'
|
||||||
|
import useCart from './use-cart'
|
||||||
|
|
||||||
|
export default useAddItem as UseAddItem<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<AddItemHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
url: '/api/cart',
|
||||||
|
method: 'POST',
|
||||||
|
},
|
||||||
|
async fetcher({ input: item, options, fetch }) {
|
||||||
|
if (
|
||||||
|
item.quantity &&
|
||||||
|
(!Number.isInteger(item.quantity) || item.quantity! < 1)
|
||||||
|
) {
|
||||||
|
throw new CommerceError({
|
||||||
|
message: 'The item quantity has to be a valid integer greater than 0',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await fetch({
|
||||||
|
...options,
|
||||||
|
body: { item },
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
useHook: ({ fetch }) => () => {
|
||||||
|
const { mutate } = useCart()
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
async function addItem(input) {
|
||||||
|
const data = await fetch({ input })
|
||||||
|
await mutate(data, false)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
[fetch, mutate]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
33
framework/kibocommerce/cart/use-cart.tsx
Normal file
33
framework/kibocommerce/cart/use-cart.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
|
import useCart, { UseCart } from '@commerce/cart/use-cart'
|
||||||
|
|
||||||
|
export default useCart as UseCart<typeof handler>
|
||||||
|
|
||||||
|
export const handler: SWRHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/cart',
|
||||||
|
},
|
||||||
|
async fetcher({ options, fetch }) {
|
||||||
|
return await fetch({ ...options })
|
||||||
|
},
|
||||||
|
useHook: ({ useData }) => (input) => {
|
||||||
|
const response = useData({
|
||||||
|
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
|
||||||
|
})
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
Object.create(response, {
|
||||||
|
isEmpty: {
|
||||||
|
get() {
|
||||||
|
return (response.data?.lineItems.length ?? 0) <= 0
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[response]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
56
framework/kibocommerce/cart/use-remove-item.tsx
Normal file
56
framework/kibocommerce/cart/use-remove-item.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import type {
|
||||||
|
MutationHookContext,
|
||||||
|
HookFetcherContext,
|
||||||
|
} from '@commerce/utils/types'
|
||||||
|
import { ValidationError } from '@commerce/utils/errors'
|
||||||
|
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
|
||||||
|
import type { Cart, LineItem, RemoveItemHook } from '@commerce/types/cart'
|
||||||
|
import useCart from './use-cart'
|
||||||
|
|
||||||
|
export type RemoveItemFn<T = any> = T extends LineItem
|
||||||
|
? (input?: RemoveItemActionInput<T>) => Promise<Cart | null | undefined>
|
||||||
|
: (input: RemoveItemActionInput<T>) => Promise<Cart | null>
|
||||||
|
|
||||||
|
export type RemoveItemActionInput<T = any> = T extends LineItem
|
||||||
|
? Partial<RemoveItemHook['actionInput']>
|
||||||
|
: RemoveItemHook['actionInput']
|
||||||
|
|
||||||
|
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||||
|
|
||||||
|
export const handler = {
|
||||||
|
fetchOptions: {
|
||||||
|
url: '/api/cart',
|
||||||
|
method: 'DELETE',
|
||||||
|
},
|
||||||
|
async fetcher({
|
||||||
|
input: { itemId },
|
||||||
|
options,
|
||||||
|
fetch,
|
||||||
|
}: HookFetcherContext<RemoveItemHook>) {
|
||||||
|
return await fetch({ ...options, body: { itemId } })
|
||||||
|
},
|
||||||
|
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => <
|
||||||
|
T extends LineItem | undefined = undefined
|
||||||
|
>(
|
||||||
|
ctx: { item?: T } = {}
|
||||||
|
) => {
|
||||||
|
const { item } = ctx
|
||||||
|
const { mutate } = useCart()
|
||||||
|
const removeItem: RemoveItemFn<LineItem> = async (input) => {
|
||||||
|
const itemId = input?.id ?? item?.id
|
||||||
|
|
||||||
|
if (!itemId) {
|
||||||
|
throw new ValidationError({
|
||||||
|
message: 'Invalid input used for this operation',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await fetch({ input: { itemId } })
|
||||||
|
await mutate(data, false)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
|
||||||
|
},
|
||||||
|
}
|
84
framework/kibocommerce/cart/use-update-item.tsx
Normal file
84
framework/kibocommerce/cart/use-update-item.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import debounce from 'lodash.debounce'
|
||||||
|
import type {
|
||||||
|
MutationHookContext,
|
||||||
|
HookFetcherContext,
|
||||||
|
} from '@commerce/utils/types'
|
||||||
|
import { ValidationError } from '@commerce/utils/errors'
|
||||||
|
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
|
||||||
|
import type { LineItem, UpdateItemHook } from '@commerce/types/cart'
|
||||||
|
import { handler as removeItemHandler } from './use-remove-item'
|
||||||
|
import useCart from './use-cart'
|
||||||
|
|
||||||
|
export type UpdateItemActionInput<T = any> = T extends LineItem
|
||||||
|
? Partial<UpdateItemHook['actionInput']>
|
||||||
|
: UpdateItemHook['actionInput']
|
||||||
|
|
||||||
|
export default useUpdateItem as UseUpdateItem<typeof handler>
|
||||||
|
|
||||||
|
export const handler = {
|
||||||
|
fetchOptions: {
|
||||||
|
url: '/api/cart',
|
||||||
|
method: 'PUT',
|
||||||
|
},
|
||||||
|
async fetcher({
|
||||||
|
input: { itemId, item },
|
||||||
|
options,
|
||||||
|
fetch,
|
||||||
|
}: HookFetcherContext<UpdateItemHook>) {
|
||||||
|
if (Number.isInteger(item.quantity)) {
|
||||||
|
// Also allow the update hook to remove an item if the quantity is lower than 1
|
||||||
|
if (item.quantity! < 1) {
|
||||||
|
return removeItemHandler.fetcher({
|
||||||
|
options: removeItemHandler.fetchOptions,
|
||||||
|
input: { itemId },
|
||||||
|
fetch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (item.quantity) {
|
||||||
|
throw new ValidationError({
|
||||||
|
message: 'The item quantity has to be a valid integer',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return await fetch({
|
||||||
|
...options,
|
||||||
|
body: { itemId, item },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => <
|
||||||
|
T extends LineItem | undefined = undefined
|
||||||
|
>(
|
||||||
|
ctx: {
|
||||||
|
item?: T
|
||||||
|
wait?: number
|
||||||
|
} = {}
|
||||||
|
) => {
|
||||||
|
const { item } = ctx
|
||||||
|
const { mutate } = useCart() as any
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
debounce(async (input: UpdateItemActionInput<T>) => {
|
||||||
|
const itemId = input.id ?? item?.id
|
||||||
|
const productId = input.productId ?? item?.productId
|
||||||
|
const variantId = input.productId ?? item?.variantId
|
||||||
|
|
||||||
|
if (!itemId || !productId || !variantId) {
|
||||||
|
throw new ValidationError({
|
||||||
|
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
|
||||||
|
}, ctx.wait ?? 500),
|
||||||
|
[fetch, mutate]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
14
framework/kibocommerce/checkout/use-checkout.tsx
Normal file
14
framework/kibocommerce/checkout/use-checkout.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
|
import useCheckout, { UseCheckout } from '@commerce/checkout/use-checkout'
|
||||||
|
|
||||||
|
export default useCheckout as UseCheckout<typeof handler>
|
||||||
|
|
||||||
|
export const handler: SWRHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {},
|
||||||
|
useHook:
|
||||||
|
({ useData }) =>
|
||||||
|
async (input) => ({}),
|
||||||
|
}
|
23
framework/kibocommerce/codegen.json
Normal file
23
framework/kibocommerce/codegen.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"schema": {
|
||||||
|
"https://t17194-s21127.dev10.kubedev.kibo-dev.com/graphql": {}
|
||||||
|
},
|
||||||
|
|
||||||
|
"generates": {
|
||||||
|
"./framework/kibocommerce/schema.d.ts": {
|
||||||
|
"plugins": ["typescript", "typescript-operations"],
|
||||||
|
"config": {
|
||||||
|
"scalars": {
|
||||||
|
"ID": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"./framework/kibocommerce/schema.graphql": {
|
||||||
|
"plugins": ["schema-ast"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hooks": {
|
||||||
|
"afterAllFileWrite": ["prettier --write"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
9
framework/kibocommerce/commerce.config.json
Normal file
9
framework/kibocommerce/commerce.config.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"provider": "kibocommerce",
|
||||||
|
"features": {
|
||||||
|
"wishlist": true,
|
||||||
|
"cart": true,
|
||||||
|
"search": true,
|
||||||
|
"customerAuth": true
|
||||||
|
}
|
||||||
|
}
|
15
framework/kibocommerce/customer/address/use-add-item.tsx
Normal file
15
framework/kibocommerce/customer/address/use-add-item.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import useAddItem, { UseAddItem } from '@commerce/customer/address/use-add-item'
|
||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
|
||||||
|
export default useAddItem as UseAddItem<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() =>
|
||||||
|
async () => ({}),
|
||||||
|
}
|
15
framework/kibocommerce/customer/card/use-add-item.tsx
Normal file
15
framework/kibocommerce/customer/card/use-add-item.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import useAddItem, { UseAddItem } from '@commerce/customer/card/use-add-item'
|
||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
|
||||||
|
export default useAddItem as UseAddItem<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() =>
|
||||||
|
async () => ({}),
|
||||||
|
}
|
1
framework/kibocommerce/customer/index.ts
Normal file
1
framework/kibocommerce/customer/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as useCustomer } from './use-customer'
|
24
framework/kibocommerce/customer/use-customer.tsx
Normal file
24
framework/kibocommerce/customer/use-customer.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
|
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
|
||||||
|
import type { CustomerHook } from '../types/customer'
|
||||||
|
|
||||||
|
export default useCustomer as UseCustomer<typeof handler>
|
||||||
|
|
||||||
|
export const handler: SWRHook<CustomerHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
url: '/api/customer',
|
||||||
|
method: 'GET',
|
||||||
|
},
|
||||||
|
async fetcher({ options, fetch }) {
|
||||||
|
const data = await fetch(options)
|
||||||
|
return data?.customer ?? null
|
||||||
|
},
|
||||||
|
useHook: ({ useData }) => (input) => {
|
||||||
|
return useData({
|
||||||
|
swrOptions: {
|
||||||
|
revalidateOnFocus: false,
|
||||||
|
...input?.swrOptions,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
41
framework/kibocommerce/fetcher.ts
Normal file
41
framework/kibocommerce/fetcher.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { FetcherError } from '@commerce/utils/errors'
|
||||||
|
import type { Fetcher } from '@commerce/utils/types'
|
||||||
|
|
||||||
|
async function getText(res: Response) {
|
||||||
|
try {
|
||||||
|
return (await res.text()) || res.statusText
|
||||||
|
} catch (error) {
|
||||||
|
return res.statusText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getError(res: Response) {
|
||||||
|
if (res.headers.get('Content-Type')?.includes('application/json')) {
|
||||||
|
const data = await res.json()
|
||||||
|
return new FetcherError({ errors: data.errors, status: res.status })
|
||||||
|
}
|
||||||
|
return new FetcherError({ message: await getText(res), status: res.status })
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetcher: Fetcher = async ({
|
||||||
|
url,
|
||||||
|
method = 'GET',
|
||||||
|
variables,
|
||||||
|
body: bodyObj,
|
||||||
|
}) => {
|
||||||
|
const hasBody = Boolean(variables || bodyObj)
|
||||||
|
const body = hasBody
|
||||||
|
? JSON.stringify(variables ? { variables } : bodyObj)
|
||||||
|
: undefined
|
||||||
|
const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined
|
||||||
|
const res = await fetch(url!, { method, body, headers })
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const { data } = await res.json()
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
throw await getError(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default fetcher
|
9
framework/kibocommerce/index.tsx
Normal file
9
framework/kibocommerce/index.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { getCommerceProvider, useCommerce as useCoreCommerce } from '@commerce'
|
||||||
|
import { kiboCommerceProvider, KibocommerceProvider } from './provider'
|
||||||
|
|
||||||
|
export { kiboCommerceProvider }
|
||||||
|
export type { KibocommerceProvider }
|
||||||
|
|
||||||
|
export const CommerceProvider = getCommerceProvider(kiboCommerceProvider)
|
||||||
|
|
||||||
|
export const useCommerce = () => useCoreCommerce()
|
8
framework/kibocommerce/lib/get-cookie-expiration-date.ts
Normal file
8
framework/kibocommerce/lib/get-cookie-expiration-date.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export function getCookieExpirationDate(maxAgeInDays: number){
|
||||||
|
const today = new Date();
|
||||||
|
const expirationDate = new Date();
|
||||||
|
|
||||||
|
const cookieExpirationDate = new Date ( expirationDate.setDate(today.getDate() + maxAgeInDays) )
|
||||||
|
|
||||||
|
return cookieExpirationDate;
|
||||||
|
}
|
5
framework/kibocommerce/lib/get-slug.ts
Normal file
5
framework/kibocommerce/lib/get-slug.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// Remove trailing and leading slash, usually included in nodes
|
||||||
|
// returned by the BigCommerce API
|
||||||
|
const getSlug = (path: string) => path.replace(/^\/|\/$/g, '')
|
||||||
|
|
||||||
|
export default getSlug
|
13
framework/kibocommerce/lib/immutability.ts
Normal file
13
framework/kibocommerce/lib/immutability.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import update, { Context } from 'immutability-helper'
|
||||||
|
|
||||||
|
const c = new Context()
|
||||||
|
|
||||||
|
c.extend('$auto', function (value, object) {
|
||||||
|
return object ? c.update(object, value) : c.update({}, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
c.extend('$autoArray', function (value, object) {
|
||||||
|
return object ? c.update(object, value) : c.update([], value)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default c.update
|
194
framework/kibocommerce/lib/normalize.ts
Normal file
194
framework/kibocommerce/lib/normalize.ts
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import update from './immutability'
|
||||||
|
import getSlug from './get-slug'
|
||||||
|
import type { PrCategory, CustomerAccountInput, Document } from '../schema'
|
||||||
|
import { Page } from '../types/page';
|
||||||
|
import { Customer } from '../types/customer'
|
||||||
|
|
||||||
|
function normalizeProductOption(productOption: any) {
|
||||||
|
const {
|
||||||
|
node: { entityId, values: { edges = [] } = {}, ...rest },
|
||||||
|
} = productOption
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: entityId,
|
||||||
|
values: edges?.map(({ node }: any) => node),
|
||||||
|
...rest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeProduct(productNode: any, config: any): any {
|
||||||
|
const product = {
|
||||||
|
id: productNode.productCode,
|
||||||
|
name: productNode.content.productName,
|
||||||
|
vendor: '',
|
||||||
|
path: `/${productNode.productCode}`,
|
||||||
|
slug: productNode.productCode,
|
||||||
|
price: {
|
||||||
|
value: productNode?.price?.price,
|
||||||
|
currencyCode: config.currencyCode,
|
||||||
|
},
|
||||||
|
descriptionHtml: productNode.content.productShortDescription,
|
||||||
|
|
||||||
|
images: productNode.content.productImages.map((p: any) => ({
|
||||||
|
url: `http:${p.imageUrl}`,
|
||||||
|
altText: p.imageLabel,
|
||||||
|
})),
|
||||||
|
|
||||||
|
variants: productNode.variations?.map((v: any) => ({
|
||||||
|
id: v.productCode,
|
||||||
|
options: v.options.map((o: any) => ({
|
||||||
|
['__typename']: 'MultipleChoiceOption',
|
||||||
|
id: o.attributeFQN,
|
||||||
|
displayName:
|
||||||
|
o.attributeFQN.split('~')[1][0].toUpperCase() +
|
||||||
|
o.attributeFQN.split('~')[1].slice(1).toLowerCase(),
|
||||||
|
values: [{ label: o.value.toString() }],
|
||||||
|
})),
|
||||||
|
})) || [
|
||||||
|
{
|
||||||
|
id: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
options:
|
||||||
|
productNode.options?.map((o: any) => ({
|
||||||
|
id: o.attributeFQN,
|
||||||
|
displayName: o.attributeDetail.name,
|
||||||
|
values: o.values.map((v: any) => ({
|
||||||
|
label: v.value.toString(),
|
||||||
|
hexColors: '',
|
||||||
|
})),
|
||||||
|
})) || [],
|
||||||
|
}
|
||||||
|
|
||||||
|
return product
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizePage(page: Document): Page {
|
||||||
|
return {
|
||||||
|
id: String(page.id),
|
||||||
|
name: String(page.name),
|
||||||
|
url: page.properties.url,
|
||||||
|
body: page.properties.body,
|
||||||
|
is_visible: page.properties.is_visible,
|
||||||
|
sort_order: page.properties.sort_order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeCart(data: any): any {
|
||||||
|
return {
|
||||||
|
id: data.id,
|
||||||
|
customerId: data.userId,
|
||||||
|
email: data?.email,
|
||||||
|
createdAt: data?.created_time,
|
||||||
|
currency: {
|
||||||
|
code: 'USD',
|
||||||
|
},
|
||||||
|
taxesIncluded: true,
|
||||||
|
lineItems: data.items.map(normalizeLineItem),
|
||||||
|
lineItemsSubtotalPrice: data?.items.reduce(
|
||||||
|
(acc: number, obj: { subtotal: number }) => acc + obj.subtotal,
|
||||||
|
0
|
||||||
|
),
|
||||||
|
subtotalPrice: data?.subtotal,
|
||||||
|
totalPrice: data?.total,
|
||||||
|
discounts: data.orderDiscounts?.map((discount: any) => ({
|
||||||
|
value: discount.impact,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeCustomer(customer: CustomerAccountInput): Customer {
|
||||||
|
return {
|
||||||
|
id: customer.id,
|
||||||
|
firstName: customer.firstName,
|
||||||
|
lastName: customer.lastName,
|
||||||
|
email: customer.emailAddress,
|
||||||
|
userName: customer.userName,
|
||||||
|
isAnonymous: customer.isAnonymous
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLineItem(item: any): any {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
variantId: item.product.variationProductCode,
|
||||||
|
productId: String(item.product.productCode),
|
||||||
|
name: item.product.name,
|
||||||
|
quantity: item.quantity,
|
||||||
|
variant: {
|
||||||
|
id: item.product.variationProductCode,
|
||||||
|
sku: item.product?.sku,
|
||||||
|
name: item.product.name,
|
||||||
|
image: {
|
||||||
|
url: item?.product?.imageUrl,
|
||||||
|
},
|
||||||
|
requiresShipping: item?.is_require_shipping,
|
||||||
|
price: item?.unitPrice.extendedAmount,
|
||||||
|
listPrice: 0,
|
||||||
|
},
|
||||||
|
options: item.product.options,
|
||||||
|
path: `${item.product.productCode}`,
|
||||||
|
discounts: item?.discounts?.map((discount: any) => ({
|
||||||
|
value: discount.discounted_amount,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeCategory(category: PrCategory): any {
|
||||||
|
return {
|
||||||
|
id: category?.categoryCode,
|
||||||
|
name: category?.content?.name,
|
||||||
|
slug: category?.content?.slug,
|
||||||
|
path: `/${category?.content?.slug}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeWishlistItem(
|
||||||
|
item: any,
|
||||||
|
config: any,
|
||||||
|
includeProducts=false
|
||||||
|
): any {
|
||||||
|
if (includeProducts) {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
product: getProuducts(item, config),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return getProuducts(item, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProuducts(item: any, config: any): any {
|
||||||
|
return {
|
||||||
|
variant_id: item.product.variationProductCode || '',
|
||||||
|
id: String(item.product.productCode),
|
||||||
|
product_id: String(item.product.productCode),
|
||||||
|
name: item.product.name,
|
||||||
|
quantity: item.quantity,
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: `http:${item.product.imageUrl}`,
|
||||||
|
alt: item.product.imageAlternateText,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
price: {
|
||||||
|
value: item.product.price.price,
|
||||||
|
retailPrice: item.product.price.retailPrice || 0,
|
||||||
|
currencyCode: config.currencyCode,
|
||||||
|
},
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: item.product.variationProductCode || '',
|
||||||
|
sku: item.product?.sku,
|
||||||
|
name: item.product.name,
|
||||||
|
image: {
|
||||||
|
url: item?.product.imageUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: item.product.options,
|
||||||
|
path: `/${item.product.productCode}`,
|
||||||
|
description: item.product.description,
|
||||||
|
}
|
||||||
|
}
|
15
framework/kibocommerce/lib/prepare-set-cookie.ts
Normal file
15
framework/kibocommerce/lib/prepare-set-cookie.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export function prepareSetCookie(name: string, value: string, options: any = {}): string {
|
||||||
|
const encodedValue = Buffer.from(value).toString('base64')
|
||||||
|
const cookieValue = [`${name}=${encodedValue}`];
|
||||||
|
|
||||||
|
if (options.maxAge) {
|
||||||
|
cookieValue.push(`Max-Age=${options.maxAge}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.expires && !options.maxAge) {
|
||||||
|
cookieValue.push(`Expires=${options.expires.toUTCString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cookie = cookieValue.join('; ')
|
||||||
|
return cookie
|
||||||
|
}
|
55
framework/kibocommerce/lib/product-search-vars.ts
Normal file
55
framework/kibocommerce/lib/product-search-vars.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
function getFacetValueFilter(categoryCode: string, filters = []) {
|
||||||
|
let facetValueFilter = '';
|
||||||
|
if (categoryCode) {
|
||||||
|
facetValueFilter = `categoryCode:${categoryCode},`;
|
||||||
|
}
|
||||||
|
return facetValueFilter + filters.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildProductSearchVars = ({
|
||||||
|
categoryCode = '',
|
||||||
|
pageSize = 5,
|
||||||
|
filters = {} as any,
|
||||||
|
startIndex = 0,
|
||||||
|
sort = '',
|
||||||
|
search = '',
|
||||||
|
}) => {
|
||||||
|
let facetTemplate = '';
|
||||||
|
let filter = '';
|
||||||
|
let sortBy;
|
||||||
|
if (categoryCode) {
|
||||||
|
facetTemplate = `categoryCode:${categoryCode}`;
|
||||||
|
filter = `categoryCode req ${categoryCode}`;
|
||||||
|
}
|
||||||
|
const facetFilterList = Object.keys(filters).filter(k => filters[k].length).reduce((accum, k): any => {
|
||||||
|
return [...accum, ...filters[k].map((facetValue: any) => `Tenant~${k}:${facetValue}`)];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const facetValueFilter = getFacetValueFilter(categoryCode, facetFilterList);
|
||||||
|
|
||||||
|
switch(sort) {
|
||||||
|
case 'latest-desc':
|
||||||
|
sortBy= 'createDate desc';
|
||||||
|
break;
|
||||||
|
case 'price-asc':
|
||||||
|
sortBy= 'price asc';
|
||||||
|
break;
|
||||||
|
case 'price-desc':
|
||||||
|
sortBy= 'price desc';
|
||||||
|
break;
|
||||||
|
case 'trending-desc':
|
||||||
|
default:
|
||||||
|
sortBy= '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
query: search,
|
||||||
|
startIndex,
|
||||||
|
pageSize,
|
||||||
|
sortBy,
|
||||||
|
filter: filter,
|
||||||
|
facetTemplate,
|
||||||
|
facetValueFilter
|
||||||
|
}
|
||||||
|
}
|
3
framework/kibocommerce/lib/set-cookie.ts
Normal file
3
framework/kibocommerce/lib/set-cookie.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function setCookies(res: any, cookies: string[]): void {
|
||||||
|
res.setHeader('Set-Cookie', cookies);
|
||||||
|
}
|
12
framework/kibocommerce/next.config.js
Normal file
12
framework/kibocommerce/next.config.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const commerce = require('./commerce.config.json')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
commerce,
|
||||||
|
serverRuntimeConfig: {
|
||||||
|
// Will only be available on the server side
|
||||||
|
kiboAuthTicket: null
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
domains: ['d1slj7rdbjyb5l.cloudfront.net', 'cdn-tp1.mozu.com', 'cdn-sb.mozu.com'],
|
||||||
|
},
|
||||||
|
}
|
2
framework/kibocommerce/product/index.ts
Normal file
2
framework/kibocommerce/product/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default as usePrice } from './use-price'
|
||||||
|
export { default as useSearch } from './use-search'
|
2
framework/kibocommerce/product/use-price.tsx
Normal file
2
framework/kibocommerce/product/use-price.tsx
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from '@commerce/product/use-price'
|
||||||
|
export { default } from '@commerce/product/use-price'
|
37
framework/kibocommerce/product/use-search.tsx
Normal file
37
framework/kibocommerce/product/use-search.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
|
import useSearch, { UseSearch } from '@commerce/product/use-search'
|
||||||
|
export default useSearch as UseSearch<typeof handler>
|
||||||
|
|
||||||
|
export const handler: SWRHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/catalog/products',
|
||||||
|
},
|
||||||
|
fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) {
|
||||||
|
// Use a dummy base as we only care about the relative path
|
||||||
|
const url = new URL(options.url!, 'http://a')
|
||||||
|
|
||||||
|
if (search) url.searchParams.set('search', search)
|
||||||
|
if (Number.isInteger(Number(categoryId)))
|
||||||
|
url.searchParams.set('categoryId', String(categoryId))
|
||||||
|
if (Number.isInteger(brandId))
|
||||||
|
url.searchParams.set('brandId', String(brandId))
|
||||||
|
if (sort) url.searchParams.set('sort', sort)
|
||||||
|
|
||||||
|
return fetch({
|
||||||
|
url: url.pathname + url.search,
|
||||||
|
method: options.method,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
useHook: ({ useData }) => (input) => {
|
||||||
|
return useData({
|
||||||
|
input: [
|
||||||
|
['search', input.search],
|
||||||
|
['categoryId', input.categoryId],
|
||||||
|
['brandId', input.brandId],
|
||||||
|
['sort', input.sort],
|
||||||
|
],
|
||||||
|
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
30
framework/kibocommerce/provider.ts
Normal file
30
framework/kibocommerce/provider.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import fetcher from './fetcher'
|
||||||
|
import { handler as useCart } from './cart/use-cart'
|
||||||
|
import { handler as useAddItem } from './cart/use-add-item'
|
||||||
|
import { handler as useUpdateItem } from './cart/use-update-item'
|
||||||
|
import { handler as useRemoveItem } from './cart/use-remove-item'
|
||||||
|
import { handler as useCustomer } from './customer/use-customer'
|
||||||
|
import { handler as useSearch } from './product/use-search'
|
||||||
|
import { handler as useLogin } from './auth/use-login'
|
||||||
|
import { handler as useLogout } from './auth/use-logout'
|
||||||
|
import { handler as useSignup } from './auth/use-signup'
|
||||||
|
import { handler as useWishlist } from './wishlist/use-wishlist'
|
||||||
|
import { handler as useWishlistAddItem } from './wishlist/use-add-item'
|
||||||
|
import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item'
|
||||||
|
|
||||||
|
export const kiboCommerceProvider = {
|
||||||
|
locale: 'en-us',
|
||||||
|
cartCookie: 'kibo_cart',
|
||||||
|
fetcher,
|
||||||
|
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
||||||
|
wishlist: {
|
||||||
|
useWishlist,
|
||||||
|
useAddItem: useWishlistAddItem,
|
||||||
|
useRemoveItem: useWishlistRemoveItem,
|
||||||
|
},
|
||||||
|
customer: { useCustomer },
|
||||||
|
products: { useSearch },
|
||||||
|
auth: { useLogin, useLogout, useSignup },
|
||||||
|
}
|
||||||
|
|
||||||
|
export type KibocommerceProvider = typeof kiboCommerceProvider
|
11399
framework/kibocommerce/schema.d.ts
vendored
Normal file
11399
framework/kibocommerce/schema.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
9129
framework/kibocommerce/schema.graphql
Normal file
9129
framework/kibocommerce/schema.graphql
Normal file
File diff suppressed because it is too large
Load Diff
27
framework/kibocommerce/types/customer.ts
Normal file
27
framework/kibocommerce/types/customer.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import * as Core from '@commerce/types/customer'
|
||||||
|
export type Maybe<T> = T | null
|
||||||
|
export * from '@commerce/types/customer'
|
||||||
|
export type Scalars = {
|
||||||
|
ID: string
|
||||||
|
String: string
|
||||||
|
Boolean: boolean
|
||||||
|
Int: 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. */
|
||||||
|
AnyScalar: any
|
||||||
|
/** DateTime custom scalar type */
|
||||||
|
DateTime: any
|
||||||
|
/** Object custom scalar type */
|
||||||
|
Object: any
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Customer = {
|
||||||
|
id: Scalars['Int'],
|
||||||
|
firstName?: Maybe<Scalars['String']>,
|
||||||
|
lastName?: Maybe<Scalars['String']>,
|
||||||
|
email?: Maybe<Scalars['String']>,
|
||||||
|
userName?: Maybe<Scalars['String']>,
|
||||||
|
isAnonymous?: Maybe<Scalars['Boolean']>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CustomerSchema = Core.CustomerSchema
|
8
framework/kibocommerce/types/login.ts
Normal file
8
framework/kibocommerce/types/login.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import * as Core from '@commerce/types/login'
|
||||||
|
import type { CustomerUserAuthInfoInput } from '../schema'
|
||||||
|
|
||||||
|
export * from '@commerce/types/login'
|
||||||
|
|
||||||
|
export type LoginOperation = Core.LoginOperation & {
|
||||||
|
variables: CustomerUserAuthInfoInput
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user