Enhance content parsing and metadata extraction in HTMLContentRenderer and ToggleTableRow

- Improved HTMLContentRenderer to better handle JSON content and extract HTML safely.
- Updated ToggleTableRow to robustly extract meta descriptions, including parsing potential JSON strings.
- Refactored Content page to conditionally display meta descriptions based on JSON parsing results, enhancing user experience.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-10 14:27:56 +00:00
parent 926ac150fd
commit e067dc759c
4 changed files with 87 additions and 28 deletions

View File

@@ -9,10 +9,6 @@ class Tasks(SiteSectorBaseModel):
STATUS_CHOICES = [
('queued', 'Queued'),
('in_progress', 'In Progress'),
('draft', 'Draft'),
('review', 'Review'),
('published', 'Published'),
('completed', 'Completed'),
]

View File

@@ -195,7 +195,47 @@ const HTMLContentRenderer: React.FC<HTMLContentRendererProps> = ({
// If content is a string, try to parse as JSON first
if (typeof content === 'string') {
// Try to parse as JSON (content outline from GPT-4o mini)
// Check if it's a JSON string that contains the actual content
if (content.trim().startsWith('{') || content.trim().startsWith('[')) {
try {
const parsed = JSON.parse(content);
if (typeof parsed === 'object' && parsed !== null) {
// If it's a full AI response JSON with a 'content' field, use that
if (parsed.content && typeof parsed.content === 'string') {
// Recursively process the extracted content
const extractedContent = parsed.content;
// Check if extracted content is HTML
if (isHTML(extractedContent)) {
const sanitized = sanitizeHTML(extractedContent);
if (sanitized.trim().startsWith('<article') || sanitized.trim().startsWith('<div')) {
return `<div class="normalized-html-content">${sanitized}</div>`;
}
return `<div class="normalized-html-content"><article>${sanitized}</article></div>`;
}
// If extracted content is still JSON, try parsing again
if (extractedContent.trim().startsWith('{')) {
try {
const nestedParsed = JSON.parse(extractedContent);
if (nestedParsed.H2 || nestedParsed.H3 || nestedParsed.introduction || nestedParsed.sections) {
return formatContentOutline(nestedParsed);
}
} catch {
// Not nested JSON, continue
}
}
// Use extracted content as-is (will be processed below)
content = extractedContent;
} else if (parsed.H2 || parsed.H3 || parsed.introduction || parsed.sections) {
// It's a content outline structure
return formatContentOutline(parsed);
}
}
} catch {
// Not valid JSON, continue with HTML/text processing
}
}
// Try to parse as JSON (content outline from GPT-4o mini) - for non-brace-starting JSON
try {
const parsed = JSON.parse(content);
if (typeof parsed === 'object' && (parsed.H2 || parsed.H3 || parsed.introduction || parsed.sections)) {

View File

@@ -169,12 +169,35 @@ const ToggleMetadata: React.FC<ToggleMetadataProps> = ({ row, contentMetadata })
contentMetadata?.metadata?.categories ||
[];
const metaDescription =
row.meta_description ||
row.content_meta_description ||
contentMetadata?.meta_description ||
contentMetadata?.metadata?.meta_description ||
null;
// Extract meta_description, avoiding JSON strings
let metaDescription: string | null = null;
// Try direct fields first
if (row.meta_description && typeof row.meta_description === 'string') {
metaDescription = row.meta_description;
} else if (row.content_meta_description && typeof row.content_meta_description === 'string') {
metaDescription = row.content_meta_description;
} else if (contentMetadata?.meta_description && typeof contentMetadata.meta_description === 'string') {
metaDescription = contentMetadata.meta_description;
} else if (contentMetadata?.metadata?.meta_description && typeof contentMetadata.metadata.meta_description === 'string') {
metaDescription = contentMetadata.metadata.meta_description;
}
// If metaDescription looks like JSON, try to parse it
if (metaDescription && metaDescription.trim().startsWith('{')) {
try {
const parsed = JSON.parse(metaDescription);
// If parsed object has meta_description, use that
if (parsed.meta_description && typeof parsed.meta_description === 'string') {
metaDescription = parsed.meta_description;
} else {
// If it's a full JSON response, extract meta_description from it
metaDescription = parsed.meta_description || null;
}
} catch {
// Not valid JSON, keep as is
}
}
const hasMetadata =
primaryKeyword ||
@@ -263,18 +286,6 @@ const ToggleMetadata: React.FC<ToggleMetadataProps> = ({ row, contentMetadata })
</div>
);
};
<div className="html-content-wrapper">
<HTMLContentRenderer
content={content}
className="text-sm text-gray-700 dark:text-gray-300 leading-relaxed"
/>
</div>
</div>
</div>
</td>
</tr>
);
};
/**
* Toggle Button Component - To be used in table cells

View File

@@ -132,11 +132,23 @@ export default function Content() {
<div className="font-medium text-gray-900 dark:text-white">
{item.meta_title || item.title || item.task_title || `Task #${item.task}`}
</div>
{item.meta_description && (
{(() => {
let metaDesc = item.meta_description;
// If meta_description is a JSON string, extract the actual value
if (metaDesc && typeof metaDesc === 'string' && metaDesc.trim().startsWith('{')) {
try {
const parsed = JSON.parse(metaDesc);
metaDesc = parsed.meta_description || metaDesc;
} catch {
// Not valid JSON, use as-is
}
}
return metaDesc ? (
<div className="mt-1 text-sm text-gray-500 dark:text-gray-400 line-clamp-2">
{item.meta_description}
{metaDesc}
</div>
)}
) : null;
})()}
</td>
<td className="px-5 py-4 align-top">
{item.primary_keyword ? (