Skip to content

SofiaKubo/weblarek

Repository files navigation

Проектная работа "Веб-ларек"

Стек: 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»

«Web-Larёk» — это интернет-магазин с товарами для веб-разработчиков, где пользователи могут просматривать товары, добавлять их в корзину и оформлять заказы. Сайт предоставляет удобный интерфейс с модальными окнами для просмотра деталей товаров, управления корзиной и выбора способа оплаты, обеспечивая полный цикл покупки с отправкой заказов на сервер.

Архитектура приложения

Код приложения разделен на слои согласно парадигме MVP (Model-View-Presenter), которая обеспечивает четкое разделение ответственности между классами слоев Model и View. Каждый слой несет свой смысл и ответственность:

Model - слой данных, отвечает за хранение и изменение данных. View - слой представления, отвечает за отображение данных на странице. Presenter - презентер содержит основную логику приложения и отвечает за связь представления и данных.

Взаимодействие между классами обеспечивается использованием событийно-ориентированного подхода. Модели и Представления генерируют события при изменении данных или взаимодействии пользователя с приложением, а Презентер обрабатывает эти события используя методы как Моделей, так и Представлений.

Базовый код

Класс Component

Является базовым классом для всех компонентов интерфейса. Класс является дженериком и принимает в переменной 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>

Класс Api

Содержит в себе базовую логику отправки запросов.

Конструктор: 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> - защищенный метод проверяющий ответ сервера на корректность и возвращающий объект с данными полученный от сервера или отклоненный промис, в случае некорректных данных.

Класс EventEmitter

Брокер событий реализует паттерн "Наблюдатель", позволяющий отправлять события и подписываться на события, происходящие в системе. Класс используется для связи слоя данных и представления.

Конструктор класса не принимает параметров.

Поля класса: _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 - возвращает функцию, при вызове которой инициализируется требуемое в параметрах событие с передачей в него данных из второго параметра.

Данные

Товар (IProduct)

Интерфейс описывает товар, отображаемый в каталоге интернет-магазина.

Поля:

id: string — уникальный идентификатор товара;

title: string — название товара;

description: string — описание товара;

image: string — ссылка на изображение товара;

category: string — категория товара;

price: number | null — цена товара. Может быть null, если товар недоступен для покупки.

interface IProduct {
  id: string;
  description: string;
  image: string;
  title: string;
  category: string;
  price: number | null;
}

Способ оплаты (TPayment)

Тип описывает доступные способы оплаты заказа.

Возможные значения:

card — оплата банковской картой;

cash — оплата наличными.

type TPayment = 'card' | 'cash';

Покупатель (IBuyer)

Интерфейс описывает данные покупателя, которые используются при оформлении заказа.

Поля:

payment: TPayment | null — выбранный способ оплаты. Может быть null, если способ оплаты ещё не выбран;

email: string — email покупателя;

phone: string — телефон покупателя;

address: string — адрес доставки.

interface IBuyer {
  payment: TPayment | null;
  email: string;
  phone: string;
  address: string;
}

ProductsResponse

Интерфейс описывает ответ сервера при запросе каталога товаров.

Поля:

total: number — общее количество товаров;

items: IProduct[] — массив товаров каталога.

interface ProductsResponse {
  total: number;
  items: IProduct[];
}

Заказ

Типы описывают данные, которые приложение отправляет на сервер при оформлении заказа, и данные, которые сервер возвращает в ответ. Он формируется на основе данных покупателя, выбранных товаров и общей стоимости заказа.

OrderRequest

Тип описывает объект заказа, отправляемый на сервер.

Заказ формируется из данных покупателя, списка выбранных товаров и общей стоимости заказа. Структура объекта соответствует формату API сервера.

Поля:

payment: TPayment — выбранный способ оплаты;

email: string — email покупателя;

phone: string — телефон покупателя;

address: string — адрес доставки;

items: string[] — массив идентификаторов выбранных товаров;

total: number — общая стоимость заказа.

export type OrderRequest = {
  payment: TPayment;
  email: string;
  phone: string;
  address: string;
  items: string[];
  total: number;
};

