metricsa dn backedn fixes

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-29 04:33:22 +00:00
parent 53fdebf733
commit 0ffd21b9bf
17 changed files with 929 additions and 266 deletions

View File

@@ -11,15 +11,10 @@ import { useOnboardingStore } from "../../store/onboardingStore";
import { useBillingStore } from "../../store/billingStore";
import { GridIcon, PlusIcon } from "../../icons";
import {
fetchKeywords,
fetchClusters,
fetchContentIdeas,
fetchTasks,
fetchContent,
fetchContentImages,
fetchSites,
Site,
} from "../../services/api";
import { getDashboardStats } from "../../services/billing.api";
import { useSiteStore } from "../../store/siteStore";
import { useSectorStore } from "../../store/sectorStore";
import { useToast } from "../../components/ui/toast/ToastContainer";
@@ -155,49 +150,33 @@ export default function Home() {
const fetchDashboardData = useCallback(async () => {
try {
setLoading(true);
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
const siteId = siteFilter === 'all' ? undefined : siteFilter;
// Fetch pipeline counts sequentially to avoid rate limiting
const keywordsRes = await fetchKeywords({ page_size: 1, site_id: siteId });
await delay(100);
const clustersRes = await fetchClusters({ page_size: 1, site_id: siteId });
await delay(100);
const ideasRes = await fetchContentIdeas({ page_size: 1, site_id: siteId });
await delay(100);
const tasksRes = await fetchTasks({ page_size: 1, site_id: siteId });
await delay(100);
const contentRes = await fetchContent({ page_size: 1, site_id: siteId });
await delay(100);
const imagesRes = await fetchContentImages({ page_size: 1, site_id: siteId });
// Fetch real dashboard stats from API
const stats = await getDashboardStats({
site_id: siteId,
days: 7
});
const totalKeywords = keywordsRes.count || 0;
const totalClusters = clustersRes.count || 0;
const totalIdeas = ideasRes.count || 0;
const totalTasks = tasksRes.count || 0;
const totalContent = contentRes.count || 0;
const totalImages = imagesRes.count || 0;
const publishedContent = Math.floor(totalContent * 0.6); // Placeholder
// Calculate completion percentage
const completionPercentage = totalKeywords > 0
? Math.round((publishedContent / totalKeywords) * 100)
// Update pipeline data from real API data
const { pipeline, counts } = stats;
const completionPercentage = pipeline.keywords > 0
? Math.round((pipeline.published / pipeline.keywords) * 100)
: 0;
// Update pipeline data
setPipelineData({
sites: sites.length,
keywords: totalKeywords,
clusters: totalClusters,
ideas: totalIdeas,
tasks: totalTasks,
drafts: totalContent,
published: publishedContent,
sites: pipeline.sites,
keywords: pipeline.keywords,
clusters: pipeline.clusters,
ideas: pipeline.ideas,
tasks: pipeline.tasks,
drafts: pipeline.drafts,
published: pipeline.published,
completionPercentage: Math.min(completionPercentage, 100),
});
// Generate attention items based on data
// Generate attention items based on real data
const attentionList: AttentionItem[] = [];
// Check for sites without sectors
@@ -213,130 +192,85 @@ export default function Home() {
});
}
// Check for content needing images
const contentWithoutImages = totalContent - Math.floor(totalContent * 0.7);
if (contentWithoutImages > 0 && totalContent > 0) {
// Check for content needing images (content in review without all images generated)
const contentWithPendingImages = counts.images.pending;
if (contentWithPendingImages > 0) {
attentionList.push({
id: 'needs_images',
type: 'pending_review',
title: 'articles need images',
count: contentWithoutImages,
title: 'images pending',
count: contentWithPendingImages,
description: 'Generate images before publishing',
actionLabel: 'Generate Images',
actionHref: '/writer/images',
});
}
// Check for content in review
if (counts.content.review > 0) {
attentionList.push({
id: 'pending_review',
type: 'pending_review',
title: 'articles ready for review',
count: counts.content.review,
description: 'Review and publish content',
actionLabel: 'Review Content',
actionHref: '/writer/content?status=review',
});
}
setAttentionItems(attentionList);
// Update content velocity (using mock calculations based on totals)
const weeklyArticles = Math.floor(totalContent * 0.15);
const monthlyArticles = Math.floor(totalContent * 0.4);
setContentVelocity({
thisWeek: {
articles: weeklyArticles,
words: weeklyArticles * 1500,
images: Math.floor(totalImages * 0.15)
},
thisMonth: {
articles: monthlyArticles,
words: monthlyArticles * 1500,
images: Math.floor(totalImages * 0.4)
},
total: {
articles: totalContent,
words: totalContent * 1500,
images: totalImages
},
trend: totalContent > 0 ? Math.floor(Math.random() * 40) - 10 : 0,
});
// Update content velocity from real API data
setContentVelocity(stats.content_velocity);
// Generate mock recent activity based on actual data
const activityList: ActivityItem[] = [];
if (totalClusters > 0) {
activityList.push({
id: 'cluster_1',
type: 'clustering',
title: `Clustered ${Math.min(45, totalKeywords)} keywords → ${Math.min(8, totalClusters)} clusters`,
description: '',
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
href: '/planner/clusters',
});
}
if (totalContent > 0) {
activityList.push({
id: 'content_1',
type: 'content',
title: `Generated ${Math.min(5, totalContent)} articles`,
description: '',
timestamp: new Date(Date.now() - 4 * 60 * 60 * 1000),
href: '/writer/content',
});
}
if (totalImages > 0) {
activityList.push({
id: 'images_1',
type: 'images',
title: `Created ${Math.min(15, totalImages)} image prompts`,
description: '',
timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000),
href: '/writer/images',
});
}
if (publishedContent > 0) {
activityList.push({
id: 'published_1',
type: 'published',
title: `Published article to WordPress`,
description: '',
timestamp: new Date(Date.now() - 24 * 60 * 60 * 1000),
href: '/writer/published',
});
}
if (totalKeywords > 0) {
activityList.push({
id: 'keywords_1',
type: 'keywords',
title: `Added ${Math.min(23, totalKeywords)} keywords`,
description: '',
timestamp: new Date(Date.now() - 48 * 60 * 60 * 1000),
href: '/planner/keywords',
});
}
// Update recent activity from real API data (convert timestamp strings to Date objects)
const activityList: ActivityItem[] = stats.recent_activity.map(item => ({
...item,
timestamp: new Date(item.timestamp),
}));
setRecentActivity(activityList);
// Update AI operations (mock data based on content created)
const clusteringOps = totalClusters > 0 ? Math.ceil(totalClusters / 3) : 0;
const ideasOps = totalIdeas > 0 ? Math.ceil(totalIdeas / 5) : 0;
const contentOps = totalContent;
const imageOps = totalImages > 0 ? Math.ceil(totalImages / 3) : 0;
// Update AI operations from real API data
// Map operation types to display types
const operationTypeMap: Record<string, string> = {
'clustering': 'clustering',
'idea_generation': 'ideas',
'content_generation': 'content',
'image_generation': 'images',
'image_prompt_extraction': 'images',
};
const mappedOperations = stats.ai_operations.operations.map(op => ({
type: operationTypeMap[op.type] || op.type,
count: op.count,
credits: op.credits,
}));
// Ensure all expected types exist
const expectedTypes = ['clustering', 'ideas', 'content', 'images'];
for (const type of expectedTypes) {
if (!mappedOperations.find(op => op.type === type)) {
mappedOperations.push({ type, count: 0, credits: 0 });
}
}
setAIOperations({
period: '7d',
operations: [
{ type: 'clustering', count: clusteringOps, credits: clusteringOps * 10 },
{ type: 'ideas', count: ideasOps, credits: ideasOps * 2 },
{ type: 'content', count: contentOps, credits: contentOps * 50 },
{ type: 'images', count: imageOps, credits: imageOps * 5 },
],
totals: {
count: clusteringOps + ideasOps + contentOps + imageOps,
credits: (clusteringOps * 10) + (ideasOps * 2) + (contentOps * 50) + (imageOps * 5),
successRate: 98.5,
avgCreditsPerOp: contentOps > 0 ? 18.6 : 0,
},
period: stats.ai_operations.period,
operations: mappedOperations,
totals: stats.ai_operations.totals,
});
// Set automation status (would come from API in real implementation)
// Set automation status (would come from automation API)
setAutomationData({
status: sites.length > 0 ? 'active' : 'not_configured',
schedule: sites.length > 0 ? 'Daily 9 AM' : undefined,
lastRun: sites.length > 0 ? {
lastRun: sites.length > 0 && counts.content.total > 0 ? {
timestamp: new Date(Date.now() - 12 * 60 * 60 * 1000),
clustered: Math.min(12, totalKeywords),
ideas: Math.min(8, totalIdeas),
content: Math.min(5, totalContent),
images: Math.min(15, totalImages),
clustered: pipeline.clusters,
ideas: pipeline.ideas,
content: counts.content.total,
images: counts.images.total,
success: true,
} : undefined,
nextRun: sites.length > 0 ? new Date(Date.now() + 12 * 60 * 60 * 1000) : undefined,
@@ -352,7 +286,7 @@ export default function Home() {
} finally {
setLoading(false);
}
}, [siteFilter, sites.length, toast]);
}, [siteFilter, sites, toast]);
// Fetch dashboard data when filter changes
useEffect(() => {