# Лабораторная работа № 8. Реализация функциональности оформления заказа
---
Реализуйте сохранение выбранных пользователем блюд, чтобы после обновления страницы выбор пользователя не сбрасывался. Добавьте страницу оформления заказа, где будет отображаться текущий состав заказа и форма ввода данных. Реализуйте возможность оформления заказа – отправку данных на сервер.
## Порядок выполнения
1. Реализуйте сохранение данных о выбранных пользователем блюдах в localStorage, чтобы в последующем иметь возможность получить эти данные на странице "Оформить заказ". В localStorage должны храниться только идентификаторы выбранных блюд. Формат хранения данных выбирается на усмотрение студента.
2. Добавьте страницу "Оформить заказ". Внешний вид страницы представлен на макете ниже. Добавьте ссылку на эту страницу в навигационное меню. На странице "Оформить заказ" должно быть два раздела: "Состав заказа" и "Оформление заказа".
- В разделе "Состав заказа" должны быть отображены ранее добавленные пользователем в заказ блюда (список выбранных блюд нужно загрузить из localStorage, а данные для отображения – загрузить с сервера).
- Формат отображения блюд такой же, как на странице "Собрать ланч".
- Вместо кнопки "Добавить" в карточке блюда должна быть кнопка "Удалить", по нажатию на которую соответствующее блюдо удаляется из заказа (т. е. информация о нём удаляется из localStorage) и карточка удаляется со страницы.
- Если ни одно блюдо не выбрано, нужно отобразить пользователю текст "Ничего не выбрано. Чтобы добавить блюда в заказ, перейдите на страницу Собрать ланч.", где текст "Собрать ланч" – гиперссылка на соответствующую страницу.
- В разделе "Оформление заказа" должна отображаться форма оформления заказа, перенесённая со страницы "Собрать ланч".
- Выбранные пользователем блюда отображаются в левой части формы (см. макет выше), напротив каждого блюда отображается его стоимость.
- При удалении блюда из заказа в соответствующем ему пункте должен отобразиться текст "Не выбран" (или "Не выбрано" для "Главного блюда") и должна быть пересчитана итоговая стоимость заказа.
- Перед отправкой данных формы на сервер должна производиться проверка соответствия состава заказа одному из доступных комбо (см. задание к ЛР 6).
3. Внесите исправления в страницу "Собрать ланч": вместо формы оформления заказа разместите панель для перехода к оформлению заказа, в которой будет отображаться текущая стоимость добавленных в заказ блюд и ссылка "Перейти к оформлению", ведущая на страницу оформления заказа.
- Панель для перехода к оформлению заказа должна быть скрыта, если пользователь не добавил в заказ ни одного блюда.
- Ссылка "Перейти к оформлению" должна быть недоступна до тех пор, пока сосав заказа не удовлетворяет одному из доступных комбо (см. задание к ЛР 6).
- Для размещения элемента на странице используйте position: sticky.
- При добавлении очередного блюда в заказ, стоимость должна пересчитываться.
- При открытии страницы "Собрать ланч" должны учитываться данные текущего заказа, хранящиеся в localStorage (т. е. в меню должны быть выделены выбранные блюда, рассчитана и отображена текущая суммарная стоимость выбранных блюд и т. д.).
4. Реализуйте отправку данных заказа на сервер по нажатию на кнопку "Отправить".
- Запрос должен быть отправлен при помощи fetch.
- В случае возникновения ошибки при отправке запроса или его обработке на стороне сервера пользователю должно быть отображено уведомление об ошибке (можно использовать функцию alert или написать кастомное модальное окно – на ваше усмотрение).
- После успешного оформления заказа данные о выбранных блюдах из localStorage должны удаляться. Если не удалось оформить заказ – данные не удаляются.
## Инструкция по работе с API
Для получения доступа к API необходимо пройти процедуру авторизации. Для авторизации нужно указать в качестве параметра запроса api_key значение уникального ключа, который выдаётся каждому пользователю. Ключ представляет собой идентификатор UUIDv4, который является случайным 16-байтным номером (например, 123e4567-e89b-12d3-a456-426655440000).
**Обратите внимание, что параметр api_key всегда передаётся в строке запроса.**
Пользователь может просматривать, редактировать и удалять только свои заказы. В один момент времени в базе данных может быть не более 10 заказов, созданных одним и тем же пользователем.
Если пользователь попробует совершить действие, не пройдя авторизацию, в качестве ответа на его запрос придёт сообщение:
{"error": "Для получения доступа к API необходимо пройти процедуру авторизации. Для этого нужно передать в запросе персональный API Key."}
При передаче параметров в POST- и PUT-запросах данные должны передаваться в теле запроса в формате application/json (нужно вручную сериализовать данные и установить значение заголовка Content-Type) или multipart/form-data (достаточно при отправке данных формы воспользоваться объектом FormData).
При оформлении заказа в запросе к серверу должны быть указаны значения обязательных полей:
|Название|Тип|Обязательное|Только для чтения|Примечание|
|---|---|---|---|---|
|id|Integer||Да|Устанавливается сервером.|
|full_name|String|Да|||
|email|String|Да||
|subscribe|Boolean|Нет||Допустимы значения 0 и 1.|
|phone|String|Да|||
|delivery_address|String|Да|||
|delivery_type|String|Да||Допустимы значения "now" и "by_time".|
|delivery_time|Time|Нет||Значение передаётся в формате HH:MM.
Доступное время доставки: с 7:00 до 23:00 с шагом 5 минут.
Не должно быть пустым, если delivery_type=by_time.
Не должно быть раньше текущего времени.|
|comment|String|Нет|||
|soup_id|Integer|Нет||Обязательность поля зависит от состава комбо.|
|main_course_id|Integer|Нет||Обязательность поля зависит от состава комбо.|
|salad_id|Integer|Нет||Обязательность поля зависит от состава комбо.|
|drink_id|Integer|Да|||
|dessert_id|Integer|Нет|||
|created_at|DateTime||Да|Устанавливается сервером.|
|updated_at|DateTime||Да|Устанавливается сервером.|
|student_id|Integer||Да|Устанавливается сервером.|
Доступные пользователю действия представлены в таблице:
|Действие|Метод и путь|Формат ответа|Примечание|
|---|---|---|---|
|Получить данные всех блюд|GET /labs/api/dishes|JSON [{item1},{item2},...{itemN}]||
|Получить данные конкретного блюда|GET /labs/api/dishes/{int:dish_id}|JSON {Item}|Вместо {int:dish_id} нужно подставить целое число – идентификатор блюда.|
|Получить данные всех заказов|GET /labs/api/orders|JSON [{item1},{item2},...{itemN}]||
|Получить данные конкретного заказа|GET /labs/api/orders/{int:order_id}|JSON {Item}|Вместо {int:order_id} нужно подставить целое число – идентификатор заказа.|
|Создать новый заказ|POST /labs/api/orders|JSON {newItem}|Нужно передать значения всех обязательных полей.|
|Изменить заказ|PUT /labs/api/orders/{int:order_id}|JSON {updateItem}|Вместо {int:order_id} нужно подставить целое число – идентификатор заказа.
Достаточно передать только значения изменившихся полей.|
|Удалить заказ|DELETE /labs/api/orders/{int:order_id}|JSON {Item}|Вместо {int:order_id} нужно подставить целое число – идентификатор заказа.|
### Материалы для изучения
[localStorage [Doka]](https://doka.guide/js/local-storage/)
[Получение данных с сервера [MDN]](https://developer.mozilla.org/ru/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data)
[Работа с JSON [MDN]](https://developer.mozilla.org/ru/docs/Learn/JavaScript/Objects/JSON)
[Промисы, async/await [Learn JS]](https://learn.javascript.ru/async)
[async/await [Doka]](https://doka.guide/js/async-await/)
[Использование промисов [MDN]](https://developer.mozilla.org/ru/docs/Web/JavaScript/Guide/Using_promises)
[Fetch [Learn JS]](https://learn.javascript.ru/fetch)
[Fetch API [MDN]](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
[fetch() [Doka]](https://doka.guide/js/fetch/)
[Cross-Origin Resource Sharing (CORS) [MDN]](https://developer.mozilla.org/ru/docs/Web/HTTP/CORS)