From 1303078c2edf333c4868e2931f24c7fd22d512df Mon Sep 17 00:00:00 2001 From: rattatwinko Date: Mon, 16 Jun 2025 20:10:14 +0200 Subject: [PATCH] fuck my butthole pls --- src/app/admin/page.tsx | 111 ++++++++++++++++++++++++++++++++++--- src/app/api/posts/route.ts | 9 +++ src/app/layout.tsx | 12 +++- src/app/page.tsx | 71 +++++++++++++----------- 4 files changed, 163 insertions(+), 40 deletions(-) diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index f67b67f..7592ffb 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -11,6 +11,7 @@ interface Post { tags: string[]; summary: string; content: string; + pinned: boolean; } interface Folder { @@ -28,6 +29,7 @@ interface Post { tags: string[]; summary: string; content: string; + pinned: boolean; } type Node = Post | Folder; @@ -52,6 +54,13 @@ export default function AdminPage() { item: null, }); const [showManageContent, setShowManageContent] = useState(false); + const [managePath, setManagePath] = useState([]); + const [pinned, setPinned] = useState(() => { + if (typeof window !== 'undefined') { + return JSON.parse(localStorage.getItem('pinnedPosts') || '[]'); + } + return []; + }); const router = useRouter(); useEffect(() => { @@ -63,6 +72,10 @@ export default function AdminPage() { } }, []); + useEffect(() => { + localStorage.setItem('pinnedPosts', JSON.stringify(pinned)); + }, [pinned]); + const loadContent = async () => { try { const response = await fetch('/api/posts'); @@ -180,6 +193,23 @@ export default function AdminPage() { })), ]; + // Get nodes for manage content + const getManageNodes = (): Node[] => { + let currentNodes: Node[] = nodes; + for (const segment of managePath) { + const folder = currentNodes.find( + (n) => n.type === 'folder' && n.name === segment + ) as Folder | undefined; + if (folder) { + currentNodes = folder.children; + } else { + break; + } + } + return currentNodes; + }; + const manageNodes = getManageNodes(); + const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); setIsDragging(true); @@ -270,6 +300,12 @@ export default function AdminPage() { } }; + const handlePin = (slug: string) => { + setPinned((prev) => + prev.includes(slug) ? prev.filter((s) => s !== slug) : [slug, ...prev] + ); + }; + return (
{!isAuthenticated ? ( @@ -483,11 +519,16 @@ export default function AdminPage() {
))} - {/* Posts */} - {currentNodes - .filter((node): node is Post => node.type === 'post') - .map((post) => ( -
+ {/* Posts: pinned first, then unpinned */} + {(() => { + const posts = currentNodes.filter((node): node is Post => node.type === 'post'); + const pinnedPosts = posts.filter(post => post.pinned); + const unpinnedPosts = posts.filter(post => !post.pinned); + return [...pinnedPosts, ...unpinnedPosts].map((post) => ( +
+ {post.pinned && ( + 📌 + )}

{post.title}

{post.date}

{post.summary}

@@ -499,7 +540,8 @@ export default function AdminPage() { ))}
- ))} + )); + })()} @@ -516,7 +558,62 @@ export default function AdminPage() {

Delete posts and folders, manage your content structure

- Go to Content Manager + {/* Folder navigation breadcrumbs */} +
+ + {managePath.map((name, idx) => ( + + ))} +
+ {/* Folders */} +
+ {manageNodes.filter((n) => n.type === 'folder').map((folder: any) => ( +
setManagePath([...managePath, folder.name])} + > + 📁 + {folder.name} +
+ ))} +
+ {/* Posts (pinned first) */} +
+ {[...manageNodes.filter((n) => n.type === 'post' && pinned.includes(n.slug)), + ...manageNodes.filter((n) => n.type === 'post' && !pinned.includes(n.slug)) + ].map((post: any) => ( +
+
+
{post.title}
+
{post.date}
+
{post.summary}
+
+ +
+ ))} +
+ Go to Content Manager )} diff --git a/src/app/api/posts/route.ts b/src/app/api/posts/route.ts index 04f4b3a..59ea028 100644 --- a/src/app/api/posts/route.ts +++ b/src/app/api/posts/route.ts @@ -7,6 +7,14 @@ import html from 'remark-html'; const postsDirectory = path.join(process.cwd(), 'posts'); +const pinnedPath = path.join(postsDirectory, 'pinned.json'); +let pinnedSlugs: string[] = []; +if (fs.existsSync(pinnedPath)) { + try { + pinnedSlugs = JSON.parse(fs.readFileSync(pinnedPath, 'utf8')); + } catch {} +} + // Function to get file creation date function getFileCreationDate(filePath: string): Date { const stats = fs.statSync(filePath); @@ -27,6 +35,7 @@ async function getPostByPath(filePath: string, relPath: string) { summary: data.summary, content: processedContent.toString(), createdAt: createdAt.toISOString(), + pinned: pinnedSlugs.includes(relPath.replace(/\.md$/, '')), }; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3f71d73..35b3a16 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -27,13 +27,21 @@ export default function RootLayout({
-
-
+
+
Markdown Next.js Tailwind CSS TypeScript
+ Admin Login diff --git a/src/app/page.tsx b/src/app/page.tsx index 8c19810..3543d4b 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -13,6 +13,7 @@ interface Post { summary: string; content: string; createdAt: string; + pinned: boolean; } interface Folder { @@ -100,38 +101,46 @@ export default function Home() {
))} {/* Posts */} - {nodes.filter((n) => n.type === 'post').map((post: any) => ( -
- -

{post.title}

-
- {post.date ? ( -
Veröffentlicht: {format(new Date(post.date), 'd. MMMM yyyy')}
- ) : ( -
-
- ⚙️ - ⚙️ + {(() => { + const posts = nodes.filter((n) => n.type === 'post'); + const pinnedPosts = posts.filter((post: any) => post.pinned); + const unpinnedPosts = posts.filter((post: any) => !post.pinned); + return [...pinnedPosts, ...unpinnedPosts].map((post: any) => ( +
+ {post.pinned && ( + 📌 + )} + +

{post.title}

+
+ {post.date ? ( +
Veröffentlicht: {format(new Date(post.date), 'd. MMMM yyyy')}
+ ) : ( +
+
+ ⚙️ + ⚙️ +
+
In Bearbeitung
-
In Bearbeitung
-
- )} -
Erstellt: {format(new Date(post.createdAt), 'd. MMMM yyyy HH:mm')}
-
-

{post.summary}

-
- {post.tags.map((tag: string) => ( - - {tag} - - ))} -
- -
- ))} + )} +
Erstellt: {format(new Date(post.createdAt), 'd. MMMM yyyy HH:mm')}
+
+

{post.summary}

+
+ {post.tags.map((tag: string) => ( + + {tag} + + ))} +
+ + + )); + })()}
);