// State let currentResults = []; // DOM const searchForm = document.getElementById('searchForm'); const searchBtn = document.getElementById('searchBtn'); const loadingState = document.getElementById('loadingState'); const errorState = document.getElementById('errorState'); const emptyState = document.getElementById('emptyState'); const resultsArea = document.getElementById('resultsArea'); const resultsBody = document.getElementById('resultsBody'); const resultsQuery = document.getElementById('resultsQuery'); const resultsCount = document.getElementById('resultsCount'); const detailPanel = document.getElementById('detailPanel'); const recentSearches = document.getElementById('recentSearches'); function showState(state) { [loadingState, errorState, emptyState, resultsArea].forEach(el => el?.classList.add('hidden')); if (recentSearches) recentSearches.classList.toggle('hidden', state !== 'empty'); state === 'loading' && loadingState?.classList.remove('hidden'); state === 'error' && errorState?.classList.remove('hidden'); state === 'empty' && emptyState?.classList.remove('hidden'); state === 'results' && resultsArea?.classList.remove('hidden'); } // Search searchForm?.addEventListener('submit', async (e) => { e.preventDefault(); const fd = new FormData(searchForm); const data = Object.fromEntries(fd); showState('loading'); searchBtn.disabled = true; searchBtn.innerHTML = ' Searching...'; try { const res = await fetch('/api/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); const json = await res.json(); if (!res.ok) throw new Error(json.error || 'Search failed'); currentResults = json.results; renderResults(json.results, json.query); } catch (err) { document.getElementById('errorMsg').textContent = err.message; showState('error'); } finally { searchBtn.disabled = false; searchBtn.innerHTML = ' Search'; } }); function renderResults(results, query) { resultsQuery.textContent = query; resultsCount.textContent = results.length; resultsBody.innerHTML = ''; results.forEach((r, i) => { const tr = document.createElement('tr'); tr.className = 'hover:bg-card-hover transition-colors cursor-pointer'; tr.onclick = () => showDetail(r); tr.innerHTML = `

${esc(r.title)}

${esc(r.categoryName || r.address || '')}

${esc(r.phone) || '—'} ${esc(r.email) || '—'} ${r.website ? `${esc(r.website.replace(/^https?:\/\//, '').replace(/\/$/, ''))}` : '—'} ${r.totalScore ? ` ${r.totalScore.toFixed(1)}` : '—'} ${r.reviewsCount || '—'} `; resultsBody.appendChild(tr); }); showState('results'); } function showDetail(place) { detailPanel.innerHTML = `

${esc(place.title)}

${place.imageUrl ? `` : ''}
${place.categoryName ? `
Category

${esc(place.categoryName)}

` : ''} ${place.address ? `
Address

${esc(place.address)}

` : ''} ${place.phone ? `
Phone

${esc(place.phone)}

` : ''} ${place.email ? `
Email

${esc(place.email)}

` : ''} ${place.website ? `
Website

${esc(place.website)}

` : ''} ${place.totalScore ? `
Rating

${place.totalScore.toFixed(1)} (${place.reviewsCount} reviews)

` : ''} ${place.url ? `
View on Google Maps →
` : ''}
`; detailPanel.classList.remove('hidden'); } async function toggleBookmark(placeId, index) { const place = currentResults[index]; if (!place) return; if (place.isBookmarked) { await fetch('/api/leads/' + encodeURIComponent(placeId), { method: 'DELETE' }); place.isBookmarked = false; } else { await fetch('/api/leads', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ place }), }); place.isBookmarked = true; } updateBookmarkUI(placeId, place.isBookmarked); } async function toggleBookmarkFromPanel(placeId) { const place = currentResults.find(r => r.placeId === placeId); if (!place) return; const index = currentResults.indexOf(place); await toggleBookmark(placeId, index); } function updateBookmarkUI(placeId, isBookmarked) { // Update table button document.querySelectorAll(`.bookmark-btn[data-place="${placeId}"]`).forEach(btn => { btn.innerHTML = isBookmarked ? '' : ''; }); // Update panel button document.querySelectorAll(`.panel-bookmark-btn[data-place="${placeId}"]`).forEach(btn => { btn.className = `w-full py-2 rounded-lg text-sm font-medium transition-colors panel-bookmark-btn ${isBookmarked ? 'bg-accent text-white' : 'bg-accent/20 text-accent hover:bg-accent/30'}`; btn.textContent = isBookmarked ? '★ Saved as Lead' : '☆ Save as Lead'; }); } // Load saved search async function loadSearch(key) { showState('loading'); try { const res = await fetch('/api/search/' + encodeURIComponent(key)); const data = await res.json(); if (!res.ok) throw new Error(data.error); currentResults = data.results || []; renderResults(currentResults, data.query); } catch (err) { document.getElementById('errorMsg').textContent = err.message; showState('error'); } } // Check URL for view param const params = new URLSearchParams(window.location.search); if (params.get('view')) loadSearch(params.get('view')); // Close detail panel on Escape document.addEventListener('keydown', (e) => { if (e.key === 'Escape') detailPanel?.classList.add('hidden'); }); function esc(str) { if (!str) return ''; const div = document.createElement('div'); div.textContent = str; return div.innerHTML; }