metrics adn insihigts

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-15 06:51:14 +00:00
parent cff00f87ff
commit c61cf7c39f
21 changed files with 749 additions and 129 deletions

View File

@@ -28,6 +28,8 @@ import { usePageSizeStore } from '../../store/pageSizeStore';
import { getDifficultyLabelFromNumber, getDifficultyRange } from '../../utils/difficulty';
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 Clusters() {
const toast = useToast();
@@ -75,6 +77,61 @@ export default function Clusters() {
const progressModal = useProgressModal();
const hasReloadedRef = useRef(false);
// Calculate workflow insights
const workflowInsights: WorkflowInsight[] = useMemo(() => {
const insights: WorkflowInsight[] = [];
const clustersWithIdeas = clusters.filter(c => (c.ideas_count || 0) > 0).length;
const totalIdeas = clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0);
const emptyClusters = clusters.filter(c => (c.keywords_count || 0) === 0).length;
const thinClusters = clusters.filter(c => (c.keywords_count || 0) > 0 && (c.keywords_count || 0) < 3).length;
const readyForGeneration = clustersWithIdeas;
const generationRate = totalCount > 0 ? Math.round((readyForGeneration / totalCount) * 100) : 0;
if (totalCount === 0) {
insights.push({
type: 'info',
message: 'Create clusters to organize keywords into topical groups for better content planning',
});
return insights;
}
// Content generation readiness
if (generationRate < 30) {
insights.push({
type: 'warning',
message: `Only ${generationRate}% of clusters have content ideas - Generate ideas to unlock content pipeline`,
});
} else if (generationRate >= 70) {
insights.push({
type: 'success',
message: `${generationRate}% of clusters have ideas (${totalIdeas} total) - Strong content pipeline ready`,
});
}
// Empty or thin clusters
if (emptyClusters > 0) {
insights.push({
type: 'warning',
message: `${emptyClusters} clusters have no keywords - Map keywords or delete unused clusters`,
});
} else if (thinClusters > 2) {
insights.push({
type: 'info',
message: `${thinClusters} clusters have fewer than 3 keywords - Consider adding more related keywords for better coverage`,
});
}
// Actionable next step
if (totalIdeas === 0) {
insights.push({
type: 'action',
message: 'Select clusters and use Auto-Generate Ideas to create content briefs',
});
}
return insights;
}, [clusters, totalCount]);
// Load clusters - wrapped in useCallback to prevent infinite loops
const loadClusters = useCallback(async () => {
setLoading(true);
@@ -353,6 +410,7 @@ export default function Clusters() {
label: metric.label,
value: metric.calculate({ clusters, totalCount }),
accentColor: metric.accentColor,
tooltip: (metric as any).tooltip,
}));
}, [pageConfig?.headerMetrics, clusters, totalCount]);
@@ -397,6 +455,7 @@ export default function Clusters() {
title="Keyword Clusters"
badge={{ icon: <GroupIcon />, color: 'purple' }}
navigation={<ModuleNavigationTabs tabs={plannerTabs} />}
workflowInsights={workflowInsights}
/>
<TablePageTemplate
columns={pageConfig.columns}
@@ -490,6 +549,40 @@ export default function Clusters() {
}}
/>
{/* Module Metrics Footer - Pipeline Style with Cross-Module Links */}
<ModuleMetricsFooter
metrics={[
{
title: 'Keywords',
value: clusters.reduce((sum, c) => sum + (c.keywords_count || 0), 0).toLocaleString(),
subtitle: `in ${totalCount} clusters`,
icon: <ListIcon className="w-5 h-5" />,
accentColor: 'blue',
href: '/planner/keywords',
},
{
title: 'Content Ideas',
value: clusters.reduce((sum, c) => sum + (c.ideas_count || 0), 0).toLocaleString(),
subtitle: `across ${clusters.filter(c => (c.ideas_count || 0) > 0).length} clusters`,
icon: <BoltIcon className="w-5 h-5" />,
accentColor: 'green',
href: '/planner/ideas',
},
{
title: 'Ready to Write',
value: clusters.filter(c => (c.ideas_count || 0) > 0 && c.status === 'active').length.toLocaleString(),
subtitle: 'clusters with approved ideas',
icon: <GroupIcon className="w-5 h-5" />,
accentColor: 'purple',
},
]}
progress={{
label: 'Idea Generation Pipeline: Clusters with content ideas generated (ready for downstream content creation)',
value: totalCount > 0 ? Math.round((clusters.filter(c => (c.ideas_count || 0) > 0).length / totalCount) * 100) : 0,
color: 'purple',
}}
/>
{/* Progress Modal for AI Functions */}
<ProgressModal
isOpen={progressModal.isOpen}