Обновление фронтэнда

This commit is contained in:
2025-12-19 08:39:02 +03:00
parent 756e941f99
commit 16a043843a
6 changed files with 110 additions and 121 deletions
+92 -112
View File
@@ -1,135 +1,115 @@
// Load authors and genres asynchronously
Promise.all([
fetch("/authors").then((response) => response.json()),
fetch("/genres").then((response) => response.json()),
])
.then(([authorsData, genresData]) => {
// Populate authors dropdown
const dropdown = document.getElementById("author-dropdown");
authorsData.authors.forEach((author) => {
const div = document.createElement("div");
div.className = "p-2 hover:bg-gray-100 cursor-pointer";
div.setAttribute("data-value", author.name);
div.textContent = author.name;
dropdown.appendChild(div);
});
$(document).ready(function () {
Promise.all([
fetch("/authors").then((response) => response.json()),
fetch("/genres").then((response) => response.json()),
])
.then(([authorsData, genresData]) => {
const $dropdown = $("#author-dropdown");
authorsData.authors.forEach((author) => {
const $div = $("<div>", {
class: "p-2 hover:bg-gray-100 cursor-pointer",
"data-value": author.name,
text: author.name,
});
$dropdown.append($div);
});
// Populate genres list
const list = document.getElementById("genres-list");
genresData.genres.forEach((genre) => {
const li = document.createElement("li");
li.className = "mb-1";
li.innerHTML = `
const $list = $("#genres-list");
genresData.genres.forEach((genre) => {
const $li = $("<li>", { class: "mb-1" });
$li.html(`
<label class="custom-checkbox flex items-center">
<input type="checkbox" />
<span class="checkmark"></span>
${genre.name}
</label>
`;
list.appendChild(li);
`);
$list.append($li);
});
initializeAuthorDropdown();
})
.catch((error) => console.error("Error loading data:", error));
function initializeAuthorDropdown() {
const $authorSearchInput = $("#author-search-input");
const $authorDropdown = $("#author-dropdown");
const $selectedAuthorsContainer = $("#selected-authors-container");
const $dropdownItems = $authorDropdown.find("[data-value]");
let selectedAuthors = new Set();
const updateDropdownHighlights = () => {
$dropdownItems.each(function () {
const value = $(this).data("value");
$(this).toggleClass("bg-gray-200", selectedAuthors.has(value));
});
};
const renderSelectedAuthors = () => {
$selectedAuthorsContainer.children().not("#author-search-input").remove();
selectedAuthors.forEach((author) => {
const $authorChip = $("<span>", {
class:
"flex items-center bg-gray-200 text-gray-800 text-sm font-medium px-2.5 py-0.5 rounded-full",
});
$authorChip.html(`
${author}
<button type="button" class="ml-1 inline-flex items-center p-0.5 text-sm text-gray-400 bg-transparent rounded-sm hover:bg-gray-200 hover:text-gray-900" data-author="${author}">
<svg class="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
<span class="sr-only">Remove author</span>
</button>
`);
$selectedAuthorsContainer.append($authorChip);
});
updateDropdownHighlights();
};
$authorSearchInput.on("focus", () => {
$authorDropdown.removeClass("hidden");
});
initializeAuthorDropdown();
})
.catch((error) => console.error("Error loading data:", error));
function initializeAuthorDropdown() {
const authorSearchInput = document.getElementById("author-search-input");
const authorDropdown = document.getElementById("author-dropdown");
const selectedAuthorsContainer = document.getElementById(
"selected-authors-container",
);
const dropdownItems = authorDropdown.querySelectorAll("[data-value]");
let selectedAuthors = new Set();
// Function to update highlights in dropdown
const updateDropdownHighlights = () => {
dropdownItems.forEach((item) => {
const value = item.dataset.value;
if (selectedAuthors.has(value)) {
item.classList.add("bg-gray-200");
} else {
item.classList.remove("bg-gray-200");
}
$authorSearchInput.on("input", function () {
const query = $(this).val().toLowerCase();
$dropdownItems.each(function () {
const text = $(this).text().toLowerCase();
$(this).css("display", text.includes(query) ? "block" : "none");
});
$authorDropdown.removeClass("hidden");
});
};
// Function to render selected authors
const renderSelectedAuthors = () => {
Array.from(selectedAuthorsContainer.children).forEach((child) => {
if (child.id !== "author-search-input") {
child.remove();
$(document).on("click", function (event) {
if (
!$selectedAuthorsContainer.is(event.target) &&
!$selectedAuthorsContainer.has(event.target).length &&
!$authorDropdown.is(event.target) &&
!$authorDropdown.has(event.target).length
) {
$authorDropdown.addClass("hidden");
}
});
selectedAuthors.forEach((author) => {
const authorChip = document.createElement("span");
authorChip.className =
"flex items-center bg-gray-200 text-gray-800 text-sm font-medium px-2.5 py-0.5 rounded-full";
authorChip.innerHTML = `
${author}
<button type="button" class="ml-1 inline-flex items-center p-0.5 text-sm text-gray-400 bg-transparent rounded-sm hover:bg-gray-200 hover:text-gray-900" data-author="${author}">
<svg class="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
<span class="sr-only">Remove author</span>
</button>
`;
selectedAuthorsContainer.insertBefore(authorChip, authorSearchInput);
});
updateDropdownHighlights();
};
// Handle input focus to show dropdown
authorSearchInput.addEventListener("focus", () => {
authorDropdown.classList.remove("hidden");
});
// Handle input for filtering
authorSearchInput.addEventListener("input", () => {
const query = authorSearchInput.value.toLowerCase();
dropdownItems.forEach((item) => {
const text = item.textContent.toLowerCase();
item.style.display = text.includes(query) ? "block" : "none";
});
authorDropdown.classList.remove("hidden");
});
// Handle clicks outside to hide dropdown
document.addEventListener("click", (event) => {
if (
!selectedAuthorsContainer.contains(event.target) &&
!authorDropdown.contains(event.target)
) {
authorDropdown.classList.add("hidden");
}
});
// Handle author selection from dropdown
authorDropdown.addEventListener("click", (event) => {
const selectedValue = event.target.dataset.value;
if (selectedValue) {
$authorDropdown.on("click", "[data-value]", function () {
const selectedValue = $(this).data("value");
if (selectedAuthors.has(selectedValue)) {
selectedAuthors.delete(selectedValue);
} else {
selectedAuthors.add(selectedValue);
}
authorSearchInput.value = "";
$authorSearchInput.val("");
renderSelectedAuthors();
authorSearchInput.focus();
}
});
$authorSearchInput.focus();
});
// Handle removing selected author chip
selectedAuthorsContainer.addEventListener("click", (event) => {
if (event.target.closest("button")) {
const authorToRemove = event.target.closest("button").dataset.author;
$selectedAuthorsContainer.on("click", "button", function () {
const authorToRemove = $(this).data("author");
selectedAuthors.delete(authorToRemove);
renderSelectedAuthors();
authorSearchInput.focus();
}
});
$authorSearchInput.focus();
});
// Initial render and highlights (without auto-focus)
renderSelectedAuthors();
}
renderSelectedAuthors();
}
});