Files
nutshell9247 852613586f
CI/CD Pipeline / Test & Lint (push) Waiting to run
CI/CD Pipeline / Build Docker Image (push) Blocked by required conditions
CI/CD Pipeline / Security Scan (push) Waiting to run
first commit, mate!
2026-08-08 14:43:59 +08:00

248 lines
8.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - Blog</title>
<link rel="stylesheet" href="/static/css/material.css">
</head>
<body>
<header class="header">
<div class="header__content">
<a href="/" class="header__logo">📝 Blog</a>
<nav class="header__nav">
<a href="/">Home</a>
<a href="#about">About</a>
<a href="/feed">RSS Feed</a>
<span style="padding: 8px 16px; background: rgba(255,255,255,0.2); border-radius: 20px;">Dashboard</span>
</nav>
</div>
</header>
<main class="container" style="padding: 40px 16px;">
<div style="display: grid; grid-template-columns: 1fr 2fr; gap: 40px; align-items: start;">
<!-- Sidebar Navigation -->
<aside style="position: sticky; top: 20px;">
<div class="card">
<h3 style="margin-top: 0;">Dashboard</h3>
<nav style="display: flex; flex-direction: column; gap: 8px;">
<button class="sidebar-btn active" data-view="editor" style="
text-align: left;
background: none;
border: none;
padding: 12px;
border-radius: 8px;
cursor: pointer;
color: var(--md-sys-color-primary);
font-weight: 500;
">
✍️ Write Post
</button>
<button class="sidebar-btn" data-view="posts" style="
text-align: left;
background: none;
border: none;
padding: 12px;
border-radius: 8px;
cursor: pointer;
color: var(--md-sys-color-on-background);
">
📚 My Posts
</button>
<button class="sidebar-btn" data-view="stats" style="
text-align: left;
background: none;
border: none;
padding: 12px;
border-radius: 8px;
cursor: pointer;
color: var(--md-sys-color-on-background);
">
📊 Statistics
</button>
<hr style="border: none; border-top: 1px solid var(--md-sys-color-outline-variant); margin: 12px 0;">
<button onclick="handleLogout()" style="
text-align: left;
background: none;
border: none;
padding: 12px;
border-radius: 8px;
cursor: pointer;
color: var(--md-sys-color-error);
">
🚪 Logout
</button>
</nav>
</div>
</aside>
<!-- Main Content -->
<section>
<!-- Editor View -->
<div id="editor-view" class="view-section" style="display: block;">
<div id="post-editor"></div>
</div>
<!-- Posts View -->
<div id="posts-view" class="view-section" style="display: none;">
<h2>My Posts</h2>
<div id="user-posts-list" style="margin-top: 24px;">
Loading your posts...
</div>
</div>
<!-- Statistics View -->
<div id="stats-view" class="view-section" style="display: none;">
<h2>Statistics</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-top: 24px;">
<div class="card">
<div style="font-size: 12px; color: var(--md-sys-color-on-surface-variant); text-transform: uppercase; letter-spacing: 0.5px;">Total Posts</div>
<div id="stat-total-posts" style="font-size: 32px; font-weight: 600; margin-top: 8px;">0</div>
</div>
<div class="card">
<div style="font-size: 12px; color: var(--md-sys-color-on-surface-variant); text-transform: uppercase; letter-spacing: 0.5px;">Published</div>
<div id="stat-published-posts" style="font-size: 32px; font-weight: 600; margin-top: 8px;">0</div>
</div>
<div class="card">
<div style="font-size: 12px; color: var(--md-sys-color-on-surface-variant); text-transform: uppercase; letter-spacing: 0.5px;">Drafts</div>
<div id="stat-draft-posts" style="font-size: 32px; font-weight: 600; margin-top: 8px;">0</div>
</div>
</div>
</div>
</section>
</div>
</main>
<footer class="footer">
<div class="footer__content">
<div class="footer__section">
<h4>Dashboard</h4>
<p>Manage your blog posts and content from here.</p>
</div>
</div>
<div class="footer__bottom">
<p>&copy; 2024. Self-hosted with care.</p>
</div>
</footer>
<script src="/static/js/app.js"></script>
<script>
// View switching
document.querySelectorAll('.sidebar-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const view = e.target.dataset.view;
// Update active button
document.querySelectorAll('.sidebar-btn').forEach(b => {
b.style.color = 'var(--md-sys-color-on-background)';
});
e.target.style.color = 'var(--md-sys-color-primary)';
// Update active view
document.querySelectorAll('.view-section').forEach(section => {
section.style.display = 'none';
});
document.getElementById(view + '-view').style.display = 'block';
// Load data for specific views
if (view === 'posts') {
loadUserPosts();
} else if (view === 'stats') {
loadStatistics();
} else if (view === 'editor') {
new PostEditor('post-editor');
}
});
});
// Initialize editor on first load
new PostEditor('post-editor');
async function loadUserPosts() {
try {
const result = await api.get('/posts?page=1');
const postsList = document.getElementById('user-posts-list');
if (!result.data || result.data.length === 0) {
postsList.innerHTML = '<p>No posts yet. Start writing!</p>';
return;
}
let html = '<ul class="post-list">';
result.data.forEach(post => {
html += `
<li class="post-item">
<div style="display: flex; justify-content: space-between; align-items: start;">
<div style="flex: 1;">
<h3 class="post-title" style="margin: 0 0 8px 0;">
<a href="/post/${post.slug}">${post.title}</a>
</h3>
<div class="post-meta" style="font-size: 12px;">
<span>${new Date(post.created_at).toLocaleDateString()}</span>
${post.category ? `<span>${post.category}</span>` : ''}
</div>
</div>
<div style="display: flex; gap: 8px;">
<button onclick="editPost('${post.id}')" class="button button--secondary" style="height: 32px; padding: 0 12px; font-size: 12px;">Edit</button>
<button onclick="deletePost('${post.id}')" class="button button--tertiary" style="height: 32px; padding: 0 12px; font-size: 12px; color: var(--md-sys-color-error);">Delete</button>
</div>
</div>
</li>
`;
});
html += '</ul>';
postsList.innerHTML = html;
} catch (error) {
document.getElementById('user-posts-list').innerHTML = '<p style="color: var(--md-sys-color-error);">Error loading posts</p>';
}
}
async function loadStatistics() {
try {
const result = await api.get('/posts?page=1');
const posts = result.data || [];
const total = posts.length;
const published = posts.filter(p => p.published).length;
const drafts = total - published;
document.getElementById('stat-total-posts').textContent = total;
document.getElementById('stat-published-posts').textContent = published;
document.getElementById('stat-draft-posts').textContent = drafts;
} catch (error) {
console.error('Error loading statistics:', error);
}
}
function editPost(postId) {
Toast.show('Edit functionality coming soon', 'info');
// TODO: Implement edit functionality
}
async function deletePost(postId) {
if (!confirm('Are you sure you want to delete this post?')) return;
try {
const result = await api.delete(`/posts/${postId}`);
if (result.success) {
Toast.show('Post deleted', 'success');
loadUserPosts();
}
} catch (error) {
Toast.show('Error deleting post', 'error');
}
}
async function handleLogout() {
try {
await api.post('/auth/logout', {});
Toast.show('Logged out', 'success');
setTimeout(() => window.location.href = '/', 1000);
} catch (error) {
Toast.show('Error logging out', 'error');
}
}
</script>
</body>
</html>