feat: WordPress tarzı rich editor (TipTap + slash menu + MediaPicker)
WordPress Gutenberg + Notion karışımı blok editor. 4 admin formunda markdown textarea yerine gerçek WYSIWYG editor. RichEditor component (components/admin/rich-editor.tsx): - TipTap v3 (@tiptap/react + starter-kit + link + image + placeholder + underline) - Üst toolbar (her zaman görünür): - B / I / U (bold, italic, underline) - H1 / H2 / H3 - Bullet list / Ordered list / Quote / Code block - Link (URL prompt) - Görsel ekle (MediaPicker modal) - Undo / Redo - Slash menu: '/' yazınca blok seçim menüsü açılır - Notion tarzı keyboard navigation (↓↑ Enter Esc) - 8 blok tipi: H1/H2/H3/ul/ol/quote/code/hr - Image picker modal (toolbar görsel butonundan) - Mevcut MediaPicker'ı kullanır - 'Yeni görsel yükle' (progress bar ile) + 'Kütüphaneden seç' grid - HTML çıktı (hidden input ile form'a) - Mevcut content alanlarıyla backward compat Formlarda değişiklik (4 dosya): - app/admin/(protected)/blog/form.tsx → content - app/admin/(protected)/hizmetler/form.tsx → content - app/admin/(protected)/projeler/form.tsx → content - app/admin/(protected)/sektorler/form.tsx → content Public render (lib/content-render.ts): - renderContent() yardımcısı: - İçerik '<' ile başlıyorsa → HTML (direkt döner) - Aksi halde → markdown (marked.parse) - 4 detay sayfası bu helper'ı kullanıyor (blog/[slug], projeler/[slug], hizmetler/[slug], sektor/[slug]) - Eski markdown içerikler hala çalışıyor, yeni içerikler HTML olarak gelir 37 route, build temiz.
This commit is contained in:
@@ -3,7 +3,7 @@ import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { ArrowLeft, Calendar } from "lucide-react";
|
||||
import { marked } from "marked";
|
||||
import { renderContent } from "@/lib/content-render";
|
||||
import { getPostBySlug } from "@/lib/data";
|
||||
import { buildMetadata } from "@/lib/seo";
|
||||
|
||||
@@ -36,7 +36,7 @@ export default async function BlogPostPage({
|
||||
const post = await getPostBySlug(slug);
|
||||
if (!post || post.status !== "published") notFound();
|
||||
|
||||
const html = post.content ? marked.parse(post.content, { async: false }) as string : "";
|
||||
const html = renderContent(post.content);
|
||||
|
||||
return (
|
||||
<article className="mx-auto max-w-3xl px-6 py-20">
|
||||
|
||||
Reference in New Issue
Block a user