diff --git a/app.js b/app.js index 70d5379..f643e2f 100644 --- a/app.js +++ b/app.js @@ -43,15 +43,28 @@ document.addEventListener('DOMContentLoaded', () => { } }; + // Import Google Trends API functions + import { fetchGoogleTrends, getTrendingDate } from './trends-api.js'; + // Fetch trending topics from multiple sources for diversity const fetchTrendingTopics = async () => { try { - // Source 1: Wikipedia trending articles + let combinedTopics = []; + + // Source 1: Google Trends API + console.log('Fetching trending topics from Google Trends...'); + const trendingData = await fetchGoogleTrends(); + if (trendingData && trendingData.length > 0) { + combinedTopics = [...combinedTopics, ...trendingData]; + } + + // Update trending date display + updateTrendingDateDisplay(); + + // Source 2: Wikipedia trending articles (as backup) const wikiResponse = await fetch('https://wikimedia.org/api/rest_v1/metrics/pageviews/top/en.wikipedia/all-access/2023/01/all-days'); const wikiData = await wikiResponse.json(); - let combinedTopics = []; - // Process Wikipedia data if (wikiData && wikiData.items && wikiData.items[0] && wikiData.items[0].articles) { const wikiTopics = wikiData.items[0].articles @@ -68,23 +81,6 @@ document.addEventListener('DOMContentLoaded', () => { combinedTopics = [...combinedTopics, ...wikiTopics]; } - // Source 2: Use Google Trends API-like data (simulated) - // In a real implementation, you would use Google Trends API - const techTrendingTopics = [ - { keyword: 'quantum computing applications', searchVolume: 85000 }, - { keyword: 'edge computing use cases', searchVolume: 62000 }, - { keyword: 'zero-knowledge proofs', searchVolume: 45000 }, - { keyword: 'synthetic data generation', searchVolume: 73000 }, - { keyword: 'federated learning models', searchVolume: 58000 }, - { keyword: 'serverless architecture patterns', searchVolume: 67000 }, - { keyword: 'homomorphic encryption', searchVolume: 41000 }, - { keyword: 'computer vision in healthcare', searchVolume: 89000 }, - { keyword: 'explainable ai techniques', searchVolume: 76000 }, - { keyword: 'graph neural networks', searchVolume: 52000 } - ].map(item => ({ ...item, source: 'tech_trends' })); - - combinedTopics = [...combinedTopics, ...techTrendingTopics]; - // Source 3: Emerging research topics (simulated) const emergingTopics = [ { keyword: 'biomimetic materials science', searchVolume: 32000 }, @@ -106,6 +102,18 @@ document.addEventListener('DOMContentLoaded', () => { } }; + // Update trending date display in the UI + const updateTrendingDateDisplay = () => { + const dateInfo = getTrendingDate(); + const dateDisplay = document.getElementById('trending-date'); + + if (dateDisplay) { + const formattedDate = new Date(dateInfo.timestamp).toLocaleString(); + dateDisplay.textContent = `Trending as of: ${formattedDate}`; + dateDisplay.style.display = 'block'; + } + }; + // Enrich keywords with search result counts const enrichKeywordsWithOpportunityData = async (keywords) => { // In a production environment, you would use a real search API @@ -154,17 +162,35 @@ document.addEventListener('DOMContentLoaded', () => { const maxResultCount = Math.max(...keywords.map(k => k.resultCount)); const normalizedResultCount = 100 - ((keyword.resultCount / maxResultCount) * 100); + // Calculate SR (Search Relevance) - how relevant the keyword is based on search volume + const searchRelevance = Math.round(normalizedSearchVolume); + + // Calculate DR (Difficulty Rating) - how difficult it is to rank for this keyword + // Lower result count means easier to rank (lower difficulty) + const difficultyRating = Math.round(100 - normalizedResultCount); + // Calculate opportunity score (weighted average) // 60% weight to search volume, 40% weight to inverse result count - const opportunityScore = (normalizedSearchVolume * 0.6) + (normalizedResultCount * 0.4); + const opportunityScore = Math.round((normalizedSearchVolume * 0.6) + (normalizedResultCount * 0.4)); // Calculate competition level (lower is better) - const competitionLevel = keyword.resultCount / keyword.searchVolume; + const competitionLevel = Math.round(keyword.resultCount / keyword.searchVolume); + + // Calculate keyword quality score (0-100) for color coding + // Higher is better - combines all metrics + const qualityScore = Math.round( + (opportunityScore * 0.5) + + (searchRelevance * 0.3) + + ((100 - difficultyRating) * 0.2) + ); return { ...keyword, - opportunityScore: Math.round(opportunityScore), - competitionLevel: Math.round(competitionLevel) + opportunityScore, + searchRelevance, + difficultyRating, + competitionLevel, + qualityScore }; }); @@ -207,13 +233,19 @@ document.addEventListener('DOMContentLoaded', () => { const resultCount = keyword.resultCount ? `${(keyword.resultCount/1000).toFixed(1)}K` : 'N/A'; const competitionLevel = keyword.competitionLevel || 'N/A'; + // Determine color based on quality score + const qualityScore = keyword.qualityScore || score; + const qualityColor = getQualityColor(qualityScore); + keywordCard.innerHTML = `
Discover globally trending topics and generate article prompts
+Discover high-opportunity keywords with low competition
+