CRITICAL FIX: initializeLibraryAdmin() was calling reloadLibraries()
which fetched data from the API and replaced the SSR-rendered library
list on page load, defeating the purpose of server-side rendering.
Changes in web/src/library.ts:
- Remove DOMContentLoaded listener (now uses Alpine x-init in template)
- Remove void reloadLibraries() call from initializeLibraryAdmin()
- Add comment explaining SSR provides initial data
- Add initializeLibraryAdmin to export statement
- Add initializeLibraryAdmin to Alpine.data() registration
- Keep reloadLibraries() as standalone function for use after CRUD ops
Rationale:
- SSR provides fast initial page load with library list
- x-init should ONLY setup event listeners, not fetch data
- reloadLibraries() is called after create/delete/update operations
- Follows SSR-first architecture: different pages have different
SSR/JS ratios (analytics is 80% JS, most pages are 80% SSR)
Documentation:
- Update COLLECTIONS_CLEANUP_GUIDE.md with SSR-first strategy
- Document page-by-page review status (dashboard ✓, collections 🔄)
- Fix template references (library.templ → admin_library.templ)
- Explain why analytics fetches data (intentional for dynamic page)
This ensures the admin library page maintains SSR benefits while
still providing interactive features via Alpine.js.
675 lines
17 KiB
Markdown
675 lines
17 KiB
Markdown
# Alpine.js and SSR Architecture Cleanup Guide
|
||
|
||
## Overview
|
||
|
||
This guide provides a systematic, page-by-page approach to fixing Alpine.js issues while maintaining SSR-first architecture.
|
||
|
||
### 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 Alpine.js Strategy
|
||
|
||
**Different pages have different SSR/JavaScript ratios:**
|
||
|
||
- **Analytics Page (80% JavaScript):** Dynamic charts/data that fetch on page load
|
||
- ✅ `x-init="loadAnalytics"` fetches data and replaces content
|
||
- ✅ This is intentional - analytics is a JavaScript-heavy page
|
||
|
||
- **Dashboard Page (SSR + Interactive):** SSR for speed, JS for drag-drop/library switching
|
||
- ✅ SSR provides initial dashboard data
|
||
- ✅ x-init ONLY sets up event listeners (drag-drop, modals)
|
||
- ✅ x-init does NOT fetch data (data already SSR'd)
|
||
- ✅ `switchLibrary()` fetches only when user changes library dropdown
|
||
|
||
- **Admin Library Page (SSR + Modals/CRUD):** SSR list, JS for management
|
||
- ✅ SSR provides initial library list
|
||
- ✅ x-init ONLY sets up event listeners and modals
|
||
- ❌ BUG: `initializeLibraryAdmin()` was calling `reloadLibraries()` which fetches data
|
||
- ❌ This replaces the SSR content on page load - MUST FIX
|
||
|
||
- **Most Pages (80% SSR):** Server-rendered with interactive areas
|
||
- ✅ Use x-init for setup only (event listeners, modals)
|
||
- ❌ NEVER fetch data in x-init if data is already SSR'd
|
||
|
||
**Key Principle:**
|
||
- ✅ DO in x-init: Set up event listeners, initialize modals, setup drag-drop
|
||
- ❌ DON'T in x-init: Fetch data via API, replace SSR innerHTML
|
||
|
||
### Review Status
|
||
|
||
**Pages methodically reviewed so far:**
|
||
- ✅ **Dashboard** - Completed, working correctly
|
||
- 🔄 **Collections** - In progress (fixes in progress)
|
||
- ⏸️ **Admin Library** - Identified bug, fix documented in this guide
|
||
- ⏸️ **Other pages** - Not yet reviewed, will be done page-by-page
|
||
|
||
**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 - SSR-FIRST FIX REQUIRED
|
||
|
||
**⚠️ CRITICAL SSR BUG:** The `initializeLibraryAdmin()` function currently calls `reloadLibraries()` which fetches data from the API and replaces the SSR-rendered library list. This defeats SSR for the admin library page!
|
||
|
||
**File:** `web/src/library.ts`
|
||
|
||
**Problem (around line 653-655):**
|
||
```typescript
|
||
function initializeLibraryAdmin(): void {
|
||
// Setup event listeners
|
||
const librariesList = document.getElementById("libraries-list");
|
||
if (librariesList) {
|
||
librariesList.addEventListener("click", handleLibraryListClick);
|
||
}
|
||
|
||
// ... more setup ...
|
||
|
||
// ❌ BUG: This fetches data and replaces SSR content on page load!
|
||
void reloadLibraries();
|
||
}
|
||
```
|
||
|
||
**Fix - Remove data fetch from init:**
|
||
```typescript
|
||
function initializeLibraryAdmin(): void {
|
||
// Setup event listeners
|
||
const librariesList = document.getElementById("libraries-list");
|
||
if (librariesList) {
|
||
librariesList.addEventListener("click", handleLibraryListClick);
|
||
}
|
||
|
||
// ... keep all existing setup code ...
|
||
|
||
// ✅ REMOVED: void reloadLibraries();
|
||
// SSR provides initial library list - no need to fetch on page load
|
||
// reloadLibraries() is still available to call AFTER create/delete/update operations
|
||
}
|
||
```
|
||
|
||
**Why this fix matters:**
|
||
- SSR renders the library list on the server for fast initial load
|
||
- Calling `reloadLibraries()` in x-init replaces this with a slower API call
|
||
- `reloadLibraries()` should ONLY be called after CRUD operations (create/delete/update)
|
||
- The `initializeLibraryAdmin()` should ONLY setup event listeners and modals
|
||
|
||
**Template Update:** `templates/admin_library.templ`
|
||
|
||
**Current state (line 11):**
|
||
```html
|
||
<body x-data="library" x-init="initializeLibraryAdmin" class="theme-{ user.Theme }">
|
||
```
|
||
|
||
**Status:** ✅ Already correct - template already has x-data and x-init setup
|
||
**Action:** No template change needed - just fix library.ts to not fetch data in init
|
||
|
||
### 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
|
||
|
||
### Progress So Far
|
||
|
||
**Pages methodically reviewed:**
|
||
- ✅ **Dashboard** - Completed (drag-drop works, SSR maintained)
|
||
- 🔄 **Collections** - In progress (dead exports removed, more fixes needed)
|
||
- ⏸️ **Admin Library** - SSR bug identified, fix documented in Step 3.3
|
||
- ⏸️ **All 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
|
||
|
||
**Template Files (1 file):**
|
||
- `templates/analytics.templ` - ✅ Added x-init="loadAnalytics" (JS-heavy page, correct)
|
||
|
||
### Pending Critical Fixes
|
||
|
||
**SSR Bug in library.ts (Step 3.3):**
|
||
- ❌ `initializeLibraryAdmin()` calls `reloadLibraries()` which fetches data and replaces SSR
|
||
- ✅ Fix documented: Remove data fetch from init, only setup event listeners
|
||
- ⚠️ Template already correct: `templates/admin_library.templ` has x-data/x-init
|
||
|
||
### 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
|
||
- **Search** - Skipped pending user's planned revamp
|
||
|
||
### SSR-First Principles Applied
|
||
|
||
✅ **Analytics (80% JS):** x-init fetches data - intentional for dynamic page
|
||
✅ **Dashboard (SSR + JS):** x-init ONLY sets up event listeners, no data fetch
|
||
✅ **Admin Library:** Will fix to only setup event listeners, SSR provides data
|
||
✅ **Collections:** SSR provides data, JS for interactivity (in progress)
|
||
|
||
### Result So Far
|
||
|
||
✅ Collections dead exports removed
|
||
✅ Analytics loads correctly with SSR-first approach
|
||
✅ Admin WebSocket fixed with server-side token injection
|
||
⏳ Admin library SSR bug identified, fix ready to implement
|
||
⏳ Other pages will be reviewed methodically, one by one
|
||
|
||
### Next Steps
|
||
|
||
**Immediate (Critical SSR Bug):**
|
||
1. Fix `web/src/library.ts` Step 3.3 - Remove `reloadLibraries()` from `initializeLibraryAdmin()`
|
||
2. Test admin library page to verify SSR content is not replaced
|
||
|
||
**Continue Page-by-Page Review:**
|
||
3. Complete collections page fixes
|
||
4. Review and fix remaining pages one at a time
|
||
5. Update this guide as each page is completed
|