Laba3
This commit is contained in:
parent
0823ed978b
commit
83e35316e3
3 proba
data.json
js
node_modules
package-lock.jsonpage3.htmlpage4.html
File diff suppressed because one or more lines are too long
@ -1,455 +1,67 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
// модуль с логикой
|
||||
// Модуль для работы с модальным окном
|
||||
// eslint-disable-next-line linebreak-style
|
||||
|
||||
// eslint-disable-next-line import/no-self-import
|
||||
import {
|
||||
// eslint-disable-next-line import/named
|
||||
hideUpdateModal,
|
||||
// eslint-disable-next-line import/named
|
||||
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";
|
||||
// импорт компонента Modal из bootstrap
|
||||
import { Modal } from "bootstrap";
|
||||
import { cntrls, imagePlaceholder } from "./lines-ui";
|
||||
|
||||
async function drawGenreSelect() {
|
||||
// вызов метода REST API для получения списка типов товаров
|
||||
const data = await getAllGengeTypes();
|
||||
const data2 = await getAllAuthorTypes();
|
||||
// очистка содержимого select
|
||||
// удаляется все, что находится между тегами <select></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));
|
||||
});
|
||||
// поиск модального окна на странице
|
||||
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.nameBook.value = "";
|
||||
cntrls.authorsType.value = "";
|
||||
cntrls.genresType.value = "";
|
||||
cntrls.year.value = "";
|
||||
cntrls.description.value = "";
|
||||
cntrls.count.value = "";
|
||||
cntrls.image.value = "";
|
||||
cntrls.imagePreview.src = imagePlaceholder;
|
||||
}
|
||||
|
||||
async function drawLinesTable() {
|
||||
console.info("Try to load data");
|
||||
if (!cntrls.table) {
|
||||
return;
|
||||
}
|
||||
// вызов метода REST API для получения всех записей
|
||||
const data = await getAllLines();
|
||||
// очистка содержимого table
|
||||
// удаляется все, что находится между тегами <table></table>
|
||||
// но не атрибуты
|
||||
cntrls.table.innerHTML = "";
|
||||
// цикл по результату ответа от сервера
|
||||
// используется лямбда-выражение
|
||||
// (item, index) => {} аналогично function(item, index) {}
|
||||
data.forEach((item, index) => {
|
||||
cntrls.table.appendChild(
|
||||
createTableRow(
|
||||
item,
|
||||
index,
|
||||
// функции передаются в качестве параметра
|
||||
// это очень удобно, так как аргументы функций доступны только
|
||||
// в данном месте кода и не передаются в сервисные модули
|
||||
() => showUpdateModal(item),
|
||||
() => removeLine(item.id),
|
||||
),
|
||||
);
|
||||
});
|
||||
// функция для показа модального окна
|
||||
// перед показом происходит заполнение формы для редактирования
|
||||
// если объект item не пуст
|
||||
export function showUpdateModal(item) {
|
||||
modalTitle.innerHTML = item === null ? "Добавить" : "Изменить";
|
||||
console.info(item);
|
||||
|
||||
if (item) {
|
||||
cntrls.lineId.value = item.id;
|
||||
cntrls.nameBook.value = item.nameBook;
|
||||
cntrls.authorsType.value = item.authorsId;
|
||||
cntrls.genresType.value = item.genresId;
|
||||
cntrls.year.value = item.year;
|
||||
cntrls.description.value = item.description;
|
||||
cntrls.count.value = item.count;
|
||||
// заполнение превью
|
||||
// Если пользователь выбрал изображение, то оно загружается
|
||||
// в тэг image с id image - preview
|
||||
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
|
||||
cntrls.imagePreview.src = item.image ? item.image : imagePlaceholder;
|
||||
} else {
|
||||
resetValues();
|
||||
}
|
||||
|
||||
myModal.show();
|
||||
}
|
||||
|
||||
async function drawLinesTableOnIndex() {
|
||||
console.info("Try to load data On Index");
|
||||
if (!cntrls.container) {
|
||||
return;
|
||||
}
|
||||
// вызов метода REST API для получения всех записей
|
||||
const data = await getAllLines();
|
||||
// очистка содержимого table
|
||||
// удаляется все, что находится между тегами <table></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();
|
||||
}
|
||||
}
|
||||
|
||||
// обработчик события отправки формы
|
||||
// возникает при нажатии на кнопку (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();
|
||||
});
|
||||
// функция для скрытия модального окна
|
||||
export function hideUpdateModal() {
|
||||
resetValues();
|
||||
|
||||
// удаление класса was-validated для скрытия результатов валидации
|
||||
cntrls.form.classList.remove("was-validated");
|
||||
|
||||
myModal.hide();
|
||||
}
|
||||
|
@ -1,12 +1,20 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
// eslint-disable-next-line linebreak-style
|
||||
// eslint-disable-next-line linebreak-style
|
||||
// eslint-disable-next-line linebreak-style
|
||||
/* eslint-disable eol-last */
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable max-len */
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable comma-dangle */
|
||||
/* eslint-disable linebreak-style */
|
||||
// модуль для работы с REST API сервера
|
||||
|
||||
// адрес сервера
|
||||
const serverUrl = "http://localhost:8081";
|
||||
|
||||
// функция возвращает объект нужной структуры для отправки на сервер
|
||||
// eslint-disable-next-line max-len
|
||||
function createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _date, _image) {
|
||||
function createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _image) {
|
||||
return {
|
||||
nameBook: _name,
|
||||
authorsId: _authorsType,
|
||||
@ -14,8 +22,7 @@ function createLineObject(_name, _authorsType, _genresType, _year, _description,
|
||||
year: _year,
|
||||
description: _description,
|
||||
count: _count,
|
||||
date: _date,
|
||||
image: _image,
|
||||
image: _image
|
||||
};
|
||||
}
|
||||
|
||||
@ -56,10 +63,8 @@ export async function getLine(id) {
|
||||
|
||||
// обращение к серверу для создания записи (post)
|
||||
// объект отправляется в теле запроса (body)
|
||||
// eslint-disable-next-line max-len
|
||||
export async function createLine(_name, _authorsType, _genresType, _year, _description, _count, _date, _image) {
|
||||
// eslint-disable-next-line max-len
|
||||
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _date, _image);
|
||||
export async function createLine(_name, _authorsType, _genresType, _year, _description, _count, _image) {
|
||||
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _image);
|
||||
|
||||
const options = {
|
||||
method: "POST",
|
||||
@ -80,10 +85,8 @@ export async function createLine(_name, _authorsType, _genresType, _year, _descr
|
||||
// обращение к серверу для обновления записи по id (put)
|
||||
// объект отправляется в теле запроса (body)
|
||||
// id передается в качестве части пути URL get-запроса
|
||||
// eslint-disable-next-line max-len
|
||||
export async function updateLine(id, _name, _authorsType, _genresType, _year, _description, _count, _date, _image) {
|
||||
// eslint-disable-next-line max-len
|
||||
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _date, _image);
|
||||
export async function updateLine(id, _name, _authorsType, _genresType, _year, _description, _count, _image) {
|
||||
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _image);
|
||||
|
||||
const options = {
|
||||
method: "PUT",
|
||||
@ -114,4 +117,4 @@ export async function deleteLine(id) {
|
||||
}
|
||||
await response.json();
|
||||
location.reload();
|
||||
}
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable eol-last */
|
||||
/* eslint-disable linebreak-style */
|
||||
// модуль для работы с элементами управления
|
||||
|
||||
// eslint-disable-next-line linebreak-style
|
||||
// объект для удобного получения элементов
|
||||
// при обращении к атрибуту объекта вызывается
|
||||
// нужная функция для поиска элемента
|
||||
@ -17,7 +20,6 @@ export const cntrls = {
|
||||
year: document.getElementById("yearPicker"),
|
||||
description: document.getElementById("description"),
|
||||
count: document.getElementById("count"),
|
||||
date: document.getElementById("date"),
|
||||
image: document.getElementById("image"),
|
||||
imagePreview: document.getElementById("image-preview"),
|
||||
};
|
||||
@ -101,7 +103,6 @@ export function createTableRow(item, index, editCallback, deleteCallback) {
|
||||
row.appendChild(createTableColumn(item.year));
|
||||
row.appendChild(createTableColumn(item.description));
|
||||
row.appendChild(createTableColumn(item.count));
|
||||
row.appendChild(createTableColumn(item.date));
|
||||
|
||||
// редактировать в модальном окне
|
||||
row.appendChild(createTableAnchor("fa-pencil", editCallback));
|
||||
@ -110,13 +111,15 @@ export function createTableRow(item, index, editCallback, deleteCallback) {
|
||||
return row;
|
||||
}
|
||||
|
||||
export function createTableRowOnIndex(item) {
|
||||
export function createTableRowOnIndex(item, index, editCallback, editPageCallback, deleteCallback) {
|
||||
console.log(item);
|
||||
|
||||
const bookCard = createBookCard(item);
|
||||
return bookCard;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-multiple-empty-lines
|
||||
|
||||
function createBookCard(item) {
|
||||
const card = document.createElement("div");
|
||||
card.classList.add("book");
|
||||
@ -128,14 +131,29 @@ function createBookCard(item) {
|
||||
const title = document.createElement("h3");
|
||||
title.textContent = `${item.nameBook}, ${item.authors.name}`;
|
||||
|
||||
const detailsButton = document.createElement("a");
|
||||
detailsButton.href = "#link";
|
||||
detailsButton.classList.add("btn", "btn-custom");
|
||||
detailsButton.role = "button";
|
||||
detailsButton.textContent = "Подробнее";
|
||||
|
||||
const favoriteButton = document.createElement("a");
|
||||
favoriteButton.href = "#link";
|
||||
favoriteButton.classList.add("btn", "btn-custom");
|
||||
favoriteButton.role = "button";
|
||||
favoriteButton.textContent = "В избранное";
|
||||
|
||||
card.appendChild(image);
|
||||
card.appendChild(title);
|
||||
card.appendChild(detailsButton);
|
||||
card.appendChild(favoriteButton);
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("col-12");
|
||||
div.classList.add("col-md-4");
|
||||
div.classList.add("col-md-6");
|
||||
div.classList.add("col-lg-4");
|
||||
div.classList.add("col-xl-3");
|
||||
div.appendChild(card);
|
||||
|
||||
return div;
|
||||
}
|
||||
}
|
@ -1,66 +1,62 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable import/named */
|
||||
// eslint-disable-next-line linebreak-style
|
||||
/* eslint-disable spaced-comment */
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable comma-dangle */
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable no-trailing-spaces */
|
||||
/* eslint-disable linebreak-style */
|
||||
// модуль с логикой
|
||||
|
||||
// eslint-disable-next-line import/named
|
||||
import { hideUpdateModal, showUpdateModal } from "./lines-modal";
|
||||
import {
|
||||
hideUpdateModal,
|
||||
showUpdateModal
|
||||
} from "./lines-modal";
|
||||
import {
|
||||
createLine,
|
||||
deleteLine,
|
||||
getAllItemTypes,
|
||||
getAllItemAuthors,
|
||||
getAllGengeTypes,
|
||||
getAllAuthorTypes,
|
||||
getAllLines,
|
||||
getLine,
|
||||
updateLine,
|
||||
} from "./lines-rest-api";
|
||||
import {
|
||||
cntrls,
|
||||
createItemsOption,
|
||||
createTableRow,
|
||||
imagePlaceholder,
|
||||
cntrls,
|
||||
createTableRow,
|
||||
imagePlaceholder,
|
||||
createGenresOption,
|
||||
createAuthorsOption,
|
||||
createTableRowOnIndex
|
||||
} from "./lines-ui";
|
||||
|
||||
async function drawItemsSelect() {
|
||||
async function drawGenreSelect() {
|
||||
// вызов метода REST API для получения списка типов товаров
|
||||
const data = await getAllItemTypes();
|
||||
const data = await getAllGengeTypes();
|
||||
const data2 = await getAllAuthorTypes();
|
||||
// очистка содержимого select
|
||||
// удаляется все, что находится между тегами <select></select>
|
||||
// но не атрибуты
|
||||
cntrls.itemsType.innerHTML = "";
|
||||
cntrls.genresType.innerHTML = "";
|
||||
cntrls.authorsType.innerHTML = "";
|
||||
// пустое значение
|
||||
cntrls.itemsType.appendChild(
|
||||
createItemsOption("Выберите значение", "", true),
|
||||
);
|
||||
cntrls.genresType.appendChild(createGenresOption("Выберите значение", "", true));
|
||||
cntrls.authorsType.appendChild(createGenresOption("Выберите значение", "", true));
|
||||
// цикл по результату ответа от сервера
|
||||
// используется лямбда-выражение
|
||||
// (item) => {} аналогично function(item) {}
|
||||
data.forEach((item) => {
|
||||
cntrls.itemsType.appendChild(createItemsOption(item.name, item.id));
|
||||
data.forEach((genre) => {
|
||||
cntrls.genresType.appendChild(createGenresOption(genre.name, genre.id));
|
||||
});
|
||||
}
|
||||
|
||||
async function drawAuthorsSelect() {
|
||||
// вызов метода REST API для получения списка типов товаров
|
||||
const data = await getAllItemAuthors();
|
||||
// очистка содержимого select
|
||||
// удаляется все, что находится между тегами <select></select>
|
||||
// но не атрибуты
|
||||
cntrls.author.innerHTML = "";
|
||||
// пустое значение
|
||||
cntrls.author.appendChild(createItemsOption("Выберите значение", "", true));
|
||||
// цикл по результату ответа от сервера
|
||||
// используется лямбда-выражение
|
||||
// (item) => {} аналогично function(item) {}
|
||||
data.forEach((item) => {
|
||||
cntrls.author.appendChild(createItemsOption(item.name, item.id));
|
||||
data2.forEach((author) => {
|
||||
cntrls.authorsType.appendChild(createAuthorsOption(author.name, author.id));
|
||||
});
|
||||
}
|
||||
|
||||
async function drawLinesTable() {
|
||||
console.info("Try to load data");
|
||||
if (!cntrls.table) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
// вызов метода REST API для получения всех записей
|
||||
const data = await getAllLines();
|
||||
@ -72,44 +68,66 @@ async function drawLinesTable() {
|
||||
// используется лямбда-выражение
|
||||
// (item, index) => {} аналогично function(item, index) {}
|
||||
data.forEach((item, index) => {
|
||||
cntrls.table.appendChild(
|
||||
createTableRow(
|
||||
item,
|
||||
index,
|
||||
// функции передаются в качестве параметра
|
||||
// это очень удобно, так как аргументы функций доступны только
|
||||
// в данном месте кода и не передаются в сервисные модули
|
||||
() => showUpdateModal(item),
|
||||
() => location.assign(`page-edit.html?id=${item.id}`),
|
||||
() => removeLine(item.id),
|
||||
),
|
||||
);
|
||||
cntrls.table.appendChild(
|
||||
createTableRow(
|
||||
item,
|
||||
index,
|
||||
// функции передаются в качестве параметра
|
||||
// это очень удобно, так как аргументы функций доступны только
|
||||
// в данном месте кода и не передаются в сервисные модули
|
||||
() => showUpdateModal(item),
|
||||
() => removeLine(item.id),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function addLine(item, author, name, desc, count, date, image) {
|
||||
async function drawLinesTableOnIndex() {
|
||||
console.info("Try to load data On Index");
|
||||
if (!cntrls.container) {
|
||||
return;
|
||||
}
|
||||
// вызов метода REST API для получения всех записей
|
||||
const data = await getAllLines();
|
||||
// очистка содержимого table
|
||||
// удаляется все, что находится между тегами <table></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, image) {
|
||||
console.info("Try to add item");
|
||||
// вызов метода REST API для добавления записи
|
||||
const data = await createLine(item, author, name, desc, count, date, image);
|
||||
const data = await createLine(nameBook, authorsType, genresType, year, description, count, image);
|
||||
console.info("Added");
|
||||
console.info(data);
|
||||
// загрузка и заполнение table
|
||||
drawLinesTable();
|
||||
}
|
||||
|
||||
async function editLine(id, item, author, name, desc, count, date, image) {
|
||||
async function editLine(id, nameBook, authorsType, genresType, year, description, count, image) {
|
||||
console.info("Try to update item");
|
||||
console.log(id, nameBook, authorsType, genresType, description, count, image);
|
||||
// вызов метода REST API для обновления записи
|
||||
const data = await updateLine(
|
||||
id,
|
||||
item,
|
||||
author,
|
||||
name,
|
||||
desc,
|
||||
count,
|
||||
date,
|
||||
image,
|
||||
);
|
||||
// eslint-disable-next-line max-len
|
||||
const data = await updateLine(id, nameBook, authorsType, genresType, year, description, count, image);
|
||||
console.info("Updated");
|
||||
console.info(data);
|
||||
// загрузка и заполнение table
|
||||
@ -117,9 +135,9 @@ async function editLine(id, item, author, name, desc, count, date, image) {
|
||||
}
|
||||
|
||||
async function removeLine(id) {
|
||||
if (!confirm("Do you really want to remove this item?")) {
|
||||
console.info("Canceled");
|
||||
return;
|
||||
if (!confirm("Вы точно хотите удалить этот товар?")) {
|
||||
console.info("Canceled");
|
||||
return;
|
||||
}
|
||||
console.info("Try to remove item");
|
||||
// вызов метода REST API для удаления записи
|
||||
@ -138,25 +156,25 @@ async function readFile(file) {
|
||||
// с помощью 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);
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
@ -178,8 +196,8 @@ export function linesForm() {
|
||||
console.info("linesForm");
|
||||
|
||||
// загрузка и заполнение select со списком товаров
|
||||
drawItemsSelect();
|
||||
drawAuthorsSelect();
|
||||
drawGenreSelect();
|
||||
//drawAuthorSelect();
|
||||
// загрузка и заполнение table
|
||||
drawLinesTable();
|
||||
|
||||
@ -194,78 +212,147 @@ export function linesForm() {
|
||||
// возникает при нажатии на кнопку (button) с типом submit
|
||||
// кнопка должна находится внутри тега form
|
||||
cntrls.form.addEventListener("submit", async (event) => {
|
||||
console.info("Form onSubmit");
|
||||
// отключение стандартного поведения формы при отправке
|
||||
// при отправке страница обновляется и JS перестает работать
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// если форма не прошла валидацию, то ничего делать не нужно
|
||||
if (!cntrls.form.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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.itemsType.value,
|
||||
cntrls.author.value,
|
||||
cntrls.name.value,
|
||||
cntrls.desc.value,
|
||||
cntrls.count.value,
|
||||
cntrls.date.value,
|
||||
imageBase64,
|
||||
);
|
||||
} else {
|
||||
await editLine(
|
||||
currentId,
|
||||
cntrls.itemsType.value,
|
||||
cntrls.author.value,
|
||||
cntrls.name.value,
|
||||
cntrls.desc.value,
|
||||
cntrls.count.value,
|
||||
cntrls.date.value,
|
||||
imageBase64,
|
||||
);
|
||||
}
|
||||
// получение 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,
|
||||
imageBase64,
|
||||
);
|
||||
} else {
|
||||
await editLine(
|
||||
currentId,
|
||||
cntrls.nameBook.value,
|
||||
cntrls.authorsType.value,
|
||||
cntrls.genresType.value,
|
||||
cntrls.year.value,
|
||||
cntrls.description.value,
|
||||
cntrls.count.value,
|
||||
imageBase64,
|
||||
);
|
||||
}
|
||||
|
||||
// после выполнения добавления/обновления модальное окно скрывается
|
||||
hideUpdateModal();
|
||||
// после выполнения добавления/обновления модальное окно скрывается
|
||||
hideUpdateModal();
|
||||
});
|
||||
}
|
||||
|
||||
// Функция для обработки создания и редактирования элементов таблицы через страницу page-edit.html
|
||||
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,
|
||||
imageBase64,
|
||||
);
|
||||
} else {
|
||||
await editLine(
|
||||
currentId,
|
||||
cntrls.nameBook.value,
|
||||
cntrls.authorsType.value,
|
||||
cntrls.genresType.value,
|
||||
cntrls.year.value,
|
||||
cntrls.description.value,
|
||||
cntrls.count.value,
|
||||
imageBase64,
|
||||
);
|
||||
}
|
||||
|
||||
// после выполнения добавления/обновления модальное окно скрывается
|
||||
hideUpdateModal();
|
||||
});
|
||||
}
|
||||
// Функция для обработки создания и редактирования элементов таблицы через страницу page-admin.html
|
||||
// Если хотите делать через модальное окно, то удалите эту функцию
|
||||
export async function linesPageForm() {
|
||||
console.info("linesPageForm");
|
||||
|
||||
// загрузка и заполнение select со списком товаров
|
||||
drawItemsSelect();
|
||||
drawAuthorsSelect();
|
||||
// drawGenreSelect();
|
||||
|
||||
// func1 = (id) => {} аналогично function func1(id) {}
|
||||
const goBack = () => location.assign("/page4.html");
|
||||
const goBack = () => location.assign("/Catalog.html");
|
||||
|
||||
// Вызов функции обновления превью изображения при возникновении
|
||||
// события onchange в тэге input с id image
|
||||
@ -280,84 +367,84 @@ export async function linesPageForm() {
|
||||
const currentId = urlParams.get("id");
|
||||
// если id задан
|
||||
if (currentId) {
|
||||
try {
|
||||
// вызов метода REST API для получения записи по первичному ключу(id)
|
||||
const line = await getLine(currentId);
|
||||
// заполнение формы для редактирования
|
||||
cntrls.itemsType.value = line.itemsId;
|
||||
cntrls.author.value = line.authorsId;
|
||||
cntrls.name.value = line.name;
|
||||
cntrls.desc.value = line.desc;
|
||||
cntrls.count.value = line.count;
|
||||
cntrls.date.value = line.date;
|
||||
// заполнение превью
|
||||
// Если пользователь выбрал изображение, то оно загружается
|
||||
// в тэг image с id image - preview
|
||||
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
|
||||
cntrls.imagePreview.src = line.image ? line.image : imagePlaceholder;
|
||||
} catch {
|
||||
// в случае ошибки происходит возврат к page4
|
||||
goBack();
|
||||
}
|
||||
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;
|
||||
// заполнение превью
|
||||
// Если пользователь выбрал изображение, то оно загружается
|
||||
// в тэг image с id image - preview
|
||||
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
|
||||
cntrls.imagePreview.src = line.image ? line.image : imagePlaceholder;
|
||||
} catch {
|
||||
// в случае ошибки происходит возврат к index
|
||||
goBack();
|
||||
}
|
||||
}
|
||||
|
||||
// обработчик события отправки формы
|
||||
// возникает при нажатии на кнопку (button) с типом submit
|
||||
// кнопка должна находится внутри тега form
|
||||
cntrls.form.addEventListener("submit", async (event) => {
|
||||
console.info("Form onSubmit");
|
||||
// отключение стандартного поведения формы при отправке
|
||||
// при отправке страница обновляется и JS перестает работать
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// если форма не прошла валидацию, то ничего делать не нужно
|
||||
if (!cntrls.form.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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.itemsType.value,
|
||||
cntrls.author.value,
|
||||
cntrls.name.value,
|
||||
cntrls.desc.value,
|
||||
cntrls.count.value,
|
||||
cntrls.date.value,
|
||||
imageBase64,
|
||||
);
|
||||
} else {
|
||||
await editLine(
|
||||
currentId,
|
||||
cntrls.itemsType.value,
|
||||
cntrls.author.value,
|
||||
cntrls.name.value,
|
||||
cntrls.desc.value,
|
||||
cntrls.count.value,
|
||||
cntrls.date.value,
|
||||
imageBase64,
|
||||
);
|
||||
}
|
||||
// возврат к странице page4
|
||||
goBack();
|
||||
// если значение параметра запроса не задано,
|
||||
// то необходимо выполнить добавление записи
|
||||
// иначе обновление записи
|
||||
if (!currentId) {
|
||||
await addLine(
|
||||
cntrls.nameBook.value,
|
||||
cntrls.authorsType.value,
|
||||
cntrls.genresType.value,
|
||||
cntrls.year.value,
|
||||
cntrls.description.value,
|
||||
cntrls.count.value,
|
||||
imageBase64,
|
||||
);
|
||||
} else {
|
||||
await editLine(
|
||||
currentId,
|
||||
cntrls.nameBook.value,
|
||||
cntrls.authorsType.value,
|
||||
cntrls.genresType.value,
|
||||
cntrls.year.value,
|
||||
cntrls.description.value,
|
||||
cntrls.count.value,
|
||||
imageBase64,
|
||||
);
|
||||
}
|
||||
// возврат к странице index
|
||||
goBack();
|
||||
});
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
// модуль используется для валидации форма на странице
|
||||
|
||||
function validation() {
|
||||
|
20
3 proba/node_modules/.package-lock.json
generated
vendored
20
3 proba/node_modules/.package-lock.json
generated
vendored
@ -1678,9 +1678,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -2756,9 +2756,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -3229,9 +3229,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.30",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
|
||||
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
|
||||
"version": "8.4.33",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
||||
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -3248,7 +3248,7 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.6",
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
|
6
3 proba/node_modules/.vite/deps/_metadata.json
generated
vendored
6
3 proba/node_modules/.vite/deps/_metadata.json
generated
vendored
@ -1,11 +1,11 @@
|
||||
{
|
||||
"hash": "2b23bead",
|
||||
"browserHash": "d63b02ce",
|
||||
"hash": "fa1fc037",
|
||||
"browserHash": "c20cb0a6",
|
||||
"optimized": {
|
||||
"bootstrap": {
|
||||
"src": "../../bootstrap/dist/js/bootstrap.esm.js",
|
||||
"file": "bootstrap.js",
|
||||
"fileHash": "c09afe4f",
|
||||
"fileHash": "fba3a4d5",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
|
2
3 proba/node_modules/.vite/deps/bootstrap.js.map
generated
vendored
2
3 proba/node_modules/.vite/deps/bootstrap.js.map
generated
vendored
File diff suppressed because one or more lines are too long
208
3 proba/node_modules/follow-redirects/index.js
generated
vendored
208
3 proba/node_modules/follow-redirects/index.js
generated
vendored
@ -6,6 +6,29 @@ var Writable = require("stream").Writable;
|
||||
var assert = require("assert");
|
||||
var debug = require("./debug");
|
||||
|
||||
// Whether to use the native URL object or the legacy url module
|
||||
var useNativeURL = false;
|
||||
try {
|
||||
assert(new URL());
|
||||
}
|
||||
catch (error) {
|
||||
useNativeURL = error.code === "ERR_INVALID_URL";
|
||||
}
|
||||
|
||||
// URL fields to preserve in copy operations
|
||||
var preservedUrlFields = [
|
||||
"auth",
|
||||
"host",
|
||||
"hostname",
|
||||
"href",
|
||||
"path",
|
||||
"pathname",
|
||||
"port",
|
||||
"protocol",
|
||||
"query",
|
||||
"search",
|
||||
];
|
||||
|
||||
// Create handlers that pass events from native requests
|
||||
var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
|
||||
var eventHandlers = Object.create(null);
|
||||
@ -15,19 +38,20 @@ events.forEach(function (event) {
|
||||
};
|
||||
});
|
||||
|
||||
// Error types with codes
|
||||
var InvalidUrlError = createErrorType(
|
||||
"ERR_INVALID_URL",
|
||||
"Invalid URL",
|
||||
TypeError
|
||||
);
|
||||
// Error types with codes
|
||||
var RedirectionError = createErrorType(
|
||||
"ERR_FR_REDIRECTION_FAILURE",
|
||||
"Redirected request failed"
|
||||
);
|
||||
var TooManyRedirectsError = createErrorType(
|
||||
"ERR_FR_TOO_MANY_REDIRECTS",
|
||||
"Maximum number of redirects exceeded"
|
||||
"Maximum number of redirects exceeded",
|
||||
RedirectionError
|
||||
);
|
||||
var MaxBodyLengthExceededError = createErrorType(
|
||||
"ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
|
||||
@ -38,6 +62,9 @@ var WriteAfterEndError = createErrorType(
|
||||
"write after end"
|
||||
);
|
||||
|
||||
// istanbul ignore next
|
||||
var destroy = Writable.prototype.destroy || noop;
|
||||
|
||||
// An HTTP(S) request that can be redirected
|
||||
function RedirectableRequest(options, responseCallback) {
|
||||
// Initialize the request
|
||||
@ -59,7 +86,13 @@ function RedirectableRequest(options, responseCallback) {
|
||||
// React to responses of native requests
|
||||
var self = this;
|
||||
this._onNativeResponse = function (response) {
|
||||
self._processResponse(response);
|
||||
try {
|
||||
self._processResponse(response);
|
||||
}
|
||||
catch (cause) {
|
||||
self.emit("error", cause instanceof RedirectionError ?
|
||||
cause : new RedirectionError({ cause: cause }));
|
||||
}
|
||||
};
|
||||
|
||||
// Perform the first request
|
||||
@ -68,10 +101,17 @@ function RedirectableRequest(options, responseCallback) {
|
||||
RedirectableRequest.prototype = Object.create(Writable.prototype);
|
||||
|
||||
RedirectableRequest.prototype.abort = function () {
|
||||
abortRequest(this._currentRequest);
|
||||
destroyRequest(this._currentRequest);
|
||||
this._currentRequest.abort();
|
||||
this.emit("abort");
|
||||
};
|
||||
|
||||
RedirectableRequest.prototype.destroy = function (error) {
|
||||
destroyRequest(this._currentRequest, error);
|
||||
destroy.call(this, error);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Writes buffered data to the current native request
|
||||
RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
||||
// Writing is not allowed if end has been called
|
||||
@ -184,6 +224,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
||||
self.removeListener("abort", clearTimer);
|
||||
self.removeListener("error", clearTimer);
|
||||
self.removeListener("response", clearTimer);
|
||||
self.removeListener("close", clearTimer);
|
||||
if (callback) {
|
||||
self.removeListener("timeout", callback);
|
||||
}
|
||||
@ -210,6 +251,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
||||
this.on("abort", clearTimer);
|
||||
this.on("error", clearTimer);
|
||||
this.on("response", clearTimer);
|
||||
this.on("close", clearTimer);
|
||||
|
||||
return this;
|
||||
};
|
||||
@ -268,8 +310,7 @@ RedirectableRequest.prototype._performRequest = function () {
|
||||
var protocol = this._options.protocol;
|
||||
var nativeProtocol = this._options.nativeProtocols[protocol];
|
||||
if (!nativeProtocol) {
|
||||
this.emit("error", new TypeError("Unsupported protocol " + protocol));
|
||||
return;
|
||||
throw new TypeError("Unsupported protocol " + protocol);
|
||||
}
|
||||
|
||||
// If specified, use the agent corresponding to the protocol
|
||||
@ -361,15 +402,14 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
||||
}
|
||||
|
||||
// The response is a redirect, so abort the current request
|
||||
abortRequest(this._currentRequest);
|
||||
destroyRequest(this._currentRequest);
|
||||
// Discard the remainder of the response to avoid waiting for data
|
||||
response.destroy();
|
||||
|
||||
// RFC7231§6.4: A client SHOULD detect and intervene
|
||||
// in cyclical redirections (i.e., "infinite" redirection loops).
|
||||
if (++this._redirectCount > this._options.maxRedirects) {
|
||||
this.emit("error", new TooManyRedirectsError());
|
||||
return;
|
||||
throw new TooManyRedirectsError();
|
||||
}
|
||||
|
||||
// Store the request headers if applicable
|
||||
@ -403,33 +443,23 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
||||
var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
|
||||
|
||||
// If the redirect is relative, carry over the host of the last request
|
||||
var currentUrlParts = url.parse(this._currentUrl);
|
||||
var currentUrlParts = parseUrl(this._currentUrl);
|
||||
var currentHost = currentHostHeader || currentUrlParts.host;
|
||||
var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
|
||||
url.format(Object.assign(currentUrlParts, { host: currentHost }));
|
||||
|
||||
// Determine the URL of the redirection
|
||||
var redirectUrl;
|
||||
try {
|
||||
redirectUrl = url.resolve(currentUrl, location);
|
||||
}
|
||||
catch (cause) {
|
||||
this.emit("error", new RedirectionError({ cause: cause }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the redirected request
|
||||
debug("redirecting to", redirectUrl);
|
||||
var redirectUrl = resolveUrl(location, currentUrl);
|
||||
debug("redirecting to", redirectUrl.href);
|
||||
this._isRedirect = true;
|
||||
var redirectUrlParts = url.parse(redirectUrl);
|
||||
Object.assign(this._options, redirectUrlParts);
|
||||
spreadUrlObject(redirectUrl, this._options);
|
||||
|
||||
// Drop confidential headers when redirecting to a less secure protocol
|
||||
// or to a different domain that is not a superdomain
|
||||
if (redirectUrlParts.protocol !== currentUrlParts.protocol &&
|
||||
redirectUrlParts.protocol !== "https:" ||
|
||||
redirectUrlParts.host !== currentHost &&
|
||||
!isSubdomain(redirectUrlParts.host, currentHost)) {
|
||||
if (redirectUrl.protocol !== currentUrlParts.protocol &&
|
||||
redirectUrl.protocol !== "https:" ||
|
||||
redirectUrl.host !== currentHost &&
|
||||
!isSubdomain(redirectUrl.host, currentHost)) {
|
||||
removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
|
||||
}
|
||||
|
||||
@ -444,23 +474,12 @@ RedirectableRequest.prototype._processResponse = function (response) {
|
||||
method: method,
|
||||
headers: requestHeaders,
|
||||
};
|
||||
try {
|
||||
beforeRedirect(this._options, responseDetails, requestDetails);
|
||||
}
|
||||
catch (err) {
|
||||
this.emit("error", err);
|
||||
return;
|
||||
}
|
||||
beforeRedirect(this._options, responseDetails, requestDetails);
|
||||
this._sanitizeOptions(this._options);
|
||||
}
|
||||
|
||||
// Perform the redirected request
|
||||
try {
|
||||
this._performRequest();
|
||||
}
|
||||
catch (cause) {
|
||||
this.emit("error", new RedirectionError({ cause: cause }));
|
||||
}
|
||||
this._performRequest();
|
||||
};
|
||||
|
||||
// Wraps the key/value object of protocols with redirect functionality
|
||||
@ -480,27 +499,16 @@ function wrap(protocols) {
|
||||
|
||||
// Executes a request, following redirects
|
||||
function request(input, options, callback) {
|
||||
// Parse parameters
|
||||
if (isString(input)) {
|
||||
var parsed;
|
||||
try {
|
||||
parsed = urlToOptions(new URL(input));
|
||||
}
|
||||
catch (err) {
|
||||
/* istanbul ignore next */
|
||||
parsed = url.parse(input);
|
||||
}
|
||||
if (!isString(parsed.protocol)) {
|
||||
throw new InvalidUrlError({ input });
|
||||
}
|
||||
input = parsed;
|
||||
// Parse parameters, ensuring that input is an object
|
||||
if (isURL(input)) {
|
||||
input = spreadUrlObject(input);
|
||||
}
|
||||
else if (URL && (input instanceof URL)) {
|
||||
input = urlToOptions(input);
|
||||
else if (isString(input)) {
|
||||
input = spreadUrlObject(parseUrl(input));
|
||||
}
|
||||
else {
|
||||
callback = options;
|
||||
options = input;
|
||||
options = validateUrl(input);
|
||||
input = { protocol: protocol };
|
||||
}
|
||||
if (isFunction(options)) {
|
||||
@ -539,27 +547,57 @@ function wrap(protocols) {
|
||||
return exports;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function noop() { /* empty */ }
|
||||
|
||||
// from https://github.com/nodejs/node/blob/master/lib/internal/url.js
|
||||
function urlToOptions(urlObject) {
|
||||
var options = {
|
||||
protocol: urlObject.protocol,
|
||||
hostname: urlObject.hostname.startsWith("[") ?
|
||||
/* istanbul ignore next */
|
||||
urlObject.hostname.slice(1, -1) :
|
||||
urlObject.hostname,
|
||||
hash: urlObject.hash,
|
||||
search: urlObject.search,
|
||||
pathname: urlObject.pathname,
|
||||
path: urlObject.pathname + urlObject.search,
|
||||
href: urlObject.href,
|
||||
};
|
||||
if (urlObject.port !== "") {
|
||||
options.port = Number(urlObject.port);
|
||||
function parseUrl(input) {
|
||||
var parsed;
|
||||
/* istanbul ignore else */
|
||||
if (useNativeURL) {
|
||||
parsed = new URL(input);
|
||||
}
|
||||
return options;
|
||||
else {
|
||||
// Ensure the URL is valid and absolute
|
||||
parsed = validateUrl(url.parse(input));
|
||||
if (!isString(parsed.protocol)) {
|
||||
throw new InvalidUrlError({ input });
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function resolveUrl(relative, base) {
|
||||
/* istanbul ignore next */
|
||||
return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
|
||||
}
|
||||
|
||||
function validateUrl(input) {
|
||||
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
|
||||
throw new InvalidUrlError({ input: input.href || input });
|
||||
}
|
||||
if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
|
||||
throw new InvalidUrlError({ input: input.href || input });
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
function spreadUrlObject(urlObject, target) {
|
||||
var spread = target || {};
|
||||
for (var key of preservedUrlFields) {
|
||||
spread[key] = urlObject[key];
|
||||
}
|
||||
|
||||
// Fix IPv6 hostname
|
||||
if (spread.hostname.startsWith("[")) {
|
||||
spread.hostname = spread.hostname.slice(1, -1);
|
||||
}
|
||||
// Ensure port is a number
|
||||
if (spread.port !== "") {
|
||||
spread.port = Number(spread.port);
|
||||
}
|
||||
// Concatenate path
|
||||
spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
|
||||
|
||||
return spread;
|
||||
}
|
||||
|
||||
function removeMatchingHeaders(regex, headers) {
|
||||
@ -585,17 +623,25 @@ function createErrorType(code, message, baseClass) {
|
||||
|
||||
// Attach constructor and set default properties
|
||||
CustomError.prototype = new (baseClass || Error)();
|
||||
CustomError.prototype.constructor = CustomError;
|
||||
CustomError.prototype.name = "Error [" + code + "]";
|
||||
Object.defineProperties(CustomError.prototype, {
|
||||
constructor: {
|
||||
value: CustomError,
|
||||
enumerable: false,
|
||||
},
|
||||
name: {
|
||||
value: "Error [" + code + "]",
|
||||
enumerable: false,
|
||||
},
|
||||
});
|
||||
return CustomError;
|
||||
}
|
||||
|
||||
function abortRequest(request) {
|
||||
function destroyRequest(request, error) {
|
||||
for (var event of events) {
|
||||
request.removeListener(event, eventHandlers[event]);
|
||||
}
|
||||
request.on("error", noop);
|
||||
request.abort();
|
||||
request.destroy(error);
|
||||
}
|
||||
|
||||
function isSubdomain(subdomain, domain) {
|
||||
@ -616,6 +662,10 @@ function isBuffer(value) {
|
||||
return typeof value === "object" && ("length" in value);
|
||||
}
|
||||
|
||||
function isURL(value) {
|
||||
return URL && value instanceof URL;
|
||||
}
|
||||
|
||||
// Exports
|
||||
module.exports = wrap({ http: http, https: https });
|
||||
module.exports.wrap = wrap;
|
||||
|
5
3 proba/node_modules/follow-redirects/package.json
generated
vendored
5
3 proba/node_modules/follow-redirects/package.json
generated
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "follow-redirects",
|
||||
"version": "1.15.2",
|
||||
"version": "1.15.4",
|
||||
"description": "HTTP and HTTPS modules that follow redirects.",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
@ -11,9 +11,8 @@
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run lint && npm run mocha",
|
||||
"lint": "eslint *.js test",
|
||||
"mocha": "nyc mocha"
|
||||
"test": "nyc mocha"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
2
3 proba/node_modules/nanoid/README.md
generated
vendored
2
3 proba/node_modules/nanoid/README.md
generated
vendored
@ -36,4 +36,4 @@ Supports modern browsers, IE [with Babel], Node.js and React Native.
|
||||
</a>
|
||||
|
||||
## Docs
|
||||
Read **[full docs](https://github.com/ai/nanoid#readme)** on GitHub.
|
||||
Read full docs **[here](https://github.com/ai/nanoid#readme)**.
|
||||
|
91
3 proba/node_modules/nanoid/index.d.cts
generated
vendored
Normal file
91
3 proba/node_modules/nanoid/index.d.cts
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Generate secure URL-friendly unique ID.
|
||||
*
|
||||
* By default, the ID will have 21 symbols to have a collision probability
|
||||
* similar to UUID v4.
|
||||
*
|
||||
* ```js
|
||||
* import { nanoid } from 'nanoid'
|
||||
* model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"
|
||||
* ```
|
||||
*
|
||||
* @param size Size of the ID. The default size is 21.
|
||||
* @returns A random string.
|
||||
*/
|
||||
export function nanoid(size?: number): string
|
||||
|
||||
/**
|
||||
* Generate secure unique ID with custom alphabet.
|
||||
*
|
||||
* Alphabet must contain 256 symbols or less. Otherwise, the generator
|
||||
* will not be secure.
|
||||
*
|
||||
* @param alphabet Alphabet used to generate the ID.
|
||||
* @param defaultSize Size of the ID. The default size is 21.
|
||||
* @returns A random string generator.
|
||||
*
|
||||
* ```js
|
||||
* const { customAlphabet } = require('nanoid')
|
||||
* const nanoid = customAlphabet('0123456789абвгдеё', 5)
|
||||
* nanoid() //=> "8ё56а"
|
||||
* ```
|
||||
*/
|
||||
export function customAlphabet(
|
||||
alphabet: string,
|
||||
defaultSize?: number
|
||||
): (size?: number) => string
|
||||
|
||||
/**
|
||||
* Generate unique ID with custom random generator and alphabet.
|
||||
*
|
||||
* Alphabet must contain 256 symbols or less. Otherwise, the generator
|
||||
* will not be secure.
|
||||
*
|
||||
* ```js
|
||||
* import { customRandom } from 'nanoid/format'
|
||||
*
|
||||
* const nanoid = customRandom('abcdef', 5, size => {
|
||||
* const random = []
|
||||
* for (let i = 0; i < size; i++) {
|
||||
* random.push(randomByte())
|
||||
* }
|
||||
* return random
|
||||
* })
|
||||
*
|
||||
* nanoid() //=> "fbaef"
|
||||
* ```
|
||||
*
|
||||
* @param alphabet Alphabet used to generate a random string.
|
||||
* @param size Size of the random string.
|
||||
* @param random A random bytes generator.
|
||||
* @returns A random string generator.
|
||||
*/
|
||||
export function customRandom(
|
||||
alphabet: string,
|
||||
size: number,
|
||||
random: (bytes: number) => Uint8Array
|
||||
): () => string
|
||||
|
||||
/**
|
||||
* URL safe symbols.
|
||||
*
|
||||
* ```js
|
||||
* import { urlAlphabet } from 'nanoid'
|
||||
* const nanoid = customAlphabet(urlAlphabet, 10)
|
||||
* nanoid() //=> "Uakgb_J5m9"
|
||||
* ```
|
||||
*/
|
||||
export const urlAlphabet: string
|
||||
|
||||
/**
|
||||
* Generate an array of random bytes collected from hardware noise.
|
||||
*
|
||||
* ```js
|
||||
* import { customRandom, random } from 'nanoid'
|
||||
* const nanoid = customRandom("abcdef", 5, random)
|
||||
* ```
|
||||
*
|
||||
* @param bytes Size of the array.
|
||||
* @returns An array of random bytes.
|
||||
*/
|
||||
export function random(bytes: number): Uint8Array
|
44
3 proba/node_modules/nanoid/package.json
generated
vendored
44
3 proba/node_modules/nanoid/package.json
generated
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nanoid",
|
||||
"version": "3.3.6",
|
||||
"version": "3.3.7",
|
||||
"description": "A tiny (116 bytes), secure URL-friendly unique string ID generator",
|
||||
"keywords": [
|
||||
"uuid",
|
||||
@ -35,31 +35,53 @@
|
||||
"module": "index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"browser": "./index.browser.js",
|
||||
"require": "./index.cjs",
|
||||
"import": "./index.js",
|
||||
"require": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./index.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./index.d.ts": "./index.d.ts",
|
||||
"./package.json": "./package.json",
|
||||
"./async/package.json": "./async/package.json",
|
||||
"./async": {
|
||||
"browser": "./async/index.browser.js",
|
||||
"require": "./async/index.cjs",
|
||||
"import": "./async/index.js",
|
||||
"require": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./async/index.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./async/index.js"
|
||||
},
|
||||
"default": "./async/index.js"
|
||||
},
|
||||
"./non-secure/package.json": "./non-secure/package.json",
|
||||
"./non-secure": {
|
||||
"require": "./non-secure/index.cjs",
|
||||
"import": "./non-secure/index.js",
|
||||
"require": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./non-secure/index.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./non-secure/index.js"
|
||||
},
|
||||
"default": "./non-secure/index.js"
|
||||
},
|
||||
"./url-alphabet/package.json": "./url-alphabet/package.json",
|
||||
"./url-alphabet": {
|
||||
"require": "./url-alphabet/index.cjs",
|
||||
"import": "./url-alphabet/index.js",
|
||||
"require": {
|
||||
"types": "./index.d.cts",
|
||||
"default": "./url-alphabet/index.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./url-alphabet/index.js"
|
||||
},
|
||||
"default": "./url-alphabet/index.js"
|
||||
}
|
||||
}
|
||||
|
2
3 proba/node_modules/postcss/README.md
generated
vendored
2
3 proba/node_modules/postcss/README.md
generated
vendored
@ -13,7 +13,7 @@ and JetBrains. The [Autoprefixer] and [Stylelint] PostCSS plugins is one of
|
||||
|
||||
---
|
||||
|
||||
<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" /> Made in <b><a href="https://evilmartians.com/?utm_source=postcss&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>.
|
||||
<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" /> Made in <b><a href="https://evilmartians.com/devtools?utm_source=postcss&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>.
|
||||
|
||||
---
|
||||
|
||||
|
5
3 proba/node_modules/postcss/lib/map-generator.js
generated
vendored
5
3 proba/node_modules/postcss/lib/map-generator.js
generated
vendored
@ -16,6 +16,7 @@ class MapGenerator {
|
||||
this.root = root
|
||||
this.opts = opts
|
||||
this.css = cssString
|
||||
this.originalCSS = cssString
|
||||
this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute
|
||||
|
||||
this.memoizedFileURLs = new Map()
|
||||
@ -74,7 +75,7 @@ class MapGenerator {
|
||||
}
|
||||
}
|
||||
} else if (this.css) {
|
||||
this.css = this.css.replace(/(\n)?\/\*#[\S\s]*?\*\/$/gm, '')
|
||||
this.css = this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm, '')
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,7 +277,7 @@ class MapGenerator {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let input = new Input(this.css, this.opts)
|
||||
let input = new Input(this.originalCSS, this.opts)
|
||||
if (input.map) this.previousMaps.push(input.map)
|
||||
}
|
||||
}
|
||||
|
3
3 proba/node_modules/postcss/lib/no-work-result.js
generated
vendored
3
3 proba/node_modules/postcss/lib/no-work-result.js
generated
vendored
@ -37,6 +37,9 @@ class NoWorkResult {
|
||||
if (generatedMap) {
|
||||
this.result.map = generatedMap
|
||||
}
|
||||
} else {
|
||||
map.clearAnnotation();
|
||||
this.result.css = map.css;
|
||||
}
|
||||
}
|
||||
|
||||
|
2
3 proba/node_modules/postcss/lib/postcss.d.ts
generated
vendored
2
3 proba/node_modules/postcss/lib/postcss.d.ts
generated
vendored
@ -309,7 +309,7 @@ declare namespace postcss {
|
||||
* The path of the CSS source file. You should always set `from`,
|
||||
* because it is used in source map generation and syntax error messages.
|
||||
*/
|
||||
from?: string
|
||||
from?: string | undefined
|
||||
|
||||
/**
|
||||
* Source map options
|
||||
|
10
3 proba/node_modules/postcss/lib/processor.js
generated
vendored
10
3 proba/node_modules/postcss/lib/processor.js
generated
vendored
@ -7,7 +7,7 @@ let Root = require('./root')
|
||||
|
||||
class Processor {
|
||||
constructor(plugins = []) {
|
||||
this.version = '8.4.30'
|
||||
this.version = '8.4.33'
|
||||
this.plugins = this.normalize(plugins)
|
||||
}
|
||||
|
||||
@ -43,10 +43,10 @@ class Processor {
|
||||
|
||||
process(css, opts = {}) {
|
||||
if (
|
||||
this.plugins.length === 0 &&
|
||||
typeof opts.parser === 'undefined' &&
|
||||
typeof opts.stringifier === 'undefined' &&
|
||||
typeof opts.syntax === 'undefined'
|
||||
!this.plugins.length &&
|
||||
!opts.parser &&
|
||||
!opts.stringifier &&
|
||||
!opts.syntax
|
||||
) {
|
||||
return new NoWorkResult(this, css, opts)
|
||||
} else {
|
||||
|
2
3 proba/node_modules/postcss/lib/tokenize.js
generated
vendored
2
3 proba/node_modules/postcss/lib/tokenize.js
generated
vendored
@ -22,7 +22,7 @@ const AT = '@'.charCodeAt(0)
|
||||
|
||||
const RE_AT_END = /[\t\n\f\r "#'()/;[\\\]{}]/g
|
||||
const RE_WORD_END = /[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g
|
||||
const RE_BAD_BRACKET = /.[\n"'(/\\]/
|
||||
const RE_BAD_BRACKET = /.[\r\n"'(/\\]/
|
||||
const RE_HEX_ESCAPE = /[\da-f]/i
|
||||
|
||||
module.exports = function tokenizer(input, options = {}) {
|
||||
|
4
3 proba/node_modules/postcss/package.json
generated
vendored
4
3 proba/node_modules/postcss/package.json
generated
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "postcss",
|
||||
"version": "8.4.30",
|
||||
"version": "8.4.33",
|
||||
"description": "Tool for transforming styles with JS plugins",
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
@ -74,7 +74,7 @@
|
||||
"url": "https://github.com/postcss/postcss/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.6",
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
|
40
3 proba/package-lock.json
generated
40
3 proba/package-lock.json
generated
@ -2031,9 +2031,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -3123,9 +3123,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -3596,9 +3596,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.30",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
|
||||
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
|
||||
"version": "8.4.33",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
||||
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -3615,7 +3615,7 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.6",
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
@ -6058,9 +6058,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"dev": true
|
||||
},
|
||||
"for-each": {
|
||||
@ -6866,9 +6866,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare": {
|
||||
@ -7216,12 +7216,12 @@
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.30",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
|
||||
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
|
||||
"version": "8.4.33",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
||||
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.6",
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
|
@ -50,13 +50,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center">
|
||||
Жирнова Алена ПИбд-21
|
||||
</footer>
|
||||
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
|
||||
Жирнова Алена ПИбд-21
|
||||
</footer>
|
||||
<script type="module">
|
||||
import validation from "./scripts/validation";
|
||||
import { linesForm } from "./scripts/lines";
|
||||
import {linesFormOnIndex} from "./scripts/lines";
|
||||
import validation from "./js/validation";
|
||||
import { linesForm } from "./js/lines";
|
||||
import {linesFormOnIndex} from "./js/lines";
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
validation();
|
||||
linesFormOnIndex();
|
||||
|
@ -37,146 +37,94 @@
|
||||
<button id="items-add" type="submit" class="btn btn-success">
|
||||
Добавить книгу (диалог)
|
||||
</button>
|
||||
<a class="btn btn-secondary" href="/page-edit.html"
|
||||
>Добавить книгу (страница)</a
|
||||
>
|
||||
<div>
|
||||
<table id="items-table" class="table table-light table-striped">
|
||||
<table id="items-table" class="table table-hover">
|
||||
<thead>
|
||||
<th scope="col">№</th>
|
||||
<th scope="col" class="w-25">Жанр</th>
|
||||
<th scope="col" class="w-25">Автор</th>
|
||||
<th scope="col" class="w-25">Название</th>
|
||||
<th scope="col" class="w-25">Описание</th>
|
||||
<th scope="col" class="w-25">Количество</th>
|
||||
<th scope="col" class="w-25">Дата</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col">№</th>
|
||||
<th scope="col" class="w-25">Название</th>
|
||||
<th scope="col" class="w-25">Автор</th>
|
||||
<th scope="col" class="w-25">Жанр</th>
|
||||
<th scope="col" class="w-15">Год выпуска</th>
|
||||
<th scope="col" class="w-25">Описание</th>
|
||||
<th scope="col" class="w-25">Кол-во</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
|
||||
Жирнова Алена ПИбд-21
|
||||
</footer>
|
||||
<div
|
||||
id="items-update"
|
||||
class="modal fade"
|
||||
tabindex="-1"
|
||||
data-bs-backdrop="static"
|
||||
data-bs-keyboard="false"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
|
||||
Жирнова Алена ПИбд-21
|
||||
</footer>
|
||||
|
||||
|
||||
<div id="items-update" class="modal fade" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog">
|
||||
<form id="items-form" class="needs-validation" novalidate>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="items-update-title"></h1>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="items-update-title"></h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center">
|
||||
<img id="image-preview" src="https://via.placeholder.com/200" class="rounded"
|
||||
alt="placeholder">
|
||||
</div>
|
||||
<input id="items-line-id" type="number" hidden>
|
||||
<div class="mb-2">
|
||||
<label for="nameBook" class="form-label">Название</label>
|
||||
<input id="nameBook" class="form-control" name="nameBook" type="text" required="">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="author" class="form-label">Автор</label>
|
||||
<select id="author" class="form-select" name="author" required>
|
||||
</div>
|
||||
<input id="genres-line-id" type="number" hidden>
|
||||
<div class="mb-2">
|
||||
<label for="genre" class="form-label">Жанр</label>
|
||||
<select id="genre" class="form-select" name="genre" required>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input id="description" class="form-control" name="description" type="text" required="">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="yearPicker" class="form-label">Год выпуска</label>
|
||||
<input id="yearPicker" class="form-control" name="yearPicker" type="text" required="">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="count" class="form-label">Количество книг</label>
|
||||
<input
|
||||
id="count"
|
||||
name="count"
|
||||
class="form-control"
|
||||
type="number"
|
||||
value="0"
|
||||
min="1"
|
||||
step="1"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="image" class="form-label">Изображение</label>
|
||||
<input id="image" type="file" name="image" class="form-control" accept="image/*">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center">
|
||||
<img
|
||||
id="image-preview"
|
||||
src="https://via.placeholder.com/200"
|
||||
class="rounded rounded-circle"
|
||||
alt="placeholder"
|
||||
/>
|
||||
</div>
|
||||
<input id="items-line-id" type="number" hidden />
|
||||
<div class="mb-2">
|
||||
<label for="item" class="form-label">Жанр</label>
|
||||
<select
|
||||
id="item"
|
||||
class="form-select"
|
||||
name="selected"
|
||||
required
|
||||
></select>
|
||||
<div class="mb-2">
|
||||
<label for="author" class="form-label">Автор</label>
|
||||
<select
|
||||
id="author"
|
||||
class="form-select"
|
||||
name="selected"
|
||||
required>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="name" class="form-label">Название</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
class="form-control"
|
||||
type="text"
|
||||
maxlength="100"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="desc" class="form-label">Описание</label>
|
||||
<input
|
||||
id="desc"
|
||||
name="desc"
|
||||
class="form-control"
|
||||
type="text"
|
||||
maxlength="100"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="count" class="form-label">Количество книг</label>
|
||||
<input
|
||||
id="count"
|
||||
name="count"
|
||||
class="form-control"
|
||||
type="number"
|
||||
value="0"
|
||||
min="1"
|
||||
step="1"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="date" class="form-label">Дата</label>
|
||||
<input
|
||||
id="date"
|
||||
name="date"
|
||||
class="form-control"
|
||||
type="date"
|
||||
value="2022-01-01"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="image" class="form-label">Изображение</label>
|
||||
<input
|
||||
id="image"
|
||||
type="file"
|
||||
name="image"
|
||||
class="form-control"
|
||||
accept="image/*"
|
||||
/>
|
||||
</div></div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit-grey"
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
>
|
||||
Закрыть
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module">
|
||||
import validation from "./js/validation";
|
||||
import { linesForm } from "./js/lines";
|
||||
|
Loading…
x
Reference in New Issue
Block a user