diff --git a/frontend/src/components/common/HTMLContentRenderer.tsx b/frontend/src/components/common/HTMLContentRenderer.tsx index 14d7aa60..7b9cb4f1 100644 --- a/frontend/src/components/common/HTMLContentRenderer.tsx +++ b/frontend/src/components/common/HTMLContentRenderer.tsx @@ -17,7 +17,15 @@ interface HTMLContentRendererProps { */ function formatContentOutline(content: any): string { if (!content) return ''; - + + // If object contains a `content` field with HTML, use that directly + if (typeof content === 'object' && content !== null && 'content' in content) { + const mainContent = (content as any).content; + if (typeof mainContent === 'string' && mainContent.trim().length > 0) { + return sanitizeHTML(mainContent); + } + } + let html = '
'; // Handle introduction section - can be object or string diff --git a/frontend/src/components/common/ToggleTableRow.tsx b/frontend/src/components/common/ToggleTableRow.tsx index 3fc63b7c..9c66dfbe 100644 --- a/frontend/src/components/common/ToggleTableRow.tsx +++ b/frontend/src/components/common/ToggleTableRow.tsx @@ -6,6 +6,7 @@ import React, { useState, useRef, useEffect } from 'react'; import { ChevronDownIcon, HorizontaLDots } from '../../icons'; import HTMLContentRenderer from './HTMLContentRenderer'; +import Badge from '../ui/badge/Badge'; interface ToggleTableRowProps { /** The row data */ @@ -44,6 +45,13 @@ const ToggleTableRow: React.FC = ({ // Get content - handle fallback to description if primary contentKey is empty let content = row[contentKey]; + let contentMetadata: Record | null = null; + + if (content && typeof content === 'object' && content !== null && 'content' in content) { + contentMetadata = { ...content }; + content = content.content; + } + if (!content || (typeof content === 'string' && content.trim().length === 0)) { // Try fallback to description if primary content is empty content = row.description || row.content_outline || null; @@ -104,9 +112,157 @@ const ToggleTableRow: React.FC = ({ className="overflow-hidden" >
-
- {contentLabel} +
+
+ {contentLabel} +
+ + {/* Metadata badges */} + + + {/* Rendered content */} +
+ +
+
+
+ + + ); +}; + +interface ToggleMetadataProps { + row: any; + contentMetadata: Record | null; +} + +const ToggleMetadata: React.FC = ({ row, contentMetadata }) => { + const primaryKeyword = + row.content_primary_keyword || + row.primary_keyword || + contentMetadata?.primary_keyword || + contentMetadata?.metadata?.primary_keyword || + null; + + const secondaryKeywords = + row.content_secondary_keywords || + row.secondary_keywords || + contentMetadata?.secondary_keywords || + contentMetadata?.metadata?.secondary_keywords || + []; + + const tags = + row.content_tags || + row.tags || + contentMetadata?.tags || + contentMetadata?.metadata?.tags || + []; + + const categories = + row.content_categories || + row.categories || + contentMetadata?.categories || + contentMetadata?.metadata?.categories || + []; + + const metaDescription = + row.meta_description || + row.content_meta_description || + contentMetadata?.meta_description || + contentMetadata?.metadata?.meta_description || + null; + + const hasMetadata = + primaryKeyword || + (secondaryKeywords && secondaryKeywords.length > 0) || + (tags && tags.length > 0) || + (categories && categories.length > 0) || + metaDescription; + + if (!hasMetadata) { + return null; + } + + const renderBadgeList = (items: any, color: 'info' | 'light' = 'light') => { + if (!items) return null; + const list = Array.isArray(items) ? items : [items]; + if (list.length === 0) return null; + + return ( +
+ {list.map((item, index) => ( + + {item} + + ))} +
+ ); + }; + + return ( +
+ {primaryKeyword && ( +
+ Primary Keyword: + + {primaryKeyword} + +
+ )} + + {(() => { + const badges = renderBadgeList(secondaryKeywords); + if (!badges) return null; + return ( +
+ Secondary Keywords: + {badges} +
+ ); + })()} + + {(() => { + const badges = renderBadgeList(tags); + if (!badges) return null; + return ( +
+ Tags: + {badges} +
+ ); + })()} + + {(() => { + const badges = renderBadgeList(categories); + if (!badges) return null; + return ( +
+ Categories: + {badges} +
+ ); + })()} + + {metaDescription && ( +
+ + Meta Description + + {metaDescription} +
+ )} +
+ ); +};
{ + const getList = (primary?: string[], fallback?: any): string[] => { + if (primary && primary.length > 0) return primary; + if (!fallback) return []; + if (Array.isArray(fallback)) return fallback; + return []; + }; + + const renderBadgeList = (items: string[], emptyLabel = '-') => { if (!items || items.length === 0) { return {emptyLabel}; } @@ -112,11 +119,18 @@ export default function Content() { {content.map((item) => { const isExpanded = expandedId === item.id; - return ( + const secondaryKeywords = getList( + item.secondary_keywords, + item.metadata?.secondary_keywords + ); + const tags = getList(item.tags, item.metadata?.tags); + const categories = getList(item.categories, item.metadata?.categories); + + return (
- {item.meta_title || item.title || `Task #${item.task}`} + {item.meta_title || item.title || item.task_title || `Task #${item.task}`}
{item.meta_description && (
@@ -134,13 +148,13 @@ export default function Content() { )} - {renderBadgeList(item.secondary_keywords)} + {renderBadgeList(secondaryKeywords)} - {renderBadgeList(item.tags)} + {renderBadgeList(tags)} - {renderBadgeList(item.categories)} + {renderBadgeList(categories)} {item.word_count?.toLocaleString?.() ?? '-'}