From bfdef1396a5e23c67cb17ec65e8aebf7a2a1a8fa Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Thu, 12 Mar 2026 17:21:28 -0400 Subject: [PATCH] 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. --- COLLECTIONS_CLEANUP_GUIDE.md | 603 +++++++++++++++++++++++++++++++++++ 1 file changed, 603 insertions(+) create mode 100644 COLLECTIONS_CLEANUP_GUIDE.md diff --git a/COLLECTIONS_CLEANUP_GUIDE.md b/COLLECTIONS_CLEANUP_GUIDE.md new file mode 100644 index 0000000..b590a51 --- /dev/null +++ b/COLLECTIONS_CLEANUP_GUIDE.md @@ -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 + + + + + +``` + +**Line 229:** Remove `addbooksToAdd` call: + +```html + + + + + +``` + +### 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 + + + + + +``` + +### 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 `` tag** and add x-init: + +```html + + + + + +``` + +### 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 `` tag** and add x-data and x-init: + +```html + + + + + +``` + +### 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 + + + + + +``` + +### 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.