diff --git a/ALPINE_GLOBAL_FIX_PART1.md b/ALPINE_GLOBAL_FIX_PART1.md new file mode 100644 index 0000000..fee9d01 --- /dev/null +++ b/ALPINE_GLOBAL_FIX_PART1.md @@ -0,0 +1,387 @@ +# Alpine.js `Alpine.global()` → `Alpine.store()` Migration Guide - Part 1 + +## 🔴 Critical Issue Identified + +**Problem**: Your codebase uses `Alpine.global()` which **DOES NOT EXIST** in Alpine.js v3.15.8. This is causing the error: + +``` +Uncaught TypeError: p.global is not a function +``` + +## 📋 Root Cause Analysis + +1. **Invalid API Usage**: `Alpine.global()` is not a valid Alpine.js v3 method +2. **26 Occurrences**: Found across 26 TypeScript files +3. **Breaking Impact**: Theme switcher and all Alpine namespaces are broken + +## ✅ Solution Overview + +Replace all `Alpine.global()` calls with `Alpine.store()`, the correct Alpine.js v3 API for registering global utilities. + +**Current Pattern (Broken)**: +```typescript +Alpine.global("namespace", { + function1: () => { ... }, + function2: () => { ... } +}); +``` + +**New Pattern (Correct)**: +```typescript +Alpine.store("namespace", { + function1: () => { ... }, + function2: () => { ... } +}); +``` + +**Template Usage Changes**: +- **Old**: `@click="namespace.function()"` → `@click="$store.namespace.function()"` + +--- + +## 📁 Files Requiring Changes (26 TypeScript files) + +### Step 1: Update TypeScript Files + +For each file, replace `Alpine.global()` with `Alpine.store()`: + +| File | Line | Namespace | Action | +|------|------|-----------|--------| +| `admin.ts` | 399 | `admin` | Replace `Alpine.global` with `Alpine.store` | +| `api.ts` | 102 | `api` | Replace `Alpine.global` with `Alpine.store` | +| `api-explorer-docs.ts` | 135 | `apiExplorerDoc` | Replace `Alpine.global` with `Alpine.store` | +| `bookshelf.ts` | 199 | `bookshelf` | Replace `Alpine.global` with `Alpine.store` | +| `collection-rules.ts` | 411 | `collectionRules` | Replace `Alpine.global` with `Alpine.store` | +| `collections.ts` | 917 | `collections` | Replace `Alpine.global` with `Alpine.store` | +| `conflicts.ts` | 219 | `conflicts` | Replace `Alpine.global` with `Alpine.store` | +| `device-management.ts` | 559 | `devices` | Replace `Alpine.global` with `Alpine.store` | +| `docs.ts` | 96, 103 | `docs` | Replace `Alpine.global` with `Alpine.store` (2 occurrences) | +| `header.ts` | 35 | `header` | Replace `Alpine.global` with `Alpine.store` | +| `index.ts` | 46 | `index` | Replace `Alpine.global` with `Alpine.store` | +| `library.ts` | 683 | `library` | Replace `Alpine.global` with `Alpine.store` | +| `linking.ts` | 204 | `linking` | Replace `Alpine.global` with `Alpine.store` | +| `login.ts` | 25 | `login` | Replace `Alpine.global` with `Alpine.store` | +| `password_validation.ts` | 192 | `validation` | Replace `Alpine.global` with `Alpine.store` | +| `profile.ts` | 44 | `profile` | Replace `Alpine.global` with `Alpine.store` | +| `profile-modal.ts` | 29 | `profileModal` | Replace `Alpine.global` with `Alpine.store` | +| `queue.ts` | 167 | `queue` | Replace `Alpine.global` with `Alpine.store` | +| `register.ts` | 14 | `register` | Replace `Alpine.global` with `Alpine.store` | +| `search.ts` | 310 | `search` | Replace `Alpine.global` with `Alpine.store` | +| `themeDropdown.ts` | 24 | `themeDropdown` | Replace `Alpine.global` with `Alpine.store` | +| `toast.ts` | 229 | `showToast` | Replace `Alpine.global` with `Alpine.store` | +| `toast-error.ts` | 33 | `toastError` | Replace `Alpine.global` with `Alpine.store` | +| `unlinked_books.ts` | 430 | `unlinkedBooks` | Replace `Alpine.global` with `Alpine.store` | +| `woodPaneling.ts` | 85 | `woodPaneling` | Replace `Alpine.global` with `Alpine.store` | + +### Step 2: Update Template Files + +For each `.templ` file that uses Alpine namespaces, update the syntax: + +**Old Syntax**: +```html +
+ +
+``` + +**New Syntax**: +```html +
+ +
+``` + +**Templates Requiring Updates** (to be identified by searching for namespace usage): +- All templates using `@click="admin."` +- All templates using `@click="api."` +- All templates using `@click="header."` +- All templates using `@click="showToast."` +- All templates using any other namespace from the list above + +--- + +## 🔧 Detailed Step-by-Step Instructions + +### Phase 1: Update TypeScript Source Files + +#### Step 1.1: Backup Current State +```bash +cd /home/nymusicman/Code/bookhoard +git add -A +git commit -m "Backup before Alpine.global() → Alpine.store() migration" +``` + +#### Step 1.2: Find All `Alpine.global` Occurrences +```bash +cd /home/nymusicman/Code/bookhoard/web/src +grep -rn "Alpine\.global" . | tee alpine-global-occurrences.txt +``` + +Expected output: 26 occurrences across 26 files + +#### Step 1.3: Replace All `Alpine.global` with `Alpine.store` + +**Option A: Manual Replacement (Recommended for Understanding)** +For each file in the table above: +1. Open the file +2. Find the `Alpine.global()` call +3. Replace `Alpine.global` with `Alpine.store` +4. Save the file + +**Example** (`web/src/toast.ts:229`): +```typescript +// BEFORE (Line 229): +Alpine.global("showToast", { + error: (message: string, duration?: number) => + showToast(message, "error", duration), + success: (message: string, duration?: number) => + showToast(message, "success", duration), + info: (message: string, duration?: number) => + showToast(message, "info", duration), +}); + +// AFTER (Line 229): +Alpine.store("showToast", { + error: (message: string, duration?: number) => + showToast(message, "error", duration), + success: (message: string, duration?: number) => + showToast(message, "success", duration), + info: (message: string, duration?: number) => + showToast(message, "info", duration), +}); +``` + +**Option B: Automated Replacement (Faster)** +```bash +cd /home/nymusicman/Code/bookhoard/web/src +find . -name "*.ts" -type f -exec sed -i 's/Alpine\.global(/Alpine.store(/g' {} \; +``` + +⚠️ **Warning**: Automated replacement will change all 26 occurrences at once. Verify with grep first: +```bash +grep -rn "Alpine\.global" . # Should return nothing after replacement +grep -rn "Alpine\.store" . # Should show all 26 occurrences +``` + +#### Step 1.4: Verify Changes +```bash +cd /home/nymusicman/Code/bookhoard/web/src +grep -c "Alpine\.store" *.ts | grep -v ":0" +``` + +Expected: Each of the 26 files should show `1` occurrence (except `docs.ts` which should show `2`) + +#### Step 1.5: Rebuild TypeScript +```bash +cd /home/nymusicman/Code/bookhoard/web +npm run build:ts +``` + +Expected output: +``` +> bookhoard@1.0.0 build:ts +> cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && esbuild web/src/main.ts --bundle --outfile=web/static/main.js --sourcemap --target=es2020 --minify + + web/static/main.js 167.9kb + web/static/main.js.map 538.7kb +⚡ Done in 18ms +``` + +--- + +### Phase 2: Update Template Files + +#### Step 2.1: Find All Namespace Usage in Templates +```bash +cd /home/nymusicman/Code/bookhoard/templates +grep -rn '@click="' . +``` + +This will show all click handlers that use namespaces. Look for patterns like: +- `@click="admin.deleteConfirm"` +- `@click="api.getUrl"` +- `@click="header.changeThemeTo"` +- `@click="showToast.error"` +- etc. + +#### Step 2.2: Create Mapping Document + +Create a text file with the old → new namespace mappings: + +``` +admin.function() → $store.admin.function() +api.function() → $store.api.function() +apiExplorerDoc.function() → $store.apiExplorerDoc.function() +bookshelf.function() → $store.bookshelf.function() +collectionRules.function() → $store.collectionRules.function() +collections.function() → $store.collections.function() +conflicts.function() → $store.conflicts.function() +devices.function() → $store.devices.function() +docs.function() → $store.docs.function() +header.function() → $store.header.function() +index.function() → $store.index.function() +library.function() → $store.library.function() +linking.function() → $store.linking.function() +login.function() → $store.login.function() +validation.function() → $store.validation.function() +profile.function() → $store.profile.function() +profileModal.function() → $store.profileModal.function() +queue.function() → $store.queue.function() +register.function() → $store.register.function() +search.function() → $store.search.function() +themeDropdown.function() → $store.themeDropdown.function() +showToast.function() → $store.showToast.function() +toastError.function() → $store.toastError.function() +unlinkedBooks.function() → $store.unlinkedBooks.function() +woodPaneling.function() → $store.woodPaneling.function() +``` + +#### Step 2.3: Update Templates (Iterative Approach) + +**For each namespace found in templates:** + +1. **Search for all usages**: + ```bash + cd /home/nymusicman/Code/bookhoard/templates + grep -rn 'namespace\.' . | grep "@click" + ``` + Replace `namespace` with the actual namespace name (e.g., `admin`, `header`, etc.) + +2. **Replace in each file** manually or using find/replace: + - Find: `@click="namespace.functionName"` + - Replace: `@click="$store.namespace.functionName"` + +**Example for `header.templ`:** + +**Before**: +```templ +@click="header.changeThemeTo('wood-light')" +@click="header.logout()" +``` + +**After**: +```templ +@click="$store.header.changeThemeTo('wood-light')" +@click="$store.header.logout()" +``` + +#### Step 2.4: Critical Template Priority + +Update templates in this order (most critical first): + +1. **`header.templ`** - Theme switcher (currently broken) +2. **`index.templ`** - Main page +3. **`library.templ`** - Core functionality +4. **`collections.templ`** - Collection management +5. **`bookshelf.templ`** - Book display +6. **`api_explorer.templ`** - API testing +7. **`admin.templ`** - Admin panel +8. **`profile.templ`** - User profile +9. **`login.templ`** - Authentication +10. **`register.templ`** - Registration +11. **All other templates** with namespace usage + +--- + +### Phase 3: Test the Migration + +#### Step 3.1: Rebuild Container +```bash +cd /home/nymusicman/Code/bookhoard +docker-compose build +``` + +#### Step 3.2: Restart Container +```bash +docker-compose down +docker-compose up -d +``` + +#### Step 3.3: Verify Bundle +```bash +podman exec bookhoard grep -c "\.store(" /root/web/static/main.js +``` + +Expected: Should show all `Alpine.store()` calls (not `Alpine.global()`) + +#### Step 3.4: Test Theme Switcher (Critical) +1. Open `http://localhost:8765` in browser +2. Hard refresh: `Ctrl+Shift+R` +3. Open browser console (F12) +4. Click theme dropdown +5. Select a theme +6. **Expected**: Theme changes without error +7. **Expected Console**: + ```javascript + Alpine.store('showToast') // Should return object, not undefined + Alpine.store('header') // Should return object, not undefined + ``` + +#### Step 3.5: Verify No Errors +Check browser console for: +- ✅ No `p.global is not a function` errors +- ✅ No `Uncaught TypeError` messages +- ✅ Alpine version shows: `3.15.8` +- ✅ Stores are accessible via `$store` + +--- + +## 📊 Testing Checklist + +After completing the migration, verify: + +- [ ] No `Alpine.global` in any `.ts` file +- [ ] All 26 `Alpine.store` registrations present +- [ ] Bundle size approximately 168KB +- [ ] Browser console shows no errors +- [ ] Theme switcher works correctly +- [ ] All dropdown menus open/close properly +- [ ] Toast notifications display correctly +- [ ] All forms submit without errors +- [ ] All click handlers work as expected +- [ ] Alpine directives (`x-show`, `@click`, etc.) work correctly + +--- + +## 🐛 Troubleshooting + +### Error: "Cannot read property 'function' of undefined" +**Cause**: Template still using old syntax `namespace.function()` +**Fix**: Change to `$store.namespace.function()` + +### Error: "Alpine.store is not a function" +**Cause**: TypeScript build didn't complete or using cached main.js +**Fix**: Run `npm run build:ts` and rebuild container + +### Error: "p.global is not a function" (still) +**Cause**: Some files still have `Alpine.global()` +**Fix**: Run `grep -rn "Alpine\.global" web/src` to find remaining occurrences + +### Theme switcher still broken +**Cause**: `header.templ` not updated with new syntax +**Fix**: Update all `@click="header."` to `@click="$store.header."` + +--- + +## 📝 Next Steps (After This Guide) + +**Part 2** will cover: +1. Advanced Alpine store patterns +2. Reactive state management with stores +3. Testing strategies for all templates +4. Performance optimization +5. Rollback plan if needed + +--- + +## 🔗 References + +- [Alpine.js Stores Documentation](https://alpinejs.dev/globals/alpine_store.html) +- [Alpine.js Magic Properties ($store)](https://alpinejs.dev/magics/$store.html) +- [Migration from Alpine v2 to v3](https://alpinejs.dev/upgrade-guide) + +--- + +**Created**: 2026-03-09 +**Alpine.js Version**: 3.15.8 +**Status**: Ready for Implementation diff --git a/web/src/main.ts b/web/src/main.ts index 8dbfeab..bf1dbd4 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -1,3 +1,6 @@ +import "./alpine"; +import { Alpine } from "./alpine"; + import "./admin"; import "./api"; import "./analytics"; @@ -23,7 +26,14 @@ import "./profile"; import "./profile-modal"; import "./queue"; import "./register"; -import "./register-alpine"; +import "./search"; import "./storage"; +import "./themeDropdown"; import "./theme"; +import "./toast"; +import "./toast-error"; +import "./unlinked_books"; import "./woodPanelingInit"; +import "./woodPaneling"; + +Alpine.start(); diff --git a/web/src/register-alpine.ts b/web/src/register-alpine.ts deleted file mode 100644 index 910dab7..0000000 --- a/web/src/register-alpine.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Alpine } from "./alpine"; -// Import all modules that register Alpine globals -// These imports trigger their Alpine.global() calls -import "./admin"; -import "./api"; -import "./analytics"; -import "./api-explorer-docs"; -import "./bookshelf"; -import "./collection-rules"; -import "./collections"; -import "./conflicts"; -import "./custom-section-builder"; -import "./dashboard"; -import "./device-management"; -import "./docs"; -import "./header"; -import "./index"; -import "./library"; -import "./login"; -import "./linking"; -import "./password_validation"; -import "./profile"; -import "./profile-modal"; -import "./queue"; -import "./register"; -import "./search"; -import "./themeDropdown"; -import "./toast"; -import "./toast-error"; -import "./unlinked_books"; -import "./woodPaneling"; -// NOW start Alpine after all registrations complete -Alpine.start();