| | |
| | | (()=>{class e{constructor(){this.container=document.querySelector(".jvb-referral"),this.container&&(this.a11y=window.jvbA11y,this.toggle=document.querySelector('button[data-action="toggle-referral"]'),this.initElements(),this.initListeners(),this.checkForReferral())}initElements(){this.selectors={copy:"button.copy",login:".login",submit:"[type=submit]"},this.forms=this.container.querySelectorAll("form"),console.log(this.forms),this.popup=new window.jvbPopup({toggle:this.toggle,popup:this.container,name:"Referral Box",onOpen:()=>{this.forms.forEach((e=>{e.addEventListener("submit",this.submitHandler)})),this.container.addEventListener("click",this.clickHandler),this.container.addEventListener("input",this.inputHandler)},onClose:()=>{this.forms.forEach((e=>{e.removeEventListener("submit",this.submitHandler)})),this.container.removeEventListener("click",this.clickHandler),this.container.removeEventListener("input",this.inputHandler)}}),this.tabs=null,this.container.querySelector("nav.tabs")&&(this.tabs=new window.jvbTabs(this.container,{updateURL:!1})),this.ui=window.uiFromSelectors(this.selectors,this.container)}initListeners(){this.clickHandler=this.handleClick.bind(this),this.inputHandler=this.handleInput.bind(this),this.submitHandler=this.handleFormSubmit.bind(this),this.changeHandler=this.handleChange.bind(this)}handleClick(e){if(e.target.classList.contains(".copy")){let t=e.target.dataset.target,r=this.container.querySelector(`#${t}`);r=!!r&&r.textContent,r&&this.handleCopy(e.target,r)}}handleChange(e){"referral-code"===e.target.id&&window.debouncer.schedule("check-referral",(()=>this.makeRequest("referrals/check-code",{code:e.target.value})))}handleInput(e){"referral-code"===e.target.id&&(e.target.value=e.target.value.toUpperCase())}initShareWidget(){this.initCopyButton(),this.loadStats()}async checkForReferral(){const e=this.getUrlParameter("seeReferral"),t=this.getUrlParameter("ref");if(!e&&!t)return;if(!t)return void this.popup.openPopup();const r=this.container.querySelector("#referral-code-input");if(!r)return;const n=t.toUpperCase();r.value=n,r.readOnly=!0,this.popup.togglePopup();try{const e=await this.validateCodeOnly(n);if(e.success){this.showReferrerBanner(e.referrer_name,n);const t=this.container.querySelector("#referral-name");t&&t.focus()}else r.readOnly=!1,this.showMessage("This referral link is invalid. Please enter a valid code.","error")}catch(e){console.error("Error validating code:",e),r.readOnly=!1}this.removeUrlParameter("ref")}getUrlParameter(e){return new URLSearchParams(window.location.search).get(e)}removeUrlParameter(e){const t=new URL(window.location);t.searchParams.delete(e),window.history.replaceState({},document.title,t.toString())}async validateCodeOnly(e){const t=await fetch(`${jvbSettings.api}/referrals/check-code`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e})});return await t.json()}showReferrerBanner(e,t){const r=this.container.querySelector(".referral-header");if(!r)return;const n=document.createElement("div");n.className="referrer-banner",n.innerHTML=`\n <div class="banner-icon">🎉</div>\n <div class="banner-content">\n <strong>${window.escapeHtml(e)}</strong> referred you!\n <div class="banner-code">Code: <code>${window.escapeHtml(t)}</code></div>\n </div>\n `,r.parentNode.insertBefore(n,r.nextSibling);const a=r.querySelector("h3");a&&(a.textContent="Complete Your Registration");const o=r.querySelector("p");o&&(o.textContent="Enter your details below to claim your welcome reward!")}handleCopy(e,t=""){if(""===t||"string"!=typeof t)return;let r=e.textContent;(navigator.clipboard||navigator.clipboard.writeText)&&navigator.clipboard.writeText(t).then((()=>{e.textContent="Copied!",e.style.background="#00a32a",setTimeout((()=>{e.textContent=r,e.style.background=""}),2e3)}))}async loadStats(){if(this.container.querySelector(".referral-stats"))try{const e=await fetch(`${jvbSettings.api}/referrals/stats`,{headers:{"X-WP-Nonce":jvbSettings.nonce}}),t=await e.json();t.success&&t.stats&&this.updateStats(t.stats)}catch(e){console.error("Error loading stats:",e)}}updateStats(e){const t={total:this.container.querySelector('[data-stat="total"]'),treated:this.container.querySelector('[data-stat="treated"]'),pending:this.container.querySelector('[data-stat="pending"]'),rewards:this.container.querySelector('[data-stat="rewards"]')};t.total&&(t.total.textContent=e.total_referrals||0),t.treated&&(t.treated.textContent=e.treated_count||0),t.pending&&(t.pending.textContent=e.pending_count||0),t.rewards&&(t.rewards.textContent="$"+parseFloat(e.available_rewards||0).toFixed(2))}async handleFormSubmit(e){console.log("Form Submission!"),window.debouncer.cancel("check-referral"),e.preventDefault(),console.log("Still working?");const t=e.target,r=new FormData(t);let n={};this.setFormLoading(!0,t);try{let e={success:!1,message:""};console.log(t),console.log(t.id),"referral-code-form"===t.id?(r.get("name")||(e.message+="We need your name to know who you are."),r.get("email")||(e.message+="We need your email to confirm you have access to it."),r.get("referral_code")||(e.message+="We need the referral code to know who sent you."),r.get("name")&&r.get("email")&&r.get("referral_code")&&(n.name=r.get("name"),n.email=r.get("email"),n.code=r.get("referral_code"),e=await this.makeRequest("referrals/register",n))):"login-form"===t.id&&r.get("login-email")&&(n.type="login",n.email=r.get("login-email"),n.context={},n.context.redirect_to=window.location.href+"?seeReferral=1",console.log("Making Request with: ",n),e=await this.makeRequest("magic-link",n)),e.success?this.handleSuccess(e):(this.showMessage(e.message||"Something went wrong. Please try again.","error"),this.setFormLoading(!1,t))}catch(e){console.error("Error registering:",e),this.showMessage("Something went wrong. Please try again.","error"),this.setFormLoading(!1,t)}finally{this.setFormLoading(!1,t)}}async makeRequest(e,t){if(!["magic-link","referrals/register","referrals/check-code"].includes(e))return{success:!1,message:"Something went wrong (Invalid endpoint)."};console.log("Endpoint: ",e),console.log("Data: ",t);const r=await fetch(`${jvbSettings.api}${e}`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":jvbSettings.nonce},body:JSON.stringify(t)});return await r.json()}handleSuccess(e){this.container.querySelectorAll("form").forEach((e=>{window.fade(e,!1)}));const t=this.container.querySelector(".success-message");t&&(t.hidden=!1,t.scrollIntoView({behavior:"smooth",block:"center"}),this.dispatchEvent("emailSent",{email:e.email}))}setFormLoading(e,t){t.querySelectorAll("input").forEach((t=>t.disabled=e));let r=t.querySelector(".status"),n=r.querySelector(".message");r.hidden=e,r.classList.toggle("loading",e),n.textContent=e?"Checking with server...":""}showMessage(e,t="success"){const r=this.container.querySelector("#referral-message");r&&(r.textContent=e,r.className="message "+t,r.style.display="block","error"===t&&setTimeout((()=>{r.style.display="none"}),5e3))}dispatchEvent(e,t){const r=new CustomEvent("referralWidget:"+e,{detail:t,bubbles:!0});this.container.dispatchEvent(r)}}document.addEventListener("DOMContentLoaded",(()=>{window.jvbReferral=new e}))})(); |
| | | (()=>{class e{constructor(){this.container=document.querySelector("aside.referral"),this.container&&(this.a11y=window.jvbA11y,this.toggle=document.querySelector('button[data-action="toggle-referral"]'),this.hasCopy=navigator.clipboard&&navigator.clipboard.writeText,this.initElements(),this.initStore(),this.initListeners(),this.checkForReferral())}initElements(){this.selectors={copyBtn:".copy-btn",checkCode:".check-code-btn",submit:"[type=submit]",recentList:".recent-referrals-list",stats:{codeUsed:'[data-stat="code_used"]',consultations:'[data-stat="consultations"]',treatments:'[data-stat="treatments"]',rewards:'[data-stat="total_rewards"]'}},this.forms=this.container.querySelectorAll("form"),this.popup=window.jvbPopup.registerPopup({toggle:this.toggle,popup:this.container,name:"Referral Box",onOpen:()=>{this.bindEventListeners(!0)},onClose:()=>{this.bindEventListeners(!1)}}),this.tabs=null,this.container.querySelector("nav.tabs")&&(this.tabs=window.jvbTabs.registerTab(this.container,{updateURL:!1})),this.ui=window.uiFromSelectors(this.selectors),this.hasCopy||document.querySelectorAll(this.selectors.copyBtn).forEach((e=>{e.remove()}))}initStore(){if(!this.isLoggedIn())return;const e=window.jvbStore.register("referrals",[{storeName:"stats",keyPath:"user_id",endpoint:"referrals/stats",TTL:3e5,showLoading:!1,delayFetch:!1,filters:{type:"dashboard",user:window.auth.getUser()}},{storeName:"list",keyPath:"id",endpoint:"referrals",TTL:6e5,showLoading:!1,delayFetch:!1,filters:{user:window.auth.getUser(),status:"all",limit:50,offset:0}}]);this.statsStore=e.stats,this.listStore=e.list,this.statsStore&&this.statsStore.subscribe(this.handleStatsEvent.bind(this)),this.listStore&&this.listStore.subscribe(this.handleListEvent.bind(this))}initListeners(){this.clickHandler=this.handleClick.bind(this),this.inputHandler=this.handleInput.bind(this),this.submitHandler=this.handleFormSubmit.bind(this)}bindEventListeners(e){const t=e?"addEventListener":"removeEventListener";this.forms.forEach((e=>{e[t]("submit",this.submitHandler)})),this.container[t]("click",this.clickHandler),this.container[t]("input",this.inputHandler)}isLoggedIn(){return Boolean(window.auth.getUser())}handleStatsEvent(e,t){switch(e){case"data-loaded":t.items&&t.items.length>0&&this.updateStatsDisplay();break;case"fetch-error":console.error("Error loading stats:",t.error)}}handleListEvent(e,t){switch(e){case"data-loaded":this.ui.recentList&&this.renderRecentReferrals();break;case"fetch-error":console.error("Error loading referrals:",t.error)}}updateStatsDisplay(){if(0===!this.statsStore.data.size)return;let e=this.statsStore.data.get(parseInt(window.auth.getUser()));const t={total:e.code_used||0,treated:e.treatments||0,pending:e.pending||0,rewards:"$"+parseFloat(e.total_rewards||0).toFixed(2)};Object.entries(t).forEach((([e,t])=>{const r=this.container.querySelector(`[data-stat="${e}"]`);r&&(r.textContent=t)}));const r=this.container.querySelectorAll(".stats .card");r.length>=4&&(r[0].querySelector(".stat-number").textContent=t.code_used,r[1].querySelector(".stat-number").textContent=t.consultations,r[2].querySelector(".stat-number").textContent=t.treatments,r[3].querySelector(".stat-number").textContent=t.total_rewards)}handleClick(e){const t=e.target.closest(".copy-btn, .check-code-btn, .attn");t&&(t.classList.contains("copy-btn")?this.handleCopyClick(t):t.classList.contains("check-code-btn")?this.handleCheckCode(e):t.classList.contains("attn")&&t.classList.remove("attn"))}handleCopyClick(e){const t=e.dataset.target,r=this.container.querySelector(`#${t}`);if(!r)return;const s=r.textContent.trim();this.hasCopy&&navigator.clipboard.writeText(s).then((()=>{e.classList.toggle("success"),setTimeout((()=>{e.classList.remove("success")}),1500)}))}handleError(e,t){const{message:r,code:s,field:a}=t;switch(a?this.showFieldError(e,a,r):this.showFormStatus(e,"error",r||"Something went wrong. Please try again."),s){case"duplicate_email":break;case"invalid_code":const t=e.querySelector('[name="referral_code"]');t&&(t.readOnly=!1,t.focus());break;case"turnstile_failed":window.turnstile&&e.querySelector(".cf-turnstile")&&window.turnstile.reset()}}showFieldError(e,t,r){let s=e.querySelector(`.field[data-field="${t}"]`);if(s||(s=e.querySelector(`.field[data-field="referral_${t}"]`)),!s)return void this.showFormStatus(e,"error",r);const a=s.querySelector("input, textarea, select"),i=s.querySelector(".validation-message"),n=s.querySelector(".validation-icon.error"),o=s.querySelector(".validation-icon.success");a?(s.classList.remove("has-success"),s.classList.add("has-error"),a.classList.add("error"),a.setAttribute("aria-invalid","true"),n&&(n.hidden=!1),o&&(o.hidden=!0),i&&(i.textContent=r,i.hidden=!1),a.focus(),this.a11y?.announce(`Error in ${t}: ${r}`)):this.showFormStatus(e,"error",r)}showFormStatus(e,t,r=""){const s=e.querySelector(".fstatus");if(!s)return void console.warn("No .fstatus element found in form");s.hidden=!1;const a=s.querySelector(".message");s.querySelector(".icon")?.remove(),s.querySelector(".actions")?.remove();const i={saving:"Sending...",submitted:"Sent successfully!",error:"Something went wrong",checking:"Checking code..."},n={submitted:"check-circle",error:"close-circle",checking:"loading"};if(n[t]&&window.getIcon){const e=window.getIcon(n[t]);e&&s.prepend(e)}a&&(a.textContent=r||i[t]||t),s.classList.toggle("loading",["saving","checking"].includes(t)),"submitted"===t&&setTimeout((()=>s.hidden=!0),3e3),this.a11y&&this.a11y.announce(r||i[t]||t)}clearFormErrors(e){e.querySelectorAll(".field.has-error, .field.has-success").forEach((e=>{this.clearFieldValidation(e)}));const t=e.querySelector(".fstatus");t&&(t.hidden=!0)}clearFieldValidation(e){if(!e)return;const t=e.querySelector("input, textarea, select"),r=e.querySelector(".validation-message"),s=e.querySelectorAll(".validation-icon");e.classList.remove("has-error","has-success"),t&&(t.classList.remove("error"),t.removeAttribute("aria-invalid")),s.forEach((e=>e.hidden=!0)),r&&(r.hidden=!0,r.textContent="")}handleInput(e){"referral_code"!==e.target.id&&"referral_code"!==e.target.name||(e.target.value=e.target.value.toUpperCase());const t=e.target.closest(".field");t&&t.classList.contains("has-error")&&this.clearFieldValidation(t)}async handleCheckCode(e){e.preventDefault();const t=e.target.closest("form"),r=t.querySelector('[name="referral_code"]'),s=t.querySelector(".code-status");if(!r||!s)return;const a=r.value.trim();if(a){s.hidden=!1,s.className="code-status loading",s.innerHTML='<span class="spinner"></span> Checking...';try{const e=await this.validateCodeOnly(a);e.success?this.showCodeStatus(s,`✓ Valid! Referred by ${e.referrer_name}`,"success"):this.showCodeStatus(s,e.message||"Invalid code","error")}catch(e){console.error("Error checking code:",e),this.showCodeStatus(s,"Error checking code","error")}}else this.showCodeStatus(s,"Please enter a code","error")}showCodeStatus(e,t,r){e.hidden=!1,e.className=`code-status ${r}`,e.textContent=t,"error"===r&&setTimeout((()=>{e.hidden=!0}),5e3)}async checkForReferral(){const e=this.getUrlParameter("ref"),t=this.getUrlParameter("rname"),r=this.getUrlParameter("remail"),s=this.getUrlParameter("seeReferral");if(!e&&!s)return;if(s&&!e)return this.popup.openPopup(),void this.removeUrlParameter("seeReferral");const a=this.container.querySelector('[name="referral_code"]');if(!a)return;const i=e.toUpperCase();if(a.value=i,a.readOnly=!0,t||r){const e=this.container.querySelector('[name="referral_name"]');e&&(e.value=t);const s=this.container.querySelector('[name="referral_email"]');s&&(s.value=r)}this.popup.openPopup();try{const e=await this.validateCodeOnly(i);if(e.success){const t=a.closest("form").querySelector(".code-status");t&&this.showCodeStatus(t,`✓ ${e.referrer_name} invited you!`,"success");const r=this.container.querySelector('[name="referral_name"]');r&&!r.value&&r.focus()}else a.readOnly=!1,this.showMessage("This referral link is invalid. Please enter a valid code.","error")}catch(e){console.error("Error validating code:",e),a.readOnly=!1}this.removeUrlParameter("ref"),this.removeUrlParameter("rname"),this.removeUrlParameter("remail")}getUrlParameter(e){return new URLSearchParams(window.location.search).get(e)}removeUrlParameter(e){const t=new URL(window.location);t.searchParams.delete(e),window.history.replaceState({},document.title,t.toString())}async validateCodeOnly(e){const t=await fetch(`${jvbSettings.api}referrals/code`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":window.auth.getNonce()},body:JSON.stringify({code:e})});return await t.json()}renderRecentReferrals(){let e=this.ui.recentList,t=Array.from(this.listStore.data.values());t&&0!==t.length?e.innerHTML=t.map((e=>`\n\t\t\t<div class="referral-item">\n\t\t\t\t<div class="referral-info">\n\t\t\t\t\t<strong>${window.escapeHtml(e.referee_name)}</strong>\n\t\t\t\t\t<span class="status-badge">${e.referral_status}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class="referral-date">${window.formatTimeAgo(e.referred_at)}</div>\n\t\t\t</div>\n\t\t`)).join(""):e.innerHTML='<p class="no-referrals">Share your code to get started!</p>'}async handleFormSubmit(e){e.preventDefault();const t=e.target,r=new FormData(t);this.clearFormErrors(t),this.setFormLoading(!0,t);try{let e={success:!1,message:""};if("referral-code-form"===t.id){let s={name:r.get("referral_name"),email:r.get("referral_email"),referral_code:r.get("referral_code")};const a=t.querySelector('input[name="cf-turnstile-response"]');a&&a.value&&(s["cf-turnstile-response"]=a.value),s.name&&s.email&&s.referral_code?e=await this.makeRequest("auth/register",s):e.message="Please fill in all fields"}else if("login-form"===t.id){let s={type:"login",user_email:r.get("login_email"),context:{redirect_to:window.location.href+"?seeReferral=1"}};const a=t.querySelector('input[name="cf-turnstile-response"]');a&&a.value&&(s["cf-turnstile-response"]=a.value),s.user_email?e=await this.makeRequest("auth/magic",s):e.message="Please fill in your email"}e.success?this.handleSuccess(t,e):this.handleError(t,e)}catch(e){console.error("Error submitting form:",e),this.showFormMessage(t,"Something went wrong. Please try again.","error")}finally{this.setFormLoading(!1,t)}}async makeRequest(e,t){if(!["auth/magic","auth/register"].includes(e))return{success:!1,message:"Invalid endpoint"};const r=await window.auth.fetch(`${jvbSettings.api}${e}`,{method:"POST",body:JSON.stringify(t)});if(!r.ok){const e=await r.text();console.error("Error response:",r.status,e);try{return JSON.parse(e)}catch{return{success:!1,message:"Server error"}}}return await r.json()}handleSuccess(e,t){e.style.display="none";const r=e.nextElementSibling;r&&r.classList.contains("success-content")&&(r.hidden=!1,r.scrollIntoView({behavior:"smooth",block:"center"})),this.dispatchEvent("emailSent",{email:t.email})}showFormMessage(e,t,r="error"){const s=e.querySelector(".status");if(!s)return;const a=s.querySelector(".message");a&&(a.textContent=t),s.hidden=!1,s.className=`status ${r}`,"error"===r&&setTimeout((()=>{s.hidden=!0}),5e3)}setFormLoading(e,t){t.querySelectorAll("input, button, textarea, select").forEach((t=>t.disabled=e)),e&&this.showFormStatus(t,"saving")}dispatchEvent(e,t){const r=new CustomEvent("referralWidget:"+e,{detail:t,bubbles:!0});this.container.dispatchEvent(r)}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbReferral=new e)}))}))})(); |