diff --git a/web/src/bookshelf.ts b/web/src/bookshelf.ts index 1aa6044..759afaf 100644 --- a/web/src/bookshelf.ts +++ b/web/src/bookshelf.ts @@ -1,5 +1,10 @@ +// Bookshelf functionality - procedural/imperative style + import { Alpine } from "./alpine"; import { showToast } from "./toast"; + +// Filter state management (Alpine.js handles this, TS just saves/restores) + async function loadSavedFilters(): Promise { const token = localStorage.getItem("token"); if (!token) return; @@ -7,6 +12,7 @@ async function loadSavedFilters(): Promise { const response = await fetch("/api/bookshelf/filters", { headers: { Authorization: `Bearer ${token}` }, }); + if (response.ok) { const filters = await response.json(); localStorage.setItem("bookshelfFilters", JSON.stringify(filters)); @@ -15,11 +21,83 @@ async function loadSavedFilters(): Promise { console.error("Failed to load saved filters:", error); } } -// Note: saveFilter and showSaveFilterModal are now methods on the Alpine component -// They access state via 'this' instead of window.Alpine + +async function saveFilter(event: Event): Promise { + event.preventDefault(); + const token = localStorage.getItem("token"); + if (!token) { + showToast("Not authenticated", "error"); + return; + } + + const filterForm = document.getElementById("filter-form") as HTMLFormElement; + const formData = new FormData(filterForm); + const filterData: Record = {}; + + formData.forEach((value, key) => { + filterData[key] = value.toString(); + }); + + // Add filter name from Alpine state + const filterName = (window as any).Alpine?.$store.bookshelf?.filterName; + if (!filterName) { + showToast("Please enter a filter name", "error"); + return; + } + + try { + const response = await fetch("/api/bookshelf/filters", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + name: filterName, + filters: filterData, + }), + }); + + if (response.ok) { + showToast("Filter saved successfully", "success"); + // Close modal via Alpine + (window as any).Alpine.$store.bookshelf.showSaveModal = false; + loadSavedFilters(); + } else { + showToast("Failed to save filter", "error"); + } + } catch (error) { + console.error("Failed to save filter:", error); + showToast("Error saving filter", "error"); + } +} + +function initBookshelf(): void { + // Load saved filters on page load + loadSavedFilters(); + + // Setup initial book load via HTMX + const librarySelect = document.getElementById( + "library-select", + ) as HTMLSelectElement; + if (librarySelect && librarySelect.value) { + const filterForm = document.getElementById( + "filter-form", + ) as HTMLFormElement; + const booksGrid = document.getElementById("books-grid"); + + if (filterForm && booksGrid) { + // Trigger initial HTMX load + window.htmx.trigger(librarySelect, "change"); + } + } +} + function clearFilters(): void { const filterForm = document.getElementById("filter-form") as HTMLFormElement; if (!filterForm) return; + + // Reset all form fields const inputs = filterForm.querySelectorAll("input, select"); inputs.forEach((input) => { if (input instanceof HTMLInputElement && input.type === "checkbox") { @@ -28,77 +106,25 @@ function clearFilters(): void { (input as HTMLInputElement).value = ""; } }); - htmx.trigger(filterForm, "change"); + + // Trigger HTMX reload with cleared filters + window.htmx.trigger(filterForm, "change"); } -// Alpine.js component - all state managed locally, no window.Alpine at runtime + +function showSaveFilterModal(): void { + // Open modal via Alpine store + (window as any).Alpine.$store.bookshelf.showSaveModal = true; +} + +// Alpine.js component Alpine.data("bookshelf", () => ({ showSaveModal: false, filterName: "", - initBookshelf(): void { - loadSavedFilters(); - const librarySelect = document.getElementById( - "library-select", - ) as HTMLSelectElement; - if (librarySelect && librarySelect.value) { - const filterForm = document.getElementById( - "filter-form", - ) as HTMLFormElement; - if (filterForm) { - htmx.trigger(librarySelect, "change"); - } - } - }, + + initBookshelf, clearFilters, - showSaveFilterModal(): void { - this.showSaveModal = true; - this.filterName = ""; - }, - hideSaveFilterModal(): void { - this.showSaveModal = false; - this.filterName = ""; - }, - async saveFilter(event: Event): Promise { - event.preventDefault(); - const token = localStorage.getItem("token"); - if (!token) { - showToast("Not authenticated", "error"); - return; - } - if (!this.filterName) { - showToast("Please enter a filter name", "error"); - return; - } - const filterForm = document.getElementById( - "filter-form", - ) as HTMLFormElement; - const formData = new FormData(filterForm); - const filterData: Record = {}; - formData.forEach((value, key) => { - filterData[key] = value.toString(); - }); - try { - const response = await fetch("/api/bookshelf/filters", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ - name: this.filterName, - filters: filterData, - }), - }); - if (response.ok) { - showToast("Filter saved successfully", "success"); - this.hideSaveFilterModal(); - loadSavedFilters(); - } else { - showToast("Failed to save filter", "error"); - } - } catch (error) { - console.error("Failed to save filter:", error); - showToast("Error saving filter", "error"); - } - }, + showSaveFilterModal, + saveFilter, })); -export { clearFilters }; + +export { initBookshelf, clearFilters, showSaveFilterModal, saveFilter };