Implement Stage 3: Enhance content generation and metadata features
- Updated AI prompts to include metadata context, cluster roles, and product attributes for improved content generation. - Enhanced GenerateContentFunction to incorporate taxonomy and keyword objects for richer context. - Introduced new metadata fields in frontend components for better content organization and filtering. - Added cluster match, taxonomy match, and relevance score to LinkResults for improved link management. - Implemented metadata completeness scoring and recommended actions in AnalysisPreview for better content optimization. - Updated API services to support new metadata structures and site progress tracking.
This commit is contained in:
@@ -17,7 +17,8 @@ import PageMeta from '../../components/common/PageMeta';
|
||||
import { Card } from '../../components/ui/card';
|
||||
import Button from '../../components/ui/button/Button';
|
||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { fetchAPI } from '../../services/api';
|
||||
import { fetchAPI, fetchSiteBlueprints } from '../../services/api';
|
||||
import SiteProgressWidget from '../../components/sites/SiteProgressWidget';
|
||||
|
||||
interface Site {
|
||||
id: number;
|
||||
@@ -51,6 +52,7 @@ export default function SiteDashboard() {
|
||||
const toast = useToast();
|
||||
const [site, setSite] = useState<Site | null>(null);
|
||||
const [stats, setStats] = useState<SiteStats | null>(null);
|
||||
const [blueprints, setBlueprints] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -62,9 +64,10 @@ export default function SiteDashboard() {
|
||||
const loadSiteData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const [siteData, statsData] = await Promise.all([
|
||||
const [siteData, statsData, blueprintsData] = await Promise.all([
|
||||
fetchAPI(`/v1/auth/sites/${siteId}/`),
|
||||
fetchSiteStats(),
|
||||
fetchSiteBlueprints({ site_id: Number(siteId) }),
|
||||
]);
|
||||
|
||||
if (siteData) {
|
||||
@@ -74,6 +77,10 @@ export default function SiteDashboard() {
|
||||
if (statsData) {
|
||||
setStats(statsData);
|
||||
}
|
||||
|
||||
if (blueprintsData && blueprintsData.results) {
|
||||
setBlueprints(blueprintsData.results);
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(`Failed to load site data: ${error.message}`);
|
||||
} finally {
|
||||
@@ -207,6 +214,19 @@ export default function SiteDashboard() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stage 3: Site Progress Widget */}
|
||||
{blueprints.length > 0 && (
|
||||
<div className="mb-6">
|
||||
{blueprints.map((blueprint) => (
|
||||
<SiteProgressWidget
|
||||
key={blueprint.id}
|
||||
blueprintId={blueprint.id}
|
||||
siteId={Number(siteId)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
|
||||
{statCards.map((stat, index) => (
|
||||
|
||||
@@ -32,6 +32,13 @@ interface Content {
|
||||
sector: number;
|
||||
word_count?: number;
|
||||
metadata?: Record<string, any>;
|
||||
// Stage 3: Metadata fields
|
||||
entity_type?: string | null;
|
||||
cluster_name?: string | null;
|
||||
cluster_id?: number | null;
|
||||
taxonomy_name?: string | null;
|
||||
taxonomy_id?: number | null;
|
||||
cluster_role?: string | null;
|
||||
}
|
||||
|
||||
export default function PostEditor() {
|
||||
@@ -269,6 +276,10 @@ export default function PostEditor() {
|
||||
<div className="p-6">
|
||||
<PageMeta title={content.id ? 'Edit Post' : 'New Post'} />
|
||||
|
||||
<div className="flex gap-6">
|
||||
{/* Main Content Area */}
|
||||
<div className="flex-1">
|
||||
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
@@ -289,10 +300,11 @@ export default function PostEditor() {
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
disabled={saving || (content.status === 'publish' && validationResult && !validationResult.is_valid)}
|
||||
title={content.status === 'publish' && validationResult && !validationResult.is_valid ? 'Please fix validation errors before publishing' : undefined}
|
||||
>
|
||||
<SaveIcon className="w-4 h-4 mr-2" />
|
||||
{saving ? 'Saving...' : 'Save Post'}
|
||||
{saving ? 'Saving...' : content.status === 'publish' ? 'Publish' : 'Save Post'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -717,6 +729,103 @@ export default function PostEditor() {
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stage 3: Sidebar with Metadata Summary */}
|
||||
{content.id && (
|
||||
<div className="w-80 flex-shrink-0">
|
||||
<Card className="p-4 sticky top-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
||||
Content Metadata
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Entity Type */}
|
||||
{content.entity_type && (
|
||||
<div>
|
||||
<div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">
|
||||
Entity Type
|
||||
</div>
|
||||
<div className="text-sm text-gray-900 dark:text-white">
|
||||
{content.entity_type ? content.entity_type.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()) : '-'}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Cluster */}
|
||||
{content.cluster_name && (
|
||||
<div>
|
||||
<div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">
|
||||
Cluster
|
||||
</div>
|
||||
<div className="text-sm text-gray-900 dark:text-white">
|
||||
{content.cluster_name}
|
||||
{content.cluster_role && (
|
||||
<span className="ml-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
({content.cluster_role})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Taxonomy */}
|
||||
{content.taxonomy_name && (
|
||||
<div>
|
||||
<div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">
|
||||
Taxonomy
|
||||
</div>
|
||||
<div className="text-sm text-gray-900 dark:text-white">
|
||||
{content.taxonomy_name}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Validation Status */}
|
||||
{validationResult && (
|
||||
<div>
|
||||
<div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">
|
||||
Validation Status
|
||||
</div>
|
||||
<div className={`text-sm font-medium ${
|
||||
validationResult.is_valid
|
||||
? 'text-green-600 dark:text-green-400'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
}`}>
|
||||
{validationResult.is_valid ? '✓ Valid' : `✗ ${validationResult.validation_errors.length} error(s)`}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Quick Links */}
|
||||
<div className="pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2">
|
||||
Quick Actions
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{content.cluster_id && (
|
||||
<button
|
||||
onClick={() => navigate(`/planner/clusters/${content.cluster_id}`)}
|
||||
className="text-xs text-blue-600 dark:text-blue-400 hover:underline w-full text-left"
|
||||
>
|
||||
View Cluster →
|
||||
</button>
|
||||
)}
|
||||
{content.taxonomy_id && (
|
||||
<button
|
||||
onClick={() => navigate(`/sites/builder?taxonomy=${content.taxonomy_id}`)}
|
||||
className="text-xs text-blue-600 dark:text-blue-400 hover:underline w-full text-left"
|
||||
>
|
||||
View Taxonomy →
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user