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 [];
});
const [pinFeedback, setPinFeedback] = useState<string | null>(null);
const router = useRouter();
useEffect(() => {
@@ -300,14 +301,45 @@ export default function AdminPage() {
}
};
const handlePin = (slug: string) => {
setPinned((prev) =>
prev.includes(slug) ? prev.filter((s) => s !== slug) : [slug, ...prev]
);
const handlePin = async (slug: string) => {
setPinned((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 (
<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 ? (
<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>
@@ -598,10 +630,15 @@ export default function AdminPage() {
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' : ''}`}
>
<div className="flex-1 text-left">
<div className="font-semibold">{post.title}</div>
<div className="text-xs text-gray-500">{post.date}</div>
<div className="text-xs text-gray-400">{post.summary}</div>
<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="text-xs text-gray-500">{post.date}</div>
<div className="text-xs text-gray-400">{post.summary}</div>
</div>
</div>
<button
onClick={() => handlePin(post.slug)}

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 }
);
}
}