async function triggerLibraryScan(): Promise { const token = localStorage.getItem('token'); if (!token) return; try { const response = await fetch('/api/libraries/scan', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { if ((window as any).showToast?.success) { (window as any).showToast.success('Library scan started'); } } else { const error = await response.json(); if ((window as any).showToast?.error) { (window as any).showToast.error(error.error || 'Failed to start scan'); } } } catch (error) { console.error('Scan error:', error); if ((window as any).showToast?.error) { (window as any).showToast.error('Failed to start library scan'); } } } async function triggerQuickScan(): Promise { const token = localStorage.getItem('token'); if (!token) return; try { const response = await fetch('/api/libraries/quick-scan', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { if ((window as any).showToast?.success) { (window as any).showToast.success('Quick scan started'); } } else { const error = await response.json(); if ((window as any).showToast?.error) { (window as any).showToast.error(error.error || 'Failed to start quick scan'); } } } catch (error) { console.error('Quick scan error:', error); if ((window as any).showToast?.error) { (window as any).showToast.error('Failed to start quick scan'); } } } async function loadSystemStats(): Promise { const token = localStorage.getItem('token'); if (!token) return; try { const response = await fetch('/api/admin/stats', { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { const stats = await response.json(); renderSystemStats(stats); } } catch (error) { console.error('Failed to load stats:', error); } } function renderSystemStats(stats: Record): void { const container = document.getElementById('system-stats'); if (!container) return; container.innerHTML = `

${stats.total_books || 0}

Total Books

${stats.total_users || 0}

Users

${stats.total_devices || 0}

Devices

${stats.total_libraries || 0}

Libraries

`; } async function scanAllLibraries(): Promise { const token = localStorage.getItem('token'); if (!token) return; try { const libsResp = await fetch('/api/libraries', { headers: { 'Authorization': `Bearer ${token}` } }); if (!libsResp.ok) { throw new Error('Failed to get libraries'); } const libsData = await libsResp.json(); if (!libsData.data || libsData.data.length === 0) { if ((window as any).showToast?.error) { (window as any).showToast.error('No libraries found. Please create a library first.'); } return; } const libraries = libsData.data; const jobs: string[] = []; const libraryNames: Record = {}; for (const lib of libraries) { const scanResp = await fetch(`/api/libraries/${lib.id}/scan`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); if (scanResp.ok) { const result = await scanResp.json(); jobs.push(result.job_id); libraryNames[result.job_id] = lib.name; } else { console.error(`Failed to scan library: ${lib.name}`); } } if (jobs.length === 0) { if ((window as any).showToast?.error) { (window as any).showToast.error('Failed to start scan for any library'); } return; } showScanProgress(jobs, libraryNames); } catch (error) { console.error('Scan error:', error); if ((window as any).showToast?.error) { (window as any).showToast.error('Failed to start scan: ' + (error as Error).message); } } } function showScanProgress(jobIds: string[], libraryNames: Record): void { const container = document.getElementById('scan-progress-container') as HTMLElement; const list = document.getElementById('library-progress-list') as HTMLElement; if (!container || !list) return; container.classList.remove('hidden'); container.classList.remove('opacity-0', '-translate-y-2.5'); list.innerHTML = jobIds.map(jobId => `
${libraryNames[jobId]} Pending...
`).join(''); pollScanProgress(jobIds, libraryNames); } function pollScanProgress(jobIds: string[], _libraryNames: Record): void { const token = localStorage.getItem('token'); const startTime = Date.now(); const interval = setInterval(async () => { let allComplete = true; let totalProgress = 0; let totalFiles = 0; let totalNewItems = 0; let totalErrors = 0; for (const jobId of jobIds) { try { const resp = await fetch(`/api/scanner/status/${jobId}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (resp.ok) { const status = await resp.json(); updateLibraryProgress(jobId, status); totalProgress += status.progress || 0; totalFiles += status.files_scanned || 0; totalNewItems += status.new_items || 0; totalErrors += status.errors || 0; if (status.status !== 'completed' && status.status !== 'failed') { allComplete = false; } } } catch (error) { console.error(`Failed to poll job ${jobId}:`, error); } } const overallProgress = Math.round(totalProgress / jobIds.length); const progressBar = document.getElementById('scan-progress-bar') as HTMLElement; const progressText = document.getElementById('scan-progress-text') as HTMLElement; const statusText = document.getElementById('scan-status') as HTMLElement; if (progressBar) progressBar.style.width = overallProgress + '%'; if (progressText) progressText.textContent = overallProgress + '%'; const elapsed = Math.round((Date.now() - startTime) / 1000); if (!allComplete && statusText) { statusText.textContent = `Scanning... ${elapsed}s elapsed • ${totalFiles} files processed`; } if (allComplete) { clearInterval(interval); showScanResults(jobIds.length, totalFiles, totalNewItems, totalErrors, elapsed); } }, 2000); } function updateLibraryProgress(jobId: string, status: any): void { const bar = document.getElementById(`bar-${jobId}`) as HTMLElement; const statusText = document.getElementById(`status-${jobId}`) as HTMLElement; if (bar) { bar.style.width = (status.progress || 0) + '%'; } if (statusText) { const statusMessages: Record = { 'pending': 'Pending...', 'running': `Scanning... ${status.progress || 0}%`, 'completed': `✓ Complete (${status.new_items || 0} items)`, 'failed': `✗ Failed` }; statusText.textContent = statusMessages[status.status] || status.status; } } function showScanResults(libCount: number, files: number, items: number, errors: number, elapsed: number): void { const resultsDiv = document.getElementById('scan-results') as HTMLElement; const contentDiv = document.getElementById('scan-results-content') as HTMLElement; if (!resultsDiv || !contentDiv) return; contentDiv.innerHTML = `

• ${libCount} librar${libCount === 1 ? 'y' : 'ies'} scanned

• ${files} files processed

• ${items} new items added

${errors > 0 ? `

• ${errors} errors

` : ''}

Completed in ${elapsed} seconds

`; resultsDiv.classList.remove('hidden'); const statusText = document.getElementById('scan-status') as HTMLElement; if (statusText) statusText.textContent = 'Scan complete!'; } function hideScanProgress(): void { const container = document.getElementById('scan-progress-container') as HTMLElement; if (container) container.classList.add('hidden'); } (window as any).triggerLibraryScan = triggerLibraryScan; (window as any).triggerQuickScan = triggerQuickScan; (window as any).loadSystemStats = loadSystemStats; (window as any).scanAllLibraries = scanAllLibraries; (window as any).hideScanProgress = hideScanProgress;