From 747d741293e064a979d7bf6c143ef969ea6d7629 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 24 May 2026 20:49:44 +0000
Subject: [PATCH] =GMBReview block minor tweaks. Refactored ReferralManager.php and ReferralRoutes.php to utilize the manager for all logic, and CustomTable for table interactions.

---
 inc/managers/ReferralManager.php |  687 ++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 386 insertions(+), 301 deletions(-)

diff --git a/inc/managers/ReferralManager.php b/inc/managers/ReferralManager.php
index c521c15..10ffbaf 100644
--- a/inc/managers/ReferralManager.php
+++ b/inc/managers/ReferralManager.php
@@ -20,7 +20,6 @@
  */
 class ReferralManager
 {
-	protected $wpdb;
 	protected MagicLinkManager $magic_link;
 	protected Cache $cache;
 	protected Cache $requestCache;
@@ -37,12 +36,12 @@
 
 	// Default reward settings
 	protected array $default_settings = [
-		'referrer_reward_applies_to' => 'per_user',  // 'per_user' or 'flat_total'
-		'referrer_reward_amount' => 25.00,
-		'referrer_reward_type'	=> 'fixed',
-		'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'
+		'from_user_reward_applies_to' => 'per_user',  // 'per_user' or 'flat_total'
+		'from_user_reward_amount' => 25.00,
+		'from_user_reward_type'	=> 'fixed',
+		'to_user_reward_type' => 'percentage',  // 'percentage' or 'fixed'
+		'to_user_reward_amount' => 20,  // 20% or $20
+		'to_user_reward_applies_to' => 'first_order',  // 'first_order' or 'all_orders'
 	];
 
 	protected string $role;
@@ -54,8 +53,7 @@
 		$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);
@@ -65,9 +63,6 @@
 			$this->statsCache->flush();
 		}
 
-		$this->referrals_table = $wpdb->prefix . BASE . 'referrals';
-		$this->rewards_table = $wpdb->prefix . BASE . 'referral_rewards';
-
 		$this->referralPage = $this->getReferralPageId();
 		$this->settings = $this->getRewardSettings();
 
@@ -220,7 +215,7 @@
 				'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",
+				'reward_type'		=> "ENUM('from_user', 'to_user') NOT NULL",
 				'amount'			=> 'decimal(10,2) NOT NULL',
 				'reward_calculation'=> "ENUM('percentage', 'fixed')",
 				'status'			=> "ENUM('available', 'redeemed', 'expired', 'cancelled') DEFAULT 'available'",
@@ -521,18 +516,24 @@
 			return false; // No referral code - regular registration
 		}
 
