Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(RSS-ECOMM-5_14): wishlist and catalog pages #349

Merged
merged 3 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/Router/model/RouterModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class RouterModel {

return {
hasRoute,
params: { [currentPage.slice(PATH_SEGMENTS_TO_KEEP)]: { id } },
params: { [currentPage]: { id } },
};
}

Expand Down
8 changes: 5 additions & 3 deletions src/entities/ProductCard/model/ProductCardModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import getCartModel from '@/shared/API/cart/model/CartModel.ts';
import modal from '@/shared/Modal/model/ModalModel.ts';
import getStore from '@/shared/Store/Store.ts';
import { LANGUAGE_CHOICE } from '@/shared/constants/common.ts';
import { buildProductPathName } from '@/shared/utils/buildPathname.ts';
import * as buildPath from '@/shared/utils/buildPathname.ts';
import { productAddedToCartMessage } from '@/shared/utils/messageTemplates.ts';
import { showErrorMessage, showSuccessMessage } from '@/shared/utils/userMessage.ts';
import ProductInfoModel from '@/widgets/ProductInfo/model/ProductInfoModel.ts';
Expand All @@ -33,7 +33,7 @@ class ProductCardModel {
this.currentSize = currentSize ?? this.params.variant[0].size;
this.currentVariant = this.params.variant.find(({ size }) => size === currentSize) ?? this.params.variant[0];
this.view = new ProductCardView(params, currentSize);
this.price = new ProductPriceModel(this.currentVariant);
this.price = new ProductPriceModel({ new: this.currentVariant.discount, old: this.currentVariant.price });
this.wishlistButton = new WishlistButtonModel(this.params);
this.init(cart);
}
Expand Down Expand Up @@ -61,7 +61,9 @@ class ProductCardModel {
const goDetailsPageLink = this.view.getGoDetailsPageLink();
goDetailsPageLink.getHTML().addEventListener('click', (event) => {
event.preventDefault();
const path = buildProductPathName(this.params.key, { size: [this.currentSize ?? this.params.variant[0].size] });
const path = buildPath.productPathWithIDAndQuery(this.params.key, {
size: [this.currentSize ?? this.params.variant[0].size],
});
RouterModel.getInstance().navigateTo(path);
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/entities/ProductCard/view/ProductCardView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { LANGUAGE_CHOICE } from '@/shared/constants/common.ts';
import { PRODUCT_INFO_TEXT } from '@/shared/constants/product.ts';
import { LOADER_SIZE } from '@/shared/constants/sizes.ts';
import SVG_DETAILS from '@/shared/constants/svg.ts';
import { buildProductPathName } from '@/shared/utils/buildPathname.ts';
import * as buildPath from '@/shared/utils/buildPathname.ts';
import createBaseElement from '@/shared/utils/createBaseElement.ts';
import createSVGUse from '@/shared/utils/createSVGUse.ts';

Expand Down Expand Up @@ -134,7 +134,7 @@ class ProductCardView {
}

private createGoDetailsPageLink(): LinkModel {
const href = `${buildProductPathName(this.params.key, { size: [this.currentSize ?? this.params.variant[0].size] })}`;
const href = `${buildPath.productPathWithIDAndQuery(this.params.key, { size: [this.currentSize ?? this.params.variant[0].size] })}`;

this.goDetailsPageLink = new LinkModel({
attrs: {
Expand Down
1 change: 1 addition & 0 deletions src/entities/ProductCard/view/productCardView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
width: 100%;
max-width: 90%;
font: var(--regular-font);
line-height: 150%;
letter-spacing: var(--one);
text-align: left;
text-overflow: ellipsis;
Expand Down
6 changes: 2 additions & 4 deletions src/entities/ProductPrice/model/ProductPriceModel.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import type { Variant } from '@/shared/types/product.ts';

import ProductPriceView from '../view/ProductPriceView.ts';

class ProductPriceModel {
private view: ProductPriceView;

constructor(currentVariant: Variant) {
this.view = new ProductPriceView(currentVariant);
constructor(params: { new: number; old: number }) {
this.view = new ProductPriceView(params);
this.init();
}

Expand Down
18 changes: 7 additions & 11 deletions src/entities/ProductPrice/view/ProductPriceView.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,32 @@
import type { Variant } from '@/shared/types/product';

import createBaseElement from '@/shared/utils/createBaseElement.ts';

import './productPriceView.scss';

class ProductPriceView {
private basicPrice: HTMLSpanElement;

private currentVariant: Variant;

private oldPrice: HTMLSpanElement;

private params: { new: number; old: number };

private view: HTMLDivElement;

constructor(currentVariant: Variant) {
this.currentVariant = currentVariant;
constructor(params: { new: number; old: number }) {
this.params = params;
this.basicPrice = this.createBasicPrice();
this.oldPrice = this.createOldPrice();
this.view = this.createHTML();
}

private createBasicPrice(): HTMLSpanElement {
const innerContent = this.currentVariant.discount
? `$${this.currentVariant.discount.toFixed(2)}`
: `$${this.currentVariant.price?.toFixed(2)}`;
const innerContent = this.params.new ? `$${this.params.new.toFixed(2)}` : `$${this.params.old?.toFixed(2)}`;
this.basicPrice = createBaseElement({
cssClasses: ['basicPrice'],
innerContent,
tag: 'span',
});

if (!this.currentVariant.discount) {
if (!this.params.new) {
this.basicPrice.classList.add('gray');
}

Expand All @@ -48,7 +44,7 @@ class ProductPriceView {
}

private createOldPrice(): HTMLSpanElement {
const innerContent = this.currentVariant.discount ? `$${this.currentVariant.price?.toFixed(2)}` : '';
const innerContent = this.params.new ? `$${this.params.old.toFixed(2)}` : '';
this.oldPrice = createBaseElement({
cssClasses: ['oldPrice'],
innerContent,
Expand Down
15 changes: 9 additions & 6 deletions src/pages/ProductPage/model/ProductPageModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts'
import { LANGUAGE_CHOICE } from '@/shared/constants/common.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { SEARCH_PARAMS_FIELD } from '@/shared/constants/product.ts';
import { buildCatalogPathName, buildMainPathName } from '@/shared/utils/buildPathname.ts';
import * as buildPath from '@/shared/utils/buildPathname.ts';
import { showErrorMessage } from '@/shared/utils/userMessage.ts';
import ProductInfoModel from '@/widgets/ProductInfo/model/ProductInfoModel.ts';

Expand All @@ -30,18 +30,21 @@ class ProductPageModel implements Page {
private createBreadcrumbLinks(currentProduct: Product): BreadcrumbLink[] {
const category = currentProduct.category[0].parent;
const subcategory = currentProduct.category[0];
const links = [
{ link: buildMainPathName(), name: PAGE_ID.MAIN_PAGE.toString() },
{ link: buildCatalogPathName(), name: PAGE_ID.CATALOG_PAGE.toString() },
const links: BreadcrumbLink[] = [
{ link: PAGE_ID.MAIN_PAGE, name: PAGE_ID.MAIN_PAGE.toString() },
{ link: PAGE_ID.CATALOG_PAGE, name: PAGE_ID.CATALOG_PAGE.toString() },
];

if (category) {
links.push({ link: buildCatalogPathName(null, { category: [category.id] }), name: category.name[0].value });
links.push({
link: buildPath.catalogPathWithIDAndQuery(null, { category: [category.id] }),
name: category.name[0].value,
});
}

if (subcategory && category) {
links.push({
link: buildCatalogPathName(null, { category: [category.id], subcategory: [subcategory.id] }),
link: buildPath.catalogPathWithIDAndQuery(null, { category: [category.id], subcategory: [subcategory.id] }),
name: subcategory.name[0].value,
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/pages/ProductPage/view/productPageView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@

.fullDescriptionWrapper {
font: var(--regular-font);
line-height: 170%;
letter-spacing: var(--one);
color: var(--steam-green-400);
}

.fullDescription {
font: var(--regular-font);
line-height: 170%;
color: var(--noble-gray-800);
}
7 changes: 3 additions & 4 deletions src/pages/WishlistPage/model/WishlistPageModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import BreadcrumbsModel from '@/features/Breadcrumbs/model/BreadcrumbsModel.ts';
import getStore from '@/shared/Store/Store.ts';
import { setCurrentPage } from '@/shared/Store/actions.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { buildCatalogPathName, buildMainPathName, buildWishlistPathName } from '@/shared/utils/buildPathname.ts';

import WishlistPageView from '../view/WishlistPageView.ts';

Expand All @@ -21,9 +20,9 @@ class WishlistPageModel implements Page {

private createBreadcrumbLinksData(): BreadcrumbLink[] {
return [
{ link: buildMainPathName(), name: PAGE_ID.MAIN_PAGE.toString() },
{ link: buildCatalogPathName(), name: PAGE_ID.CATALOG_PAGE.toString() },
{ link: buildWishlistPathName(), name: PAGE_ID.WISHLIST_PAGE.toString() },
{ link: PAGE_ID.MAIN_PAGE, name: PAGE_ID.MAIN_PAGE.toString() },
{ link: PAGE_ID.CATALOG_PAGE, name: PAGE_ID.CATALOG_PAGE.toString() },
{ link: PAGE_ID.WISHLIST_PAGE, name: PAGE_ID.WISHLIST_PAGE.toString() },
];
}

Expand Down
19 changes: 15 additions & 4 deletions src/pages/WishlistPage/view/WishlistPageView.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { ProductWithCount } from '@/shared/API/types/type';
import type { Cart } from '@/shared/types/cart';
import type { OptionsRequest, ProductWithCount } from '@/shared/API/types/type.ts';
import type { Cart } from '@/shared/types/cart.ts';
import type { ShoppingList } from '@/shared/types/shopping-list';

import ProductCardModel from '@/entities/ProductCard/model/ProductCardModel.ts';
import getCartModel from '@/shared/API/cart/model/CartModel.ts';
import getProductModel from '@/shared/API/product/model/ProductModel.ts';
import FilterProduct from '@/shared/API/product/utils/filter.ts';
import getShoppingListModel from '@/shared/API/shopping-list/model/ShoppingListModel.ts';
import { FilterFields } from '@/shared/API/types/type.ts';
import EventMediatorModel from '@/shared/EventMediator/model/EventMediatorModel.ts';
import LoaderModel from '@/shared/Loader/model/LoaderModel.ts';
import getStore from '@/shared/Store/Store.ts';
Expand Down Expand Up @@ -76,11 +78,20 @@ class WishlistPageView {
const loader = new LoaderModel(LOADER_SIZE.EXTRA_LARGE);
loader.setAbsolutePosition();
this.wishlist.append(loader.getHTML());
const [shoppingList, cart, products] = await Promise.all([
const [shoppingList, cart] = await Promise.all([
getShoppingListModel().getShoppingList(),
getCartModel().getCart(),
getProductModel().getProducts(),
]);

const filter = new FilterProduct();
shoppingList.products.forEach((product) => {
filter.addFilter(FilterFields.ID, product.productId);
});
const options: OptionsRequest = {
filter,
limit: shoppingList.products.length,
};
const products = await getProductModel().getProducts(options);
loader.getHTML().remove();
this.drawWishlistItems(shoppingList, cart, products);
this.switchEmptyList(!shoppingList.products.length);
Expand Down
69 changes: 58 additions & 11 deletions src/shared/utils/buildPathname.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,71 @@
import { PAGE_ID } from '../constants/pages.ts';

export const buildPathName = (
type QueryParamsType = {
[key: string]: (null | string)[];
};

type BuildQuery = (queryParams: QueryParamsType | null) => string;

type BuildPathWithID = (endpoint: null | string, id: null | string) => string;
type BuildPathWithIDAndQuery = (
endpoint: null | string,
id: null | string = null,
queryParams: { [key: string]: (null | string)[] } | null = null,
): string => {
id: null | string,
queryParams: QueryParamsType | null,
) => string;
type BuildPathWithIDAndQueryAndHash = (
endpoint: null | string,
id: null | string,
queryParams: QueryParamsType | null,
hash: null | string,
) => string;

export const buildPathWithID: BuildPathWithID = (endpoint, id = null) =>
`${endpoint ? `${endpoint}` : ''}${id ? `/${id}` : ''}`;

export const buildQuery: BuildQuery = (queryParams) => {
if (!queryParams) {
return `${endpoint ? `${endpoint}` : ''}${id ? `/${id} ` : ''}`;
return '';
}

const queryString = Object.entries(queryParams)
.filter(([, values]) => values.some(Boolean))
.map(([key, values]) => `${key}=${values.filter(Boolean).join('_')}`)
.join('&');

return `${endpoint ? `${endpoint}` : ''}${id ? `/${id}` : ''}${queryString ? `${`?${queryString}`}` : ''}`;
return queryString ? `?${queryString}` : '';
};

export const buildMainPathName = buildPathName.bind(null, PAGE_ID.MAIN_PAGE);
export const buildCatalogPathName = buildPathName.bind(null, PAGE_ID.CATALOG_PAGE);
export const buildProductPathName = buildPathName.bind(null, PAGE_ID.PRODUCT_PAGE);
export const buildWishlistPathName = buildPathName.bind(null, PAGE_ID.WISHLIST_PAGE);
export const buildCartPathName = buildPathName.bind(null, PAGE_ID.CART_PAGE);
export const buildPathWithIDAndQuery: BuildPathWithIDAndQuery = (endpoint, id = null, queryParams = null) => {
const pathWithID = buildPathWithID(endpoint, id);
const queryPart = buildQuery(queryParams);
return `${pathWithID}${queryPart}`;
};

export const buildPathWithIDAndQueryAndHash: BuildPathWithIDAndQueryAndHash = (
endpoint,
id = null,
queryParams = null,
hash = null,
) => {
const pathWithIDAndQuery = buildPathWithIDAndQuery(endpoint, id, queryParams);
const hashPart = hash ? `#${hash}` : '';
return `${pathWithIDAndQuery}${hashPart}`;
};

export const mainPathWithID = buildPathWithID.bind(null, PAGE_ID.MAIN_PAGE);
export const catalogPathWithID = buildPathWithID.bind(null, PAGE_ID.CATALOG_PAGE);
export const productPathWithID = buildPathWithID.bind(null, PAGE_ID.PRODUCT_PAGE);
export const wishlistPathWithID = buildPathWithID.bind(null, PAGE_ID.WISHLIST_PAGE);
export const cartPathWithID = buildPathWithID.bind(null, PAGE_ID.CART_PAGE);

export const mainPathWithIDAndQuery = buildPathWithIDAndQuery.bind(null, PAGE_ID.MAIN_PAGE);
export const catalogPathWithIDAndQuery = buildPathWithIDAndQuery.bind(null, PAGE_ID.CATALOG_PAGE);
export const productPathWithIDAndQuery = buildPathWithIDAndQuery.bind(null, PAGE_ID.PRODUCT_PAGE);
export const wishlistPathWithIDAndQuery = buildPathWithIDAndQuery.bind(null, PAGE_ID.WISHLIST_PAGE);
export const cartPathWithIDAndQuery = buildPathWithIDAndQuery.bind(null, PAGE_ID.CART_PAGE);

export const mainPathWithIDAndQueryAndHash = buildPathWithIDAndQueryAndHash.bind(null, PAGE_ID.MAIN_PAGE);
export const catalogPathWithIDAndQueryAndHash = buildPathWithIDAndQueryAndHash.bind(null, PAGE_ID.CATALOG_PAGE);
export const productPathWithIDAndQueryAndHash = buildPathWithIDAndQueryAndHash.bind(null, PAGE_ID.PRODUCT_PAGE);
export const wishlistPathWithIDAndQueryAndHash = buildPathWithIDAndQueryAndHash.bind(null, PAGE_ID.WISHLIST_PAGE);
export const cartPathWithIDAndQueryAndHash = buildPathWithIDAndQueryAndHash.bind(null, PAGE_ID.CART_PAGE);
4 changes: 2 additions & 2 deletions src/widgets/Footer/model/FooterModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Category } from '@/shared/types/product.ts';
import getProductModel from '@/shared/API/product/model/ProductModel.ts';
import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { buildCatalogPathName } from '@/shared/utils/buildPathname.ts';
import * as buildPath from '@/shared/utils/buildPathname.ts';
import { showErrorMessage } from '@/shared/utils/userMessage.ts';

import FooterView from '../view/FooterView.ts';
Expand Down Expand Up @@ -92,7 +92,7 @@ function generateRandomCategoryLink(categoriesArr: Category[]): Link[] {
const category = subCategory[randomIndex];
subCategory.splice(randomIndex, 1);
result.push({
href: buildCatalogPathName(null, { subcategory: [category.id] }),
href: buildPath.catalogPathWithIDAndQuery(null, { subcategory: [category.id] }),
name: {
en: category.name[0].value,
ru: category.name[1].value,
Expand Down
8 changes: 4 additions & 4 deletions src/widgets/ProductInfo/model/ProductInfoModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import getStore from '@/shared/Store/Store.ts';
import { LANGUAGE_CHOICE } from '@/shared/constants/common.ts';
import MEDIATOR_EVENT from '@/shared/constants/events.ts';
import { LOADER_SIZE } from '@/shared/constants/sizes.ts';
import { buildProductPathName } from '@/shared/utils/buildPathname.ts';
import * as buildPath from '@/shared/utils/buildPathname.ts';
import { productAddedToCartMessage, productRemovedFromCartMessage } from '@/shared/utils/messageTemplates.ts';
import { showErrorMessage, showSuccessMessage } from '@/shared/utils/userMessage.ts';
import Swiper from 'swiper';
Expand Down Expand Up @@ -47,7 +47,7 @@ class ProductInfoModel {
this.view = new ProductInfoView(this.params);
this.currentVariant =
this.params.variant.find(({ size }) => size === this.params.currentSize) ?? this.params.variant[0];
this.price = new ProductPriceModel(this.currentVariant);
this.price = new ProductPriceModel({ new: this.currentVariant.discount, old: this.currentVariant.price });
this.wishlistButton = new WishlistButtonModel(this.params);
this.init();
}
Expand Down Expand Up @@ -134,14 +134,14 @@ class ProductInfoModel {
this.view.getSizeButtons().forEach((sizeButton) => {
sizeButton.getHTML().addEventListener('click', () => {
const currentVariant = this.params.variant.find(({ size }) => size === sizeButton.getHTML().textContent);
const path = `${buildProductPathName(this.params.key, { size: [currentVariant?.size ?? this.params.variant[0].size] })}`;
const path = `${buildPath.productPathWithIDAndQuery(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.price = new ProductPriceModel({ new: this.currentVariant.discount, old: this.currentVariant.price });
this.view.getRightWrapper().append(this.price.getHTML());
});
});
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/ProductInfo/view/productInfoView.scss
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
.shortDescriptionWrapper {
order: 3;
font: var(--regular-font);
line-height: 170%;
letter-spacing: var(--one);
color: var(--steam-green-400);

Expand All @@ -103,6 +104,7 @@
.shortDescription,
.fullDescription {
font: var(--regular-font);
line-height: 170%;
color: var(--noble-gray-800);

@media (max-width: 768px) {
Expand Down
Loading
Loading