SaifulSir.com আপনাকে স্বাগতম!!!

এখনই দেখুন
SaifulSir.com

কমিট করার পরে নতুন ডিপ্লয় শুরু হয়েছে কি না Vercel-এ দেখে নিন। সেখানে নতুন লগেও এই এরর এলে লগের পুরোটা, বিশেষ করে সময়সহ, আমাকে পাঠান।

16/10/2026

"use client"; import { useActionState, useEffect, useState } from "react"; import Image from "next/image"; import { savePost, type PostState } from "@/app/actions/posts"; import { parseTags } from "@/lib/tags"; import RichEditor from "@/components/rich-editor"; import SeoPanel from "@/components/seo-panel"; import { Code, Eye, Pencil, Wrench } from "lucide-react"; // HTML ট্যাবে লেখা পোস্ট চেনার জন্য একটি র‍্যাপার ব্যবহার হয়। // not-prose ক্লাস Tailwind prose এর স্টাইল থেকে পোস্টকে আলাদা রাখে। const OPEN = '
'; const isRawBody = (b: string) => b.trim().startsWith(OPEN); const wrapRaw = (h: string) => (isRawBody(h) ? h.trim() : `${OPEN}${h.trim()}
`); const unwrapRaw = (b: string) => { const t = b.trim(); return isRawBody(t) ? t.slice(OPEN.length).replace(/<\/div>\s*$/, "").trim() : b; }; export type PostFormData = { id?: string; title: string; slug: string; categoryId: string; tags: string; published: boolean; focusKeyword: string; keywords: string; metaTitle: string; metaDescription: string; coverUrl: string | null; coverAlt: string; downloadUrl: string; body: string; }; const empty: PostFormData = { title: "", slug: "", categoryId: "", tags: "", published: true, focusKeyword: "", keywords: "", metaTitle: "", metaDescription: "", coverUrl: null, coverAlt: "", downloadUrl: "", body: "", }; const f = "w-full rounded-xl border border-slate-300 bg-white px-3 py-2"; const init: PostState = { msg: "" }; type Tab = "compose" | "html" | "preview"; const isEscapedTable = (s: string) => /<\s*table\b/i.test(s); const risky = (s: string) => / হয়ে যাওয়া কোড আবার ঠিক করে function repairEscapedHtml(s: string): string { return s .replace(/<\/(div|p)>\s*<(div|p)[^>]*>/gi, "\n") .replace(/^\s*<(div|p)[^>]*>/i, "") .replace(/<\/(div|p)>\s*$/i, "") .replace(//gi, "\n") .replace(/<\/?a\b[^>]*>/gi, "") .replace(//g, ">").replace(/"/g, '"') .replace(/'/g, "'").replace(/ /g, " ").replace(/&/g, "&") .trim(); } function L({ t, hint, n, max }: { t: string; hint?: string; n?: number; max?: number }) { return ( {t}{hint && {hint}} {max !== undefined && n !== undefined && ( max ? "text-red-600" : n >= max * 0.8 ? "text-orange-600" : "text-slate-500"}`}>{n}/{max} )} ); } export default function PostForm({ post = empty, cats }: { post?: PostFormData; cats: { id: string; name: string }[] }) { const [state, action, pending] = useActionState(savePost.bind(null, post.id ?? null), init); const [v, setV] = useState({ title: post.title, slug: post.slug, tags: post.tags, focusKeyword: post.focusKeyword, keywords: post.keywords, metaTitle: post.metaTitle, metaDescription: post.metaDescription, }); const startRaw = isRawBody(post.body) || /(startRaw ? "html" : "compose"); const [info, setInfo] = useState({ text: "", headings: [] as string[], words: 0 }); const on = (k: keyof typeof v) => (e: React.ChangeEvent) => setV({ ...v, [k]: e.target.value }); useEffect(() => { const doc = new DOMParser().parseFromString(html.replace(/<\/(p|h[1-6]|li|blockquote|div)>/gi, " "), "text/html"); const text = (doc.body.textContent ?? "").replace(/\s+/g, " ").trim(); setInfo({ text, headings: Array.from(doc.querySelectorAll("h2,h3,h4")).map((h) => (h.textContent ?? "").trim()), words: text ? text.split(" ").length : 0 }); }, [html]); const goTab = (next: Tab) => { if (next === "compose" && raw) { if (risky(html) && !confirm("Compose এডিটর টেবিল ও স্টাইল মুছে ফেলতে পারে। তবুও Compose-এ যাবেন?")) return; setRaw(false); } setTab(next); }; const previewDoc = `` + `` + `${html}`; const tabBtn = (t: Tab, label: string, icon: React.ReactNode) => ( goTab(t)} className={`inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold transition ${tab === t ? "bg-royal text-white" : "bg-slate-100 text-slate-700 hover:bg-slate-200"}`} > {icon}{label} ); const tagList = parseTags(v.tags, info.text); return (
{tabBtn("compose", "Compose", )} {tabBtn("html", "HTML", )} {tabBtn("preview", "Preview", )}
{isEscapedTable(html) && (
এই পোস্টের টেবিল কোড লেখা হিসেবে সেভ হয়ে গেছে। এক ক্লিকে ঠিক করা যাবে। { setHtml(repairEscapedHtml(html)); setRaw(true); setTab("html"); }} className="inline-flex items-center gap-1.5 rounded-lg bg-amber-600 px-3 py-1.5 font-semibold text-white hover:bg-amber-700" > টেবিল কোড ঠিক করুন
)} {/* সার্ভারে সবসময় এই একটি ফিল্ডই যায়। raw হলে কোড হুবহু র‍্যাপারসহ সেভ হয় */} {tab === "compose" && } {tab === "html" && (
শেয়ার করুনFacebookWhatsAppTelegram