-		// Find the referrer
-		$referrer = $this->codes->pluck('user_id', ['code' => $referral['referral_code']]);
-		if (empty($referrer)) {
+		// Find the from_user
+		$from_user = $this->codes->pluck('user_id', ['code' => $referral['referral_code']]);
+		if (empty($from_user)) {
 			//This should not happen, but whatever
 			return false;
 		}
-		$referrer = $referrer[0];
+
+		// Check if this email already has a referral record
+		if ($this->isEmailInvited($referral['to_email'])) {
+			return false;
+		}
+
+		$from_user = $from_user[0];
 		$record = $this->referrals->findOrCreate([
 			'to_user'	=> $user_id,
 			'referral_code'	=> $referral['referral_code'],
 		], [
-			'from_user'		=> $referrer,
+			'from_user'		=> $from_user,
 			'to_email'		=> $referral['to_email'],
 			'to_name'		=> $userData['first_name'],
 //			'to_phone'		=>
@@ -558,39 +559,38 @@
 		$this->cache->flush();
 
 		// Fire action for tracking
-		do_action('jvb_referral_processed', $user_id, $referrer->ID, $referral['referral_code']);
+		do_action('jvb_referral_processed', $user_id, $from_user->ID, $referral['referral_code']);
 
-		// Send notification to referrer
-		$this->sendReferrerNotification($referrer->ID, $userData['display_name']);
+		// Send notification to from_user
+		$this->sendReferrerNotification($from_user->ID, $userData['display_name']);
 		return true;
 	}
 
 	/**
 	 * Create a referral record in the database
 	 *
-	 * @param int $referrer_id
-	 * @param int $referee_id
+	 * @param int $from_user_id
+	 * @param int $to_user_id
 	 * @param string $code
 	 * @return int|false
 	 */
-	public function createReferral(int $referrer_id, int $referee_id, string $code)
+	public function createReferral(int $from_user_id, int $to_user_id, string $code)
 	{
-		$user = get_user_by('ID', $referee_id);
+		$user = get_user_by('ID', $to_user_id);
 
-		return $this->wpdb->insert(
-			$this->referrals_table,
+		return $this->referrals->findOrCreate([
 			[
-				'referrer_id' => $referrer_id,
-				'referee_id' => $referee_id,
-				'referee_name' => $user->display_name,
-				'referee_email' => $user->user_email,
-				'referee_phone' => get_user_meta($referee_id, BASE . 'phone', true) ?: '',
+				'to_user' => $to_user_id,
+			],
+			[
+				'from_user' => $from_user_id,
+				'to_name' => $user->display_name,
+				'to_email' => $user->user_email,
+				'to_phone' => get_user_meta($to_user_id, BASE . 'phone', true) ?: '',
 				'referral_code' => $code,
 				'status' => 'pending',
-				'referred_at' => current_time('mysql')
-			],
-			['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s']
-		);
+			]
+		]);
 	}
 
 	/**
@@ -615,17 +615,16 @@
 	}
 
 	/**
-	 * Get referral record by referee ID
+	 * Get referral record by to_user ID
 	 *
-	 * @param int $referee_id
+	 * @param int $to_user_id
 	 * @return object|null
 	 */
-	public function getReferralByReferee(int $referee_id): ?object
+	public function getReferralByReferee(int $to_user_id): ?object
 	{
-		$result = $this->wpdb->get_row($this->wpdb->prepare(
-			"SELECT * FROM {$this->referrals_table} WHERE referee_id = %d",
-			$referee_id
-		));
+		$result = $this->referrals->get([
+			'to_user'	=> $to_user_id
+		]);
 
 		return $result ?: null;
 	}
@@ -641,15 +640,14 @@
 	{
 		$status = $treated ? 'treated' : 'pending';
 
-		$result = $this->wpdb->update(
-			$this->referrals_table,
+		$result = $this->referrals->update(
 			[
-				'status' => $status,
+				'status'	=> $status,
 				'treated_at' => $treated ? current_time('mysql') : null
 			],
-			['id' => $referral_id],
-			['%s', '%s'],
-			['%d']
+			[
+				'id'	=> $referral_id
+			]
 		);
 
 		if ($result && $treated) {
@@ -661,53 +659,41 @@
 	}
 
 	/**
-	 * Create reward records for both referrer and referee
+	 * Create reward records for both from_user and to_user
 	 *
 	 * @param int $referral_id
 	 */
 	protected function createRewardRecords(int $referral_id): void
 	{
-		$referral = $this->wpdb->get_row($this->wpdb->prepare(
-			"SELECT * FROM {$this->referrals_table} WHERE id = %d",
-			$referral_id
-		));
+		$referral = $this->referrals->get(['id' => $referral_id]);
 
 		if (!$referral) {
 			return;
 		}
 
-		// Create referrer reward
-		$this->wpdb->insert(
-			$this->rewards_table,
-			[
-				'referral_id' => $referral_id,
-				'user_id' => $referral->referrer_id,
-				'reward_type' => 'referrer',
-				'amount' => $this->settings['referrer_reward_amount'],
-				'status' => 'available',
-				'created_at' => current_time('mysql')
-			],
-			['%d', '%d', '%s', '%f', '%s', '%s']
-		);
+		// Create from_user reward
+		$fromUserReward = $this->rewards->insert([
+			'referral_id'	=> $referral_id,
+			'user_id'		=> $referral->from_user,
+			'reward_type'	=> 'from_user',
+			'amount'		=> $this->settings['from_user_reward_amount'],
+			'reward_calculation' => $this->settings['from_user_reward_type']
+		]);
 
-		// Create referee reward
-		$referee_amount = $this->settings['referee_reward_type'] === 'percentage'
-			? $this->settings['referee_reward_amount']  // Store as percentage
-			: $this->settings['referee_reward_amount'];  // Store as fixed amount
 
-		$this->wpdb->insert(
-			$this->rewards_table,
-			[
-				'referral_id' => $referral_id,
-				'user_id' => $referral->referee_id,
-				'reward_type' => 'referee',
-				'amount' => $referee_amount,
-				'reward_calculation' => $this->settings['referee_reward_type'],
-				'status' => 'available',
-				'created_at' => current_time('mysql')
-			],
-			['%d', '%d', '%s', '%f', '%s', '%s', '%s']
-		);
+		// Create to_user reward
+		$to_user_amount = $this->settings['to_user_reward_type'] === 'percentage'
+			? $this->settings['to_user_reward_amount']  // Store as percentage
+			: $this->settings['to_user_reward_amount'];  // Store as fixed amount
+
+		$toUserReward = $this->rewards->insert([
+			'referral_id'	=> $referral_id,
+			'user_id'		=> $referral->to_user,
+			'reward_type'	=> 'to_user',
+			'amount'		=> $to_user_amount,
+			'reward_calculation' => $this->settings['to_user_reward_type']
+		]);
+
 	}
 
 	/**
@@ -723,47 +709,43 @@
 			'status' => 'all',
 			'limit' => 100,
 			'offset' => 0,
-			'orderby' => 'referred_at',
+			'orderby' => 'created_at',
 			'order' => 'DESC'
 		];
 
 		$args = wp_parse_args($args, $defaults);
+		$args['status'] = strtolower($args['status']);
+		$tableArgs = [
+			'where' => [
+				'from_user'	=> $user_id,
+			],
+			'limit'		=> $args['limit'],
+			'offset'	=> $args['offset'],
+			'orderby'	=> $args['orderby'],
+			'order'		=> $args['order']
+		];
+		if (in_array($args['status'], ['pending', 'consulted', 'treated', 'cancelled'])){
+			$tableArgs['status'] = $args['status'];
+		}
+		$referrals = $this->referrals->getMany($tableArgs);
 
-		return $this->requestCache->remember(
-			$this->requestCache->generateKey(array_merge(['user'=>$user_id], $args)),
-			function() use ($user_id, $args) {
-				$where = $this->wpdb->prepare("WHERE referrer_id = %d", $user_id);
-
-				if ($args['status'] !== 'all') {
-					$where .= $this->wpdb->prepare(" AND status = %s", $args['status']);
-				}
-
-				$query = "SELECT * FROM {$this->referrals_table}
-                  {$where}
-                  ORDER BY {$args['orderby']} {$args['order']}
-                  LIMIT {$args['limit']} OFFSET {$args['offset']}";
-
-				$results =  $this->wpdb->get_results($query);
-
-				return array_map(function($referral) {
-					$last_invite = get_transient('referral_last_invite_' . md5($referral->referee_email));
-					$can_resend = !$last_invite || (time() - $last_invite) > WEEK_IN_SECONDS;
-					$status = match($referral->status) {
-						'consulted' => 'Awaiting Treatment',
-						'treated'	=> 'Rewarded!',
-						default => 'Pending',
-					};
-					return [
-						'id'			=> $referral->id,
-						'referee_name'	=> $referral->referee_name,
-						'referee_email'	=> $referral->referee_email,
-						'referred_at'	=> JVB()->routes('referral')->formatTimestamp($referral->referred_at),
-						'referral_status'=> $status,
-						'can_resend'	=> $can_resend
-					];
-				}, $results);
-			}
-		);
+		return array_map(function($referral) {
+			$last_invite = get_transient('referral_last_invite_' . md5($referral->to_email));
+			$can_resend = !$last_invite || (time() - $last_invite) > WEEK_IN_SECONDS;
+			$status = match($referral->status) {
+				'consulted' => 'Awaiting Treatment',
+				'treated'	=> 'Rewarded!',
+				default => 'Pending',
+			};
+			return [
+				'id'			=> $referral->id,
+				'to_name'	=> $referral->to_name,
+				'to_email'	=> $referral->to_email,
+				'referred_at'	=> JVB()->routes('referral')->formatTimestamp($referral->referred_at),
+				'referral_status'=> $status,
+				'can_resend'	=> $can_resend
+			];
+		}, $referrals);
 
 	}
 
@@ -778,24 +760,23 @@
 		return $this->statsCache->remember(
 			$user_id,
 			function() use ($user_id) {
-				$stats = $this->wpdb->get_row($this->wpdb->prepare(
+				$stats = $this->referrals->queryResults(
 					"SELECT
-			COUNT(*) as code_used,
-			SUM(CASE WHEN status IN ('consulted', 'treated') THEN 1 ELSE 0 END) as consultations,
-			SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treatments,
-			SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending
-		FROM {$this->referrals_table}
-		WHERE referrer_id = %d",
-					$user_id
-				), ARRAY_A);
+                    COUNT(*) as code_used,
+                    SUM(CASE WHEN status IN ('consulted', 'treated') THEN 1 ELSE 0 END) as consultations,
+                    SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treatments,
+                    SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending
+                FROM {table}
+                WHERE from_user = %d",
+					[$user_id],
+					ARRAY_A
+				);
+				$stats = $stats[0] ?? [];
 
-				// Get total rewards earned (available + redeemed)
-				$rewards = $this->wpdb->get_var($this->wpdb->prepare(
-					"SELECT SUM(amount)
-		FROM {$this->rewards_table}
-		WHERE user_id = %d AND reward_type = 'referrer'",
-					$user_id
-				));
+				$rewards = $this->rewards->queryVar(
+					"SELECT SUM(amount) FROM {table} WHERE user_id = %d AND reward_type = 'from_user'",
+					[$user_id]
+				);
 
 				$stats['total_rewards'] = floatval($rewards ?? 0);
 				$stats['user_id'] = $user_id;
@@ -805,7 +786,7 @@
 	}
 
 	/**
-	 * Get top referrers for a time period
+	 * Get top from_users for a time period
 	 *
 	 * @param int $limit
 	 * @param string $period 'day'|'week'|'month'|'all'
@@ -814,44 +795,40 @@
 	public function getTopReferrers(int $limit = 10, string $period = 'all'): array
 	{
 		return $this->statsCache->remember(
-			$this->statsCache->generateKey(['limit'=>$limit, 'period' => $period]),
+			$this->statsCache->generateKey(['limit' => $limit, 'period' => $period]),
 			function() use ($limit, $period) {
 				$where = '';
-
 				if ($period !== 'all') {
-					$date_where = match($period) {
-						'day' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
-						'week' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)",
-						'month' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
+					$date_clause = match($period) {
+						'day'   => "created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
+						'week'  => "created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)",
+						'month' => "created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
 						default => "1=1"
 					};
-
-					$where = "WHERE {$date_where}";
+					$where = "WHERE {$date_clause}";
 				}
 
-				$query = "SELECT
-                    referrer_id,
+				$results = $this->referrals->queryResults(
+					"SELECT
+                    from_user,
                     COUNT(*) as referral_count,
                     SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treated_count
-                  FROM {$this->referrals_table}
-                  {$where}
-                  GROUP BY referrer_id
-                  ORDER BY referral_count DESC
-                  LIMIT {$limit}";
+                FROM {table}
+                {$where}
+                GROUP BY from_user
+                ORDER BY referral_count DESC
+                LIMIT {$limit}"
+				);
 
-				$results = $this->wpdb->get_results($query);
-
-				// Enrich with user data
 				foreach ($results as &$result) {
-					$user = get_user_by('ID', $result->referrer_id);
-					$result->user_name = $user ? $user->display_name : 'Unknown';
-					$result->user_email = $user ? $user->user_email : '';
+					$user = get_user_by('ID', $result->from_user);
+					$result->user_name  = $user ? $user->display_name : 'Unknown';
+					$result->user_email = $user ? $user->user_email   : '';
 				}
 
 				return $results;
 			}
 		);
-
 	}
 
 	/**
@@ -861,14 +838,14 @@
 	{
 		$yesterday = date('Y-m-d', strtotime('-1 day'));
 
-		$new_referrals = $this->wpdb->get_results($this->wpdb->prepare(
-			"SELECT r.*, u.display_name as referrer_name
-        FROM {$this->referrals_table} r
-        JOIN {$this->wpdb->users} u ON r.referrer_id = u.ID
-        WHERE DATE(r.referred_at) = %s
-        ORDER BY r.referred_at DESC",
-			$yesterday
-		));
+		$new_referrals = $this->referrals->queryResults(
+			"SELECT {table}.*, u.display_name as from_user_name
+        FROM {table}
+        JOIN {$this->referrals->getUserTable()} u ON {table}.from_user = u.ID
+        WHERE DATE({table}.created_at) = %s
+        ORDER BY {table}.created_at DESC",
+			[$yesterday]
+		);
 
 		if (empty($new_referrals)) {
 			return;
@@ -888,13 +865,13 @@
 		foreach ($new_referrals as $ref) {
 			$cardContent = sprintf(
 				'<p><strong>%s</strong> (%s)</p>',
-				esc_html($ref->referee_name),
-				esc_html($ref->referee_email)
+				esc_html($ref->to_name),
+				esc_html($ref->to_email)
 			);
 			$cardContent .= sprintf(
 				'<p style="font-size:13px;color:%s;">Referred by: %s | Code: %s</p>',
 				JVB()->email()->colours['dark-200'],
-				esc_html($ref->referrer_name),
+				esc_html($ref->from_name),
 				JVB()->email()->badge($ref->referral_code, 'info')
 			);
 
@@ -913,14 +890,13 @@
 	}
 
 	/**
-	 * Send weekly report with top referrers
+	 * Send weekly report with top from_users
 	 */
 	public function sendWeeklyReport(): void
 	{
-		$top_referrers = $this->getTopReferrers(10, 'week');
-		$total_referrals = $this->wpdb->get_var(
-			"SELECT COUNT(*) FROM {$this->referrals_table}
-         WHERE referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)"
+		$top_from_users = $this->getTopReferrers(10, 'week');
+		$total_referrals = $this->referrals->queryVar(
+			"SELECT COUNT(*) FROM {table} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)"
 		);
 
 		if ($total_referrals == 0) {
@@ -939,7 +915,7 @@
 
 		// Leaderboard style
 		$rank = 1;
-		foreach ($top_referrers as $referrer) {
+		foreach ($top_from_users as $from_user) {
 			$rankBadge = $rank <= 3
 				? JVB()->email()->badge('#' . $rank, $rank === 1 ? 'success' : 'info')
 				: '<span style="font-weight:600;color:' . JVB()->email()->colours['dark-200'] . ';">#' . $rank . '</span>';
@@ -947,12 +923,12 @@
 			$cardContent = sprintf(
 				'<p>%s <strong>%s</strong></p>',
 				$rankBadge,
-				esc_html($referrer->user_name)
+				esc_html($from_user->user_name)
 			);
 
 			$stats = [
-				JVB()->email()->stat($referrer->referral_count, 'Total Referrals'),
-				JVB()->email()->stat($referrer->treated_count, 'Treated')
+				JVB()->email()->stat($from_user->referral_count, 'Total Referrals'),
+				JVB()->email()->stat($from_user->treated_count, 'Treated')
 			];
 			$cardContent .= JVB()->email()->grid($stats, 2);
 
@@ -983,10 +959,10 @@
 				foreach ($referrals as $referral) {
 					$csv .= sprintf(
 						'"%s","%s","%s","%s","%s","%s","%s","%s"' . "\n",
-						$referral->referrer_name ?? 'Unknown',
-						$referral->referee_name,
-						$referral->referee_email,
-						$referral->referee_phone,
+						$referral->from_name ?? 'Unknown',
+						$referral->to_name,
+						$referral->to_email,
+						$referral->to_phone,
 						$referral->referral_code,
 						$referral->status,
 						$referral->referred_at,
@@ -1003,11 +979,11 @@
 	/**
 	 * Generate HTML email for weekly report
 	 *
-	 * @param array $top_referrers
+	 * @param array $top_from_users
 	 * @param int $total_referrals
 	 * @return string
 	 */
-	protected function generateWeeklyReportEmail(array $top_referrers, int $total_referrals): string
+	protected function generateWeeklyReportEmail(array $top_from_users, int $total_referrals): string
 	{
 		$content = sprintf(
 			'<p>This week you had <strong>%d total referral%s</strong>.</p>',
@@ -1015,20 +991,20 @@
 			$total_referrals !== 1 ? 's' : ''
 		);
 
-		$referrers = [];
+		$from_users = [];
 		$rank = 1;
-		foreach ($top_referrers as $referrer) {
-			$referrers[] = [
-				'label' => '#' . $rank++ . ' - ' . esc_html($referrer->user_name),
+		foreach ($top_from_users as $from_user) {
+			$from_users[] = [
+				'label' => '#' . $rank++ . ' - ' . esc_html($from_user->user_name),
 				'value' => sprintf(
 					'<strong>Total Referrals:</strong> %d | <strong>Treated:</strong> %d',
-					$referrer->referral_count,
-					$referrer->treated_count
+					$from_user->referral_count,
+					$from_user->treated_count
 				)
 			];
 		}
 
-		$content .= JVB()->email()->table($referrers, 'Top 10 Referrers This Week');
+		$content .= JVB()->email()->table($from_users, 'Top 10 Referrers This Week');
 
 		return $content;
 	}
@@ -1119,8 +1095,8 @@
 			<tbody>
 			<?php foreach ($referrals as $referral): ?>
 				<tr>
-					<td><?php echo esc_html($referral->referee_name); ?></td>
-					<td><?php echo esc_html($referral->referee_email); ?></td>
+					<td><?php echo esc_html($referral->to_name); ?></td>
+					<td><?php echo esc_html($referral->to_email); ?></td>
 					<td><?php echo esc_html(ucfirst($referral->status)); ?></td>
 					<td><?php echo esc_html($referral->referred_at); ?></td>
 					<td>
@@ -1250,17 +1226,17 @@
 
 		// Pre-fill code if from referral link
 		$prefill_code = $_GET['ref'] ?? '';
-		$referrer_name = '';
+		$from_user_name = '';
 		if ($prefill_code) {
-			$referrer = $this->getUserByReferralCode($prefill_code);
-			$referrer_name = $referrer ? strtok($referrer->display_name, ' ') : '';
+			$from_user = $this->getUserByReferralCode($prefill_code);
+			$from_user_name = $from_user ? strtok($from_user->display_name, ' ') : '';
 		}
 
 		$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>' : '')
+			($from_user_name ? '<p>' . esc_html($from_user_name) . ' invited you to join us</p>' : '')
 		);
 
 		$codeForm = sprintf(
@@ -1311,7 +1287,7 @@
 				'pattern'	=> '[A-Za-z0-9]+',
 				'maxLength'	=> 20,
 				'autocomplete'=>'off',
-				'data-referrer' => $referrer_name
+				'data-from-user' => $from_user_name
 			]),
 			$turnstile,
 			jvbIcon('check-circle')
@@ -1375,14 +1351,14 @@
 
 	protected function getReferralSuccessMessage(string $code): string
 	{
-		$referrer = $this->getUserByReferralCode($code);
+		$from_user = $this->getUserByReferralCode($code);
 
-		if (!$referrer) {
+		if (!$from_user) {
 			return '';
 		}
 
-		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
-		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
+		$reward_amount = $this->settings['to_user_reward_amount'] ?? 20;
+		$reward_type = $this->settings['to_user_reward_type'] ?? 'percentage';
 
 		$reward_text = $reward_type === 'percentage'
 			? $reward_amount . '% off'
@@ -1411,7 +1387,7 @@
 			</a>
 
 			<div class="referred-by">
-				Referred by <strong><?php echo esc_html($referrer->display_name); ?></strong>
+				Referred by <strong><?php echo esc_html($from_user->display_name); ?></strong>
 			</div>
 		</div>
 		<?php
@@ -1506,8 +1482,8 @@
 			return $content;
 		}
 
-		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
-		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
+		$reward_amount = $this->settings['to_user_reward_amount'] ?? 20;
+		$reward_type = $this->settings['to_user_reward_type'] ?? 'percentage';
 
 		$reward_text = $reward_type === 'percentage'
 			? $reward_amount . '% off'
@@ -1582,8 +1558,8 @@
 			return new WP_Error('user_exists', 'This person already has an account');
 		}
 
-		// Get referrer info
-		$referrer = get_user_by('ID', $user_id);
+		// Get from_user info
+		$from_user = get_user_by('ID', $user_id);
 		$referral_code = $this->getUserReferralCode($user_id);
 
 		if ($referral_code) {
@@ -1609,9 +1585,9 @@
 		], home_url('/'));
 
 		// Get reward text for email
-		$reward_text = $this->settings['referee_reward_type'] === 'percentage'
-			? "{$this->settings['referee_reward_amount']}% off"
-			: "\${$this->settings['referee_reward_amount']} off";
+		$reward_text = $this->settings['to_user_reward_type'] === 'percentage'
+			? "{$this->settings['to_user_reward_amount']}% off"
+			: "\${$this->settings['to_user_reward_amount']} off";
 
 		// Build email content
 		$email_content =
@@ -1624,7 +1600,7 @@
 			<p>Click the button below to register and claim your reward:</p>
 			%s
 			<p><small>This invitation expires in 30 days.</small></p>',
-				esc_html($referrer->display_name),
+				esc_html($from_user->display_name),
 				esc_html(get_bloginfo('name')),
 				nl2br(esc_html($message)),
 				esc_html($reward_text),
@@ -1743,10 +1719,7 @@
 		}
 
 		// Check if there's a pending referral for this email
-		$existing = $this->wpdb->get_var($this->wpdb->prepare(
-			"SELECT id FROM {$this->referrals_table} WHERE referee_email = %s",
-			$email
-		));
+		$existing = $this->referrals->pluck('id', ['to_email' => $email]);
 
 		return !empty($existing);
 	}
@@ -1818,25 +1791,24 @@
 	 */
 	public function exportReferrals(string $start_date, string $end_date): string
 	{
-		$referrals = $this->wpdb->get_results($this->wpdb->prepare(
+		$referrals = $this->referrals->queryResults(
 			"SELECT
-            r.id,
-            r.referee_name,
-            r.referee_email,
-            r.referee_phone,
-            r.referral_code,
-            r.referred_at,
-            r.status,
-            r.treated_at,
-            u.display_name as referrer_name,
-            u.user_email as referrer_email
-        FROM {$this->referrals_table} r
-        JOIN {$this->wpdb->users} u ON r.referrer_id = u.ID
-        WHERE DATE(r.referred_at) BETWEEN %s AND %s
-        ORDER BY r.referred_at DESC",
-			$start_date,
-			$end_date
-		));
+            {table}.id,
+            {table}.to_name,
+            {table}.to_email,
+            {table}.to_phone,
+            {table}.referral_code,
+            {table}.created_at,
+            {table}.status,
+            {table}.treated_at,
+            u.display_name as from_name,
+            u.user_email as from_email
+        FROM {table}
+        JOIN {$this->referrals->getUserTable()} u ON {table}.from_user = u.ID
+        WHERE DATE({table}.created_at) BETWEEN %s AND %s
+        ORDER BY {table}.created_at DESC",
+			[$start_date, $end_date]
+		);
 
 		// Build CSV
 		$csv_lines = [];
@@ -1859,15 +1831,15 @@
 		foreach ($referrals as $ref) {
 			$csv_lines[] = [
 				$ref->id,
-				$ref->referee_name,
-				$ref->referee_email,
-				$ref->referee_phone ?: 'N/A',
+				$ref->to_name,
+				$ref->to_email,
+				$ref->to_phone ?: 'N/A',
 				$ref->referral_code,
 				$ref->referred_at,
 				ucfirst($ref->status),
 				$ref->treated_at ?: 'N/A',
-				$ref->referrer_name,
-				$ref->referrer_email
+				$ref->from_name,
+				$ref->from_email
 			];
 		}
 
@@ -1944,19 +1916,19 @@
 	public function sanitizeRewardSettings(array $settings): array
 	{
 		return [
-			'referrer_reward_applies_to' => in_array($settings['referrer_reward_applies_to'] ?? '', ['per_user', 'flat_total'])
-				? $settings['referrer_reward_applies_to']
+			'from_user_reward_applies_to' => in_array($settings['from_user_reward_applies_to'] ?? '', ['per_user', 'flat_total'])
+				? $settings['from_user_reward_applies_to']
 				: 'per_user',
-			'referrer_reward_amount' => floatval($settings['referrer_reward_amount'] ?? 25.00),
-			'referrer_reward_type' => in_array($settings['referrer_reward_type'] ?? '', ['fixed', 'percentage'])
-				? $settings['referrer_reward_type']
+			'from_user_reward_amount' => floatval($settings['from_user_reward_amount'] ?? 25.00),
+			'from_user_reward_type' => in_array($settings['from_user_reward_type'] ?? '', ['fixed', 'percentage'])
+				? $settings['from_user_reward_type']
 				: 'fixed',
-			'referee_reward_type' => in_array($settings['referee_reward_type'] ?? '', ['percentage', 'fixed'])
-				? $settings['referee_reward_type']
+			'to_user_reward_type' => in_array($settings['to_user_reward_type'] ?? '', ['percentage', 'fixed'])
+				? $settings['to_user_reward_type']
 				: 'percentage',
-			'referee_reward_amount' => floatval($settings['referee_reward_amount'] ?? 20),
-			'referee_reward_applies_to' => in_array($settings['referee_reward_applies_to'] ?? '', ['first_order', 'all_orders'])
-				? $settings['referee_reward_applies_to']
+			'to_user_reward_amount' => floatval($settings['to_user_reward_amount'] ?? 20),
+			'to_user_reward_applies_to' => in_array($settings['to_user_reward_applies_to'] ?? '', ['first_order', 'all_orders'])
+				? $settings['to_user_reward_applies_to']
 				: 'first_order',
 		];
 	}
@@ -2246,12 +2218,12 @@
 					} else {
 						data.items.forEach(function(ref) {
 							html += '<tr>';
-							html += '<td>' + (ref.referrer_name || 'Unknown') + '</td>';
-							html += '<td>' + (ref.referee_display_name || ref.referee_name) + '</td>';
-							html += '<td>' + (ref.referee_display_email || ref.referee_email) + '</td>';
+							html += '<td>' + (ref.from_name || 'Unknown') + '</td>';
+							html += '<td>' + (ref.to_user_display_name || ref.to_name) + '</td>';
+							html += '<td>' + (ref.to_user_display_email || ref.to_email) + '</td>';
 							html += '<td><span class="referral-status ' + ref.status + '">' + ref.status + '</span></td>';
 							html += '<td>' + new Date(ref.referred_at).toLocaleDateString() + '</td>';
-							html += '<td>' + (ref.referrer_total_referrals || 0) + '</td>';
+							html += '<td>' + (ref.from_user_total_referrals || 0) + '</td>';
 							html += '<td class="referral-actions">';
 
 							if (ref.status === 'pending') {
@@ -2408,24 +2380,24 @@
 						</tr>
 						<tr>
 							<th scope="row">
-								<label for="referrer_reward_type">Reward Type</label>
+								<label for="from_user_reward_type">Reward Type</label>
 							</th>
 							<td>
-								<select name="referrer_reward_type" id="referrer_reward_type">
-									<option value="fixed" <?php selected($this->settings['referrer_reward_type'], 'fixed'); ?>>Fixed Amount</option>
-									<option value="percentage" <?php selected($this->settings['referrer_reward_type'], 'percentage'); ?>>Percentage</option>
+								<select name="from_user_reward_type" id="from_user_reward_type">
+									<option value="fixed" <?php selected($this->settings['from_user_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+									<option value="percentage" <?php selected($this->settings['from_user_reward_type'], 'percentage'); ?>>Percentage</option>
 								</select>
 							</td>
 						</tr>
 						<tr>
 							<th scope="row">
-								<label for="referrer_reward_amount">Reward Amount</label>
+								<label for="from_user_reward_amount">Reward Amount</label>
 							</th>
 							<td>
 								<input type="number"
-									   name="referrer_reward_amount"
-									   id="referrer_reward_amount"
-									   value="<?= esc_attr($this->settings['referrer_reward_amount']) ?>"
+									   name="from_user_reward_amount"
+									   id="from_user_reward_amount"
+									   value="<?= esc_attr($this->settings['from_user_reward_amount']) ?>"
 									   step="0.01"
 									   min="0">
 								<p class="description">Amount in dollars or percentage</p>
@@ -2433,12 +2405,12 @@
 						</tr>
 						<tr>
 							<th scope="row">
-								<label for="referrer_reward_applies_to">Applies To</label>
+								<label for="from_user_reward_applies_to">Applies To</label>
 							</th>
 							<td>
-								<select name="referrer_reward_applies_to" id="referrer_reward_applies_to">
-									<option value="per_user" <?php selected($this->settings['referrer_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
-									<option value="flat_total" <?php selected($this->settings['referrer_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
+								<select name="from_user_reward_applies_to" id="from_user_reward_applies_to">
+									<option value="per_user" <?php selected($this->settings['from_user_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
+									<option value="flat_total" <?php selected($this->settings['from_user_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
 								</select>
 							</td>
 						</tr>
@@ -2448,24 +2420,24 @@
 						</tr>
 						<tr>
 							<th scope="row">
-								<label for="referee_reward_type">Reward Type</label>
+								<label for="to_user_reward_type">Reward Type</label>
 							</th>
 							<td>
-								<select name="referee_reward_type" id="referee_reward_type">
-									<option value="percentage" <?php selected($this->settings['referee_reward_type'], 'percentage'); ?>>Percentage</option>
-									<option value="fixed" <?php selected($this->settings['referee_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+								<select name="to_user_reward_type" id="to_user_reward_type">
+									<option value="percentage" <?php selected($this->settings['to_user_reward_type'], 'percentage'); ?>>Percentage</option>
+									<option value="fixed" <?php selected($this->settings['to_user_reward_type'], 'fixed'); ?>>Fixed Amount</option>
 								</select>
 							</td>
 						</tr>
 						<tr>
 							<th scope="row">
-								<label for="referee_reward_amount">Reward Amount</label>
+								<label for="to_user_reward_amount">Reward Amount</label>
 							</th>
 							<td>
 								<input type="number"
-									   name="referee_reward_amount"
-									   id="referee_reward_amount"
-									   value="<?= esc_attr($this->settings['referee_reward_amount']) ?>"
+									   name="to_user_reward_amount"
+									   id="to_user_reward_amount"
+									   value="<?= esc_attr($this->settings['to_user_reward_amount']) ?>"
 									   step="0.01"
 									   min="0">
 								<p class="description">Amount in dollars or percentage</p>
@@ -2473,12 +2445,12 @@
 						</tr>
 						<tr>
 							<th scope="row">
-								<label for="referee_reward_applies_to">Applies To</label>
+								<label for="to_user_reward_applies_to">Applies To</label>
 							</th>
 							<td>
-								<select name="referee_reward_applies_to" id="referee_reward_applies_to">
-									<option value="first_order" <?php selected($this->settings['referee_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
-									<option value="all_orders" <?php selected($this->settings['referee_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
+								<select name="to_user_reward_applies_to" id="to_user_reward_applies_to">
+									<option value="first_order" <?php selected($this->settings['to_user_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
+									<option value="all_orders" <?php selected($this->settings['to_user_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
 								</select>
 							</td>
 						</tr>
@@ -2798,11 +2770,11 @@
 			->content('referral', 'Referral', 'Referrals')
 //			->initMeta('custom', 'referral')
 			->setFields([
-				'referee_name' => [
+				'to_name' => [
 					'label' => 'Name',
 					'type' => 'text',
 				],
-				'referee_email' => [
+				'to_email' => [
 					'label' => 'Email',
 					'type' => 'text',
 				],
@@ -2876,12 +2848,12 @@
 
 			// Save reward settings
 			$settings = [
-				'referrer_reward_type' => sanitize_text_field($post_data['referrer_reward_type'] ?? 'fixed'),
-				'referrer_reward_amount' => floatval($post_data['referrer_reward_amount'] ?? 25.00),
-				'referrer_reward_applies_to' => sanitize_text_field($post_data['referrer_reward_applies_to'] ?? 'per_user'),
-				'referee_reward_type' => sanitize_text_field($post_data['referee_reward_type'] ?? 'percentage'),
-				'referee_reward_amount' => floatval($post_data['referee_reward_amount'] ?? 20),
-				'referee_reward_applies_to' => sanitize_text_field($post_data['referee_reward_applies_to'] ?? 'first_order')
+				'from_user_reward_type' => sanitize_text_field($post_data['from_user_reward_type'] ?? 'fixed'),
+				'from_user_reward_amount' => floatval($post_data['from_user_reward_amount'] ?? 25.00),
+				'from_user_reward_applies_to' => sanitize_text_field($post_data['from_user_reward_applies_to'] ?? 'per_user'),
+				'to_user_reward_type' => sanitize_text_field($post_data['to_user_reward_type'] ?? 'percentage'),
+				'to_user_reward_amount' => floatval($post_data['to_user_reward_amount'] ?? 20),
+				'to_user_reward_applies_to' => sanitize_text_field($post_data['to_user_reward_applies_to'] ?? 'first_order')
 			];
 
 			update_option(BASE . 'referral_settings', $settings);
@@ -2900,15 +2872,15 @@
 	}
 
 	/**
-	 * Get formatted reward text for referee
+	 * Get formatted reward text for to_user
 	 *
 	 * @param bool $full Include "off your first treatment" text
 	 * @return string
 	 */
 	public function getRewardText(bool $full = true): string
 	{
-		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
-		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
+		$reward_amount = $this->settings['to_user_reward_amount'] ?? 20;
+		$reward_type = $this->settings['to_user_reward_type'] ?? 'percentage';
 
 		$reward_text = $reward_type === 'percentage'
 			? $reward_amount . '% off'
@@ -2973,28 +2945,28 @@
 	}
 
 	/**
-	 * Send notification to referrer when someone registers
+	 * Send notification to from_user when someone registers
 	 *
-	 * @param int $referrer_id
-	 * @param string $referee_name
+	 * @param int $from_user_id
+	 * @param string $to_name
 	 */
-	protected function sendReferrerNotification(int $referrer_id, string $referee_name): void
+	protected function sendReferrerNotification(int $from_user_id, string $to_name): void
 	{
-		$referrer = get_userdata($referrer_id);
-		if (!$referrer) {
+		$from_user = get_userdata($from_user_id);
+		if (!$from_user) {
 			return;
 		}
 
-		$subject = sprintf('%s signed up with your referral code!', $referee_name);
+		$subject = sprintf('%s signed up with your referral code!', $to_name);
 		$message = sprintf(
 			"Great news! %s just signed up using your referral code.\n\n" .
 			"View your referrals: %s",
-			$referee_name,
+			$to_name,
 			home_url('/dash/referrals')
 		);
 
 		JVB()->email()->sendEmail(
-			$referrer->user_email,
+			$from_user->user_email,
 			$subject,
 			$message
 		);
@@ -3021,9 +2993,9 @@
 			return '';
 		}
 
-		// Get referrer name
-		$referrer = get_userdata($referral->referrer_id);
-		$referrer_first_name = $referrer ? strtok($referrer->display_name, ' ') : 'Your friend';
+		// Get from_user name
+		$from_user = get_userdata($referral->from_user);
+		$from_user_first_name = $from_user ? strtok($from_user->display_name, ' ') : 'Your friend';
 
 		// Get reward text
 		$reward_text = $this->getRewardText(); // Just "20% off" or "$25 off"
@@ -3035,7 +3007,7 @@
 		?>
 		<div class="welcome-banner referral-welcome">
 			<div class="banner-content">
-				<h3><?= jvbIcon('confetti') ?>Welcome! <small><b><?= esc_html($referrer_first_name) ?></b> invited you to save <b><?= esc_html($reward_text) ?></b>!</small></h3>
+				<h3><?= jvbIcon('confetti') ?>Welcome! <small><b><?= esc_html($from_user_first_name) ?></b> invited you to save <b><?= esc_html($reward_text) ?></b>!</small></h3>
 				<p>But we're not done yet! Here's what happens next:</p>
 				<div class="callout">
 					<ol>
@@ -3064,5 +3036,118 @@
 		<?php
 		return ob_get_clean();
 	}
+
+	public function updateStatus(int $referral_id, string $status): bool|WP_Error
+	{
+		$referral = $this->referrals->get(['id' => $referral_id]);
+		if (!$referral) {
+			return new WP_Error('not_found', 'Referral not found');
+		}
+
+		$data = ['status' => $status, "{$status}_at" => current_time('mysql')];
+		if ($status === 'treated') {
+			$data['treatment_count'] = ($referral->treatment_count ?? 0) + 1;
+		}
+
+		$result = $this->referrals->update($data, ['id' => $referral_id]);
+		if ($result === false) {
+			return new WP_Error('update_failed', 'Failed to update referral status');
+		}
+
+		if ($status === 'treated') {
+			$this->createRewardRecords($referral_id);
+		}
+
+		$this->cache->flush();
+		return true;
+	}
+
+	public function removeReferral(int $referral_id, int $user_id): bool|WP_Error
+	{
+		$referral = $this->referrals->get(['id' => $referral_id]);
+		if (!$referral) {
+			return new WP_Error('not_found', 'Referral not found');
+		}
+
+		if ($referral->from_user != $user_id && !current_user_can('manage_options')) {
+			return new WP_Error('unauthorized', 'Unauthorized');
+		}
+
+		if ($referral->status !== 'pending') {
+			return new WP_Error('invalid_status', 'Can only remove pending referrals');
+		}
+
+		$this->referrals->delete(['id' => $referral_id]);
+		$this->cache->flush();
+		return true;
+	}
+
+	public function resendInvitation(int $referral_id, int $user_id): bool|WP_Error
+	{
+		$referral = $this->referrals->where(['id' => $referral_id, 'from_user' => $user_id])->first();
+		if (!$referral) {
+			return new WP_Error('not_found', 'Referral not found');
+		}
+
+		$transient_key = 'referral_last_invite_' . md5($referral->to_email);
+		if (get_transient($transient_key)) {
+			return new WP_Error('rate_limit', 'Can only resend once per week');
+		}
+
+		$result = $this->sendReferralInvitation(
+			$user_id,
+			$referral->to_email,
+			$referral->to_name,
+			sprintf('Reminder: Join %s', get_bloginfo('name')),
+			'Just a friendly reminder about my invitation!'
+		);
+
+		if (is_wp_error($result)) {
+			return $result;
+		}
+
+		set_transient($transient_key, time(), WEEK_IN_SECONDS);
+		return true;
+	}
+
+	public function getAllReferrals(array $args = []): array
+	{
+		$conditions = ['1=1'];
+		$values = [];
+
+		if (!empty($args['status']) && $args['status'] !== 'all') {
+			$conditions[] = '{table}.status = %s';
+			$values[] = $args['status'];
+		}
+
+		if (!empty($args['date_start'])) {
+			$conditions[] = 'DATE({table}.created_at) >= %s';
+			$values[] = $args['date_start'];
+		}
+
+		if (!empty($args['date_end'])) {
+			$conditions[] = 'DATE({table}.created_at) <= %s';
+			$values[] = $args['date_end'];
+		}
+
+		if (!empty($args['search'])) {
+			global $wpdb;
+			$like = '%' . $wpdb->esc_like($args['search']) . '%';
+			$conditions[] = '({table}.to_name LIKE %s OR {table}.to_email LIKE %s OR {table}.referral_code LIKE %s OR u.display_name LIKE %s)';
+			array_push($values, $like, $like, $like, $like);
+		}
+
+		array_push($values, absint($args['limit'] ?? 50), absint($args['offset'] ?? 0));
+
+		return $this->referrals->queryResults(
+			"SELECT {table}.*, u.display_name as from_name
+        FROM {table}
+        LEFT JOIN {$this->referrals->getUserTable()} u ON {table}.from_user = u.ID
+        WHERE " . implode(' AND ', $conditions) . "
+        ORDER BY {table}.created_at DESC
+        LIMIT %d OFFSET %d",
+			$values
+		);
+	}
 }
 

--
Gitblit v1.10.0