Clean Choice

Kalkylator för tjänsteprisberäkning

Kalkylator för städprisberäkning

Kalkylator för tjänsteprisberäkning

1. Välj kundtyp

2. Välj grundläggande tjänst

3. Välj tilläggstjänster (valfritt)

4. Välj specialtjänster (individuell prissättning

Sammanfattning av prisberäkningen

Välj tjänster för att se priset.

`; }; const sendQuote = async () => { const customerName = document.getElementById('customer-name').value; const customerPhone = document.getElementById('customer-phone').value; const customerEmail = document.getElementById('customer-email').value; if (!customerName || !customerPhone || !customerEmail) { webhookStatus.textContent = 'Vänligen fyll i alla kontaktuppgifter.'; webhookStatus.className = 'mt-4 text-sm font-semibold text-red-600'; return; } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(customerEmail)) { webhookStatus.textContent = 'Ange en giltig e-postadress.'; webhookStatus.className = 'mt-4 text-sm font-semibold text-red-600'; return; } if (!lastCalculatedSummary || (lastCalculatedSummary.core.length === 0 && lastCalculatedSummary.additional.length === 0 && lastCalculatedSummary.special.length === 0)) { webhookStatus.textContent = 'Vänligen välj och prissätt tjänsterna först.'; webhookStatus.className = 'mt-4 text-sm font-semibold text-yellow-600'; return; } const totalNetto = lastCalculatedSummary.totals.netto + lastCalculatedSummary.totals.materialNetto; const totalMoms = lastCalculatedSummary.totals.moms + lastCalculatedSummary.totals.materialMoms; const finalPrice = state.customerType === 'private' ? (totalNetto + totalMoms - lastCalculatedSummary.totals.rut) : (totalNetto + totalMoms); const summaryHTML = getSummaryAsHTML(); const payload = { customerInfo: { name: customerName, phone: customerPhone, email: customerEmail }, summaryHTML: summaryHTML, customerType: state.customerType, coreServices: lastCalculatedSummary.core.map(s => ({ name: s.name, quantity: s.displayQty, interval: intervals[s.interval], monthlyMultiplier: s.multiplier, netto: s.netto.toFixed(2), moms: s.moms.toFixed(2), rut: s.rut.toFixed(2), materialNetto: s.material.netto.toFixed(2), materialMoms: s.material.moms.toFixed(2) })), additionalServices: lastCalculatedSummary.additional.map(s => ({ name: s.name, quantity: s.displayQty, interval: intervals[s.interval], monthlyMultiplier: s.multiplier, netto: s.netto.toFixed(2), moms: s.moms.toFixed(2), rut: s.rut.toFixed(2) })), specialServices: lastCalculatedSummary.special.map(s => ({ name: s.name, quantity: s.qty > 0 ? `${s.qty} ${s.unit}` : 'N/A' })), summary: { totalServiceAndMaterial: totalNetto.toFixed(2), totalMoms: totalMoms.toFixed(2), rutSaving: lastCalculatedSummary.totals.rut.toFixed(2), finalPrice: finalPrice.toFixed(2) }, hasIndividualQuote: lastCalculatedSummary.hasIndividual, quoteDate: new Date().toISOString() }; sendQuoteBtn.disabled = true; webhookStatus.textContent = 'Skickar...'; webhookStatus.className = 'mt-4 text-sm font-semibold text-blue-600'; try { const response = await fetch(WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (response.ok) { webhookStatus.textContent = 'Offerten har skickats framgångsrikt!'; webhookStatus.className = 'mt-4 text-sm font-semibold text-green-600'; } else { throw new Error(`Serverfel: ${response.status}`); } } catch (error) { console.error('Fel vid sändning till webhook:', error); webhookStatus.textContent = 'Ett fel uppstod vid sändning av offerten. Försök igen.'; webhookStatus.className = 'mt-4 text-sm font-semibold text-red-600'; } finally { sendQuoteBtn.disabled = false; } }; app.addEventListener('change', (e) => { if (e.target.name === 'customerType') { state.customerType = e.target.value; renderServices(); calculateAndRenderSummary(); return; } if (e.target.type === 'checkbox') { const type = e.target.dataset.serviceType; const id = e.target.value; const controls = document.getElementById(`controls-${type}-${id}`); if (controls) { controls.classList.toggle('hidden', !e.target.checked); if (!e.target.checked) { const qtyInput = controls.querySelector('input[type="number"]'); if(qtyInput) qtyInput.value = ''; const inlineSummary = document.getElementById(`inline-summary-${type}-${id}`); if(inlineSummary) inlineSummary.style.display = 'none'; } } } calculateAndRenderSummary(); }); app.addEventListener('input', (e) => { if (e.target.type === 'number' || e.target.tagName === 'SELECT') { calculateAndRenderSummary(); } }); sendQuoteBtn.addEventListener('click', sendQuote); renderServices(); calculateAndRenderSummary(); });