/
miragecatv
/
library-api-csharp
Обзор
Документация
Войти
/
miragecatv
/
library-api-csharp
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
frontend/app.js
211 строк
6 KB
Nikita P
Initial BookDB project
22 июл 2026, 21:33
22 июл 2026, 21:33
1c0f3fe
Код
Авторство
О чём код?
const API_URL = "/api/books"; console.log("app.js запущен"); const state = { books: [], query: "", sortKey: "title", sortDirection: "asc" }; const elements = { body: document.querySelector("#books-body"), count: document.querySelector("#book-count"), search: document.querySelector("#search-input"), status: document.querySelector("#status"), tableWrapper: document.querySelector("#table-wrapper"), sortButtons: [...document.querySelectorAll(".sort-button")] }; document.addEventListener("DOMContentLoaded", () => { elements.search.addEventListener("input", handleSearch); for (const button of elements.sortButtons) { button.addEventListener("click", () => handleSort(button.dataset.sort)); } loadBooks(); }); async function loadBooks() { setStatus("Загрузка каталога…"); try { const response = await fetch(API_URL, { headers: { Accept: "application/json" } }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const payload = await response.json(); state.books = normalizeResponse(payload); elements.status.hidden = true; elements.tableWrapper.hidden = false; render(); } catch (error) { elements.tableWrapper.hidden = true; setStatus(`Ошибка загрузки: ${error.message}`, true); } } function normalizeResponse(payload) { const rows = Array.isArray(payload) ? payload : payload.items ?? payload.data ?? payload.books ?? []; return rows.map((book) => ({ editionId: book.editionId ?? book.edition_id ?? book.id ?? "", title: book.title ?? "", authors: book.authors ?? "", publisher: book.publisher ?? "", series: book.series ?? "", publicationYear: book.publicationYear ?? book.publication_year ?? "", pageCount: book.pageCount ?? book.page_count ?? "", genres: book.genres ?? "", readingStatus: book.readingStatus ?? book.reading_status ?? "" })); } function handleSearch(event) { state.query = event.target.value.trim().toLocaleLowerCase("ru"); render(); } function handleSort(key) { if (state.sortKey === key) { state.sortDirection = state.sortDirection === "asc" ? "desc" : "asc"; } else { state.sortKey = key; state.sortDirection = "asc"; } render(); } function render() { const books = getVisibleBooks(); elements.body.replaceChildren(...books.map(createRow)); elements.count.textContent = formatCount(books.length, state.books.length); for (const button of elements.sortButtons) { if (button.dataset.sort === state.sortKey) { button.dataset.direction = state.sortDirection; button.setAttribute( "aria-sort", state.sortDirection === "asc" ? "ascending" : "descending" ); } else { delete button.dataset.direction; button.removeAttribute("aria-sort"); } } if (books.length === 0) { const row = document.createElement("tr"); const cell = document.createElement("td"); cell.colSpan = 9; cell.className = "muted"; cell.textContent = "Ничего не найдено."; row.append(cell); elements.body.append(row); } } function getVisibleBooks() { const query = state.query; return state.books .filter((book) => { if (!query) return true; return [ book.title, book.authors, book.publisher, book.series, book.genres, book.readingStatus, book.publicationYear ] .join(" ") .toLocaleLowerCase("ru") .includes(query); }) .sort(compareBooks); } function compareBooks(left, right) { const a = left[state.sortKey]; const b = right[state.sortKey]; const aNumber = Number(a); const bNumber = Number(b); let result; if (a !== "" && b !== "" && Number.isFinite(aNumber) && Number.isFinite(bNumber)) { result = aNumber - bNumber; } else { result = String(a ?? "").localeCompare(String(b ?? ""), "ru", { sensitivity: "base", numeric: true }); } return state.sortDirection === "asc" ? result : -result; } function createRow(book) { const row = document.createElement("tr"); row.append( createCell(book.editionId, "numeric"), createCell(book.title), createCell(book.authors), createCell(book.publisher), createCell(book.series), createCell(book.publicationYear, "numeric"), createCell(book.pageCount, "numeric"), createCell(book.genres), createCell(book.readingStatus) ); return row; } function createCell(value, className = "") { const cell = document.createElement("td"); const text = String(value ?? "").trim(); cell.textContent = text || "—"; if (!text) { cell.classList.add("muted"); } if (className) { cell.classList.add(className); } return cell; } function setStatus(message, isError = false) { elements.status.hidden = false; elements.status.textContent = message; elements.status.classList.toggle("error", isError); } function formatCount(visible, total) { if (visible === total) { return `Книг: ${total}`; } return `Показано: ${visible} из ${total}`; }