From ac444cba221832c012c0435fdc8339fe9f37febb Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 11 May 2026 18:35:04 +0000
Subject: [PATCH] =Some changes to the CRUD.js editing, timeline post configuration

---
 inc/managers/ReferralManager.php |  543 ++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 382 insertions(+), 161 deletions(-)

diff --git a/inc/managers/ReferralManager.php b/inc/managers/ReferralManager.php
index b3c1644..fe97fea 100644
--- a/inc/managers/ReferralManager.php
+++ b/inc/managers/ReferralManager.php
@@ -1,12 +1,10 @@
 <?php
 namespace JVBase\managers;
 
-use JVBase\managers\MagicLinkManager;
-use JVBase\integrations\Cloudflare;
-use JVBase\meta\MetaForm;
+use JVBase\meta\Form;
 use JVBase\ui\CRUDSkeleton;
 use JVBase\ui\Tabs;
-use JVBase\utility\Features;
+use JVBase\base\Site;
 use WP_User;
 use WP_Error;
 
@@ -31,6 +29,12 @@
 	protected ?int $referralPage = null;
 	protected string $rewards_table;
 
+	protected CustomTable $referrals;
+	protected CustomTable $codes;
+	protected CustomTable $janeClients;
+	protected CustomTable $rewards;
+	protected CustomTable $treatments;
+
 	// Default reward settings
 	protected array $default_settings = [
 		'referrer_reward_applies_to' => 'per_user',  // 'per_user' or 'flat_total'
@@ -39,20 +43,27 @@
 		'referee_reward_type' => 'percentage',  // 'percentage' or 'fixed'
 		'referee_reward_amount' => 20,  // 20% or $20
 		'referee_reward_applies_to' => 'first_order',  // 'first_order' or 'all_orders'
-		'referral_role'	=> BASE.'client'
 	];
 
-	protected string $role = BASE.'client';
+	protected string $role;
 
 	protected array $settings;
 
 	public function __construct()
 	{
+		$this->defineTables();
+		$this->role = Site::getDefaultReferralRole();
+		$this->default_settings['referral_role'] = $this->role;
 		global $wpdb;
 		$this->wpdb = $wpdb;
 		$this->cache = Cache::for('referrals', WEEK_IN_SECONDS);
 		$this->requestCache = Cache::for('referral_requests', WEEK_IN_SECONDS)->connect('referrals', true);
 		$this->statsCache = Cache::for('referral_stats', WEEK_IN_SECONDS)->connect('referrals', true);
+		if (JVB_TESTING) {
+			$this->cache->flush();
+			$this->requestCache->flush();
+			$this->statsCache->flush();
+		}
 
 		$this->referrals_table = $wpdb->prefix . BASE . 'referrals';
 		$this->rewards_table = $wpdb->prefix . BASE . 'referral_rewards';
@@ -63,7 +74,7 @@
 
 		add_action('jvbUserRegistered', [$this, 'processRegistrationToken'], 10, 3);
 		add_action('jvb_add_token_inputs', [$this, 'addLoginInputs'], 10, 1);
-		add_action('jvbUserRegistered', [$this, 'processReferral'], 10, 1);
+		add_action('user_register', [$this, 'processReferral'], 10, 1);
 
 		// Add meta boxes for admin to manage referrals
 		add_action('show_user_profile', [$this, 'displayUserReferralInfo']);
@@ -97,6 +108,182 @@
 		add_filter('jvb_admin_page_submission', [$this, 'handleAdminSubmission'], 10, 3);
 	}
 
