document.addEventListener('DOMContentLoaded', () => { // Initialize Lucide Icons lucide.createIcons(); // Elements const themeToggle = document.getElementById('themeToggle'); const startScrape = document.getElementById('startScrape'); const userInput = document.getElementById('userInput'); const landingHero = document.getElementById('landingHero'); const loadingState = document.getElementById('loadingState'); const resultsSection = document.getElementById('resultsSection'); const statusText = document.getElementById('statusText'); const progressBar = document.getElementById('progressBar'); // Config Elements const reviewCountType = document.getElementById('reviewCountType'); const sortOrder = document.getElementById('sortOrder'); const starRating = document.getElementById('starRating'); // Theme Logic themeToggle.addEventListener('click', () => { const html = document.documentElement; if (html.classList.contains('dark')) { html.classList.remove('dark'); localStorage.setItem('theme', 'light'); } else { html.classList.add('dark'); localStorage.setItem('theme', 'dark'); } }); // Load Saved Theme if (localStorage.getItem('theme') === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.documentElement.classList.add('dark'); } // Scrape Logic startScrape.addEventListener('click', async () => { const identifier = userInput.value.trim(); if (!identifier) { alert('Please enter an app name or URL'); return; } // 1. Transition to Loading landingHero.classList.add('hidden'); loadingState.classList.remove('hidden'); // 2. Mock Progress Simulation let progress = 0; const progressInterval = setInterval(() => { if (progress < 90) { progress += Math.random() * 5; progressBar.style.width = `${progress}%`; if (progress < 30) statusText.innerText = 'Connecting to Google Play...'; else if (progress < 60) statusText.innerText = 'Bypassing Protection...'; else statusText.innerText = 'Parsing Reviews...'; } }, 500); // 3. Actual API Call try { const countValue = reviewCountType.value === 'fixed_500' ? 500 : (reviewCountType.value === 'all' ? 'all' : 150); const response = await fetch('/scrape', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier, review_count_type: reviewCountType.value === 'all' ? 'all' : 'fixed', review_count: countValue, sort_order: sortOrder.value, star_rating: starRating.value }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Failed to fetch reviews'); } // 4. Success - Render Results clearInterval(progressInterval); progressBar.style.width = '100%'; statusText.innerText = 'Done!'; setTimeout(() => { renderResults(data); loadingState.classList.add('hidden'); resultsSection.classList.remove('hidden'); }, 600); } catch (err) { clearInterval(progressInterval); alert(err.message); loadingState.classList.add('hidden'); landingHero.classList.remove('hidden'); progressBar.style.width = '0%'; } }); function renderResults(data) { const { app_info, reviews } = data; // Update Header document.getElementById('appIcon').src = app_info.icon; document.getElementById('appTitle').innerText = app_info.title; document.getElementById('appScore').innerText = app_info.score.toFixed(1); document.getElementById('appReviewCount').innerText = formatNumber(app_info.reviews); document.getElementById('appDesc').innerText = app_info.summary; document.getElementById('reviewStats').innerText = `Showing ${reviews.length} results`; // Stars const stars = Math.round(app_info.score); const starContainer = document.getElementById('starContainer'); starContainer.innerHTML = ''; for (let i = 0; i < 5; i++) { const starIcon = document.createElement('i'); starIcon.setAttribute('data-lucide', 'star'); starIcon.classList.add('w-5', 'h-5'); if (i < stars) starIcon.classList.add('fill-current'); starContainer.appendChild(starIcon); } // Review Feed const feed = document.getElementById('reviewFeed'); feed.innerHTML = ''; reviews.forEach(review => { const card = document.createElement('div'); card.className = 'bg-white dark:bg-slate-900 p-6 rounded-2xl border border-slate-200 dark:border-slate-800 space-y-3 transition-hover hover:border-primary/30'; card.innerHTML = `
${review.userName}
${new Date(review.at).toLocaleDateString()}
${Array(review.score).fill('').join('')}

${review.content}

${review.thumbsUpCount}
`; feed.appendChild(card); }); lucide.createIcons(); // Download Action document.getElementById('downloadCSV').onclick = () => { downloadCSV(data.reviews, `${app_info.appId}_reviews.csv`); }; } function downloadCSV(arr, filename) { const headers = Object.keys(arr[0]).join(','); const rows = arr.map(obj => Object.values(obj).map(val => `"${val}"`).join(',')).join('\\n'); const csvContent = "data:text/csv;charset=utf-8," + headers + '\\n' + rows; const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", filename); document.body.appendChild(link); link.click(); document.body.removeChild(link); } function formatNumber(num) { if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M'; if (num >= 1000) return (num / 1000).toFixed(1) + 'K'; return num; } });