Location Scouting Dashboard

Location Scouting Dashboard

${loc.address}, ${loc.city}, ${loc.state}

${loc.type} ${ratingStars}

Contact: ${loc.contactPerson || 'N/A'} (${loc.contactPhone || 'N/A'})

Notes: ${loc.notes || 'No notes available.'}

`; dashboardContent.innerHTML += card; }); }; const renderLocationsTable = () => { const tableBody = document.getElementById('locations-table-body'); if (!tableBody) return; tableBody.innerHTML = ''; locations.forEach(loc => { const ratingStars = '★'.repeat(loc.rating) + '☆'.repeat(5 - loc.rating); const row = ` ${loc.name} ${loc.city} ${loc.type} ${ratingStars} `; tableBody.innerHTML += row; }); }; const renderSummaryStats = () => { const statsContainer = document.getElementById('summary-stats'); if (!statsContainer) return; const totalLocations = locations.length; const averageRating = totalLocations > 0 ? (locations.reduce((sum, loc) => sum + parseInt(loc.rating, 10), 0) / totalLocations).toFixed(1) : 'N/A'; const locationTypes = new Set(locations.map(loc => loc.type)).size; statsContainer.innerHTML = `

Total Locations

${totalLocations}

Average Rating

${averageRating}

Location Types

${locationTypes}

`; }; // --- FORM HANDLING & CRUD LOGIC --- const resetForm = () => { if (!form) return; form.reset(); locationIdInput.value = ''; formTitle.textContent = 'Add New Location'; submitBtn.textContent = 'Add Location'; cancelBtn.classList.add('hidden'); }; form.addEventListener('submit', (e) => { e.preventDefault(); const id = locationIdInput.value; const locationData = { name: document.getElementById('location-name').value, address: document.getElementById('location-address').value, city: document.getElementById('location-city').value, state: document.getElementById('location-state').value, zip: document.getElementById('location-zip').value, image: document.getElementById('location-image').value, type: document.getElementById('location-type').value, rating: parseInt(document.getElementById('location-rating').value, 10), contactPerson: document.getElementById('contact-person').value, contactPhone: document.getElementById('contact-phone').value, notes: document.getElementById('location-notes').value, }; if (id) { // Editing existing location const index = locations.findIndex(loc => loc.id == id); if (index !== -1) { locations[index] = { ...locations[index], ...locationData }; } } else { // Adding new location if (!locationData.name) { alert("Location Name is required."); return; } locationData.id = Date.now(); // Simple unique ID locations.push(locationData); } resetForm(); updateAllViews(); }); cancelBtn.addEventListener('click', () => { resetForm(); }); // Make handlers globally accessible for inline onclick window.handleEdit = (id) => { const location = locations.find(loc => loc.id === id); if (!location) return; // Populate form locationIdInput.value = location.id; document.getElementById('location-name').value = location.name; document.getElementById('location-address').value = location.address; document.getElementById('location-city').value = location.city; document.getElementById('location-state').value = location.state; document.getElementById('location-zip').value = location.zip; document.getElementById('location-image').value = location.image; document.getElementById('location-type').value = location.type; document.getElementById('location-rating').value = location.rating; document.getElementById('contact-person').value = location.contactPerson; document.getElementById('contact-phone').value = location.contactPhone; document.getElementById('location-notes').value = location.notes; // Update UI formTitle.textContent = 'Edit Location'; submitBtn.textContent = 'Save Changes'; cancelBtn.classList.remove('hidden'); window.scrollTo(0, 0); // Scroll to top of form }; window.handleDelete = (id) => { if (confirm('Are you sure you want to delete this location?')) { locations = locations.filter(loc => loc.id !== id); updateAllViews(); } }; // --- PDF GENERATION --- downloadPdfBtn.addEventListener('click', () => { const { jsPDF } = window.jspdf; const pdfContainer = document.getElementById('pdf-content-container'); if (!pdfContainer) return; // 1. Generate clean HTML for the PDF let pdfHtml = `

Location Scouting Dashboard

`; pdfHtml += ``; locations.forEach(loc => { const ratingStars = '★'.repeat(loc.rating) + '☆'.repeat(5 - loc.rating); pdfHtml += ` `; }); pdfHtml += `
Name Address Type Rating Notes
${loc.name} ${loc.city}, ${loc.state} ${loc.type} ${ratingStars} ${loc.notes || ''}
`; pdfContainer.innerHTML = pdfHtml; // 2. Use html2canvas to capture the generated content html2canvas(pdfContainer, { scale: 2, // Higher scale for better quality useCORS: true, width: pdfContainer.scrollWidth, height: pdfContainer.scrollHeight, windowWidth: pdfContainer.scrollWidth, windowHeight: pdfContainer.scrollHeight }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasWidth / canvasHeight; const newCanvasWidth = pdfWidth; const newCanvasHeight = newCanvasWidth / ratio; // Check if content exceeds one page if (newCanvasHeight > pdfHeight) { // This simple implementation adds the image and lets it be scrollable in PDF viewers. // For true multi-page, a more complex slicing logic would be needed. console.warn("PDF content may exceed one page."); } pdf.addImage(imgData, 'PNG', 0, 0, newCanvasWidth, newCanvasHeight); pdf.save('Location_Scouting_Dashboard.pdf'); // Clean up pdfContainer.innerHTML = ''; }).catch(err => { console.error("PDF generation failed:", err); alert("Sorry, there was an error creating the PDF."); }); }); // --- INITIALIZATION --- const updateAllViews = () => { renderDashboard(); renderLocationsTable(); renderSummaryStats(); }; // Initial load showTab(0); // Show Dashboard tab first updateAllViews(); });
Scroll to Top