Skip to content

Commit

Permalink
refactor(RSS-ECOMM-5_18): router (#358)
Browse files Browse the repository at this point in the history
* refactor: put methods for search parameters into separate helper functions

* refactor: use key instead of id to display categories in URL
  • Loading branch information
Kleostro authored Jun 10, 2024
1 parent b543802 commit fd6c1b5
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 102 deletions.
9 changes: 9 additions & 0 deletions src/app/Router/helpers/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const remove = (url: URL, key: string | string[]): void => {
if (Array.isArray(key)) {
key.forEach((k) => url.searchParams.delete(k));
} else {
url.searchParams.delete(key);
}
};
export const append = (url: URL, key: string, value: string): void => url.searchParams.append(key, value);
export const set = (url: URL, key: string, value: string): void => url.searchParams.set(key, value);
38 changes: 11 additions & 27 deletions src/app/Router/model/RouterModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ class RouterModel {
return;
}

this.handleRequest(currentPage, currentPath);
this.handleRequest(currentPage, currentPath).catch(showErrorMessage);
});
}

public static appendSearchParams(key: string, value: string): void {
public static changeSearchParams(callback: (url: URL) => void): void {
const url = new URL(decodeURIComponent(window.location.href));
url.searchParams.append(key, value);
callback(url);
const path = url.pathname + url.search.toString();
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
}
Expand All @@ -62,13 +62,6 @@ class RouterModel {
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
}

public static deleteSearchParams(key: string): void {
const url = new URL(decodeURIComponent(window.location.href));
url.searchParams.delete(key);
const path = url.pathname + url.search.toString();
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
}

public static getCurrentPage(): string {
return window.location.pathname.slice(PATH_SEGMENTS_TO_KEEP).split(DEFAULT_SEGMENT)[NEXT_SEGMENT];
}
Expand Down Expand Up @@ -98,14 +91,6 @@ class RouterModel {
return new URL(decodeURIComponent(window.location.href)).searchParams;
}

public static setSearchParams(key: string, value: string): void {
const url = new URL(decodeURIComponent(window.location.href));
url.searchParams.delete(key);
url.searchParams.set(key, value);
const path = url.pathname + url.search.toString();
window.history.pushState({ path: path.slice(NEXT_SEGMENT) }, '', path);
}

private async checkPageAndParams(currentPage: string, path: string): Promise<boolean | null> {
const hasRoute = this.routes.has(currentPage);

Expand All @@ -120,14 +105,13 @@ class RouterModel {
return hasRoute;
}

private handleRequest(currentPage: string, path: string): void {
this.checkPageAndParams(currentPage, path)
.then((check) => {
if (check) {
this.routes.get(currentPage)?.().catch(showErrorMessage);
}
})
.catch(showErrorMessage);
private async handleRequest(currentPage: string, path: string): Promise<void> {
try {
await this.checkPageAndParams(currentPage, path);
this.routes.get(currentPage)?.().catch(showErrorMessage);
} catch (error) {
showErrorMessage(error);
}
}

public navigateTo(path: string): void {
Expand All @@ -136,7 +120,7 @@ class RouterModel {
PAGE_ID.DEFAULT_PAGE;

if (currentPage !== getStore().getState().currentPage || currentPage === PAGE_ID.DEFAULT_PAGE) {
this.handleRequest(currentPage, path);
this.handleRequest(currentPage, path).catch(showErrorMessage);
}
history.pushState({ path }, '', `/${path}`);
}
Expand Down
11 changes: 7 additions & 4 deletions src/entities/ProductModalSlider/model/ProductModalSliderModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ProductInfoParams } from '@/shared/types/product.ts';

import { set } from '@/app/Router/helpers/helpers.ts';
import RouterModel from '@/app/Router/model/RouterModel.ts';
import { SEARCH_PARAMS_FIELD } from '@/shared/constants/product.ts';
import Swiper from 'swiper';
Expand Down Expand Up @@ -53,9 +54,9 @@ class ProductModalSliderModel {
const slideInSearch = Number(RouterModel.getSearchParams().get(SEARCH_PARAMS_FIELD.SLIDE));

if (slideInSearch < this.view.getModalSliderSlides().length) {
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.SLIDE, String(slideInSearch + 1));
RouterModel.changeSearchParams((url) => set(url, SEARCH_PARAMS_FIELD.SLIDE, String(slideInSearch + 1)));
} else {
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.SLIDE, String(1));
RouterModel.changeSearchParams((url) => set(url, SEARCH_PARAMS_FIELD.SLIDE, String(1)));
}
});
}
Expand All @@ -66,9 +67,11 @@ class ProductModalSliderModel {
const slideInSearch = Number(RouterModel.getSearchParams().get(SEARCH_PARAMS_FIELD.SLIDE));

if (slideInSearch > 1) {
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.SLIDE, String(slideInSearch - 1));
RouterModel.changeSearchParams((url) => set(url, SEARCH_PARAMS_FIELD.SLIDE, String(slideInSearch - 1)));
} else {
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.SLIDE, String(this.view.getModalSliderSlides().length));
RouterModel.changeSearchParams((url) =>
set(url, SEARCH_PARAMS_FIELD.SLIDE, String(this.view.getModalSliderSlides().length)),
);
}
});
}
Expand Down
86 changes: 49 additions & 37 deletions src/features/ProductFilters/view/ProductFiltersView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { SizeProductCount } from '@/shared/API/types/type';
import type { Category } from '@/shared/types/product';
import type ProductFiltersParams from '@/shared/types/productFilters';

