Skip to content

Commit

Permalink
feat(RSS-ECOMM-4_16): display promo on main page (#338)
Browse files Browse the repository at this point in the history
feat: display promo on main page
  • Loading branch information
Kleostro authored Jun 4, 2024
1 parent 159cc37 commit c011816
Show file tree
Hide file tree
Showing 10 changed files with 488 additions and 1 deletion.
Binary file added public/img/png/promo-slider-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/png/promo-slider-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/png/promo-slider-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions src/entities/PromocodeSlider/model/PromoCodeSliderModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Swiper from 'swiper';
import 'swiper/css';
import 'swiper/css/autoplay';
import 'swiper/css/bundle';
import 'swiper/css/pagination';
import { Autoplay, Pagination } from 'swiper/modules';

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

const SLIDER_DELAY = 10000;
const SLIDER_PER_VIEW = 1;

class PromoCodeSliderModel {
private slider: Swiper | null = null;

private view: PromoCodeSliderView;

constructor() {
this.view = new PromoCodeSliderView();
this.init();
}

private init(): void {
this.initSlider();
}

private initSlider(): void {
this.slider = new Swiper(this.view.getSlider(), {
autoplay: {
delay: SLIDER_DELAY,
},
direction: 'horizontal',
loop: true,
modules: [Autoplay, Pagination],
pagination: {
clickable: true,
el: this.view.getPaginationWrapper(),
},
slidesPerView: SLIDER_PER_VIEW,
});
this.slider.autoplay.start();
}

public getHTML(): HTMLDivElement {
return this.view.getHTML();
}
}

export default PromoCodeSliderModel;
218 changes: 218 additions & 0 deletions src/entities/PromocodeSlider/view/PromoCodeSliderView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import type { User } from '@/shared/types/user';

import getCustomerModel from '@/shared/API/customer/model/CustomerModel.ts';
import InputModel from '@/shared/Input/model/InputModel.ts';
import serverMessageModel from '@/shared/ServerMessage/model/ServerMessageModel.ts';
import getStore from '@/shared/Store/Store.ts';
import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts';
import { AUTOCOMPLETE_OPTION } 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 PROMO_SLIDER_CONTENT from '@/shared/constants/promo.ts';
import SVG_DETAILS from '@/shared/constants/svg.ts';
import calcUserBirthDayRange from '@/shared/utils/calcUserBirthDayRange.ts';
import createBaseElement from '@/shared/utils/createBaseElement.ts';
import createSVGUse from '@/shared/utils/createSVGUse.ts';
import showErrorMessage from '@/shared/utils/userMessage.ts';

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

class PromoCodeSliderView {
private paginationWrapper: HTMLDivElement;

private slider: HTMLDivElement;

private view: HTMLDivElement;

constructor() {
this.paginationWrapper = this.createPaginationWrapper();
this.slider = this.createSlider();
this.view = this.createHTML();
}

private createDateSpan(index: number, currentUser?: User): HTMLSpanElement {
const date = createBaseElement({
cssClasses: [styles.sliderDate],
tag: 'span',
});

const start = createBaseElement({
cssClasses: [styles.sliderDateStart],
innerContent: currentUser
? calcUserBirthDayRange(currentUser.birthDate).start
: PROMO_SLIDER_CONTENT[index].en.date.start ?? '',
tag: 'span',
});

const end = createBaseElement({
cssClasses: [styles.sliderDateEnd],
innerContent: currentUser
? calcUserBirthDayRange(currentUser.birthDate).end
: PROMO_SLIDER_CONTENT[index].en.date.end ?? '',
tag: 'span',
});

date.append(start, end);
return date;
}

private createHTML(): HTMLDivElement {
this.view = createBaseElement({
cssClasses: [styles.wrapper],
tag: 'div',
});

this.view.append(this.slider);
return this.view;
}

private createPaginationWrapper(): HTMLDivElement {
this.paginationWrapper = createBaseElement({
cssClasses: [styles.paginationWrapper],
tag: 'div',
});

return this.paginationWrapper;
}

private createPromoCodeSpan(code: string): HTMLSpanElement {
const promoCode = createBaseElement({
cssClasses: [styles.sliderPromoCode],
tag: 'span',
});

const currentPromoCode = new InputModel({
autocomplete: AUTOCOMPLETE_OPTION.ON,
id: '',
placeholder: '',
type: INPUT_TYPE.TEXT,
value: code,
});

currentPromoCode.getHTML().classList.add(styles.currentPromoCode);

const svg = document.createElementNS(SVG_DETAILS.SVG_URL, 'svg');
svg.append(createSVGUse(SVG_DETAILS.COPY));

svg.addEventListener('click', () => {
window.navigator.clipboard
.writeText(currentPromoCode.getValue())
.then(() =>
serverMessageModel.showServerMessage(
SERVER_MESSAGE_KEYS.SUCCESSFUL_COPY_PROMO_CODE_TO_CLIPBOARD,
MESSAGE_STATUS.SUCCESS,
),
)
.catch(() => serverMessageModel.showServerMessage(SERVER_MESSAGE_KEYS.BAD_REQUEST, MESSAGE_STATUS.ERROR));
});

promoCode.append(svg, currentPromoCode.getHTML());
return promoCode;
}

private createSlider(): HTMLDivElement {
this.slider = createBaseElement({
cssClasses: ['swiper', styles.slider],
tag: 'div',
});

this.slider.append(this.createSliderWrapper(), this.paginationWrapper);

return this.slider;
}

private async createSliderSlideContent(index: number): Promise<HTMLDivElement> {
const slide = createBaseElement({
cssClasses: [styles.sliderContent],
tag: 'div',
});

const { description, img, title } = this.createSliderSlideInfo(index);

slide.append(
title,
description,
this.createPromoCodeSpan(PROMO_SLIDER_CONTENT[index][getStore().getState().currentLanguage].promoCode),
img,
);

observeStore(selectCurrentLanguage, () => {
title.textContent = PROMO_SLIDER_CONTENT[index][getStore().getState().currentLanguage].title;
description.textContent = PROMO_SLIDER_CONTENT[index][getStore().getState().currentLanguage].description;
});

if (PROMO_SLIDER_CONTENT[index].en.date.start === null) {
const currentUser = await getCustomerModel().getCurrentUser();
if (currentUser) {
slide.append(this.createDateSpan(index, currentUser));
}
} else {
slide.append(this.createDateSpan(index));
}

return slide;
}

private createSliderSlideInfo(index: number): {
description: HTMLParagraphElement;
img: HTMLImageElement;
title: HTMLHeadingElement;
} {
const img = createBaseElement({
attributes: {
src: PROMO_SLIDER_CONTENT[index][getStore().getState().currentLanguage].img,
},
cssClasses: [styles.sliderImage],
tag: 'img',
});

const title = createBaseElement({
cssClasses: [styles.sliderTitle],
innerContent: PROMO_SLIDER_CONTENT[index][getStore().getState().currentLanguage].title,
tag: 'h3',
});

const description = createBaseElement({
cssClasses: [styles.sliderDescription],
innerContent: PROMO_SLIDER_CONTENT[index][getStore().getState().currentLanguage].description,
tag: 'p',
});

return { description, img, title };
}

private createSliderWrapper(): HTMLDivElement {
const sliderWrapper = createBaseElement({
cssClasses: ['swiper-wrapper', styles.sliderWrapper],
tag: 'div',
});

PROMO_SLIDER_CONTENT.forEach((_, index) => {
const slideWrapper = createBaseElement({
cssClasses: ['swiper-slide', styles.sliderSlide],
tag: 'div',
});
this.createSliderSlideContent(index)
.then((slide) => slideWrapper.append(slide))
.catch(showErrorMessage);

sliderWrapper.append(slideWrapper);
});

return sliderWrapper;
}

public getHTML(): HTMLDivElement {
return this.view;
}

public getPaginationWrapper(): HTMLDivElement {
return this.paginationWrapper;
}

public getSlider(): HTMLDivElement {
return this.slider;
}
}

export default PromoCodeSliderView;
111 changes: 111 additions & 0 deletions src/entities/PromocodeSlider/view/promoCodeSliderView.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
.slider {
border: calc(var(--two) * 1.5) solid var(--steam-green-1100);
border-radius: var(--large-br);
background-color: var(--steam-green-1000);
}

.sliderContent {
position: relative;
padding: var(--medium-offset);
}

.sliderTitle {
margin-bottom: var(--medium-offset);
max-width: 50%;
font: var(--extra-black-font);
letter-spacing: var(--one);
color: var(--steam-green-800);

@media (max-width: 600px) {
max-width: 100%;
}
}

.sliderImage {
position: absolute;
right: var(--medium-offset);
top: 50%;
width: 20%;
height: auto;
transform: translateY(-50%);

@media (max-width: 950px) {
width: 30%;
}

@media (max-width: 600px) {
display: none;
}
}

.sliderDescription {
margin-bottom: var(--small-offset);
max-width: 50%;
font: var(--bold-font);
letter-spacing: var(--one);
color: var(--noble-gray-800);

@media (max-width: 600px) {
max-width: 100%;
}
}

.sliderPromoCode {
display: flex;
align-items: center;
margin-bottom: var(--small-offset);
max-width: max-content;
gap: var(--tiny-offset);

svg {
width: 1.5rem;
height: 1.5rem;
fill: transparent;
stroke: var(--steam-green-400);
transition: stroke 0.2s;
cursor: copy;

@media (hover: hover) {
&:hover {
stroke: var(--steam-green-800);
}
}
}
}

.currentPromoCode {
font: var(--medium-font);
letter-spacing: var(--one);
color: var(--steam-green-400);
background-color: transparent;
pointer-events: none;
}

.paginationWrapper {
z-index: 2;
display: flex;
margin: 0 auto;
margin-bottom: var(--tiny-offset);
max-width: max-content;
gap: var(--tiny-offset);

> * {
border-radius: 50%;
width: 1rem;
height: 1rem;
background-color: var(--steam-green-800);
cursor: pointer;
}
}

.sliderDate {
display: flex;
gap: var(--tiny-offset);
}

.sliderDateStart,
.sliderDateEnd {
font: var(--regular-font);
letter-spacing: var(--one);
color: var(--noble-gray-800);
}
Loading

0 comments on commit c011816

Please sign in to comment.