Clean Choice
Stäng
Stäng
Hem
Våra tjänster
Provstädning
Hemstädning
Fönsterputs
Storstädning
Flyttstädning
Kontorsstädning
Trappstädning
Byggstädning
Visningsstädning
Offertförfrågan
Vanliga frågor
Jobba hos oss
Nyheter
Kontakta oss
Menu
Hem
Våra tjänster
Provstädning
Hemstädning
Fönsterputs
Storstädning
Flyttstädning
Kontorsstädning
Trappstädning
Byggstädning
Visningsstädning
Offertförfrågan
Vanliga frågor
Jobba hos oss
Nyheter
Kontakta oss
Facebook
Instagram
BOKA HEMBESÖK
Kalkylator för tjänsteprisberäkning
Kalkylator för städprisberäkning
Kalkylator för tjänsteprisberäkning
1. Välj kundtyp
Privatkund
Företagskund
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.
5. Fyll i dina uppgifter
Namn
Telefon
E-post
Skicka prisberäkningen
`; }; 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(); });