|
|
|
|
|
class EstimateCalculator extends HTMLElement { |
|
|
connectedCallback() { |
|
|
this.attachShadow({ mode: 'open' }); |
|
|
this.shadowRoot.innerHTML = ` |
|
|
<style> |
|
|
.calculator-card { |
|
|
background: white; |
|
|
border-radius: 12px; |
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
|
padding: 2rem; |
|
|
max-width: 600px; |
|
|
margin: 0 auto; |
|
|
font-family: Arial, sans-serif; |
|
|
} |
|
|
select, input { |
|
|
transition: all 0.2s ease-in-out; |
|
|
} |
|
|
select:focus, input:focus { |
|
|
outline: none; |
|
|
border-color: #3b82f6; |
|
|
box-shadow: 0 0 5px rgba(59,130,246,0.5); |
|
|
} |
|
|
.form-group { |
|
|
margin-bottom: 1.5rem; |
|
|
} |
|
|
.form-label { |
|
|
display: block; |
|
|
font-weight: 600; |
|
|
color: #1e293b; |
|
|
margin-bottom: 0.5rem; |
|
|
} |
|
|
.form-input, .form-select { |
|
|
width: 100%; |
|
|
padding: 0.75rem; |
|
|
border: 1px solid #e2e8f0; |
|
|
border-radius: 0.5rem; |
|
|
transition: all 0.3s ease; |
|
|
font-size: 1rem; |
|
|
} |
|
|
.result { |
|
|
margin-top: 2rem; |
|
|
padding: 1.5rem; |
|
|
background: #f8fafc; |
|
|
border-radius: 0.5rem; |
|
|
text-align: center; |
|
|
font-weight: 600; |
|
|
color: #1e293b; |
|
|
border: 1px solid #e2e8f0; |
|
|
font-size: 1.25rem; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.hidden { |
|
|
display: none; |
|
|
} |
|
|
.error { |
|
|
color: #dc2626; |
|
|
font-size: 0.875rem; |
|
|
margin-top: 0.5rem; |
|
|
} |
|
|
input[type="number"]::-webkit-outer-spin-button, |
|
|
input[type="number"]::-webkit-inner-spin-button { |
|
|
-webkit-appearance: none; |
|
|
margin: 0; |
|
|
} |
|
|
input[type="number"] { |
|
|
-moz-appearance: textfield; |
|
|
} |
|
|
</style> |
|
|
<div class="calculator-card"> |
|
|
<h2 class="text-2xl font-bold text-center text-blue-700 mb-4">Free Quote Calculator</h2> |
|
|
|
|
|
<div class="form-group"> |
|
|
<label class="form-label">Service Type</label> |
|
|
<select id="service" class="form-select"> |
|
|
<option value="">Select a Service</option> |
|
|
<option value="driveway">Driveway Cleaning</option> |
|
|
<option value="fence">Fence Cleaning</option> |
|
|
<option value="roof">Roof Cleaning</option> |
|
|
<option value="gutter">Gutter Cleaning ($0.20/sqft)</option> |
|
|
<option value="deck">Deck/Patio Cleaning</option> |
|
|
<option value="house">House Washing</option> |
|
|
<option value="fleet">Fleet Car Washing</option> |
|
|
</select> |
|
|
</div> |
|
|
|
|
|
<div id="inputs" class="space-y-3"></div> |
|
|
|
|
|
<div id="result" class="result"></div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
this.renderInputs(); |
|
|
this.shadowRoot.getElementById('service').addEventListener('change', () => this.renderInputs()); |
|
|
} |
|
|
|
|
|
renderInputs() { |
|
|
const inputsDiv = this.shadowRoot.getElementById('inputs'); |
|
|
const resultDiv = this.shadowRoot.getElementById('result'); |
|
|
const service = this.shadowRoot.getElementById('service').value; |
|
|
|
|
|
inputsDiv.innerHTML = ''; |
|
|
resultDiv.textContent = ''; |
|
|
|
|
|
if (!service) return; |
|
|
|
|
|
if (service === 'fleet') { |
|
|
inputsDiv.innerHTML = ` |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Number Of Vehicles</label> |
|
|
<input id="numCars" type="number" class="form-input" min="1" placeholder="e.g. 5"> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Plans</label> |
|
|
<select id="plan" class="form-select"> |
|
|
<option value="standard">Standard</option> |
|
|
<option value="premium">Premium</option> |
|
|
</select> |
|
|
</div> |
|
|
`; |
|
|
} else { |
|
|
inputsDiv.innerHTML = ` |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Condition</label> |
|
|
<select id="condition" class="form-select"> |
|
|
<option value="light">Light</option> |
|
|
<option value="average">Average</option> |
|
|
<option value="heavy">Heavy</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="form-group"> |
|
|
<label class="form-label">Size [Sqr Feet]</label> |
|
|
<input id="size" type="number" class="form-input" min="1" placeholder="e.g. 1200"> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
this.shadowRoot.querySelectorAll('input, select').forEach(el => |
|
|
el.addEventListener('input', () => this.calculate()) |
|
|
); |
|
|
} |
|
|
|
|
|
calculate() { |
|
|
const service = this.shadowRoot.getElementById('service').value; |
|
|
const resultDiv = this.shadowRoot.getElementById('result'); |
|
|
let total = 0; |
|
|
|
|
|
if (service === 'fleet') { |
|
|
const cars = parseFloat(this.shadowRoot.getElementById('numCars')?.value) || 0; |
|
|
const plan = this.shadowRoot.getElementById('plan')?.value || 'standard'; |
|
|
if (!cars) return (resultDiv.textContent = ''); |
|
|
|
|
|
let base = plan === 'standard' ? 25 : 35; |
|
|
let discount = 0; |
|
|
|
|
|
if (cars >= 3 && cars <= 5) discount = 2; |
|
|
else if (cars >= 6 && cars <= 10) discount = 3; |
|
|
else if (cars > 10) discount = 5; |
|
|
|
|
|
total = cars * (base - discount); |
|
|
} else { |
|
|
const size = parseFloat(this.shadowRoot.getElementById('size')?.value) || 0; |
|
|
const condition = this.shadowRoot.getElementById('condition')?.value || 'light'; |
|
|
|
|
|
if (!size) return (resultDiv.textContent = ''); |
|
|
|
|
|
let rate = 0; |
|
|
switch (service) { |
|
|
case 'driveway': |
|
|
if (size <= 500) total = 75.99; |
|
|
else total = 75.99 + (size - 500) * 0.17; |
|
|
break; |
|
|
case 'fence': |
|
|
case 'deck': |
|
|
rate = condition === 'light' ? 0.25 : condition === 'average' ? 0.35 : 0.45; |
|
|
total = size * rate; |
|
|
break; |
|
|
case 'roof': |
|
|
rate = condition === 'light' ? 0.35 : condition === 'average' ? 0.45 : 0.55; |
|
|
total = size * rate; |
|
|
break; |
|
|
case 'house': |
|
|
rate = condition === 'light' ? 0.30 : condition === 'average' ? 0.40 : 0.50; |
|
|
total = size * rate; |
|
|
break; |
|
|
case 'gutter': |
|
|
total = size * 0.20; |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
if (service === 'driveway') { |
|
|
const multiplier = condition === 'light' ? 1.0 : condition === 'average' ? 1.2 : 1.4; |
|
|
total *= multiplier; |
|
|
} |
|
|
} |
|
|
|
|
|
resultDiv.textContent = total > 0 ? `Estimated Total: ${total.toFixed(2)}` : ''; |
|
|
} |
|
|
} |
|
|
|
|
|
customElements.define('estimate-calculator', EstimateCalculator); |