@@ -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\t 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 (\n\t\t\t\t\t\t\t\t message.type === \" collection_updated\" &&\n\t\t\t\t\t\t\t\t message.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\t message.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\t activeElement &&\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 {