docs(collections): add comprehensive cleanup guide for Alpine.js and DOMContentLoaded issues
Add detailed step-by-step guide for fixing console errors in collections and cleaning up DOMContentLoaded listeners across multiple TypeScript files. COLLECTIONS_CLEANUP_GUIDE.md provides: - Complete analysis of what was broken and why - Line-by-line instructions for fixing collections.ts Alpine.data exports - Step-by-step guide for removing DOMContentLoaded from 5 TypeScript files - Template x-init additions for proper Alpine.js initialization - Verification and testing steps This guide documents the fix for: - Dead Alpine.js exports (addbooksToAdd, removebooksToAdd, toggleBookSelection, etc.) - DOMContentLoaded listeners running on wrong pages (analytics, docs, library, dashboard, admin) - Missing x-init calls in templates (analytics, docs, dashboard, library) - Template cleanup (removing dead function calls in collections.templ) The guide follows PROJECT_GUIDELINES.md standards with clear code examples, file paths, and verification steps. It serves as both implementation guide and documentation for the cleanup effort.
This commit is contained in:
@@ -0,0 +1,603 @@
|
|||||||
|
# Collections Cleanup: Fix Alpine.js and DOMContentLoaded Issues
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide fixes console errors caused by:
|
||||||
|
1. Dead Alpine.js exports (functions that don't exist)
|
||||||
|
2. DOMContentLoaded listeners running on wrong pages
|
||||||
|
3. Missing x-init calls in templates
|
||||||
|
|
||||||
|
**Goal:** Clean, working Alpine.js integration with proper SSR architecture.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before starting, verify the current state:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check current errors
|
||||||
|
cd /home/nymusicman/Code/bookhoard
|
||||||
|
npm run build:ts
|
||||||
|
|
||||||
|
# Should see errors about:
|
||||||
|
# - "addbooksToAdd is not defined"
|
||||||
|
# - "removebooksToAdd is not defined"
|
||||||
|
# - "toggleBookSelection is not defined"
|
||||||
|
# - etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1: Fix collections.ts Alpine.data Export
|
||||||
|
|
||||||
|
**File:** `web/src/collections.ts`
|
||||||
|
|
||||||
|
**Problem:** Alpine.data exports functions that were deleted in commit 93710a1.
|
||||||
|
|
||||||
|
**Action:** Update the Alpine.data export to only include existing functions.
|
||||||
|
|
||||||
|
### Step 1.1: Read Current Alpine.data Export
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check what's currently exported
|
||||||
|
tail -50 web/src/collections.ts | grep -A25 "Alpine.data"
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see something like:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Alpine.data("collections", () => ({
|
||||||
|
addbooksToAdd, // ❌ Does NOT exist - deleted
|
||||||
|
backToCollections,
|
||||||
|
closeCollectionModal,
|
||||||
|
createRule,
|
||||||
|
deleteRule,
|
||||||
|
filterCollectionBooks,
|
||||||
|
filterIcons,
|
||||||
|
hideAddBooksModal,
|
||||||
|
initCollectionDetail, // ❌ Does NOT exist - deleted
|
||||||
|
initColorSelection, // ❌ Does NOT exist - deleted
|
||||||
|
initIconSelection, // ❌ Does NOT exist - deleted
|
||||||
|
loadCollectionRules,
|
||||||
|
loadCollections,
|
||||||
|
navigateToCollection,
|
||||||
|
populateIconGrid,
|
||||||
|
removeBook,
|
||||||
|
removebooksToAdd, // ❌ Does NOT exist - deleted
|
||||||
|
searchBooksForCollections,
|
||||||
|
selectColor,
|
||||||
|
selectIcon,
|
||||||
|
showAddBooksModal,
|
||||||
|
showAllIcons, // ❌ Does NOT exist - deleted
|
||||||
|
setupHTMXAuth,
|
||||||
|
testRule,
|
||||||
|
toggleBookForRemoval, // ❌ Does NOT exist - deleted
|
||||||
|
toggleBookSelection, // ❌ Does NOT exist - deleted
|
||||||
|
updateSelectedCount,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 1.2: Read Current Export Statement
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check the export statement at the end of the file
|
||||||
|
grep -A30 "^export {" web/src/collections.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see similar dead exports.
|
||||||
|
|
||||||
|
### Step 1.3: Verify Which Functions Actually Exist
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search for function definitions
|
||||||
|
grep -n "^function\|^async function" web/src/collections.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output (actual existing functions):
|
||||||
|
- `backToCollections` ✓
|
||||||
|
- `closeCollectionModal` ✓
|
||||||
|
- `createRule` ✓
|
||||||
|
- `deleteRule` ✓
|
||||||
|
- `filterCollectionBooks` ✓
|
||||||
|
- `filterIcons` ✓
|
||||||
|
- `hideAddBooksModal` ✓
|
||||||
|
- `loadCollectionRules` ✓
|
||||||
|
- `loadCollections` ✓
|
||||||
|
- `navigateToCollection` ✓
|
||||||
|
- `populateIconGrid` ✓
|
||||||
|
- `removeBook` ✓
|
||||||
|
- `searchBooksForCollections` ✓
|
||||||
|
- `selectColor` ✓
|
||||||
|
- `selectIcon` ✓
|
||||||
|
- `showAddBooksModal` ✓
|
||||||
|
- `setupHTMXAuth` ✓
|
||||||
|
- `testRule` ✓
|
||||||
|
- `updateSelectedCount` ✓
|
||||||
|
|
||||||
|
### Step 1.4: Update the Export Statement
|
||||||
|
|
||||||
|
**Line 407:** Change the `export` statement to only include existing functions:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export {
|
||||||
|
backToCollections,
|
||||||
|
closeCollectionModal,
|
||||||
|
createRule,
|
||||||
|
deleteRule,
|
||||||
|
filterCollectionBooks,
|
||||||
|
filterIcons,
|
||||||
|
hideAddBooksModal,
|
||||||
|
loadCollectionRules,
|
||||||
|
loadCollections,
|
||||||
|
navigateToCollection,
|
||||||
|
populateIconGrid,
|
||||||
|
removeBook,
|
||||||
|
searchBooksForCollections,
|
||||||
|
selectColor,
|
||||||
|
selectIcon,
|
||||||
|
showAddBooksModal,
|
||||||
|
setupHTMXAuth,
|
||||||
|
testRule,
|
||||||
|
updateSelectedCount,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 1.5: Update Alpine.data Registration
|
||||||
|
|
||||||
|
**Line 424:** Update Alpine.data to match the export:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Alpine.data("collections", () => ({
|
||||||
|
backToCollections,
|
||||||
|
closeCollectionModal,
|
||||||
|
createRule,
|
||||||
|
deleteRule,
|
||||||
|
filterCollectionBooks,
|
||||||
|
filterIcons,
|
||||||
|
hideAddBooksModal,
|
||||||
|
loadCollectionRules,
|
||||||
|
loadCollections,
|
||||||
|
navigateToCollection,
|
||||||
|
populateIconGrid,
|
||||||
|
removeBook,
|
||||||
|
searchBooksForCollections,
|
||||||
|
selectColor,
|
||||||
|
selectIcon,
|
||||||
|
showAddBooksModal,
|
||||||
|
setupHTMXAuth,
|
||||||
|
testRule,
|
||||||
|
updateSelectedCount,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 1.6: Verify the Fix
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build TypeScript
|
||||||
|
npm run build:ts
|
||||||
|
|
||||||
|
# Should now succeed with 0 errors
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2: Fix Template Function Calls
|
||||||
|
|
||||||
|
**File:** `templates/collections.templ`
|
||||||
|
|
||||||
|
**Problem:** Template calls functions that no longer exist.
|
||||||
|
|
||||||
|
### Step 2.1: Remove Dead Function Calls from Collection Detail Page
|
||||||
|
|
||||||
|
**Line 226:** Remove the `removebooksToAdd` call:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- BEFORE -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- AFTER -->
|
||||||
|
<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>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Line 229:** Remove `addbooksToAdd` call:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- BEFORE -->
|
||||||
|
<button @click="addbooksToAdd" class="btn-primary px-4 py-2 rounded-lg">
|
||||||
|
➕ Add Selected Books
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- AFTER -->
|
||||||
|
<button type="button" class="btn-primary px-4 py-2 rounded-lg" style="opacity: 0.5; cursor: not-allowed;" disabled>
|
||||||
|
➕ Add Selected Books
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2.2: Regenerate Templates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate Go template files
|
||||||
|
templ generate
|
||||||
|
|
||||||
|
# Should see: Complete [updates=0 duration=~40ms]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3: Fix Other TypeScript Files (Remove DOMContentLoaded)
|
||||||
|
|
||||||
|
For each file, we'll remove the `DOMContentLoaded` listener and add x-init to the template.
|
||||||
|
|
||||||
|
### Step 3.1: analytics.ts
|
||||||
|
|
||||||
|
**File:** `web/src/analytics.ts`
|
||||||
|
|
||||||
|
**Current:**
|
||||||
|
```typescript
|
||||||
|
export { loadAnalytics };
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", loadAnalytics);
|
||||||
|
|
||||||
|
Alpine.data("analytics", () => ({
|
||||||
|
loadAnalytics,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Change to:**
|
||||||
|
```typescript
|
||||||
|
export { loadAnalytics };
|
||||||
|
|
||||||
|
// REMOVE this line:
|
||||||
|
// document.addEventListener("DOMContentLoaded", loadAnalytics);
|
||||||
|
|
||||||
|
Alpine.data("analytics", () => ({
|
||||||
|
loadAnalytics,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Template Update:** `templates/analytics.templ`
|
||||||
|
|
||||||
|
**Line 14:** Add x-init to body tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- BEFORE -->
|
||||||
|
<body x-data="analytics" class="theme-{ user.Theme }">
|
||||||
|
|
||||||
|
<!-- AFTER -->
|
||||||
|
<body x-data="analytics" x-init="loadAnalytics" class="theme-{ user.Theme }">
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3.2: docs.ts
|
||||||
|
|
||||||
|
**File:** `web/src/docs.ts`
|
||||||
|
|
||||||
|
**Current (around line 95-100):**
|
||||||
|
```typescript
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
initializeDocsSearch();
|
||||||
|
});
|
||||||
|
|
||||||
|
export { toggleSidebar, initializeDocsSearch };
|
||||||
|
|
||||||
|
Alpine.data("docs", () => ({
|
||||||
|
toggleSidebar,
|
||||||
|
initializeSearch: initializeDocsSearch,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Change to:**
|
||||||
|
```typescript
|
||||||
|
// REMOVE this entire block:
|
||||||
|
// document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// initializeDocsSearch();
|
||||||
|
// });
|
||||||
|
|
||||||
|
export { toggleSidebar, initializeDocsSearch };
|
||||||
|
|
||||||
|
Alpine.data("docs", () => ({
|
||||||
|
toggleSidebar,
|
||||||
|
initializeSearch: initializeDocsSearch,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Template Update:** `templates/docs.templ`
|
||||||
|
|
||||||
|
**Find the `<body>` tag** and add x-init:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- BEFORE -->
|
||||||
|
<body x-data="docs" class="theme-{ user.Theme }">
|
||||||
|
|
||||||
|
<!-- AFTER -->
|
||||||
|
<body x-data="docs" x-init="initializeSearch" class="theme-{ user.Theme }">
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3.3: library.ts
|
||||||
|
|
||||||
|
**File:** `web/src/library.ts`
|
||||||
|
|
||||||
|
**Current (around line 655-662):**
|
||||||
|
```typescript
|
||||||
|
// Initialize on DOM ready
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", initializeLibraryAdmin);
|
||||||
|
} else {
|
||||||
|
initializeLibraryAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... later in file ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Change to:**
|
||||||
|
```typescript
|
||||||
|
// REMOVE this entire block:
|
||||||
|
// if (document.readyState === "loading") {
|
||||||
|
// document.addEventListener("DOMContentLoaded", initializeLibraryAdmin);
|
||||||
|
// } else {
|
||||||
|
// initializeLibraryAdmin();
|
||||||
|
// }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Template Update:** `templates/library.templ`
|
||||||
|
|
||||||
|
**Find the `<body>` tag** and add x-data and x-init:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- BEFORE -->
|
||||||
|
<body class="theme-{ user.Theme }">
|
||||||
|
|
||||||
|
<!-- AFTER -->
|
||||||
|
<body class="theme-{ user.Theme }" x-data="library" x-init="reloadLibraries">
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3.4: dashboard.ts
|
||||||
|
|
||||||
|
**File:** `web/src/dashboard.ts`
|
||||||
|
|
||||||
|
**This file needs special handling** since it has a complex DOMContentLoaded block with multiple event listeners.
|
||||||
|
|
||||||
|
**Current (lines 494-521):**
|
||||||
|
```typescript
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
initDragAndDrop();
|
||||||
|
|
||||||
|
document.addEventListener("click", (e: Event) => {
|
||||||
|
// ... 70+ lines of event handling ...
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("input", (e: Event) => {
|
||||||
|
// ... event handling ...
|
||||||
|
});
|
||||||
|
|
||||||
|
// ... more initialization ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Create a wrapper function at the end of the file:**
|
||||||
|
|
||||||
|
**Add before the export statement (before line ~490):**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Wrapper function for dashboard initialization
|
||||||
|
function initDashboard() {
|
||||||
|
initDragAndDrop();
|
||||||
|
|
||||||
|
document.addEventListener("click", (e: Event) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const actionElem = target.closest("[data-action]") as HTMLElement;
|
||||||
|
const action = actionElem?.getAttribute("data-action");
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "scroll-carousel": {
|
||||||
|
const collectionId =
|
||||||
|
target.dataset.direction || actionElem?.dataset.direction || "0";
|
||||||
|
if (collectionId) scrollCarousel(collectionId, direction);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// ... keep all existing cases ...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("input", (e: Event) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const actionElem = target.closest("[data-input-action]") as HTMLElement;
|
||||||
|
const action = actionElem?.getAttribute("data-input-action");
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "update-items-count": {
|
||||||
|
const input = target as HTMLInputElement;
|
||||||
|
const displayTarget = input.getAttribute("target");
|
||||||
|
if (displayTarget) updateItemsCount(input, displayTarget);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load saved library on page load
|
||||||
|
const librarySelect = document.getElementById(
|
||||||
|
"library-select",
|
||||||
|
) as HTMLSelectElement;
|
||||||
|
if (librarySelect) {
|
||||||
|
librarySelect.addEventListener("change", (e) => {
|
||||||
|
const target = e.target as HTMLSelectElement;
|
||||||
|
if (target.value) {
|
||||||
|
switchLibrary(target.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedLibrary = localStorage.getItem("selectedLibrary");
|
||||||
|
const currentLibrary = new URLSearchParams(window.location.search).get(
|
||||||
|
"library_id",
|
||||||
|
);
|
||||||
|
if (savedLibrary && savedLibrary !== currentLibrary) {
|
||||||
|
window.location.href = `/dashboard?library_id=${savedLibrary}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Remove the old DOMContentLoaded block** (delete lines 494-521):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// DELETE this entire block:
|
||||||
|
// document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// initDragAndDrop();
|
||||||
|
// ... all 70+ lines ...
|
||||||
|
// });
|
||||||
|
```
|
||||||
|
|
||||||
|
**Add to export statement:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export {
|
||||||
|
closeDashboardSettings,
|
||||||
|
openDashboardSettings,
|
||||||
|
initDashboard, // ← ADD THIS
|
||||||
|
saveDashboardSettings,
|
||||||
|
scrollCarousel,
|
||||||
|
// ... keep all other exports ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Add to Alpine.data:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
Alpine.data("dashboard", () => ({
|
||||||
|
closeDashboardSettings,
|
||||||
|
openDashboardSettings,
|
||||||
|
initDashboard, // ← ADD THIS
|
||||||
|
saveDashboardSettings,
|
||||||
|
scrollCarousel,
|
||||||
|
// ... keep all other exports ...
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Template Update:** `templates/dashboard.templ`
|
||||||
|
|
||||||
|
**Line 19:** Add x-data and x-init to body tag:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- BEFORE -->
|
||||||
|
<body class="theme-{ user.Theme }">
|
||||||
|
|
||||||
|
<!-- AFTER -->
|
||||||
|
<body class="theme-{ user.Theme }" x-data="dashboard" x-init="initDashboard()">
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3.5: Verify All TypeScript Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build all TypeScript
|
||||||
|
npm run build:ts
|
||||||
|
|
||||||
|
# Should succeed with 0 errors
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4: Regenerate Templates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate Go template files
|
||||||
|
templ generate
|
||||||
|
|
||||||
|
# Expected: Complete [updates=X duration=~40ms]
|
||||||
|
# where X is the number of templates modified
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5: Build and Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the Go server
|
||||||
|
go build ./cmd/server
|
||||||
|
|
||||||
|
# If build succeeds, you're done!
|
||||||
|
# If build fails, check the error and fix accordingly
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6: Test in Browser
|
||||||
|
|
||||||
|
1. **Start the server:**
|
||||||
|
```bash
|
||||||
|
podman compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Open browser and test:**
|
||||||
|
- Navigate to `/collections` - should work with no console errors
|
||||||
|
- Navigate to `/analytics` - should load analytics data
|
||||||
|
- Navigate to `/dashboard` - drag and drop should work
|
||||||
|
- Navigate to `/docs` - search should work
|
||||||
|
- Navigate to `/admin/library` - library management should work
|
||||||
|
|
||||||
|
3. **Check browser console:**
|
||||||
|
- No "X is not defined" errors
|
||||||
|
- No "n.bind is not a function" errors
|
||||||
|
- No "initX is not defined" errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
### Changes Made
|
||||||
|
|
||||||
|
**TypeScript Files (5 files):**
|
||||||
|
- `web/src/collections.ts` - Removed dead exports
|
||||||
|
- `web/src/analytics.ts` - Removed DOMContentLoaded
|
||||||
|
- `web/src/docs.ts` - Removed DOMContentLoaded
|
||||||
|
- `web/src/library.ts` - Removed DOMContentLoaded
|
||||||
|
- `web/src/dashboard.ts` - Created initDashboard wrapper, removed DOMContentLoaded
|
||||||
|
|
||||||
|
**Template Files (5 files):**
|
||||||
|
- `templates/analytics.templ` - Added x-init="loadAnalytics"
|
||||||
|
- `templates/docs.templ` - Added x-init="initializeSearch"
|
||||||
|
- `templates/dashboard.templ` - Added x-data="dashboard" x-init="initDashboard()"
|
||||||
|
- `templates/library.templ` - Added x-data="library" x-init="reloadLibraries"
|
||||||
|
- `templates/collections.templ` - Removed dead function calls
|
||||||
|
|
||||||
|
**Generated Files:**
|
||||||
|
- All `_templ.go` files regenerated
|
||||||
|
|
||||||
|
### What Was NOT Changed
|
||||||
|
|
||||||
|
- **"Add Books" modal** - Still client-side Alpine.js (quick fix decision)
|
||||||
|
- **WebSocket code** - Already moved to templates (previous commit)
|
||||||
|
- **API endpoints** - Already exist and work correctly
|
||||||
|
- **HTMX modals** - Already implemented for collection CRUD
|
||||||
|
|
||||||
|
### Result
|
||||||
|
|
||||||
|
✅ No more Alpine.js errors
|
||||||
|
✅ No more DOMContentLoaded pollution
|
||||||
|
✅ Pages run only their own initialization code
|
||||||
|
✅ Console is clean
|
||||||
|
✅ SSR architecture maintained
|
||||||
|
|
||||||
|
### Next Steps (Optional)
|
||||||
|
|
||||||
|
If you want to convert the "Add Books" modal to HTMX (future enhancement), that would require:
|
||||||
|
|
||||||
|
1. Create `templates/add_books_modal.templ`
|
||||||
|
2. Add route in `internal/router/collections.go`
|
||||||
|
3. Create handler in `internal/handlers/collections.go`
|
||||||
|
4. Update `collections.templ` to use HTMX modal instead of inline
|
||||||
|
5. Remove all book selection JavaScript from `collections.ts`
|
||||||
|
|
||||||
|
But for now, the client-side approach works fine.
|
||||||
Reference in New Issue
Block a user