Files
bookhoard/templates/dashboard.templ
T
john-okeefe 5f5870b95d Migrate to templ: Convert all HTML templates to type-safe templ components
- Add templ dependency for type-safe HTML rendering
- Convert index.html, login.html, register.html, dashboard.html, admin.html, admin_profile.html, admin_library.html to .templ files
- Generate Go code with templ generate for compile-time safety
- Update server routes to use templ components with proper rendering
- Refactor JavaScript in dashboard to avoid backtick parsing conflicts
- Delete old HTML template files
- Clean up project by removing unnecessary files and binaries
2026-01-23 16:33:08 -05:00

163 lines
7.7 KiB
Templ

package templates
templ Dashboard(user User) {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - Bookmann</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<style>
:root {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--text-primary: #a9b1d6;
--text-secondary: #565f89;
--accent: #7aa2f7;
--border: #414868;
}
.theme-tokyo-night {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--text-primary: #a9b1d6;
--text-secondary: #565f89;
--accent: #7aa2f7;
--border: #414868;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
}
.card {
background-color: var(--bg-secondary);
border-color: var(--border);
}
.btn-primary {
background-color: var(--accent);
color: var(--bg-primary);
}
.btn-primary:hover {
opacity: 0.8;
}
</style>
</head>
<body class="theme-tokyo-night">
<!-- Navigation Header -->
<nav class="border-b" style="border-color: var(--border); background-color: var(--bg-secondary)">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<div class="flex items-center">
<h1 class="text-xl font-bold" style="color: var(--text-primary)">📚 Bookmann</h1>
</div>
<div class="flex items-center space-x-4">
<a href="/" class="px-3 py-2 text-sm hover:opacity-80" style="color: var(--text-secondary)">Dashboard</a>
<a href="/admin" class="px-3 py-2 text-sm hover:opacity-80" style="color: var(--text-secondary)">Admin</a>
<span style="color: var(--text-secondary)">Welcome, { user.Username }!</span>
<button onclick="logout()" class="btn-primary px-4 py-2 rounded text-sm">
Logout
</button>
</div>
</div>
</div>
</nav>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Header Section -->
<div class="mb-8">
<div class="flex justify-between items-center">
<div>
<h2 class="text-3xl font-bold" style="color: var(--text-primary)">Your Ebook Library</h2>
<p style="color: var(--text-secondary)">Manage your ebook collection and reading progress</p>
</div>
<button onclick="showCreateForm()" class="btn-primary px-6 py-3 rounded-lg font-medium">
+ Add New Ebook
</button>
</div>
</div>
<!-- Search and Filter -->
<div class="mb-6">
<div class="flex flex-col sm:flex-row gap-4">
<input type="text" id="search-input" placeholder="Search ebooks..." class="flex-1 px-4 py-2 border rounded-lg" style="background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border)" onkeyup="filterEbooks()">
<select id="sort-select" class="px-4 py-2 border rounded-lg" style="background-color: var(--bg-secondary); color: var(--text-primary); border-color: var(--border)" onchange="loadEbooks()">
<option value="created_at DESC">Newest First</option>
<option value="title ASC">Title A-Z</option>
<option value="author ASC">Author A-Z</option>
<option value="created_at ASC">Oldest First</option>
</select>
</div>
</div>
<!-- Ebooks Grid -->
<div id="ebooks-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Ebooks will be loaded here -->
</div>
<!-- Loading indicator -->
<div id="loading" class="text-center py-8" style="color: var(--text-secondary)">
Loading your ebooks...
</div>
</div>
<script>
function loadEbooks() {
const loading = document.getElementById('loading');
const container = document.getElementById('ebooks-container');
loading.style.display = 'block';
container.innerHTML = '';
const sort = document.getElementById('sort-select').value;
fetch(`/api/ebooks?limit=12&offset=0&sort=${sort}`, {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
loading.style.display = 'none';
renderEbooks(data);
})
.catch(error => {
loading.style.display = 'none';
console.error('Error loading ebooks:', error);
container.innerHTML = '<div class="col-span-full text-center py-8" style="color: var(--text-secondary)">Error loading ebooks. Please try again.</div>';
});
}
function renderEbooks(ebooks) {
const container = document.getElementById('ebooks-container');
container.innerHTML = '';
ebooks.forEach(ebook => {
const card = document.createElement('div');
card.className = 'card p-4 rounded-lg border hover:shadow-lg transition-shadow';
card.style.cssText = 'background-color: var(--bg-secondary); border-color: var(--border);';
const coverUrl = ebook.cover_image_path ? ebook.cover_image_path : '/static/placeholder-book.svg';
card.innerHTML = '<div class="flex flex-col h-full"><div class="flex-1"><div class="aspect-w-3 aspect-h-4 mb-3 overflow-hidden rounded"><img src="' + coverUrl + '" alt="Cover" class="w-full h-48 object-cover rounded" onerror="this.src=\'/static/placeholder-book.svg\'"></div><h3 class="font-semibold text-lg mb-1 line-clamp-2" style="color: var(--text-primary)">' + ebook.title + '</h3>' + (ebook.author ? '<p class="text-sm mb-2" style="color: var(--text-secondary)">by ' + ebook.author + '</p>' : '') + '<div class="flex justify-between items-center mt-4"><div class="flex space-x-2"><button onclick="viewEbook(\'' + ebook.id + '\')" class="px-3 py-1 text-sm border rounded hover:opacity-80" style="border-color: var(--border); color: var(--text-secondary)">View</button></div></div></div>';
container.appendChild(card);
});
}
function logout() {
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = '/';
}
// Load ebooks on page load
document.addEventListener('DOMContentLoaded', function() {
loadTheme();
loadEbooks();
});
</script>
</body>
</html>
}