diff --git a/3 proba/js/lines-modal.js b/3 proba/js/lines-modal.js
index 2291b51..b337365 100644
--- a/3 proba/js/lines-modal.js
+++ b/3 proba/js/lines-modal.js
@@ -1,65 +1,453 @@
-// Модуль для работы с модальным окном
+/* eslint-disable linebreak-style */
+// модуль с логикой
-// импорт компонента Modal из bootstrap
-import { Modal } from "bootstrap";
-import { cntrls, imagePlaceholder } from "./lines-ui";
+// eslint-disable-next-line import/no-self-import
+import {
+ hideUpdateModal,
+ showUpdateModal,
+} from "./lines-modal";
+import {
+ createLine,
+ deleteLine,
+ getAllGengeTypes,
+ getAllAuthorTypes,
+ getAllLines,
+ getLine,
+ updateLine,
+} from "./lines-rest-api";
+import {
+ cntrls,
+ createTableRow,
+ imagePlaceholder,
+ createGenresOption,
+ createAuthorsOption,
+ createTableRowOnIndex,
+} from "./lines-ui";
-// поиск модального окна на странице
-const modal = document.getElementById("items-update");
-// если он найден, то создается экземпляр компонента Modal
-// для программного управления модальным окном
-const myModal = modal ? new Modal(modal, {}) : null;
-
-// поиск тега с заголовком модального кона для его смены
-const modalTitle = document.getElementById("items-update-title");
-
-// обнуление значений модального окна, т. к.
-// используется одно окно для всех операций
-function resetValues() {
- cntrls.lineId.value = "";
- cntrls.itemsType.value = "";
- cntrls.author.value = "";
- cntrls.name.value = "";
- cntrls.desc.value = "";
- cntrls.count.value = 0;
- cntrls.date.value = "";
- cntrls.image.value = "";
- cntrls.imagePreview.src = imagePlaceholder;
+async function drawGenreSelect() {
+ // вызов метода REST API для получения списка типов товаров
+ const data = await getAllGengeTypes();
+ const data2 = await getAllAuthorTypes();
+ // очистка содержимого select
+ // удаляется все, что находится между тегами
+ // но не атрибуты
+ cntrls.genresType.innerHTML = "";
+ cntrls.authorsType.innerHTML = "";
+ // пустое значение
+ cntrls.genresType.appendChild(createGenresOption("Выберите значение", "", true));
+ cntrls.authorsType.appendChild(createGenresOption("Выберите значение", "", true));
+ // цикл по результату ответа от сервера
+ // используется лямбда-выражение
+ // (item) => {} аналогично function(item) {}
+ data.forEach((genre) => {
+ cntrls.genresType.appendChild(createGenresOption(genre.name, genre.id));
+ });
+ data2.forEach((author) => {
+ cntrls.authorsType.appendChild(createAuthorsOption(author.name, author.id));
+ });
}
-// функция для показа модального окна
-// перед показом происходит заполнение формы для редактирования
-// если объект item не пуст
-export function showUpdateModal(item) {
- modalTitle.innerHTML = item === null ? "Добавить" : "Изменить";
- console.info(item);
+async function drawLinesTable() {
+ console.info("Try to load data");
+ if (!cntrls.table) {
+ return;
+ }
+ // вызов метода REST API для получения всех записей
+ const data = await getAllLines();
+ // очистка содержимого table
+ // удаляется все, что находится между тегами
+ // но не атрибуты
+ cntrls.table.innerHTML = "";
+ // цикл по результату ответа от сервера
+ // используется лямбда-выражение
+ // (item, index) => {} аналогично function(item, index) {}
+ data.forEach((item, index) => {
+ cntrls.table.appendChild(
+ createTableRow(
+ item,
+ index,
+ // функции передаются в качестве параметра
+ // это очень удобно, так как аргументы функций доступны только
+ // в данном месте кода и не передаются в сервисные модули
+ () => showUpdateModal(item),
+ () => removeLine(item.id),
+ ),
+ );
+ });
+}
- if (item) {
- cntrls.lineId.value = item.id;
- cntrls.itemsType.value = item.itemsId;
- cntrls.author.value = item.authorsId;
- cntrls.name.value = item.name;
- cntrls.desc.value = item.desc;
- cntrls.count.value = item.count;
- cntrls.date.value = item.date;
- // заполнение превью
- // Если пользователь выбрал изображение, то оно загружается
- // в тэг image с id image - preview
- // иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
- cntrls.imagePreview.src = item.image ? item.image : imagePlaceholder;
- } else {
- resetValues();
+async function drawLinesTableOnIndex() {
+ console.info("Try to load data On Index");
+ if (!cntrls.container) {
+ return;
+ }
+ // вызов метода REST API для получения всех записей
+ const data = await getAllLines();
+ // очистка содержимого table
+ // удаляется все, что находится между тегами
+ // но не атрибуты
+ cntrls.container.innerHTML = "";
+ // цикл по результату ответа от сервера
+ // используется лямбда-выражение
+ // (item, index) => {} аналогично function(item, index) {}
+ data.forEach((item, index) => {
+ cntrls.container.appendChild(
+ createTableRowOnIndex(
+ item,
+ index,
+ // функции передаются в качестве параметра
+ // это очень удобно, так как аргументы функций доступны только
+ // в данном месте кода и не передаются в сервисные модули
+ () => showUpdateModal(item),
+ () => location.assign(`page3.html?id=${item.id}`),
+ () => removeLine(item.id),
+ ),
+ );
+ });
+}
+
+async function addLine(nameBook, authorsType, genresType, year, description, count, date, image) {
+ console.info("Try to add item");
+ // вызов метода REST API для добавления записи
+ // eslint-disable-next-line max-len
+ const data = await createLine(nameBook, authorsType, genresType, year, description, count, date, image);
+ console.info("Added");
+ console.info(data);
+ // загрузка и заполнение table
+ drawLinesTable();
+}
+
+// eslint-disable-next-line max-len
+async function editLine(id, nameBook, authorsType, genresType, year, description, count, date, image) {
+ console.info("Try to update item");
+ console.log(id, nameBook, authorsType, genresType, description, count, date, image);
+ // вызов метода REST API для обновления записи
+ // eslint-disable-next-line max-len
+ const data = await updateLine(id, nameBook, authorsType, genresType, year, description, count, date, image);
+ console.info("Updated");
+ console.info(data);
+ // загрузка и заполнение table
+ drawLinesTable();
+}
+
+async function removeLine(id) {
+ if (!confirm("Вы точно хотите удалить этот товар?")) {
+ console.info("Canceled");
+ return;
+ }
+ console.info("Try to remove item");
+ // вызов метода REST API для удаления записи
+ const data = await deleteLine(id);
+ console.info(data);
+ // загрузка и заполнение table
+ drawLinesTable();
+}
+
+// функция для получения содержимого файла в виде base64 строки
+// https://ru.wikipedia.org/wiki/Base64
+async function readFile(file) {
+ const reader = new FileReader();
+
+ // создание Promise-объекта для использования функции
+ // с помощью await (асинхронно) без коллбэков (callback)
+ // https://learn.javascript.ru/promise
+ return new Promise((resolve, reject) => {
+ // 2. "Возвращаем" содержимое когда файл прочитан
+ // через вызов resolve
+ // Если не использовать Promise, то всю работу по взаимодействию
+ // с REST API пришлось бы делать в обработчике (callback) функции
+ // onloadend
+ reader.onloadend = () => {
+ const fileContent = reader.result;
+ // Здесь могла бы быть работа с REST API
+ // Чтение заканчивает выполняться здесь
+ resolve(fileContent);
+ };
+ // 3. Возвращаем ошибку
+ reader.onerror = () => {
+ // Или здесь в случае ошибки
+ reject(new Error("oops, something went wrong with the file reader."));
+ };
+ // Шаг 1. Сначала читаем файл
+ // Чтение начинает выполняться здесь
+ reader.readAsDataURL(file);
+ });
+}
+
+// функция для обновления блока с превью выбранного изображения
+async function updateImagePreview() {
+ // получение выбранного файла
+ // возможен выбор нескольких файлов, поэтому необходимо получить только первый
+ const file = cntrls.image.files[0];
+ // чтение содержимого файла в виде base64 строки
+ const fileContent = await readFile(file);
+ console.info("base64 ", fileContent);
+ // обновление атрибута src для тега img с id image-preview
+ cntrls.imagePreview.src = fileContent;
+}
+
+// Функция для обработки создания и редактирования элементов таблицы через модальное окно
+// Если хотите делать через страницу, то удалите эту функцию
+export function linesForm() {
+ console.info("linesForm");
+
+ // загрузка и заполнение select со списком товаров
+ drawGenreSelect();
+ // drawAuthorSelect();
+ // загрузка и заполнение table
+ drawLinesTable();
+
+ // Вызов функции обновления превью изображения при возникновении
+ // события oncahnge в тэге input с id image
+ cntrls.image.addEventListener("change", () => updateImagePreview());
+
+ // обработчик события нажатия на кнопку для показа модального окна
+ cntrls.button.addEventListener("click", () => showUpdateModal(null));
+
+ // обработчик события отправки формы
+ // возникает при нажатии на кнопку (button) с типом submit
+ // кнопка должна находится внутри тега form
+ cntrls.form.addEventListener("submit", async (event) => {
+ console.info("Form onSubmit");
+ // отключение стандартного поведения формы при отправке
+ // при отправке страница обновляется и JS перестает работать
+ event.preventDefault();
+ event.stopPropagation();
+ // если форма не прошла валидацию, то ничего делать не нужно
+ if (!cntrls.form.checkValidity()) {
+ return;
+ }
+
+ let imageBase64 = "";
+ // Получение выбранного пользователем изображения в виде base64 строки
+ // Если пользователь ничего не выбрал, то не нужно сохранять в БД
+ // дефолтное изображение
+ if (cntrls.imagePreview.src !== imagePlaceholder) {
+ // Загрузка содержимого атрибута src тэга img с id image-preview
+ // Здесь выполняется HTTP запрос с типом GET
+ const result = await fetch(cntrls.imagePreview.src);
+ // Получение из HTTP-ответа бинарного содержимого
+ const blob = await result.blob();
+ // Получение base64 строки для файла
+ // Здесь выполняется Promise из функции readFile
+ // Promise позволяет писать линейный код для работы с асинхронными методами
+ // без использования обработчиков (callback) с помощью await
+ imageBase64 = await readFile(blob);
+ }
+
+ // получение id строки для редактирования
+ // это значение содержится в скрытом input
+ const currentId = cntrls.lineId.value;
+ // если значение id не задано,
+ // то необходимо выполнить добавление записи
+ // иначе обновление записи
+ if (!currentId) {
+ await addLine(
+ cntrls.nameBook.value,
+ cntrls.authorsType.value,
+ cntrls.genresType.value,
+ cntrls.year.value,
+ cntrls.description.value,
+ cntrls.count.value,
+ cntrls.date.value,
+ imageBase64,
+ );
+ } else {
+ await editLine(
+ currentId,
+ cntrls.nameBook.value,
+ cntrls.authorsType.value,
+ cntrls.genresType.value,
+ cntrls.year.value,
+ cntrls.description.value,
+ cntrls.count.value,
+ cntrls.date.value,
+ imageBase64,
+ );
+ }
+
+ // после выполнения добавления/обновления модальное окно скрывается
+ hideUpdateModal();
+ });
+}
+
+export async function linesFormOnIndex() {
+ console.info("linesFormOnIndex");
+
+ // await drawGenreSelect();
+ await drawLinesTableOnIndex();
+
+ // обработчик события отправки формы
+ // возникает при нажатии на кнопку (button) с типом submit
+ // кнопка должна находится внутри тега form
+ cntrls.form.addEventListener("submit", async (event) => {
+ console.info("Form onSubmit");
+ // отключение стандартного поведения формы при отправке
+ // при отправке страница обновляется и JS перестает работать
+ event.preventDefault();
+ event.stopPropagation();
+ // если форма не прошла валидацию, то ничего делать не нужно
+ if (!cntrls.form.checkValidity()) {
+ return;
+ }
+
+ let imageBase64 = "";
+ // Получение выбранного пользователем изображения в виде base64 строки
+ // Если пользователь ничего не выбрал, то не нужно сохранять в БД
+ // дефолтное изображение
+ if (cntrls.imagePreview.src !== imagePlaceholder) {
+ // Загрузка содержимого атрибута src тэга img с id image-preview
+ // Здесь выполняется HTTP запрос с типом GET
+ const result = await fetch(cntrls.imagePreview.src);
+ // Получение из HTTP-ответа бинарного содержимого
+ const blob = await result.blob();
+ // Получение base64 строки для файла
+ // Здесь выполняется Promise из функции readFile
+ // Promise позволяет писать линейный код для работы с асинхронными методами
+ // без использования обработчиков (callback) с помощью await
+ imageBase64 = await readFile(blob);
+ }
+
+ // получение id строки для редактирования
+ // это значение содержится в скрытом input
+ const currentId = cntrls.lineId.value;
+ // если значение id не задано,
+ // то необходимо выполнить добавление записи
+ // иначе обновление записи
+ if (!currentId) {
+ await addLine(
+ cntrls.nameBook.value,
+ cntrls.authorsType.value,
+ cntrls.genresType.value,
+ cntrls.year.value,
+ cntrls.description.value,
+ cntrls.count.value,
+ cntrls.date.value,
+ imageBase64,
+ );
+ } else {
+ await editLine(
+ currentId,
+ cntrls.nameBook.value,
+ cntrls.authorsType.value,
+ cntrls.genresType.value,
+ cntrls.year.value,
+ cntrls.description.value,
+ cntrls.count.value,
+ cntrls.date.value,
+ imageBase64,
+ );
+ }
+
+ // после выполнения добавления/обновления модальное окно скрывается
+ hideUpdateModal();
+ });
+}
+// Функция для обработки создания и редактирования элементов таблицы через страницу page-admin.html
+// Если хотите делать через модальное окно, то удалите эту функцию
+export async function linesPageForm() {
+ console.info("linesPageForm");
+
+ // загрузка и заполнение select со списком товаров
+ // drawGenreSelect();
+
+ // func1 = (id) => {} аналогично function func1(id) {}
+ const goBack = () => location.assign("/page3.html");
+
+ // Вызов функции обновления превью изображения при возникновении
+ // события onchange в тэге input с id image
+ cntrls.image.addEventListener("change", () => updateImagePreview());
+
+ // получение параметров GET-запроса из URL
+ // параметры перечислены после символа ? (?id=1&color=black&...)
+ const urlParams = new URLSearchParams(location.search);
+
+ // получение значения конкретного параметра (id)
+ // указан только при редактировании
+ const currentId = urlParams.get("id");
+ // если id задан
+ if (currentId) {
+ try {
+ // вызов метода REST API для получения записи по первичному ключу(id)
+ const line = await getLine(currentId);
+ // заполнение формы для редактирования
+ cntrls.nameBook.value = line.nameBook;
+ cntrls.authorsType.value = line.authorsType;
+ cntrls.genresType.value = line.genresType;
+ cntrls.year.value = line.year;
+ cntrls.description.value = line.description;
+ cntrls.count.value = line.count;
+ cntrls.date.value = line.date;
+ // заполнение превью
+ // Если пользователь выбрал изображение, то оно загружается
+ // в тэг image с id image - preview
+ // иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
+ cntrls.imagePreview.src = line.image ? line.image : imagePlaceholder;
+ } catch {
+ // в случае ошибки происходит возврат к index
+ goBack();
+ }
}
- myModal.show();
-}
-
-// функция для скрытия модального окна
-export function hideUpdateModal() {
- resetValues();
-
- // удаление класса was-validated для скрытия результатов валидации
- cntrls.form.classList.remove("was-validated");
-
- myModal.hide();
+ // обработчик события отправки формы
+ // возникает при нажатии на кнопку (button) с типом submit
+ // кнопка должна находится внутри тега form
+ cntrls.form.addEventListener("submit", async (event) => {
+ console.info("Form onSubmit");
+ // отключение стандартного поведения формы при отправке
+ // при отправке страница обновляется и JS перестает работать
+ event.preventDefault();
+ event.stopPropagation();
+ // если форма не прошла валидацию, то ничего делать не нужно
+ if (!cntrls.form.checkValidity()) {
+ return;
+ }
+
+ let imageBase64 = "";
+ // Получение выбранного пользователем изображения в виде base64 строки
+ // Если пользователь ничего не выбрал, то не нужно сохранять в БД
+ // дефолтное изображение
+ if (cntrls.imagePreview.src !== imagePlaceholder) {
+ // Загрузка содержимого атрибута src тэга img с id image-preview
+ // Здесь выполняется HTTP запрос с типом GET
+ const result = await fetch(cntrls.imagePreview.src);
+ // Получение из HTTP-ответа бинарного содержимого
+ const blob = await result.blob();
+ // Получение base64 строки для файла
+ // Здесь выполняется Promise из функции readFile
+ // Promise позволяет писать линейный код для работы с асинхронными методами
+ // без использования обработчиков (callback) с помощью await
+ imageBase64 = await readFile(blob);
+ }
+
+ // если значение параметра запроса не задано,
+ // то необходимо выполнить добавление записи
+ // иначе обновление записи
+ if (!currentId) {
+ await addLine(
+ cntrls.nameBook.value,
+ cntrls.authorsType.value,
+ cntrls.genresType.value,
+ cntrls.year.value,
+ cntrls.description.value,
+ cntrls.count.value,
+ cntrls.date.value,
+ imageBase64,
+ );
+ } else {
+ await editLine(
+ currentId,
+ cntrls.nameBook.value,
+ cntrls.authorsType.value,
+ cntrls.genresType.value,
+ cntrls.year.value,
+ cntrls.description.value,
+ cntrls.count.value,
+ cntrls.date.value,
+ imageBase64,
+ );
+ }
+ // возврат к странице index
+ goBack();
+ });
}
diff --git a/3 proba/js/lines-rest-api.js b/3 proba/js/lines-rest-api.js
index daaac3b..3a382a6 100644
--- a/3 proba/js/lines-rest-api.js
+++ b/3 proba/js/lines-rest-api.js
@@ -1,3 +1,4 @@
+/* eslint-disable linebreak-style */
// модуль для работы с REST API сервера
// адрес сервера
diff --git a/3 proba/js/lines-ui.js b/3 proba/js/lines-ui.js
index d89e46b..8e63ed7 100644
--- a/3 proba/js/lines-ui.js
+++ b/3 proba/js/lines-ui.js
@@ -1,5 +1,7 @@
+/* eslint-disable linebreak-style */
// модуль для работы с элементами управления
+// eslint-disable-next-line linebreak-style
// объект для удобного получения элементов
// при обращении к атрибуту объекта вызывается
// нужная функция для поиска элемента
diff --git a/3 proba/js/lines.js b/3 proba/js/lines.js
index 622216f..ac7e063 100644
--- a/3 proba/js/lines.js
+++ b/3 proba/js/lines.js
@@ -1,5 +1,9 @@
+/* eslint-disable linebreak-style */
+/* eslint-disable import/named */
+/* eslint-disable linebreak-style */
// модуль с логикой
+// eslint-disable-next-line import/named
import { hideUpdateModal, showUpdateModal } from "./lines-modal";
import {
createLine,
diff --git a/3 proba/отчет 3.docx b/3 proba/отчет 3.docx
new file mode 100644
index 0000000..48b5c49
Binary files /dev/null and b/3 proba/отчет 3.docx differ