From e067dc759c34e12a6d0e8fcd2059c6c45f092f5b Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Mon, 10 Nov 2025 14:27:56 +0000 Subject: [PATCH] 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. --- backend/igny8_core/modules/writer/models.py | 4 -- .../components/common/HTMLContentRenderer.tsx | 42 ++++++++++++++++- .../src/components/common/ToggleTableRow.tsx | 47 ++++++++++++------- frontend/src/pages/Writer/Content.tsx | 22 +++++++-- 4 files changed, 87 insertions(+), 28 deletions(-) diff --git a/backend/igny8_core/modules/writer/models.py b/backend/igny8_core/modules/writer/models.py index bed4350b..72865d66 100644 --- a/backend/igny8_core/modules/writer/models.py +++ b/backend/igny8_core/modules/writer/models.py @@ -9,10 +9,6 @@ class Tasks(SiteSectorBaseModel): STATUS_CHOICES = [ ('queued', 'Queued'), - ('in_progress', 'In Progress'), - ('draft', 'Draft'), - ('review', 'Review'), - ('published', 'Published'), ('completed', 'Completed'), ] diff --git a/frontend/src/components/common/HTMLContentRenderer.tsx b/frontend/src/components/common/HTMLContentRenderer.tsx index 7b9cb4f1..c20f5c6b 100644 --- a/frontend/src/components/common/HTMLContentRenderer.tsx +++ b/frontend/src/components/common/HTMLContentRenderer.tsx @@ -195,7 +195,47 @@ const HTMLContentRenderer: React.FC = ({ // 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('${sanitized}`; + } + return `
${sanitized}
`; + } + // 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)) { diff --git a/frontend/src/components/common/ToggleTableRow.tsx b/frontend/src/components/common/ToggleTableRow.tsx index 9c66dfbe..ee966a0f 100644 --- a/frontend/src/components/common/ToggleTableRow.tsx +++ b/frontend/src/components/common/ToggleTableRow.tsx @@ -169,12 +169,35 @@ const ToggleMetadata: React.FC = ({ 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 = ({ row, contentMetadata }) ); }; -
- -
- - - - - ); -}; /** * Toggle Button Component - To be used in table cells diff --git a/frontend/src/pages/Writer/Content.tsx b/frontend/src/pages/Writer/Content.tsx index 1b1e0625..be2dce10 100644 --- a/frontend/src/pages/Writer/Content.tsx +++ b/frontend/src/pages/Writer/Content.tsx @@ -132,11 +132,23 @@ export default function Content() {
{item.meta_title || item.title || item.task_title || `Task #${item.task}`}
- {item.meta_description && ( -
- {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 ? ( +
+ {metaDesc} +
+ ) : null; + })()} {item.primary_keyword ? (