From cb2d1095938aaf7cd5a63c8935c0297bf4fb355e Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Wed, 14 Jan 2026 18:55:08 +0000 Subject: [PATCH] more impeorventes for kewyrods libreary --- backend/igny8_core/auth/views.py | 60 +++++++ .../dashboard/KeywordLibraryStatsWidget.tsx | 160 ++++++++++++++++++ frontend/src/pages/Dashboard/Home.tsx | 37 ++++ .../pages/Setup/IndustriesSectorsKeywords.tsx | 12 +- frontend/src/services/api.ts | 28 +++ 5 files changed, 291 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/dashboard/KeywordLibraryStatsWidget.tsx diff --git a/backend/igny8_core/auth/views.py b/backend/igny8_core/auth/views.py index b6782836..4fb13e44 100644 --- a/backend/igny8_core/auth/views.py +++ b/backend/igny8_core/auth/views.py @@ -873,6 +873,66 @@ class SeedKeywordViewSet(viewsets.ReadOnlyModelViewSet): return queryset + @action(detail=False, methods=['get'], url_path='stats', url_name='stats') + def stats(self, request): + """ + Get aggregated keyword statistics by industry and country. + Returns top industries and countries with keyword counts and total volume. + """ + from django.db.models import Count, Sum, Q + + try: + # Top industries by keyword count + industries = Industry.objects.annotate( + keyword_count=Count('seed_keywords', filter=Q(seed_keywords__is_active=True)), + total_volume=Sum('seed_keywords__volume', filter=Q(seed_keywords__is_active=True)) + ).filter( + keyword_count__gt=0 + ).order_by('-keyword_count')[:10] + + industries_data = [{ + 'name': ind.name, + 'slug': ind.slug, + 'keyword_count': ind.keyword_count or 0, + 'total_volume': ind.total_volume or 0, + } for ind in industries] + + # Keywords by country + countries = SeedKeyword.objects.filter( + is_active=True + ).values('country').annotate( + keyword_count=Count('id'), + total_volume=Sum('volume') + ).order_by('-keyword_count') + + countries_data = [{ + 'country': c['country'], + 'country_display': dict(SeedKeyword.COUNTRY_CHOICES).get(c['country'], c['country']), + 'keyword_count': c['keyword_count'], + 'total_volume': c['total_volume'] or 0, + } for c in countries] + + # Total stats + total_stats = SeedKeyword.objects.filter(is_active=True).aggregate( + total_keywords=Count('id'), + total_volume=Sum('volume') + ) + + data = { + 'industries': industries_data, + 'countries': countries_data, + 'total_keywords': total_stats['total_keywords'] or 0, + 'total_volume': total_stats['total_volume'] or 0, + } + + return success_response(data=data, request=request) + except Exception as e: + return error_response( + error=f'Failed to fetch keyword stats: {str(e)}', + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + request=request + ) + @action(detail=False, methods=['post'], url_path='import_seed_keywords', url_name='import_seed_keywords') def import_seed_keywords(self, request): """ diff --git a/frontend/src/components/dashboard/KeywordLibraryStatsWidget.tsx b/frontend/src/components/dashboard/KeywordLibraryStatsWidget.tsx new file mode 100644 index 00000000..a80e969e --- /dev/null +++ b/frontend/src/components/dashboard/KeywordLibraryStatsWidget.tsx @@ -0,0 +1,160 @@ +/** + * Keyword Library Stats Widget + * Shows global seed keyword statistics: industries, countries, and totals + */ + +import { Card } from '../ui/card'; +import Badge from '../ui/badge/Badge'; +import { Spinner } from '../ui/spinner/Spinner'; +import { DocsIcon, GridIcon, GlobeIcon } from '../../icons'; +import { KeywordStats } from '../../services/api'; + +interface KeywordLibraryStatsWidgetProps { + stats: KeywordStats | null; + loading: boolean; +} + +export default function KeywordLibraryStatsWidget({ stats, loading }: KeywordLibraryStatsWidgetProps) { + if (loading) { + return ( +
+ {[1, 2, 3].map(i => ( + +
+ +
+
+ ))} +
+ ); + } + + if (!stats) { + return null; + } + + return ( +
+ {/* Card 1: Top Industries */} + +
+
+ +
+
+

+ Starter Keywords +

+

+ By Industry +

+
+
+ +
+ {stats.industries.slice(0, 7).map((industry) => ( +
+
+

+ {industry.name} +

+

+ Vol: {industry.total_volume.toLocaleString()} +

+
+ + {industry.keyword_count.toLocaleString()} + +
+ ))} +
+ + {stats.industries.length > 7 && ( +
+

+ +{stats.industries.length - 7} more industries +

+
+ )} +
+ + {/* Card 2: By Country */} + +
+
+ +
+
+

+ By Country +

+

+ Geographic coverage +

+
+
+ +
+ {stats.countries.map((country) => ( +
+
+

+ {country.country_display} +

+

+ Vol: {country.total_volume.toLocaleString()} +

+
+ + {country.keyword_count.toLocaleString()} + +
+ ))} +
+
+ + {/* Card 3: Total Stats */} + +
+
+ +
+
+

+ Total Available +

+

+ Global library +

+
+
+ +
+
+

Total Keywords

+

+ {stats.total_keywords.toLocaleString()} +

+
+ +
+

Total Search Volume

+

+ {stats.total_volume.toLocaleString()} +

+

+ Monthly searches +

+
+ +
+

Industries Covered

+

+ {stats.industries.length} +

+
+
+
+
+ ); +} diff --git a/frontend/src/pages/Dashboard/Home.tsx b/frontend/src/pages/Dashboard/Home.tsx index 836be0b4..8a341af0 100644 --- a/frontend/src/pages/Dashboard/Home.tsx +++ b/frontend/src/pages/Dashboard/Home.tsx @@ -13,6 +13,8 @@ import { GridIcon, PlusIcon } from "../../icons"; import { fetchSites, Site, + fetchKeywordStats, + KeywordStats, } from "../../services/api"; import { getDashboardStats } from "../../services/billing.api"; import { useSiteStore } from "../../store/siteStore"; @@ -33,6 +35,7 @@ import AutomationStatusWidget, { AutomationData } from "../../components/dashboa import SitesOverviewWidget from "../../components/dashboard/SitesOverviewWidget"; import CreditsUsageWidget from "../../components/dashboard/CreditsUsageWidget"; import AccountInfoWidget from "../../components/dashboard/AccountInfoWidget"; +import KeywordLibraryStatsWidget from "../../components/dashboard/KeywordLibraryStatsWidget"; import { getSubscriptions, Subscription } from "../../services/billing.api"; export default function Home() { @@ -52,6 +55,8 @@ export default function Home() { const [showAddSite, setShowAddSite] = useState(false); const [loading, setLoading] = useState(true); const [subscription, setSubscription] = useState(null); + const [keywordStats, setKeywordStats] = useState(null); + const [keywordStatsLoading, setKeywordStatsLoading] = useState(true); // Dashboard data state const [attentionItems, setAttentionItems] = useState([]); @@ -114,9 +119,23 @@ export default function Home() { loadSites(); loadBalance(); loadSubscription(); + loadKeywordStats(); loadFromBackend().catch(() => {}); }, [loadFromBackend, loadBalance]); + // Load keyword stats + const loadKeywordStats = async () => { + try { + setKeywordStatsLoading(true); + const stats = await fetchKeywordStats(); + setKeywordStats(stats); + } catch (error) { + console.error('Failed to load keyword stats:', error); + } finally { + setKeywordStatsLoading(false); + } + }; + // Load subscription info const loadSubscription = async () => { try { @@ -414,6 +433,24 @@ export default function Home() { /> + {/* Row 6: Keyword Library Stats (3 columns) */} +
+
+
+

