Property Valuation Estimator

Property Valuation Estimator

Subject Property Details

Poor Fair Good Very Good Excellent

Estimated Property Value

$0

Based on comparable sales and property details.

Valuation Breakdown

Base Value from Comps: $0
Condition Adjustment: $0
Age Adjustment: $0
Feature Adjustments (Beds/Baths): $0

You can add a maximum of 5 comparable properties.

`; document.body.appendChild(modal); return; } compCount++; const compId = `comp-${compCount}`; const compElement = document.createElement('div'); compElement.id = compId; compElement.className = 'comp-item border border-gray-200 rounded-lg p-4 relative'; const sampleData = [ { address: '456 Oak Ave, Anytown, CA', price: '750000', sqft: '1900', beds: '4', baths: '2.5', year: '2005' }, { address: '789 Pine Ln, Anytown, CA', price: '680000', sqft: '1750', beds: '3', baths: '2', year: '1990' }, { address: '101 Maple Dr, Anytown, CA', price: '715000', sqft: '1850', beds: '3', baths: '2.5', year: '2000' }, { address: '212 Birch Rd, Anytown, CA', price: '780000', sqft: '2000', beds: '4', baths: '3', year: '2010' }, { address: '333 Cedar Ct, Anytown, CA', price: '650000', sqft: '1600', beds: '3', baths: '2', year: '1985' } ]; const data = useSampleData && compCount <= sampleData.length ? sampleData[compCount - 1] : { address: '', price: '', sqft: '', beds: '', baths: '', year: '' }; compElement.innerHTML = `

Comparable Property #${compCount}

