/**
|
* Referral Widget Manager
|
* Handles both logged-in share widget and public code validation widget
|
*
|
*/
|
|
class Referral {
|
constructor() {
|
this.container = document.querySelector('.jvb-referral');
|
if (!this.container) {
|
return;
|
}
|
|
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(form => {
|
form.addEventListener('submit', this.submitHandler);
|
});
|
this.container.addEventListener('click', this.clickHandler);
|
this.container.addEventListener('input', this.inputHandler);
|
},
|
onClose: () => {
|
this.forms.forEach(form => {
|
form.removeEventListener('submit', this.submitHandler);
|
});
|
this.container.removeEventListener('click', this.clickHandler);
|
this.container.removeEventListener('input', this.inputHandler);
|
}
|
});
|
|
this.tabs = null;
|
if (this.container.querySelector('nav.tabs')) {
|
this.tabs = new window.jvbTabs(this.container, {updateURL: false});
|
}
|
|
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 target = e.target.dataset.target;
|
let value = this.container.querySelector(`#${target}`);
|
value = (value) ? value.textContent : false;
|
if (value) {
|
this.handleCopy(e.target, value);
|
}
|
}
|
}
|
|
handleChange(e) {
|
if (e.target.id === 'referral-code') {
|
window.debouncer.schedule(
|
'check-referral',
|
()=> this.makeRequest('referrals/check-code', {code: e.target.value})),
|
150
|
}
|
}
|
|
handleInput(e) {
|
if (e.target.id === 'referral-code') {
|
e.target.value = e.target.value.toUpperCase();
|
}
|
}
|
|
// ==========================================
|
// SHARE WIDGET (Logged-In Users)
|
// ==========================================
|
|
initShareWidget() {
|
this.initCopyButton();
|
this.loadStats();
|
}
|
|
/**
|
* Check for ?ref parameter in URL and pre-fill code
|
*/
|
async checkForReferral() {
|
const isLoggedIn = this.getUrlParameter('seeReferral');
|
const refCode = this.getUrlParameter('ref');
|
if (!isLoggedIn && !refCode) {
|
return;
|
}
|
if (!refCode) {
|
this.popup.openPopup();
|
return;
|
}
|
|
const codeInput = this.container.querySelector('#referral-code-input');
|
if (!codeInput) return;
|
|
// Convert to uppercase
|
const code = refCode.toUpperCase();
|
|
// Pre-fill the code input
|
codeInput.value = code;
|
codeInput.readOnly = true; // Make it read-only since it came from link
|
|
this.popup.togglePopup();
|
|
// Validate the code immediately to show referrer info
|
try {
|
const referrer = await this.validateCodeOnly(code);
|
|
if (referrer.success) {
|
// Show referrer info banner
|
this.showReferrerBanner(referrer.referrer_name, code);
|
|
// Focus on name input (first empty field)
|
const nameInput = this.container.querySelector('#referral-name');
|
if (nameInput) {
|
nameInput.focus();
|
}
|
} else {
|
// Invalid code - make input editable and show error
|
codeInput.readOnly = false;
|
this.showMessage('This referral link is invalid. Please enter a valid code.', 'error');
|
}
|
} catch (error) {
|
console.error('Error validating code:', error);
|
codeInput.readOnly = false;
|
}
|
|
// Clean up URL (remove ?ref parameter)
|
this.removeUrlParameter('ref');
|
}
|
|
/**
|
* Get URL parameter value
|
*/
|
getUrlParameter(name) {
|
const urlParams = new URLSearchParams(window.location.search);
|
return urlParams.get(name);
|
}
|
|
/**
|
* Remove URL parameter (clean URL)
|
*/
|
removeUrlParameter(name) {
|
const url = new URL(window.location);
|
url.searchParams.delete(name);
|
window.history.replaceState({}, document.title, url.toString());
|
}
|
|
/**
|
* Validate code without registering (just check if valid)
|
*/
|
async validateCodeOnly(code) {
|
const response = await fetch(`${jvbSettings.api}/referrals/check-code`, {
|
method: 'POST',
|
headers: {
|
'Content-Type': 'application/json'
|
},
|
body: JSON.stringify({ code: code })
|
});
|
|
return await response.json();
|
}
|
|
/**
|
* Show banner with referrer info
|
*/
|
showReferrerBanner(referrerName, code) {
|
const header = this.container.querySelector('.referral-header');
|
if (!header) return;
|
|
// Create banner
|
const banner = document.createElement('div');
|
banner.className = 'referrer-banner';
|
banner.innerHTML = `
|
<div class="banner-icon">🎉</div>
|
<div class="banner-content">
|
<strong>${window.escapeHtml(referrerName)}</strong> referred you!
|
<div class="banner-code">Code: <code>${window.escapeHtml(code)}</code></div>
|
</div>
|
`;
|
|
// Insert after header
|
header.parentNode.insertBefore(banner, header.nextSibling);
|
|
// Update header text
|
const headerTitle = header.querySelector('h3');
|
if (headerTitle) {
|
headerTitle.textContent = 'Complete Your Registration';
|
}
|
|
const headerDesc = header.querySelector('p');
|
if (headerDesc) {
|
headerDesc.textContent = 'Enter your details below to claim your welcome reward!';
|
}
|
}
|
|
/**
|
* Copy referral link to clipboard
|
*/
|
handleCopy(button, text = '') {
|
if (text === '' || typeof text !== 'string') {
|
return;
|
}
|
let originalText = button.textContent;
|
if (navigator.clipboard || navigator.clipboard.writeText) {
|
navigator.clipboard.writeText(text).then(() => {
|
button.textContent = 'Copied!';
|
button.style.background = '#00a32a';
|
|
setTimeout(() => {
|
button.textContent = originalText;
|
button.style.background = '';
|
}, 2000);
|
})
|
}
|
}
|
|
async loadStats() {
|
const statsContainer = this.container.querySelector('.referral-stats');
|
if (!statsContainer) return;
|
|
try {
|
const response = await fetch(`${jvbSettings.api}/referrals/stats`, {
|
headers: { 'X-WP-Nonce': jvbSettings.nonce }
|
});
|
|
const data = await response.json();
|
if (data.success && data.stats) {
|
this.updateStats(data.stats);
|
}
|
} catch (error) {
|
console.error('Error loading stats:', error);
|
}
|
}
|
|
/**
|
* Update stats display
|
*/
|
updateStats(stats) {
|
const elements = {
|
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"]')
|
};
|
|
if (elements.total) elements.total.textContent = stats.total_referrals || 0;
|
if (elements.treated) elements.treated.textContent = stats.treated_count || 0;
|
if (elements.pending) elements.pending.textContent = stats.pending_count || 0;
|
if (elements.rewards) {
|
elements.rewards.textContent = '$' + parseFloat(stats.available_rewards || 0).toFixed(2);
|
}
|
}
|
|
/**
|
* Handle form submission
|
*/
|
async handleFormSubmit(event) {
|
console.log('Form Submission!');
|
window.debouncer.cancel('check-referral');
|
event.preventDefault();
|
console.log('Still working?');
|
|
const form = event.target;
|
|
// Get form data
|
const formData = new FormData(form);
|
|
let data = {};
|
|
// Disable form
|
this.setFormLoading(true, form);
|
|
try {
|
|
let result = {success: false, message: ''};
|
console.log(form);
|
console.log(form.id);
|
if (form.id === 'referral-code-form') {
|
if (!formData.get('name')) {
|
result.message += 'We need your name to know who you are.';
|
}
|
if (!formData.get('email')) {
|
result.message += 'We need your email to confirm you have access to it.';
|
}
|
if (!formData.get('referral_code')) {
|
result.message += 'We need the referral code to know who sent you.';
|
}
|
if (formData.get('name') && formData.get('email') && formData.get('referral_code')) {
|
data.name = formData.get('name');
|
data.email = formData.get('email');
|
data.code = formData.get('referral_code');
|
result = await this.makeRequest('referrals/register', data);
|
}
|
} else if (form.id === 'login-form' && formData.get('login-email')) {
|
data.type = 'login';
|
data.email = formData.get('login-email');
|
data.context = {};
|
data.context['redirect_to'] = window.location.href+'?seeReferral=1';
|
console.log('Making Request with: ', data);
|
result = await this.makeRequest('magic', data);
|
}
|
|
|
if (result.success) {
|
this.handleSuccess(result);
|
} else {
|
this.showMessage(result.message || 'Something went wrong. Please try again.', 'error');
|
this.setFormLoading(false, form);
|
}
|
} catch (error) {
|
console.error('Error registering:', error);
|
this.showMessage('Something went wrong. Please try again.', 'error');
|
this.setFormLoading(false, form);
|
} finally {
|
this.setFormLoading(false, form);
|
}
|
}
|
|
async makeRequest(endpoint, data) {
|
if (![
|
'magic',
|
'referrals/register',
|
'referrals/check-code'
|
].includes(endpoint)) {
|
return {success:false, message: 'Something went wrong (Invalid endpoint).'}
|
}
|
console.log('Endpoint: ', endpoint);
|
console.log('Data: ', data);
|
const response = await fetch(`${jvbSettings.api}${endpoint}`, {
|
method: 'POST',
|
headers: {
|
'Content-Type': 'application/json',
|
'X-WP-Nonce': jvbSettings.nonce,
|
},
|
body: JSON.stringify(data)
|
});
|
|
return await response.json();
|
}
|
|
/**
|
* Show success state
|
*/
|
handleSuccess(result) {
|
//Hide forms
|
this.container.querySelectorAll('form').forEach(form => {
|
window.fade(form, false);
|
});
|
|
const successState = this.container.querySelector('.success-message');
|
if (!successState) return;
|
|
// Show success message
|
successState.hidden = false;
|
|
// Scroll to message
|
successState.scrollIntoView({
|
behavior: 'smooth',
|
block: 'center'
|
});
|
|
// Fire custom event
|
this.dispatchEvent('emailSent', {
|
email: result.email
|
});
|
}
|
|
/**
|
* Set form loading state
|
*/
|
setFormLoading(loading, form) {
|
const inputs = form.querySelectorAll('input');
|
|
inputs.forEach(input => input.disabled = loading);
|
let status = form.querySelector('.status');
|
let message = status.querySelector('.message');
|
status.hidden = loading;
|
status.classList.toggle('loading', loading);
|
if (loading) {
|
message.textContent = 'Checking with server...';
|
} else {
|
message.textContent = '';
|
}
|
}
|
|
/**
|
* Show message
|
*/
|
showMessage(text, type = 'success') {
|
const messageDiv = this.container.querySelector('#referral-message');
|
if (!messageDiv) return;
|
|
messageDiv.textContent = text;
|
messageDiv.className = 'message ' + type;
|
messageDiv.style.display = 'block';
|
|
if (type === 'error') {
|
setTimeout(() => {
|
messageDiv.style.display = 'none';
|
}, 5000);
|
}
|
}
|
|
/**
|
* Dispatch custom event
|
*/
|
dispatchEvent(eventName, detail) {
|
const event = new CustomEvent('referralWidget:' + eventName, {
|
detail: detail,
|
bubbles: true
|
});
|
this.container.dispatchEvent(event);
|
}
|
}
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
window.jvbReferral = new Referral();
|
});
|