+ Quick-Start Keywords — Complimentary +

+

+ Ready-to-use, pre-vetted keywords to jumpstart your content creation — no research needed +

+
+
+ +
+ {/* Add Site Button - Floating */} {canAddMoreSites && (
diff --git a/frontend/src/pages/Setup/IndustriesSectorsKeywords.tsx b/frontend/src/pages/Setup/IndustriesSectorsKeywords.tsx index c9dbdd8c..aaa3d7e3 100644 --- a/frontend/src/pages/Setup/IndustriesSectorsKeywords.tsx +++ b/frontend/src/pages/Setup/IndustriesSectorsKeywords.tsx @@ -954,7 +954,7 @@ export default function IndustriesSectorsKeywords() {

- High Opportunity Keywords Complete + Quick-Start Keywords Added Successfully

{addedCount} keywords added to your workflow @@ -982,11 +982,11 @@ export default function IndustriesSectorsKeywords() {

- High Opportunity Keywords + Quick-Start Keywords — Complimentary

- Curated + Pre-Vetted
{!allAdded && ( @@ -1000,7 +1000,7 @@ export default function IndustriesSectorsKeywords() { )}

- Add top keywords for each of your sectors. Keywords will be added to your planner workflow. + Ready-to-use keywords to jumpstart your content — no research needed. Simply add to your workflow and start creating.

@@ -1151,8 +1151,8 @@ export default function IndustriesSectorsKeywords() {

- 💡 Recommended: Start by adding High Opportunity Keywords from the section above. - They're curated for your sectors and ready to use. Once you've added those, you can browse our full keyword library below for additional targeted keywords. + 💡 Recommended: Start with the complimentary Quick-Start Keywords above to accelerate your workflow. + They're pre-vetted and ready to use immediately. Once added, you can browse our full library below for additional targeted keywords.