From 25be5747a6e462a3d09fc6607b3639b79e4d9374 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 23 Dec 2025 20:11:26 +0000
Subject: [PATCH] =EmailManager.php refactor, Turnstile properly integrated with form submissions

---
 assets/js/concise/Referral.js |  829 ++++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 592 insertions(+), 237 deletions(-)

diff --git a/assets/js/concise/Referral.js b/assets/js/concise/Referral.js
index 9fe722d..6435e98 100644
--- a/assets/js/concise/Referral.js
+++ b/assets/js/concise/Referral.js
@@ -1,31 +1,37 @@
 /**
  * Referral Widget Manager
  * Handles both logged-in share widget and public code validation widget
- *
  */
 
 class Referral {
 	constructor() {
-		this.container = document.querySelector('.jvb-referral');
+		this.container = document.querySelector('aside.referral');
 		if (!this.container) {
 			return;
 		}
 
 		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()
-	{
+	initElements() {
 		this.selectors = {
-			copy: 'button.copy',
-			login: '.login',
+			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');
@@ -34,87 +40,475 @@
 			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);
+				this.bindEventListeners(true);
 			},
 			onClose: () => {
-				this.forms.forEach(form => {
-					form.removeEventListener('submit', this.submitHandler);
-				});
-				this.container.removeEventListener('click', this.clickHandler);
-				this.container.removeEventListener('input', this.inputHandler);
+				this.bindEventListeners(false);
 			}
 		});
 
 		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);
+
+		this.ui = window.uiFromSelectors(this.selectors);
+
+
+
+		if (!this.hasCopy) {
+			document.querySelectorAll(this.selectors.copyBtn).forEach(btn => {
+				btn.remove();
+			});
+		}
+		this.formController = null;
 	}
 
+	initStore() {
+		if (!this.isLoggedIn()) return;
+
+		const stores = window.jvbStore.register(
+			'referrals',
+			[
+				// Dashboard stats store
+				{
+					storeName: 'stats',
+					keyPath: 'user_id',
+					endpoint: 'referrals/stats',
+					TTL: 5 * 60 * 1000,
+					showLoading: false,
+					delayFetch: false,
+					filters: {
+						type: 'dashboard',
+						user: window.auth.getUser()
+					}
+				},
+				// Referrals list store
+				{
+					storeName: 'list',
+					keyPath: 'id',
+					endpoint: 'referrals',
+					TTL: 10 * 60 * 1000,
+					showLoading: false,
+					delayFetch: false,
+					filters: {
+						user: window.auth.getUser(),
+						status: 'all',
+						limit: 50,
+						offset: 0
+					}
+				}
+			]
+		);
+
+		this.statsStore = stores.stats;
+		this.listStore = stores.list;
+
+		// Subscribe to store events
+		if (this.statsStore) {
+			this.statsStore.subscribe(this.handleStatsEvent.bind(this));
+		}
+		if (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);
-		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);
-			}
+	bindEventListeners(bind) {
+		const method = bind ? 'addEventListener' : 'removeEventListener';
+
+		this.forms.forEach(form => {
+			form[method]('submit', this.submitHandler);
+		});
+
+		this.container[method]('click', this.clickHandler);
+		this.container[method]('input', this.inputHandler);
+	}
+
+	isLoggedIn() {
+		return Boolean(window.auth.getUser());
+	}
+
+	/**
+	 * Handle DataStore stats events
+	 */
+	handleStatsEvent(event, data) {
+		switch(event) {
+			case 'data-loaded':
+				if (data.items && data.items.length > 0) {
+					this.updateStatsDisplay();
+				}
+				break;
+			case 'fetch-error':
+				console.error('Error loading stats:', data.error);
+				break;
 		}
 	}
 
