This commit is contained in:
rattatwinko
2025-06-17 12:48:09 +02:00
parent cbfb4c667c
commit 93b879d69e
3 changed files with 66 additions and 8 deletions

2
posts/pinned.json Normal file
View File

@@ -0,0 +1,2 @@
[
]

View File

@@ -61,6 +61,7 @@ export default function AdminPage() {
} }
return []; return [];
}); });
const [pinFeedback, setPinFeedback] = useState<string | null>(null);
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
@@ -300,14 +301,45 @@ export default function AdminPage() {
} }
}; };
const handlePin = (slug: string) => { const handlePin = async (slug: string) => {
setPinned((prev) => setPinned((prev) => {
prev.includes(slug) ? prev.filter((s) => s !== slug) : [slug, ...prev] const newPinned = prev.includes(slug)
? prev.filter((s) => s !== slug)
: [slug, ...prev];
// Update pinned.json on the server
fetch('/api/admin/posts', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pinned: newPinned }),
})
.then((res) => {
if (!res.ok) {
res.json().then((data) => {
setPinFeedback(data.error || 'Failed to update pinned posts');
});
} else {
setPinFeedback(
newPinned.includes(slug)
? 'Post pinned!'
: 'Post unpinned!'
); );
setTimeout(() => setPinFeedback(null), 2000);
}
})
.catch((err) => {
setPinFeedback('Failed to update pinned posts');
});
return newPinned;
});
}; };
return ( return (
<div className="min-h-screen bg-gray-100 p-8"> <div className="min-h-screen bg-gray-100 p-8">
{pinFeedback && (
<div className="fixed top-4 left-1/2 transform -translate-x-1/2 bg-blue-600 text-white px-6 py-2 rounded shadow-lg z-50">
{pinFeedback}
</div>
)}
{!isAuthenticated ? ( {!isAuthenticated ? (
<div className="max-w-md mx-auto bg-white p-8 rounded-lg shadow-md"> <div className="max-w-md mx-auto bg-white p-8 rounded-lg shadow-md">
<h1 className="text-2xl font-bold mb-6">Admin Login</h1> <h1 className="text-2xl font-bold mb-6">Admin Login</h1>
@@ -598,11 +630,16 @@ export default function AdminPage() {
key={post.slug} key={post.slug}
className={`border rounded-lg p-3 flex items-center gap-3 justify-between ${pinned.includes(post.slug) ? 'bg-yellow-100 border-yellow-400' : ''}`} className={`border rounded-lg p-3 flex items-center gap-3 justify-between ${pinned.includes(post.slug) ? 'bg-yellow-100 border-yellow-400' : ''}`}
> >
<div className="flex-1 text-left"> <div className="flex-1 text-left flex items-center gap-2">
{pinned.includes(post.slug) && (
<span title="Pinned" className="text-xl">📌</span>
)}
<div>
<div className="font-semibold">{post.title}</div> <div className="font-semibold">{post.title}</div>
<div className="text-xs text-gray-500">{post.date}</div> <div className="text-xs text-gray-500">{post.date}</div>
<div className="text-xs text-gray-400">{post.summary}</div> <div className="text-xs text-gray-400">{post.summary}</div>
</div> </div>
</div>
<button <button
onClick={() => handlePin(post.slug)} onClick={() => handlePin(post.slug)}
className={`text-2xl focus:outline-none ${pinned.includes(post.slug) ? 'text-yellow-500' : 'text-gray-400 hover:text-yellow-500'}`} className={`text-2xl focus:outline-none ${pinned.includes(post.slug) ? 'text-yellow-500' : 'text-gray-400 hover:text-yellow-500'}`}

View File

@@ -37,3 +37,22 @@ export async function POST(request: Request) {
); );
} }
} }
export async function PATCH(request: Request) {
try {
const body = await request.json();
const { pinned } = body; // expects an array of slugs
if (!Array.isArray(pinned)) {
return NextResponse.json({ error: 'Invalid pinned data' }, { status: 400 });
}
const pinnedPath = path.join(postsDirectory, 'pinned.json');
fs.writeFileSync(pinnedPath, JSON.stringify(pinned, null, 2), 'utf8');
return NextResponse.json({ success: true });
} catch (error) {
console.error('Error updating pinned.json:', error);
return NextResponse.json(
{ error: 'Error updating pinned.json' },
{ status: 500 }
);
}
}