refactor(bookshelf): migrate from DOM manipulation to Alpine.js reactive state

Replace direct DOM manipulation with Alpine.js reactive state variables:
- Add isLoading and hasBooks state to bookshelf component
- Convert loadBookshelf() to update isLoading state instead of toggling DOM visibility
- Convert renderBookshelf() to use reactive state for empty state handling
- Remove redundant getElementById() calls for loading/empty-state elements

This change improves maintainability by:
- Centralizing UI state in the Alpine component
- Eliminating direct DOM manipulation scattered across functions
- Making the component's state more explicit and trackable
- Following Alpine.js reactive programming patterns

The UI will now respond to state changes automatically rather than requiring
manual DOM updates throughout the lifecycle methods.
This commit is contained in:
2026-03-15 21:02:58 -04:00
parent 855cbd1b74
commit a9bbd1ee2e
+11 -20
View File
@@ -33,13 +33,7 @@ async function loadBookshelf(libraryId: string): Promise<void> {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
if (!token || !libraryId) return; if (!token || !libraryId) return;
const loading = document.getElementById("loading"); this.isLoading = true;
const booksGrid = document.getElementById("books-grid");
const emptyState = document.getElementById("empty-state");
if (loading) loading.style.display = "block";
if (booksGrid) booksGrid.classList.add("hidden");
if (emptyState) emptyState.classList.add("hidden");
try { try {
const response = await fetch( const response = await fetch(
@@ -60,8 +54,7 @@ async function loadBookshelf(libraryId: string): Promise<void> {
} catch (error) { } catch (error) {
console.error("Error loading bookshelf:", error); console.error("Error loading bookshelf:", error);
showToast("Error loading books", "error"); showToast("Error loading books", "error");
} finally { this.isLoading = false;
if (loading) loading.style.display = "none";
} }
} }
@@ -76,21 +69,14 @@ function showEmptyState(): void {
} }
function renderBookshelf(): void { function renderBookshelf(): void {
const booksGrid = document.getElementById("books-grid");
const emptyState = document.getElementById("empty-state");
const loading = document.getElementById("loading");
if (!booksGrid) return;
if (loading) loading.style.display = "none";
if (emptyState) emptyState.classList.add("hidden");
if (!mediaItems || mediaItems.length === 0) { if (!mediaItems || mediaItems.length === 0) {
showEmptyState(); this.isLoading = false;
this.hasBooks = false;
return; return;
} }
booksGrid.classList.remove("hidden"); const booksGrid = document.getElementById("books-grid");
if (!booksGrid) return;
const booksPerShelf = 6; const booksPerShelf = 6;
const shelves: unknown[][] = []; const shelves: unknown[][] = [];
@@ -199,6 +185,11 @@ export {
}; };
Alpine.data("bookshelf", () => ({ Alpine.data("bookshelf", () => ({
// State Variables
isLoading: true,
hasBooks: false,
// Methods
changePage, changePage,
initBookshelf, initBookshelf,
loadBookshelf, loadBookshelf,