| | |
| | | |
| | | 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; |
| | |
| | | { |
| | | protected $wpdb; |
| | | protected MagicLinkManager $magic_link; |
| | | protected CacheManager $cache; |
| | | protected Cache $cache; |
| | | protected Cache $requestCache; |
| | | protected Cache $statsCache; |
| | | protected string $referrals_table; |
| | | protected ?int $referralPage = null; |
| | | protected string $rewards_table; |
| | |
| | | { |
| | | global $wpdb; |
| | | $this->wpdb = $wpdb; |
| | | $this->cache = CacheManager::for('referrals', WEEK_IN_SECONDS); |
| | | $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); |
| | | |
| | | $this->referrals_table = $wpdb->prefix . BASE . 'referrals'; |
| | | $this->rewards_table = $wpdb->prefix . BASE . 'referral_rewards'; |
| | | |
| | |
| | | } |
| | | |
| | | // Clear caches |
| | | $this->cache->clear(); |
| | | $this->cache->flush(); |
| | | |
| | | // Fire action for tracking |
| | | do_action('jvb_referral_processed', $user_id, $referrer->ID, $referral_code); |
| | |
| | | */ |
| | | public function getUserReferrals(int $user_id, array $args = []): array |
| | | { |
| | | return $this->cache->remember( |
| | | $user_id, |
| | | $defaults = [ |
| | | 'status' => 'all', |
| | | 'limit' => 100, |
| | | 'offset' => 0, |
| | | 'orderby' => 'referred_at', |
| | | 'order' => 'DESC' |
| | | ]; |
| | | |
| | | $args = wp_parse_args($args, $defaults); |
| | | |
| | | return $this->requestCache->remember( |
| | | $this->requestCache->generateKey(array_merge(['user'=>$user_id], $args)), |
| | | function() use ($user_id, $args) { |
| | | $defaults = [ |
| | | 'status' => 'all', |
| | | 'limit' => 100, |
| | | 'offset' => 0, |
| | | 'orderby' => 'referred_at', |
| | | 'order' => 'DESC' |
| | | ]; |
| | | |
| | | $args = wp_parse_args($args, $defaults); |
| | | |
| | | $where = $this->wpdb->prepare("WHERE referrer_id = %d", $user_id); |
| | | |
| | | if ($args['status'] !== 'all') { |
| | |
| | | */ |
| | | public function getUserStats(int $user_id): array |
| | | { |
| | | $cache_key = 'stats_' . $user_id; |
| | | $cached = $this->cache->get($cache_key); |
| | | |
| | | if ($cached !== false) { |
| | | return $cached; |
| | | } |
| | | |
| | | $stats = $this->wpdb->get_row($this->wpdb->prepare( |
| | | "SELECT |
| | | return $this->statsCache->remember( |
| | | $user_id, |
| | | function() use ($user_id) { |
| | | $stats = $this->wpdb->get_row($this->wpdb->prepare( |
| | | "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); |
| | | $user_id |
| | | ), ARRAY_A); |
| | | |
| | | // Get total rewards earned (available + redeemed) |
| | | $rewards = $this->wpdb->get_var($this->wpdb->prepare( |
| | | "SELECT SUM(amount) |
| | | // 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 |
| | | )); |
| | | $user_id |
| | | )); |
| | | |
| | | $stats['total_rewards'] = floatval($rewards ?? 0); |
| | | $stats['user_id'] = $user_id; |
| | | $this->cache->set($cache_key, $stats, HOUR_IN_SECONDS); |
| | | |
| | | return $stats; |
| | | $stats['total_rewards'] = floatval($rewards ?? 0); |
| | | $stats['user_id'] = $user_id; |
| | | return $stats; |
| | | } |
| | | ); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public function getTopReferrers(int $limit = 10, string $period = 'all'): array |
| | | { |
| | | $where = ''; |
| | | return $this->statsCache->remember( |
| | | $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)", |
| | | default => "1=1" |
| | | }; |
| | | 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)", |
| | | default => "1=1" |
| | | }; |
| | | |
| | | $where = "WHERE {$date_where}"; |
| | | } |
| | | $where = "WHERE {$date_where}"; |
| | | } |
| | | |
| | | $query = "SELECT |
| | | $query = "SELECT |
| | | referrer_id, |
| | | COUNT(*) as referral_count, |
| | | SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treated_count |
| | |
| | | ORDER BY referral_count DESC |
| | | LIMIT {$limit}"; |
| | | |
| | | $results = $this->wpdb->get_results($query); |
| | | $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 : ''; |
| | | } |
| | | // 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 : ''; |
| | | } |
| | | |
| | | return $results; |
| | | return $results; |
| | | } |
| | | ); |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | protected function generateCSV(array $referrals): string |
| | | { |
| | | $csv = "Referred By,Referee Name,Referee Email,Referee Phone,Referral Code,Status,Referred At,Treated At\n"; |
| | | $cache = Cache::for('referralCSV', HOUR_IN_SECONDS)->connect('referrals'); |
| | | return $cache->remember( |
| | | 'csv', |
| | | function () use ($referrals) { |
| | | $csv = "Referred By,Referee Name,Referee Email,Referee Phone,Referral Code,Status,Referred At,Treated At\n"; |
| | | |
| | | 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->referral_code, |
| | | $referral->status, |
| | | $referral->referred_at, |
| | | $referral->treated_at ?? 'Not yet' |
| | | ); |
| | | } |
| | | 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->referral_code, |
| | | $referral->status, |
| | | $referral->referred_at, |
| | | $referral->treated_at ?? 'Not yet' |
| | | ); |
| | | } |
| | | |
| | | return $csv; |
| | | return $csv; |
| | | } |
| | | ); |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | JVB()->connect('cloudflare')->renderTurnstile(); |
| | | $turnstile = ob_get_clean(); |
| | | |
| | | $meta = new MetaForm(); |
| | | $reward_text = $this->getRewardText(true); |
| | | |
| | | // Pre-fill code if from referral link |
| | |
| | | <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, [ |
| | | ' .Form::render('referral_name', null, [ |
| | | 'required' => true, |
| | | 'type' => 'text', |
| | | 'label' => 'Your Name', |
| | | 'placeholder'=> 'Mister Meeseeks', |
| | | 'autocomplete'=>'name' |
| | | ]). |
| | | $meta->return('referral_email', null, [ |
| | | Form::render('referral_email', null, [ |
| | | 'required' => true, |
| | | 'type' => 'email', |
| | | 'label' => 'Your Email', |
| | | 'placeholder'=> 'look@me.com', |
| | | 'autocomplete'=> 'email' |
| | | ]). |
| | | $meta->return('referral_code', $prefill_code, [ |
| | | Form::render('referral_code', null, $prefill_code, [ |
| | | 'required' => true, |
| | | 'type' => 'text', |
| | | 'label' => 'Referral Code', |
| | |
| | | </div>'; |
| | | |
| | | $loginForm = '<form id="login-form"> |
| | | '.jvbFormStatus().$meta->return('login_email', null, [ |
| | | '.jvbFormStatus().Form::render('login_email', null, [ |
| | | 'required' => true, |
| | | 'type' => 'email', |
| | | 'label' => 'Your Email', |
| | |
| | | <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', |
| | | 'label' => 'Invite Your Friends', |
| | |
| | | 'hint' => 'We\'ll add your code and a link automatically.' |
| | | ] |
| | | ]; |
| | | $meta->render('invite', [], $invite); |
| | | echo Form::render('invite', null, $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> |