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.
This commit is contained in:
@@ -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
|
||||
<!-- 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 - 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
|
||||
<!-- 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**
|
||||
|
||||
### 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
|
||||
<body x-data="library" x-init="initializeLibraryAdmin" class="theme-{ user.Theme }">
|
||||
```
|
||||
|
||||
**✅ 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
|
||||
<!-- templates/dashboard.templ line 19 -->
|
||||
<body x-data="dashboard" x-init="initDashboard" class="theme-{ user.Theme }">
|
||||
```
|
||||
|
||||
**✅ 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
|
||||
<!-- 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
|
||||
|
||||
### 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
|
||||
Reference in New Issue
Block a user