docs: consolidate Alpine.js cleanup guide into main completion guide
Merge COLLECTIONS_CLEANUP_GUIDE.md into ALPINE_COMPLETION_GUIDE.md to create a single, comprehensive migration guide. This consolidates documentation and reduces redundancy. Changes: - Update guide structure from three guides to two guides - Remove references to COLLECTIONS_CLEANUP_GUIDE.md - Add Phase 0 (dead export removal) with detailed step-by-step instructions - Add Phase 3 (DOMContentLoaded cleanup) with file-by-file instructions - Incorporate detailed fixes for collections.ts, analytics.ts, docs.ts, dashboard.ts, and library.ts - Update all cross-references to point to consolidated guide - Add implementation steps and verification commands Documentation consolidation rationale: - Single source of truth for Alpine.js migration - Eliminates need to reference multiple documents - Maintains all step-by-step instructions in one place - Simplifies maintenance and updates Deleted: COLLECTIONS_CLEANUP_GUIDE.md (content merged into ALPINE_COMPLETION_GUIDE.md)
This commit is contained in:
+595
-97
@@ -11,7 +11,6 @@ This guide completes the migration from **hybrid onclick/@click with manual DOM
|
|||||||
|
|
||||||
**References:**
|
**References:**
|
||||||
- **`SSR_FIRST_ALPINE_GUIDE.md`** - SSR-first architecture principles (READ THIS FIRST)
|
- **`SSR_FIRST_ALPINE_GUIDE.md`** - SSR-first architecture principles (READ THIS FIRST)
|
||||||
- **`COLLECTIONS_CLEANUP_GUIDE.md`** - Immediate console error fixes (quick reference)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -44,7 +43,7 @@ This guide completes the migration from **hybrid onclick/@click with manual DOM
|
|||||||
|
|
||||||
### Documentation Structure
|
### Documentation Structure
|
||||||
|
|
||||||
**Three complementary guides:**
|
**Two complementary guides:**
|
||||||
|
|
||||||
1. **`SSR_FIRST_ALPINE_GUIDE.md`** - **READ THIS FIRST**
|
1. **`SSR_FIRST_ALPINE_GUIDE.md`** - **READ THIS FIRST**
|
||||||
- SSR-first architecture principles
|
- SSR-first architecture principles
|
||||||
@@ -53,15 +52,11 @@ This guide completes the migration from **hybrid onclick/@click with manual DOM
|
|||||||
- Server-side token injection
|
- Server-side token injection
|
||||||
- **Prerequisite for understanding this guide**
|
- **Prerequisite for understanding this guide**
|
||||||
|
|
||||||
2. **`COLLECTIONS_CLEANUP_GUIDE.md`** - Quick reference for immediate fixes
|
2. **`ALPINE_COMPLETION_GUIDE.md`** - **This document**
|
||||||
- Dead export removal (causes console errors)
|
|
||||||
- DOMContentLoaded cleanup (prevents wrong-page execution)
|
|
||||||
- Step-by-step instructions for common fixes
|
|
||||||
- **Use as reference during this migration**
|
|
||||||
|
|
||||||
3. **`ALPINE_COMPLETION_GUIDE.md`** - **This document**
|
|
||||||
- Full reactive Alpine.js migration path
|
- Full reactive Alpine.js migration path
|
||||||
- Eliminate all manual DOM manipulation
|
- Dead export removal (Phase 0)
|
||||||
|
- DOMContentLoaded cleanup (Phase 3)
|
||||||
|
- Template function call fixes
|
||||||
- Complete code examples and patterns
|
- Complete code examples and patterns
|
||||||
- **Long-term architecture goal**
|
- **Long-term architecture goal**
|
||||||
|
|
||||||
@@ -147,55 +142,262 @@ When functions are deleted from TypeScript but remain in `Alpine.data()` exports
|
|||||||
|
|
||||||
These must be fixed before attempting full migration.
|
These must be fixed before attempting full migration.
|
||||||
|
|
||||||
### Quick Reference
|
---
|
||||||
|
|
||||||
For detailed step-by-step instructions, see **`COLLECTIONS_CLEANUP_GUIDE.md`** - **Step 1** covers this process comprehensively.
|
### Step 0.1: Verify Current State
|
||||||
|
|
||||||
### The Process
|
Before starting, check the current errors:
|
||||||
|
|
||||||
**For collections.ts (and similar files):**
|
```bash
|
||||||
|
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 0.2: Fix collections.ts Alpine.data Export
|
||||||
|
|
||||||
|
**File:** `web/src/collections.ts`
|
||||||
|
|
||||||
|
**Problem:** Alpine.data exports functions that were deleted in commit 93710a1.
|
||||||
|
|
||||||
|
#### Step 0.2.1: Read Current Alpine.data Export
|
||||||
|
|
||||||
|
Check what's currently exported:
|
||||||
|
|
||||||
|
```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 0.2.2: Read Current Export Statement
|
||||||
|
|
||||||
|
Check the export statement at the end of the file:
|
||||||
|
|
||||||
|
```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 0.2.3: Verify Which Functions Actually Exist
|
||||||
|
|
||||||
|
Search for function definitions:
|
||||||
|
|
||||||
|
```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 0.2.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 0.2.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 0.2.6: Verify the Fix
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build TypeScript
|
||||||
|
npm run build:ts
|
||||||
|
|
||||||
|
# Should now succeed with 0 errors
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 0.3: Fix Template Function Calls
|
||||||
|
|
||||||
|
**File:** `templates/collections.templ`
|
||||||
|
|
||||||
|
**Problem:** Template calls functions that no longer exist.
|
||||||
|
|
||||||
|
#### Step 0.3.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 0.3.2: Regenerate Templates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate Go template files
|
||||||
|
templ generate
|
||||||
|
|
||||||
|
# Should see: Complete [updates=0 duration=~40ms]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 0.4: Check Other Files for Similar Issues
|
||||||
|
|
||||||
|
Based on commit 93710a1 and current errors:
|
||||||
|
- ✅ `web/src/collections.ts` - Fixed above
|
||||||
|
- Check other files for similar issues as you encounter them
|
||||||
|
|
||||||
|
**General process for any file:**
|
||||||
|
|
||||||
1. **Identify dead exports:**
|
1. **Identify dead exports:**
|
||||||
```bash
|
```bash
|
||||||
# Check what's exported
|
# Check what's exported
|
||||||
grep -A25 "Alpine.data" web/src/collections.ts
|
grep -A25 "Alpine.data" web/src/FILENAME.ts
|
||||||
|
|
||||||
# Find actual function definitions
|
# Find actual function definitions
|
||||||
grep -n "^function\|^async function" web/src/collections.ts
|
grep -n "^function\|^async function" web/src/FILENAME.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Update export statement:**
|
2. **Update export statement** to remove dead functions
|
||||||
```typescript
|
|
||||||
// Remove dead functions from export
|
|
||||||
export {
|
|
||||||
// Keep only existing functions
|
|
||||||
backToCollections,
|
|
||||||
closeCollectionModal,
|
|
||||||
// ... etc ...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Update Alpine.data registration:**
|
3. **Update Alpine.data registration** to remove dead functions
|
||||||
```typescript
|
|
||||||
Alpine.data("collections", () => ({
|
|
||||||
// Keep only existing functions
|
|
||||||
backToCollections,
|
|
||||||
closeCollectionModal,
|
|
||||||
// ... etc ...
|
|
||||||
}));
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Verify:**
|
4. **Update templates** to remove dead function calls
|
||||||
```bash
|
|
||||||
npm run build:ts
|
|
||||||
# Should succeed with 0 errors
|
|
||||||
```
|
|
||||||
|
|
||||||
### Files That Need This Fix
|
5. **Verify:** `npm run build:ts` and `templ generate`
|
||||||
|
|
||||||
Based on commit 93710a1 and current errors:
|
|
||||||
- ✅ `web/src/collections.ts` - Already documented in COLLECTIONS_CLEANUP_GUIDE.md
|
|
||||||
- Check other files for similar issues as you encounter them
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -801,68 +1003,356 @@ Delete `showConflictModal()`, `hideConflictModal()` functions.
|
|||||||
|
|
||||||
**See `SSR_FIRST_ALPINE_GUIDE.md`** for complete SSR-first architecture principles.
|
**See `SSR_FIRST_ALPINE_GUIDE.md`** for complete SSR-first architecture principles.
|
||||||
|
|
||||||
### Quick Reference
|
---
|
||||||
|
|
||||||
For detailed instructions on dashboard, docs, and other pages, see **`COLLECTIONS_CLEANUP_GUIDE.md`** - **Step 3** covers DOMContentLoaded removal.
|
### The Problem
|
||||||
|
|
||||||
### The Pattern
|
**DOMContentLoaded listeners run on EVERY page** due to `main.ts` importing all modules.
|
||||||
|
|
||||||
**Current (WRONG):**
|
**Example:**
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ Runs on EVERY page (main.ts imports all modules)
|
// web/src/admin.ts
|
||||||
document.addEventListener("DOMContentLoaded", initializePage);
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
initializeAdmin(); // Runs on index page!
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
**Solution 1: x-init Wrapper (Current Approach):**
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ Wrap in named function, call via x-init
|
// web/src/main.ts
|
||||||
function initializePage() {
|
import "./admin"; // Imports admin module on ALL pages
|
||||||
|
import "./dashboard"; // Imports dashboard module on ALL pages
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### The Solution: Two Approaches
|
||||||
|
|
||||||
|
#### Approach 1: x-init Wrapper (Current Approach)
|
||||||
|
|
||||||
|
Wrap DOMContentLoaded logic in named function, call via x-init:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// web/src/admin.ts
|
||||||
|
function initializeAdmin() {
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
export { initializePage };
|
Alpine.data("admin", () => ({
|
||||||
|
initializeAdmin,
|
||||||
Alpine.data("page", () => ({
|
|
||||||
initializePage,
|
|
||||||
}));
|
}));
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Template -->
|
<!-- templates/admin.templ -->
|
||||||
<body x-data="page" x-init="initializePage">
|
<body x-data="admin" x-init="initializeAdmin">
|
||||||
```
|
```
|
||||||
|
|
||||||
**Solution 2: Event Delegation Only (Future Goal):**
|
#### Approach 2: Event Delegation Only (Future)
|
||||||
|
|
||||||
|
Remove x-init entirely, rely on global event delegation:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ Rely on global event delegation, no init needed
|
// web/src/admin.ts
|
||||||
// See ALPINE_COMPLETION_GUIDE.md for full migration path
|
// NO init function - use global event delegation
|
||||||
|
|
||||||
|
// Global event listener checks for data-action attributes
|
||||||
|
document.addEventListener("click", (e) => {
|
||||||
|
const action = e.target.closest("[data-action]")?.dataset.action;
|
||||||
|
if (action === "delete-library") deleteLibrary();
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- templates/admin.templ -->
|
||||||
|
<body>
|
||||||
|
<!-- No x-init needed -->
|
||||||
|
<button data-action="delete-library">Delete</button>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Which to Use?**
|
||||||
|
- **Current state:** Use Approach 1 (x-init wrapper)
|
||||||
|
- **Future goal:** Use Approach 2 (event delegation only)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Files Requiring Cleanup
|
### Files Requiring Cleanup
|
||||||
|
|
||||||
**analytics.ts** (Type 3 - 80% JavaScript page):
|
#### 3.1: analytics.ts - ALREADY CORRECT ✅
|
||||||
- ✅ Already correct - uses `x-init="loadAnalytics"`
|
|
||||||
- ✅ Data fetch is intentional for this dynamic page
|
|
||||||
|
|
||||||
**docs.ts** (Type 1 - 80% SSR page):
|
**File:** `web/src/analytics.ts`
|
||||||
- ✅ Remove DOMContentLoaded
|
|
||||||
- ✅ Add `x-init="initializeDocsSearch"` to template
|
|
||||||
- ✅ Simple setup only, no data fetch
|
|
||||||
|
|
||||||
**dashboard.ts** (Type 2 - SSR + Interactive page):
|
**Type:** Type 3 (80% JavaScript page)
|
||||||
- ✅ Wrap existing DOMContentLoaded code in `initDashboard()` function
|
|
||||||
- ✅ Add `x-data="dashboard" x-init="initDashboard"` to template
|
|
||||||
- ✅ Does NOT fetch data on page load (SSR provides initial dashboard)
|
|
||||||
- ✅ Event delegation already in place with `data-action` attributes
|
|
||||||
|
|
||||||
**library.ts** (Type 2 - SSR + Interactive page):
|
**Status:** Already correct - no changes needed
|
||||||
- ✅ Already fixed (commit 1b9bc64)
|
|
||||||
- ✅ Removed `reloadLibraries()` from `initializeLibraryAdmin()`
|
|
||||||
- ✅ SSR provides initial library list
|
|
||||||
|
|
||||||
### Implementation Steps
|
**Current:**
|
||||||
|
```typescript
|
||||||
|
export { loadAnalytics };
|
||||||
|
|
||||||
For each file:
|
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 }">
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ Simple setup only**
|
||||||
|
**✅ Data fetch is intentional** (analytics is a dynamic dashboard)
|
||||||
|
**✅ x-init is appropriate here**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3.2: docs.ts - SIMPLE SETUP ONLY
|
||||||
|
|
||||||
|
**File:** `web/src/docs.ts`
|
||||||
|
|
||||||
|
**Type:** Type 1 (80% SSR page)
|
||||||
|
|
||||||
|
**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
|
||||||
|
<!-- templates/docs.templ -->
|
||||||
|
<body x-data="docs" x-init="initializeDocsSearch" class="theme-{ user.Theme }">
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ Simple setup only**
|
||||||
|
**✅ No data fetch** (search is client-side)
|
||||||
|
**✅ x-init is appropriate here**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3.3: library.ts - ALREADY FIXED ✅
|
||||||
|
|
||||||
|
**File:** `web/src/library.ts`
|
||||||
|
|
||||||
|
**Type:** Type 2 (SSR + Interactive page)
|
||||||
|
|
||||||
|
**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 only
|
||||||
|
- 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
|
||||||
|
<body x-data="library" x-init="initializeLibraryAdmin" class="theme-{ user.Theme }">
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ No changes needed** - SSR bug is already fixed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3.4: dashboard.ts - WRAP EXISTING CODE
|
||||||
|
|
||||||
|
**File:** `web/src/dashboard.ts`
|
||||||
|
|
||||||
|
**Type:** Type 2 (SSR + Interactive page)
|
||||||
|
|
||||||
|
**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": {
|
||||||
|
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}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { initDashboard };
|
||||||
|
|
||||||
|
Alpine.data("dashboard", () => ({
|
||||||
|
initDashboard,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
**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()">
|
||||||
|
```
|
||||||
|
|
||||||
|
**✅ 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**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Implementation Steps (For Each File)
|
||||||
|
|
||||||
|
For each file that needs DOMContentLoaded cleanup:
|
||||||
|
|
||||||
1. **Remove DOMContentLoaded:**
|
1. **Remove DOMContentLoaded:**
|
||||||
```typescript
|
```typescript
|
||||||
@@ -898,6 +1388,22 @@ Alpine.data("page", () => ({
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Summary of Changes
|
||||||
|
|
||||||
|
**Files Updated:**
|
||||||
|
- ✅ `web/src/analytics.ts` - Remove DOMContentLoaded, add x-init
|
||||||
|
- ✅ `web/src/docs.ts` - Remove DOMContentLoaded, add x-init
|
||||||
|
- ✅ `web/src/dashboard.ts` - Wrap existing code in initDashboard()
|
||||||
|
- ✅ `web/src/library.ts` - Already fixed (commit 1b9bc64)
|
||||||
|
|
||||||
|
**Templates Updated:**
|
||||||
|
- ✅ `templates/analytics.templ` - Add x-init="loadAnalytics"
|
||||||
|
- ✅ `templates/docs.templ` - Add x-init="initializeDocsSearch"
|
||||||
|
- ✅ `templates/dashboard.templ` - Add x-data and x-init
|
||||||
|
- ✅ `templates/admin_library.templ` - Already correct
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Phase 4: Verification & Testing
|
## Phase 4: Verification & Testing
|
||||||
|
|
||||||
### For Each Migrated Template
|
### For Each Migrated Template
|
||||||
@@ -1006,9 +1512,9 @@ Add completion note:
|
|||||||
- 60% less TypeScript code (header.ts: 100 → 40 lines)
|
- 60% less TypeScript code (header.ts: 100 → 40 lines)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.3: How This Guide Relates to Others
|
### 4.3: Documentation Guide
|
||||||
|
|
||||||
**Three complementary guides:**
|
**Two complementary guides:**
|
||||||
|
|
||||||
1. **`SSR_FIRST_ALPINE_GUIDE.md`** - **READ THIS FIRST**
|
1. **`SSR_FIRST_ALPINE_GUIDE.md`** - **READ THIS FIRST**
|
||||||
- SSR-first architecture principles
|
- SSR-first architecture principles
|
||||||
@@ -1017,16 +1523,11 @@ Add completion note:
|
|||||||
- Server-side token injection
|
- Server-side token injection
|
||||||
- **Must read before using this guide**
|
- **Must read before using this guide**
|
||||||
|
|
||||||
2. **`COLLECTIONS_CLEANUP_GUIDE.md`** - Quick reference for immediate fixes
|
2. **`ALPINE_COMPLETION_GUIDE.md`** - **This document**
|
||||||
- Dead export removal (Phase 0 prerequisites)
|
|
||||||
- DOMContentLoaded cleanup (Phase 3)
|
|
||||||
- Template regeneration
|
|
||||||
- Build verification steps
|
|
||||||
- **Use as step-by-step reference**
|
|
||||||
|
|
||||||
3. **`ALPINE_COMPLETION_GUIDE.md`** - **This document**
|
|
||||||
- Full reactive Alpine.js migration
|
- Full reactive Alpine.js migration
|
||||||
- Eliminate all manual DOM manipulation
|
- Dead export removal (Phase 0)
|
||||||
|
- DOMContentLoaded cleanup (Phase 3)
|
||||||
|
- Template function call fixes
|
||||||
- Header template reference implementation
|
- Header template reference implementation
|
||||||
- Modal templates migration
|
- Modal templates migration
|
||||||
- **Long-term architecture goal**
|
- **Long-term architecture goal**
|
||||||
@@ -1575,7 +2076,7 @@ export { addSelectedBooks, searchBooksForCollections };
|
|||||||
- Know when to fetch data
|
- Know when to fetch data
|
||||||
|
|
||||||
2. **Fix immediate console errors** (if needed)
|
2. **Fix immediate console errors** (if needed)
|
||||||
- See `COLLECTIONS_CLEANUP_GUIDE.md` Step 1
|
- See this guide, Phase 0: Dead Export Removal
|
||||||
- Remove dead exports
|
- Remove dead exports
|
||||||
- Clean up DOMContentLoaded listeners
|
- Clean up DOMContentLoaded listeners
|
||||||
- Verify builds work
|
- Verify builds work
|
||||||
@@ -1602,12 +2103,9 @@ export { addSelectedBooks, searchBooksForCollections };
|
|||||||
|
|
||||||
### Documentation Strategy
|
### Documentation Strategy
|
||||||
|
|
||||||
**Goal:** Eventually deprecate `COLLECTIONS_CLEANUP_GUIDE.md` once all patterns are understood.
|
|
||||||
|
|
||||||
**Current state:**
|
**Current state:**
|
||||||
- `SSR_FIRST_ALPINE_GUIDE.md` - Architecture principles (permanent reference)
|
- `SSR_FIRST_ALPINE_GUIDE.md` - Architecture principles (permanent reference)
|
||||||
- `ALPINE_COMPLETION_GUIDE.md` - Full migration guide (active use)
|
- `ALPINE_COMPLETION_GUIDE.md` - Complete migration guide with all steps (this document)
|
||||||
- `COLLECTIONS_CLEANUP_GUIDE.md` - Step-by-step fixes (quick reference, will be deprecate)
|
|
||||||
|
|
||||||
### Key Success Factors
|
### Key Success Factors
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user