Skip to content

Commit

Permalink
feat(RSS-ECOMM-5_96): update requests (#355)
Browse files Browse the repository at this point in the history
* feat: add sale category request

* feat: filter *

* feat: options for shop list

* feat: merge
  • Loading branch information
YulikK authored Jun 10, 2024
1 parent fd6c1b5 commit da951e9
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 35 deletions.
11 changes: 10 additions & 1 deletion src/shared/API/discount/DiscountApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { ClientResponse, DiscountCodePagedQueryResponse } from '@commercetools/platform-sdk';
import type {
ClientResponse,
DiscountCodePagedQueryResponse,
ProductDiscountPagedQueryResponse,
} from '@commercetools/platform-sdk';

import getApiClient, { type ApiClient } from '../sdk/client.ts';

Expand All @@ -13,6 +17,11 @@ export class DiscountApi {
const data = await this.client.apiRoot().discountCodes().get().execute();
return data;
}

public async getDiscounts(): Promise<ClientResponse<ProductDiscountPagedQueryResponse>> {
const data = await this.client.apiRoot().productDiscounts().get().execute();
return data;
}
}

const createDiscountApi = (): DiscountApi => new DiscountApi();
Expand Down
40 changes: 38 additions & 2 deletions src/shared/API/discount/model/DiscountModel.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import type { Coupon } from '@/shared/types/cart.ts';
import type { ClientResponse, DiscountCode, DiscountCodePagedQueryResponse } from '@commercetools/platform-sdk';
import type { Category } from '@/shared/types/product.ts';
import type {
ClientResponse,
DiscountCode,
DiscountCodePagedQueryResponse,
ProductDiscountPagedQueryResponse,
} from '@commercetools/platform-sdk';

import { showErrorMessage } from '@/shared/utils/userMessage.ts';

import { isClientResponse, isDiscountCodePagedQueryResponse } from '../../types/validation.ts';
import getProductModel from '../../product/model/ProductModel.ts';
import {
isClientResponse,
isDiscountCodePagedQueryResponse,
isProductDiscountPagedQueryResponse,
} from '../../types/validation.ts';
import getDiscountApi, { type DiscountApi } from '../DiscountApi.ts';

export class DiscountModel {
Expand All @@ -24,6 +35,25 @@ export class DiscountModel {
};
}

private getCategorySaleFromData(
data: ClientResponse<ProductDiscountPagedQueryResponse>,
categories: Category[],
): Category[] {
const result: Category[] = [];
if (isClientResponse(data) && isProductDiscountPagedQueryResponse(data.body)) {
const allReferences = data.body.results.flatMap((result) => result.references);
allReferences.forEach((rule) => {
if (rule.typeId === 'category') {
const categorySale = categories.find((category) => category.id === rule.id);
if (categorySale) {
result.push(categorySale);
}
}
});
}
return result;
}