OrderResponse

Тип описывает ответ сервера после успешного оформления заказа.

Поля:

id: string — уникальный идентификатор оформленного заказа;

total: number — итоговая стоимость оформленного заказа.

export type OrderResponse = {
  id: string;
  total: number;
};

Типы событий приложения

Типы событий, используемые EventEmitter, объявлены в src/types/index.ts.

ProductsListChangedEvent

interface ProductsListChangedEvent {
  products: IProduct[];
}

ProductSelectionChangedEvent

interface ProductSelectionChangedEvent {
  product: IProduct | null;
}

BasketStateChangedEvent

interface BasketStateChangedEvent {
  items: IProduct[];
  total: number;
}

BuyerDataChangedEvent

interface BuyerDataChangedEvent {
  buyer: IBuyer;
}

FormSubmitTriggeredEvent

interface FormSubmitTriggeredEvent {
  form: 'order' | 'contacts';
}

FormFieldChangedEvent

type OrderFieldChangedEvent =
  | { field: 'payment'; value: TPayment }
  | { field: 'address'; value: string };

type ContactsFieldChangedEvent =
  | { field: 'email'; value: string }
  | { field: 'phone'; value: string };

type FormFieldChangedEvent =
  | ({ form: 'order' } & OrderFieldChangedEvent)
  | ({ form: 'contacts' } & ContactsFieldChangedEvent);

Модели данных

Каталог товаров (ProductsModel)

Класс отвечает за хранение и управление каталогом товаров, полученных с сервера. Класс не содержит логики отображения, работы с DOM или сетевых запросов.

Конструктор: constructor(events: IEvents) — принимает экземпляр брокера событий. Начальное состояние модели инициализируется пустыми значениями.

Поля класса:

items: IProduct[] — массив всех товаров каталога;

selectedItem: IProduct | null — товар, выбранный для подробного просмотра.

Методы класса:

setItems(items: IProduct[]): void — сохраняет массив товаров в модель и генерирует событие изменения каталога;

getItems(): IProduct[] — возвращает массив всех товаров;

getItemById(id: string): IProduct | undefined — возвращает товар по идентификатору;

setSelectedItem(item: IProduct | null): void — сохраняет выбранный товар или сбрасывает выбор, если передан null, затем генерирует событие изменения выбранного товара;

getSelectedItem(): IProduct | null — возвращает выбранный товар.


Корзина (BasketModel)

Класс отвечает за хранение товаров, выбранных пользователем, и за расчёт агрегированных значений (общая стоимость, количество товаров, наличие товара в корзине). Класс не отвечает за отображение корзины и обработку пользовательских событий.

Конструктор: constructor(events: IEvents) — принимает экземпляр брокера событий. Корзина инициализируется пустым массивом товаров.

Поля класса:

items: IProduct[] — массив товаров в корзине.

Методы класса:

getItems(): IProduct[] — возвращает массив товаров, находящихся в корзине на текущий момент;

addItem(item: IProduct): void — добавляет переданный товар в корзину, если его там ещё нет, и генерирует событие изменения корзины;

removeItem(itemId: string): void — удаляет товар из корзины по его идентификатору и генерирует событие изменения корзины;

clear(): void — очищает корзину, удаляя из неё все товары, используется после успешного оформления заказа и генерирует событие изменения корзины;

getTotalPrice(): number — возвращает общую стоимость всех товаров, находящихся в корзине. При расчёте учитываются только товары с заданной ценой;

getItemsCount(): number — возвращает количество товаров, находящихся в корзине;

hasItem(itemId: string): boolean — проверяет наличие товара в корзине по его идентификатору. Возвращает true, если товар найден, и false — в противном случае.


Покупатель (BuyerModel)

Класс отвечает за хранение данных покупателя, вводимых при оформлении заказа, а также за их валидацию. Класс не выполняет отправку данных на сервер и не управляет отображением форм.

Конструктор: constructor(events: IEvents) — принимает экземпляр брокера событий. Все поля модели инициализируются пустыми значениями или null.