+	protected function defineTables():void
+	{
+		$this->defineReferralsTable();
+		$this->defineCodeTable();
+		$this->defineJaneClientsTable();
+		$this->defineRewardsTable();
+		$this->defineTreatmentsTable();
+	}
+		protected function defineReferralsTable():void
+		{
+			$table = CustomTable::for('referrals');
+
+			$table->setColumns([
+				'id'		=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'from_user'	=> "{$table->getUserIDType()} NOT NULL",
+				'to_user'	=> "{$table->getUserIDType()} NOT NULL",
+				'to_name'	=> 'varchar(255) NOT NULL',
+				'to_email'	=> 'varchar(255) NOT NULL',
+				'to_phone'	=> 'varchar(50) NOT NULL',
+				'referral_code'=> 'varchar(50) NOT NULL',
+				'status'	=> "ENUM('pending', 'consulted', 'treated', 'cancelled') DEFAULT 'pending'",
+				'created_at'=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+				'consulted_at'=> 'datetime DEFAULT NULL',
+				'treated_at'=> 'datetime DEFAULT NULL',
+				'treatment_count' => 'int DEFAULT 0',
+				'notes'		=> 'text DEFAULT NULL',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => 'id'],
+				['key' => 'UNIQUE', 'value' => '(`to_user`)'],
+				'from_user (`from_user`)',
+				'status (`status`)',
+				'code (`referral_code`)',
+				'date (`created_at`)',
+				'consult (`consulted_at`)'
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}referral_from_user_fk` FOREIGN KEY (`from_user`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE",
+				"CONSTRAINT `{$base}referral_to_user_fk` FOREIGN KEY (`to_user`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
+			]);
+			$table->defineTable();
+			$this->referrals = $table;
+		}
+
+		protected function defineCodeTable():void
+		{
+			$table = CustomTable::for('referrals_codes');
+
+			$table->setColumns([
+				'id'		=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'user_id'	=> "{$table->getUserIDType()} NOT NULL",
+				'code'		=> 'varchar(50) NOT NULL',
+				'created_at'=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				['key' => 'UNIQUE', 'value' => '(`code`)'],
+				'user (`user_id`)',
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}referral_code_user_fk` FOREIGN KEY (`user_id`)
+					REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE",
+			]);
+			$table->defineTable();
+			$this->codes = $table;
+		}
+		protected function defineJaneClientsTable():void
+		{
+			$table = CustomTable::for('referrals_jane_clients');
+
+			$table->setColumns([
+				'id'			=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'patient_guid'	=> 'varchar(50) NOT NULL',
+				'user_id'		=> "{$table->getUserIDType()} NOT NULL",
+				'first_name'	=> 'varchar(100) NOT NULL',
+				'last_name'		=> 'varchar(100) NOT NULL',
+				'email'			=> 'varchar(255) NOT NULL',
+				'imported_at'	=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+				'updated_at'	=> 'datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				['key' => 'UNIQUE', 'value' => '(`patient_guid`)'],
+				'user (`user_id`)',
+				'email (`email`)',
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}jane_clients_user` FOREIGN KEY (`user_id`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE",
+			]);
+			$table->defineTable();
+			$this->janeClients = $table;
+		}
+		protected function defineRewardsTable():void
+		{
+			$table = CustomTable::for('referrals_rewards');
+
+			$table->setColumns([
+				'id'				=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'referral_id'		=> 'bigint(20) unsigned NOT NULL',
+				'user_id'			=> "{$table->getUserIDType()} NOT NULL",
+				'reward_type'		=> "ENUM('referrer', 'referee') NOT NULL",
+				'amount'			=> 'decimal(10,2) NOT NULL',
+				'reward_calculation'=> "ENUM('percentage', 'fixed')",
+				'status'			=> "ENUM('available', 'redeemed', 'expired', 'cancelled') DEFAULT 'available'",
+				'created_at'		=> 'datetime NOT NULL DEFAULT CURRENT_TIMESTAMP',
+				'updated_at'		=> 'datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
+				'redeemed_at'		=> 'datetime DEFAULT NULL',
+				'expires_at'		=> 'datetime DEFAULT NULL',
+				'notes'				=> 'text DEFAULT NULL',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				'referral (`referral_id`)',
+				'user (`user_id`)',
+				'status (`status`)',
+				'type (`reward_type`)'
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}reward_referral` FOREIGN KEY (`referral_id`)
+				REFERENCES `{$this->referrals->getFullTableName()}` (`id`) ON DELETE CASCADE",
+				"CONSTRAINT `{$base}reward_user` FOREIGN KEY (`user_id`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
+			]);
+			$table->defineTable();
+			$this->rewards = $table;
+		}
+		protected function defineTreatmentsTable():void
+		{
+			$table = CustomTable::for('referral_treatments');
+
+			$table->setColumns([
+				'id'			=> 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+				'referral_id'	=> 'bigint(20) unsigned NOT NULL',
+				'user_id'		=> "{$table->getUserIDType()} NOT NULL",
+				'treatment_type'=> 'varchar(100) NOT NULL',		//Tier 1-6, Brows, etc
+				'treatment_date'=> 'datetime NOT NULL',
+				'invoice_number'=> 'varchar(50) DEFAULT NULL',
+				'amount'		=> 'decimal(10,2) DEFAULT NULL',
+				'status'		=> "ENUM('completed', 'no_show', 'cancelled') DEFAULT 'completed'",
+				'imported_at'	=> 'datetime DEFAULT CURRENT_TIMESTAMP',
+			]);
+
+			$table->setKeys([
+				['key' => 'PRIMARY', 'value' => '(`id`)'],
+				'referral (`referral_id`)',
+				'user (`user_id`)',
+				'date (`treatment_date`)',
+				'type (`treatment_type`)',
+			]);
+
+			$base = BASE;
+			$table->setConstraints([
+				"CONSTRAINT `{$base}treatment_referral` FOREIGN KEY (`referral_id`)
+				REFERENCES `{$this->referrals->getFullTableName()}` (`id`) ON DELETE CASCADE",
+				"CONSTRAINT `{$base}treatment_user` FOREIGN KEY (`user_id`)
+				REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
+			]);
+			$table->defineTable();
+			$this->treatments = $table;
+		}
+
 	public function getSettings():array
 	{
 		return $this->settings;
@@ -146,7 +333,7 @@
 			'jvb-data-store',
 		];
 
