diff --git a/src/entities/ProductCard/view/ProductCardView.ts b/src/entities/ProductCard/view/ProductCardView.ts index 598b13f7..b600eca8 100644 --- a/src/entities/ProductCard/view/ProductCardView.ts +++ b/src/entities/ProductCard/view/ProductCardView.ts @@ -8,6 +8,7 @@ import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts' import { MORE_TEXT } from '@/shared/constants/buttons.ts'; import { LANGUAGE_CHOICE } from '@/shared/constants/common.ts'; import { PAGE_ID } from '@/shared/constants/pages.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 { buildPathName } from '@/shared/utils/buildPathname.ts'; @@ -25,6 +26,8 @@ class ProductCardView { private currentSize: null | string; + private discountLabel: HTMLSpanElement; + private goDetailsPageLink: LinkModel; private moreButton: ButtonModel; @@ -51,6 +54,7 @@ class ProductCardView { this.goDetailsPageLink = this.createGoDetailsPageLink(); this.buttonsWrapper = this.createButtonsWrapper(); this.productImage = this.createProductImage(); + this.discountLabel = this.createDiscountLabel(); this.productImageWrapper = this.createProductImageWrapper(); this.productName = this.createProductName(); this.productShortDescription = this.createProductShortDescription(); @@ -113,6 +117,30 @@ class ProductCardView { return this.buttonsWrapper; } + private createDiscountLabel(): HTMLSpanElement { + const currentVariant = this.params.variant.find(({ size }) => size === this.currentSize) ?? this.params.variant[0]; + const innerContent = `${Math.round((1 - currentVariant.discount / currentVariant.price) * 100)}%`; + this.discountLabel = createBaseElement({ + cssClasses: [styles.discountLabel], + innerContent, + tag: 'span', + }); + + const discountSpan = createBaseElement({ + cssClasses: [styles.discountSpan], + innerContent: PRODUCT_INFO_TEXT[getStore().getState().currentLanguage].DISCOUNT_LABEL, + tag: 'span', + }); + + observeStore(selectCurrentLanguage, () => { + discountSpan.textContent = PRODUCT_INFO_TEXT[getStore().getState().currentLanguage].DISCOUNT_LABEL; + }); + + this.discountLabel.append(discountSpan); + + return this.discountLabel; + } + private createGoDetailsPageLink(): LinkModel { const href = `${buildPathName(PAGE_ID.PRODUCT_PAGE, this.params.key, { size: [this.currentSize ?? this.params.variant[0].size], @@ -181,6 +209,10 @@ class ProductCardView { this.productImage.classList.remove(styles.hidden); loader.remove(); }); + + if (this.params.variant.some(({ discount }) => discount)) { + this.productImageWrapper.append(this.discountLabel); + } return this.productImageWrapper; } diff --git a/src/entities/ProductCard/view/productCardView.module.scss b/src/entities/ProductCard/view/productCardView.module.scss index 79afd1df..dae155ed 100644 --- a/src/entities/ProductCard/view/productCardView.module.scss +++ b/src/entities/ProductCard/view/productCardView.module.scss @@ -77,6 +77,7 @@ width: var(--small-offset); height: var(--small-offset); background-color: var(--noble-gray-1000); + transition: outline 0.2s; backdrop-filter: blur(10px); svg { @@ -148,6 +149,7 @@ flex-grow: 1; flex-direction: column; align-items: center; + border-radius: var(--medium-br); padding: calc(var(--extra-small-offset) / 2) calc(var(--extra-small-offset) / 4); width: 100%; background-color: var(--noble-white-200); @@ -189,6 +191,7 @@ order: 4; margin-top: calc(var(--extra-small-offset) * (-0.2)); margin-right: var(--five); + padding: var(--five); font: var(--regular-font); letter-spacing: var(--one); color: var(--steam-green-700); @@ -204,3 +207,20 @@ .hidden { display: none; } + +.discountLabel { + position: absolute; + left: 0; + top: calc(var(--two) * 5); + z-index: 1; + display: flex; + border-radius: 0 var(--five) var(--five) 0; + padding: calc(var(--tiny-offset) / 2); + font: var(--regular-font); + letter-spacing: var(--one); + text-align: center; + text-transform: uppercase; + color: var(--steam-green-400); + background-color: var(--steam-green-800); + gap: var(--two); +} diff --git a/src/entities/UserAddress/view/userAddressView.module.scss b/src/entities/UserAddress/view/userAddressView.module.scss index 77f0a48b..2a31ee77 100644 --- a/src/entities/UserAddress/view/userAddressView.module.scss +++ b/src/entities/UserAddress/view/userAddressView.module.scss @@ -36,18 +36,22 @@ } .billing { + order: 1; background-color: #d5e4f9; } .shipping { + order: 2; background-color: #e9fffe; } .defaultBilling { + order: 3; background-color: #dfd6ef; } .defaultShipping { + order: 4; background-color: #fee1c9; } diff --git a/src/features/AddressAdd/model/AddressAddModel.ts b/src/features/AddressAdd/model/AddressAddModel.ts index 7b031bdd..56a73890 100644 --- a/src/features/AddressAdd/model/AddressAddModel.ts +++ b/src/features/AddressAdd/model/AddressAddModel.ts @@ -154,19 +154,13 @@ class AddressAddModel { } private setInputFieldHandlers(inputField: InputFieldModel): boolean { - if (inputField?.getView && typeof inputField.getView === 'function') { - const inputHTML = inputField.getView().getInput().getHTML(); - inputHTML.addEventListener('input', () => { - this.switchSubmitFormButtonAccess(); - }); - } + const inputHTML = inputField.getView().getInput().getHTML(); + inputHTML.addEventListener('input', () => this.switchSubmitFormButtonAccess()); return true; } private setPreventDefaultToForm(): boolean { - this.getHTML().addEventListener('submit', (event) => { - event.preventDefault(); - }); + this.getHTML().addEventListener('submit', (event) => event.preventDefault()); return true; } diff --git a/src/features/AddressAdd/view/AddressAddView.ts b/src/features/AddressAdd/view/AddressAddView.ts index bb26f80e..7ffc48e5 100644 --- a/src/features/AddressAdd/view/AddressAddView.ts +++ b/src/features/AddressAdd/view/AddressAddView.ts @@ -1,6 +1,6 @@ import ButtonModel from '@/shared/Button/model/ButtonModel.ts'; import getStore from '@/shared/Store/Store.ts'; -import { BUTTON_TEXT } from '@/shared/constants/buttons.ts'; +import { BUTTON_TEXT, BUTTON_TYPE } from '@/shared/constants/buttons.ts'; import createBaseElement from '@/shared/utils/createBaseElement.ts'; import styles from './addressAddView.module.scss'; @@ -31,12 +31,15 @@ class AddressAddView { cssClasses: [styles.wrapper], tag: 'form', }); - this.view.append(this.cancelButton.getHTML(), this.saveChangesButton.getHTML()); + this.view.append(this.saveChangesButton.getHTML(), this.cancelButton.getHTML()); return this.view; } private createSaveChangesButton(): ButtonModel { this.saveChangesButton = new ButtonModel({ + attrs: { + type: BUTTON_TYPE.SUBMIT, + }, classes: [styles.saveChangesButton], text: BUTTON_TEXT[getStore().getState().currentLanguage].ADD_ADDRESS, }); diff --git a/src/features/AddressEdit/model/AddressEditModel.ts b/src/features/AddressEdit/model/AddressEditModel.ts index b05a5dfd..185c1294 100644 --- a/src/features/AddressEdit/model/AddressEditModel.ts +++ b/src/features/AddressEdit/model/AddressEditModel.ts @@ -121,16 +121,12 @@ class AddressEditModel { private setInputFieldHandlers(inputField: InputFieldModel): boolean { const inputHTML = inputField.getView().getInput().getHTML(); - inputHTML.addEventListener('input', () => { - this.switchSubmitFormButtonAccess(); - }); + inputHTML.addEventListener('input', () => this.switchSubmitFormButtonAccess()); return true; } private setPreventDefaultToForm(): boolean { - this.getHTML().addEventListener('submit', (event) => { - event.preventDefault(); - }); + this.getHTML().addEventListener('submit', (event) => event.preventDefault()); return true; } diff --git a/src/features/AddressEdit/view/AddressEditView.ts b/src/features/AddressEdit/view/AddressEditView.ts index ce2963a5..96fac017 100644 --- a/src/features/AddressEdit/view/AddressEditView.ts +++ b/src/features/AddressEdit/view/AddressEditView.ts @@ -32,7 +32,7 @@ class AddressEditView { tag: 'form', }); - this.view.append(this.cancelButton.getHTML(), this.saveChangesButton.getHTML()); + this.view.append(this.saveChangesButton.getHTML(), this.cancelButton.getHTML()); return this.view; } diff --git a/src/features/PersonalInfoEdit/view/PersonalInfoEditView.ts b/src/features/PersonalInfoEdit/view/PersonalInfoEditView.ts index f6b25789..49ffbfb4 100644 --- a/src/features/PersonalInfoEdit/view/PersonalInfoEditView.ts +++ b/src/features/PersonalInfoEdit/view/PersonalInfoEditView.ts @@ -55,7 +55,7 @@ class PersonalInfoEditView { this.view.append(inputFieldElement.getHTML()); } - this.view.append(this.cancelButton.getHTML(), this.saveChangesButton.getHTML()); + this.view.append(this.saveChangesButton.getHTML(), this.cancelButton.getHTML()); return this.view; } diff --git a/src/shared/constants/forms.ts b/src/shared/constants/forms.ts index 4167446f..744b16f5 100644 --- a/src/shared/constants/forms.ts +++ b/src/shared/constants/forms.ts @@ -88,12 +88,12 @@ export const ADDRESS_TEXT = { CITY: 'City: ', COUNTRY: 'Country: ', POSTAL_CODE: 'Postal code: ', - STREET: 'Street: ', + STREET: 'Address: ', }, ru: { CITY: 'Город: ', COUNTRY: 'Страна: ', POSTAL_CODE: 'Почтовый индекс: ', - STREET: 'Улица: ', + STREET: 'Адрес: ', }, } as const; diff --git a/src/shared/constants/messages.ts b/src/shared/constants/messages.ts index e7aed233..637727d9 100644 --- a/src/shared/constants/messages.ts +++ b/src/shared/constants/messages.ts @@ -28,7 +28,7 @@ export const SERVER_MESSAGE = { PASSWORD_CHANGED: 'Your password has been changed successfully', PASSWORD_NOT_CHANGED: 'Your password has not been changed. Please, try again', PERSONAL_INFO_CHANGED: 'Personal information has been changed successfully', - SUCCESSFUL_ADD_COUPON_TO_CART: 'Coupon has been added to your cart successfully', + SUCCESSFUL_ADD_COUPON_TO_CART: 'Coupon has been applied to your cart successfully', SUCCESSFUL_ADD_PRODUCT_TO_CART: 'Product has been added successfully to your cart', SUCCESSFUL_ADD_PRODUCT_TO_WISHLIST: 'Product has been added successfully to your wishlist', SUCCESSFUL_COPY_TO_CLIPBOARD: 'SKU has been copied to clipboard', @@ -39,10 +39,10 @@ export const SERVER_MESSAGE = { USER_EXISTS: 'User with this email already exists, please check your email', }, ru: { - ADDRESS_ADDED: 'Адрес был успешно добавлен', - ADDRESS_CHANGED: 'Адрес был успешно изменен', - ADDRESS_DELETED: 'Адрес был успешно удален', - ADDRESS_STATUS_CHANGED: 'Статус адреса был успешно изменен', + ADDRESS_ADDED: 'Адрес успешно добавлен', + ADDRESS_CHANGED: 'Адрес успешно изменен', + ADDRESS_DELETED: 'Адрес успешно удален', + ADDRESS_STATUS_CHANGED: 'Статус адреса успешно изменен', BAD_REQUEST: 'Извините, что-то пошло не так. Попробуйте позже.', COPY_TO_CLIPBOARD: 'SKU скопирован в буфер обмена', GREETING: 'Здравствуйте! Добро пожаловать в наш магазин. Приятных покупок!', @@ -50,15 +50,15 @@ export const SERVER_MESSAGE = { INVALID_COUPON: 'Неверный купон', INVALID_EMAIL: 'Пользователь с таким адресом не существует. Пожалуйста, сначала зарегистрируйтесь', LANGUAGE_CHANGED: 'Настройки языка успешно обновлены', - PASSWORD_CHANGED: 'Ваш пароль был успешно изменен', + PASSWORD_CHANGED: 'Ваш пароль успешно изменен', PASSWORD_NOT_CHANGED: 'Ваш пароль не был изменен. Пожалуйста, попробуйте ещё раз', - PERSONAL_INFO_CHANGED: 'Персональные данные были успешно изменены', - SUCCESSFUL_ADD_COUPON_TO_CART: 'Купон был успешно добавлен в корзину', - SUCCESSFUL_ADD_PRODUCT_TO_CART: 'Товар был успешно добавлен в корзину', - SUCCESSFUL_ADD_PRODUCT_TO_WISHLIST: 'Товар был успешно добавлен в избранное', + PERSONAL_INFO_CHANGED: 'Персональные данные успешно изменены', + SUCCESSFUL_ADD_COUPON_TO_CART: 'Купон успешно применен к корзине', + SUCCESSFUL_ADD_PRODUCT_TO_CART: 'Товар успешно добавлен в корзину', + SUCCESSFUL_ADD_PRODUCT_TO_WISHLIST: 'Товар успешно добавлен в избранное', SUCCESSFUL_COPY_TO_CLIPBOARD: 'SKU успешно скопирован в буфер обмена', - SUCCESSFUL_DELETE_PRODUCT_FROM_CART: 'Товар был успешно удален из корзины', - SUCCESSFUL_DELETE_PRODUCT_FROM_WISHLIST: 'Товар был успешно удален из избранного', + SUCCESSFUL_DELETE_PRODUCT_FROM_CART: 'Товар успешно удален из корзины', + SUCCESSFUL_DELETE_PRODUCT_FROM_WISHLIST: 'Товар успешно удален из избранного', SUCCESSFUL_LOGIN: 'Добро пожаловать в наш магазин. Приятных покупок!', SUCCESSFUL_REGISTRATION: 'Регистрация прошла успешно', USER_EXISTS: 'Пользователь с таким адресом уже существует, пожалуйста, проверьте свою почту', diff --git a/src/shared/constants/product.ts b/src/shared/constants/product.ts index a4e4f5e3..ea8f9f37 100644 --- a/src/shared/constants/product.ts +++ b/src/shared/constants/product.ts @@ -31,6 +31,7 @@ export const PRODUCT_INFO_TEXT = { en: { CATEGORY: 'Categories: ', DIFFICULTY: 'Difficulty: ', + DISCOUNT_LABEL: 'OFF', FULL_DESCRIPTION: 'Full description:', SHORT_DESCRIPTION: 'Short description:', SIZE: 'Size:', @@ -38,6 +39,7 @@ export const PRODUCT_INFO_TEXT = { ru: { CATEGORY: 'Категории: ', DIFFICULTY: 'Сложность: ', + DISCOUNT_LABEL: 'Скидка', FULL_DESCRIPTION: 'Полное описание:', SHORT_DESCRIPTION: 'Краткое описание:', SIZE: 'Размер:', @@ -47,6 +49,7 @@ export const PRODUCT_INFO_TEXT = { export const PRODUCT_INFO_TEXT_KEYS = { CATEGORY: 'CATEGORY', DIFFICULTY: 'DIFFICULTY', + DISCOUNT_LABEL: 'DISCOUNT_LABEL', FULL_DESCRIPTION: 'FULL_DESCRIPTION', SHORT_DESCRIPTION: 'SHORT_DESCRIPTION', SIZE: 'SIZE',