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)
This commit is contained in:
2026-03-12 18:13:12 -04:00
parent 75c454b661
commit 06461a3202
11 changed files with 58 additions and 88 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ templ AdminLibrary(user User, libraries []LibraryData, users []User) {
<title>Library Management - Bookhoard</title>
<link href="/static/style.css" rel="stylesheet"/>
</head>
<body class="theme-{ user.Theme }">
<body x-data="library" x-init="initializeLibraryAdmin" class="theme-{ user.Theme }">
@Header(user, "/admin/library")
<div class="flex min-h-screen" style="background-color: var(--bg-primary)">
@AdminSidebar(user, "/admin/library")
+1 -1
View File
@@ -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, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Library Management - Bookhoard</title><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-{ user.Theme }\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Library Management - Bookhoard</title><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body x-data=\"library\" x-init=\"initializeLibraryAdmin\" class=\"theme-{ user.Theme }\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+1 -1
View File
@@ -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, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Admin Dashboard - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/main.js\" defer></script><link href=\"/static/style.css\" rel=\"stylesheet\"><script>\n\t\t\t\tlet scanWs = null;\n\t\t\t\tfunction connectScanWebSocket() {\n\t\t\t\t\tconst protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n\t\t\t\t\tconst wsUrl = `${protocol}//${window.location.host}/ws/sync?token={ user.Token }`;\n\t\t\t\t\tscanWs = new WebSocket(wsUrl);\n\n\t\t\t\t\tscanWs.onmessage = function(event) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst message = JSON.parse(event.data);\n\t\t\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\t\t\tcase 'scan_progress':\n\t\t\t\t\t\t\t\t\tupdateScanProgress(message.data);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase 'scan_complete':\n\t\t\t\t\t\t\t\t\tshowScanComplete(message.data);\n\t\t\t\t\t\t\t\t\tstopScanStatusPolling();\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase 'scan_error':\n\t\t\t\t\t\t\t\t\tshowScanError(message.data);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error('Failed to parse WebSocket message:', error);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tscanWs.onerror = function(error) {\n\t\t\t\t\t\tconsole.error('WebSocket error:', error);\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tfunction updateScanProgress(data) {\n\t\t\t\t\tconst progressBar = document.getElementById('scan-progress-bar');\n\t\t\t\t\tif (progressBar) {\n\t\t\t\t\t\tprogressBar.style.width = (data.progress * 100) + '%';\n\t\t\t\t\t}\n\n\t\t\t\t\tconst progressText = document.getElementById('scan-progress-text');\n\t\t\t\t\tif (progressText) {\n\t\t\t\t\t\tprogressText.textContent = `${data.files_scanned} files scanned (${data.new_items} new)`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction showScanComplete(data) {\n\t\t\t\t\tconsole.log('Scan complete:', data);\n\t\t\t\t}\n\n\t\t\t\tfunction showScanError(data) {\n\t\t\t\t\tconsole.error('Scan error:', data);\n\t\t\t\t}\n\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\tconnectScanWebSocket();\n\t\t\t\t});\n\t\t\t</script></head><body class=\"theme-tokyo-night\" x-data=\"admin\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Admin Dashboard - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/main.js\" defer></script><link href=\"/static/style.css\" rel=\"stylesheet\"><script>\n\t\t\t\tlet scanWs = null;\n\t\t\t\tfunction connectScanWebSocket() {\n\t\t\t\t\tconst protocol =\n\t\t\t\t\t\twindow.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\t\t\t\t\tconst wsUrl = `${protocol}//${window.location.host}/ws/sync?token={ user.Token }`;\n\t\t\t\t\tscanWs = new WebSocket(wsUrl);\n\n\t\t\t\t\tscanWs.onmessage = function (event) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst message = JSON.parse(event.data);\n\t\t\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\t\t\tcase \"scan_progress\":\n\t\t\t\t\t\t\t\t\tupdateScanProgress(message.data);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"scan_complete\":\n\t\t\t\t\t\t\t\t\tshowScanComplete(message.data);\n\t\t\t\t\t\t\t\t\tstopScanStatusPolling();\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"scan_error\":\n\t\t\t\t\t\t\t\t\tshowScanError(message.data);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error(\"Failed to parse WebSocket message:\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tscanWs.onerror = function (error) {\n\t\t\t\t\t\tconsole.error(\"WebSocket error:\", error);\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tfunction updateScanProgress(data) {\n\t\t\t\t\tconst progressBar = document.getElementById(\"scan-progress-bar\");\n\t\t\t\t\tif (progressBar) {\n\t\t\t\t\t\tprogressBar.style.width = data.progress * 100 + \"%\";\n\t\t\t\t\t}\n\n\t\t\t\t\tconst progressText = document.getElementById(\"scan-progress-text\");\n\t\t\t\t\tif (progressText) {\n\t\t\t\t\t\tprogressText.textContent = `${data.files_scanned} files scanned (${data.new_items} new)`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction showScanComplete(data) {\n\t\t\t\t\tconsole.log(\"Scan complete:\", data);\n\t\t\t\t}\n\n\t\t\t\tfunction showScanError(data) {\n\t\t\t\t\tconsole.error(\"Scan error:\", data);\n\t\t\t\t}\n\n\t\t\t\tdocument.addEventListener(\"DOMContentLoaded\", function () {\n\t\t\t\t\tconnectScanWebSocket();\n\t\t\t\t});\n\t\t\t</script></head><body x-data=\"admin\" x-init=\"loadWatchStatus()\" class=\"theme-tokyo-night\" x-data=\"admin\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+1 -1
View File
@@ -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, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Reading Analytics - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/main.js\" defer></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body x-data=\"analytics\" class=\"theme-{ user.Theme }\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Reading Analytics - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/main.js\" defer></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body x-data=\"analytics\" x-init=\"loadAnalytics\" class=\"theme-{ user.Theme }\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+36 -32
View File
@@ -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
</div>
<button
id="bulk-remove-btn"
@click="removebooksToAdd"
disabled
class="btn-danger px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed"
>
🗑️ Remove Selected
</button>
<button @click="showAddBooksModal" class="btn-primary px-4 py-2 rounded-lg">
<button type="button" class="btn-primary px-4 py-2 rounded-lg" style="opacity: 0.5; cursor: not-allowed;" disabled>
Add Books
</button>
</div>
+16 -16
View File
@@ -31,7 +31,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Collections - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/main.js\" defer></script><link href=\"/static/style.css\" rel=\"stylesheet\"><script>\n\t\t\t\tlet ws = null;\n\t\t\t\tlet collectionId = null;\n\t\t\t\tlet libraryId = null;\n\n\t\t\t\tfunction connectWebSocket() {\n\t\t\t\t\tconst protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';\n\t\t\t\t\tconst wsUrl = `${protocol}//${window.location.host}/ws/sync?token={ user.Token }`;\n\t\t\t\t\tws = new WebSocket(wsUrl);\n\n\t\t\t\t\tws.onopen = function() {\n\t\t\t\t\t\tconsole.log('WebSocket connected');\n\t\t\t\t\t};\n\n\t\t\t\t\tws.onmessage = function(event) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst message = JSON.parse(event.data);\n\t\t\t\t\t\t\tif (message.type === 'collection_updated' && message.data.collection_id === collectionId) {\n\t\t\t\t\t\t\t\tconst actionText = message.data.action === 'books_added'\n\t\t\t\t\t\t\t\t\t? `Added ${message.data.count || 0} book(s)`\n\t\t\t\t\t\t\t\t\t: message.data.action === 'book_removed'\n\t\t\t\t\t\t\t\t\t? 'Removed a book'\n\t\t\t\t\t\t\t\t\t: message.data.action === 'books_bulk_removed'\n\t\t\t\t\t\t\t\t\t? `Removed ${message.data.count || 0} book(s)`\n\t\t\t\t\t\t\t\t\t: 'Collection updated';\n\n\t\t\t\t\t\t\t\t// Show toast notification\n\t\t\t\t\t\t\t\tif (window.showToast) {\n\t\t\t\t\t\t\t\t\twindow.showToast(actionText, 'info');\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Check if user is actively typing\n\t\t\t\t\t\t\t\tconst activeElement = document.activeElement;\n\t\t\t\t\t\t\t\tconst isUserActive = activeElement && (\n\t\t\t\t\t\t\t\t\tactiveElement.tagName === 'INPUT' ||\n\t\t\t\t\t\t\t\t\tactiveElement.tagName === 'TEXTAREA' ||\n\t\t\t\t\t\t\t\t\tactiveElement.tagName === 'SELECT' ||\n\t\t\t\t\t\t\t\t\tactiveElement.getAttribute('contenteditable') === 'true'\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tif (!isUserActive) {\n\t\t\t\t\t\t\t\t\tsetTimeout(function() {\n\t\t\t\t\t\t\t\t\t\tlocation.reload();\n\t\t\t\t\t\t\t\t\t}, 1000);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tconsole.log('User actively typing - skipping auto-reload');\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error('Failed to parse WebSocket message:', error);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tws.onclose = function() {\n\t\t\t\t\t\tconsole.log('WebSocket disconnected, reconnecting in 5s...');\n\t\t\t\t\t\tsetTimeout(connectWebSocket, 5000);\n\t\t\t\t\t};\n\n\t\t\t\t\tws.onerror = function(error) {\n\t\t\t\t\t\tconsole.error('WebSocket error:', error);\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\t\t\t// Get collection and library IDs from data attributes\n\t\t\t\t\tconst dataEl = document.getElementById('collection-data');\n\t\t\t\t\tif (dataEl) {\n\t\t\t\t\t\tcollectionId = dataEl.dataset.id || '';\n\t\t\t\t\t\tlibraryId = dataEl.dataset.libraryId || '';\n\t\t\t\t\t\tconnectWebSocket();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></head><body x-data=\"collections\" class=\"theme-{ user.Theme }\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Collections - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/main.js\" defer></script><link href=\"/static/style.css\" rel=\"stylesheet\"><script>\n\t\t\t\tlet ws = null;\n\t\t\t\tlet collectionId = null;\n\t\t\t\tlet libraryId = null;\n\n\t\t\t\tfunction connectWebSocket() {\n\t\t\t\t\tconst protocol =\n\t\t\t\t\t\twindow.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\t\t\t\t\tconst wsUrl = `${protocol}//${window.location.host}/ws/sync?token={ user.Token }`;\n\t\t\t\t\tws = new WebSocket(wsUrl);\n\n\t\t\t\t\tws.onopen = function () {\n\t\t\t\t\t\tconsole.log(\"WebSocket connected\");\n\t\t\t\t\t};\n\n\t\t\t\t\tws.onmessage = function (event) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst message = JSON.parse(event.data);\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tmessage.type === \"collection_updated\" &&\n\t\t\t\t\t\t\t\tmessage.data.collection_id === collectionId\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tconst actionText =\n\t\t\t\t\t\t\t\t\tmessage.data.action === \"books_added\"\n\t\t\t\t\t\t\t\t\t\t? `Added ${message.data.count || 0} book(s)`\n\t\t\t\t\t\t\t\t\t\t: message.data.action === \"book_removed\"\n\t\t\t\t\t\t\t\t\t\t\t? \"Removed a book\"\n\t\t\t\t\t\t\t\t\t\t\t: message.data.action === \"books_bulk_removed\"\n\t\t\t\t\t\t\t\t\t\t\t\t? `Removed ${message.data.count || 0} book(s)`\n\t\t\t\t\t\t\t\t\t\t\t\t: \"Collection updated\";\n\n\t\t\t\t\t\t\t\t// Show toast notification\n\t\t\t\t\t\t\t\tif (window.showToast) {\n\t\t\t\t\t\t\t\t\twindow.showToast(actionText, \"info\");\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Check if user is actively typing\n\t\t\t\t\t\t\t\tconst activeElement = document.activeElement;\n\t\t\t\t\t\t\t\tconst isUserActive =\n\t\t\t\t\t\t\t\t\tactiveElement &&\n\t\t\t\t\t\t\t\t\t(activeElement.tagName === \"INPUT\" ||\n\t\t\t\t\t\t\t\t\t\tactiveElement.tagName === \"TEXTAREA\" ||\n\t\t\t\t\t\t\t\t\t\tactiveElement.tagName === \"SELECT\" ||\n\t\t\t\t\t\t\t\t\t\tactiveElement.getAttribute(\"contenteditable\") === \"true\");\n\n\t\t\t\t\t\t\t\tif (!isUserActive) {\n\t\t\t\t\t\t\t\t\tsetTimeout(function () {\n\t\t\t\t\t\t\t\t\t\tlocation.reload();\n\t\t\t\t\t\t\t\t\t}, 1000);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tconsole.log(\"User actively typing - skipping auto-reload\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error(\"Failed to parse WebSocket message:\", error);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\tws.onclose = function () {\n\t\t\t\t\t\tconsole.log(\"WebSocket disconnected, reconnecting in 5s...\");\n\t\t\t\t\t\tsetTimeout(connectWebSocket, 5000);\n\t\t\t\t\t};\n\n\t\t\t\t\tws.onerror = function (error) {\n\t\t\t\t\t\tconsole.error(\"WebSocket error:\", error);\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tdocument.addEventListener(\"DOMContentLoaded\", function () {\n\t\t\t\t\t// Get collection and library IDs from data attributes\n\t\t\t\t\tconst dataEl = document.getElementById(\"collection-data\");\n\t\t\t\t\tif (dataEl) {\n\t\t\t\t\t\tcollectionId = dataEl.dataset.id || \"\";\n\t\t\t\t\t\tlibraryId = dataEl.dataset.libraryId || \"\";\n\t\t\t\t\t\tconnectWebSocket();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t</script></head><body x-data=\"collections\" class=\"theme-{ user.Theme }\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -57,7 +57,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("/collections/" + col.ID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 136, Col: 82}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 141, Col: 82}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
@@ -70,7 +70,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(col.Color)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 140, Col: 30}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 145, Col: 30}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@@ -83,7 +83,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(col.Icon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 143, Col: 41}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 148, Col: 41}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
@@ -96,7 +96,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("/collections/" + col.ID + "/edit-modal")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 146, Col: 60}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 151, Col: 60}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@@ -109,7 +109,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs("/api/collections/" + col.ID + "")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 155, Col: 56}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 160, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@@ -122,7 +122,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(col.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 165, Col: 92}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 170, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
@@ -135,7 +135,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(col.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 166, Col: 86}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 171, Col: 86}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
@@ -190,7 +190,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 183, Col: 27}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 188, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
@@ -211,7 +211,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Icon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 196, Col: 81}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 201, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
@@ -224,7 +224,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 198, Col: 90}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 203, Col: 90}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
@@ -237,13 +237,13 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Description)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 199, Col: 71}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 204, Col: 71}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</p></div></div></div><div class=\"mb-6 flex justify-between items-center\"><div class=\"flex items-center gap-4\"><h2 class=\"text-xl font-semibold\" style=\"color: var(--text-primary)\">Books in this Collection</h2><span id=\"selected-count\" class=\"hidden px-3 py-1 text-sm rounded\" style=\"background-color: var(--accent); color: var(--bg-primary);\">0 selected</span></div><div class=\"flex gap-3\"><div class=\"flex-1 max-w-md\"><input type=\"text\" id=\"collection-search\" placeholder=\"Search within collection...\" onkeyup=\"filterCollectionBooks()\" class=\"w-full px-4 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\"></div><button id=\"bulk-remove-btn\" @click=\"removebooksToAdd\" disabled class=\"btn-danger px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed\">🗑️ Remove Selected</button> <button @click=\"showAddBooksModal\" class=\"btn-primary px-4 py-2 rounded-lg\"> Add Books</button></div></div><div id=\"books-container\" class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</p></div></div></div><div class=\"mb-6 flex justify-between items-center\"><div class=\"flex items-center gap-4\"><h2 class=\"text-xl font-semibold\" style=\"color: var(--text-primary)\">Books in this Collection</h2><span id=\"selected-count\" class=\"hidden px-3 py-1 text-sm rounded\" style=\"background-color: var(--accent); color: var(--bg-primary);\">0 selected</span></div><div class=\"flex gap-3\"><div class=\"flex-1 max-w-md\"><input type=\"text\" id=\"collection-search\" placeholder=\"Search within collection...\" onkeyup=\"filterCollectionBooks()\" class=\"w-full px-4 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\"></div><button id=\"bulk-remove-btn\" disabled class=\"btn-danger px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed\">🗑️ Remove Selected</button> <button type=\"button\" class=\"btn-primary px-4 py-2 rounded-lg\" style=\"opacity: 0.5; cursor: not-allowed;\" disabled> Add Books</button></div></div><div id=\"books-container\" class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6\">")
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 {
+1 -1
View File
@@ -13,7 +13,7 @@ templ DocsLayout(nav Navigation, doc Document, user User, currentPath string) {
<link rel="stylesheet" href="/static/highlight-dark.min.css"/>
<script src="/static/main.js" defer></script>
</head>
<body class={ "theme-" + user.Theme + " page-docs bg-background-primary text-text-primary font-sans antialiased" }>
<body x-data="docs" x-init="initializeSearch" class={ "theme-" + user.Theme + " page-docs bg-background-primary text-text-primary font-sans antialiased" }>
@Header(user, currentPath)
<!-- Mobile Menu Button -->
<button
+1 -1
View File
@@ -53,7 +53,7 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<body class=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<body x-data=\"docs\" x-init=\"initializeSearch\" class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
-2
View File
@@ -122,8 +122,6 @@ function renderPopularBooks(popular: PopularBooksResponse): void {
.join("");
}
document.addEventListener("DOMContentLoaded", loadAnalytics);
export { loadAnalytics };
Alpine.data("analytics", () => ({
-28
View File
@@ -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,
}));
-4
View File
@@ -89,10 +89,6 @@ function performDocsSearch(query: string): void {
}
}
document.addEventListener("DOMContentLoaded", () => {
initializeDocsSearch();
});
Alpine.data("docs", () => ({
toggleSidebar,
initializeSearch: initializeDocsSearch,