import { Alpine } from "./alpine"; import { showToast } from "./toast"; import { setSelectedLibrary } from "./storage"; interface SeriesItem { name: string; book_count: number; total_in_series: number; cover_paths: string[]; last_entry_at: string; } function renderSeriesCard(series: SeriesItem, libraryId: string): string { const href = `/bookshelf?series_filter=${encodeURIComponent(series.name)}&sort=series&library_id=${libraryId}`; const coverCount = series.cover_paths.length; const coverClass = `cover-count-${coverCount}`; let coversHtml = ""; for (let i = 0; i < coverCount; i++) { coversHtml += `${series.name}`; } if (coverCount === 0) { coversHtml = `
📚
`; } return `
${coversHtml}

${series.name}

${series.book_count} of ${series.total_in_series} books

`; } function renderSeriesContent( seriesList: SeriesItem[], libraryId: string, totalPages: number, currentPage: number, ): string { if (seriesList.length === 0) { return `

Series

📚

No Series Found

Books with series metadata will appear here

`; } const cardsHtml = seriesList.map((s) => renderSeriesCard(s, libraryId)).join(""); let paginationHtml = ""; if (totalPages > 1) { const prevLink = currentPage > 1 ? `← Previous` : ""; const nextLink = currentPage < totalPages ? `Next →` : ""; paginationHtml = `
${prevLink} Page ${currentPage} of ${totalPages} ${nextLink}
`; } return `

Series

${cardsHtml}
${paginationHtml}
`; } async function switchLibrary(libraryId: string): Promise { const container = document.getElementById("series-container") as HTMLElement; const loading = document.getElementById("loading-spinner") as HTMLElement; if (!container || !loading) return; try { container.classList.add("opacity-0", "transition-opacity", "duration-150"); await new Promise((resolve) => setTimeout(resolve, 150)); loading.classList.remove("hidden"); const response = await fetch( `/api/series?library_id=${libraryId}&limit=24&offset=0`, { headers: { Authorization: `Bearer ${localStorage.getItem("token")}`, "Content-Type": "application/json", }, }, ); if (!response.ok) { throw new Error("Failed to load series"); } const data = await response.json(); const seriesList: SeriesItem[] = data.series || []; const total: number = data.total || 0; const totalPages = Math.max(1, Math.ceil(total / 24)); container.innerHTML = renderSeriesContent(seriesList, libraryId, totalPages, 1); setSelectedLibrary(libraryId); } catch (error) { showToast("Failed to load series", "error"); console.error("Switch library error:", error); } finally { loading.classList.add("hidden"); container.classList.remove("duration-150"); container.classList.add("duration-300"); void container.offsetHeight; container.classList.remove("opacity-0"); setTimeout(() => { container.classList.remove("transition-opacity", "duration-300"); }, 300); } } Alpine.data("seriesPage", () => ({ initSeriesPage() { const librarySelect = document.getElementById( "library-select", ) as HTMLSelectElement; if (librarySelect) { librarySelect.addEventListener("change", () => { if (librarySelect.value) { switchLibrary(librarySelect.value); } }); } }, }));