more impeorventes for kewyrods libreary

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-14 18:55:08 +00:00
parent d2fc5b1a6b
commit cb2d109593
5 changed files with 291 additions and 6 deletions

View File

@@ -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 (
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
{[1, 2, 3].map(i => (
<Card key={i} className="p-6">
<div className="flex items-center justify-center py-8">
<Spinner size="md" />
</div>
</Card>
))}
</div>
);
}
if (!stats) {
return null;
}
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-5">
{/* Card 1: Top Industries */}
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-brand-100 dark:bg-brand-900/50 flex items-center justify-center">
<GridIcon className="w-5 h-5 text-brand-600 dark:text-brand-400" />
</div>
<div>
<h3 className="text-sm font-semibold text-gray-900 dark:text-white">
Starter Keywords
</h3>
<p className="text-xs text-gray-500 dark:text-gray-400">
By Industry
</p>
</div>
</div>
<div className="space-y-3">
{stats.industries.slice(0, 7).map((industry) => (
<div key={industry.slug} className="flex items-center justify-between">
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 dark:text-white truncate">
{industry.name}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
Vol: {industry.total_volume.toLocaleString()}
</p>
</div>
<Badge tone="brand" variant="soft" size="sm">
{industry.keyword_count.toLocaleString()}
</Badge>
</div>
))}
</div>
{stats.industries.length > 7 && (
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<p className="text-xs text-gray-500 dark:text-gray-400 text-center">
+{stats.industries.length - 7} more industries
</p>
</div>
)}
</Card>
{/* Card 2: By Country */}
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-purple-100 dark:bg-purple-900/50 flex items-center justify-center">
<GlobeIcon className="w-5 h-5 text-purple-600 dark:text-purple-400" />
</div>
<div>
<h3 className="text-sm font-semibold text-gray-900 dark:text-white">
By Country
</h3>
<p className="text-xs text-gray-500 dark:text-gray-400">
Geographic coverage
</p>
</div>
</div>
<div className="space-y-3">
{stats.countries.map((country) => (
<div key={country.country} className="flex items-center justify-between">
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 dark:text-white">
{country.country_display}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
Vol: {country.total_volume.toLocaleString()}
</p>
</div>
<Badge tone="purple" variant="soft" size="sm">
{country.keyword_count.toLocaleString()}
</Badge>
</div>
))}
</div>
</Card>
{/* Card 3: Total Stats */}
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-lg bg-success-100 dark:bg-success-900/50 flex items-center justify-center">
<DocsIcon className="w-5 h-5 text-success-600 dark:text-success-400" />
</div>
<div>
<h3 className="text-sm font-semibold text-gray-900 dark:text-white">
Total Available
</h3>
<p className="text-xs text-gray-500 dark:text-gray-400">
Global library
</p>
</div>
</div>
<div className="space-y-6">
<div>
<p className="text-xs text-gray-500 dark:text-gray-400 mb-1">Total Keywords</p>
<p className="text-3xl font-bold text-gray-900 dark:text-white">
{stats.total_keywords.toLocaleString()}
</p>
</div>
<div>
<p className="text-xs text-gray-500 dark:text-gray-400 mb-1">Total Search Volume</p>
<p className="text-2xl font-bold text-success-600 dark:text-success-400">
{stats.total_volume.toLocaleString()}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
Monthly searches
</p>
</div>
<div>
<p className="text-xs text-gray-500 dark:text-gray-400 mb-1">Industries Covered</p>
<p className="text-xl font-semibold text-gray-900 dark:text-white">
{stats.industries.length}
</p>
</div>
</div>
</Card>
</div>
);
}

View File

@@ -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<Subscription | null>(null);
const [keywordStats, setKeywordStats] = useState<KeywordStats | null>(null);
const [keywordStatsLoading, setKeywordStatsLoading] = useState(true);
// Dashboard data state
const [attentionItems, setAttentionItems] = useState<AttentionItem[]>([]);
@@ -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() {
/>
</div>
{/* Row 6: Keyword Library Stats (3 columns) */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
Quick-Start Keywords Complimentary
</h2>
<p className="text-sm text-gray-500 dark:text-gray-400">
Ready-to-use, pre-vetted keywords to jumpstart your content creation no research needed
</p>
</div>
</div>
<KeywordLibraryStatsWidget
stats={keywordStats}
loading={keywordStatsLoading}
/>
</div>
{/* Add Site Button - Floating */}
{canAddMoreSites && (
<div className="fixed bottom-6 right-6 z-50">

View File

@@ -954,7 +954,7 @@ export default function IndustriesSectorsKeywords() {
<CheckCircleIcon className="w-5 h-5 text-success-600 dark:text-success-400" />
<div>
<h3 className="text-sm font-medium text-success-900 dark:text-success-200">
High Opportunity Keywords Complete
Quick-Start Keywords Added Successfully
</h3>
<p className="text-xs text-success-700 dark:text-success-300">
{addedCount} keywords added to your workflow
@@ -982,11 +982,11 @@ export default function IndustriesSectorsKeywords() {
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-3">
<h2 className="text-xl font-bold text-gray-900 dark:text-white">
High Opportunity Keywords
Quick-Start Keywords Complimentary
</h2>
<Badge tone="brand" variant="soft" size="sm">
<BoltIcon className="w-3 h-3 mr-1" />
Curated
Pre-Vetted
</Badge>
</div>
{!allAdded && (
@@ -1000,7 +1000,7 @@ export default function IndustriesSectorsKeywords() {
)}
</div>
<p className="text-sm text-gray-600 dark:text-gray-400">
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.
</p>
</div>
@@ -1151,8 +1151,8 @@ export default function IndustriesSectorsKeywords() {
</h3>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4 max-w-2xl mx-auto">
💡 <strong>Recommended:</strong> 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.
💡 <strong>Recommended:</strong> 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.
</p>
<Button
variant="outline"

View File

@@ -2105,6 +2105,34 @@ export async function fetchSeedKeywords(filters?: {
return fetchAPI(`/v1/auth/seed-keywords/${queryString ? `?${queryString}` : ''}`);
}
/**
* Fetch Seed Keyword Statistics
*/
export interface KeywordStatsIndustry {
name: string;
slug: string;
keyword_count: number;
total_volume: number;
}
export interface KeywordStatsCountry {
country: string;
country_display: string;
keyword_count: number;
total_volume: number;
}
export interface KeywordStats {
industries: KeywordStatsIndustry[];
countries: KeywordStatsCountry[];
total_keywords: number;
total_volume: number;
}
export async function fetchKeywordStats(): Promise<KeywordStats> {
return fetchAPI('/v1/auth/seed-keywords/stats/');
}
/**
* Add SeedKeywords to workflow (create Keywords records)
*/