private getCouponsFromData(data: ClientResponse<DiscountCodePagedQueryResponse>): Coupon[] {
const coupons: Coupon[] = [];
if (isClientResponse(data) && isDiscountCodePagedQueryResponse(data.body)) {
Expand All @@ -43,6 +73,12 @@ export class DiscountModel {
public getAllCoupons(): Coupon[] {
return this.coupons;
}

public async getCategorySales(): Promise<Category[]> {
const data = await this.root.getDiscounts();
const categories = await getProductModel().getCategories();
return this.getCategorySaleFromData(data, categories);
}
}

const createDiscountModel = (): DiscountModel => new DiscountModel();
Expand Down
23 changes: 12 additions & 11 deletions src/shared/API/product/ProductApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import type {
ProductProjectionPagedSearchResponse,
} from '@commercetools/platform-sdk';

import { DEFAULT_PAGE, MAX_PRICE, MIN_PRICE, PRICE_FRACTIONS, PRODUCT_LIMIT } from '@/shared/constants/product.ts';
import { DEFAULT_PAGE, MAX_PRICE, MIN_PRICE, PRODUCT_LIMIT } from '@/shared/constants/product.ts';

import type { OptionsRequest, PriceRange } from '../types/type.ts';

import getApiClient, { type ApiClient } from '../sdk/client.ts';
import { type OptionsRequest } from '../types/type.ts';
import { getDefaultPriceRange } from './utils/filter.ts';
import makeSortRequest from './utils/sort.ts';

const Search = 'text';
const FACET_ADD = 1;
enum Facets {
category = 'categories.id counting products',
price = 'variants.price.centAmount',
Expand Down Expand Up @@ -63,25 +64,25 @@ export class ProductApi {
return data;
}

public async getProducts(options?: OptionsRequest): Promise<ClientResponse<ProductProjectionPagedSearchResponse>> {
public async getProducts(
options?: OptionsRequest,
productsPriceRange?: PriceRange,
): Promise<ClientResponse<ProductProjectionPagedSearchResponse>> {
const { filter, limit = PRODUCT_LIMIT, page = DEFAULT_PAGE, search, sort } = options || {};
const filterQuery = filter?.getFilter();
const priceRange = filter?.getPriceRange();
const min = Math.round((priceRange?.min ?? MIN_PRICE) * PRICE_FRACTIONS - FACET_ADD);
const max = Math.round((priceRange?.max ?? MAX_PRICE) * PRICE_FRACTIONS + FACET_ADD);
const filterQuery = filter?.getFilter(productsPriceRange);
const priceRange = filter ? filter.getPriceRange(productsPriceRange) : getDefaultPriceRange();
const fuzzyLevel = this.getFuzzyLevel(search?.value ? search?.value : '');

const data = await this.client
.apiRoot()
.productProjections()
.search()
.get({
queryArgs: {
facet: [Facets.category, Facets.size, `${Facets.price}:${QueryParams.range}(${min} to ${max})`],
facet: [Facets.category, Facets.size, priceRange],
limit,
markMatchingVariants: true,
offset: (page - 1) * PRODUCT_LIMIT,
...(search && { [`${Search}.${search.locale}`]: search.value }),
...(search && { [`${Search}.${search.locale}`]: `*${search.value}*` }),
...(search && { fuzzy: true }),
...(search?.value && { fuzzyLevel }),
...(sort && { sort: makeSortRequest(sort) }),
Expand Down
14 changes: 13 additions & 1 deletion src/shared/API/product/model/ProductModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ enum ProductConstant {
export class ProductModel {
private categories: Category[] = [];

private priceRange: PriceRange = {
max: 0,
min: 0,
};

private root: ProductApi;

constructor() {
Expand Down Expand Up @@ -268,6 +273,9 @@ export class ProductModel {
});
}
}
if (this.priceRange.min === 0 && this.priceRange.max === 0) {
this.priceRange = priceRange;
}
return priceRange;
}

Expand Down Expand Up @@ -395,7 +403,7 @@ export class ProductModel {

public async getProducts(options?: OptionsRequest): Promise<ProductWithCount> {
await getProductModel().getCategories();
const data = await this.root.getProducts(options);
const data = await this.root.getProducts(options, this.priceRange);
const products = this.getProductsFromData(data);
if (options?.sort) {
this.sortVariants(products, options?.sort);
Expand All @@ -413,6 +421,10 @@ export class ProductModel {
};
return result;
}

public getProductsPriceRange(): PriceRange {
return this.priceRange;
}
}

const createProductModel = (): ProductModel => new ProductModel();
Expand Down
24 changes: 16 additions & 8 deletions src/shared/API/product/utils/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export default class FilterProduct {
private newArrival = '';

private price: PriceRange = {
max: MAX_PRICE,
min: MIN_PRICE,
max: 0,
min: 0,
};

private sale = '';
Expand Down Expand Up @@ -55,7 +55,7 @@ export default class FilterProduct {
return this.getFilter();
}

public getFilter(): string[] {
public getFilter(productsPriceRange?: PriceRange): string[] {
const result = [];
if (this.id.length) {
result.push(`${FilterFields.ID}:${this.id.map((id) => `"${id}"`).join(',')}`);
Expand All @@ -70,17 +70,25 @@ export default class FilterProduct {
result.push(this.newArrival);
}
if (this.price) {
result.push(
`${FilterFields.PRICE}: range(${Math.round(this.price.min * PRICE_FRACTIONS)} to ${Math.round(this.price.max * PRICE_FRACTIONS)})`,
);
result.push(this.getPriceRange(productsPriceRange));
}
if (this.sale) {
result.push(this.sale);
}
return result;
}

public getPriceRange(): PriceRange {
return this.price;
public getPriceRange(productsPriceRange?: PriceRange): string {
const min = Math.round(this.price.min * PRICE_FRACTIONS);
const max =
this.price.max && productsPriceRange && this.price.max !== productsPriceRange.max
? Math.round(this.price.max * PRICE_FRACTIONS)
: MAX_PRICE;

return `${FilterFields.PRICE}: range(${min} to ${max})`;
}
}

export function getDefaultPriceRange(): string {
return `${FilterFields.PRICE}: range(${MIN_PRICE} to ${MAX_PRICE})`;
}
39 changes: 35 additions & 4 deletions src/shared/API/shopping-list/ShoppingListApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import type {
ShoppingListSetAnonymousIdAction,
} from '@commercetools/platform-sdk';

import { DEFAULT_PAGE, PRODUCT_LIMIT } from '@/shared/constants/product.ts';

import type { OptionsRequest } from '../types/type.ts';

import makeSortRequest from '../product/utils/sort.ts';
import getApiClient, { type ApiClient } from '../sdk/client.ts';

export class ShoppingListApi {
Expand Down Expand Up @@ -92,13 +97,39 @@ export class ShoppingListApi {
return data;
}

public async get(): Promise<ClientResponse<ShoppingListPagedQueryResponse>> {
const data = await this.client.apiRoot().me().shoppingLists().get().execute();
public async get(options?: OptionsRequest): Promise<ClientResponse<ShoppingListPagedQueryResponse>> {
const { limit = PRODUCT_LIMIT, page = DEFAULT_PAGE, sort } = options || {};
const data = await this.client
.apiRoot()
.me()
.shoppingLists()
.get({
queryArgs: {
limit,
offset: (page - 1) * PRODUCT_LIMIT,
...(sort && { sort: makeSortRequest(sort) }),
withTotal: true,
},
})
.execute();
return data;
}

public async getAnonymList(ID: string): Promise<ClientResponse<ShoppingListResponse>> {
const data = await this.client.apiRoot().shoppingLists().withId({ ID }).get().execute();
public async getAnonymList(ID: string, options?: OptionsRequest): Promise<ClientResponse<ShoppingListResponse>> {
const { limit = PRODUCT_LIMIT, page = DEFAULT_PAGE, sort } = options || {};
const data = await this.client
.apiRoot()
.shoppingLists()
.withId({ ID })
.get({
queryArgs: {
limit,
offset: (page - 1) * PRODUCT_LIMIT,
...(sort && { sort: makeSortRequest(sort) }),
withTotal: true,
},
})
.execute();
return data;
}

Expand Down
19 changes: 12 additions & 7 deletions src/shared/API/shopping-list/model/ShoppingListModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import getStore from '@/shared/Store/Store.ts';
import { setAnonymousId, setAnonymousShopListId } from '@/shared/Store/actions.ts';
import { showErrorMessage } from '@/shared/utils/userMessage.ts';

import type { OptionsRequest } from '../../types/type.ts';

import {
isClientResponse,
isErrorResponse,
Expand Down Expand Up @@ -66,8 +68,11 @@ export class ShoppingListModel {
};
}

private async getAnonymousShoppingList(anonymousShopListId: string): Promise<ShoppingList | null> {
const dataAnonymList = await this.root.getAnonymList(anonymousShopListId);
private async getAnonymousShoppingList(
anonymousShopListId: string,
options?: OptionsRequest,
): Promise<ShoppingList | null> {
const dataAnonymList = await this.root.getAnonymList(anonymousShopListId, options);
const anonymShoppingList = this.getShopListFromData(dataAnonymList);
if (anonymShoppingList.id && !dataAnonymList.body.customer?.id) {
this.shoppingList = anonymShoppingList;
Expand All @@ -94,8 +99,8 @@ export class ShoppingListModel {
return cart;
}

private async getUserShoppingLists(): Promise<ShoppingList> {
const data = await this.root.get();
private async getUserShoppingLists(options?: OptionsRequest): Promise<ShoppingList> {
const data = await this.root.get(options);
if (data.body.count === 0) {
const newShopList = await this.root.create();
this.shoppingList = this.getShopListFromData(newShopList);
Expand Down Expand Up @@ -183,14 +188,14 @@ export class ShoppingListModel {
return this.shoppingList;
}

public async getShoppingList(): Promise<ShoppingList> {
public async getShoppingList(options?: OptionsRequest): Promise<ShoppingList> {
if (!this.shoppingList) {
const { anonymousId, anonymousShopListId } = getStore().getState();
if (anonymousShopListId && anonymousId) {
this.shoppingList = await this.getAnonymousShoppingList(anonymousShopListId);
this.shoppingList = await this.getAnonymousShoppingList(anonymousShopListId, options);
}
if (!this.shoppingList) {
this.shoppingList = await this.getUserShoppingLists();
this.shoppingList = await this.getUserShoppingLists(options);
}
}
return this.shoppingList;
Expand Down
16 changes: 16 additions & 0 deletions src/shared/API/types/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
FacetTerm,
LocalizedString,
Product,
ProductDiscountPagedQueryResponse,
ProductPagedQueryResponse,
ProductProjection,
ProductProjectionPagedQueryResponse,
Expand Down Expand Up @@ -314,3 +315,18 @@ export function isDiscountCodePagedQueryResponse(data: unknown): data is Discoun
Array.isArray(data.results),
);
}

export function isProductDiscountPagedQueryResponse(data: unknown): data is ProductDiscountPagedQueryResponse {
return Boolean(
typeof data === 'object' &&
data &&
'count' in data &&
typeof data.count === 'number' &&
'limit' in data &&
typeof data.limit === 'number' &&
'total' in data &&
typeof data.total === 'number' &&
'results' in data &&
Array.isArray(data.results),
);
}
2 changes: 1 addition & 1 deletion src/shared/constants/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export const PRICE_FRACTIONS = 100;
export const PRODUCT_LIMIT = 9;
export const DEFAULT_PAGE = 1;
export const MIN_PRICE = 0;
export const MAX_PRICE = 1000000;
export const MAX_PRICE = '*';
export const CURRENCY = 'USD';

export const EMPTY_PRODUCT = {
Expand Down
1 change: 1 addition & 0 deletions src/widgets/Catalog/model/CatalogModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ class CatalogModel {
if (currentSort) {
result = {
filter,

page: Number(params.page),
search: params.searchValue ? { locale: currentLanguage, value: params.searchValue } : null,
sort: currentSort ?? null,
Expand Down

0 comments on commit da951e9

Please sign in to comment.