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

@@ -651,9 +651,69 @@ export default function Keywords() {
label: metric.label,
value: metric.calculate({ keywords, totalCount, clusters }),
accentColor: metric.accentColor,
tooltip: (metric as any).tooltip, // Add tooltip support
}));
}, [pageConfig?.headerMetrics, keywords, totalCount, clusters]);
// Calculate workflow insights based on UX doc principles
const workflowInsights = useMemo(() => {
const insights = [];
const clusteredCount = keywords.filter(k => k.cluster_id).length;
const unclusteredCount = totalCount - clusteredCount;
const pipelineReadiness = totalCount > 0 ? Math.round((clusteredCount / totalCount) * 100) : 0;
if (totalCount === 0) {
insights.push({
type: 'info' as const,
message: 'Import keywords to begin building your content strategy and unlock SEO opportunities',
});
return insights;
}
// Pipeline Readiness Score insight
if (pipelineReadiness < 30) {
insights.push({
type: 'warning' as const,
message: `Pipeline readiness at ${pipelineReadiness}% - Most keywords need clustering before content ideation can begin`,
});
} else if (pipelineReadiness < 60) {
insights.push({
type: 'info' as const,
message: `Pipeline readiness at ${pipelineReadiness}% - Clustering progress is moderate, continue organizing keywords`,
});
} else if (pipelineReadiness >= 85) {
insights.push({
type: 'success' as const,
message: `Excellent pipeline readiness (${pipelineReadiness}%) - Ready for content ideation phase`,
});
}
// Clustering Potential (minimum batch size check)
if (unclusteredCount >= 5) {
insights.push({
type: 'action' as const,
message: `${unclusteredCount} keywords available for auto-clustering (minimum batch size met)`,
});
} else if (unclusteredCount > 0 && unclusteredCount < 5) {
insights.push({
type: 'info' as const,
message: `${unclusteredCount} unclustered keywords - Need ${5 - unclusteredCount} more to run auto-cluster`,
});
}
// Coverage Gaps - thin clusters that need more research
const thinClusters = clusters.filter(c => (c.keywords_count || 0) === 1);
if (thinClusters.length > 3) {
const thinVolume = thinClusters.reduce((sum, c) => sum + (c.volume || 0), 0);
insights.push({
type: 'warning' as const,
message: `${thinClusters.length} clusters have only 1 keyword each (${thinVolume.toLocaleString()} monthly volume) - Consider expanding research`,
});
}
return insights;
}, [keywords, totalCount, clusters]);
// Handle create/edit
const handleSave = async () => {
try {
@@ -736,6 +796,7 @@ export default function Keywords() {
title="Keywords"
badge={{ icon: <ListIcon />, color: 'green' }}
navigation={<ModuleNavigationTabs tabs={plannerTabs} />}
workflowInsights={workflowInsights}
/>
<TablePageTemplate
columns={pageConfig.columns}
@@ -857,9 +918,9 @@ export default function Keywords() {
<ModuleMetricsFooter
metrics={[
{
title: 'Total Keywords',
title: 'Keywords',
value: totalCount.toLocaleString(),
subtitle: `${clusters.length} clusters`,
subtitle: `in ${clusters.length} clusters`,
icon: <ListIcon className="w-5 h-5" />,
accentColor: 'blue',
href: '/planner/keywords',
@@ -867,21 +928,21 @@ export default function Keywords() {
{
title: 'Clustered',
value: keywords.filter(k => k.cluster_id).length.toLocaleString(),
subtitle: `${Math.round((keywords.filter(k => k.cluster_id).length / Math.max(totalCount, 1)) * 100)}% coverage`,
subtitle: `${Math.round((keywords.filter(k => k.cluster_id).length / Math.max(totalCount, 1)) * 100)}% organized`,
icon: <GroupIcon className="w-5 h-5" />,
accentColor: 'purple',
href: '/planner/clusters',
},
{
title: 'Active',
value: keywords.filter(k => k.status === 'active').length.toLocaleString(),
subtitle: `${keywords.filter(k => k.status === 'pending').length} pending`,
title: 'Easy Wins',
value: keywords.filter(k => k.difficulty && k.difficulty <= 3 && (k.volume || 0) > 0).length.toLocaleString(),
subtitle: `Low difficulty with ${keywords.filter(k => k.difficulty && k.difficulty <= 3).reduce((sum, k) => sum + (k.volume || 0), 0).toLocaleString()} volume`,
icon: <BoltIcon className="w-5 h-5" />,
accentColor: 'green',
},
]}
progress={{
label: 'Keyword Clustering Progress',
label: 'Keyword Clustering Pipeline: Keywords organized into topical clusters',
value: totalCount > 0 ? Math.round((keywords.filter(k => k.cluster_id).length / totalCount) * 100) : 0,
color: 'primary',
}}