14 blog post schedule

This commit is contained in:
Timo Knuth
2026-01-26 16:43:47 +01:00
parent 702e2710de
commit 2771faf3ba
44 changed files with 3561 additions and 2174 deletions

View File

@@ -0,0 +1,23 @@
"use client";
import React from 'react';
import sanitizeHtml from 'sanitize-html';
type Props = { html: string };
export function AnswerBox({ html }: Props) {
const cleanHtml = sanitizeHtml(html, {
allowedTags: ['p', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'br', 'code', 'pre', 'blockquote', 'h3', 'h4'],
allowedAttributes: { 'a': ['href', 'class'], 'code': ['class'], 'pre': ['class'] },
allowedClasses: {
'a': ['text-primary-600', 'hover:underline'],
}
});
return (
<section className="rounded-xl border border-blue-100 bg-blue-50/50 p-6 my-8">
<div className="text-sm font-semibold text-blue-800 uppercase tracking-wider mb-2">Quick Answer</div>
<div className="prose prose-blue max-w-none text-gray-800" dangerouslySetInnerHTML={{ __html: cleanHtml }} />
</section>
);
}

View File

@@ -0,0 +1,35 @@
"use client";
import React from 'react';
import sanitizeHtml from 'sanitize-html';
import type { FAQItem } from "@/lib/types";
type Props = { items: FAQItem[]; title?: string };
export function FAQSection({ items, title = "Frequently Asked Questions" }: Props) {
if (!items?.length) return null;
return (
<section className="rounded-xl border border-gray-100 bg-gray-50/50 p-6 my-8">
<h2 className="text-xl font-bold text-gray-900 mb-6">{title}</h2>
<div className="space-y-4">
{items.map((f) => {
const cleanAnswer = sanitizeHtml(f.answer, {
allowedTags: ['p', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'br', 'code'],
allowedAttributes: { 'a': ['href'] }
});
return (
<details key={f.question} className="group rounded-lg border border-gray-200 bg-white p-4 open:shadow-sm open:border-blue-200 transition-all">
<summary className="cursor-pointer font-semibold text-gray-800 flex justify-between items-center group-open:text-blue-700">
{f.question}
<span className="text-gray-400 group-open:rotate-180 transition-transform"></span>
</summary>
<div className="prose max-w-none mt-3 text-gray-600 border-t border-gray-100 pt-3" dangerouslySetInnerHTML={{ __html: cleanAnswer }} />
</details>
);
})}
</div>
</section>
);
}

View File

@@ -0,0 +1,18 @@
import React from 'react';
type Props = { steps: string[]; title?: string };
export function StepList({ steps, title = "How to do it" }: Props) {
if (!steps?.length) return null;
return (
<section className="rounded-xl border border-gray-200 bg-white p-6 my-8 shadow-sm">
<h2 className="text-xl font-bold text-gray-900 mb-4">{title}</h2>
<ol className="list-decimal pl-6 space-y-3">
{steps.map((s, i) => (
<li key={`step-${i}`} className="text-gray-700 font-medium pl-2">{s}</li>
))}
</ol>
</section>
);
}