-		if (Features::hasIntegration('cloudflare') && JVB()->connect('cloudflare')->isSetUp()) {
+		if (Site::hasIntegration('cloudflare') && JVB()->connect('cloudflare')->isSetUp()) {
 			JVB()->connect('cloudflare')->enqueueTurnstileScripts();
 		}
 		if (is_singular(BASE.'dash')) {
@@ -186,6 +373,26 @@
 		}
 	}
 
+	public function createCode(int $user_id, string $code):string|false
+	{
+		$code = sanitize_title($code);
+		$existing = $this->codes->get(['code' => $code]);
+		if ($existing) {
+			if ($existing['user_id'] !== $user_id) {
+				return false;
+			}
+			return $code;
+		}
+		$success = $this->codes->insert([
+			'user_id' 	=> $user_id,
+			'code'		=> $code
+		]);
+		if ($success) {
+			return $code;
+		}
+		return false;
+	}
+
 	/**
 	 * Generate or get existing referral code for a user
 	 *
@@ -193,32 +400,35 @@
 	 * @param string|null $custom_code Optional custom code
 	 * @return string|WP_Error
 	 */
-	public function getUserReferralCode(int $user_id, ?string $custom_code = null)
+	public function getUserReferralCode(int $user_id, ?string $custom_code = null):string|false
 	{
 		$user = get_userdata($user_id);
 		if (!$user) {
-			return new WP_Error('invalid_user', 'User not found');
+			return false;
 		}
 
-		// Check if user already has a code
-		$existing_code = get_user_meta($user_id, BASE . 'referral_code', true);
-
-		if ($existing_code && !$custom_code) {
-			return $existing_code;
+		$existing = $this->codes->pluck('code', ['user_id' => $user_id],'created_at', 'DESC');
+		if (!empty($existing) && !$custom_code) {
+			return $existing[0];
+		}
+		if ($custom_code && !empty($existing) && !in_array($custom_code, $existing)) {
+			$test = $this->createCode($user_id, $custom_code);
+			if ($test) {
+				return $this->codes->pluck('code', ['user_id' => $user_id], 'created_at', 'DESC')[0];
+			} else {
+				return $existing[0];
+			}
 		}
 
 		// Generate new code if custom provided or none exists
-		$code = $custom_code ?: $this->generateReferralCode($user);
+		$code = $this->generateReferralCode($user);
 
-		// Validate uniqueness
-		if ($this->isCodeTaken($code, $user_id)) {
-			return new WP_Error('code_taken', 'This referral code is already in use');
+		$success = $this->createCode($user_id, $code);
+		if ($success) {
+			return $this->codes->pluck('code', ['user_id' => $user_id], 'created_at', 'DESC')[0];
 		}
 
-		// Save the code
-		update_user_meta($user_id, BASE . 'referral_code', $code);
-
-		return $code;
+		return false;
 	}
 
 	/**
@@ -251,24 +461,11 @@
 	 * Check if a referral code is already taken
 	 *
 	 * @param string $code
-	 * @param int|null $exclude_user_id
 	 * @return bool
 	 */
-	protected function isCodeTaken(string $code, ?int $exclude_user_id = null): bool
+	protected function isCodeTaken(string $code): bool
 	{
-		$args = [
-			'meta_key' => BASE . 'referral_code',
-			'meta_value' => $code,
-			'fields' => 'ID',
-			'number' => 1
-		];
-
-		if ($exclude_user_id) {
-			$args['exclude'] = [$exclude_user_id];
-		}
-
-		$users = get_users($args);
-		return !empty($users);
+		return (bool) $this->codes->get(['code' => $code]);
 	}
 
 	public function processRegistrationToken(int $user_id, string $email, array $data): void
@@ -295,60 +492,58 @@
 	 * Track a new referral when user registers
 	 *
 	 * @param int $user_id
+	 * @param array $userData
 	 * @return bool;
 	 */
-	public function processReferral(int $user_id): bool
+	public function processReferral(int $user_id, array $userData): bool
 	{
-		// Try to get code from user meta first (set during registration)
-		$referral_code = get_user_meta($user_id, BASE . 'pending_referral_code', true);
+		$referral = $this->referrals->get(['to_user' => $user_id]);
 
-		if (empty($referral_code)) {
+		if (empty($referral)) {
+			$referral = $this->referrals->get(['to_email' => $userData['email']]);
+		}
+		if (empty($referral)) {
 			// Check session/cookie if not in meta
 			if (session_status() === PHP_SESSION_NONE) {
 				session_start();
 			}
 			$referral_code = $_SESSION[BASE . 'referral_code'] ?? $_COOKIE[BASE . 'referral_code'] ?? '';
+			if (!empty ($referral_code)) {
+				$referral = [
+					'to_user'	=> $user_id,
+					'referral_code'	=> $referral_code,
+					'to_email'		=> $userData['user_email']
+				];
+			}
 		}
 
-		if (empty($referral_code)) {
+		if (empty($referral)) {
 			return false; // No referral code - regular registration
 		}
 
 		// Find the referrer
-		$referrer = $this->getUserByReferralCode($referral_code);
-		if (!$referrer) {
-			delete_user_meta($user_id, BASE . 'pending_referral_code');
+		$referrer = $this->codes->pluck('user_id', ['code' => $referral['referral_code']]);
+		if (empty($referrer)) {
+			//This should not happen, but whatever
+			return false;
+		}
+		$referrer = $referrer[0];
+		$record = $this->referrals->findOrCreate([
+			'to_user'	=> $user_id,
+			'referral_code'	=> $referral['referral_code'],
+		], [
+			'from_user'		=> $referrer,
+			'to_email'		=> $referral['to_email'],
+			'to_name'		=> $userData['first_name'],
+//			'to_phone'		=>
+			'status'		=> 'pending'
+		]);
+
+		if (!$record) {
+			error_log('[ReferralManager]::processReferral Could not update record for user: '.print_r($referral, true));
 			return false;
 		}
 
-		$user = get_userdata($user_id);
-
-		// Check if referral already exists for this user
-		$existing = $this->wpdb->get_row($this->wpdb->prepare(
-			"SELECT * FROM {$this->referrals_table}
-		WHERE referrer_id = %d AND (referee_email = %s OR referee_id = %d)",
-			$referrer->ID,
-			$user->user_email,
-			$user_id
-		));
-
-		if (!$existing) {
-			// Create new referral record - referred_at captures registration time
-			$this->wpdb->insert(
-				$this->referrals_table,
-				[
-					'referrer_id' => $referrer->ID,
-					'referee_id' => $user_id,
-					'referee_name' => $user->display_name,
-					'referee_email' => $user->user_email,
-					'referee_phone' => get_user_meta($user_id, BASE . 'phone', true) ?: '',
-					'referral_code' => $referral_code,
-					'status' => 'pending', // pending first treatment
-					'referred_at' => current_time('mysql') // When they registered
-				],
-				['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s']
-			);
-		}
 
 		// Clean up temp data
 		delete_user_meta($user_id, BASE . 'pending_referral_code');
@@ -363,10 +558,10 @@
 		$this->cache->flush();
 
 		// Fire action for tracking
-		do_action('jvb_referral_processed', $user_id, $referrer->ID, $referral_code);
+		do_action('jvb_referral_processed', $user_id, $referrer->ID, $referral['referral_code']);
 
 		// Send notification to referrer
-		$this->sendReferrerNotification($referrer->ID, $user->display_name);
+		$this->sendReferrerNotification($referrer->ID, $userData['display_name']);
 		return true;
 	}
 
@@ -1051,7 +1246,6 @@
 		JVB()->connect('cloudflare')->renderTurnstile();
 		$turnstile = ob_get_clean();
 
-		$meta = new MetaForm();
 		$reward_text = $this->getRewardText(true);
 
 		// Pre-fill code if from referral link
@@ -1062,47 +1256,30 @@
 			$referrer_name = $referrer ? strtok($referrer->display_name, ' ') : '';
 		}
 
-		$codeForm = '<div class="referral-reward-banner">
-		'.jvbIcon('confetti').'
-		<h4>Get ' . esc_html($reward_text) . '!</h4>
-		' . ($referrer_name ? '<p>' . esc_html($referrer_name) . ' invited you to join us</p>' : '') . '
-	</div>
-	<form id="referral-code-form">
-				'.jvbFormStatus(). '
-    <input type="hidden" name="user_select" value="' . esc_attr(get_option(BASE.'referral_role','client')) . '">
-    ' .$meta->return('referral_name', null, [
-				'required'	=> true,
-				'type'		=> 'text',
-				'label'		=> 'Your Name',
-				'placeholder'=> 'Mister Meeseeks',
-				'autocomplete'=>'name'
-			]).
-			$meta->return('referral_email', null, [
-				'required'	=> true,
-				'type'		=> 'email',
-				'label'		=> 'Your Email',
-				'placeholder'=> 'look@me.com',
-				'autocomplete'=> 'email'
-			]).
-			$meta->return('referral_code', $prefill_code, [
-				'required'	=> true,
-				'type'		=> 'text',
-				'label'		=> 'Referral Code',
-				'pattern'	=> '[A-Za-z0-9]+',
-				'maxLength'	=> 20,
-				'autocomplete'=>'off',
-				'data-referrer' => $referrer_name
-			]).'
-				'.$turnstile.'
-				<button type="button" class="button-secondary check-code-btn">
-					'.jvbIcon('check-circle', ['size' => 16]).' Verify Code
-				</button>
-				<div class="code-status" hidden></div>
-				<button type="submit">
-					Get Started
-				</button>
+		$header = sprintf(
+			'<header><h2>%sGet %s.</h2></header><h3>Have a code?</h3>%s<p>Enter your referral code to get started!</p>',
+			jvbIcon('confetti'),
+			esc_html($reward_text),
+			($referrer_name ? '<p>' . esc_html($referrer_name) . ' invited you to join us</p>' : '')
+		);
 
-				<p class="helper-text">
+		$codeForm = sprintf(
+			'<form id="referral-code-form">
+				%s
+   	 			<input type="hidden" name="user_select" value="%s">
+   	 			%s%s%s%s
+   	 			<div class="row">
+					<button type="button" class="button-secondary check-code-btn">
+						%s Verify Code
+					</button>
+					<button type="submit">
+						Get Started
+					</button>
+				</div>
+
+				<div class="code-status" hidden></div>
+
+				<p class="hint">
 					We\'ll send you a link to complete your registration.
 				</p>
 			</form>
@@ -1110,36 +1287,78 @@
 				<h3>Check Your Email!</h3>
 				<p>We\'ve sent you a magic link to complete your registration. Click the link to activate your account and claim your reward!</p>
 				<p class="hint">Can\'t find it? Check your spam folder.</p>
-			</div>';
-
-		$loginForm = '<form id="login-form">
-	'.jvbFormStatus().$meta->return('login_email', null, [
+			</div>',
+			jvbFormStatus(),
+			esc_attr(get_option(BASE.'referral_role','client')),
+			Form::render('referral_name', '', [
+				'required'	=> true,
+				'type'		=> 'text',
+				'label'		=> 'Your Name',
+				'placeholder'=> 'Mister Meeseeks',
+				'autocomplete'=>'name'
+			]),
+			Form::render('referral_email', '', [
 				'required'	=> true,
 				'type'		=> 'email',
 				'label'		=> 'Your Email',
-				'autocomplete'=>'email'
-			]).'
-	'.$turnstile.'
-	<button type="submit">Login With Magic Link</button>
-</form>
-<div class="success-content" hidden>
-	<h3>Check Your Email!</h3>
-	<p>We\'ve sent you a magic link to log in - no password required! Click the link in your email to log in.</p>
-	<p class="hint">Can\'t find it? Check your spam folder.</p>
-</div>';
+				'placeholder'=> 'look@me.com',
+				'autocomplete'=> 'email'
+			]),
+			Form::render('referral_code', $prefill_code, [
+				'required'	=> true,
+				'type'		=> 'text',
+				'label'		=> 'Referral Code',
+				'pattern'	=> '[A-Za-z0-9]+',
+				'maxLength'	=> 20,
+				'autocomplete'=>'off',
+				'data-referrer' => $referrer_name
+			]),
+			$turnstile,
+			jvbIcon('check-circle')
+		);
 
