Стек: HTML, SCSS, TS, Vite
Структура проекта:
- src/ — исходные файлы проекта
- src/components/ — папка с JS компонентами
- src/components/base/ — папка с базовым кодом
Важные файлы:
- index.html — HTML-файл главной страницы
- src/types/index.ts — файл с типами
- src/main.ts — точка входа приложения
- src/scss/styles.scss — корневой файл стилей
- src/utils/constants.ts — файл с константами
- src/utils/utils.ts — файл с утилитами
Для установки и запуска проекта необходимо выполнить команды
npm install
npm run dev
или
yarn
yarn dev
npm run build
или
yarn build
«Web-Larёk» — это интернет-магазин с товарами для веб-разработчиков, где пользователи могут просматривать товары, добавлять их в корзину и оформлять заказы. Сайт предоставляет удобный интерфейс с модальными окнами для просмотра деталей товаров, управления корзиной и выбора способа оплаты, обеспечивая полный цикл покупки с отправкой заказов на сервер.
Код приложения разделен на слои согласно парадигме MVP (Model-View-Presenter), которая обеспечивает четкое разделение ответственности между классами слоев Model и View. Каждый слой несет свой смысл и ответственность:
Model - слой данных, отвечает за хранение и изменение данных.
View - слой представления, отвечает за отображение данных на странице.
Presenter - презентер содержит основную логику приложения и отвечает за связь представления и данных.
Взаимодействие между классами обеспечивается использованием событийно-ориентированного подхода. Модели и Представления генерируют события при изменении данных или взаимодействии пользователя с приложением, а Презентер обрабатывает эти события используя методы как Моделей, так и Представлений.
Является базовым классом для всех компонентов интерфейса.
Класс является дженериком и принимает в переменной T тип данных, которые могут быть переданы в метод render для отображения.
Конструктор:
constructor(container: HTMLElement) - принимает ссылку на DOM элемент за отображение, которого он отвечает.
Поля класса:
container: HTMLElement - поле для хранения корневого DOM элемента компонента.
Методы класса:
render(data?: Partial<T>): HTMLElement - Главный метод класса. Он принимает данные, которые необходимо отобразить в интерфейсе, записывает эти данные в поля класса и возвращает ссылку на DOM-элемент. Предполагается, что в классах, которые будут наследоваться от Component будут реализованы сеттеры для полей с данными, которые будут вызываться в момент вызова render и записывать данные в необходимые DOM элементы.
setImage(element: HTMLImageElement, src: string, alt?: string): void - утилитарный метод для модификации DOM-элементов <img>
Содержит в себе базовую логику отправки запросов.
Конструктор:
constructor(baseUrl: string, options: RequestInit = {}) - В конструктор передается базовый адрес сервера и опциональный объект с заголовками запросов.
Поля класса:
baseUrl: string - базовый адрес сервера
options: RequestInit - объект с заголовками, которые будут использованы для запросов.
Методы:
get(uri: string): Promise<object> - выполняет GET запрос на переданный в параметрах ендпоинт и возвращает промис с объектом, которым ответил сервер
post(uri: string, data: object, method: ApiPostMethods = 'POST'): Promise<object> - принимает объект с данными, которые будут переданы в JSON в теле запроса, и отправляет эти данные на ендпоинт переданный как параметр при вызове метода. По умолчанию выполняется POST запрос, но метод запроса может быть переопределен заданием третьего параметра при вызове.
handleResponse(response: Response): Promise<object> - защищенный метод проверяющий ответ сервера на корректность и возвращающий объект с данными полученный от сервера или отклоненный промис, в случае некорректных данных.
Брокер событий реализует паттерн "Наблюдатель", позволяющий отправлять события и подписываться на события, происходящие в системе. Класс используется для связи слоя данных и представления.
Конструктор класса не принимает параметров.
Поля класса:
_events: Map<string | RegExp, Set<Function>>) - хранит коллекцию подписок на события. Ключи коллекции - названия событий или регулярное выражение, значения - коллекция функций обработчиков, которые будут вызваны при срабатывании события.
Методы класса:
on<T extends object>(event: EventName, callback: (data: T) => void): void - подписка на событие, принимает название события и функцию обработчик.
emit<T extends object>(event: string, data?: T): void - инициализация события. При вызове события в метод передается название события и объект с данными, который будет использован как аргумент для вызова обработчика.
trigger<T extends object>(event: string, context?: Partial<T>): (data: T) => void - возвращает функцию, при вызове которой инициализируется требуемое в параметрах событие с передачей в него данных из второго параметра.
В ходе анализа проекта было установлено: в приложении используются две сущности, которые описывают данные, — товар и покупатель.
Описывает данные товара.
Поля интерфейса:
id: string - идентификатор товара
description: string - описание товара
image: string - изображения товара
title: string - название товара
category: string - категория товара
price: number | null - цена товара (значение null означает, что товар недоступен для покупки)
Описывает данные покупателя.
Поля интерфейса:
payment: TPayment - способ оплаты
email: string - адрес электронной почты покупателя
phone: string - номер телефона покупателя
address: string - адрес доставки
Задает способ оплаты заказа:
card - онлайн;
cash - при получении;
'' - способ оплаты не выбран (начальное состояние).
type TPayment = 'card' | 'cash' | '';Используется для типизации объекта с ошибками валидации данных покупателя.
type TBuyerErrors = Partial<Record<keyof IBuyer, string>>;Описывает ответ сервера при получении списка товаров.
Поля интерфейса:
total: number - общее количество товаров
items: IProduct[] - массив товаров
Описывает данные заказа, которые отправляются на сервер. Расширяет интерфейс IBuyer (наследует поля payment, email, phone, address) и добавляет свои поля.
Поля интерфейса:
total: number - общая сумма заказа
items: string[] - массив идентификаторов товаров
Описывает ответ сервера при успешном оформлении заказа.
Поля интерфейса:
id: string - идентификатор заказа
total: number - подтвержденная сумма заказа
Описывает данные для отображения шапки сайта.
Поля интерфейса:
counter: number - количество товаров в корзине
Описывает данные для управления модальным окном.
Поля интерфейса:
content: HTMLElement - HTML-элемент, отображаемый внутри модального окна
Описывает данные для отображения каталога товаров.
Поля интерфейса:
content: HTMLElement[] - массив HTML-элементов карточек товаров
Описывает данные для отображения корзины.
Поля интерфейса:
content: HTMLElement[] - массив HTML-элементов карточек товаров в корзине
total: number - общая стоимость товаров в корзине
Описывает данные для отображения сообщения об успешном оформлении заказа.
Поля интерфейса:
total: number - сумма списанных синапсов
Описывает базовое состояние любой формы.
Поля интерфейса:
isValid: boolean - флаг валидности формы (true - все поля заполнены корректно)
errors: string - текст ошибки для отображения пользователю
Базовый тип для карточки товара. Содержит минимальный набор полей, общий для всех карточек.
Поля типа:
title: string - название товара
price: number | null - цена товара (null - товар недоступен)
Тип для карточки товара в каталоге. Расширяет базовый тип TCard.
Поля типа:
category: string - категория товара (для стилизации фона)
image: string - путь к изображению товара
Тип для карточки товара в режиме предпросмотра (модальное окно). Расширяет TCardCatalog.
Поля типа:
description: string - подробное описание товара
isUnavailable: boolean - флаг недоступности товара (блокирует кнопку покупки)
isInBasket: boolean - флаг наличия товара в корзине (меняет текст кнопки)
Тип для карточки товара в корзине. Расширяет базовый тип TCard.
Поля типа: index: number - порядковый номер товара в списке корзины
Тип для формы оформления заказа (способ оплаты и адрес). Расширяет IForm.
Поля типа:
payment: TPayment - выбранный способ оплаты
address: string - адрес доставки
Тип для формы ввода контактных данных. Расширяет IForm.
Поля типа:
email: string - адрес электронной почты
phone: string - номер телефона
Тип для описания действий, доступных на карточке товара.
Поля типа:
onClick: () => void - функция-обработчик клика по карточке товара
Для учёта данных в приложении должны быть три класса, которые будут разделены между собой по смыслу и зонам ответственности.
Хранение товаров, которые можно купить в приложении.
Конструктор:
constructor(protected events: IEvents) - принимает брокер событий для уведомления об изменениях в каталоге
Поля класса:
products: IProduct[] - массив всех товаров
selectedProduct: IProduct | null - товар, выбранный для подробного отображения
events: IEvents - брокер событий
Методы класса:
setProducts(products: IProduct[]): void - сохранение массива товаров полученного в параметрах метода
getProducts(): IProduct[] - получение массива товаров из модели
getProductById(id: string): IProduct | null - получение одного товара по его id
setSelectedProduct(product: IProduct): void - сохранение товара для подробного отображения
getSelectedProduct(): IProduct | null - получение товара для подробного отображения
Хранение товаров, которые пользователь выбрал для покупки.
Конструктор:
constructor(protected events: IEvents) - принимает брокер событий для уведомления об изменениях в корзине
Поля класса:
selectedProducts: IProduct[] - массив товаров, выбранных покупателем для покупки
events: IEvents - брокер событий
Методы класса:
getSelectedProducts(): IProduct[] - получение массива товаров, которые находятся в корзине
addProduct(product: IProduct): void - добавление товара, который был получен в параметре, в массив корзины
deleteProduct(product: IProduct): void - удаление товара, полученного в параметре из массива корзины
clearBasket(): void - очистка корзины
getTotalPrice(): number - получение стоимости всех товаров в корзине
getProductsCount(): number - получение количества товаров в корзине
hasProductInBasket(id: string): boolean - проверка наличия товара в корзине по его id, полученного в параметр метода
Данные покупателя, которые тот должен указать при оформлении заказа.
Конструктор:
constructor(protected events: IEvents) - принимает брокер событий для уведомления об изменениях данных покупателя
Поля класса:
payment: TPayment - способ оплаты
email: string - адрес электронной почты покупателя
phone: string - номер телефона покупателя
address: string - адрес доставки
events: IEvents - брокер событий
Методы класса:
setPayment(payment: TPayment): void - сохранение способа оплаты
setEmail(email: string): void - сохранение адреса электронной почты покупателя
setPhone(phone: string): void - сохранение номера телефона покупателя
setAddress(address: string): void - сохранение адреса доставки
getBuyerData(): IBuyer - получение всех данных покупателя
clearBuyerData(): void - очистка данных покупателя
validateBuyerData(): TBuyerErrors - валидация данных покупателя. Возвращает объект, где ключи названия полей с ошибками, а значения текст ошибки. Если поле валидно, оно отсутствует в возвращаемом объекте.
Отвечает за получение данных с сервера и отправку данных на сервер.
Конструктор:
constructor(api: IApi) - принимает объект, реализующий интерфейс IApi, который будет использоваться для выполнения запросов к серверу.
Поля класса:
api: IApi - поле для хранения экземпляра класса, реализующего интерфейс IApi, через который выполняются HTTP-запросы
Методы класса:
getProducts(): Promise<IProductsResponse> - выполняет GET запрос на эндпоинт /product/ и возвращает промис с объектом, полученным от сервера, в котором находится массив товаров.
postOrder(order: IOrderData): Promise<IOrderResponse> - выполняет POST запрос на эндпоинт /order/, передаёт в него данные, полученные в параметрах метода, и возвращает промис с объектом, подтверждающим покупку на определенную сумму.
Шапка сайта с корзиной.
Конструктор:
constructor(container: HTMLElement, events: IEvents) - принимает контейнер шапки и брокер событий
Поля:
counterElement: HTMLElement - элемент для отображения количества товаров в корзине
basketButton: HTMLButtonElement - кнопка-иконка корзины
Методы:
set counter(value: number) - устанавливает значение счетчика товаров в корзине
Каталог товаров.
Конструктор:
constructor(container: HTMLElement) - принимает DOM-элемент-контейнер для каталога
Поля:
galleryElement: HTMLElement - элемент галереи для размещения карточек товаров
Методы:
set content(value: HTMLElement[]) - заполняет каталог массивом DOM-элементов карточек товаров, заменяя текущее содержимое
Модальное окно.
Конструктор:
constructor(container: HTMLElement, events: IEvents) - принимает контейнер модального окна и брокер событий
Поля:
contentElement: HTMLElement - элемент для размещения основного контента модального окна
closeButton: HTMLButtonElement - кнопка закрытия (крестик)
Методы:
set content(value: HTMLElement) - устанавливает содержимое модального окна
open(): void - открывает модальное окно (добавляет класс modal_active) и генерирует событие modal:open
close(): void - закрывает модальное окно, очищает содержимое и генерирует событие modal:close
Базовый абстрактный класс для всех карточек товара.
Конструктор:
constructor(container: HTMLElement) - принимает контейнер карточки
Поля:
cardTitleElement: HTMLElement - элемент для отображения названия товара
cardPriceElement: HTMLElement - элемент для отображения цены товара
Методы:
set title(value: string) - устанавливает название товара
set price(value: number | null) - устанавливает цену (при null отображает "Бесценно")
Карточка товара в каталоге.
Конструктор:
constructor(container: HTMLElement, actions: TCardActions) - принимает контейнер и объект с обработчиком события
Поля:
cardCategoryElement: HTMLElement - элемент для отображения категории товара
cardImageElement: HTMLImageElement - элемент для отображения изображения товара
Методы:
set category(value: string) - устанавливает категорию с соответствующим CSS-классом
set image(value: string) - устанавливает изображение (добавляет базовый URL CDN)
Карточка товара в корзине.
Конструктор:
constructor(container: HTMLElement, actions: TCardActions) - принимает контейнер и объект с обработчиком события
Поля:
cardIndexElement: HTMLElement - элемент для отображения порядкового номера
cardDeleteButton: HTMLButtonElement - кнопка удаления товара из корзины
Методы:
set index(value: number) - устанавливает порядковый номер товара
Предпросмотр карточки товара.
Конструктор:
constructor(container: HTMLElement, protected events: IEvents) - принимает контейнер и брокер событий
Поля:
cardCategoryElement: HTMLElement - элемент для отображения категории
cardImageElement: HTMLImageElement - элемент для отображения изображения
cardDescriptionElement: HTMLElement - элемент для отображения описания
cardButtonElement: HTMLButtonElement - кнопка действия (Купить/Удалить)
Методы:
set image(value: string) - устанавливает изображение (добавляет базовый URL CDN)
set description(value: string) - устанавливает описание товара
set buttonText(value: string) - устанавливает текст кнопки
set buttonDisabled(value: boolean) - блокирует/разблокирует кнопку
set category(value: string) - устанавливает категорию с соответствующим CSS-классом
Базовый абстрактный класс для всех форм приложения.
Конструктор:
constructor(container: HTMLFormElement, events: IEvents) - принимает элемент формы и брокер событий
Поля:
formElement: HTMLFormElement - элемент формы
errorsElement: HTMLElement - элемент для отображения текста ошибок
submitButton: HTMLButtonElement - кнопка отправки формы
Методы:
set isValid(value: boolean) - блокирует/разблокирует кнопку отправки в зависимости от валидности формы
set errors(value: string) - устанавливает текст ошибки
Форма для выбора способа оплаты и ввода адреса доставки.
Конструктор:
constructor(container: HTMLFormElement, events: IEvents) - принимает элемент формы и брокер событий
Поля:
cardButton: HTMLButtonElement - кнопка выбора оплаты картой
cashButton: HTMLButtonElement - кнопка выбора оплаты наличными
addressInput: HTMLInputElement - поле ввода адреса доставки
Методы:
set payment(value: string) - устанавливает активный способ оплаты
set address(value: string) - устанавливает значение поля адреса
Форма для ввода контактных данных покупателя.
Конструктор:
constructor(container: HTMLFormElement, events: IEvents) - принимает элемент формы и брокер событий
Поля:
emailInput: HTMLInputElement - поле ввода email
phoneInput: HTMLInputElement - поле ввода телефона
Методы:
set email(value: string) - устанавливает значение поля email
set phone(value: string) - устанавливает значение поля phone
Компонент корзины.
Конструктор:
constructor(container: HTMLElement, events: IEvents) - принимает контейнер и брокер событий
Поля:
listElement: HTMLElement - элемент списка товаров
priceElements: HTMLElement - элемент отображения суммы
orderButton: HTMLButtonElement - кнопка оформления заказа
Методы:
set content(value: HTMLElement[]) - заполняет список карточками товаров
set total(value: number) - устанавливает текст общей суммы
set buttonDisabled(value: boolean) - блокирует/разблокирует кнопку оформления заказа
Экран успешного оформления заказа.
Конструктор:
constructor(container: HTMLElement, events: IEvents) - принимает контейнер и брокер событий
Поля:
descriptionElement: HTMLElement - элемент для отображения сообщения о списании
closeButton: HTMLButtonElement - кнопка закрытия
Методы:
set total(value: number) - устанавливает текст сообщения с суммой списанных синапсов
Презентер реализован в файле main.ts и связывает модели данных с компонентами представления. Выбран подход с использованием единого файла-презентера без выделения отдельного класса, что соответствует рекомендациям для одностраничного приложения.
catalog:change - изменение каталога товаров
product:select - изменение выбранного для просмотра товара
basket:change - изменение содержимого корзины
buyer:change - изменение данных покупателя
card:click - выбор карточки для просмотра
product:toggle - нажатие кнопки в карточке предпросмотра (добавление/удаление из корзины)
basket:open - нажатие кнопки открытия корзины
basket:remove - нажатие кнопки удаления товара из корзины (в корзине)
basket:order - нажатие кнопки оформления заказа
order:submit - нажатие кнопки перехода ко второй форме оформления заказа
contacts:submit - нажатие кнопки оплаты/завершения оформления заказа
success:close - закрытие окна успешного оформления заказа
payment:change - изменение способа оплаты
address:change - изменение адреса доставки
email:change - изменение email
phone:change - изменение телефона
modal:open - открытие модального окна
modal:close - закрытие модального окна