feat: Implement blog API service and refactor components for improved data fetching

This commit is contained in:
Wini_Fy
2026-02-04 15:33:02 +07:00
parent d46c420aaf
commit 9a71d39ebf
16 changed files with 790 additions and 149 deletions

View File

@@ -60,7 +60,7 @@ function blockToHtml(block: EditorJSBlock, baseUrl?: string): string {
case 'header': {
const level = data.level || 2;
const text = escapeHtml(data.text || '');
return `<h${level}>${text}</h${level}>`;
return `<h${level} style="margin:0 0 14px 0;">${text}</h${level}>`;
}
case 'paragraph': {
@@ -78,7 +78,7 @@ function blockToHtml(block: EditorJSBlock, baseUrl?: string): string {
text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
// Convert line breaks
text = text.replace(/\n/g, '<br>');
return `<p>${text}</p>`;
return `<p style="margin:0 0 14px 0;">${text}</p>`;
}
case 'list': {
@@ -115,11 +115,15 @@ function blockToHtml(block: EditorJSBlock, baseUrl?: string): string {
if (withBackground) classes.push('with-background');
if (stretched) classes.push('stretched');
const classAttr = classes.length > 0 ? ` class="${classes.join(' ')}"` : '';
const captionHtml = caption ? `<figcaption>${escapeHtml(caption)}</figcaption>` : '';
const classAttr =
classes.length > 0 ? ` class="${classes.join(' ')} editorjs-image"` : ' class="editorjs-image"';
return `<figure${classAttr}>
<img src="${escapeHtml(imageUrl)}" alt="${escapeHtml(caption)}" />
const captionHtml = caption
? `<figcaption class="editorjs-image__caption" style="text-align:center;margin-top:0.5rem;margin-bottom:0;">${escapeHtml(caption)}</figcaption>`
: '';
return `<figure${classAttr} style="margin-top:1rem;margin-bottom:1rem;">
<img src="${escapeHtml(imageUrl)}" alt="${escapeHtml(caption)}" style="display:block;margin-left:auto;margin-right:auto;" />
${captionHtml}
</figure>`;
}
@@ -172,7 +176,7 @@ function blockToHtml(block: EditorJSBlock, baseUrl?: string): string {
}
case 'delimiter': {
return '<div class="delimiter">***</div>';
return '<hr style="border:0;border-top:2px solid rgba(0,0,0,0.75);margin:16px 0;" />';
}
case 'table': {
@@ -223,7 +227,7 @@ function blockToHtml(block: EditorJSBlock, baseUrl?: string): string {
const title = meta.title || data.title || link;
const description = meta.description || '';
const image = meta.image || '';
let imageHtml = '';
if (image) {
let imageUrl = image;
@@ -295,7 +299,7 @@ function blockToHtml(block: EditorJSBlock, baseUrl?: string): string {
</div>`;
}
const linkHtml = linkUrl && linkText
const linkHtml = linkUrl && linkText
? `<a href="${escapeHtml(linkUrl)}" class="personality-link">${escapeHtml(linkText)}</a>`
: '';

View File

@@ -3,7 +3,7 @@
*
* Rules:
* - If already a full URL (http/https) → return as is
* - If starts with `/uploads/` → prepend API URL (NEXT_PUBLIC_API_URL or default localhost)
* - If starts with `/uploads/` or `/img/` → prepend API URL (NEXT_PUBLIC_API_URL or default localhost)
* - If starts with `/` → use as-is (served by Next/public)
* - Otherwise → treat as relative path under `/`
*/
@@ -14,7 +14,7 @@ export function getCmsImageUrl(imagePath: string | undefined): string {
return imagePath;
}
if (imagePath.startsWith("/uploads/")) {
if (imagePath.startsWith("/uploads/") || imagePath.startsWith("/img/")) {
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";
return `${apiUrl}${imagePath}`;
}