import { Alpine } from "./alpine"; import { showToast } from "./toast"; async function loadUnlinkedBooks(): Promise { const token = localStorage.getItem("token"); if (!token) return; try { const response = await fetch("/api/sync/unlinked-books", { headers: { Authorization: `Bearer ${token}` }, }); if (response.ok) { const data = await response.json(); renderUnlinkedBooks(data.unlinked || []); } } catch (error) { console.error("Failed to load unlinked books:", error); } } function renderUnlinkedBooks(books: UnlinkedBookData[]): void { const container = document.getElementById("unlinked-books-list"); if (!container) return; if (books.length === 0) { container.innerHTML = '

No unlinked books

'; return; } container.innerHTML = books .map( (book) => `

${book.title_from_device}

${book.device_name} (${book.device_type})

${book.file_path}

Confidence: ${Math.round(book.confidence_score * 100)}%

${ book.potential_matches && book.potential_matches.length > 0 ? `

Potential Matches:

${book.potential_matches .slice(0, 3) .map( (match) => `

${match.title}

${match.author} (${Math.round(match.confidence * 100)}%)

`, ) .join("")}
` : "" }
`, ) .join(""); } async function linkBook( progressId: string, mediaItemId: string, ): Promise { const token = localStorage.getItem("token"); if (!token) return; try { const response = await fetch("/api/sync/link-book", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ progress_id: progressId, media_item_id: mediaItemId, }), }); if (response.ok) { showToast("Book linked successfully", "success"); loadUnlinkedBooks(); } else { const error = await response.json(); showToast(error.error || "Failed to link book", "error"); } } catch (error) { console.error("Failed to link book:", error); showToast("Failed to link book", "error"); } } async function autoLinkBooks(): Promise { const token = localStorage.getItem("token"); if (!token) return; if (!confirm("Auto-link all books with high confidence matches?")) return; try { const response = await fetch("/api/sync/auto-link", { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ confidence_threshold: 0.9 }), }); if (response.ok) { const data = await response.json(); showToast(`Auto-linked ${data.linked_count || 0} books`, "success"); loadUnlinkedBooks(); } } catch (error) { console.error("Failed to auto-link:", error); showToast("Failed to auto-link books", "error"); } } async function getSuggestions(progressId: string): Promise { const token = localStorage.getItem("token"); if (!token) return; try { const response = await fetch(`/api/sync/suggestions/${progressId}`, { headers: { Authorization: `Bearer ${token}` }, }); if (response.ok) { const suggestions = await response.json(); showSuggestionsModal(progressId, suggestions); } } catch (error) { console.error("Failed to get suggestions:", error); } } function showSuggestionsModal( progressId: string, suggestions: PotentialMatchData[], ): void { const modal = document.getElementById("match-modal"); const content = document.getElementById("match-modal-content"); if (!modal || !content) return; content.innerHTML = `

Select a match

${suggestions .map( (s) => `

${s.title}

${s.author}

${Math.round(s.confidence * 100)}% match

`, ) .join("")}
`; modal.classList.remove("hidden"); } function hideMatchModal(): void { const modal = document.getElementById("match-modal"); if (modal) { modal.classList.add("hidden"); } } export { autoLinkBooks, getSuggestions, hideMatchModal, linkBook, loadUnlinkedBooks, showSuggestionsModal, }; Alpine.data("linking", () => ({ autoLinkBooks, getSuggestions, hideMatchModal, linkBook, loadUnlinkedBooks, showSuggestionsModal, }));