build: compile templates and JavaScript after theme fixes
- Regenerate admin_library_templ.go with admin sidebar integration - Regenerate admin_users_templ.go with sidebar layout and theme fixes - Regenerate bookshelf_templ.go with dynamic theme rendering - Regenerate index_templ.go with progressive enhancement script - Regenerate login_templ.go with progressive enhancement script - Regenerate register_templ.go with progressive enhancement script - Compile header.js with consolidated theme logic
This commit is contained in:
@@ -1,318 +0,0 @@
|
||||
# Admin Panel Sidebar Reusable Component Plan
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The admin panel sidebar is currently implemented with code duplication and inconsistencies:
|
||||
|
||||
### Current Issues
|
||||
|
||||
1. **Code Duplication**: Sidebar markup is duplicated across templates:
|
||||
- `admin.templ` (lines 17-32)
|
||||
- `admin_library.templ` (lines 14-29)
|
||||
|
||||
2. **Inconsistent Navigation Links**:
|
||||
- `admin.templ`: Dashboard → **"User Administration"** → Library Management
|
||||
- `admin_library.templ`: Dashboard → **"Profile Settings"** → Library Management
|
||||
- "Profile Settings" link points to `/admin/profile` (doesn't exist)
|
||||
- "User Administration" link points to `/admin/users` (correct)
|
||||
|
||||
3. **Missing Sidebar**: `admin_users.templ` has **no sidebar at all** - only uses main `@Header` component
|
||||
|
||||
4. **Inconsistent Layouts**:
|
||||
- `admin.templ` & `admin_library.templ`: Full sidebar layout with `<aside>` + `<main>` in flex container
|
||||
- `admin_users.templ`: Regular layout without sidebar, just `@Header` + `<main>`
|
||||
|
||||
### Root Cause
|
||||
|
||||
Each admin template implements its own sidebar inline instead of using a shared component, leading to:
|
||||
- Maintenance burden (changes require updating multiple files)
|
||||
- Inconsistency (different links, different styling)
|
||||
- Missing features (admin_users has no sidebar)
|
||||
|
||||
---
|
||||
|
||||
## Solution Strategy
|
||||
|
||||
Create a reusable `AdminSidebar` component that:
|
||||
- Can be included in all admin pages with a single line
|
||||
- Accepts `user` and `currentPath` parameters
|
||||
- Automatically highlights the active page
|
||||
- Provides consistent, maintainable navigation
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Create AdminSidebar Component
|
||||
|
||||
**New file**: `templates/admin_sidebar.templ`
|
||||
|
||||
```templ
|
||||
package templates
|
||||
|
||||
templ AdminSidebar(user User, currentPath string) {
|
||||
<aside class="w-64 border-r" style="background-color: var(--bg-secondary); border-color: var(--border)">
|
||||
<div class="p-6">
|
||||
<h2 class="text-lg font-semibold mb-6" style="color: var(--text-primary)">Admin Panel</h2>
|
||||
<nav class="space-y-2">
|
||||
<a href="/admin"
|
||||
class={"block px-4 py-2 rounded-lg " +
|
||||
("bg-accent text-bg-primary" if currentPath == "/admin" else "hover:opacity-80")}
|
||||
style="color: var(--text-primary)">
|
||||
🏠 Dashboard
|
||||
</a>
|
||||
<a href="/admin/users"
|
||||
class={"block px-4 py-2 rounded-lg " +
|
||||
("bg-accent text-bg-primary" if currentPath == "/admin/users" else "hover:opacity-80")}
|
||||
style="color: var(--text-primary)">
|
||||
👤 User Administration
|
||||
</a>
|
||||
<a href="/admin/library"
|
||||
class={"block px-4 py-2 rounded-lg " +
|
||||
("bg-accent text-bg-primary" if currentPath == "/admin/library" else "hover:opacity-80")}
|
||||
style="color: var(--text-primary)">
|
||||
📚 Library Management
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
}
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- Single source of truth for admin navigation
|
||||
- Dynamic active state highlighting based on `currentPath`
|
||||
- Consistent link labels and destinations
|
||||
- Icon + text format for clarity
|
||||
- No code duplication
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Update admin.templ
|
||||
|
||||
**File**: `templates/admin.templ`
|
||||
|
||||
**Replace sidebar section (lines 17-32) with component call:**
|
||||
|
||||
**Before:**
|
||||
```templ
|
||||
<aside class="w-64 border-r" style="background-color: var(--bg-secondary); border-color: var(--border)">
|
||||
<div class="p-6">
|
||||
<h2 class="text-lg font-semibold mb-6" style="color: var(--text-primary)">Admin Panel</h2>
|
||||
<nav class="space-y-2">
|
||||
<a href="/admin" class="block px-4 py-2 rounded-lg bg-accent text-bg-primary" style="color: var(--text-primary)">
|
||||
🏠 Dashboard
|
||||
</a>
|
||||
<a href="/admin/users" class="block px-4 py-2 rounded-lg hover:opacity-80" style="color: var(--text-primary)">
|
||||
👤 User Administration
|
||||
</a>
|
||||
<a href="/admin/library" class="block px-4 py-2 rounded-lg hover:opacity-80" style="color: var(--text-primary)">
|
||||
📚 Library Management
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```templ
|
||||
@AdminSidebar(user, "/admin")
|
||||
```
|
||||
|
||||
**Also update theme on line 13:**
|
||||
```templ
|
||||
<body class="theme-{ user.Theme }">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Update admin_library.templ
|
||||
|
||||
**File**: `templates/admin_library.templ`
|
||||
|
||||
**Replace sidebar section (lines 14-29) with component call:**
|
||||
|
||||
**Before:**
|
||||
```templ
|
||||
<aside class="w-64 border-r" style="background-color: var(--bg-secondary); border-color: var(--border)">
|
||||
<div class="p-6">
|
||||
<h2 class="text-lg font-semibold mb-6" style="color: var(--text-primary)">Admin Panel</h2>
|
||||
<nav class="space-y-2">
|
||||
<a href="/admin" class="block px-4 py-2 rounded-lg hover:opacity-80" style="color: var(--text-primary)">
|
||||
🏠 Dashboard
|
||||
</a>
|
||||
<a href="/admin/profile" class="block px-4 py-2 rounded-lg hover:opacity-80" style="color: var(--text-primary)">
|
||||
👤 Profile Settings
|
||||
</a>
|
||||
<a href="/admin/library" class="block px-4 py-2 rounded-lg bg-accent text-bg-primary" style="color: var(--text-primary)">
|
||||
📚 Library Management
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```templ
|
||||
@AdminSidebar(user, "/admin/library")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Add Sidebar to admin_users.templ
|
||||
|
||||
**File**: `templates/admin_users.templ`
|
||||
|
||||
**Current state**: Page has no sidebar, just `@Header` and `<main>`
|
||||
|
||||
**Add sidebar layout wrapper:**
|
||||
|
||||
**Before:**
|
||||
```templ
|
||||
<body class="theme-{ currentUser.Theme }">
|
||||
@Header(currentUser, "/admin/users")
|
||||
|
||||
<!-- Modal Container -->
|
||||
<div id="modal-container"></div>
|
||||
|
||||
<main class="max-w-7xl mx-auto px-4 py-8">
|
||||
```
|
||||
|
||||
**After:**
|
||||
```templ
|
||||
<body class="theme-{ currentUser.Theme }">
|
||||
@Header(currentUser, "/admin/users")
|
||||
|
||||
<!-- Modal Container -->
|
||||
<div id="modal-container"></div>
|
||||
|
||||
<div class="flex min-h-screen" style="background-color: var(--bg-primary)">
|
||||
@AdminSidebar(currentUser, "/admin/users")
|
||||
|
||||
<main class="flex-1 p-8">
|
||||
<div class="max-w-7xl">
|
||||
```
|
||||
|
||||
**Also need to close the new div wrapper at the end of the file (before closing `</body>`):**
|
||||
```templ
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Fix Layout Inconsistency in admin_users.templ
|
||||
|
||||
**File**: `templates/admin_users.templ`
|
||||
|
||||
**Remove max-width and padding from main element (now handled by sidebar layout):**
|
||||
|
||||
Current line 21:
|
||||
```templ
|
||||
<main class="max-w-7xl mx-auto px-4 py-8">
|
||||
```
|
||||
|
||||
Change to:
|
||||
```templ
|
||||
<main class="flex-1 p-8">
|
||||
<div class="max-w-7xl">
|
||||
```
|
||||
|
||||
**This wraps the content in a max-width container to match other admin pages.**
|
||||
|
||||
---
|
||||
|
||||
## Navigation Links Decision
|
||||
|
||||
**DECISION: Use exact links from `admin.templ`**
|
||||
|
||||
The AdminSidebar component will use the same 3 navigation links as `admin.templ`:
|
||||
|
||||
1. 🏠 **Dashboard** → `/admin`
|
||||
2. 👤 **User Administration** → `/admin/users`
|
||||
3. 📚 **Library Management** → `/admin/library`
|
||||
|
||||
**Rationale:**
|
||||
- These links already exist and work correctly
|
||||
- Consistent with current admin.templ implementation
|
||||
- "Profile Settings" link in admin_library.templ pointed to non-existent `/admin/profile`
|
||||
- Clean, functional navigation without broken links
|
||||
|
||||
---
|
||||
|
||||
## Benefits of This Approach
|
||||
|
||||
1. **Maintainability**: Update sidebar in one place, all pages benefit
|
||||
2. **Consistency**: All admin pages have identical navigation
|
||||
3. **Active States**: Automatic highlighting of current page
|
||||
4. **DRY Principle**: No code duplication
|
||||
5. **Scalability**: Easy to add new admin pages - just include component
|
||||
6. **User Experience**: Clear visual indication of current location
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
After implementation:
|
||||
|
||||
- [ ] Visit `/admin` → Dashboard link highlighted
|
||||
- [ ] Visit `/admin/users` → User Administration link highlighted
|
||||
- [ ] Visit `/admin/library` → Library Management link highlighted
|
||||
- [ ] Click each link → navigates to correct page
|
||||
- [ ] All three pages have consistent sidebar appearance
|
||||
- [ ] admin_users page now has sidebar (previously missing)
|
||||
- [ ] No broken links (removed /admin/profile)
|
||||
- [ ] Hover states work on all links
|
||||
- [ ] Active state has different styling (bg-accent)
|
||||
- [ ] Layout consistent across all admin pages
|
||||
- [ ] Templates compile successfully
|
||||
|
||||
---
|
||||
|
||||
## Files Summary
|
||||
|
||||
### New Files Created
|
||||
- `templates/admin_sidebar.templ` - Reusable admin sidebar component
|
||||
|
||||
### Files Modified
|
||||
- `templates/admin.templ` - Replace inline sidebar with component, fix theme
|
||||
- `templates/admin_library.templ` - Replace inline sidebar with component
|
||||
- `templates/admin_users.templ` - Add sidebar (previously missing), fix layout wrapper
|
||||
|
||||
---
|
||||
|
||||
## Migration Notes
|
||||
|
||||
### Breaking Changes
|
||||
- None - purely refactoring, same functionality
|
||||
|
||||
### Backward Compatibility
|
||||
- Full - all URLs and functionality remain the same
|
||||
|
||||
### Database Dependencies
|
||||
- None
|
||||
|
||||
### Performance
|
||||
- No impact - same HTML rendered, just defined once instead of duplicated
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise:
|
||||
1. Revert individual template changes
|
||||
2. Delete `admin_sidebar.templ`
|
||||
3. Each admin page returns to its previous standalone state
|
||||
4. No data loss (purely visual component refactoring)
|
||||
|
||||
---
|
||||
|
||||
## Future Improvements
|
||||
|
||||
1. **Collapsible sidebar**: Add toggle to hide/show sidebar
|
||||
2. **Badge notifications**: Show counts (e.g., "3 pending users")
|
||||
3. **Dropdown menus**: Group related admin functions
|
||||
4. **Permission-based links**: Hide/show links based on user role
|
||||
5. **Breadcrumbs**: Add breadcrumb navigation for admin section
|
||||
6. **Additional admin pages**: Easy to extend - just include `@AdminSidebar` with new path
|
||||
@@ -29,7 +29,7 @@ func AdminLibrary(user User, libraries []LibraryData, users []User) templ.Compon
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Library Management - Bookhoard</title><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Library Management - Bookhoard</title><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-{ user.Theme }\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -191,7 +191,7 @@ func AdminLibrary(user User, libraries []LibraryData, users []User) templ.Compon
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</select></div><div id=\"user-libraries\" class=\"space-y-3\"><!-- User library checkboxes will be loaded here --></div></div></div></main></div><!-- Create Library Modal --><div id=\"create-library-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"card rounded-lg p-6 w-full max-w-md mx-4\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-6\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Create Library</h2><button type=\"button\" data-action=\"hide-create-modal\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><form id=\"create-library-form\"><input type=\"hidden\" id=\"library-id\" name=\"id\"><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-primary)\">Library Name</label> <input type=\"text\" name=\"name\" placeholder=\"My Ebook Library\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-primary)\">Description</label> <textarea name=\"description\" placeholder=\"Optional description\" rows=\"3\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\"></textarea></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-primary)\">Library Type</label> <select name=\"type\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required><option value=\"ebooks\">📚 Ebooks</option> <option value=\"comics\">📖 Comics</option> <option value=\"manga\">🗾 Manga</option></select></div><div class=\"flex justify-end space-x-3\"><button type=\"button\" data-action=\"hide-create-modal\" class=\"btn-secondary px-4 py-2 rounded-lg\">Cancel</button> <button type=\"submit\" class=\"btn-primary px-4 py-2 rounded-lg\">Create</button></div></form></div></div><!-- Folder Browser Modal --><div id=\"folder-browser-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"card rounded-lg p-6 w-full max-w-md mx-4\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-4\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Browse Folders</h2><button type=\"button\" data-action=\"browse-cancel\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><div id=\"folder-browser-content\"><!-- Directory listings will be rendered here --></div></div></div><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/api.js\"></script><script src=\"/static/theme.js\"></script><script src=\"/static/library.js\"></script></body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</select></div><div id=\"user-libraries\" class=\"space-y-3\"><!-- User library checkboxes will be loaded here --></div></div></div></main></div><!-- Create Library Modal --><div id=\"create-library-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"card rounded-lg p-6 w-full max-w-md mx-4\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-6\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Create Library</h2><button type=\"button\" data-action=\"hide-create-modal\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><form id=\"create-library-form\"><input type=\"hidden\" id=\"library-id\" name=\"id\"><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-primary)\">Library Name</label> <input type=\"text\" name=\"name\" placeholder=\"My Ebook Library\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-primary)\">Description</label> <textarea name=\"description\" placeholder=\"Optional description\" rows=\"3\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\"></textarea></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-primary)\">Library Type</label> <select name=\"type\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required><option value=\"ebooks\">📚 Ebooks</option> <option value=\"comics\">📖 Comics</option> <option value=\"manga\">🗾 Manga</option></select></div><div class=\"flex justify-end space-x-3\"><button type=\"button\" data-action=\"hide-create-modal\" class=\"btn-secondary px-4 py-2 rounded-lg\">Cancel</button> <button type=\"submit\" class=\"btn-primary px-4 py-2 rounded-lg\">Create</button></div></form></div></div><!-- Folder Browser Modal --><div id=\"folder-browser-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"card rounded-lg p-6 w-full max-w-md mx-4\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-4\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Browse Folders</h2><button type=\"button\" data-action=\"browse-cancel\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><div id=\"folder-browser-content\"><!-- Directory listings will be rendered here --></div></div></div><!-- Delete Library Confirmation Modal --><div id=\"delete-library-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"card rounded-lg p-6 w-full max-w-md mx-4\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-4\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Delete Library</h2><button type=\"button\" data-action=\"hide-delete-modal\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><div id=\"delete-modal-content\" class=\"mb-6\" style=\"color: var(--text-primary)\"><!-- Dynamic content will be injected here --></div><div class=\"flex justify-end space-x-3\"><button type=\"button\" data-action=\"hide-delete-modal\" class=\"btn-secondary px-4 py-2 rounded-lg\">Cancel</button> <button type=\"button\" data-action=\"confirm-delete\" class=\"btn-primary px-4 py-2 rounded-lg bg-red-500 hover:bg-red-600\">Delete</button></div></div></div><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/api.js\"></script><script src=\"/static/theme.js\"></script><script src=\"/static/library.js\"></script></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Users - Bookhoard Admin</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/header.js\"></script><script src=\"/static/theme.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Users - Bookhoard Admin</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/header.js\"></script><script src=\"/static/theme.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-{ currentUser.Theme }\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func BookShelf(user User, libraries []LibraryData) templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Library - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/search.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Library - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/search.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-{ user.Theme }\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func Index(loggedIn bool) templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Bookhoard - Home</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night\"><!-- Hero Section --><div class=\"relative overflow-hidden\"><div class=\"absolute inset-0 bg-gradient-to-r from-blue-600 to-purple-700 opacity-10\"></div><div class=\"relative max-w-7xl mx-auto px-4 py-16 sm:px-6 sm:py-24 lg:py-32 lg:px-8\"><div class=\"flex justify-between items-start mb-8\"><div></div><select id=\"theme-select\" class=\"px-3 py-2 border rounded text-sm\" style=\"background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border)\" onchange=\"changeTheme()\"><option value=\"tokyo-night\">Tokyo Night</option> <option value=\"dracula\">Dracula</option> <option value=\"nord\">Nord</option> <option value=\"solarized-dark\">Solarized Dark</option> <option value=\"monokai\">Monokai</option> <option value=\"one-dark-pro\">One Dark Pro</option> <option value=\"material-dark\">Material Dark</option> <option value=\"catppuccin-mocha\">Catppuccin Mocha</option> <option value=\"catppuccin-macchiato\">Catppuccin Macchiato</option> <option value=\"catppuccin-frappe\">Catppuccin Frappé</option> <option value=\"catppuccin-latte\">Catppuccin Latte</option></select></div><div class=\"text-center\"><h1 class=\"text-4xl sm:text-6xl lg:text-7xl font-extrabold\" style=\"color: var(--text-primary)\">📚 Bookhoard</h1><p class=\"mt-6 max-w-2xl mx-auto text-xl\" style=\"color: var(--text-secondary)\">Your personal ebook management system. Track reading progress, organize your library, and enjoy beautiful themes.</p><div class=\"mt-10\"><a href=\"#auth\" class=\"btn-primary px-8 py-3 rounded-lg font-medium text-lg\">Get Started</a></div></div></div></div><!-- Features Section --><div class=\"py-16\" style=\"background-color: var(--bg-secondary)\"><div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\"><div class=\"text-center mb-12\"><h2 class=\"text-3xl font-extrabold\" style=\"color: var(--text-primary)\">Why Bookhoard?</h2><p class=\"mt-4 text-lg\" style=\"color: var(--text-secondary)\">Discover the features that make ebook management effortless</p></div><div class=\"grid grid-cols-1 md:grid-cols-3 gap-8\"><div class=\"card p-6 rounded-xl border\"><div class=\"text-center\"><div class=\"mx-auto w-12 h-12 bg-blue-500 rounded-lg flex items-center justify-center mb-4\"><span class=\"text-white text-2xl\">📖</span></div><h3 class=\"text-lg font-semibold mb-2\" style=\"color: var(--text-primary)\">Reading Progress</h3><p style=\"color: var(--text-secondary)\">Track your reading progress across all your ebooks with detailed statistics.</p></div></div><div class=\"card p-6 rounded-xl border\"><div class=\"text-center\"><div class=\"mx-auto w-12 h-12 bg-purple-500 rounded-lg flex items-center justify-center mb-4\"><span class=\"text-white text-2xl\">🎨</span></div><h3 class=\"text-lg font-semibold mb-2\" style=\"color: var(--text-primary)\">Beautiful Themes</h3><p style=\"color: var(--text-secondary)\">Choose from 11 stunning themes including Tokyo Night, Dracula, and Catppuccin variants.</p></div></div><div class=\"card p-6 rounded-xl border\"><div class=\"text-center\"><div class=\"mx-auto w-12 h-12 bg-green-500 rounded-lg flex items-center justify-center mb-4\"><span class=\"text-white text-2xl\">⚡</span></div><h3 class=\"text-lg font-semibold mb-2\" style=\"color: var(--text-primary)\">Fast & Modern</h3><p style=\"color: var(--text-secondary)\">Built with Go and HTMX for lightning-fast performance and smooth interactions.</p></div></div></div></div></div><!-- Auth Section -->")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Bookhoard - Home</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night\"><script>\n // Progressive enhancement: check localStorage immediately\n (function() {\n const savedTheme = localStorage.getItem('theme');\n if (savedTheme && savedTheme !== 'tokyo-night') {\n document.body.className = 'theme-' + savedTheme;\n }\n })();\n </script><!-- Hero Section --><div class=\"relative overflow-hidden\"><div class=\"absolute inset-0 bg-gradient-to-r from-blue-600 to-purple-700 opacity-10\"></div><div class=\"relative max-w-7xl mx-auto px-4 py-16 sm:px-6 sm:py-24 lg:py-32 lg:px-8\"><div class=\"flex justify-between items-start mb-8\"><div></div><select id=\"theme-select\" class=\"px-3 py-2 border rounded text-sm\" style=\"background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border)\" onchange=\"changeTheme()\"><option value=\"tokyo-night\">Tokyo Night</option> <option value=\"dracula\">Dracula</option> <option value=\"nord\">Nord</option> <option value=\"solarized-dark\">Solarized Dark</option> <option value=\"monokai\">Monokai</option> <option value=\"one-dark-pro\">One Dark Pro</option> <option value=\"material-dark\">Material Dark</option> <option value=\"catppuccin-mocha\">Catppuccin Mocha</option> <option value=\"catppuccin-macchiato\">Catppuccin Macchiato</option> <option value=\"catppuccin-frappe\">Catppuccin Frappé</option> <option value=\"catppuccin-latte\">Catppuccin Latte</option></select></div><div class=\"text-center\"><h1 class=\"text-4xl sm:text-6xl lg:text-7xl font-extrabold\" style=\"color: var(--text-primary)\">📚 Bookhoard</h1><p class=\"mt-6 max-w-2xl mx-auto text-xl\" style=\"color: var(--text-secondary)\">Your personal ebook management system. Track reading progress, organize your library, and enjoy beautiful themes.</p><div class=\"mt-10\"><a href=\"#auth\" class=\"btn-primary px-8 py-3 rounded-lg font-medium text-lg\">Get Started</a></div></div></div></div><!-- Features Section --><div class=\"py-16\" style=\"background-color: var(--bg-secondary)\"><div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\"><div class=\"text-center mb-12\"><h2 class=\"text-3xl font-extrabold\" style=\"color: var(--text-primary)\">Why Bookhoard?</h2><p class=\"mt-4 text-lg\" style=\"color: var(--text-secondary)\">Discover the features that make ebook management effortless</p></div><div class=\"grid grid-cols-1 md:grid-cols-3 gap-8\"><div class=\"card p-6 rounded-xl border\"><div class=\"text-center\"><div class=\"mx-auto w-12 h-12 bg-blue-500 rounded-lg flex items-center justify-center mb-4\"><span class=\"text-white text-2xl\">📖</span></div><h3 class=\"text-lg font-semibold mb-2\" style=\"color: var(--text-primary)\">Reading Progress</h3><p style=\"color: var(--text-secondary)\">Track your reading progress across all your ebooks with detailed statistics.</p></div></div><div class=\"card p-6 rounded-xl border\"><div class=\"text-center\"><div class=\"mx-auto w-12 h-12 bg-purple-500 rounded-lg flex items-center justify-center mb-4\"><span class=\"text-white text-2xl\">🎨</span></div><h3 class=\"text-lg font-semibold mb-2\" style=\"color: var(--text-primary)\">Beautiful Themes</h3><p style=\"color: var(--text-secondary)\">Choose from 11 stunning themes including Tokyo Night, Dracula, and Catppuccin variants.</p></div></div><div class=\"card p-6 rounded-xl border\"><div class=\"text-center\"><div class=\"mx-auto w-12 h-12 bg-green-500 rounded-lg flex items-center justify-center mb-4\"><span class=\"text-white text-2xl\">⚡</span></div><h3 class=\"text-lg font-semibold mb-2\" style=\"color: var(--text-primary)\">Fast & Modern</h3><p style=\"color: var(--text-secondary)\">Built with Go and HTMX for lightning-fast performance and smooth interactions.</p></div></div></div></div></div><!-- Auth Section -->")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func Login(sessionExpired bool, deleted bool) templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Login</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/theme.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night min-h-screen\"><div class=\"container mx-auto px-4 py-8 max-w-md\"><div class=\"flex justify-between items-center mb-8\"><h1 class=\"text-3xl font-bold\">Login to Bookhoard</h1><select id=\"theme-select\" class=\"px-3 py-2 border rounded\" style=\"background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border)\" onchange=\"changeTheme()\"><option value=\"tokyo-night\">Tokyo Night</option> <option value=\"dracula\">Dracula</option> <option value=\"nord\">Nord</option> <option value=\"solarized-dark\">Solarized Dark</option> <option value=\"monokai\">Monokai</option> <option value=\"one-dark-pro\">One Dark Pro</option> <option value=\"material-dark\">Material Dark</option> <option value=\"catppuccin-mocha\">Catppuccin Mocha</option> <option value=\"catppuccin-macchiato\">Catppuccin Macchiato</option> <option value=\"catppuccin-frappe\">Catppuccin Frappé</option> <option value=\"catppuccin-latte\">Catppuccin Latte</option></select></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Login</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/theme.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night min-h-screen\"><script>\n // Progressive enhancement: check localStorage immediately\n (function() {\n const savedTheme = localStorage.getItem('theme');\n if (savedTheme && savedTheme !== 'tokyo-night') {\n document.body.className = 'theme-' + savedTheme + ' min-h-screen';\n }\n })();\n </script><div class=\"container mx-auto px-4 py-8 max-w-md\"><div class=\"flex justify-between items-center mb-8\"><h1 class=\"text-3xl font-bold\">Login to Bookhoard</h1><select id=\"theme-select\" class=\"px-3 py-2 border rounded\" style=\"background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border)\" onchange=\"changeTheme()\"><option value=\"tokyo-night\">Tokyo Night</option> <option value=\"dracula\">Dracula</option> <option value=\"nord\">Nord</option> <option value=\"solarized-dark\">Solarized Dark</option> <option value=\"monokai\">Monokai</option> <option value=\"one-dark-pro\">One Dark Pro</option> <option value=\"material-dark\">Material Dark</option> <option value=\"catppuccin-mocha\">Catppuccin Mocha</option> <option value=\"catppuccin-macchiato\">Catppuccin Macchiato</option> <option value=\"catppuccin-frappe\">Catppuccin Frappé</option> <option value=\"catppuccin-latte\">Catppuccin Latte</option></select></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func Register() templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Register</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/theme.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night min-h-screen\"><div class=\"container mx-auto px-4 py-8 max-w-md\"><h1 class=\"text-3xl font-bold text-center mb-8\">Register for Bookhoard</h1><form hx-post=\"/api/auth/register\" hx-target=\"#result\" hx-swap=\"innerHTML\" class=\"card p-6 rounded-lg shadow-md border\"><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Email</label> <input type=\"email\" id=\"email\" name=\"email\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Username</label> <input type=\"text\" id=\"username\" name=\"username\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">First Name</label> <input type=\"text\" name=\"first_name\" placeholder=\"Optional\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\"></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Last Name</label> <input type=\"text\" name=\"last_name\" placeholder=\"Optional\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\"></div><div id=\"password-requirements\" class=\"mb-4 p-4 rounded-lg border text-sm\" style=\"background-color: var(--bg-secondary); border-color: var(--border)\"><p class=\"font-semibold mb-2\" style=\"color: var(--text-primary)\">Password Requirements:</p><ul class=\"space-y-1\" style=\"color: var(--text-secondary)\"><li id=\"req-length\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> At least 8 characters</li><li id=\"req-upper\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One uppercase letter</li><li id=\"req-lower\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One lowercase letter</li><li id=\"req-number\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One number</li><li id=\"req-special\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One special character</li><li id=\"req-match\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> Passwords match</li></ul></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Password</label> <input type=\"password\" id=\"password\" name=\"password\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Confirm Password</label> <input type=\"password\" id=\"confirm-password\" name=\"confirm_password\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><button type=\"submit\" id=\"register-btn\" class=\"btn-primary w-full py-2 rounded opacity-50 cursor-not-allowed\" disabled>Register</button></form><div id=\"result\" class=\"mt-4 text-center\"></div><p class=\"text-center mt-4\"><a href=\"/login\" class=\"text-blue-500 hover:underline\">Already have an account? Login</a></p></div><script src=\"/static/password_validation.js\"></script><script>\n document.addEventListener('DOMContentLoaded', () => {\n if (window.initPasswordValidation) {\n window.initPasswordValidation();\n }\n });\n </script></body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Register</title><script src=\"/static/htmx.min.js\"></script><script src=\"/static/toast.js\"></script><script src=\"/static/theme.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-tokyo-night min-h-screen\"><script>\n // Progressive enhancement: check localStorage immediately\n (function() {\n const savedTheme = localStorage.getItem('theme');\n if (savedTheme && savedTheme !== 'tokyo-night') {\n document.body.className = 'theme-' + savedTheme + ' min-h-screen';\n }\n })();\n </script><div class=\"container mx-auto px-4 py-8 max-w-md\"><h1 class=\"text-3xl font-bold text-center mb-8\">Register for Bookhoard</h1><form hx-post=\"/api/auth/register\" hx-target=\"#result\" hx-swap=\"innerHTML\" class=\"card p-6 rounded-lg shadow-md border\"><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Email</label> <input type=\"email\" id=\"email\" name=\"email\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Username</label> <input type=\"text\" id=\"username\" name=\"username\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">First Name</label> <input type=\"text\" name=\"first_name\" placeholder=\"Optional\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\"></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Last Name</label> <input type=\"text\" name=\"last_name\" placeholder=\"Optional\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\"></div><div id=\"password-requirements\" class=\"mb-4 p-4 rounded-lg border text-sm\" style=\"background-color: var(--bg-secondary); border-color: var(--border)\"><p class=\"font-semibold mb-2\" style=\"color: var(--text-primary)\">Password Requirements:</p><ul class=\"space-y-1\" style=\"color: var(--text-secondary)\"><li id=\"req-length\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> At least 8 characters</li><li id=\"req-upper\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One uppercase letter</li><li id=\"req-lower\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One lowercase letter</li><li id=\"req-number\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One number</li><li id=\"req-special\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> One special character</li><li id=\"req-match\" class=\"flex items-center gap-2\"><span class=\"requirement-icon\">○</span> Passwords match</li></ul></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Password</label> <input type=\"password\" id=\"password\" name=\"password\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-1\" style=\"color: var(--text-secondary)\">Confirm Password</label> <input type=\"password\" id=\"confirm-password\" name=\"confirm_password\" class=\"w-full px-3 py-2 border rounded\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border)\" required></div><button type=\"submit\" id=\"register-btn\" class=\"btn-primary w-full py-2 rounded opacity-50 cursor-not-allowed\" disabled>Register</button></form><div id=\"result\" class=\"mt-4 text-center\"></div><p class=\"text-center mt-4\"><a href=\"/login\" class=\"text-blue-500 hover:underline\">Already have an account? Login</a></p></div><script src=\"/static/password_validation.js\"></script><script>\n document.addEventListener('DOMContentLoaded', () => {\n if (window.initPasswordValidation) {\n window.initPasswordValidation();\n }\n });\n </script></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
+3
-29
@@ -23,36 +23,10 @@ const toggleUserMenu = () => {
|
||||
}
|
||||
};
|
||||
const changeThemeTo = (theme) => {
|
||||
// Apply the theme
|
||||
if (theme.startsWith('wood-')) {
|
||||
// Apply wood background theme
|
||||
document.body.className = `theme-${theme}`;
|
||||
// Set wood background style
|
||||
let woodGradient = '';
|
||||
switch (theme) {
|
||||
case 'wood-light':
|
||||
woodGradient = 'linear-gradient(135deg, #deb887 0%, #d2a679 50%, #c9975b 100%)';
|
||||
break;
|
||||
case 'wood-dark':
|
||||
woodGradient = 'linear-gradient(135deg, #8b7355 0%, #6b5344 50%, #5a4636 100%)';
|
||||
break;
|
||||
case 'wood-mahogany':
|
||||
woodGradient = 'linear-gradient(135deg, #a0522d 0%, #8b4513 50%, #7a3c10 100%)';
|
||||
break;
|
||||
}
|
||||
document.body.style.background = woodGradient;
|
||||
document.body.style.backgroundSize = 'cover';
|
||||
document.body.style.backgroundAttachment = 'fixed';
|
||||
// Apply the theme using the consolidated function from theme.ts
|
||||
if (window.applyTheme) {
|
||||
window.applyTheme(theme);
|
||||
}
|
||||
else {
|
||||
// Apply regular theme
|
||||
document.body.className = `theme-${theme}`;
|
||||
document.body.style.background = '';
|
||||
document.body.style.backgroundSize = '';
|
||||
document.body.style.backgroundAttachment = '';
|
||||
}
|
||||
// Save to localStorage
|
||||
localStorage.setItem('theme', theme);
|
||||
// Save to server if logged in
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
|
||||
Reference in New Issue
Block a user