From 16a043843aebd4fe2e83bb5cb509e9b23dcd768f Mon Sep 17 00:00:00 2001 From: wowlikon Date: Fri, 19 Dec 2025 08:39:02 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=84=D1=80=D0=BE=D0=BD=D1=82=D1=8D=D0=BD?= =?UTF-8?q?=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library_service/routers/__init__.py | 10 +- library_service/routers/misc.py | 7 + library_service/static/script.js | 204 ++++++++++++--------------- library_service/templates/api.html | 5 +- library_service/templates/auth.html | 0 library_service/templates/index.html | 5 +- 6 files changed, 110 insertions(+), 121 deletions(-) create mode 100644 library_service/templates/auth.html diff --git a/library_service/routers/__init__.py b/library_service/routers/__init__.py index 7661a50..624e55e 100644 --- a/library_service/routers/__init__.py +++ b/library_service/routers/__init__.py @@ -11,9 +11,9 @@ from .misc import router as misc_router api_router = APIRouter() # Подключение всех маршрутов -api_router.include_router(auth_router) -api_router.include_router(authors_router) -api_router.include_router(books_router) -api_router.include_router(genres_router) -api_router.include_router(relationships_router) api_router.include_router(misc_router) +api_router.include_router(auth_router, prefix="/api") +api_router.include_router(authors_router, prefix="/api") +api_router.include_router(books_router, prefix="/api") +api_router.include_router(genres_router, prefix="/api") +api_router.include_router(relationships_router, prefix="/api") diff --git a/library_service/routers/misc.py b/library_service/routers/misc.py index b00687d..678305d 100644 --- a/library_service/routers/misc.py +++ b/library_service/routers/misc.py @@ -34,6 +34,13 @@ async def root(request: Request, app=Depends(get_app)): return templates.TemplateResponse(request, "index.html", get_info(app)) +@router.get("/auth", include_in_schema=False) +async def root(request: Request, app=Depends(get_app)): + """Эндпоинт страницы авторизации""" + return templates.TemplateResponse(request, "auth.html", get_info(app)) + + + @router.get("/api", include_in_schema=False) async def root(request: Request, app=Depends(get_app)): """Страница с сылками на документацию API""" diff --git a/library_service/static/script.js b/library_service/static/script.js index 4cb272f..48e6c97 100644 --- a/library_service/static/script.js +++ b/library_service/static/script.js @@ -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 = $("
", { + 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 = $("
  • ", { class: "mb-1" }); + $li.html(` - `; - 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 = $("", { + 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} + + `); + $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} - - `; - 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(); + } +}); diff --git a/library_service/templates/api.html b/library_service/templates/api.html index a1307fa..17ed01c 100644 --- a/library_service/templates/api.html +++ b/library_service/templates/api.html @@ -50,11 +50,12 @@

    Current Time: {{ server_time }}

    Status: {{ status }}

    diff --git a/library_service/templates/auth.html b/library_service/templates/auth.html new file mode 100644 index 0000000..e69de29 diff --git a/library_service/templates/index.html b/library_service/templates/index.html index c9beb6d..44bee1b 100644 --- a/library_service/templates/index.html +++ b/library_service/templates/index.html @@ -1,9 +1,10 @@ + LiB - LiB + @@ -21,7 +22,7 @@ Home
  • - Products + books
  • About