🚕 Drivers Pipeline

📊Dashboard 👥CRM 📋Pipeline 📺Screens ⚙️Settings
Hyper-Local Digital Advertising · Harlow, Essex

${title}

Date: ${date}

${detailsHeading}

${e.business_name?`
${isOperator?'Company / fleet name':'Business name'}${esc(e.business_name)}
`:''}
Contact name${esc(e.contact_name)}
Email${esc(e.email)}
${e.phone?`
Phone${esc(e.phone)}
`:''}${e.postcode?`
Postcode${esc(e.postcode)}
`:''}${e.vehicle_type?`
${vehicleLabel}${esc(e.vehicle_type)}
`:''}

Terms & Conditions

${terms}
Signed: ${esc(e.contact_name)}${isOperator&&e.business_name?` (on behalf of ${esc(e.business_name)})`:''}
Date: ___________________
Signed: Space4Promotion
Date: ___________________
`; const win=window.open('','_blank');win.document.write(html);win.document.close();setTimeout(()=>win.print(),500); } async function saveEnquiry(){ if(!currentId)return; const e=enquiries.find(e=>e.id===currentId); const stage=document.getElementById('modal-stage').value; const rateVal=document.getElementById('modal-rate').value; const cbVal=document.getElementById('modal-callback').value; // If moving to callback stage, show timescale picker if(stage==='callback' && e.stage!=='callback'){ // Save current state first without stage change const payload={ stage:e.stage, notes:document.getElementById('modal-notes').value.trim(), contract_sent:e.contract_sent||0, contract_signed:e.contract_signed||0, monthly_rate:rateVal?parseFloat(rateVal):null, callback_date:cbVal||null }; await fetch(API+'/enquiries/'+currentId,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}); const _cbId = currentId; closeModal(); openCallbackModal(_cbId); return; } const payload={ stage:stage, notes:document.getElementById('modal-notes').value.trim(), contract_sent:e.contract_sent||0, contract_signed:e.contract_signed||0, monthly_rate:rateVal?parseFloat(rateVal):null, callback_date:cbVal||null }; await fetch(API+'/enquiries/'+currentId,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}); toast('Saved ✓');closeModal();await load(); } async function deleteEnquiry(){ if(!currentId)return; const e=enquiries.find(e=>e.id===currentId); if(!confirm(`Delete enquiry from ${e?.contact_name}?`))return; await fetch(API+'/enquiries/'+currentId,{method:'DELETE'}); toast('Deleted');closeModal();await load(); } function esc(s){return String(s||'').replace(/&/g,'&').replace(//g,'>');} // Driver-type enquiries cover two shapes submitted from the same public // form (driverenquiry.html, owner/operator toggle added 18 Sep 2026): an // individual owner driver, or a company enquiring on behalf of a fleet. // There's no dedicated DB column for that split — the form encodes it as // the first line of `message` ("Enquiry type: Owner driver" / "Fleet / // Operator") and, as a fleet always carries a company name, business_name // being set is an equally reliable fallback for older/unmarked rows. function driverKind(e){ if(e.type!=='driver')return null; const m=/^Enquiry type:\s*(Owner driver|Fleet\s*\/\s*Operator)/i.exec(e.message||''); if(m)return /fleet/i.test(m[1])?'operator':'owner'; return e.business_name?'operator':'owner'; } function toast(msg){const t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');clearTimeout(t._t);t._t=setTimeout(()=>t.classList.remove('show'),2500);} load(); function createTestEnquiry(){ const names=['Harlow Barbers','Pizza Palace','The Gym Harlow','Nail Studio','Harlow Dental','Quick Lube','Florist Express']; const name=names[Math.floor(Math.random()*names.length)]; const payload={type:'shop',business_name:name,contact_name:'Test Contact',email:'manager@space4promotion.com',phone:'07700900000',postcode:'CM20 1BE',message:'Test enquiry created from pipeline'}; fetch('/api/enquiries',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}) .then(r=>r.json()).then(()=>{toast('Test enquiry created ✓');loadEnquiries();}); } let contractEnquiryId = null; function openContractModal(id) { const e = enquiries.find(e => e.id === id); if (!e) return; contractEnquiryId = id; document.getElementById('cm-business').textContent = e.business_name || '—'; document.getElementById('cm-contact').textContent = e.contact_name || '—'; document.getElementById('cm-email').textContent = e.email || '—'; document.getElementById('cm-slots').value = '1'; document.getElementById('cm-slot-type').value = 'single'; document.getElementById('cm-price-per-slot').value = e.monthly_rate || ''; document.getElementById('cm-length').value = 'rolling'; const today = new Date().toISOString().split('T')[0]; document.getElementById('cm-start-date').value = today; updateContractTotal(); document.getElementById('contract-modal-bg').classList.add('open'); document.body.style.overflow = 'hidden'; } function closeContractModal() { document.getElementById('contract-modal-bg').classList.remove('open'); document.body.style.overflow = ''; contractEnquiryId = null; } function closeContractModalBg(e) { if (e.target === document.getElementById('contract-modal-bg')) closeContractModal(); } function updateContractTotal() { const slots = parseInt(document.getElementById('cm-slots').value) || 1; const price = parseFloat(document.getElementById('cm-price-per-slot').value) || 0; const total = slots * price; document.getElementById('cm-total').textContent = '£' + total.toFixed(2); } async function sendContract() { if (!contractEnquiryId) return; const slots = parseInt(document.getElementById('cm-slots').value); const slotType = document.getElementById('cm-slot-type').value; const pricePerSlot = parseFloat(document.getElementById('cm-price-per-slot').value); const contractLength = document.getElementById('cm-length').value; const startDate = document.getElementById('cm-start-date').value; if (!pricePerSlot || pricePerSlot <= 0) { alert('Please enter a price per slot.'); return; } if (!startDate) { alert('Please select a start date.'); return; } const monthlyRate = slots * pricePerSlot; const btn = document.querySelector('#contract-modal-bg .btn-primary'); btn.textContent = 'Sending...'; btn.disabled = true; try { const res = await fetch('/api/contracts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enquiry_id: contractEnquiryId, slots, slot_type: slotType, monthly_rate: monthlyRate, contract_length: contractLength, start_date: startDate }) }); const data = await res.json(); if (res.ok && data.success) { closeContractModal(); toast('Contract sent ✓'); await load(); } else if (res.ok) { // Success but unexpected response — still worked closeContractModal(); toast('Contract sent ✓'); await load(); } else { alert('Failed to send contract: ' + (data.error || 'Unknown error')); btn.textContent = '📧 Send Contract'; btn.disabled = false; } } catch(e) { // If it errored but contract may still have sent — just close and reload closeContractModal(); toast('Contract sent ✓'); await load(); } } let callbackEnquiryId = null; function openCallbackModal(id) { const e = enquiries.find(e => e.id === id); if (!e) return; callbackEnquiryId = id; document.getElementById('cb-name').textContent = e.business_name || e.contact_name; // Default to 5 days from now at 9am setCallbackPreset(5); document.getElementById('callback-modal-bg').classList.add('open'); document.body.style.overflow = 'hidden'; } function closeCallbackModal() { document.getElementById('callback-modal-bg').classList.remove('open'); document.body.style.overflow = ''; callbackEnquiryId = null; } function setCallbackPreset(days, btn) { const d = new Date(); d.setDate(d.getDate() + days); d.setHours(9, 0, 0, 0); const iso = d.toISOString().slice(0, 16); document.getElementById('cb-datetime').value = iso; updateCbPreview(d); document.querySelectorAll('.cb-preset-btn').forEach(b => b.classList.remove('selected')); if(btn) btn.classList.add('selected'); } document.addEventListener('DOMContentLoaded', () => { const dtInput = document.getElementById('cb-datetime'); if (dtInput) { dtInput.addEventListener('change', () => { if (dtInput.value) updateCbPreview(new Date(dtInput.value)); }); } }); function updateCbPreview(date) { const preview = document.getElementById('cb-preview'); const text = document.getElementById('cb-preview-text'); preview.style.display = 'block'; text.textContent = date.toLocaleString('en-GB', {weekday:'long', day:'numeric', month:'long', year:'numeric', hour:'2-digit', minute:'2-digit'}); } async function saveCallbackSchedule() { if (!callbackEnquiryId) return; const dt = document.getElementById('cb-datetime').value; if (!dt) { alert('Please select a callback date.'); return; } const payload = { stage: 'callback', callback_date: dt, callback_reminded: 0 }; const e = enquiries.find(e => e.id === callbackEnquiryId); const fullPayload = { stage: 'callback', notes: e.notes || '', contract_sent: e.contract_sent || 0, contract_signed: e.contract_signed || 0, monthly_rate: e.monthly_rate || null, callback_date: dt }; await fetch(API + '/enquiries/' + callbackEnquiryId, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(fullPayload) }); closeCallbackModal(); toast('Callback scheduled ✓'); await load(); }