mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 14:42:31 +00:00
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_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>
|
||||
BigCommerce team has been notified and they plan to add more details about this subject.
|
||||
</details>
|
||||
</details>
|
@ -15,7 +15,8 @@ const PROVIDERS = [
|
||||
'swell',
|
||||
'vendure',
|
||||
'ordercloud',
|
||||
'spree',
|
||||
'kibocommerce',
|
||||
'spree'
|
||||
]
|
||||
|
||||
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