Поля класса:

payment: TPayment | null — выбранный способ оплаты. Может быть null, если способ оплаты ещё не выбран;

email: string — адрес электронной почты покупателя;

phone: string — телефон покупателя;

address: string — адрес доставки.

Методы класса:

setData(data: Partial<IBuyer>): void — сохраняет данные покупателя в модели. Метод принимает объект с частичным набором полей и обновляет только переданные значения, не затирая остальные данные, уже сохранённые в модели. Используется при пошаговом заполнении формы оформления заказа. После сохранения генерирует событие изменения данных покупателя;

getData(): IBuyer — возвращает текущие данные покупателя, сохранённые в модели;

clear(): void — очищает данные покупателя, сбрасывая все поля модели в начальное состояние. Используется после успешного оформления заказа и генерирует событие изменения данных покупателя;

validate(): Partial<Record<keyof IBuyer, string>> — выполняет валидацию данных покупателя в соответствии с функциональными требованиями приложения.

Метод возвращает объект с ошибками валидации, где:

ключ — имя поля;

значение — текст ошибки, связанный с проверкой этого поля.

Если поле прошло проверку, соответствующее свойство отсутствует в возвращаемом объекте. Пустой объект означает, что все данные валидны и оформление заказа может быть продолжено.

Текущие сообщения валидации: paymentВыберите способ оплаты; emailУкажите email; phoneУкажите телефон; addressНеобходимо указать адрес.

Слой коммуникации

Слой коммуникации отвечает за взаимодействие приложения с сервером. Он используется для получения данных с сервера и отправки данных на сервер и представляет собой изолированный слой, не связанный напрямую с моделями данных или представлением.

Данный слой реализован в виде отдельного класса, который использует композицию и работает с сервером через базовый класс Api, предоставленный в стартовом наборе проекта.

Слой коммуникации выполняет следующие задачи:

  • получение каталога товаров с сервера;
  • отправка данных оформленного заказа на сервер.

Класс слоя коммуникации не хранит состояние приложения, не изменяет модели данных напрямую и не содержит логики отображения интерфейса. Он только выполняет HTTP-запросы и возвращает результат вызывающему коду.


Класс WebLarekApi

Класс WebLarekApi является представителем коммуникационного слоя приложения.

  • выполняет GET-запрос для получения списка товаров и POST-запрос для отправки данных заказа;
  • работает с сервером через методы get и post базового класса Api.

Конструктор: constructor(api: IApi) — принимает зависимость для HTTP-запросов, соответствующую интерфейсу IApi. Использование композиции позволяет изолировать логику коммуникации от конкретной реализации сетевых запросов.

Методы класса:

getProducts(): Promise<IProduct[]> — выполняет GET-запрос на эндпоинт /product и получает объект типа ProductsResponse. Из ответа извлекается массив товаров items, который возвращается вызывающему коду. Возвращаемые данные используются для сохранения каталога товаров в модели данных;

postOrder(data: OrderRequest): Promise<OrderResponse> — выполняет POST-запрос к эндпоинту /order/ и отправляет на сервер данные оформленного заказа. В параметры метода передаются данные покупателя, выбранные товары и итоговая стоимость заказа в формате, соответствующем API сервера. Метод возвращает ответ сервера, подтверждающий успешную обработку заказа.


Взаимодействие с другими слоями

Слой коммуникации:

не зависит от моделей данных;

не изменяет состояние моделей напрямую;

используется в основном скрипте приложения для получения и передачи данных.

После получения данных с сервера ответственность за их хранение и обработку передаётся моделям данных.

Слой представления (View)

Компоненты представления отвечают за отображение разметки и генерацию пользовательских событий. Компоненты не содержат бизнес-логики и не работают с моделями данных напрямую.

Галерея (Gallery)

Класс отвечает за вывод списка карточек каталога на главной странице.

Конструктор: constructor(container: HTMLElement) — принимает контейнер каталога.

Поля класса: container: HTMLElement — корневой DOM-элемент компонента.

