refactor: Add Alpine.js registration to existing TypeScript modules

Added Alpine.global() registration to enable template access to functions:

- admin.ts: Added Alpine for scan, stats, and settings functions
- api-explorer.ts: Already had Alpine (kept as is)
- bookshelf.ts: Added Alpine for library/bookshelf interactions
- collections.ts: Added Alpine for collection management
- conflicts.ts: Added Alpine for conflict resolution
- device-management.ts: Added Alpine with event delegation for dynamic content
- header.ts: Added Alpine for theme dropdown and user menu
- library.ts: Added Alpine registrations
- linking.ts: Added Alpine registrations
- queue.ts: Added Alpine for queue operations
- search.ts: Added Alpine registrations
- themeDropdown.ts: Added Alpine for theme switching

Each module now exports functions both traditionally and via Alpine.global() for template access.
This commit is contained in:
2026-03-08 21:36:24 -04:00
parent dc288e6169
commit 6728ba83a1
12 changed files with 1028 additions and 393 deletions
+84 -70
View File
@@ -1,3 +1,6 @@
import { Alpine } from "./alpine";
import { showToast } from "./toast";
async function loadCollections(): Promise<void> {
const token = localStorage.getItem("token");
if (!token) return;
@@ -109,21 +112,15 @@ async function createRule(
});
if (response.ok) {
if ((window as any).showToast?.success) {
(window as any).showToast.success("Rule created");
}
showToast("Rule created", "success");
loadCollectionRules(collectionId);
} else {
const error = await response.json();
if ((window as any).showToast?.error) {
(window as any).showToast.error(error.error || "Failed to create rule");
}
showToast(error.error || "Failed to create rule", "error");
}
} catch (error) {
console.error("Failed to create rule:", error);
if ((window as any).showToast?.error) {
(window as any).showToast.error("Failed to create rule");
}
showToast("Failed to create rule", "error");
}
}
@@ -143,16 +140,12 @@ async function deleteRule(collectionId: string, ruleId: string): Promise<void> {
);
if (response.ok) {
if ((window as any).showToast?.success) {
(window as any).showToast.success("Rule deleted");
}
showToast("Rule deleted", "success");
loadCollectionRules(collectionId);
}
} catch (error) {
console.error("Failed to delete rule:", error);
if ((window as any).showToast?.error) {
(window as any).showToast.error("Failed to delete rule");
}
showToast("Failed to delete rule", "error");
}
}
@@ -182,9 +175,7 @@ async function testRule(
}
} catch (error) {
console.error("Failed to test rule:", error);
if ((window as any).showToast?.error) {
(window as any).showToast.error("Failed to test rule");
}
showToast("Failed to test rule", "error");
}
}
@@ -201,12 +192,6 @@ function renderTestResults(results: unknown[]): void {
container.innerHTML = `<p class="p-2 text-sm" style="color: var(--text-secondary)">${Array.isArray(results) ? results.length : 0} matching books</p>`;
}
(window as any).loadCollections = loadCollections;
(window as any).loadCollectionRules = loadCollectionRules;
(window as any).createRule = createRule;
(window as any).deleteRule = deleteRule;
(window as any).testRule = testRule;
// Add authorization header to all HTMX requests
function setupHTMXAuth(): void {
document.body.addEventListener("htmx:configRequest", function (evt: Event) {
@@ -240,8 +225,6 @@ function navigateToCollection(element: HTMLElement): void {
}
}
(window as any).navigateToCollection = navigateToCollection;
// ============================================================================
// Collection Modal UI Helpers
// ============================================================================
@@ -311,10 +294,6 @@ if (document.readyState === "loading") {
} else {
initColorSelection();
}
// Export functions for global access
(window as any).selectColor = selectColor;
(window as any).closeCollectionModal = closeCollectionModal;
(window as any).initColorSelection = initColorSelection;
// Initialize icon grid when modal is loaded via HTMX
function setupHTMXModalInit(): void {
@@ -373,7 +352,6 @@ const iconData: Record<string, string[]> = {
"🎮": ["game", "play", "video", "gaming"],
};
// Helper: Get just the emoji list
const allIcons = Object.keys(iconData);
function populateIconGrid(): void {
const iconGrid = document.getElementById("icon-grid");
@@ -468,12 +446,6 @@ function initIconSelection(): void {
selectIcon(iconInput.value);
}
}
// Export for global access
(window as any).selectIcon = selectIcon;
(window as any).filterIcons = filterIcons;
(window as any).showAllIcons = showAllIcons;
(window as any).populateIconGrid = populateIconGrid;
(window as any).initIconSelection = initIconSelection;
// ============================================================================
// Collection Detail Page - TypeScript with WebSocket Support
@@ -575,7 +547,7 @@ function connectWebSocket(): void {
? `Removed ${message.data.count || 0} book(s)`
: "Collection updated";
(window as any).showToast?.(actionText, "info");
showToast(actionText, "info");
// Mitigation: Skip auto-reload if user is actively typing or interacting
const activeElement = document.activeElement;
@@ -738,14 +710,14 @@ function toggleBookSelection(bookId: string): void {
// Add selected books to collection
async function addbooksToAdd(): Promise<void> {
if (booksToAdd.size === 0) {
(window as any).showToast?.("Please select at least one book", "error");
showToast("Please select at least one book", "error");
return;
}
const bookIds = Array.from(booksToAdd);
const token = localStorage.getItem("token");
if (!token) {
(window as any).showToast?.("Authentication required", "error");
showToast("Authentication required", "error");
return;
}
@@ -760,18 +732,15 @@ async function addbooksToAdd(): Promise<void> {
});
if (response.ok) {
(window as any).showToast?.(
`Added ${bookIds.length} book(s) to collection`,
"success",
);
showToast(`Added ${bookIds.length} book(s) to collection`, "success");
hideAddBooksModal();
// Note: WebSocket will trigger page reload automatically
} else {
(window as any).showToast?.("Failed to add books", "error");
showToast("Failed to add books", "error");
}
} catch (error) {
console.error("Add books error:", error);
(window as any).showToast?.("Failed to add books", "error");
showToast("Failed to add books", "error");
}
}
@@ -781,7 +750,7 @@ async function removeBook(bookId: string): Promise<void> {
const token = localStorage.getItem("token");
if (!token) {
(window as any).showToast?.("Authentication required", "error");
showToast("Authentication required", "error");
return;
}
@@ -795,14 +764,14 @@ async function removeBook(bookId: string): Promise<void> {
);
if (response.ok) {
(window as any).showToast?.("Book removed from collection", "success");
showToast("Book removed from collection", "success");
// Note: WebSocket will trigger page reload automatically
} else {
(window as any).showToast?.("Failed to remove book", "error");
showToast("Failed to remove book", "error");
}
} catch (error) {
console.error("Remove book error:", error);
(window as any).showToast?.("Failed to remove book", "error");
showToast("Failed to remove book", "error");
}
}
@@ -837,7 +806,7 @@ function updateSelectedCount(): void {
async function removebooksToAdd(): Promise<void> {
if (booksToRemove.size === 0) {
(window as any).showToast?.("No books selected", "error");
showToast("No books selected", "error");
return;
}
@@ -847,7 +816,7 @@ async function removebooksToAdd(): Promise<void> {
const bookIds = Array.from(booksToRemove);
const token = localStorage.getItem("token");
if (!token) {
(window as any).showToast?.("Authentication required", "error");
showToast("Authentication required", "error");
return;
}
@@ -867,20 +836,20 @@ async function removebooksToAdd(): Promise<void> {
if (response.ok) {
const result = (await response.json()) as { removed: number };
if (result.removed > 0) {
(window as any).showToast?.(
showToast(
`Removed ${result.removed} book(s) from collection`,
"success",
);
// Note: WebSocket will trigger page reload automatically
} else {
(window as any).showToast?.("Failed to remove books", "error");
showToast("Failed to remove books", "error");
}
} else {
(window as any).showToast?.("Failed to remove books", "error");
showToast("Failed to remove books", "error");
}
} catch (error) {
console.error("Bulk remove error:", error);
(window as any).showToast?.("Failed to remove books", "error");
showToast("Failed to remove books", "error");
}
}
@@ -908,23 +877,68 @@ function filterCollectionBooks(): void {
}
}
// Export functions globally
(window as any).initCollectionDetail = initCollectionDetail;
(window as any).showAddBooksModal = showAddBooksModal;
(window as any).hideAddBooksModal = hideAddBooksModal;
(window as any).searchBooksForCollections = searchBooksForCollections;
(window as any).toggleBookSelection = toggleBookSelection;
(window as any).addbooksToAdd = addbooksToAdd;
(window as any).removeBook = removeBook;
(window as any).toggleBookForRemoval = toggleBookForRemoval;
(window as any).updateSelectedCount = updateSelectedCount;
(window as any).removebooksToAdd = removebooksToAdd;
(window as any).filterCollectionBooks = filterCollectionBooks;
(window as any).backToCollections = backToCollections;
// Auto-initialize
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initCollectionDetail);
} else {
initCollectionDetail();
}
// Export functions globally
export {
addbooksToAdd,
backToCollections,
closeCollectionModal,
createRule,
deleteRule,
filterCollectionBooks,
filterIcons,
hideAddBooksModal,
initCollectionDetail,
initColorSelection,
initIconSelection,
loadCollectionRules,
loadCollections,
navigateToCollection,
populateIconGrid,
removeBook,
removebooksToAdd,
searchBooksForCollections,
selectColor,
selectIcon,
showAddBooksModal,
showAllIcons,
testRule,
toggleBookForRemoval,
toggleBookSelection,
updateSelectedCount,
};
Alpine.global("collections", {
addbooksToAdd,
backToCollections,
closeCollectionModal,
createRule,
deleteRule,
filterCollectionBooks,
filterIcons,
hideAddBooksModal,
initCollectionDetail,
initColorSelection,
initIconSelection,
loadCollectionRules,
loadCollections,
navigateToCollection,
populateIconGrid,
removeBook,
removebooksToAdd,
searchBooksForCollections,
selectColor,
selectIcon,
showAddBooksModal,
showAllIcons,
testRule,
toggleBookForRemoval,
toggleBookSelection,
updateSelectedCount,
});