fix: resolve rich text rendering and data mapping issues

Hardened rich text conversion logic to handle malformed Tiptap documents and added null checks for CMS data in About section.
This commit is contained in:
2026-02-16 01:01:27 +01:00
parent 18f8fb7407
commit 3cf1b9144d
2 changed files with 80 additions and 59 deletions

View File

@@ -118,8 +118,8 @@ const About = () => {
description="Tools & Technologies"
header={
<div className="flex flex-wrap gap-2 p-2 overflow-y-auto max-h-40 scrollbar-hide">
{techStack.length > 0 ? (
techStack.flatMap(cat => cat.items.map((item: any) => (
{techStack && techStack.length > 0 ? (
techStack.flatMap(cat => cat.items?.map((item: any) => (
<motion.span
key={item.id}
whileHover={{ scale: 1.05 }}
@@ -184,7 +184,7 @@ const About = () => {
description="Beyond the screen"
header={
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mt-2">
{hobbies.length > 0 ? hobbies.map((hobby, i) => {
{hobbies && hobbies.length > 0 ? hobbies.map((hobby, i) => {
const Icon = iconMap[hobby.icon] || Lightbulb;
return (
<motion.div

View File

@@ -10,62 +10,83 @@ import Highlight from "@tiptap/extension-highlight";
import { FontFamily } from "@/lib/tiptap/fontFamily";
export function richTextToSafeHtml(doc: JSONContent): string {
const raw = generateHTML(doc, [
StarterKit,
Underline,
Link.configure({
openOnClick: false,
autolink: false,
HTMLAttributes: { rel: "noopener noreferrer", target: "_blank" },
}),
TextStyle,
FontFamily,
Color,
Highlight,
]);
if (!doc || typeof doc !== "object" || Object.keys(doc).length === 0) {
return "";
}
return sanitizeHtml(raw, {
allowedTags: [
"p",
"br",
"h1",
"h2",
"h3",
"blockquote",
"strong",
"em",
"u",
"a",
"ul",
"ol",
"li",
"code",
"pre",
"span"
],
allowedAttributes: {
a: ["href", "rel", "target"],
span: ["style"],
code: ["class"],
pre: ["class"],
p: ["class"],
h1: ["class"],
h2: ["class"],
h3: ["class"],
blockquote: ["class"],
ul: ["class"],
ol: ["class"],
li: ["class"]
},
allowedSchemes: ["http", "https", "mailto"],
allowProtocolRelative: false,
allowedStyles: {
span: {
color: [/^#[0-9a-fA-F]{3,8}$/],
"background-color": [/^#[0-9a-fA-F]{3,8}$/],
"font-family": [/^(Inter|ui-sans-serif|ui-serif|ui-monospace)$/],
// Ensure type is present to satisfy Tiptap requirement
const typedDoc = { ...doc };
if (!typedDoc.type) {
typedDoc.type = "doc";
}
// Ensure content is an array
if (!typedDoc.content) {
typedDoc.content = [];
}
try {
const raw = generateHTML(typedDoc, [
StarterKit,
Underline,
Link.configure({
openOnClick: false,
autolink: false,
HTMLAttributes: { rel: "noopener noreferrer", target: "_blank" },
}),
TextStyle,
FontFamily,
Color,
Highlight,
]);
return sanitizeHtml(raw, {
allowedTags: [
"p",
"br",
"h1",
"h2",
"h3",
"blockquote",
"strong",
"em",
"u",
"a",
"ul",
"ol",
"li",
"code",
"pre",
"span"
],
allowedAttributes: {
a: ["href", "rel", "target"],
span: ["style"],
code: ["class"],
pre: ["class"],
p: ["class"],
h1: ["class"],
h2: ["class"],
h3: ["class"],
blockquote: ["class"],
ul: ["class"],
ol: ["class"],
li: ["class"]
},
},
});
allowedSchemes: ["http", "https", "mailto"],
allowProtocolRelative: false,
allowedStyles: {
span: {
color: [/^#[0-9a-fA-F]{3,8}$/],
"background-color": [/^#[0-9a-fA-F]{3,8}$/],
"font-family": [/^(Inter|ui-sans-serif|ui-serif|ui-monospace)$/],
},
},
});
} catch (error) {
if (process.env.NODE_ENV === "development") {
console.error("Error generating HTML from rich text:", error);
}
return "";
}
}