Методы: set catalog(items: HTMLElement[]) — устанавливает массив карточек каталога.

Шапка (Header)

Класс отвечает за отображение счётчика корзины и кнопку открытия корзины.

Конструктор: constructor(container: HTMLElement, events: IEvents) — принимает контейнер шапки и брокер событий.

Поля класса: counterElement: HTMLElement — элемент счётчика; basketButton: HTMLButtonElement — кнопка корзины.

Методы: set counter(value: number) — обновляет значение счётчика в шапке.

Модальное окно (Modal)

Класс отвечает за контейнер модального окна, отображение контента и закрытие модального окна.

Конструктор: constructor(container: HTMLElement, events: IEvents) — принимает контейнер модального окна и брокер событий.

Поля класса: closeButton: HTMLButtonElement — кнопка закрытия; contentElement: HTMLElement — контейнер контента; isOpen: boolean — флаг открытого состояния.

Методы: open(): void — открывает модальное окно; close(): void — закрывает модальное окно; set content(node: HTMLElement | null) — устанавливает контент модального окна.

Базовая карточка (CardBase)

Абстрактный родительский класс карточек каталога, превью и корзины.

Конструктор: constructor(container: HTMLElement) — принимает корневой элемент карточки.

Поля класса: titleElement, priceTextElement.

Методы: сеттеры для управления отображением: title, priceText.

Базовая визуальная карточка (CardVisualBase)

Абстрактный промежуточный класс для визуальных карточек каталога и превью.

Конструктор: constructor(container: HTMLElement) — принимает корневой элемент карточки.

Поля класса: categoryElement, imageElement.

Методы: сеттеры для управления визуальной частью: category, imageSrc, imageAlt.

Карточка каталога (CardCatalog)

Отвечает за отображение карточки товара в каталоге.

Конструктор: constructor(container: HTMLElement, actions: ICatalogCardActions) — принимает контейнер и обработчик клика по карточке.

Наследование: CardCatalog наследуется от CardVisualBase.

Карточка превью (CardPreview)

Отвечает за отображение карточки товара в модальном окне просмотра.

Конструктор: constructor(container: HTMLElement, actions: IPreviewCardActions) — принимает контейнер и обработчик кнопки действия.

Наследование: CardPreview наследуется от CardVisualBase.

Поля класса: descriptionElement: HTMLElement — элемент описания товара; actionButton: HTMLButtonElement — кнопка действия (купить/удалить).

Методы: set description(value: string) — устанавливает описание товара; set actionDisabled(value: boolean) — управляет доступностью кнопки действия; set actionText(value: string) — устанавливает текст кнопки действия.

Карточка корзины (CardBasket)

Отвечает за отображение позиции товара в корзине.

Конструктор: constructor(container: HTMLElement, actions: IBasketCardActions) — принимает контейнер и обработчик удаления товара.

Наследование: CardBasket наследуется напрямую от CardBase.

Поля класса: indexElement: HTMLElement — номер позиции; removeButton: HTMLButtonElement — кнопка удаления.

Методы: set index(value: number) — устанавливает номер позиции.

Корзина (Basket)

Отвечает за отображение списка товаров в корзине, итоговой стоимости и кнопки оформления.

Конструктор: constructor(container: HTMLElement, events: IEvents) — принимает контейнер корзины и брокер событий.

Поля класса: listElement: HTMLElement — контейнер списка; totalElement: HTMLElement — элемент итоговой суммы; submitButton: HTMLButtonElement — кнопка оформления.

Методы: set items(cards: HTMLElement[]) — устанавливает элементы списка корзины; set submitDisabled(value: boolean) — управляет доступностью кнопки оформления; set total(value: number) — устанавливает итоговую стоимость в формате ru-RU (с разделением разрядов) и выводит значение с подписью синапсов.

Базовая форма (FormBase)

Абстрактный родительский класс форм оформления заказа.

Конструктор: constructor(container: HTMLFormElement, events: IEvents) — принимает форму и брокер событий.

Поля класса: formElement, submitButton, errorsElement, events.