`; if (compsContainer) compsContainer.appendChild(compElement); compElement.querySelector('.remove-comp-btn').addEventListener('click', removeComparableProperty); compElement.querySelectorAll('.comp-input').forEach(input => { input.addEventListener('input', calculateValuation); }); if (addCompBtn) addCompBtn.disabled = (compCount >= MAX_COMPS); calculateValuation(); }; const removeComparableProperty = (e) => { const targetId = e.currentTarget.getAttribute('data-target'); const elementToRemove = document.getElementById(targetId); if (elementToRemove) { elementToRemove.remove(); compCount--; if (addCompBtn) addCompBtn.disabled = (compCount >= MAX_COMPS); calculateValuation(); } }; // --- VALUATION CALCULATION LOGIC --- const calculateValuation = () => { const subject = { sqft: parseFloat(document.getElementById('prop-sqft').value) || 0, bedrooms: parseFloat(document.getElementById('prop-bedrooms').value) || 0, bathrooms: parseFloat(document.getElementById('prop-bathrooms').value) || 0, yearBuilt: parseInt(document.getElementById('prop-year-built').value) || new Date().getFullYear(), condition: parseInt(document.getElementById('prop-condition').value) || 3, }; const comps = []; document.querySelectorAll('.comp-item').forEach(item => { const price = parseFloat(item.querySelector('[data-field="price"]').value) || 0; const sqft = parseFloat(item.querySelector('[data-field="sqft"]').value) || 0; if (price > 0 && sqft > 0) { comps.push({ price, sqft, pricePerSqft: price / sqft, bedrooms: parseFloat(item.querySelector('[data-field="beds"]').value) || 0, bathrooms: parseFloat(item.querySelector('[data-field="baths"]').value) || 0, yearBuilt: parseInt(item.querySelector('[data-field="year"]').value) || new Date().getFullYear(), }); } }); if (comps.length === 0) { updateUI(0, { base: 0, condition: 0, age: 0, features: 0 }); return; } const avg = { pricePerSqft: comps.reduce((sum, c) => sum + c.pricePerSqft, 0) / comps.length, bedrooms: comps.reduce((sum, c) => sum + c.bedrooms, 0) / comps.length, bathrooms: comps.reduce((sum, c) => sum + c.bathrooms, 0) / comps.length, yearBuilt: comps.reduce((sum, c) => sum + c.yearBuilt, 0) / comps.length, }; const baseValue = subject.sqft * avg.pricePerSqft; const conditionAdjustmentValue = baseValue * (subject.condition - 3) * 0.05; const ageDifference = subject.yearBuilt - avg.yearBuilt; const ageAdjustmentValue = baseValue * (ageDifference * -0.003); const bedValue = 15000; const bathValue = 10000; const bedDifference = subject.bedrooms - avg.bedrooms; const bathDifference = subject.bathrooms - avg.bathrooms; const featureAdjustmentValue = (bedDifference * bedValue) + (bathDifference * bathValue); const finalValue = baseValue + conditionAdjustmentValue + ageAdjustmentValue + featureAdjustmentValue; updateUI(finalValue, { base: baseValue, condition: conditionAdjustmentValue, age: ageAdjustmentValue, features: featureAdjustmentValue }); }; // --- UI UPDATE & FORMATTING --- const formatCurrency = (value) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(value); }; const updateUI = (finalValue, breakdown) => { document.getElementById('estimated-value').textContent = formatCurrency(finalValue); document.getElementById('breakdown-base').textContent = formatCurrency(breakdown.base); document.getElementById('breakdown-condition').textContent = formatCurrency(breakdown.condition); document.getElementById('breakdown-age').textContent = formatCurrency(breakdown.age); document.getElementById('breakdown-features').textContent = formatCurrency(breakdown.features); }; // --- PDF GENERATION (REVISED AND IMPROVED) --- const generatePDF = async () => { if (typeof window.jspdf === 'undefined' || typeof window.html2canvas === 'undefined') { console.error('PDF generation libraries not loaded.'); return; } const { jsPDF } = window.jspdf; const pdfReportEl = document.createElement('div'); // Style the report container for off-screen rendering Object.assign(pdfReportEl.style, { position: 'absolute', left: '-9999px', top: '0', width: '800px', backgroundColor: '#ffffff', padding: '40px', fontFamily: 'Arial, sans-serif', color: '#333', }); // --- GATHER DATA FOR PDF --- const subject = { address: document.getElementById('prop-address').value, type: document.getElementById('prop-type').value, sqft: document.getElementById('prop-sqft').value, lotSize: document.getElementById('prop-lot-size').value, bedrooms: document.getElementById('prop-bedrooms').value, bathrooms: document.getElementById('prop-bathrooms').value, yearBuilt: document.getElementById('prop-year-built').value, condition: conditionMap[document.getElementById('prop-condition').value], }; const valuation = { final: document.getElementById('estimated-value').textContent, base: document.getElementById('breakdown-base').textContent, condition: document.getElementById('breakdown-condition').textContent, age: document.getElementById('breakdown-age').textContent, features: document.getElementById('breakdown-features').textContent, }; const comps = Array.from(document.querySelectorAll('.comp-item')).map(item => ({ address: item.querySelector('[data-field="address"]').value, price: formatCurrency(parseFloat(item.querySelector('[data-field="price"]').value) || 0), sqft: item.querySelector('[data-field="sqft"]').value, beds: item.querySelector('[data-field="beds"]').value, baths: item.querySelector('[data-field="baths"]').value, year: item.querySelector('[data-field="year"]').value, })); // --- BUILD HTML FOR PDF --- let compsHtml = ''; if (comps.length > 0) { compsHtml = `

Comparable Properties Summary

${comps.map(c => ` `).join('')}
Address Sale Price Sq.Ft. Beds Baths Year
${c.address} ${c.price} ${c.sqft} ${c.beds} ${c.baths} ${c.year}
`; } pdfReportEl.innerHTML = `

Property Valuation Report

Generated on: ${new Date().toLocaleDateString()}

Subject Property

${subject.address}

Type: ${subject.type}
Sq. Footage: ${subject.sqft} sq. ft.
Lot Size: ${subject.lotSize} acres
Bedrooms: ${subject.bedrooms}
Bathrooms: ${subject.bathrooms}
Year Built: ${subject.yearBuilt}
Condition: ${subject.condition}

Valuation Summary

${valuation.final}

Base Value: ${valuation.base}
Condition Adj: ${valuation.condition}
Age Adj: ${valuation.age}
Features Adj: ${valuation.features}
${compsHtml} `; document.body.appendChild(pdfReportEl); try { const canvas = await html2canvas(pdfReportEl, { scale: 2, useCORS: true }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'portrait', unit: 'px', format: [canvas.width, canvas.height] }); pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height); pdf.save(`Property-Valuation-${subject.address.replace(/[^a-zA-Z0-9]/g, '_')}.pdf`); } catch (error) { console.error("Error generating PDF:", error); } finally { document.body.removeChild(pdfReportEl); } }; // --- START THE APP --- initialize(); });
Scroll to Top