Skip to content

Commit

Permalink
fix(RSS-ECOMM-4_33): reloading product page (#320)
Browse files Browse the repository at this point in the history
fix: reloading product page
  • Loading branch information
Kleostro authored May 28, 2024
1 parent 7b6e156 commit 5c48af6
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 67 deletions.
19 changes: 11 additions & 8 deletions src/app/Router/model/RouterModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { PageParams, PagesType } from '@/shared/types/page';

import getStore from '@/shared/Store/Store.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { isValidPath, isValidState } from '@/shared/types/validation/paths.ts';
import formattedText from '@/shared/utils/formattedText.ts';
Expand Down Expand Up @@ -118,14 +119,16 @@ class RouterModel {

public navigateTo(path: string): void {
const currentPage = path.split(DEFAULT_SEGMENT)[PATH_SEGMENTS_TO_KEEP] + DEFAULT_SEGMENT || PAGE_ID.DEFAULT_PAGE;
this.checkPageAndParams(currentPage, path)
.then((check) => {
if (check) {
this.routes.get(currentPage)?.(check.params).catch(showErrorMessage);
history.pushState({ path }, '', `/${path}`);
}
})
.catch(showErrorMessage);
if (currentPage !== getStore().getState().currentPage) {
this.checkPageAndParams(currentPage, path)
.then((check) => {
if (check) {
this.routes.get(currentPage)?.(check.params).catch(showErrorMessage);
}
})
.catch(showErrorMessage);
}
history.pushState({ path }, '', `/${path}`);
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/features/Pagination/view/PaginationView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,8 @@ class PaginationView {
}

private switchStateNavigationButtons(page: number): void {
const prevButton = this.prevPageButton.getHTML();
const nextButton = this.nextPageButton.getHTML();

prevButton.disabled = page === DEFAULT_PAGE;
nextButton.disabled = page === this.pageButtons.length;
this.prevPageButton.getHTML().disabled = page === DEFAULT_PAGE;
this.nextPageButton.getHTML().disabled = page === this.pageButtons.length;
}

public getHTML(): HTMLDivElement {
Expand Down
4 changes: 0 additions & 4 deletions src/features/ProductFilters/model/ProductFiltersModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ class ProductFiltersModel {
public getView(): ProductFiltersView {
return this.view;
}

public updateParams(params: ProductFiltersParams | null): void {
this.view.updateParams(params);
}
}

export default ProductFiltersModel;
30 changes: 18 additions & 12 deletions src/features/ProductFilters/view/ProductFiltersView.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SizeProductCount } from '@/shared/API/types/type';
import type { PriceRange, SizeProductCount } from '@/shared/API/types/type';
import type { Category } from '@/shared/types/product';
import type ProductFiltersParams from '@/shared/types/productFilters';

Expand Down Expand Up @@ -629,17 +629,6 @@ class ProductFiltersView {

public updateParams(params: ProductFiltersParams | null): void {
this.params = params;
this.priceSlider.updateOptions(
{
range: { max: params?.priceRange?.max ?? 0, min: params?.priceRange?.min ?? 0 },
start: [params?.priceRange?.min ?? 0, params?.priceRange?.max ?? 0],
},
true,
);
const fromInput = this.priceInputs.get(PRICE_RANGE_LABEL[getStore().getState().currentLanguage].FROM);
const toInput = this.priceInputs.get(PRICE_RANGE_LABEL[getStore().getState().currentLanguage].TO);
fromInput?.setValue(this.params?.priceRange?.min.toFixed(2) ?? '');
toInput?.setValue(this.params?.priceRange?.max.toFixed(2) ?? '');

this.categoryCountSpan.forEach((span) => {
const currentSpan = span;
Expand All @@ -651,6 +640,23 @@ class ProductFiltersView {
});
this.redrawProductsCount();
}

public updatePriceRange(params: PriceRange): void {
if (this.params) {
this.params.priceRange = params;
}
this.priceSlider.updateOptions(
{
range: { max: params.max ?? 0, min: params.min ?? 0 },
start: [params.min ?? 0, params.max ?? 0],
},
true,
);
const fromInput = this.priceInputs.get(PRICE_RANGE_LABEL[getStore().getState().currentLanguage].FROM);
const toInput = this.priceInputs.get(PRICE_RANGE_LABEL[getStore().getState().currentLanguage].TO);
fromInput?.setValue(params.min.toFixed(2) ?? '');
toInput?.setValue(params.max.toFixed(2) ?? '');
}
}

export default ProductFiltersView;
25 changes: 9 additions & 16 deletions src/features/ProductSorts/view/ProductSortsView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { DATA_KEYS } from '@/shared/constants/common.ts';
import { SEARCH_PARAMS_FIELD } from '@/shared/constants/product.ts';
import { SORTING_ID, TEXT } from '@/shared/constants/sorting.ts';
import createBaseElement from '@/shared/utils/createBaseElement.ts';
import { isKeyOfSortField } from '@/shared/utils/isKeyOf.ts';

import styles from './productSortsView.module.scss';

Expand Down Expand Up @@ -47,26 +46,20 @@ class ProductSortsView {
private createCurrentSortingSpan(): HTMLSpanElement {
const selectedSorting = RouterModel.getSearchParams().get(SEARCH_PARAMS_FIELD.FIELD);

const upperText = selectedSorting
? selectedSorting.toUpperCase()
: TEXT[getStore().getState().currentLanguage].DEFAULT.toUpperCase();
if (isKeyOfSortField(upperText)) {
this.currentSortingSpan = createBaseElement({
cssClasses: [styles.currentSortingSpan],
innerContent: TEXT[getStore().getState().currentLanguage][upperText],
tag: 'span',
});
}
this.currentSortingSpan = createBaseElement({
cssClasses: [styles.currentSortingSpan],
innerContent: selectedSorting
? selectedSorting.toUpperCase()
: TEXT[getStore().getState().currentLanguage].DEFAULT.toUpperCase(),
tag: 'span',
});

observeStore(selectCurrentLanguage, () => {
const selectedSorting = RouterModel.getSearchParams().get(SEARCH_PARAMS_FIELD.FIELD);
const upperText = selectedSorting

this.currentSortingSpan.innerText = selectedSorting
? selectedSorting.toUpperCase()
: TEXT[getStore().getState().currentLanguage].DEFAULT.toUpperCase();

if (isKeyOfSortField(upperText)) {
this.currentSortingSpan.innerText = TEXT[getStore().getState().currentLanguage][upperText];
}
});

return this.currentSortingSpan;
Expand Down
6 changes: 5 additions & 1 deletion src/shared/Store/Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Reducer, ReduxStore } from './types';
import initialState from '../constants/initialState.ts';
import { parseToLoad } from '../services/helper.ts';
import { STORAGE_KEY, saveCurrentStateToLocalStorage } from '../services/localStorage.ts';
import { setCurrentPage } from './actions.ts';
import { rootReducer } from './reducer.ts';

export class Store<S, A> implements ReduxStore<S, A> {
Expand All @@ -28,7 +29,10 @@ export class Store<S, A> implements ReduxStore<S, A> {
this.rootReducer = rootReducer;

// If you have unexpected bugs related to State of the app, or you need to clear out Local Storage to start afresh, comment out the next line, go to the browser tab, clear out the storage manually, and update the page one more time. Then come back here and uncomment it
window.addEventListener('beforeunload', () => saveCurrentStateToLocalStorage(this.state));
window.addEventListener('beforeunload', () => {
getStore().dispatch(setCurrentPage(null));
saveCurrentStateToLocalStorage(this.state);
});
}

public dispatch(action: A): A {
Expand Down
4 changes: 3 additions & 1 deletion src/shared/Store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export const switchIsUserLoggedIn = (
type: ACTION.SWITCH_IS_USER_LOGGED_IN,
});

export const setCurrentPage = (value: PageIdType): ActionWithPayload<PageIdType, typeof ACTION.SET_CURRENT_PAGE> => ({
export const setCurrentPage = (
value: PageIdType | null,
): ActionWithPayload<PageIdType | null, typeof ACTION.SET_CURRENT_PAGE> => ({
payload: value,
type: ACTION.SET_CURRENT_PAGE,
});
Expand Down
2 changes: 1 addition & 1 deletion src/shared/Store/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ export const selectCurrentLanguage = (state: State): string => state.currentLang

export const selectIsUserLoggedIn = (state: State): boolean => state.isUserLoggedIn;

export const selectCurrentPage = (state: State): string => state.currentPage;
export const selectCurrentPage = (state: State): null | string => state.currentPage;

export default observeStore;
2 changes: 1 addition & 1 deletion src/shared/Store/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface State {
authToken: TokenStore | null;
billingCountry: string;
currentLanguage: LanguageChoiceType;
currentPage: PageIdType;
currentPage: PageIdType | null;
isAppThemeLight: boolean;
isUserLoggedIn: boolean;
shippingCountry: string;
Expand Down
10 changes: 10 additions & 0 deletions src/shared/constants/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,25 @@ export const SEARCH_PARAMS_FIELD = {

export const PRODUCT_INFO_TEXT = {
en: {
CATEGORY: 'Categories: ',
DIFFICULTY: 'Difficulty: ',
FULL_DESCRIPTION: 'Full description:',
SHORT_DESCRIPTION: 'Short description:',
SIZE: 'Size:',
},
ru: {
CATEGORY: 'Категории: ',
DIFFICULTY: 'Сложность: ',
FULL_DESCRIPTION: 'Полное описание:',
SHORT_DESCRIPTION: 'Краткое описание:',
SIZE: 'Размер:',
},
} as const;

export const PRODUCT_INFO_TEXT_KEYS = {
CATEGORY: 'CATEGORY',
DIFFICULTY: 'DIFFICULTY',
FULL_DESCRIPTION: 'FULL_DESCRIPTION',
SHORT_DESCRIPTION: 'SHORT_DESCRIPTION',
SIZE: 'SIZE',
} as const;
4 changes: 0 additions & 4 deletions src/shared/utils/isKeyOf.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import type MetaFilters from '../types/productFilters.ts';
import type { UserCredentials } from '../types/user';

import { TEXT_KEYS, type TextKeysType } from '../constants/sorting.ts';

export const isKeyOfUserData = (context: UserCredentials, key: string): key is keyof UserCredentials =>
Object.hasOwnProperty.call(context, key);

export const isKeyOfMetaFilters = (context: MetaFilters, key: string): key is keyof MetaFilters => key in context;

export const isKeyOfSortField = (key: string): key is TextKeysType => key in TEXT_KEYS;
10 changes: 5 additions & 5 deletions src/widgets/Catalog/model/CatalogModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,25 +95,25 @@ class CatalogModel {
productList.innerHTML = '';
const options = await this.getOptions();
const productsInfo = await this.getProductsInfo(options);
this.pagination?.getHTML().remove();
if (productsInfo?.products?.length) {
const shoppingList = await getShoppingListModel().getShoppingList();
const cart = await getCartModel().getCart();
productList.innerHTML = '';
productsInfo.products.forEach((productData) => {
const product = new ProductCardModel(productData, this.currentSize, shoppingList, cart);
productList.append(product.getHTML());
});
this.view.switchEmptyList(!productsInfo?.products?.length);
this.pagination?.getHTML().remove();
this.pagination = new PaginationModel(
{ productTotalCount: productsInfo?.totalProductCount ?? 0, productsPerPageCount: PRODUCT_LIMIT },
{ productTotalCount: productsInfo?.totalProductCount, productsPerPageCount: PRODUCT_LIMIT },
this.setCurrentPage.bind(this),
);
this.pagination.getView().setSelectedButton(options.page ?? DEFAULT_PAGE);
this.view.getRightTopWrapper().append(this.pagination.getHTML());
}

this.productFilters?.updateParams(productsInfo);
this.productFilters?.getView().updateParams(productsInfo);
const priceRange = await getProductModel().getPriceRange();
this.productFilters?.getView().updatePriceRange(priceRange);
this.view.switchEmptyList(!productsInfo?.products?.length);
}

Expand Down
18 changes: 11 additions & 7 deletions src/widgets/ProductInfo/model/ProductInfoModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,17 @@ class ProductInfoModel {
sizeButton.getHTML().addEventListener('click', () => {
const currentVariant = this.params.variant.find(({ size }) => size === sizeButton.getHTML().textContent);

if (currentVariant) {
const path = `${buildPathName(PAGE_ID.PRODUCT_PAGE, this.params.key, {
size: [currentVariant.size ?? this.params.variant[0].size],
})}`;
RouterModel.getInstance().navigateTo(path);
modal.hide();
}
const path = `${buildPathName(PAGE_ID.PRODUCT_PAGE, this.params.key, {
size: [currentVariant?.size ?? this.params.variant[0].size],
})}`;
RouterModel.getInstance().navigateTo(path);
modal.hide();
this.currentVariant = currentVariant ?? this.params.variant[0];
this.params.currentSize = currentVariant?.size ?? this.params.variant[0].size;
this.view.updateParams(this.params);
this.price.getHTML().remove();
this.price = new ProductPriceModel(this.currentVariant);
this.view.getRightWrapper().append(this.price.getHTML());
});
});
}
Expand Down
24 changes: 22 additions & 2 deletions src/widgets/ProductInfo/view/ProductInfoView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import { BUTTON_TEXT } from '@/shared/constants/buttons.ts';
import { AUTOCOMPLETE_OPTION, LANGUAGE_CHOICE } from '@/shared/constants/common.ts';
import { INPUT_TYPE } from '@/shared/constants/forms.ts';
import { MESSAGE_STATUS, SERVER_MESSAGE_KEYS } from '@/shared/constants/messages.ts';
import { PRODUCT_INFO_TEXT } from '@/shared/constants/product.ts';
import { PRODUCT_INFO_TEXT, PRODUCT_INFO_TEXT_KEYS } from '@/shared/constants/product.ts';
import { LOADER_SIZE } from '@/shared/constants/sizes.ts';
import SVG_DETAILS from '@/shared/constants/svg.ts';
import createBaseElement from '@/shared/utils/createBaseElement.ts';
import createSVGUse from '@/shared/utils/createSVGUse.ts';
import observeCurrentLanguage from '@/shared/utils/observeCurrentLanguage.ts';
import showErrorMessage from '@/shared/utils/userMessage.ts';

import './productInfoView.scss';
Expand Down Expand Up @@ -136,10 +137,12 @@ class ProductInfoView {
private createCategoriesSpan(): HTMLSpanElement {
this.categoriesSpan = createBaseElement({
cssClasses: ['categoriesSpan'],
innerContent: 'Categories: ',
innerContent: PRODUCT_INFO_TEXT[getStore().getState().currentLanguage].CATEGORY,
tag: 'span',
});

observeCurrentLanguage(this.categoriesSpan, PRODUCT_INFO_TEXT, PRODUCT_INFO_TEXT_KEYS.CATEGORY);

const category =
this.params.category[0].parent?.name[Number(getStore().getState().currentLanguage === LANGUAGE_CHOICE.RU)].value;
const subcategory =
Expand Down Expand Up @@ -242,6 +245,8 @@ class ProductInfoView {
tag: 'span',
});

observeCurrentLanguage(difficultySpan, PRODUCT_INFO_TEXT, PRODUCT_INFO_TEXT_KEYS.DIFFICULTY);

difficultySpan.append(...this.createDifficultyPoints());
this.rightWrapper.append(difficultySpan);
}
Expand Down Expand Up @@ -317,6 +322,16 @@ class ProductInfoView {
button.setDisabled();
button.getHTML().classList.add('selected');
}

button.getHTML().addEventListener('click', () => {
this.sizeButtons.forEach((btn) => {
btn.setEnabled();
btn.getHTML().classList.remove('selected');
});
button.getHTML().classList.add('selected');
button.setDisabled();
});

this.sizeButtons.push(button);
return button;
}
Expand Down Expand Up @@ -490,6 +505,11 @@ class ProductInfoView {
this.switchToCartButton.getHTML().textContent = BUTTON_TEXT[getStore().getState().currentLanguage].ADD_PRODUCT;
}
}

public updateParams(params: ProductInfoParams): void {
this.params = params;
this.hasProductInToCart();
}
}

export default ProductInfoView;

0 comments on commit 5c48af6

Please sign in to comment.