Методы: set valid(value: boolean) — управляет доступностью кнопки отправки; set errors(value: string[]) — устанавливает текст ошибок; reset(): void — очищает форму и сбрасывает состояние.

Форма шага оплаты и адреса (FormOrder)

Отвечает за отображение первого шага оформления заказа.

Конструктор: constructor(container: HTMLFormElement, events: IEvents) — принимает форму и брокер событий.

Поля класса: cardButton, cashButton, addressInput.

Методы: set payment(value: 'card' | 'cash' | null) — подсвечивает выбранный способ оплаты; set address(value: string) — устанавливает адрес доставки.

Форма шага контактов (FormContacts)

Отвечает за отображение второго шага оформления заказа.

Конструктор: constructor(container: HTMLFormElement, events: IEvents) — принимает форму и брокер событий.

Поля класса: emailInput, phoneInput.

Методы: set email(value: string) — устанавливает email; set phone(value: string) — устанавливает телефон.

Окно успешного заказа (OrderSuccess)

Отвечает за отображение подтверждения успешной оплаты.

Конструктор: constructor(container: HTMLElement, events: IEvents) — принимает контейнер и брокер событий.

Поля класса: descriptionElement: HTMLElement — элемент описания успешного заказа; closeButton: HTMLButtonElement — кнопка закрытия окна.

Методы: set total(value: number) — устанавливает сумму списания в формате ru-RU (с разделением разрядов).

События приложения

Ниже приведены события и пользовательские действия, используемые в приложении. Все типы событий приложения объявлены в src/types/index.ts.

События моделей данных

products:list-changed — изменение каталога товаров; products:selection-changed — изменение выбранного товара для просмотра; basket:state-changed — изменение содержимого корзины; buyer:data-changed — изменение данных покупателя.

События представления (EventEmitter)

basket:icon-clicked — нажатие на иконку корзины; basket:checkout-clicked — нажатие на кнопку оформления в корзине; modal:close-triggered — закрытие модального окна по крестику, оверлею или Escape; form:field-changed — изменение поля в форме оформления; form:submit-triggered — отправка формы оформления; order-success:close-clicked — закрытие окна успешного заказа.

Пользовательские действия View (callback)

card-catalog click — выбор карточки товара в каталоге; card-preview action click — нажатие кнопки купить/удалить в карточке просмотра; card-basket remove click — удаление позиции в корзине.

Презентер

В проекте реализован отдельный класс Presenter, который отвечает за оркестрацию взаимодействия между Model, View и API.

Назначение

Класс Presenter:

  • подписывается на события моделей и представлений;
  • вызывает публичные методы моделей;
  • подготавливает данные для отображения;
  • вызывает публичные методы компонентов представления;
  • управляет переходами между шагами оформления заказа;
  • выполняет загрузку каталога и отправку заказа через WebLarekApi.

Конструктор

constructor(deps: PresenterDependencies) — принимает все зависимости приложения (модели, представления, API, шаблоны, брокер событий и базовый URL изображений).

Основные обработчики событий

handleProductsListChanged — рендер каталога; handleProductSelectionChanged — открытие карточки просмотра; handleBasketStateChanged — обновление счётчика корзины и содержимого корзины в модальном окне; handleBasketCheckoutClick — открытие первого шага оформления; handleFormFieldChanged — сохранение изменений полей в BuyerModel; handleFormSubmitTriggered — переход между шагами оформления и запуск отправки заказа; handleBuyerDataChanged — реактивное обновление состояния открытой формы; handleOrderSuccessCloseClicked и handleModalCloseTriggered — закрытие модального окна и сброс UI-состояния.

При успешной отправке заказа итоговая сумма для окна успеха берётся из ответа сервера (OrderResponse.total).

Роль в MVP

Presenter не хранит бизнес-данные каталога/корзины/покупателя и не выполняет прямых манипуляций с DOM через document.querySelector, innerHTML и подобные API. Все изменения состояния данных выполняются через модели, а отображение — через публичный интерфейс компонентов View.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published