From 06461a3202163e7fa534dd767fa63e894c81ad16 Mon Sep 17 00:00:00 2001
From: John O'Keefe
Date: Thu, 12 Mar 2026 18:13:12 -0400
Subject: [PATCH] refactor(alpine): migrate remaining pages to x-init
declarative initialization
- Remove DOMContentLoaded event listeners from analytics.ts and docs.ts
- Rely on x-init attribute in templates for page initialization
- Clean up unused exports from collections.ts Alpine data
- Add x-init calls to admin_library, analytics, and docs templates
- Normalize quote style in collections WebSocket script (single to double)
- Disable Add Books button in collection detail (pending implementation)
---
templates/admin_library.templ | 2 +-
templates/admin_library_templ.go | 2 +-
templates/admin_templ.go | 2 +-
templates/analytics_templ.go | 2 +-
templates/collections.templ | 68 +++++++++++++++++---------------
templates/collections_templ.go | 32 +++++++--------
templates/docs.templ | 2 +-
templates/docs_templ.go | 2 +-
web/src/analytics.ts | 2 -
web/src/collections.ts | 28 -------------
web/src/docs.ts | 4 --
11 files changed, 58 insertions(+), 88 deletions(-)
diff --git a/templates/admin_library.templ b/templates/admin_library.templ
index c91d740..522a40e 100644
--- a/templates/admin_library.templ
+++ b/templates/admin_library.templ
@@ -8,7 +8,7 @@ templ AdminLibrary(user User, libraries []LibraryData, users []User) {
Library Management - Bookhoard
-
+
@Header(user, "/admin/library")
@AdminSidebar(user, "/admin/library")
diff --git a/templates/admin_library_templ.go b/templates/admin_library_templ.go
index 967d624..16ebb7f 100644
--- a/templates/admin_library_templ.go
+++ b/templates/admin_library_templ.go
@@ -29,7 +29,7 @@ func AdminLibrary(user User, libraries []LibraryData, users []User) templ.Compon
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
Library Management - Bookhoard")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Library Management - Bookhoard")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/templates/admin_templ.go b/templates/admin_templ.go
index 513ecd6..f75f8d5 100644
--- a/templates/admin_templ.go
+++ b/templates/admin_templ.go
@@ -29,7 +29,7 @@ func Admin(user User) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Admin Dashboard - Bookhoard")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Admin Dashboard - Bookhoard")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/templates/analytics_templ.go b/templates/analytics_templ.go
index bcc65af..5e507e3 100644
--- a/templates/analytics_templ.go
+++ b/templates/analytics_templ.go
@@ -29,7 +29,7 @@ func Analytics(user User) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Reading Analytics - Bookhoard")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Reading Analytics - Bookhoard")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/templates/collections.templ b/templates/collections.templ
index ae67fdb..503b6d8 100644
--- a/templates/collections.templ
+++ b/templates/collections.templ
@@ -18,69 +18,74 @@ templ Collection(user User, collections []CollectionData, errorMessage string) {
let libraryId = null;
function connectWebSocket() {
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
+ const protocol =
+ window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//${window.location.host}/ws/sync?token={ user.Token }`;
ws = new WebSocket(wsUrl);
- ws.onopen = function() {
- console.log('WebSocket connected');
+ ws.onopen = function () {
+ console.log("WebSocket connected");
};
- ws.onmessage = function(event) {
+ ws.onmessage = function (event) {
try {
const message = JSON.parse(event.data);
- if (message.type === 'collection_updated' && message.data.collection_id === collectionId) {
- const actionText = message.data.action === 'books_added'
- ? `Added ${message.data.count || 0} book(s)`
- : message.data.action === 'book_removed'
- ? 'Removed a book'
- : message.data.action === 'books_bulk_removed'
- ? `Removed ${message.data.count || 0} book(s)`
- : 'Collection updated';
+ if (
+ message.type === "collection_updated" &&
+ message.data.collection_id === collectionId
+ ) {
+ const actionText =
+ message.data.action === "books_added"
+ ? `Added ${message.data.count || 0} book(s)`
+ : message.data.action === "book_removed"
+ ? "Removed a book"
+ : message.data.action === "books_bulk_removed"
+ ? `Removed ${message.data.count || 0} book(s)`
+ : "Collection updated";
// Show toast notification
if (window.showToast) {
- window.showToast(actionText, 'info');
+ window.showToast(actionText, "info");
}
// Check if user is actively typing
const activeElement = document.activeElement;
- const isUserActive = activeElement && (
- activeElement.tagName === 'INPUT' ||
- activeElement.tagName === 'TEXTAREA' ||
- activeElement.tagName === 'SELECT' ||
- activeElement.getAttribute('contenteditable') === 'true'
- );
+ const isUserActive =
+ activeElement &&
+ (activeElement.tagName === "INPUT" ||
+ activeElement.tagName === "TEXTAREA" ||
+ activeElement.tagName === "SELECT" ||
+ activeElement.getAttribute("contenteditable") === "true");
if (!isUserActive) {
- setTimeout(function() {
+ setTimeout(function () {
location.reload();
}, 1000);
} else {
- console.log('User actively typing - skipping auto-reload');
+ console.log("User actively typing - skipping auto-reload");
}
}
} catch (error) {
- console.error('Failed to parse WebSocket message:', error);
+ console.error("Failed to parse WebSocket message:", error);
}
};
- ws.onclose = function() {
- console.log('WebSocket disconnected, reconnecting in 5s...');
+ ws.onclose = function () {
+ console.log("WebSocket disconnected, reconnecting in 5s...");
setTimeout(connectWebSocket, 5000);
};
- ws.onerror = function(error) {
- console.error('WebSocket error:', error);
+ ws.onerror = function (error) {
+ console.error("WebSocket error:", error);
};
}
- document.addEventListener('DOMContentLoaded', function() {
+ document.addEventListener("DOMContentLoaded", function () {
// Get collection and library IDs from data attributes
- const dataEl = document.getElementById('collection-data');
+ const dataEl = document.getElementById("collection-data");
if (dataEl) {
- collectionId = dataEl.dataset.id || '';
- libraryId = dataEl.dataset.libraryId || '';
+ collectionId = dataEl.dataset.id || "";
+ libraryId = dataEl.dataset.libraryId || "";
connectWebSocket();
}
});
@@ -220,13 +225,12 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
- Books in this Collection
0 selected")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "
Books in this Collection
0 selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -261,7 +261,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 257, Col: 22}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 261, Col: 22}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
@@ -279,7 +279,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(book.Author)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 264, Col: 27}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 268, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
@@ -302,7 +302,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(book.CoverImagePath)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 272, Col: 36}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 276, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
diff --git a/templates/docs.templ b/templates/docs.templ
index 34fdb87..af5fbf4 100644
--- a/templates/docs.templ
+++ b/templates/docs.templ
@@ -13,7 +13,7 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
-
+
@Header(user, currentPath)
({
diff --git a/web/src/collections.ts b/web/src/collections.ts
index b5f177a..73e8b98 100644
--- a/web/src/collections.ts
+++ b/web/src/collections.ts
@@ -405,60 +405,32 @@ function filterIcons(searchTerm: string): void {
// 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,
setupHTMXAuth,
testRule,
- toggleBookForRemoval,
- toggleBookSelection,
- updateSelectedCount,
};
Alpine.data("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,
}));
diff --git a/web/src/docs.ts b/web/src/docs.ts
index f8a0624..089815b 100644
--- a/web/src/docs.ts
+++ b/web/src/docs.ts
@@ -89,10 +89,6 @@ function performDocsSearch(query: string): void {
}
}
-document.addEventListener("DOMContentLoaded", () => {
- initializeDocsSearch();
-});
-
Alpine.data("docs", () => ({
toggleSidebar,
initializeSearch: initializeDocsSearch,