Работает добавление и удаление фильмов в админке

This commit is contained in:
ekallin 2023-11-13 15:11:53 +04:00 committed by ns.potapov
parent f75924a03c
commit 52f9769b41
5 changed files with 273 additions and 78 deletions

View File

@ -77,7 +77,7 @@
</header> </header>
<main class="flex-fill d-flex flex-column px-1 px-md-2"> <main class="flex-fill d-flex flex-column px-1 px-md-2">
<div class="control-group py-2"> <div class="control-group py-2">
<button id="items-add" class="btn btn-light"> <button id="movie-button-add" class="btn btn-light">
Добавить фильм (диалог) Добавить фильм (диалог)
</button> </button>
</div> </div>
@ -150,7 +150,7 @@
<img <img
id="image-preview" id="image-preview"
src="https://via.placeholder.com/160x230" src="https://via.placeholder.com/160x230"
class="rounded-2" class="rounded-2 img-fluid"
alt="placeholder" alt="placeholder"
/> />
</div> </div>

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,151 @@
import { ApiEndpoint } from "./apiendpoint"; import { ApiEndpoint, createMovieObject } from "./apiendpoint";
import { Modal } from "bootstrap"; import { Modal } from "bootstrap";
import { modalControls } from "./modalui";
const moviesApiEndpoint = new ApiEndpoint("movies"); const moviesApiEndpoint = new ApiEndpoint("movies");
const categoriesApiEndpoint = new ApiEndpoint("categories"); const categoriesApiEndpoint = new ApiEndpoint("categories");
document.addEventListener("DOMContentLoaded", loadMoviesTableData()); document.addEventListener("DOMContentLoaded", loadMoviesTableData());
document
.getElementById("movie-button-add")
.addEventListener("click", buttonMovieAddClicked);
const modal = document.getElementById("modal-movie"); const modal = document.getElementById("modal-movie");
const myModal = modal ? new Modal(modal, {}) : null; const myModal = modal ? new Modal(modal, {}) : null;
// myModal.show(); // myModal.show();
function createItemsOption(name, value = "", isSelected = false) {
const option = document.createElement("option");
option.value = value || "";
option.selected = isSelected;
option.text = name;
return option;
}
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);
});
}
// TODO
// функция для обновления блока с превью выбранного изображения
async function updateImagePreview() {
// получение выбранного файла
// возможен выбор нескольких файлов, поэтому необходимо получить только первый
const file = modalControls.inputImage.files[0];
// чтение содержимого файла в виде base64 строки
const fileContent = await readFile(file);
console.info("base64 ", fileContent);
// обновление атрибута src для тега img с id image-preview
modalControls.imagePreview.src = fileContent;
}
const imagePlaceholder = "./static/images/cover-placeholder160x230.png";
modalControls.form.addEventListener("submit", async (event) => {
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
// if (!cntrls.form.checkValidity()) {
// return;
// }
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
// modalControls.imagePreview.src !== imagePlaceholder
// TODO
// прописать логиук отображения превью
if (modalControls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(modalControls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = modalControls.movieId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
const movie = createMovieObject(
modalControls.inputName.value,
parseInt(modalControls.selectCategory.value),
modalControls.inputDuration.value,
modalControls.inputDescription.value,
imageBase64
);
await moviesApiEndpoint.createObject(movie);
} else {
// TODO
// await editLine(
// currentId,
// cntrls.itemsType.value,
// cntrls.price.value,
// cntrls.count.value,
// imageBase64,
// );
}
// после выполнения добавления/обновления модальное окно скрывается
myModal.hide();
await loadMoviesTableData();
});
async function buttonMovieAddClicked(e) {
modalControls.title.innerText = "Добавить фильм";
modalControls.selectCategory.innerHTML = "";
const categories = await categoriesApiEndpoint.getObjects();
modalControls.selectCategory.appendChild(
createItemsOption("Выберите жанр", "", true)
);
for (let i = 0; i < categories.length; i++) {
const category = categories[i];
modalControls.selectCategory.appendChild(
createItemsOption(category.name, category.id)
);
}
myModal.show();
}
async function movieDeleteButtonClicked(e) { async function movieDeleteButtonClicked(e) {
console.log(e.target); console.log(e.target);
const movieId = parseInt(e.target.id.split("-")[3]); const movieId = parseInt(e.target.id.split("-")[3]);
@ -18,12 +153,24 @@ async function movieDeleteButtonClicked(e) {
await loadMoviesTableData(); await loadMoviesTableData();
} }
async function movieEditButtonClicked(e) {} async function movieEditButtonClicked(e) {
// TODO
// получить объект по id
// заполнение модального окна по данным объекта
// показ модального окна
}
async function loadMoviesTableData() { async function loadMoviesTableData() {
const movies = await moviesApiEndpoint.getObjects(); const movies = await moviesApiEndpoint.getObjects();
const category = await categoriesApiEndpoint.getObjects(); const category = await categoriesApiEndpoint.getObjects();
const tableWrapper = document.getElementById("movie-table-body"); const tableWrapper = document.getElementById("movie-table-body");
// Вызов функции обновления превью изображения при возникновении
// события oncahnge в тэге input с id image
modalControls.inputImage.addEventListener("change", () =>
updateImagePreview()
);
tableWrapper.innerHTML = ""; tableWrapper.innerHTML = "";
for (let i = 0; i < movies.length; i++) { for (let i = 0; i < movies.length; i++) {

View File

@ -1,89 +1,91 @@
const serverUrl = "http://localhost:8081"; const serverUrl = "http://localhost:8081";
// export function createPostObject( export function createMovieObject(
// userId, name,
// createdDateTime, categoryId,
// text, duration,
// img, description,
// ) { img
// return { ) {
// userId, return {
// createdDateTime, name,
// text, categoryId,
// img, duration,
// }; description,
// } img,
};
}
export class ApiEndpoint { export class ApiEndpoint {
constructor(endpoint) { constructor(endpoint) {
this.endpoint = endpoint; this.endpoint = endpoint;
}
async getObjects() {
const response = await fetch(`${serverUrl}/${this.endpoint}`);
if (!response.ok) {
throw response.statusText;
} }
return response.json();
}
async getObjects() { async getObject(id) {
const response = await fetch(`${serverUrl}/${this.endpoint}`); const response = await fetch(`${serverUrl}/${this.endpoint}/${id}`);
if (!response.ok) { if (!response.ok) {
throw response.statusText; throw response.statusText;
}
return response.json();
} }
return response.json();
}
async getObject(id) { async createObject(obj) {
const response = await fetch(`${serverUrl}/${this.endpoint}/${id}`); const options = {
if (!response.ok) { method: "POST",
throw response.statusText; body: JSON.stringify(obj),
} headers: {
return response.json(); Accept: "application/json",
"Content-Type": "application/json",
},
};
const response = await fetch(`${serverUrl}/${this.endpoint}`, options);
if (!response.ok) {
throw response.statusText;
} }
return response.json();
}
async createObject(obj) { async updateObject(obj) {
const options = { const options = {
method: "POST", method: "PUT",
body: JSON.stringify(obj), body: JSON.stringify(obj),
headers: { headers: {
"Accept": "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
}; };
const response = await fetch(`${serverUrl}/${this.endpoint}`, options); const response = await fetch(
if (!response.ok) { `${serverUrl}/${this.endpoint}/${obj.id}`,
throw response.statusText; options
} );
return response.json(); if (!response.ok) {
throw response.statusText;
} }
return response.json();
}
async updateObject(obj) { async deleteObject(id) {
const options = { const options = {
method: "PUT", method: "DELETE",
body: JSON.stringify(obj), };
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
};
const response = await fetch( const response = await fetch(
`${serverUrl}/${this.endpoint}/${obj.id}`, `${serverUrl}/${this.endpoint}/${id}`,
options, options
); );
if (!response.ok) { if (!response.ok) {
throw response.statusText; throw response.statusText;
}
return response.json();
}
async deleteObject(id) {
const options = {
method: "DELETE",
};
const response = await fetch(
`${serverUrl}/${this.endpoint}/${id}`,
options,
);
if (!response.ok) {
throw response.statusText;
}
return response.json();
} }
return response.json();
}
} }

13
static/js/modalui.js Normal file
View File

@ -0,0 +1,13 @@
const movieModal = document.getElementById("modal-movie");
export const modalControls = {
title: document.getElementById("movie-modal-title"),
imagePreview: document.getElementById("image-preview"),
selectCategory: document.getElementById("category"),
inputName: document.getElementById("name"),
inputDuration: document.getElementById("duration"),
inputDescription: document.getElementById("description"),
inputImage: document.getElementById("image"),
form: document.getElementById("movie-form"),
movieId: document.getElementById("movie-id"),
};