From d2d44d2aebb165c21c4949c2cc9b1d46c0832aa3 Mon Sep 17 00:00:00 2001 From: Kelly Date: Thu, 4 Dec 2025 18:26:56 -0700 Subject: [PATCH] Improve ScraperMonitor tab loading efficiency and add New/Updated columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tab-specific data loading: Only fetch APIs needed for the active tab - AZ Live tab fetches only AZ monitor APIs - Dispensary Jobs tab fetches only legacy job APIs - Crawl History tab fetches only scraper history APIs - Auto-refresh now respects active tab - Added New and Updated columns to Crawl History table with color coding 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- frontend/src/pages/ScraperMonitor.tsx | 200 ++++++++++++++++---------- 1 file changed, 121 insertions(+), 79 deletions(-) diff --git a/frontend/src/pages/ScraperMonitor.tsx b/frontend/src/pages/ScraperMonitor.tsx index df685cb3..e9f4967f 100644 --- a/frontend/src/pages/ScraperMonitor.tsx +++ b/frontend/src/pages/ScraperMonitor.tsx @@ -21,45 +21,42 @@ export function ScraperMonitor() { const [azRecentJobs, setAzRecentJobs] = useState({ jobLogs: [], crawlJobs: [] }); const [azErrors, setAzErrors] = useState([]); - useEffect(() => { - loadData(); - - if (autoRefresh) { - const interval = setInterval(loadData, 3000); // Refresh every 3 seconds - return () => clearInterval(interval); - } - }, [autoRefresh]); - - const loadData = async () => { + // Load data based on active tab for efficiency + const loadTabData = async (tab: typeof activeTab) => { try { - const [activeData, historyData, statsData, jobsData, workersData, recentJobsData] = await Promise.all([ - api.getActiveScrapers(), - api.getScraperHistory(), - api.getJobStats(), - api.getActiveJobs(), - api.getWorkerStats(), - api.getRecentJobs({ limit: 50 }) - ]); - - setActiveScrapers(activeData.scrapers || []); - setHistory(historyData.history || []); - setJobStats(statsData); - setActiveJobs(jobsData.jobs || []); - setWorkers(workersData.workers || []); - setRecentJobs(recentJobsData.jobs || []); - - // Load AZ monitor data - const [azSummaryData, azActiveData, azRecentData, azErrorsData] = await Promise.all([ - api.getAZMonitorSummary().catch(() => null), - api.getAZMonitorActiveJobs().catch(() => ({ scheduledJobs: [], crawlJobs: [], inMemoryScrapers: [], totalActive: 0 })), - api.getAZMonitorRecentJobs(30).catch(() => ({ jobLogs: [], crawlJobs: [] })), - api.getAZMonitorErrors({ limit: 10, hours: 24 }).catch(() => ({ errors: [] })), - ]); - - setAzSummary(azSummaryData); - setAzActiveJobs(azActiveData); - setAzRecentJobs(azRecentData); - setAzErrors(azErrorsData?.errors || []); + if (tab === 'az-live') { + // Only load AZ data for AZ Live tab + const [azSummaryData, azActiveData, azRecentData, azErrorsData] = await Promise.all([ + api.getAZMonitorSummary().catch(() => null), + api.getAZMonitorActiveJobs().catch(() => ({ scheduledJobs: [], crawlJobs: [], inMemoryScrapers: [], totalActive: 0 })), + api.getAZMonitorRecentJobs(30).catch(() => ({ jobLogs: [], crawlJobs: [] })), + api.getAZMonitorErrors({ limit: 10, hours: 24 }).catch(() => ({ errors: [] })), + ]); + setAzSummary(azSummaryData); + setAzActiveJobs(azActiveData); + setAzRecentJobs(azRecentData); + setAzErrors(azErrorsData?.errors || []); + } else if (tab === 'jobs') { + // Only load legacy job data for Dispensary Jobs tab + const [statsData, jobsData, workersData, recentJobsData] = await Promise.all([ + api.getJobStats(), + api.getActiveJobs(), + api.getWorkerStats(), + api.getRecentJobs({ limit: 50 }) + ]); + setJobStats(statsData); + setActiveJobs(jobsData.jobs || []); + setWorkers(workersData.workers || []); + setRecentJobs(recentJobsData.jobs || []); + } else if (tab === 'scrapers') { + // Only load scraper data for Crawl History tab + const [activeData, historyData] = await Promise.all([ + api.getActiveScrapers(), + api.getScraperHistory() + ]); + setActiveScrapers(activeData.scrapers || []); + setHistory(historyData.history || []); + } } catch (error) { console.error('Failed to load scraper data:', error); } finally { @@ -67,6 +64,19 @@ export function ScraperMonitor() { } }; + // Initial load and tab change handler + useEffect(() => { + loadTabData(activeTab); + }, [activeTab]); + + // Auto-refresh based on active tab + useEffect(() => { + if (autoRefresh) { + const interval = setInterval(() => loadTabData(activeTab), 3000); + return () => clearInterval(interval); + } + }, [autoRefresh, activeTab]); + const formatDuration = (ms: number) => { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); @@ -264,53 +274,77 @@ export function ScraperMonitor() { ))} - {/* Individual Crawl Jobs */} - {azActiveJobs.crawlJobs.map((job: any) => ( -
0 && ( +
-
-
-
- {job.dispensary_name || 'Unknown Store'} -
-
- {job.city} | {job.job_type || 'crawl'} -
-
-
-
Products Found
-
{job.products_found || 0}
-
-
-
Snapshots
-
{job.snapshots_created || 0}
-
-
-
Duration
-
- {Math.floor((job.duration_seconds || 0) / 60)}m {Math.floor((job.duration_seconds || 0) % 60)}s -
-
-
-
-
- CRAWLING -
+
+

Active Crawler Sessions ({azActiveJobs.crawlJobs.length})

+ + + + + + + + + + + + + + {azActiveJobs.crawlJobs.map((job: any) => ( + + + + + + + + + + ))} + +
StoreWorkerPageProductsSnapshotsDurationStatus
+
{job.dispensary_name || 'Unknown'}
+
{job.city} | ID: {job.dispensary_id}
+
+
+ {job.worker_id ? job.worker_id.substring(0, 8) : '-'} +
+ {job.worker_hostname && ( +
{job.worker_hostname}
+ )} +
+ {job.current_page && job.total_pages ? ( + {job.current_page}/{job.total_pages} + ) : '-'} + + {job.products_found || 0} + + {job.snapshots_created || 0} + + {Math.floor((job.duration_seconds || 0) / 60)}m {Math.floor((job.duration_seconds || 0) % 60)}s + + 60000) ? '#fef3c7' : '#dbeafe', + color: job.last_heartbeat_at && (Date.now() - new Date(job.last_heartbeat_at).getTime() > 60000) ? '#92400e' : '#1e40af' + }}> + {job.last_heartbeat_at && (Date.now() - new Date(job.last_heartbeat_at).getTime() > 60000) ? 'STALE' : 'CRAWLING'} + +
- ))} + )}
)}
@@ -843,6 +877,8 @@ export function ScraperMonitor() { Dispensary Status Found + New + Updated Products Last Crawled @@ -866,6 +902,12 @@ export function ScraperMonitor() { {item.products_found || '-'} + + {item.products_new || 0} + + + {item.products_updated || 0} + {item.product_count}