Off lead.
Finally.

NW London · Home Boarding & Residential Recall

Reviews

Rated 4.8 “Excellent” on Trustpilot

★★★★★
The sighthound guy is awesome!
The sight hound guy is a truly lovely, knowledgeable, kind person! After finding his page on Instagram, I binge-watched every video and after commenting on one with a question about what would be best for my on-lead reactive whippet, he answered thoroughly, sent me more information and offered a call to talk it through. Even when I said I couldn’t be a potential client as I live too far away, he didn’t mind spending 20 minutes with me talking through my issues. He really understands sighthounds and gave me loads of valuable tips which I have been putting into practice. I am trying to be calm and stop scanning for threats! It’s a work on progress but getting better! I wish I lived in London and my dog could join his pack!
Anna June 23, 2026 Trustpilot
'; } function printContract(c){ post('get',{id:c.id}).then(function(res){ var ok=res?res.success:false; var d=ok?res.data:null; if(!d){window.alert('Could not load the contract.');return;} var w=window.open('','_blank'); if(!w){window.alert('Please allow pop-ups to open the printable contract.');return;} w.document.open();w.document.write(contractDoc(d));w.document.close(); setTimeout(function(){try{w.focus();w.print();}catch(e){}},350); }); } function render(root){ root.textContent='Loading...'; post('list').then(function(res){ root.innerHTML=''; if(!res||!res.success){root.appendChild(el('p','tsg-fr-err','Could not load the list. Make sure you are signed in as an admin.'));return;} var clients=(res.data||{}).clients||[]; root.appendChild(leadForm(root)); if(!clients.length){root.appendChild(el('p','tsg-fr-empty','No clients in the pipeline yet. Suitable Concierge enquiries land here automatically, or add a lead above.'));return;} var signed=[],pipeline=[],declined=[]; clients.forEach(function(c){ if(isSigned(c)){signed.push(c);} else if(c.stage==='declined'){declined.push(c);} else{pipeline.push(c);} }); if(pipeline.length){root.appendChild(el('div','tsg-fr-sub','Pipeline'));pipeline.forEach(function(c){root.appendChild(card(c,root));});} if(signed.length){root.appendChild(el('div','tsg-fr-sub','Contract signed, can book their board'));signed.forEach(function(c){root.appendChild(card(c,root));});} if(declined.length){root.appendChild(el('div','tsg-fr-sub','Declined'));declined.forEach(function(c){root.appendChild(card(c,root));});} }); } function leadForm(root){ var box=el('div','tsg-fr-card'); box.appendChild(el('div','tsg-fr-sub','New lead')); var wrap=el('div','tsg-fr-act'); var f={}; [['name','Name'],['email','Email'],['phone','Phone'],['dog','Dog'],['breed','Breed']].forEach(function(p){ var i=document.createElement('input');i.placeholder=p[1];i.style.cssText='background:#111;border:1px solid rgba(200,184,154,0.3);border-radius:7px;color:#eee;padding:8px 10px;font-size:0.8rem;width:130px;';f[p[0]]=i;wrap.appendChild(i); }); var add=el('button','tsg-fr-approve','Add lead'); add.addEventListener('click',function(){ var em=f.email.value.trim(); if(em===''||em.indexOf('@')===-1){window.alert('An email address is needed.');return;} add.disabled=true; post('lead-add',{email:em,name:f.name.value,phone:f.phone.value,dog:f.dog.value,breed:f.breed.value}).then(function(res){ add.disabled=false; var ok=res?res.success:false; if(ok){render(root);}else{window.alert('Could not add the lead.');} }); }); wrap.appendChild(add);box.appendChild(wrap); box.appendChild(el('div','tsg-fr-hint','Leads from the Concierge chat and the eligibility page appear here on their own. Use this form for people who reach you on WhatsApp first.')); return box; } function doContract(c,root,btn,force){ btn.disabled=true; var payload={id:c.id};if(force){payload.force=1;} post('send-contract',payload).then(function(res){ btn.disabled=false; if(!res){window.alert('Could not reach the server.');return;} if(!res.success){ if(res.reason==='pricing_unset'){window.alert('Choose Legacy or New pricing for this client first, then send the contract.');return;} if(res.reason==='q_incomplete'){if(window.confirm('Their questionnaire shows incomplete. Send the contract anyway?')){doContract(c,root,btn,1);}return;} window.alert('Could not send: '+(res.reason||'unknown'));return; } var d=res.data||{}; window.location.href=waUrl(waPhone(d.wa_phone||c.phone),d.message||d.link||''); setTimeout(function(){render(root);},800); }); } function card(c,root){ var wrap=el('div','tsg-fr-card'+(isApproved(c)?' is-approved':'')); var head=el('div','tsg-fr-head'); var left=el('div','tsg-fr-left'); left.appendChild(el('div','tsg-fr-name',c.name||'(no name)')); left.appendChild(el('div','tsg-fr-meta',[c.dog,c.breed,c.email].filter(Boolean).join(' / '))); head.appendChild(left); var badges=el('div','tsg-fr-badges'); badges.appendChild(el('span','tsg-fr-badge '+(isSigned(c)?'ok':(c.stage==='declined'?'no':'pend')),stageLabel(c))); var qok=(''+c.q_complete)==='1'; if(!isSigned(c)){badges.appendChild(el('span','tsg-fr-q '+(qok?'ok':'warn'),qok?'Form complete':'Form incomplete'));} if(c.pricing){badges.appendChild(el('span','tsg-fr-q ok',c.pricing==='old'?'Legacy rates':'New rates'));} else{badges.appendChild(el('span','tsg-fr-q warn','Pricing not set'));} head.appendChild(badges); wrap.appendChild(head); var prWrap=el('div','tsg-fr-act'); prWrap.appendChild(el('span','tsg-fr-hint','Pricing for this client:')); ['old','new'].forEach(function(s){ var on=(c.pricing===s); var b=el('button','tsg-fr-link',s==='old'?'Legacy rates':'New rates'); if(on){b.style.background='#c9a84c';b.style.color='#1a1a18';b.style.fontWeight='700';} b.addEventListener('click',function(){ if(on)return; b.disabled=true; setPricing(c,s,function(ok){b.disabled=false;if(ok){render(root);}else{window.alert('Could not save the pricing choice.');}}); }); prWrap.appendChild(b); }); wrap.appendChild(prWrap); var act=el('div','tsg-fr-act'); var dbtn=el('button','tsg-fr-link','View details'); dbtn.addEventListener('click',function(){toggle(wrap,c);}); act.appendChild(dbtn); if(!isSigned(c)){ if(c.stage!=='declined'){ var sq=el('button','tsg-fr-wa',c.q_sent_at?'Re-send questions (WhatsApp)':'Send enrolment questions (WhatsApp)'); sq.addEventListener('click',function(){ sq.disabled=true; post('send-questionnaire',{id:c.id}).then(function(res){ sq.disabled=false; var ok=res?res.success:false; if(!ok){window.alert('Could not mint the questionnaire link.');return;} var d=res.data||{}; window.location.href=waUrl(waPhone(d.wa_phone||c.phone),d.message||d.link||''); setTimeout(function(){render(root);},800); }); }); act.appendChild(sq); } } if(c.stage!=='declined'){ var sc=el('button','tsg-fr-approve',isSigned(c)?'Send registration link (WhatsApp)':'Send contract (WhatsApp)'); sc.addEventListener('click',function(){doContract(c,root,sc,0);}); act.appendChild(sc); } if(c.stage!=='declined'){ var dc=el('button','tsg-fr-decline','Decline'); dc.addEventListener('click',function(){if(!window.confirm('Decline this client?'))return;dc.disabled=true;post('decline',{id:c.id}).then(function(){render(root);});}); act.appendChild(dc); } act.appendChild(el('div','tsg-fr-hint','The order is: send the enrolment questions, they complete the form and book their meet and greet, then choose their pricing and send the contract. Sending the contract is your approval. Each send opens WhatsApp with the message and link ready; if no number is saved, pick the contact in WhatsApp and press send.')); var del=el('button','tsg-fr-del','Delete'); del.addEventListener('click',function(){ if(!window.confirm('Permanently delete the pre-enrolment record for '+(c.name||'this client')+'? This removes their questionnaire and onboarding entry and cannot be undone.'))return; del.disabled=true;del.textContent='Deleting...'; post('delete',{id:c.id}).then(function(res){ if(res?res.success:false){render(root);} else{del.disabled=false;del.textContent='Delete';window.alert('Delete failed. Make sure you are signed in as an admin.');} }); }); act.appendChild(del); wrap.appendChild(act); var det=el('div','tsg-fr-details');det.style.display='none';wrap.appendChild(det); return wrap; } function toggle(wrap,c){ var det=wrap.querySelector('.tsg-fr-details'); if(det.style.display!=='none'){det.style.display='none';return;} det.style.display='block';det.textContent='Loading...'; post('get',{id:c.id}).then(function(res){ det.innerHTML=''; if(!res||!res.success){det.appendChild(el('p','tsg-fr-err','Could not load details.'));return;} var d=res.data||{};var a=d.answers||{}; var rows=[['Owner',d.name],['Dog',d.dog],['Breed',d.breed],['Email',d.email],['Phone',d.phone||a.cPhone],['Address',[a.aStreet,a.aLine2,a.aCity,a.aPost].filter(Boolean).join(', ')],['Background',a.intake_background||d.chat_note],['Questions sent',d.q_sent_at],['Submitted',d.created],['Meet and greet',d.mg_date?(d.mg_date+' '+(d.mg_time||'')):''],['Contract sent',d.contract_sent_at]]; var signed=(''+d.contract_signed)==='1'; rows.push(['Contract',signed?'Signed':(d.contract_sent_at?'Sent, awaiting signature':'Not sent')]); if(signed){ rows.push(['Signed by',d.contract_name]); rows.push(['Signed on',d.contract_signed_at]); var ct=d.contract||{}; if(ct.emergency_name){rows.push(['Emergency contact',ct.emergency_name+(ct.emergency_phone?' / '+ct.emergency_phone:'')]);} } var dl=el('div','tsg-fr-dl'); rows.forEach(function(r){if(!r[1])return;var row=el('div','tsg-fr-row');row.appendChild(el('span','tsg-fr-k',r[0]));row.appendChild(el('span','tsg-fr-v',r[1]));dl.appendChild(row);}); det.appendChild(dl); if(signed){ if(d.contract_terms){ det.appendChild(el('div','tsg-fr-sub','Signed contract')); var terms=el('div','tsg-fr-terms'); d.contract_terms.forEach(function(p){terms.appendChild(el('p',null,p));}); det.appendChild(terms); var labels=d.contract_consent_labels||{}; var consentsObj=(d.contract||{}).consents||{}; Object.keys(labels).forEach(function(k){ var crow=el('div','tsg-fr-crow'); crow.appendChild(el('span','tsg-fr-ctick',consentsObj[k]?'Agreed':'Not agreed')); crow.appendChild(el('span',null,labels[k])); det.appendChild(crow); }); det.appendChild(el('div','tsg-fr-sig','Contract version '+d.contract_version+'. Emailed to the client and to '+'admin@k9exw.com'+' at signing.')); } var prbtn=el('button','tsg-fr-link','Print / save contract as PDF'); prbtn.addEventListener('click',function(){printContract(c);}); det.appendChild(prbtn); } var qk=[['insured','Insured'],['chip','Microchipped'],['vet','Registered vet'],['age','Over 1 year'],['feed','Group feeding'],['sleep','Kept together'],['exercise','Exercise consent'],['neuter','Spayed/neutered'],['vacc','Vaccinated'],['kennel','Kennel cough'],['offlead','Off-lead permission'],['gps','GPS tracker']]; var qd=el('div','tsg-fr-dl'); qk.forEach(function(k){if(!a[k[0]])return;var row=el('div','tsg-fr-row');row.appendChild(el('span','tsg-fr-k',k[1]));row.appendChild(el('span','tsg-fr-v',a[k[0]]));qd.appendChild(row);}); if(qd.children.length){det.appendChild(el('div','tsg-fr-sub','Questionnaire'));det.appendChild(qd);} }); } function init(){var root=document.getElementById(ROOT);if(!root)return;render(root);} if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',init);}else{init();} })();