Enhance Admin page with post editing functionality; implement PUT API for saving post edits, including frontmatter parsing and error handling. Update welcome post metadata and improve UI for editing posts.

This commit is contained in:
2025-06-19 13:53:32 +02:00
parent 60b66ef57c
commit 1b77b028d0
4 changed files with 126 additions and 15 deletions

View File

@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { marked } from 'marked';
import hljs from 'highlight.js';
import matter from 'gray-matter';
interface Post {
slug: string;
@@ -70,6 +71,7 @@ export default function AdminPage() {
const [changePwConfirm, setChangePwConfirm] = useState('');
const [changePwFeedback, setChangePwFeedback] = useState<string | null>(null);
const [previewHtml, setPreviewHtml] = useState('');
const [editingPost, setEditingPost] = useState<{ slug: string, path: string } | null>(null);
const router = useRouter();
const usernameRef = useRef<HTMLInputElement>(null);
const passwordRef = useRef<HTMLInputElement>(null);
@@ -416,6 +418,62 @@ export default function AdminPage() {
}
};
// Function to load a post's raw markdown
const loadPostRaw = async (slug: string, folderPath: string) => {
const params = new URLSearchParams({ slug, path: folderPath });
const res = await fetch(`/api/admin/posts/raw?${params.toString()}`);
if (!res.ok) {
alert('Error loading post');
return;
}
const text = await res.text();
const parsed = matter(text);
setNewPost({
title: parsed.data.title || '',
date: parsed.data.date || new Date().toISOString().split('T')[0],
tags: Array.isArray(parsed.data.tags) ? parsed.data.tags.join(', ') : (parsed.data.tags || ''),
summary: parsed.data.summary || '',
content: parsed.content || '',
});
setEditingPost({ slug, path: folderPath });
};
// Function to save edits
const handleEditPost = async (e: React.FormEvent) => {
e.preventDefault();
if (!editingPost) return;
try {
// Always update date to today if changed
const today = new Date().toISOString().split('T')[0];
const newDate = newPost.date !== today ? today : newPost.date;
const newFrontmatter = matter.stringify(newPost.content, {
title: newPost.title,
date: newDate,
tags: newPost.tags.split(',').map(tag => tag.trim()),
summary: newPost.summary,
author: process.env.NEXT_PUBLIC_BLOG_OWNER + "'s" || 'Anonymous',
});
const response = await fetch('/api/admin/posts', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
slug: editingPost.slug,
path: editingPost.path,
content: newFrontmatter,
}),
});
if (response.ok) {
setEditingPost(null);
setNewPost({ title: '', date: today, tags: '', summary: '', content: '' });
loadContent();
} else {
alert('Error saving post');
}
} catch (error) {
alert('Error saving post');
}
};
return (
<div className="min-h-screen bg-gray-100 p-8">
{pinFeedback && (
@@ -633,7 +691,7 @@ export default function AdminPage() {
{/* Create Post Form */}
<div className="bg-white rounded-lg shadow p-6 mb-8">
<h2 className="text-2xl font-bold mb-4">Create New Post</h2>
<form onSubmit={handleCreatePost} className="space-y-4">
<form onSubmit={editingPost ? handleEditPost : handleCreatePost} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700">Title</label>
<input
@@ -696,7 +754,7 @@ export default function AdminPage() {
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700"
>
Create Post
{editingPost ? 'Save Changes' : 'Create Post'}
</button>
</form>
</div>
@@ -727,11 +785,19 @@ export default function AdminPage() {
const pinnedPosts = posts.filter(post => post.pinned);
const unpinnedPosts = posts.filter(post => !post.pinned);
return [...pinnedPosts, ...unpinnedPosts].map((post) => (
<div key={post.slug} className="border rounded-lg p-4 relative">
{post.pinned && (
<span title="Angeheftet" className="absolute top-2 right-2 text-2xl">📌</span>
)}
<h3 className="text-xl font-semibold">{post.title}</h3>
<div key={post.slug} className="border rounded-lg p-4 relative flex flex-col gap-2">
<div className="flex items-center gap-4">
<h3 className="text-xl font-semibold flex-1">{post.title}</h3>
<button
onClick={() => loadPostRaw(post.slug, currentPath.join('/'))}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 text-lg font-bold shadow focus:outline-none focus:ring-2 focus:ring-blue-400"
>
Edit
</button>
{post.pinned && (
<span title="Angeheftet" className="text-2xl ml-2">📌</span>
)}
</div>
<p className="text-gray-600">{post.date}</p>
<p className="text-sm text-gray-500">{post.summary}</p>
<div className="mt-2 flex gap-2">