feat: implement Hybrid SSR for bookshelf page
- Add LibraryData type to templates/types.go - Update bookshelf template to accept libraries parameter - Render libraries server-side for faster initial page load - Libraries now populated from server data instead of AJAX fetch - JavaScript still uses API for dynamic content (bookshelf items) - Update /bookshelf route to fetch libraries server-side before render - Properly handle UUID and pgtype.Text conversions - Maintain API endpoint compatibility for JavaScript calls This improves initial page load performance while preserving dynamic functionality via API calls.
This commit is contained in:
+41
-13
@@ -1,6 +1,6 @@
|
||||
package templates
|
||||
|
||||
templ BookShelf(user User) {
|
||||
templ BookShelf(user User, libraries []LibraryData) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -15,14 +15,20 @@ templ BookShelf(user User) {
|
||||
</head>
|
||||
<body class="theme-tokyo-night">
|
||||
@Header(user, "/bookshelf")
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:8 py-8">
|
||||
<!-- Library Selector -->
|
||||
<div class="mb-8">
|
||||
<div class="flex flex-col sm:flex-row gap-4 items-center justify-between">
|
||||
<div class="flex-1 w-full">
|
||||
<label class="block text-sm font-medium mb-2" style="color: var(--text-secondary)">Select Library</label>
|
||||
<select id="library-select" onchange="selectLibrary()" class="w-full px-4 py-3 border rounded-lg text-lg" style="background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border);">
|
||||
<option value="">Loading libraries...</option>
|
||||
if len(libraries) == 0 {
|
||||
<option value="">No libraries available</option>
|
||||
} else {
|
||||
for _, lib := range libraries {
|
||||
<option value={ lib.ID }>{ lib.Name }</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
@@ -57,21 +63,43 @@ templ BookShelf(user User) {
|
||||
|
||||
<script src="/static/theme.js"></script>
|
||||
<script>
|
||||
let libraries = [];
|
||||
// Libraries already loaded server-side
|
||||
const libraries = [
|
||||
if len(libraries) > 0 {
|
||||
for _, lib := range libraries {
|
||||
{
|
||||
id: "{ lib.ID }",
|
||||
name: "{ lib.Name }",
|
||||
description: "{ lib.Description }",
|
||||
type_name: "{ lib.TypeName }"
|
||||
},
|
||||
}
|
||||
}
|
||||
];
|
||||
let currentLibrary = null;
|
||||
let mediaItems = [];
|
||||
|
||||
function loadLibraries() {
|
||||
const loading = document.getElementById('loading');
|
||||
function selectLibrary() {
|
||||
const librarySelect = document.getElementById('library-select');
|
||||
|
||||
loading.style.display = 'block';
|
||||
const selectedId = librarySelect.value;
|
||||
currentLibrary = libraries.find(lib => lib.id === selectedId);
|
||||
if (currentLibrary) {
|
||||
loadBookshelf();
|
||||
}
|
||||
}
|
||||
|
||||
fetch('/api/libraries/visible', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + localStorage.getItem('token'),
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
// Auto-select first library if available
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadTheme();
|
||||
if (libraries.length > 0) {
|
||||
const librarySelect = document.getElementById('library-select');
|
||||
currentLibrary = libraries[0];
|
||||
librarySelect.value = currentLibrary.id;
|
||||
loadBookshelf();
|
||||
} else {
|
||||
showEmptyState();
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
|
||||
Reference in New Issue
Block a user