import { append, remove, set } from '@/app/Router/helpers/helpers.ts';
import RouterModel from '@/app/Router/model/RouterModel.ts';
import ButtonModel from '@/shared/Button/model/ButtonModel.ts';
import EventMediatorModel from '@/shared/EventMediator/model/EventMediatorModel.ts';
Expand All @@ -14,6 +15,7 @@ import { AUTOCOMPLETE_OPTION, LANGUAGE_CHOICE } from '@/shared/constants/common.
import MEDIATOR_EVENT from '@/shared/constants/events.ts';
import { META_FILTERS, META_FILTERS_ID, PRICE_RANGE_LABEL, TITLE } from '@/shared/constants/filters.ts';
import { INPUT_TYPE } from '@/shared/constants/forms.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { SEARCH_PARAMS_FIELD } from '@/shared/constants/product.ts';
import createBaseElement from '@/shared/utils/createBaseElement.ts';
import * as noUiSlider from 'nouislider';
Expand Down Expand Up @@ -66,16 +68,17 @@ class ProductFiltersView {

private categoryClickHandler(parentCategory: { category: Category; count: number } | null): void {
const searchParams = RouterModel.getSearchParams();
if (
searchParams.has(SEARCH_PARAMS_FIELD.CATEGORY) &&
searchParams.get(SEARCH_PARAMS_FIELD.CATEGORY) === parentCategory?.category.parent?.id
) {
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.CATEGORY);
} else {
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.CATEGORY, parentCategory?.category.parent?.id ?? '');
}

RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.PAGE);
const handleCategoryChange = (url: URL, key: string, value: string): void => {
if (searchParams.has(key) && searchParams.get(key) === value) {
remove(url, key);
} else {
set(url, key, value);
}
};
RouterModel.changeSearchParams((url) => {
handleCategoryChange(url, SEARCH_PARAMS_FIELD.CATEGORY, parentCategory?.category.parent?.key ?? '');
remove(url, SEARCH_PARAMS_FIELD.PAGE);
});

this.callback();
}
Expand Down Expand Up @@ -132,7 +135,7 @@ class ProductFiltersView {

const span = createBaseElement({
attributes: {
id: category.category.id,
id: category.category.key,
},
cssClasses: [styles.categoryLinkCount],
innerContent,
Expand Down Expand Up @@ -264,8 +267,10 @@ class ProductFiltersView {
link.getHTML().addEventListener('click', (event) => {
event.preventDefault();

RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.META, id);
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.PAGE);
RouterModel.changeSearchParams((url) => {
set(url, SEARCH_PARAMS_FIELD.META, id);
remove(url, SEARCH_PARAMS_FIELD.PAGE);
});
this.metaLinks.forEach((link) => this.switchSelectedFilter(link, false));
this.switchSelectedFilter(link, true);
this.callback();
Expand Down Expand Up @@ -335,10 +340,11 @@ class ProductFiltersView {
const [min, max] = values;
this.priceInputs.get(PRICE_RANGE_LABEL[getStore().getState().currentLanguage].FROM)?.setValue(String(min));
this.priceInputs.get(PRICE_RANGE_LABEL[getStore().getState().currentLanguage].TO)?.setValue(String(max));
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.MIN_PRICE);
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.MAX_PRICE);
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.MIN_PRICE, String(min));
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.MAX_PRICE, String(max));
RouterModel.changeSearchParams((url) => {
remove(url, [SEARCH_PARAMS_FIELD.MIN_PRICE, SEARCH_PARAMS_FIELD.MAX_PRICE]);
set(url, SEARCH_PARAMS_FIELD.MIN_PRICE, String(min));
set(url, SEARCH_PARAMS_FIELD.MAX_PRICE, String(max));
});
this.callback();
});

