metrics adn insihigts
This commit is contained in:
@@ -25,6 +25,7 @@ import { useProgressModal } from '../../hooks/useProgressModal';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
|
||||
import { WorkflowInsight } from '../../components/common/WorkflowInsights';
|
||||
|
||||
export default function Content() {
|
||||
const toast = useToast();
|
||||
@@ -55,6 +56,59 @@ export default function Content() {
|
||||
const progressModal = useProgressModal();
|
||||
const hasReloadedRef = useRef(false);
|
||||
|
||||
// Calculate workflow insights
|
||||
const workflowInsights: WorkflowInsight[] = useMemo(() => {
|
||||
const insights: WorkflowInsight[] = [];
|
||||
const draftCount = content.filter(c => c.status === 'draft').length;
|
||||
const reviewCount = content.filter(c => c.status === 'review').length;
|
||||
const publishedCount = content.filter(c => c.status === 'published').length;
|
||||
const publishingRate = totalCount > 0 ? Math.round((publishedCount / totalCount) * 100) : 0;
|
||||
|
||||
if (totalCount === 0) {
|
||||
insights.push({
|
||||
type: 'info',
|
||||
message: 'No content yet - Generate content from tasks to build your content library',
|
||||
});
|
||||
return insights;
|
||||
}
|
||||
|
||||
// Draft vs Review status
|
||||
if (draftCount > reviewCount * 3 && draftCount >= 5) {
|
||||
insights.push({
|
||||
type: 'warning',
|
||||
message: `${draftCount} drafts waiting for review - Move content to review stage for quality assurance`,
|
||||
});
|
||||
} else if (draftCount > 0) {
|
||||
insights.push({
|
||||
type: 'info',
|
||||
message: `${draftCount} drafts in progress - Review and refine before moving to publish stage`,
|
||||
});
|
||||
}
|
||||
|
||||
// Review queue status
|
||||
if (reviewCount > 0) {
|
||||
insights.push({
|
||||
type: 'action',
|
||||
message: `${reviewCount} pieces awaiting final review - Approve and publish when ready`,
|
||||
});
|
||||
}
|
||||
|
||||
// Publishing readiness
|
||||
if (publishingRate >= 60 && publishedCount >= 10) {
|
||||
insights.push({
|
||||
type: 'success',
|
||||
message: `Strong publishing rate (${publishingRate}%) - ${publishedCount} articles ready for WordPress sync`,
|
||||
});
|
||||
} else if (publishedCount > 0) {
|
||||
insights.push({
|
||||
type: 'success',
|
||||
message: `${publishedCount} articles published (${publishingRate}%) - Continue moving content through the pipeline`,
|
||||
});
|
||||
}
|
||||
|
||||
return insights;
|
||||
}, [content, totalCount]);
|
||||
|
||||
// Load content - wrapped in useCallback
|
||||
const loadContent = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -167,6 +221,7 @@ export default function Content() {
|
||||
label: metric.label,
|
||||
value: metric.calculate({ content, totalCount }),
|
||||
accentColor: metric.accentColor,
|
||||
tooltip: (metric as any).tooltip,
|
||||
}));
|
||||
}, [pageConfig?.headerMetrics, content, totalCount]);
|
||||
|
||||
@@ -238,6 +293,7 @@ export default function Content() {
|
||||
title="Content Drafts"
|
||||
badge={{ icon: <FileIcon />, color: 'purple' }}
|
||||
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
|
||||
workflowInsights={workflowInsights}
|
||||
/>
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
@@ -283,27 +339,43 @@ export default function Content() {
|
||||
getItemDisplayName={(row: ContentType) => row.title || `Content #${row.id}`}
|
||||
/>
|
||||
|
||||
{/* Module Metrics Footer */}
|
||||
{/* Module Metrics Footer - Pipeline Style with Cross-Module Links */}
|
||||
<ModuleMetricsFooter
|
||||
metrics={[
|
||||
{
|
||||
title: 'Total Content',
|
||||
value: totalCount.toLocaleString(),
|
||||
subtitle: `${content.filter(c => c.status === 'published').length} published`,
|
||||
icon: <FileIcon className="w-5 h-5" />,
|
||||
accentColor: 'green',
|
||||
href: '/writer/content',
|
||||
title: 'Tasks',
|
||||
value: content.length.toLocaleString(),
|
||||
subtitle: 'generated from queue',
|
||||
icon: <TaskIcon className="w-5 h-5" />,
|
||||
accentColor: 'blue',
|
||||
href: '/writer/tasks',
|
||||
},
|
||||
{
|
||||
title: 'Draft',
|
||||
value: content.filter(c => c.status === 'draft').length.toLocaleString(),
|
||||
subtitle: `${content.filter(c => c.status === 'published').length} published`,
|
||||
icon: <TaskIcon className="w-5 h-5" />,
|
||||
subtitle: 'needs editing',
|
||||
icon: <FileIcon className="w-5 h-5" />,
|
||||
accentColor: 'amber',
|
||||
},
|
||||
{
|
||||
title: 'In Review',
|
||||
value: content.filter(c => c.status === 'review').length.toLocaleString(),
|
||||
subtitle: 'awaiting approval',
|
||||
icon: <CheckCircleIcon className="w-5 h-5" />,
|
||||
accentColor: 'blue',
|
||||
href: '/writer/review',
|
||||
},
|
||||
{
|
||||
title: 'Published',
|
||||
value: content.filter(c => c.status === 'published').length.toLocaleString(),
|
||||
subtitle: 'ready for sync',
|
||||
icon: <CheckCircleIcon className="w-5 h-5" />,
|
||||
accentColor: 'green',
|
||||
href: '/writer/published',
|
||||
},
|
||||
]}
|
||||
progress={{
|
||||
label: 'Content Publishing Progress',
|
||||
label: 'Content Publishing Pipeline: Content moved from draft through review to published (Draft \u2192 Review \u2192 Published)',
|
||||
value: totalCount > 0 ? Math.round((content.filter(c => c.status === 'published').length / totalCount) * 100) : 0,
|
||||
color: 'success',
|
||||
}}
|
||||
|
||||
@@ -492,6 +492,7 @@ export default function Images() {
|
||||
label: metric.label,
|
||||
value: metric.calculate({ images, totalCount }),
|
||||
accentColor: metric.accentColor,
|
||||
tooltip: (metric as any).tooltip,
|
||||
}));
|
||||
}, [pageConfig?.headerMetrics, images, totalCount]);
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ export default function Review() {
|
||||
pageConfig.headerMetrics.map(metric => ({
|
||||
...metric,
|
||||
value: metric.calculate({ content, totalCount }),
|
||||
tooltip: (metric as any).tooltip,
|
||||
})),
|
||||
[pageConfig.headerMetrics, content, totalCount]
|
||||
);
|
||||
|
||||
@@ -31,6 +31,8 @@ import { useSectorStore } from '../../store/sectorStore';
|
||||
import { usePageSizeStore } from '../../store/pageSizeStore';
|
||||
import PageHeader from '../../components/common/PageHeader';
|
||||
import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs';
|
||||
import ModuleMetricsFooter, { MetricItem, ProgressMetric } from '../../components/dashboard/ModuleMetricsFooter';
|
||||
import { WorkflowInsight } from '../../components/common/WorkflowInsights';
|
||||
|
||||
export default function Tasks() {
|
||||
const toast = useToast();
|
||||
@@ -79,6 +81,63 @@ export default function Tasks() {
|
||||
// Progress modal for AI functions
|
||||
const progressModal = useProgressModal();
|
||||
|
||||
// Calculate workflow insights
|
||||
const workflowInsights: WorkflowInsight[] = useMemo(() => {
|
||||
const insights: WorkflowInsight[] = [];
|
||||
const queuedCount = tasks.filter(t => t.status === 'queued').length;
|
||||
const processingCount = tasks.filter(t => t.status === 'in_progress').length;
|
||||
const completedCount = tasks.filter(t => t.status === 'completed').length;
|
||||
const failedCount = tasks.filter(t => t.status === 'failed').length;
|
||||
const completionRate = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0;
|
||||
|
||||
if (totalCount === 0) {
|
||||
insights.push({
|
||||
type: 'info',
|
||||
message: 'No tasks yet - Queue ideas from Planner to start generating content automatically',
|
||||
});
|
||||
return insights;
|
||||
}
|
||||
|
||||
// Queue status
|
||||
if (queuedCount > 10) {
|
||||
insights.push({
|
||||
type: 'warning',
|
||||
message: `Large queue detected (${queuedCount} tasks) - Content generation may take time, consider prioritizing`,
|
||||
});
|
||||
} else if (queuedCount > 0) {
|
||||
insights.push({
|
||||
type: 'info',
|
||||
message: `${queuedCount} tasks in queue - Content generation pipeline is active`,
|
||||
});
|
||||
}
|
||||
|
||||
// Processing status
|
||||
if (processingCount > 0) {
|
||||
insights.push({
|
||||
type: 'action',
|
||||
message: `${processingCount} tasks actively generating content - Check back soon for completed drafts`,
|
||||
});
|
||||
}
|
||||
|
||||
// Failed tasks
|
||||
if (failedCount > 0) {
|
||||
insights.push({
|
||||
type: 'warning',
|
||||
message: `${failedCount} tasks failed - Review errors and retry or adjust task parameters`,
|
||||
});
|
||||
}
|
||||
|
||||
// Completion success
|
||||
if (completionRate >= 70 && completedCount >= 5) {
|
||||
insights.push({
|
||||
type: 'success',
|
||||
message: `High completion rate (${completionRate}%) - ${completedCount} pieces of content ready for review`,
|
||||
});
|
||||
}
|
||||
|
||||
return insights;
|
||||
}, [tasks, totalCount]);
|
||||
|
||||
// AI Function Logs state
|
||||
const [aiLogs, setAiLogs] = useState<Array<{
|
||||
timestamp: string;
|
||||
@@ -522,6 +581,7 @@ export default function Tasks() {
|
||||
label: metric.label,
|
||||
value: metric.calculate({ tasks, totalCount }),
|
||||
accentColor: metric.accentColor,
|
||||
tooltip: (metric as any).tooltip,
|
||||
}));
|
||||
}, [pageConfig?.headerMetrics, tasks, totalCount]);
|
||||
|
||||
@@ -573,6 +633,7 @@ export default function Tasks() {
|
||||
title="Content Queue"
|
||||
badge={{ icon: <TaskIcon />, color: 'indigo' }}
|
||||
navigation={<ModuleNavigationTabs tabs={writerTabs} />}
|
||||
workflowInsights={workflowInsights}
|
||||
/>
|
||||
<TablePageTemplate
|
||||
columns={pageConfig.columns}
|
||||
@@ -678,6 +739,47 @@ export default function Tasks() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Module Metrics Footer - Pipeline Style with Cross-Module Links */}
|
||||
<ModuleMetricsFooter
|
||||
metrics={[
|
||||
{
|
||||
title: 'Ideas',
|
||||
value: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0).toLocaleString(),
|
||||
subtitle: 'from planner',
|
||||
icon: <TaskIcon className="w-5 h-5" />,
|
||||
accentColor: 'orange',
|
||||
href: '/planner/ideas',
|
||||
},
|
||||
{
|
||||
title: 'In Queue',
|
||||
value: tasks.filter(t => t.status === 'queued').length.toLocaleString(),
|
||||
subtitle: 'waiting for processing',
|
||||
icon: <TaskIcon className="w-5 h-5" />,
|
||||
accentColor: 'amber',
|
||||
},
|
||||
{
|
||||
title: 'Processing',
|
||||
value: tasks.filter(t => t.status === 'in_progress').length.toLocaleString(),
|
||||
subtitle: 'generating content',
|
||||
icon: <TaskIcon className="w-5 h-5" />,
|
||||
accentColor: 'blue',
|
||||
},
|
||||
{
|
||||
title: 'Ready for Review',
|
||||
value: tasks.filter(t => t.status === 'completed').length.toLocaleString(),
|
||||
subtitle: 'content generated',
|
||||
icon: <CheckCircleIcon className="w-5 h-5" />,
|
||||
accentColor: 'green',
|
||||
href: '/writer/content',
|
||||
},
|
||||
]}
|
||||
progress={{
|
||||
label: 'Content Generation Pipeline: Tasks successfully completed (Queued → Processing → Completed)',
|
||||
value: totalCount > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / totalCount) * 100) : 0,
|
||||
color: 'success',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Progress Modal for AI Functions */}
|
||||
<ProgressModal
|
||||
isOpen={progressModal.isOpen}
|
||||
|
||||
Reference in New Issue
Block a user