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:
+84
-70
@@ -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,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user