-	handleChange(e) {
-		if (e.target.id === 'referral-code') {
-			window.debouncer.schedule(
-				'check-referral',
-				()=> this.makeRequest('referrals/check-code', {code: e.target.value})),
-				150
+	/**
+	 * Handle DataStore list events
+	 */
+	handleListEvent(event, data) {
+		switch(event) {
+			case 'data-loaded':
+				// Let ViewController handle main list rendering
+				// Only update sidebar preview if it exists
+				if (this.ui.recentList) {
+					this.renderRecentReferrals();
+				}
+				break;
+			case 'fetch-error':
+				console.error('Error loading referrals:', data.error);
+				break;
+		}
+	}
+
+	/**
+	 * Update stats display
+	 */
+	updateStatsDisplay() {
+		if (!this.statsStore.data.size === 0) return;
+		let stats = this.statsStore.data.get(parseInt(window.auth.getUser()));
+		const updates = {
+			total: stats['code_used'] || 0,
+			treated: stats.treatments || 0,
+			pending: stats.pending || 0,
+			rewards: '$' + parseFloat(stats['total_rewards'] || 0).toFixed(2)
+		};
+
+		Object.entries(updates).forEach(([key, value]) => {
+			const element = this.container.querySelector(`[data-stat="${key}"]`);
+			if (element) {
+				element.textContent = value;
+			}
+		});
+
+		// Also update stat cards if on dashboard
+		const statCards = this.container.querySelectorAll('.stats .card');
+		if (statCards.length >= 4) {
+			statCards[0].querySelector('.stat-number').textContent = updates.code_used;
+			statCards[1].querySelector('.stat-number').textContent = updates.consultations;
+			statCards[2].querySelector('.stat-number').textContent = updates.treatments;
+			statCards[3].querySelector('.stat-number').textContent = updates.total_rewards;
+		}
+	}
+
+
+
+
+	handleClick(e) {
+		const target = e.target.closest('.copy-btn, .check-code-btn, .attn');
+		if (!target) return;
+
+		if (target.classList.contains('copy-btn')) {
+			this.handleCopyClick(target);
+		} else if (target.classList.contains('check-code-btn')) {
+			this.handleCheckCode(e);
+		} else if (target.classList.contains('attn')) {
+			target.classList.remove('attn');
+		}
+	}
+
+	/**
+	 * Handle copy button click with fallback
+	 */
+	handleCopyClick(button) {
+		const targetId = button.dataset.target;
+		const codeElement = this.container.querySelector(`#${targetId}`);
+
+		if (!codeElement) return;
+
+		const text = codeElement.textContent.trim();
+
+		// Try clipboard API first
+		if (this.hasCopy) {
+			navigator.clipboard.writeText(text).then(() => {
+				button.classList.toggle('success');
+				setTimeout(() => {
+					button.classList.remove('success');
+				}, 1500);
+			});
+		}
+	}
+
+
+	/**
+	 * Handle error response with field-specific feedback
+	 */
+	handleError(form, result) {
+		const { message, code, field } = result;
+
+		// If there's a specific field, highlight it
+		if (field) {
+			this.showFieldError(form, field, message);
+		} else {
+			// Show general form error using FormController pattern
+			this.showFormStatus(form, 'error', message || 'Something went wrong. Please try again.');
+		}
+
+		// Handle specific error codes
+		switch(code) {
+			case 'duplicate_email':
+				// Could add additional UI feedback
+				break;
+			case 'invalid_code':
+				// Unlock the referral code field so user can correct it
+				const codeInput = form.querySelector('[name="referral_code"]');
+				if (codeInput) {
+					codeInput.readOnly = false;
+					codeInput.focus();
+				}
+				break;
+			case 'turnstile_failed':
+				// Refresh Turnstile widget if available
+				if (window.turnstile && form.querySelector('.cf-turnstile')) {
+					window.turnstile.reset();
+				}
+				break;
+		}
+	}
+
+	/**
+	 * Show error for specific field
+	 */
+	showFieldError(form, fieldName, message) {
+		// Find the field wrapper (handles both direct names and referral_ prefixed names)
+		let fieldWrapper = form.querySelector(`.field[data-field="${fieldName}"]`);
+		if (!fieldWrapper) {
+			fieldWrapper = form.querySelector(`.field[data-field="referral_${fieldName}"]`);
+		}
+
+		if (!fieldWrapper) {
+			// If no field wrapper found, show as general form error
+			this.showFormStatus(form, 'error', message);
+			return;
+		}
+
+		const input = fieldWrapper.querySelector('input, textarea, select');
+		const validationMessage = fieldWrapper.querySelector('.validation-message');
+		const errorIcon = fieldWrapper.querySelector('.validation-icon.error');
+		const successIcon = fieldWrapper.querySelector('.validation-icon.success');
+
+		if (!input) {
+			this.showFormStatus(form, 'error', message);
+			return;
+		}
+
+		// Apply error state (following FormController pattern)
+		fieldWrapper.classList.remove('has-success');
+		fieldWrapper.classList.add('has-error');
+		input.classList.add('error');
+		input.setAttribute('aria-invalid', 'true');
+
+		// Show error icon, hide success icon
+		if (errorIcon) errorIcon.hidden = false;
+		if (successIcon) successIcon.hidden = true;
+
+		// Show error message
+		if (validationMessage) {
+			validationMessage.textContent = message;
+			validationMessage.hidden = false;
+		}
+
+		// Focus the problematic field
+		input.focus();
+
+		// Announce to screen readers
+		this.a11y?.announce(`Error in ${fieldName}: ${message}`);
+	}
+
+	showFormStatus(form, status, message = '') {
+		const statusWrap = form.querySelector('.fstatus');
+		if (!statusWrap) {
+			console.warn('No .fstatus element found in form');
+			return;
+		}
+
+		statusWrap.hidden = false;
+		const statusElement = statusWrap.querySelector('.message');
+
+		// Clear previous state
+		statusWrap.querySelector('.icon')?.remove();
+		statusWrap.querySelector('.actions')?.remove();
+
+		// Status messages
+		const messages = {
+			'saving': 'Sending...',
+			'submitted': 'Sent successfully!',
+			'error': 'Something went wrong',
+			'checking': 'Checking code...'
+		};
+
+		// Status icons (using window.getIcon like FormController)
+		const icons = {
+			'submitted': 'check-circle',
+			'error': 'close-circle',
+			'checking': 'loading'
+		};
+
+		// Add icon if available
+		if (icons[status] && window.getIcon) {
+			const icon = window.getIcon(icons[status]);
+			if (icon) {
+				statusWrap.prepend(icon);
+			}
+		}
+
+		// Set message
+		if (statusElement) {
+			statusElement.textContent = message || messages[status] || status;
+		}
+
+		// Add loading class for pending states
+		statusWrap.classList.toggle('loading', ['saving', 'checking'].includes(status));
+
+		// Auto-hide success messages
+		if (status === 'submitted') {
+			setTimeout(() => statusWrap.hidden = true, 3000);
+		}
+
+		// Announce to screen readers
+		if (this.a11y) {
+			this.a11y.announce(message || messages[status] || status);
+		}
+	}
+
+	/**
+	 * Clear all form errors
+	 */
+	clearFormErrors(form) {
+		// Clear field-level errors
+		form.querySelectorAll('.field.has-error, .field.has-success').forEach(fieldWrapper => {
+			this.clearFieldValidation(fieldWrapper);
+		});
+
+		// Hide form status
+		const statusWrap = form.querySelector('.fstatus');
+		if (statusWrap) {
+			statusWrap.hidden = true;
+		}
+	}
+
+	clearFieldValidation(fieldWrapper) {
+		if (!fieldWrapper) return;
+
+		const input = fieldWrapper.querySelector('input, textarea, select');
+		const validationMessage = fieldWrapper.querySelector('.validation-message');
+		const validationIcons = fieldWrapper.querySelectorAll('.validation-icon');
+
+		// Remove classes
+		fieldWrapper.classList.remove('has-error', 'has-success');
+		if (input) {
+			input.classList.remove('error');
+			input.removeAttribute('aria-invalid');
+		}
+
+		// Hide icons and messages
+		validationIcons.forEach(icon => icon.hidden = true);
+		if (validationMessage) {
+			validationMessage.hidden = true;
+			validationMessage.textContent = '';
 		}
 	}
 
 	handleInput(e) {
-		if (e.target.id === 'referral-code') {
+		if (e.target.id === 'referral_code' || e.target.name === 'referral_code') {
 			e.target.value = e.target.value.toUpperCase();
 		}
+		// Clear field error when user types
+		const fieldWrapper = e.target.closest('.field');
+		if (fieldWrapper && fieldWrapper.classList.contains('has-error')) {
+			this.clearFieldValidation(fieldWrapper);
+		}
 	}
 
-	// ==========================================
-	// SHARE WIDGET (Logged-In Users)
-	// ==========================================
+	/**
+	 * Handle code verification
+	 */
+	async handleCheckCode(e) {
+		e.preventDefault();
 
-	initShareWidget() {
-		this.initCopyButton();
-		this.loadStats();
+		const form = e.target.closest('form');
+		const codeInput = form.querySelector('[name="referral_code"]');
+		const statusDiv = form.querySelector('.code-status');
+
+		if (!codeInput || !statusDiv) return;
+
+		const code = codeInput.value.trim();
+
+		if (!code) {
+			this.showCodeStatus(statusDiv, 'Please enter a code', 'error');
+			return;
+		}
+
+		// Show loading
+		statusDiv.hidden = false;
+		statusDiv.className = 'code-status loading';
+		statusDiv.innerHTML = '<span class="spinner"></span> Checking...';
+
+		try {
+			const result = await this.validateCodeOnly(code);
+
+			if (result.success) {
+				this.showCodeStatus(
+					statusDiv,
+					`✓ Valid! Referred by ${result.referrer_name}`,
+					'success'
+				);
+			} else {
+				this.showCodeStatus(statusDiv, result.message || 'Invalid code', 'error');
+			}
+		} catch (error) {
+			console.error('Error checking code:', error);
+			this.showCodeStatus(statusDiv, 'Error checking code', 'error');
+		}
+	}
+
+	/**
+	 * Show code verification status
+	 */
+	showCodeStatus(statusDiv, message, type) {
+		statusDiv.hidden = false;
+		statusDiv.className = `code-status ${type}`;
+		statusDiv.textContent = message;
+
+		if (type === 'error') {
+			setTimeout(() => {
+				statusDiv.hidden = true;
+			}, 5000);
+		}
 	}
 
 	/**
 	 * 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();
+		const refName = this.getUrlParameter('rname');
+		const refEmail = this.getUrlParameter('remail');
+		const seeReferral = this.getUrlParameter('seeReferral');
+
+		if (!refCode && !seeReferral) {
 			return;
 		}
 
-		const codeInput = this.container.querySelector('#referral-code-input');
+		// If logged in user just wants to see referral popup
+		if (seeReferral && !refCode) {
+			this.popup.openPopup();
+			this.removeUrlParameter('seeReferral');
+			return;
+		}
+
+		const codeInput = this.container.querySelector('[name="referral_code"]');
 		if (!codeInput) return;
 
 		// Convert to uppercase
@@ -122,25 +516,44 @@
 
 		// Pre-fill the code input
 		codeInput.value = code;
-		codeInput.readOnly = true; // Make it read-only since it came from link
+		codeInput.readOnly = true;
 
-		this.popup.togglePopup();
+		// If we have token data, prefill name and email too
+		if (refName || refEmail) {
+			const nameInput = this.container.querySelector('[name="referral_name"]');
+			if (nameInput) {
+				nameInput.value = refName;
+			}
 
-		// Validate the code immediately to show referrer info
+			const emailInput = this.container.querySelector('[name="referral_email"]');
+			if (emailInput) {
+				emailInput.value = refEmail;
+			}
+		}
+
+		// Open the sidebar popup
+		this.popup.openPopup();
+
+		// Validate the code immediately
 		try {
 			const referrer = await this.validateCodeOnly(code);
 
 			if (referrer.success) {
-				// Show referrer info banner
-				this.showReferrerBanner(referrer.referrer_name, code);
+				const statusDiv = codeInput.closest('form').querySelector('.code-status');
+				if (statusDiv) {
+					this.showCodeStatus(
+						statusDiv,
+						`✓ ${referrer.referrer_name} invited you!`,
+						'success'
+					);
+				}
 
-				// Focus on name input (first empty field)
-				const nameInput = this.container.querySelector('#referral-name');
-				if (nameInput) {
+				// Focus on name input if not prefilled
+				const nameInput = this.container.querySelector('[name="referral_name"]');
+				if (nameInput && !nameInput.value) {
 					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');
 			}
@@ -149,21 +562,17 @@
 			codeInput.readOnly = false;
 		}
 
-		// Clean up URL (remove ?ref parameter)
+		// Clean up URL
 		this.removeUrlParameter('ref');
+		this.removeUrlParameter('rname');
+		this.removeUrlParameter('remail');
 	}
 
-	/**
-	 * 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);
@@ -171,13 +580,14 @@
 	}
 
 	/**
-	 * Validate code without registering (just check if valid)
+	 * Validate code without registering
 	 */
 	async validateCodeOnly(code) {
-		const response = await fetch(`${jvbSettings.api}/referrals/check-code`, {
+		const response = await fetch(`${jvbSettings.api}referrals/code`, {
 			method: 'POST',
 			headers: {
-				'Content-Type': 'application/json'
+				'Content-Type': 'application/json',
+				'X-WP-Nonce': window.auth.getNonce()
 			},
 			body: JSON.stringify({ code: code })
 		});
@@ -186,199 +596,142 @@
 	}
 
 	/**
-	 * Show banner with referrer info
+	 * Render recent referrals list
 	 */
-	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') {
+	renderRecentReferrals() {
+		let container = this.ui.recentList;
+		let referrals = Array.from(this.listStore.data.values());
+		if (!referrals || referrals.length === 0) {
+			container.innerHTML = '<p class="no-referrals">Share your code to get started!</p>';
 			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);
-		}
+		container.innerHTML = referrals.map(ref => `
+			<div class="referral-item">
+				<div class="referral-info">
+					<strong>${window.escapeHtml(ref.referee_name)}</strong>
+					<span class="status-badge">${ref.referral_status}</span>
+				</div>
+				<div class="referral-date">${window.formatTimeAgo(ref.referred_at)}</div>
+			</div>
+		`).join('');
 	}
 
 	/**
 	 * Handle form submission
 	 */
 	async handleFormSubmit(event) {
-		window.debouncer.cancel('check-referral');
 		event.preventDefault();
 
 		const form = event.target;
-
-		// Get form data
 		const formData = new FormData(form);
 
-		let data = {};
-
-		// Disable form
+		// Clear any existing errors
+		this.clearFormErrors(form);
 		this.setFormLoading(true, form);
 
 		try {
+			let result = { success: false, message: '' };
 
-			let result = {success: false, message: ''};
 			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);
-			}
+				// Registration with referral code - goes to LoginRoutes
+				let data = {
+					name: formData.get('referral_name'),
+					email: formData.get('referral_email'),
+					referral_code: formData.get('referral_code')
+				};
 
+				if (formData.get('cf-turnstile-response')) {
+					data['cf-turnstile-response'] = formData.get('cf-turnstile-response');
+				}
+
+				if (!data.name || !data.email || !data.referral_code) {
+					result.message = 'Please fill in all fields';
+				} else {
+					result = await this.makeRequest('auth/register', data); // UPDATED endpoint
+				}
+			} else if (form.id === 'login-form') {
+				let data = {
+					type: 'login',
+					email: formData.get('login_email'),
+					context: {
+						redirect_to: window.location.href + '?seeReferral=1'
+					}
+				};
+				if (formData.get('cf-turnstile-response')) {
+					data['cf-turnstile-response'] = formData.get('cf-turnstile-response');
+				}
+				if (!data.email) {
+					result.message = 'Please fill in your email';
+				} else {
+					result = await this.makeRequest('magic', data);
+				}
+			}
 
 			if (result.success) {
-				this.handleSuccess(result);
+				this.handleSuccess(form, result);
 			} else {
-				this.showMessage(result.message || 'Something went wrong. Please try again.', 'error');
-				this.setFormLoading(false, form);
+				this.handleError(form, result);
 			}
 		} catch (error) {
-			console.error('Error registering:', error);
-			this.showMessage('Something went wrong. Please try again.', 'error');
-			this.setFormLoading(false, form);
+			console.error('Error submitting form:', error);
+			this.showFormMessage(form, 'Something went wrong. Please try again.', 'error');
 		} finally {
 			this.setFormLoading(false, form);
 		}
 	}
 
 	async makeRequest(endpoint, data) {
-		if (![
+		const validEndpoints = [
 			'magic',
-			'referrals/register',
-			'referrals/check-code'
-		].includes(endpoint)) {
-			return {success:false, message: 'Something went wrong (Invalid endpoint).'}
+			'auth/register'
+		];
+
+		if (!validEndpoints.includes(endpoint)) {
+			return { success: false, message: '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,
+				'X-WP-Nonce': window.auth.getNonce(),
 			},
 			body: JSON.stringify(data)
 		});
 
+		// Add error handling to see the actual response
+		if (!response.ok) {
+			const errorText = await response.text();
+			console.error('Error response:', response.status, errorText);
+			try {
+				return JSON.parse(errorText);
+			} catch {
+				return { success: false, message: 'Server error' };
+			}
+		}
+
 		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;
+	handleSuccess(form, result) {
+		// Hide form
+		form.style.display = 'none';
 
 		// Show success message
-		successState.hidden = false;
+		const successDiv = form.nextElementSibling;
+		if (successDiv && successDiv.classList.contains('success-content')) {
+			successDiv.hidden = false;
 
-		// Scroll to message
-		successState.scrollIntoView({
-			behavior: 'smooth',
-			block: 'center'
-		});
+			// Scroll to message
+			successDiv.scrollIntoView({
+				behavior: 'smooth',
+				block: 'center'
+			});
+		}
 
 		// Fire custom event
 		this.dispatchEvent('emailSent', {
@@ -387,38 +740,36 @@
 	}
 
 	/**
-	 * Set form loading state
+	 * Show message in form status area
 	 */
-	setFormLoading(loading, form) {
-		const inputs = form.querySelectorAll('input');
+	showFormMessage(form, text, type = 'error') {
+		const status = form.querySelector('.status');
+		if (!status) return;
 
-		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 = '';
+		const message = status.querySelector('.message');
+		if (message) {
+			message.textContent = text;
+		}
+
+		status.hidden = false;
+		status.className = `status ${type}`;
+
+		if (type === 'error') {
+			setTimeout(() => {
+				status.hidden = true;
+			}, 5000);
 		}
 	}
 
 	/**
-	 * Show message
+	 * Set form loading state
 	 */
-	showMessage(text, type = 'success') {
-		const messageDiv = this.container.querySelector('#referral-message');
-		if (!messageDiv) return;
+	setFormLoading(loading, form) {
+		const inputs = form.querySelectorAll('input, button, textarea, select');
+		inputs.forEach(input => input.disabled = loading);
 
-		messageDiv.textContent = text;
-		messageDiv.className = 'message ' + type;
-		messageDiv.style.display = 'block';
-
-		if (type === 'error') {
-			setTimeout(() => {
-				messageDiv.style.display = 'none';
-			}, 5000);
+		if (loading) {
+			this.showFormStatus(form, 'saving');
 		}
 	}
 
@@ -432,10 +783,14 @@
 		});
 		this.container.dispatchEvent(event);
 	}
+
+
 }
 
-
-document.addEventListener('DOMContentLoaded', () => {
-	window.jvbReferral = new Referral();
+document.addEventListener('DOMContentLoaded', async function () {
+	window.auth.subscribe((event) => {
+		if (event === 'auth-loaded') {
+			window.jvbReferral = new Referral();
+		}
+	});
 });
-

--
Gitblit v1.10.0