Expand Down Expand Up @@ -394,7 +400,7 @@ class ProductFiltersView {
}
});

RouterModel.clearSearchParams();
RouterModel.getInstance().navigateTo(PAGE_ID.CATALOG_PAGE);
EventMediatorModel.getInstance().notify(MEDIATOR_EVENT.CLEAR_CATALOG_SEARCH, '');
this.callback();
});
Expand All @@ -418,8 +424,10 @@ class ProductFiltersView {

sizeLink.getHTML().addEventListener('click', (event) => {
event.preventDefault();
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.PAGE);
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.SIZE, size.size);
RouterModel.changeSearchParams((url) => {
remove(url, SEARCH_PARAMS_FIELD.PAGE);
set(url, SEARCH_PARAMS_FIELD.SIZE, size.size);
});
this.sizeLinks.forEach((link) => this.switchSelectedFilter(link, false));
this.switchSelectedFilter(sizeLink, true);
this.callback();
Expand Down Expand Up @@ -516,7 +524,7 @@ class ProductFiltersView {

private redrawProductsCount(): void {
this.params?.categoriesProductCount?.forEach((categoryCount) => {
const currentSpan = this.categoryCountSpan.find((span) => span.id === categoryCount.category.id) ?? null;
const currentSpan = this.categoryCountSpan.find((span) => span.id === categoryCount.category.key) ?? null;
if (currentSpan) {
currentSpan.innerText = `(${categoryCount.count})`;
}
Expand All @@ -539,30 +547,32 @@ class ProductFiltersView {
}

private subcategoryClickHandler(subcategory: { category: Category; count: number }): void {
const currentSubcategories = RouterModel.getSearchParams().getAll(SEARCH_PARAMS_FIELD.SUBCATEGORY);
const currentSubcategory = currentSubcategories?.find((id) => id === subcategory.category.id);
const currentLink = this.categoryLinks.find((link) => link.getHTML().id === subcategory.category.id);
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.PAGE);
const subcategories = RouterModel.getSearchParams().getAll(SEARCH_PARAMS_FIELD.SUBCATEGORY);
const currentSubcategory = subcategories?.find((key) => key === subcategory.category.key);
const currentLink = this.categoryLinks.find((link) => link.getHTML().id === subcategory.category.key);
RouterModel.changeSearchParams((url) => remove(url, SEARCH_PARAMS_FIELD.PAGE));

if (currentSubcategory) {
const filteredSubcategories = currentSubcategories.filter((id) => id !== currentSubcategory);
if (!filteredSubcategories.length) {
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.SUBCATEGORY);
const filteredSubcategory = subcategories.filter((key) => key !== currentSubcategory);
if (!filteredSubcategory.length) {
RouterModel.changeSearchParams((url) => remove(url, SEARCH_PARAMS_FIELD.SUBCATEGORY));
if (currentLink) {
this.switchSelectedFilter(currentLink, false);
}
} else {
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.SUBCATEGORY);
filteredSubcategories.forEach((id) => RouterModel.appendSearchParams(SEARCH_PARAMS_FIELD.SUBCATEGORY, id));
filteredSubcategories.forEach((id) => {
const currentLink = this.categoryLinks.find((link) => link.getHTML().id === id);
RouterModel.changeSearchParams((url) => remove(url, SEARCH_PARAMS_FIELD.SUBCATEGORY));
filteredSubcategory.forEach((key) =>
RouterModel.changeSearchParams((url) => append(url, SEARCH_PARAMS_FIELD.SUBCATEGORY, key)),
);
filteredSubcategory.forEach((key) => {
const currentLink = this.categoryLinks.find((link) => link.getHTML().id === key);
if (currentLink) {
this.switchSelectedFilter(currentLink, true);
}
});
}
} else {
RouterModel.appendSearchParams(SEARCH_PARAMS_FIELD.SUBCATEGORY, subcategory.category.id);
RouterModel.changeSearchParams((url) => append(url, SEARCH_PARAMS_FIELD.SUBCATEGORY, subcategory.category.key));
if (currentLink) {
this.switchSelectedFilter(currentLink, true);
}
Expand All @@ -584,8 +594,10 @@ class ProductFiltersView {
}

private updateSelectedPrice(from: InputModel | null = null, to: InputModel | null = null): void {
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.MIN_PRICE, from?.getValue() ?? '0');
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.MAX_PRICE, to?.getValue() ?? '0');
RouterModel.changeSearchParams((url) => {
set(url, SEARCH_PARAMS_FIELD.MIN_PRICE, from?.getValue() ?? '0');
set(url, SEARCH_PARAMS_FIELD.MAX_PRICE, to?.getValue() ?? '0');
});
this.callback();
}

Expand Down Expand Up @@ -630,8 +642,8 @@ class ProductFiltersView {
this.categoryLinks.forEach((link) => this.switchSelectedFilter(link, false));
this.metaLinks.forEach((link) => this.switchSelectedFilter(link, false));

activeFilters.categoryLinks.forEach((id) => {
const currentLink = this.categoryLinks.find((link) => link.getHTML().id === id);
activeFilters.categoryLinks.forEach((key) => {
const currentLink = this.categoryLinks.find((link) => link.getHTML().id === key);
if (currentLink) {
this.switchSelectedFilter(currentLink, true);
}
Expand Down
7 changes: 5 additions & 2 deletions src/features/ProductSearch/model/ProductSearchModel.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { remove, set } from '@/app/Router/helpers/helpers.ts';
import RouterModel from '@/app/Router/model/RouterModel.ts';
import EventMediatorModel from '@/shared/EventMediator/model/EventMediatorModel.ts';
import MEDIATOR_EVENT from '@/shared/constants/events.ts';
Expand All @@ -19,8 +20,10 @@ class ProductSearchModel {
}

private handleSearchInput(): void {
RouterModel.deleteSearchParams(SEARCH_PARAMS_FIELD.PAGE);
RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.SEARCH, this.view.getSearchField().getValue());
RouterModel.changeSearchParams((url) => {
remove(url, SEARCH_PARAMS_FIELD.PAGE);
set(url, SEARCH_PARAMS_FIELD.SEARCH, this.view.getSearchField().getValue());
});
this.callback();
}

Expand Down
11 changes: 5 additions & 6 deletions src/features/ProductSorts/view/ProductSortsView.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { set } from '@/app/Router/helpers/helpers.ts';
import RouterModel from '@/app/Router/model/RouterModel.ts';
import { SortDirection } from '@/shared/API/types/type.ts';
import LinkModel from '@/shared/Link/model/LinkModel.ts';
Expand Down Expand Up @@ -99,12 +100,10 @@ class ProductSortsView {
link.getHTML().classList.add(styles.activeLink);

this.currentSortingSpan.innerText = text;

RouterModel.setSearchParams(SEARCH_PARAMS_FIELD.FIELD, link.getHTML().id);
RouterModel.setSearchParams(
SEARCH_PARAMS_FIELD.DIRECTION,
String(link.getHTML().getAttribute(DATA_KEYS.DIRECTION)),
);
RouterModel.changeSearchParams((url) => {
set(url, SEARCH_PARAMS_FIELD.FIELD, link.getHTML().id);
set(url, SEARCH_PARAMS_FIELD.DIRECTION, String(link.getHTML().getAttribute(DATA_KEYS.DIRECTION)));
});
this.callback();
});

Expand Down
4 changes: 2 additions & 2 deletions src/pages/ProductPage/model/ProductPageModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ class ProductPageModel implements Page {

if (category) {
links.push({
link: buildPath.catalogPathWithQuery({ category: [category.id] }),
link: buildPath.catalogPathWithQuery({ category: [category.key] }),
name: category.name[Number(isRuLanguage)].value,
});
}

if (subcategory && category) {
links.push({
link: buildPath.catalogPathWithQuery({ subcategory: [subcategory.id] }),
link: buildPath.catalogPathWithQuery({ subcategory: [subcategory.key] }),
name: subcategory.name[Number(isRuLanguage)].value,
});
}
Expand Down
Loading

0 comments on commit fd6c1b5

Please sign in to comment.