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

feat(RSS-ECOMM-5_06): implement cooperation page #342

Merged
merged 1 commit into from
Jun 5, 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
4 changes: 4 additions & 0 deletions src/app/App/model/AppModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class AppModel {
const { default: CatalogPageModel } = await import('@/pages/CatalogPage/model/CatalogPageModel.ts');
return new CatalogPageModel(this.appView.getHTML());
},
[PAGE_ID.COOPERATION_PAGE]: async (): Promise<Page> => {
const { default: CooperationPageModel } = await import('@/pages/CooperationPage/model/CooperationPageModel.ts');
return new CooperationPageModel(this.appView.getHTML());
},
[PAGE_ID.DEFAULT_PAGE]: async (): Promise<Page> => {
const { default: MainPageModel } = await import('@/pages/MainPage/model/MainPageModel.ts');
return new MainPageModel(this.appView.getHTML());
Expand Down
4 changes: 2 additions & 2 deletions src/app/styles/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
--bold-font: 700 1rem 'Cerapro', sans-serif; // 16px
--medium-bold-font: 700 1.125rem 'Cerapro', sans-serif; // 18px
--extra-bold-font: 700 2.1875rem 'Cerapro', sans-serif; // 35px
--black-font: 900 2rem 'Cerapro', sans-serif; // 32px
--extra-black-font: 900 3.5rem 'Cerapro', sans-serif; // 70px
--black-font: 900 2.1875rem 'Cerapro', sans-serif; // 35px
--extra-black-font: 900 2.5rem 'Cerapro', sans-serif; // 40px

body.light {
// colors
Expand Down
1 change: 1 addition & 0 deletions src/pages/CartPage/view/cartPageView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ $color: var(--steam-green-800);
width: max-content;
height: max-content;
text-transform: none;
cursor: pointer;

&::after {
bottom: var(--five);
Expand Down
44 changes: 44 additions & 0 deletions src/pages/CooperationPage/model/CooperationPageModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { CooperationData } from '@/shared/types/validation/cooperationData.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 isCooperationData from '@/shared/types/validation/cooperationData.ts';
import getCooperationData from '@/shared/utils/getCooperationData.ts';
import showErrorMessage from '@/shared/utils/userMessage.ts';

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

class CooperationPageModel {
private view: CooperationPageView;

constructor(parent: HTMLDivElement) {
this.view = new CooperationPageView(parent);
this.init();
}

private init(): void {
getCooperationData()
.then((data) => {
if (isCooperationData(data)) {
this.view.drawCooperationInfo(data);
this.observeState(data);
}
})
.catch(showErrorMessage);
getStore().dispatch(setCurrentPage(PAGE_ID.COOPERATION_PAGE));
}

private observeState(data: CooperationData[]): void {
observeStore(selectCurrentLanguage, () => {
this.view.redrawCooperationInfo(data);
});
}

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

export default CooperationPageModel;
136 changes: 136 additions & 0 deletions src/pages/CooperationPage/view/CooperationPageView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import type { CooperationData } from '@/shared/types/validation/cooperationData';

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

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

class CooperationPageView {
private page: HTMLDivElement;

private parent: HTMLDivElement;

private wrapper: HTMLDivElement;

constructor(parent: HTMLDivElement) {
this.parent = parent;
this.parent.innerHTML = '';
this.wrapper = this.createCooperationWrapper();
this.page = this.createHTML();
window.scrollTo(0, 0);
}

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

return this.wrapper;
}

private createDescription(description: string): HTMLParagraphElement {
const descriptionElement = createBaseElement({
cssClasses: [styles.cooperationDescription],
tag: 'p',
});
descriptionElement.textContent = description;
return descriptionElement;
}

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

this.page.append(this.wrapper);
this.parent.append(this.page);

return this.page;
}

private createItem(text: string): HTMLLIElement {
const listItem = createBaseElement({
cssClasses: [styles.cooperationListItem],
innerContent: text,
tag: 'li',
});
return listItem;
}

private createItemList(): HTMLUListElement {
const itemList = createBaseElement({
cssClasses: [styles.cooperationItemList],
tag: 'ul',
});
return itemList;
}

private createSubtitle(subtitle: string): HTMLHeadingElement {
const subtitleElement = createBaseElement({
cssClasses: [styles.cooperationSubtitle],
tag: 'h2',
});
subtitleElement.textContent = subtitle;
return subtitleElement;
}

private createTitle(title: string): HTMLHeadingElement {
const titleElement = createBaseElement({
cssClasses: [styles.cooperationTitle],
tag: 'h2',
});
titleElement.textContent = title;
return titleElement;
}

public drawCooperationInfo(data: CooperationData[]): void {
data.forEach((item) => {
const section = createBaseElement({
cssClasses: [styles.cooperationSection],
tag: 'div',
});
const currentTitle = item[getStore().getState().currentLanguage].title;
const currentDescription = item[getStore().getState().currentLanguage].description;
const currentSubtitle = item[getStore().getState().currentLanguage].subtitle;
const currentItems = item[getStore().getState().currentLanguage].items;
if (currentTitle) {
const title = this.createTitle(currentTitle);
section.append(title);
}

if (currentDescription) {
const title = this.createDescription(currentDescription);
section.append(title);
}

if (currentSubtitle) {
const title = this.createSubtitle(currentSubtitle);
section.append(title);
}

if (currentItems) {
const createItemList = this.createItemList();
currentItems.forEach((item) => {
const listItem = this.createItem(item.text);
createItemList.append(listItem);
});
section.append(createItemList);
}

this.wrapper.append(section);
});
}

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

public redrawCooperationInfo(data: CooperationData[]): void {
this.wrapper.innerHTML = '';
this.drawCooperationInfo(data);
}
}

export default CooperationPageView;
79 changes: 79 additions & 0 deletions src/pages/CooperationPage/view/cooperationPageView.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
.cooperationPage {
position: relative;
display: block;
padding: 0 var(--small-offset);
animation: show 0.2s ease-out forwards;
}

@keyframes show {
0% {
opacity: 0;
}

100% {
display: block;
opacity: 1;
}
}

.cooperationWrapper {
display: flex;
flex-direction: column;
margin: 0 auto;
max-width: 80%;
gap: var(--extra-small-offset);

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

.cooperationSection {
display: flex;
flex-direction: column;
border-radius: var(--medium-br);
padding: var(--small-offset);
background-color: var(--steam-green-1000);
gap: var(--extra-small-offset);
}

.cooperationTitle {
font: var(--medium-font);
letter-spacing: var(--one);
color: var(--steam-green-800);
}

.cooperationSubtitle {
font: var(--medium-bold-font);
letter-spacing: var(--one);
color: var(--noble-gray-1000);
}

.cooperationDescription {
font: var(--regular-font);
line-height: 170%;
letter-spacing: var(--one);
color: var(--noble-gray-800);
}

.cooperationItemList {
display: flex;
flex-direction: column;
gap: var(--tiny-offset);
}

.cooperationListItem {
position: relative;
margin-left: var(--extra-small-offset);
font: var(--regular-font);
letter-spacing: var(--one);
color: var(--noble-gray-700);

&::after {
content: 'βœ”';
position: absolute;
left: -1.7rem;
top: 0;
padding: 0.15rem 0.3rem;
}
}
3 changes: 3 additions & 0 deletions src/shared/constants/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const PAGE_TITLE: Record<LanguageChoiceType, Record<string, string>> = {
blog: 'Blog',
cart: 'Cart',
catalog: 'Catalog',
cooperation: 'Cooperation',
login: 'Login',
main: 'Main',
product: 'Product',
Expand All @@ -22,6 +23,7 @@ export const PAGE_TITLE: Record<LanguageChoiceType, Record<string, string>> = {
blog: 'Π‘Π»ΠΎΠ³',
cart: 'ΠšΠΎΡ€Π·ΠΈΠ½Π°',
catalog: 'ΠšΠ°Ρ‚Π°Π»ΠΎΠ³',
cooperation: 'БотрудничСство',
login: 'Π’Ρ…ΠΎΠ΄',
main: 'Главная',
product: 'Π’ΠΎΠ²Π°Ρ€',
Expand Down Expand Up @@ -125,6 +127,7 @@ export const PAGE_ID = {
BLOG: 'blog',
CART_PAGE: 'cart',
CATALOG_PAGE: 'catalog',
COOPERATION_PAGE: 'cooperation',
DEFAULT_PAGE: '',
LOGIN_PAGE: 'login',
MAIN_PAGE: 'main',
Expand Down
61 changes: 61 additions & 0 deletions src/shared/types/validation/cooperationData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
interface CooperationListItem {
text: string;
}

interface CooperationItem {
description?: string;
items?: CooperationListItem[];
subtitle?: string;
title?: string;
}

export interface CooperationData {
en: CooperationItem;
ru: CooperationItem;
}

const isCooperationItem = (data: unknown): data is CooperationItem => {
let result = true;
if (data === null || typeof data !== 'object') {
result = false;
return result;
}

if ('description' in data && typeof data.description === 'string') {
result = true;
}

if ('title' in data && typeof data.title === 'string') {
result = true;
}

if ('subtitle' in data && typeof data.subtitle === 'string') {
result = true;
}

if ('items' in data && Array.isArray(data.items)) {
data.items.forEach((item: CooperationListItem) => {
result = 'text' in item && typeof item.text === 'string';
});
}

return result;
};

const isCooperationData = (data: unknown): data is CooperationData[] => {
let result = true;
if (!Array.isArray(data)) {
return false;
}
data.forEach((item: CooperationData) => {
if ('en' in item && 'ru' in item) {
result = isCooperationItem(item.en) && isCooperationItem(item.ru);
} else {
result = false;
}
});

return result;
};

export default isCooperationData;
2 changes: 1 addition & 1 deletion src/shared/utils/calcUserBirthDayRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const calcUserBirthDayRange = (birthDay: string): { end: string; start: string }
const birthDate = new Date(birthDay);

const start = new Date(birthDate.getFullYear(), birthDate.getMonth(), birthDate.getDate() - 3);
const end = new Date(birthDate.getFullYear(), birthDate.getMonth(), birthDate.getDate() + 4);
const end = new Date(birthDate.getFullYear(), birthDate.getMonth(), birthDate.getDate() + 3);

if (start.getDate() < 1) {
start.setMonth(start.getMonth() - 1);
Expand Down
9 changes: 9 additions & 0 deletions src/shared/utils/getCooperationData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const COOPERATION_URL = 'https://raw.githubusercontent.com/stardustmeg/greenshop-db/main/cooperation/cooperation.json';

const getCooperationData = async (): Promise<unknown> => {
const response = await fetch(COOPERATION_URL);
const data: unknown = await response.json();
return data;
};

export default getCooperationData;
Loading