Стек: HTML, SCSS, TS, Webpack
Структура проекта:
- src/ — исходные файлы проекта
- src/components/ — папка с JS компонентами
- src/components/base/ — папка с базовым кодом + класс Presenter
- src/components/models/ - папка с используемыми классами Model
- src/components/views/ - папка с используемыми классами View
Важные файлы:
- src/pages/index.html — HTML-файл главной страницы
- src/types/index.ts — файл с типами
- src/index.ts — точка входа приложения и реализация класса Presenter
- src/scss/styles.scss — корневой файл стилей
- src/utils/constants.ts — файл с константами
- src/utils/utils.ts — файл с утилитами
Для установки и запуска проекта необходимо выполнить команды
npm install
npm run start
или
yarn
yarn start
npm run build
или
yarn build
Приложение реализовано по MVP архитектуре и состоит из компонентов:
№ | Компонент | Описание | Базовый класс | Связанный класс |
---|---|---|---|---|
1 | Model | Модель данных | Model |
|
2 | View | Модель отображения | Component |
|
3 | Presenter | Модель связи | - | Реализуется в файле index.ts |
В приложении используется событийно-ориентированный подход. В качестве инструмента, который обеспечивает данных подход, выступает EventEmitter.
Базовый класс доступа к веб-серверу, который реализует 2 типа основных операций: безопасные (GET) и небезопасные (POST, DELETE)
Базовый класс для наследования модели отображения. Реализует базовые элементы работы с элементами, такие как переключение классов, установка текста у элемента и т.д.
Реализует паттерн «Наблюдатель» и позволяет подписываться на события и уведомлять подписчиков о наступлении события.
Класс имеет методы on
, off
, emit
— для подписки на событие, отписки от события и уведомления
подписчиков о наступлении события соответственно.
Дополнительно реализованы методы onAll
и offAll
— для подписки на все события и сброса всех подписчиков.
Базовый класс для компонентов модели данных. Позволяем связать переданный данные со свойствами объекта (реализуется в конструкторе) и инициализировать вызов именованных событий через метод emitChanges
.
Класс данных всего приложения. Позволяет отслеживать состояние всего приложения. Содержит внутри себя свойство:
catalog
- для отслеживания списка доступных лотов - установка данного свойства вызывает событиеcatalog:changed
basket
- отслеживание лотов, которые находятся в корзине.order
- отслеживает состояние заказаpreview
- отслеживает лот, который используется для подробного изучения в модальном окне
Также реализует дополнительные методы для доступа к методам перечисленных выше свойств
Класс данных отдельной карточки. Структура карточки определяется ответом от API - сервера с добавлением свойства и методов, реализующих логику взаимодействия с корзиной через вызов события lot:changed
Класс данных процесса оформления заказа. Содержит свойства, которые отображаются на полях соответствующих форм и реализует простейшую логику валидации свойств на наличие значений. Изменения в любом из свойств вызывают проверку всех полей и генерации события formErrors:changed
Класс представления всей страницы. Позволяет задать:
counter
- элемент отображения количества товаров в корзинеgalery
- элемент отображения всех доступных карточекwrapper
- обёртка, позволяющая блокировать прокрутку страницы при открытии модального окнаbasket
- кнопка для отображения корзины. Клик по кнопке вызывает событиеbasket:open
Класс представления модального окна. Позволяет задать
content
- для отображения внутреннего содержания модального окнаcloseButton
- для отображения кнопки закрытия модального окна
Привязывает события закрытие модального окна (modal:close
) к кликам по кнопке закрытия формы и по родительскому контейнеру модального окна
Класс представления корзины. Позволяет задать:
list
- список отображаемых элементов в корзинеtotal
- общую ценность корзиныbutton
- кнопку открытия формы оформления заказа. Вызывает событиеorder_payment:open
Класс представления элементов корзины. Позволяет задать:
index
- порядковый номер элемента в корзинеtitle
- название элемента в корзинеprice
- стоимость элемента в корзинеdeleteBtn
- кнопка удаления элемента из корзины
Класс представления базовой формы. Позволяет задать:
submit
- кнопку отправки формыerrors
- блок отображения ошибок в форме
В данном классе на весь контейнер отображение привязываем событие отслеживание input, для вызова событий вида container.field:change
и событие container:submit
Класс представления, наследующийся от класса Form, для отображения формы оформления заказа с информацией об способе оплаты с адресом доставки. Задаются следующие свойства:
- payment - способ оплаты
- address - адрес доставки
Класс представления, наследующийся от класса Form, для отображения формы оформления заказа с контактной информацией. Задаются следующие свойства:
- email - почта для связи
- phone - телефон для связи
Класс представления, определяющий отображение основной информации об оформленном заказе:
- total - общая сумма заказа (забираем из ответа сервера)
Класс взаимодействия с конкретным API-сервером. Реализует такие методы, как:
getLotItem
- для чтения информации по конкретному лотуgetLotList
- для чтения информации по всем доступным лотамpostOrderLots
- оформления заказа через соответствующий запрос на сервер
// Модель лота
interface ILot {
id: string; // идентификатор лота
title: string; // заголовок лота
description: string; // описание лота
image: string; // полный путь до файла картинки лота
category: ILotCategory; // категория лота
price: number; // цена лота
isOrdered: boolean; // признак включения в заказ
placeInBasket: () => void; // добавляем лот в корзину
removeFromBasket: () => void; // удаляем лот из корзины
}
// Модель заказа
interface IOrder { // Модель заказа
payment: IPaymentType; // способ оплаты
address: string; // адрес доставки
email: string; // почта для связи
phone: string; // телефон для связи
items: ILot[]; // объекты лотов в корзине
formErrors: IFormErrors; // массив ошибок у формы
validateOrder(): void; // проверка полей формы
clearOrder(): void; // обнуляем поля заказа
validatePayment(): void; // проверяем способ оплаты
validateAddress(): void; // проверяем адрес доставки
validateEmail(): void; // проверяем почту
validatePhone(): void; // проверяем телефон
postOrder(): void; // завершаем заказ
}
interface IAppState { // Модель приложения
catalog: ILot[]; // доступные лоты
basket: ILot[]; // лоты в корзине
order: IOrder; // заказ
preview: ILot; // лот для модального окна
isLotInBasket(item: ILot): boolean; // проверка находится ли лот в корзине
clearBasket(): void; // очищаем корзину
getTotalAmount(): number; // получить стоимость корзины
getBasketIds(): number; // получить список индексов в корзине
getBasketLength(): number; // получить количество товаров в корзине
initOrder(): IOrder; // инициализируем объект заказа}
// Все события в ларьке
enum Events {
LOAD_LOTS = 'catalog:changed', // подгружаем доступные лоты
OPEN_LOT = 'card:open', // открываем карточку лота для просмотра
OPEN_BASKET = 'basket:open', // открываем корзину
CHANGE_LOT_IN_BASKET = 'lot:changed', // добавляем/удаляем лот из корзины
VALIDATE_ORDER = 'formErrors:changed', // проверяем форму отправки
OPEN_FIRST_ORDER_PART = 'order_payment:open', // начинаем оформление заказа
FINISH_FIRST_ORDER_PART = 'order:submit', // заполнили первую форму
OPEN_SECOND_ORDER_PART = 'order_contacts:open', // продолжаем оформление заказа
FINISH_SECOND_ORDER_PART = 'contacts:submit', // заполнили первую форму
PLACE_ORDER = 'order:post', // завершаем заказ
SELECT_PAYMENT = 'payment:changed', // выбираем способ оплаты
INPUT_ORDER_ADDRESS = 'order.address:change', // изменили адрес доставки
INPUT_ORDER_EMAIL = 'contacts.email:change', // изменили почту для связи
INPUT_ORDER_PHONE = 'contacts.phone:change', // изменили телефон для связи
OPEN_MODAL = 'modal:open', // блокировка при открытии модального окна
CLOSE_MODAL = 'modal:close', // снятие блокировки при закрытии модального окна
}