folder support
This commit is contained in:
@@ -5,6 +5,7 @@ import Link from 'next/link';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
interface Post {
|
||||
type: 'post';
|
||||
slug: string;
|
||||
title: string;
|
||||
date: string;
|
||||
@@ -14,35 +15,92 @@ interface Post {
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface Folder {
|
||||
type: 'folder';
|
||||
name: string;
|
||||
path: string;
|
||||
children: (Folder | Post)[];
|
||||
}
|
||||
|
||||
type Node = Folder | Post;
|
||||
|
||||
export default function Home() {
|
||||
const [posts, setPosts] = useState<Post[]>([]);
|
||||
const [tree, setTree] = useState<Node[]>([]);
|
||||
const [currentPath, setCurrentPath] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// Initial load
|
||||
loadPosts();
|
||||
|
||||
// Set up polling for changes
|
||||
const interval = setInterval(loadPosts, 2000);
|
||||
|
||||
// Cleanup
|
||||
loadTree();
|
||||
const interval = setInterval(loadTree, 2000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const loadPosts = async () => {
|
||||
const loadTree = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/posts');
|
||||
const data = await response.json();
|
||||
setPosts(data);
|
||||
setTree(data);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Beiträge:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Traverse the tree to the current path
|
||||
const getCurrentNodes = (): Node[] => {
|
||||
let nodes: Node[] = tree;
|
||||
for (const segment of currentPath) {
|
||||
const folder = nodes.find(
|
||||
(n) => n.type === 'folder' && n.name === segment
|
||||
) as Folder | undefined;
|
||||
if (folder) {
|
||||
nodes = folder.children;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
};
|
||||
|
||||
const nodes = getCurrentNodes();
|
||||
|
||||
// Breadcrumbs
|
||||
const breadcrumbs = [
|
||||
{ name: 'Startseite', path: [] },
|
||||
...currentPath.map((name, idx) => ({
|
||||
name,
|
||||
path: currentPath.slice(0, idx + 1),
|
||||
})),
|
||||
];
|
||||
|
||||
return (
|
||||
<main className="min-h-screen p-8 max-w-4xl mx-auto">
|
||||
<h1 className="text-4xl font-bold mb-8">Sebastian Zinkls - Blog</h1>
|
||||
<nav className="mb-6 text-sm text-gray-600 flex gap-2 items-center">
|
||||
{breadcrumbs.map((bc, idx) => (
|
||||
<span key={bc.path.join('/') + idx}>
|
||||
{idx > 0 && <span className="mx-1">/</span>}
|
||||
<button
|
||||
className="hover:underline"
|
||||
onClick={() => setCurrentPath(bc.path)}
|
||||
disabled={idx === breadcrumbs.length - 1}
|
||||
>
|
||||
{bc.name}
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</nav>
|
||||
<div className="grid gap-8">
|
||||
{posts.map((post) => (
|
||||
{/* Folders */}
|
||||
{nodes.filter((n) => n.type === 'folder').map((folder: any) => (
|
||||
<div
|
||||
key={folder.path}
|
||||
className="border rounded-lg p-6 bg-gray-50 cursor-pointer hover:bg-gray-100 transition"
|
||||
onClick={() => setCurrentPath([...currentPath, folder.name])}
|
||||
>
|
||||
<span className="font-semibold text-lg">📁 {folder.name}</span>
|
||||
</div>
|
||||
))}
|
||||
{/* Posts */}
|
||||
{nodes.filter((n) => n.type === 'post').map((post: any) => (
|
||||
<article key={post.slug} className="border rounded-lg p-6 hover:shadow-lg transition-shadow">
|
||||
<Link href={`/posts/${post.slug}`}>
|
||||
<h2 className="text-2xl font-semibold mb-2">{post.title}</h2>
|
||||
@@ -52,7 +110,7 @@ export default function Home() {
|
||||
</div>
|
||||
<p className="text-gray-700 mb-4">{post.summary}</p>
|
||||
<div className="flex gap-2">
|
||||
{post.tags.map((tag) => (
|
||||
{post.tags.map((tag: string) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="bg-gray-100 text-gray-800 px-3 py-1 rounded-full text-sm"
|
||||
|
||||
Reference in New Issue
Block a user