-		$footer = '<div class="referral-footer">
-		<a href="' . wp_login_url() . '" class="text-link">Prefer to use a password?</a>
-	</div>';
+		$loginHeader = sprintf(
+			'<header><h2>%sLogin</h2></header><p>Already have an account?<br>Log in to see your rewards!</p>',
+			jvbIcon('sign-in')
+		);
+		$loginForm = sprintf(
+			'<form id="login-form">%s%s%s
+			<button type="submit">%sLogin With Magic Link</button>
+		</form>
+		<div class="success-content" hidden>
+			<h3>Check Your Email!</h3>
+			<p>We\'ve sent you a magic link to log in - no password required! Click the link in your email to log in.</p>
+			<p class="hint">Can\'t find it? Check your spam folder.</p>
+		</div>',
+			jvbFormStatus(),
+		Form::render('login_email', null, [
+			'required'	=> true,
+			'type'		=> 'email',
+			'label'		=> 'Your Email',
+			'autocomplete'=>'email'
+		]),
+		$turnstile,
+			jvbIcon('magic-wand')
+		);
+
+
+		$footer = sprintf(
+			'<p class="hint">
+			<a href="%s" class="text-link">Prefer to use a password?</a>
+	</p>',
+			wp_login_url()
+		);
 		$tabs = [
 			'enterCode' => [
 				'title'	=> 'Have a Code?',
 				'description'	=> [
 					'Enter your referral code to get started'
 				],
+				'header' => $header,
 				'content'	=> $codeForm
 			],
 			'login'	=> [
+				'header'	=> $loginHeader,
 				'title'		=> 'Login',
 				'description'	=> [
 					'Already have an account? Log in to see your rewards'
@@ -1205,13 +1424,14 @@
 	public function getLoggedInReferral(int $user_id): string
 	{
 		$referral_code = $this->getUserReferralCode($user_id);
-		if (is_wp_error($referral_code)) {
+		if (!$referral_code) {
 			return '';
 		}
 
 		$share_url = $this->getShareURL($referral_code);
 		ob_start();
 		?>
+		<div class="wrap">
 		<header>
 			<h3>Share the ♡</h3>
 			<p>Invite friends. Earn rewards.</p>
@@ -1219,7 +1439,7 @@
 
 		<?php $this->getShareButtons($user_id); ?>
 
-		<div class="copy-section">
+		<section class="copy">
 			<h4>Your Referral Link</h4>
 			<div class="copy-group row btw nowrap">
 				<code id="referral-link" class="copy-target"><?= esc_url($share_url) ?></code>
@@ -1241,39 +1461,39 @@
 			</div>
 			<p class="hint">Manually copy and paste the code</p>
 
-		</div>
+		</section>
 
-		<div class="recent-referrals-section">
+		<section class="recent-referrals">
 			<h4>Recent Referrals</h4>
 			<div class="recent-referrals-list" data-user-id="<?= $user_id ?>">
 				<div class="loading">Loading...</div>
 			</div>
-		</div>
+		</section>
 
-		<div class="stats-summary">
-			<div class="stat-row">
+		<section class="stats-summary">
+			<div class="row btw">
 				<span class="stat-label">Total Referrals</span>
 				<span class="stat-value" data-stat="total">-</span>
 			</div>
-			<div class="stat-row">
+			<div class="row btw">
 				<span class="stat-label">Successful</span>
 				<span class="stat-value" data-stat="treated">-</span>
 			</div>
-			<div class="stat-row">
+			<div class="row btw">
 				<span class="stat-label">Pending</span>
 				<span class="stat-value" data-stat="pending">-</span>
 			</div>
-			<div class="stat-row highlight">
+			<div class="row btw highlight">
 				<span class="stat-label">Available Rewards</span>
 				<span class="stat-value" data-stat="rewards">$0.00</span>
 			</div>
-		</div>
+		</section>
 
 		<a href="<?= get_home_url(null, '/dash/referrals')?>" class="view-dashboard-btn">
 			Dashboard <?= jvbIcon('arrow-right', ['size' => 16]); ?>
 		</a>
 		<p class="hint">Bulk-invite your friends via email - the link will pre-fill their name, email, and code!</p>
-
+		</div>
 		<?php
 		return ob_get_clean();
 	}
@@ -1304,7 +1524,7 @@
 
 		$code = $this->getUserReferralCode($user->ID);
 		$yourCode = '';
-		if (!is_wp_error($code)) {
+		if ($code) {
 			$share_url = $this->getShareURL($code);
 			$yourCode = sprintf(
 				'<div class="callout">
@@ -1366,7 +1586,7 @@
 		$referrer = get_user_by('ID', $user_id);
 		$referral_code = $this->getUserReferralCode($user_id);
 
-		if (is_wp_error($referral_code)) {
+		if ($referral_code) {
 			return $referral_code;
 		}
 
@@ -2431,10 +2651,11 @@
 
 		// Regular users get their referral dashboard
 		$user_id = get_current_user_id();
-		$referral_code = get_user_meta($user_id, BASE . 'referral_code', true);
 
+		$referral_code = $this->getUserReferralCode($user_id);
 		if (!$referral_code) {
-			$referral_code = $this->getUserReferralCode($user_id);
+			$user = get_userdata($user_id);
+			$referral_code = $this->generateReferralCode($user);
 		}
 
 		$referrals = $this->getUserReferrals($user_id, ['limit' => 20]);
@@ -2468,7 +2689,8 @@
 		<?php $this->getShareButtons($user_id); ?>
 
 		<!-- Referral Code Card -->
-		<div class="card">
+		<details open>
+			<summary>Your Code</summary>
 			<h3>Share Link</h3>
 			<div class="row btw nowrap">
 				<code id="referral-link" class="copy-target"><?= home_url('/?ref=' . $referral_code) ?></code>
@@ -2485,19 +2707,18 @@
 					<?= jvbIcon('check-circle'); ?>
 				</button>
 			</div>
-		</div>
+		</details>
 		<form class="invite">
 			<h2>Invite your Friends</h2>
 			<p>Or, if you prefer, enter your friends name(s) and email(s), and we'll send off some emails.</p>
 			<p><small>(No data is stored. Your friends will get an email from our email.)</small></p>
 			<?php
-			$meta = new MetaForm();
 			$invite = [
-				'type' => 'tag_list',
+				'type' => 'taglist',
 				'label' => 'Invite Your Friends',
 				'hint' => 'Add friends to send them a referral link',
 				'add_label' => 'Add Invite',
-				'tag_format' => '{name} ({email})', // or 'first_field', 'all_fields', 'email', etc.
+				'tag_format' => '{{name}} ({{email}})', // or 'first_field', 'all_fields', 'email', etc.
 				'fields' => [
 					'name' => [
 						'type' => 'text',
@@ -2528,14 +2749,14 @@
 					'hint'		=> 'We\'ll add your code and a link automatically.'
 				]
 			];
-			$meta->render('invite', [], $invite);
+			echo Form::render('invite', '', $invite);
 			?>
 			<details>
 				<summary class="icon icon-caret-down">Customize Message</summary>
 				<?php
 				foreach ($fields as $fieldName => $field) {
 					$value = (array_key_exists('value', $field)) ? $field['value'] : [];
-					$meta->render($fieldName, $value, $field);
+					echo Form::render($fieldName, $value, $field);
 				}
 				?>
 			</details>
@@ -2575,7 +2796,7 @@
 		$crud = new CRUDSkeleton();
 		$crud->title('Your Referrals', 'Track friends you\'ve invited and rewards earned')
 			->content('referral', 'Referral', 'Referrals')
-			->initMeta('custom', 'referral')
+//			->initMeta('custom', 'referral')
 			->setFields([
 				'referee_name' => [
 					'label' => 'Name',
@@ -2650,7 +2871,7 @@
 			update_option(BASE . 'referral_page_id', $page_id);
 
 			// Save client import role
-			$import_role = sanitize_text_field($post_data[BASE . 'referral_role'] ?? JVB_USER);
+			$import_role = sanitize_text_field($post_data[BASE . 'referral_role'] ?? Site::getDefaultReferralRole());
 			update_option(BASE . 'referral_role', $import_role);
 
 			// Save reward settings
@@ -2703,7 +2924,7 @@
 	public function getShareButtons(int $user_id):void
 	{
 		$referral_code = $this->getUserReferralCode($user_id);
-		if (is_wp_error($referral_code)) {
+		if (!$referral_code) {
 			return;
 		}
 
@@ -2719,7 +2940,7 @@
 		?>
 		<nav class="share">
 			<h4>Quick Share</h4>
-			<ul class="share-buttons-grid">
+			<ul>
 				<a href="mailto:?subject=<?php echo urlencode('Check out ' . get_bloginfo('name')); ?>&body=<?php echo urlencode($share_message . ' ' . $share_url); ?>"
 				   class="button" title="Email">
 					<?php echo jvbIcon('envelope'); ?>

--
Gitblit v1.10.0