diff --git a/src/app/App/model/AppModel.ts b/src/app/App/model/AppModel.ts index 0adc04b7..5dceb5ca 100644 --- a/src/app/App/model/AppModel.ts +++ b/src/app/App/model/AppModel.ts @@ -26,8 +26,8 @@ class AppModel { return new AboutUsPageModel(this.appView.getHTML()); }, [PAGE_ID.BLOG]: async (): Promise => { - const { default: PostListModel } = await import('@/pages/Blog/PostList/model/PostListModel.ts'); - return new PostListModel(this.appView.getHTML()); + const { default: BlogPageModel } = await import('@/pages/BlogPage/model/BlogPageModel.ts'); + return new BlogPageModel(this.appView.getHTML()); }, [PAGE_ID.CART_PAGE]: async (): Promise => { const { default: CartPageModel } = await import('@/pages/CartPage/model/CartPageModel.ts'); diff --git a/src/pages/Blog/Post/view/PostView.ts b/src/entities/Post/view/PostView.ts similarity index 96% rename from src/pages/Blog/Post/view/PostView.ts rename to src/entities/Post/view/PostView.ts index 1647263e..5a01b7fc 100644 --- a/src/pages/Blog/Post/view/PostView.ts +++ b/src/entities/Post/view/PostView.ts @@ -40,7 +40,7 @@ export default class PostView { private createCardInfoHTML(): string { const ln = getStore().getState().currentLanguage; const read = - ln === 'en' ? `Read in ${this.post.time.toString()} minutes` : `Читать за ${this.post.time.toString()} минуты`; + ln === 'en' ? `Read in ${this.post.time.toString()} min.` : `Читать за ${this.post.time.toString()} мин.`; const readMore = ln === 'en' ? 'Read more...' : 'Читать далее...'; const content = `
diff --git a/src/pages/Blog/Post/view/post.module.scss b/src/entities/Post/view/post.module.scss similarity index 99% rename from src/pages/Blog/Post/view/post.module.scss rename to src/entities/Post/view/post.module.scss index 26437cd1..b831e188 100644 --- a/src/pages/Blog/Post/view/post.module.scss +++ b/src/entities/Post/view/post.module.scss @@ -18,7 +18,7 @@ display: block; margin: 0 auto; width: 100%; - height: auto; + object-fit: cover; } .dataTime { diff --git a/src/pages/Blog/PostList/model/PostListModel.ts b/src/pages/BlogPage/model/BlogPageModel.ts similarity index 87% rename from src/pages/Blog/PostList/model/PostListModel.ts rename to src/pages/BlogPage/model/BlogPageModel.ts index e62cc6cd..967baf52 100644 --- a/src/pages/Blog/PostList/model/PostListModel.ts +++ b/src/pages/BlogPage/model/BlogPageModel.ts @@ -1,16 +1,16 @@ import type { Post } from '@/shared/constants/blog.ts'; import type { Page } from '@/shared/types/page.ts'; -import PostView from '@/pages/Blog/Post/view/PostView.ts'; +import PostView from '@/entities/Post/view/PostView.ts'; import getStore from '@/shared/Store/Store.ts'; import { setCurrentPage } from '@/shared/Store/actions.ts'; import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts'; import { PAGE_ID } from '@/shared/constants/pages.ts'; -import postsData from '../../data/posts.ts'; -import BlogPageView from '../view/PostListView.ts'; +import postsData from '../../../shared/Posts/posts.ts'; +import BlogPageView from '../view/BlogPageView.ts'; -export default class PostListModel implements Page { +export default class BlogPageModel implements Page { private parent: HTMLDivElement; private postClickHandler = (post: PostView): void => { diff --git a/src/pages/Blog/PostList/view/PostListView.ts b/src/pages/BlogPage/view/BlogPageView.ts similarity index 94% rename from src/pages/Blog/PostList/view/PostListView.ts rename to src/pages/BlogPage/view/BlogPageView.ts index c67dd24f..e33459e3 100644 --- a/src/pages/Blog/PostList/view/PostListView.ts +++ b/src/pages/BlogPage/view/BlogPageView.ts @@ -1,12 +1,12 @@ -import type BlogPostView from '@/pages/Blog/Post/view/PostView'; +import type BlogPostView from '@/entities/Post/view/PostView'; import getStore from '@/shared/Store/Store.ts'; import { BLOG_DESCRIPTION } from '@/shared/constants/pages.ts'; import createBaseElement from '@/shared/utils/createBaseElement.ts'; -import styles from './postListView.module.scss'; +import styles from './blogPageView.module.scss'; -export default class PostListView { +export default class BlogPageView { private description: HTMLParagraphElement; private page: HTMLDivElement; diff --git a/src/pages/Blog/PostList/view/postListView.module.scss b/src/pages/BlogPage/view/blogPageView.module.scss similarity index 100% rename from src/pages/Blog/PostList/view/postListView.module.scss rename to src/pages/BlogPage/view/blogPageView.module.scss diff --git a/src/pages/CartPage/model/CartPageModel.ts b/src/pages/CartPage/model/CartPageModel.ts index 0e1c66e8..3fd8d0b5 100644 --- a/src/pages/CartPage/model/CartPageModel.ts +++ b/src/pages/CartPage/model/CartPageModel.ts @@ -18,7 +18,38 @@ import CartPageView from '../view/CartPageView.ts'; class CartPageModel implements Page { private cart: Cart | null = null; - private changeProductHandler = (cart: Cart): void => { + private productsItem: ProductOrderModel[] = []; + + private view: CartPageView; + + constructor(parent: HTMLDivElement) { + this.view = new CartPageView(parent, this.clearCart.bind(this), this.addDiscountHandler.bind(this)); + + this.init().catch(showErrorMessage); + } + + private async addDiscountHandler(discountCode: string): Promise { + if (discountCode.trim()) { + const loader = new LoaderModel(LOADER_SIZE.SMALL).getHTML(); + this.view.getCouponButton().append(loader); + await getCartModel() + .addCoupon(discountCode) + .then((cart) => { + if (cart) { + serverMessageModel.showServerMessage( + SERVER_MESSAGE_KEYS.SUCCESSFUL_ADD_COUPON_TO_CART, + MESSAGE_STATUS.SUCCESS, + ); + this.cart = cart; + this.view.updateTotal(this.cart); + } + }) + .catch(showErrorMessage) + .finally(() => loader.remove()); + } + } + + private changeProductHandler(cart: Cart): void { this.cart = cart; this.productsItem = this.productsItem.filter((productItem) => { const searchEl = this.cart?.products.find((item) => item.lineItemId === productItem.getProduct().lineItemId); @@ -33,9 +64,9 @@ class CartPageModel implements Page { this.view.renderEmpty(); } this.view.updateTotal(this.cart); - }; + } - private clearCart = async (): Promise => { + private async clearCart(): Promise { await getCartModel() .clearCart() .then((cart) => { @@ -55,37 +86,6 @@ class CartPageModel implements Page { showErrorMessage(error); return this.cart; }); - }; - - private productsItem: ProductOrderModel[] = []; - - private view: CartPageView; - - constructor(parent: HTMLDivElement) { - this.view = new CartPageView(parent, this.clearCart, this.addDiscountHandler.bind(this)); - - this.init().catch(showErrorMessage); - } - - private async addDiscountHandler(discountCode: string): Promise { - if (discountCode.trim()) { - const loader = new LoaderModel(LOADER_SIZE.SMALL).getHTML(); - this.view.getCouponButton().append(loader); - await getCartModel() - .addCoupon(discountCode) - .then((cart) => { - if (cart) { - serverMessageModel.showServerMessage( - SERVER_MESSAGE_KEYS.SUCCESSFUL_ADD_COUPON_TO_CART, - MESSAGE_STATUS.SUCCESS, - ); - this.cart = cart; - this.view.updateTotal(this.cart); - } - }) - .catch(showErrorMessage) - .finally(() => loader.remove()); - } } private async init(): Promise { @@ -98,7 +98,7 @@ class CartPageModel implements Page { private renderCart(): void { if (this.cart) { this.cart.products.forEach((product) => { - this.productsItem.push(new ProductOrderModel(product, this.changeProductHandler)); + this.productsItem.push(new ProductOrderModel(product, this.changeProductHandler.bind(this))); }); if (this.productsItem.length) { diff --git a/src/pages/CartPage/view/cartPageView.module.scss b/src/pages/CartPage/view/cartPageView.module.scss index 3f51d986..8bd65ec5 100644 --- a/src/pages/CartPage/view/cartPageView.module.scss +++ b/src/pages/CartPage/view/cartPageView.module.scss @@ -149,6 +149,10 @@ .applyBtn { border-radius: 0 var(--small-br) var(--small-br) 0; + div { + position: absolute; + } + &:active { transform: scale(1); } diff --git a/src/pages/MainPage/model/MainPageModel.ts b/src/pages/MainPage/model/MainPageModel.ts index 979067ed..e4e48121 100644 --- a/src/pages/MainPage/model/MainPageModel.ts +++ b/src/pages/MainPage/model/MainPageModel.ts @@ -1,15 +1,15 @@ import type { Page } from '@/shared/types/page.ts'; import PromoCodeSliderModel from '@/entities/PromocodeSlider/model/PromoCodeSliderModel.ts'; -import PostWidgetModel from '@/pages/Blog/PostWidget/model/PostWidgetModel.ts'; import getStore from '@/shared/Store/Store.ts'; import { setCurrentPage } from '@/shared/Store/actions.ts'; import { PAGE_ID } from '@/shared/constants/pages.ts'; +import BlogWidgetModel from '@/widgets/Blog/model/BogWidgetModel.ts'; import MainPageView from '../view/MainPageView.ts'; class MainPageModel implements Page { - private blogWidget: PostWidgetModel; + private blogWidget: BlogWidgetModel; private parent: HTMLDivElement; @@ -20,7 +20,7 @@ class MainPageModel implements Page { constructor(parent: HTMLDivElement) { this.parent = parent; this.view = new MainPageView(this.parent); - this.blogWidget = new PostWidgetModel(this.view.getHTML()); + this.blogWidget = new BlogWidgetModel(this.view.getHTML()); this.init(); } diff --git a/src/shared/API/cart/model/CartModel.ts b/src/shared/API/cart/model/CartModel.ts index 4d619b46..8e81bd34 100644 --- a/src/shared/API/cart/model/CartModel.ts +++ b/src/shared/API/cart/model/CartModel.ts @@ -33,22 +33,31 @@ export class CartModel { private cart: Cart | null = null; - private isSetAnonymousId = false; - private root: CartApi; constructor() { this.root = getCartApi(); - this.getCart().catch(showErrorMessage); + this.getCart() + .then(() => { + const { anonymousId } = getStore().getState(); + if (anonymousId && this.cart?.anonymousId !== anonymousId) { + this.updateCartCustomer(anonymousId).catch(showErrorMessage); + } + }) + .catch(showErrorMessage); } private adaptCart(data: CartResponse): Cart { - if (data.anonymousId && !getStore().getState().authToken) { + const { anonymousId, authToken } = getStore().getState(); + if (data.anonymousId && !authToken) { getStore().dispatch(setAnonymousCartId(data.id)); + } + if (data.anonymousId && !authToken && !anonymousId) { getStore().dispatch(setAnonymousId(data.anonymousId)); } const discount = data.discountOnTotalPrice?.discountedAmount?.centAmount; return { + anonymousId: data.anonymousId || null, discounts: discount ? discount / PRICE_FRACTIONS : 0, id: data.id, products: data.lineItems.map((lineItem) => this.adaptLineItem(lineItem)), @@ -97,20 +106,10 @@ export class CartModel { this.callback.forEach((callback) => (this.cart !== null ? callback(this.cart) : null)); } - private async getAnonymousCart(anonymousCartId: string, anonymousId: string): Promise { + private async getAnonymousCart(anonymousCartId: string): Promise { const data = await this.root.getAnonymCart(anonymousCartId); if (!data.body.customerId) { this.cart = this.getCartFromData(data); - const cartAnonymId = data.body.anonymousId; - if (cartAnonymId !== anonymousId && !this.isSetAnonymousId) { - this.isSetAnonymousId = true; - const actions: CartSetAnonymousIdAction = { - action: ACTIONS.setAnonymousId, - anonymousId, - }; - const dataSetId = await this.root.setAnonymousId(this.cart, actions); - this.cart = this.getCartFromData(dataSetId); - } } return this.cart; @@ -118,6 +117,7 @@ export class CartModel { private getCartFromData(data: CartResponse | ClientResponse): Cart { let cart: Cart = { + anonymousId: null, discounts: 0, id: '', products: [], @@ -149,6 +149,18 @@ export class CartModel { return this.cart; } + private async updateCartCustomer(anonymousId: string): Promise { + if (this.cart) { + const actions: CartSetAnonymousIdAction = { + action: ACTIONS.setAnonymousId, + anonymousId, + }; + const dataSetId = await this.root.setAnonymousId(this.cart, actions); + this.cart = this.getCartFromData(dataSetId); + } + return this.cart; + } + public async addCoupon(discountCode: string): Promise { if (!this.cart) { this.cart = await this.getCart(); @@ -261,10 +273,7 @@ export class CartModel { if (!this.cart) { const { anonymousCartId, anonymousId } = getStore().getState(); if (anonymousCartId && anonymousId) { - // const data = await this.root.getAnonymCart(anonymousCartId); - // this.cart = this.getCartFromData(data); - - this.cart = await this.getAnonymousCart(anonymousCartId, anonymousId); + this.cart = await this.getAnonymousCart(anonymousCartId); } if (!this.cart) { this.cart = await this.getUserCart(); diff --git a/src/shared/API/sdk/client.ts b/src/shared/API/sdk/client.ts index 0fe1aa04..91e9b139 100644 --- a/src/shared/API/sdk/client.ts +++ b/src/shared/API/sdk/client.ts @@ -2,7 +2,6 @@ import type { UserCredentials } from '@/shared/types/user'; import getStore from '@/shared/Store/Store.ts'; import { setAnonymousId } from '@/shared/Store/actions.ts'; -import showErrorMessage from '@/shared/utils/userMessage.ts'; import { type ByProjectKeyRequestBuilder, createApiBuilderFromCtpClient } from '@commercetools/platform-sdk'; import { type AnonymousAuthMiddlewareOptions, @@ -63,8 +62,6 @@ export class ApiClient { this.anonymConnection = this.createAnonymConnection(); } this.adminConnection = this.createAdminConnection(); - - this.init().catch(showErrorMessage); } private addAuthMiddleware( @@ -169,15 +166,6 @@ export class ApiClient { }; } - private async init(): Promise { - await this.apiRoot() - .get() - .execute() - .catch((error: Error) => { - showErrorMessage(error); - }); - } - public adminRoot(): ByProjectKeyRequestBuilder { return this.adminConnection; } diff --git a/src/shared/API/shopping-list/model/ShoppingListModel.ts b/src/shared/API/shopping-list/model/ShoppingListModel.ts index ff15a043..75de49ef 100644 --- a/src/shared/API/shopping-list/model/ShoppingListModel.ts +++ b/src/shared/API/shopping-list/model/ShoppingListModel.ts @@ -44,10 +44,13 @@ export class ShoppingListModel { } private adaptShopList(data: ShoppingListResponse): ShoppingList { - if (data.anonymousId && !getStore().getState().authToken) { - getStore().dispatch(setAnonymousId(data.anonymousId)); + const { anonymousId, authToken } = getStore().getState(); + if (data.anonymousId && !authToken) { getStore().dispatch(setAnonymousShopListId(data.id)); } + if (data.anonymousId && !authToken && !anonymousId) { + getStore().dispatch(setAnonymousId(data.anonymousId)); + } return { id: data.id, products: data.lineItems.map((lineItem) => this.adaptLineItem(lineItem)), diff --git a/src/pages/Blog/data/posts.ts b/src/shared/Posts/posts.ts similarity index 100% rename from src/pages/Blog/data/posts.ts rename to src/shared/Posts/posts.ts diff --git a/src/shared/types/cart.ts b/src/shared/types/cart.ts index ca9b7f28..840ab7a4 100644 --- a/src/shared/types/cart.ts +++ b/src/shared/types/cart.ts @@ -1,6 +1,7 @@ import type { SizeType, localization } from './product.ts'; export interface Cart { + anonymousId: null | string; discounts: number; id: string; products: CartProduct[]; diff --git a/src/pages/Blog/PostWidget/model/PostWidgetModel.ts b/src/widgets/Blog/model/BogWidgetModel.ts similarity index 73% rename from src/pages/Blog/PostWidget/model/PostWidgetModel.ts rename to src/widgets/Blog/model/BogWidgetModel.ts index 529720d8..6d69db7d 100644 --- a/src/pages/Blog/PostWidget/model/PostWidgetModel.ts +++ b/src/widgets/Blog/model/BogWidgetModel.ts @@ -1,26 +1,26 @@ -import PostView from '@/pages/Blog/Post/view/PostView.ts'; +import PostView from '@/entities/Post/view/PostView.ts'; import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts'; -import postsData from '../../data/posts.ts'; -import PostWidgetView from '../view/PostWidgetView.ts'; +import postsData from '../../../shared/Posts/posts.ts'; +import BlogWidgetView from '../view/BlogWidgetView.ts'; const CART_COUNT = 3; const HALF_RANDOM = 0.5; -export default class PostWidgetModel { +export default class BlogWidgetModel { private postClickHandler = (post: PostView): void => { this.view.openPost(post); }; private posts: PostView[]; - private view: PostWidgetView; + private view: BlogWidgetView; constructor(parent: HTMLDivElement) { const shuffledPosts = [...postsData].sort(() => HALF_RANDOM - Math.random()); const newPost = shuffledPosts.slice(0, CART_COUNT); this.posts = newPost.map((post) => new PostView(post, this.postClickHandler)); - this.view = new PostWidgetView(parent, this.posts); + this.view = new BlogWidgetView(parent, this.posts); this.init(); } diff --git a/src/pages/Blog/PostWidget/view/PostWidgetView.ts b/src/widgets/Blog/view/BlogWidgetView.ts similarity index 94% rename from src/pages/Blog/PostWidget/view/PostWidgetView.ts rename to src/widgets/Blog/view/BlogWidgetView.ts index 73a78337..e8916737 100644 --- a/src/pages/Blog/PostWidget/view/PostWidgetView.ts +++ b/src/widgets/Blog/view/BlogWidgetView.ts @@ -1,12 +1,12 @@ -import type BlogPostView from '@/pages/Blog/Post/view/PostView'; +import type BlogPostView from '@/entities/Post/view/PostView'; import getStore from '@/shared/Store/Store.ts'; import { BLOG_DESCRIPTION } from '@/shared/constants/pages.ts'; import createBaseElement from '@/shared/utils/createBaseElement.ts'; -import styles from './postWidgetView.module.scss'; +import styles from './blogWidgetView.module.scss'; -export default class PostWidgetView { +export default class BlogWidgetView { private description: HTMLParagraphElement; private page: HTMLDivElement; diff --git a/src/pages/Blog/PostWidget/view/postWidgetView.module.scss b/src/widgets/Blog/view/blogWidgetView.module.scss similarity index 88% rename from src/pages/Blog/PostWidget/view/postWidgetView.module.scss rename to src/widgets/Blog/view/blogWidgetView.module.scss index 0d2f7031..707d0887 100644 --- a/src/pages/Blog/PostWidget/view/postWidgetView.module.scss +++ b/src/widgets/Blog/view/blogWidgetView.module.scss @@ -17,6 +17,13 @@ margin: 0 auto; gap: var(--extra-small-offset); + div { + overflow: hidden; + width: 100%; + height: calc(var(--extra-large-offset) * 2); + object-fit: cover; + } + @media (max-width: 768px) { display: flex; flex-direction: column; diff --git a/src/widgets/Header/model/HeaderModel.ts b/src/widgets/Header/model/HeaderModel.ts index 86e3684d..f31c41fd 100644 --- a/src/widgets/Header/model/HeaderModel.ts +++ b/src/widgets/Header/model/HeaderModel.ts @@ -54,7 +54,6 @@ class HeaderModel { this.setLogoutButtonHandler(); this.setCartLinkHandler(); this.observeCartChange(); - this.setCartCount().catch(showErrorMessage); this.setChangeLanguageCheckboxHandler(); } @@ -79,12 +78,6 @@ class HeaderModel { }); } - private async setCartCount(): Promise { - const cart = await getCartModel().getCart(); - this.view.updateCartCount(cart.products.length); - return true; - } - private setCartLinkHandler(): void { const logo = this.view.getToCartLink().getHTML(); logo.addEventListener('click', (event) => { diff --git a/src/widgets/ProductOrder/view/productOrderView.module.scss b/src/widgets/ProductOrder/view/productOrderView.module.scss index 02523d78..4ff26fe2 100644 --- a/src/widgets/ProductOrder/view/productOrderView.module.scss +++ b/src/widgets/ProductOrder/view/productOrderView.module.scss @@ -45,6 +45,14 @@ } .deleteButton { + display: flex; + align-items: center; + justify-content: center; + + div { + position: absolute; + } + svg { width: var(--extra-small-offset); height: var(--extra-small-offset);