From 5670f02c7fbd16587720f5fdc45c679af310da87 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Thu, 12 Mar 2026 20:01:43 -0400 Subject: [PATCH] docs: remove redundant COLLECTIONS_CLEANUP_GUIDE.md Delete the standalone cleanup guide as its content has been fully consolidated into ALPINE_COMPLETION_GUIDE.md (Phase 0 and Phase 3). All step-by-step instructions for dead export removal and DOMContentLoaded cleanup are now in the main completion guide, creating a single source of truth for Alpine.js migration. --- COLLECTIONS_CLEANUP_GUIDE.md | 709 ----------------------------------- 1 file changed, 709 deletions(-) delete mode 100644 COLLECTIONS_CLEANUP_GUIDE.md diff --git a/COLLECTIONS_CLEANUP_GUIDE.md b/COLLECTIONS_CLEANUP_GUIDE.md deleted file mode 100644 index 41a71dc..0000000 --- a/COLLECTIONS_CLEANUP_GUIDE.md +++ /dev/null @@ -1,709 +0,0 @@ -# Collections and Alpine.js Cleanup Guide - -## Overview - -This guide provides fixes for Alpine.js issues while maintaining SSR-first architecture. - -**References:** -- **`SSR_FIRST_ALPINE_GUIDE.md`** - Complete SSR-first architecture principles (READ THIS FIRST) -- **`ALPINE_COMPLETION_GUIDE.md`** - Full reactive Alpine.js migration (eventual goal) - -### Problem Statement - -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 -4. **CRITICAL:** x-init functions that fetch data and replace SSR content - -### SSR-First Principles - -**See `SSR_FIRST_ALPINE_GUIDE.md` for complete documentation** - -**Quick Summary:** -- ❌ **NEVER fetch data in x-init** if data is already SSR'd -- ✅ x-init ONLY for setup (event listeners, modals) -- ✅ Data fetch ONLY after user actions (create/delete/update) -- ✅ State lives in template (`x-data`), not in TypeScript - -### Page Types - -**Type 1: 80% SSR** (Collections, Conflicts, Queue) -- Backend provides all data -- Alpine for modals only -- No data fetch in x-init - -**Type 2: SSR + Interactive** (Dashboard, Admin Library) -- Backend provides initial data -- Alpine for interactivity (drag-drop, CRUD) -- x-init sets up listeners only - -**Type 3: 80% JavaScript** (Analytics) -- Backend renders empty shell -- x-init fetches ALL data (intentional) - -### Review Status - -**Pages methodically reviewed so far:** -- ✅ **Dashboard** - Completed -- 🔄 **Collections** - In progress (this guide) -- ⏸️ **Admin Library** - Fixed (see SSR_FIRST_ALPINE_GUIDE.md) -- ⏸️ **Other pages** - Not yet reviewed - -**Goal:** Fix console errors and maintain 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 - SIMPLE SETUP ONLY - -**File:** `web/src/docs.ts` - -**Good news:** `initializeDocsSearch()` ONLY sets up an event listener - no data fetch! - -**Current (around line 95-100):** -```typescript -document.addEventListener("DOMContentLoaded", () => { - initializeDocsSearch(); -}); -``` - -**Solution:** Remove DOMContentLoaded, use x-init - -**Remove DOMContentLoaded:** -```typescript -// DELETE: -// document.addEventListener("DOMContentLoaded", () => { -// initializeDocsSearch(); -// }); - -export { toggleSidebar, initializeDocsSearch }; - -Alpine.data("docs", () => ({ - toggleSidebar, - initializeDocsSearch, // Keep as-is -})); -``` - -**Update template:** -```html - - -``` - -**✅ Simple setup only** -**✅ No data fetch** (search is client-side) -**✅ x-init is appropriate here** - -### Step 3.3: library.ts - ALREADY FIXED - -**Status:** ✅ **COMPLETED** - See commit 1b9bc64 - -**The SSR bug has been fixed:** -- Removed `void reloadLibraries()` from `initializeLibraryAdmin()` -- SSR provides initial library list (no fetch on page load) -- `reloadLibraries()` available for after CRUD operations -- See `SSR_FIRST_ALPINE_GUIDE.md` for complete SSR-first principles - -**Current state (web/src/library.ts:653-655):** -```typescript -function initializeLibraryAdmin(): void { - // Setup event listeners - const librariesList = document.getElementById("libraries-list"); - if (librariesList) { - librariesList.addEventListener("click", handleLibraryListClick); - } - - // ... setup code ... - - // ✅ FIXED: No data fetch - SSR provides initial library list - // reloadLibraries() is called AFTER create/delete/update operations only -} -``` - -**Template (templates/admin_library.templ:11):** -```html - -``` - -**✅ No changes needed** - SSR bug is already fixed. - -### Step 3.4: dashboard.ts - WRAP EXISTING CODE - -**File:** `web/src/dashboard.ts` - -**Good news:** Dashboard already uses event delegation with `data-action` attributes! - -**Current (lines 494-521):** -```typescript -document.addEventListener("DOMContentLoaded", () => { - initDragAndDrop(); - - document.addEventListener("click", (e: Event) => { - // ... event delegation with data-action ... - }); - - // ... library select setup ... -}); -``` - -**Solution:** Wrap existing DOMContentLoaded code in `initDashboard()` function - -**Create wrapper function at end of dashboard.ts:** -```typescript -function initDashboard() { - initDragAndDrop(); // Setup drag-drop - - 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": /* ... */ break; - // ... existing cases ... - } - }); - - document.addEventListener("input", (e: Event) => { - // ... existing input handler ... - }); - - // Load saved library on page load - 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}`; - } -} - -export { initDashboard }; - -Alpine.data("dashboard", () => ({ - initDashboard, -})); -``` - -**Update template:** -```html - - -``` - -**✅ Keeps event delegation pattern** -**✅ Wraps existing code (minimal changes)** -**✅ x-init does NOT fetch data (SSR provides initial dashboard)** -**✅ localStorage redirect is user preference, not data fetch** - -**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 - -### Progress So Far - -**Pages methodically reviewed so far:** -- ✅ **Dashboard** - Completed, working correctly with SSR -- ✅ **Admin Library** - SSR bug fixed (commit 1b9bc64) -- 🔄 **Collections** - In progress (this guide) -- ⏸️ **Other pages** - Not yet reviewed, will be done page-by-page - -### Completed Changes - -**TypeScript Files (3 files):** -- `web/src/collections.ts` - ✅ Removed dead Alpine.js exports -- `web/src/analytics.ts` - ✅ Removed DOMContentLoaded, SSR-first (intentional data fetch) -- `web/src/admin.ts` - ✅ Removed DOMContentLoaded, WebSocket moved to template -- `web/src/library.ts` - ✅ Fixed SSR bug, removed reloadLibraries() from init - -**Template Files (1 file):** -- `templates/analytics.templ` - ✅ Added x-init="loadAnalytics" (JS-heavy page, correct) - -### Pending Fixes - -**Collections (This Guide):** -- Dead exports removed (Step 1) -- DOMContentLoaded cleanup (Step 3) -- x-init calls needed (Step 3) - -**Dashboard:** -- Wrap existing code in `initDashboard()` function -- Add x-init to template -- Already uses event delegation correctly - -**Docs:** -- Remove DOMContentLoaded -- Add x-init="initializeDocsSearch" to template -- Simple setup only, no data fetch - -### What Was NOT Changed - -- **"Add Books" modal** - Still client-side Alpine.js (quick fix decision) -- **WebSocket code** - Already moved to templates with server-side token injection -- **API endpoints** - Already exist and work correctly -- **HTMX modals** - Already implemented for collection CRUD - -### SSR-First Principles - -**See `SSR_FIRST_ALPINE_GUIDE.md` for complete documentation:** - -✅ **Type 1 (80% SSR):** Collections, Conflicts - backend provides data, Alpine for modals only -✅ **Type 2 (SSR + Interactive):** Dashboard, Admin Library - SSR data + Alpine for interactivity -✅ **Type 3 (80% JS):** Analytics - x-init fetches data (intentional) - -**Key Principle:** -- ❌ **NEVER fetch data in x-init** if data is already SSR'd -- ✅ x-init ONLY for setup (event listeners, modals) -- ✅ Data fetch ONLY after user actions (create/delete/update) - -### Next Steps - -1. **Complete collections fixes** (this guide) -2. **Continue page-by-page review** - one page at a time -3. **Reference `SSR_FIRST_ALPINE_GUIDE.md`** for SSR-first principles -4. **Reference `ALPINE_COMPLETION_GUIDE.md`** for full Alpine.js migration (future goal) - -### Architecture - -**State location:** -- Templates: UI state (`x-data`, `x-show`) -- Backend: SSR data -- TypeScript: Business logic only - -**No hybrid approach:** -- ✅ Follow SSR-first principles -- ✅ Full Alpine.js is eventual goal (see ALPINE_COMPLETION_GUIDE.md) -- ✅ Fix issues methodically as you encounter them