Jake Vanderwerf
2025-11-10 e9967fa22781d922ba4eb8fb44fe72d200ac4b14
inc/managers/ReferralManager.php
@@ -3,6 +3,7 @@
use JVBase\managers\MagicLinkManager;
use JVBase\integrations\Cloudflare;
use JVBase\meta\MetaForm;
use JVBase\utility\Features;
use WP_User;
use WP_Error;
@@ -23,6 +24,7 @@
   protected MagicLinkManager $magic_link;
   protected CacheManager $cache;
   protected string $referrals_table;
   protected ?int $referralPage = null;
   protected string $rewards_table;
   // Default reward settings
@@ -35,17 +37,24 @@
      'referee_reward_applies_to' => 'first_order'  // 'first_order' or 'all_orders'
   ];
   protected array $settings;
   public function __construct()
   {
      global $wpdb;
      $this->wpdb = $wpdb;
      $this->cache = new CacheManager('referrals');
      $this->cache = CacheManager::for('referrals', WEEK_IN_SECONDS);
      $this->referrals_table = BASE . 'referrals';
      $this->rewards_table = BASE . 'referral_rewards';
      $this->magic_link = new MagicLinkManager();
      // Hook into user registration to track referrals
      add_action('user_register', [$this, 'processReferral'], 10, 1);
      $this->referralPage = $this->getReferralPageId();
      $this->settings = $this->getRewardSettings();
      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 meta boxes for admin to manage referrals
      add_action('show_user_profile', [$this, 'displayUserReferralInfo']);
@@ -55,14 +64,60 @@
      add_action('personal_options_update', [$this, 'saveUserReferralCode']);
      add_action('edit_user_profile_update', [$this, 'saveUserReferralCode']);
      add_filter(BASE.'new_user_email_content', [$this, 'addReferralToWelcomeEmail'], 99, 2);
      add_filter('jvbNewUserEmail', [$this, 'addReferralToWelcomeEmail'], 99, 2);
      add_action('jvbAdditionalActions', [$this, 'outputShareWidget']);
      add_filter('jvbAdditionalActions', [$this, 'outputShareWidget']);
      add_action('wp_enqueue_scripts', [$this, 'enqueueScripts']);
      // Schedule cron jobs for reports
      $this->registerCronJobs();
      // Register admin subpage
//    add_filter('jvbAdminSubpages', [$this, 'addSubpage'], 10, 1);
      // Add admin bar label for referral page
      add_action('admin_bar_menu', [$this, 'addReferralPageLabel'], 999);
      // Add admin notice to referral page edit screen
      add_action('admin_notices', [$this, 'showReferralPageNotice']);
      add_filter('jvbDashboardPage', [$this, 'renderDashPage'], 10, 2);
      // Handle settings save
      add_action('admin_init', [$this, 'registerSettings']);
   }
   public function addLoginInputs(string $action):void
   {
      if (array_key_exists('ref', $_GET)) {
         echo '<input type="hidden" name="referral_code" value="'.$_GET['ref'].'">';
      }
   }
   public function modifyLoginText(array $defaults):array
   {
      if (!array_key_exists('ref', $_GET)){
         return $defaults;
      }
      $code = $_GET['ref'];
      $user = $this->getUserByReferralCode($code);
      $desc = ($user) ? strtok($user->display_name, ' ') . ' invited you ' : 'You\'ve been invited ';
      $defaults['title'] = 'Register your Account. Get Your Reward.';
      $defaults['description'] = [
         $desc.' to see the difference with us.',
         'Oh, and you\'ll get 20% off your first treatment!',
         'Finish this account creation (you\'ll still need to make an account on Jane App to book), or let us know when you come in for your appointment.'
      ];
      $defaults['extra'] = [
         'Once you\'re in, you\'ll get your own code you can share.',
         'Get a $25 credit for <b>each</b> person you send our way who comes in for their first treatment!'
      ];
      return $defaults;
   }
   public function enqueueScripts():void
@@ -189,17 +244,47 @@
      return !empty($users);
   }
   public function processRegistrationToken(int $user_id, string $email, array $data): void
   {
      // Check for referral code in data
      $code = $data['referral_code'] ?? '';
      if (empty($code)) {
         return;
      }
      // Store in session/cookie for processReferral to pick up
      if (session_status() === PHP_SESSION_NONE) {
         session_start();
      }
      $_SESSION[BASE . 'referral_code'] = sanitize_text_field($code);
      setcookie(
         BASE . 'referral_code',
         sanitize_text_field($code),
         time() + (86400 * 30),
         '/'
      );
   }
   /**
    * Track a new referral when user registers
    *
    * @param int $user_id
    */
   public function processReferral(int $user_id): void
   public function processReferral(int $user_id, string $email, array $data): void
   {
      // Check if user was created via referral magic link
      $referral_code = get_user_meta($user_id, BASE . 'pending_referral_code', true);
      // Try to get code from multiple sources
      $referral_code = $data['referral_code'] ??
      get_user_meta($user_id, BASE . 'pending_referral_code', true);
      if (!$referral_code) {
      // Check session/cookie if not in data
      if (empty($referral_code)) {
         if (session_status() === PHP_SESSION_NONE) {
            session_start();
         }
         $referral_code = $_SESSION[BASE . 'referral_code'] ?? $_COOKIE[BASE . 'referral_code'] ?? '';
      }
      if (empty($referral_code)) {
         return;
      }
@@ -238,7 +323,7 @@
    * @param string $code
    * @return int|false
    */
   protected function createReferral(int $referrer_id, int $referee_id, string $code)
   public function createReferral(int $referrer_id, int $referee_id, string $code)
   {
      $user = get_user_by('ID', $referee_id);
@@ -266,13 +351,17 @@
    */
   public function getUserByReferralCode(string $code): ?WP_User
   {
      $users = get_users([
         'meta_key' => BASE . 'referral_code',
         'meta_value' => $code,
         'number' => 1
      ]);
      return $users[0] ?? null;
      return $this->cache->remember(
         $code,
         function () use ($code) {
            $users = get_users([
               'meta_key' => BASE . 'referral_code',
               'meta_value' => $code,
               'number' => 1
            ]);
            return $users[0] ?? null;
         }
      );
   }
   /**
@@ -337,8 +426,6 @@
         return;
      }
      $settings = $this->getRewardSettings();
      // Create referrer reward
      $this->wpdb->insert(
         $this->rewards_table,
@@ -346,7 +433,7 @@
            'referral_id' => $referral_id,
            'user_id' => $referral->referrer_id,
            'reward_type' => 'referrer',
            'amount' => $settings['referrer_reward_amount'],
            'amount' => $this->settings['referrer_reward_amount'],
            'status' => 'available',
            'created_at' => current_time('mysql')
         ],
@@ -354,9 +441,9 @@
      );
      // Create referee reward
      $referee_amount = $settings['referee_reward_type'] === 'percentage'
         ? $settings['referee_reward_amount']  // Store as percentage
         : $settings['referee_reward_amount'];  // Store as fixed amount
      $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,
@@ -365,7 +452,7 @@
            'user_id' => $referral->referee_id,
            'reward_type' => 'referee',
            'amount' => $referee_amount,
            'reward_calculation' => $settings['referee_reward_type'],
            'reward_calculation' => $this->settings['referee_reward_type'],
            'status' => 'available',
            'created_at' => current_time('mysql')
         ],
@@ -877,17 +964,16 @@
   {
      $user_id = get_current_user_id();
      jvbDump($user_id);
      $content = '<aside class="jvb-referral right">';
      if (!$user_id) {
         $content .= $this->getUnloggedInReferral();
      } else {
         $content .= $this->getLoggedInReferral($user_id);
      }
      $content .= '<button type="button" class="close">'.jvbIcon('close').'</button></aside>';
      $content .= '</aside>';
      $actions[] =[
         'button' => '<button type="button" class="toggle-referral row" title="Your Referrals" data-action="toggle-referral" aria-label="Open Referral Sidebar" aria-controls="referral" aria-expanded="false">
         'button' => '<button type="button" class="attn toggle-referral row" title="Your Referrals" data-action="toggle-referral" aria-label="Open Referral Sidebar" aria-controls="referral" aria-expanded="false">
               '.jvbIcon('hand-heart').'<span class="screen-reader-text"></span>
            </button>',
         'content'   => $content
@@ -901,44 +987,30 @@
      ob_start();
      JVB()->connect('cloudflare')->renderTurnstile();
      $turnstile = ob_get_clean();
      $meta = new MetaForm();
      $codeForm = '<form id="referral-code-form">
               <div class="status" hidden>
                  <div class="spinner"></div>
                  <p class="message"></p>
               </div>
               <div class="field text">
                  <label for="referral-name">Your Name</label>
                  <input type="text"
                        id="referral-name"
                        name="name"
                        placeholder="Mister Meeseeks"
                        autocomplete="name"
                        required>
               </div>
               <div class="field email">
                  <label for="referral-email">Your Email</label>
                  <input type="email"
                        id="referral-email"
                        name="email"
                        placeholder="look@me.com"
                        autocomplete="email"
                        required>
               </div>
               <div class="field text">
                  <label for="referral-code-input">Referral Code</label>
                  <input type="text"
                        id="referral-code-input"
                        name="referral_code"
                        placeholder="e.g., THISISFAKE1234"
                        required
                        pattern="[A-Za-z0-9]+"
                        maxlength="20"
                        autocomplete="off"
                        style="text-transform: uppercase;">
               </div>
               '.jvbFormStatus().$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', null, [
                  'required'  => true,
                  'type'      => 'text',
                  'label'     => 'Referral Code',
                  'pattern'   => '[A-Za-z0-9]+',
                  'maxLength' => 20,
                  'autocomplete'=>'off'
               ]).'
               <button type="submit">
                  Get Started
               </button>
@@ -954,14 +1026,12 @@
            </div>';
      $loginForm = '<form id ="login-form">
      <div class="status" hidden>
         <div class="spinner"></div>
         <p class="message"></p>
      </div>
      <div class="field email">
         <label for="login-email">Your Email</label>
         <input id="login-email" name="login-email" type="email" autocomplete="email">
      </div>
      '.jvbFormStatus().$meta->return('login_email', null, [
            'required'  => true,
            'type'      => 'email',
            'label'     => 'Your Email',
            'autocomplete'=>'email'
         ]).'
      '.$turnstile.'
      <button type="submit">Login With Magic Link</button>
</form>
@@ -999,9 +1069,8 @@
         return '';
      }
      $settings = $this->getRewardSettings();
      $reward_amount = $settings['referee_reward_amount'] ?? 20;
      $reward_type = $settings['referee_reward_type'] ?? 'percentage';
      $reward_amount = $this->settings['referee_reward_amount'] ?? 20;
      $reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
      $reward_text = $reward_type === 'percentage'
         ? $reward_amount . '% off'
@@ -1037,7 +1106,7 @@
      return ob_get_clean();
   }
   function getLoggedInReferral(int $user_id):string
   public function getLoggedInReferral(int $user_id):string
   {
      // Logged-in user widget
      $referral_code = get_user_meta($user_id, BASE . 'referral_code', true);
@@ -1050,7 +1119,7 @@
         }
      }
      $share_url = home_url('/?ref=' . $referral_code);
      $share_url = $this->getShareURL($referral_code);
      ob_start();
      ?>
@@ -1070,14 +1139,14 @@
            target="_blank"
            rel="noopener noreferrer"
            class="share-btn facebook-share">
            <?php echo jvbIcon('facebook', ['size' => 20]); ?>
            <?php echo jvbIcon('facebook-logo', ['size' => 20]); ?>
            Facebook
         </a>
         <a href="https://twitter.com/intent/tweet?url=<?php echo urlencode($share_url); ?>&text=<?php echo urlencode('Check this out!'); ?>"
            target="_blank"
            rel="noopener noreferrer"
            class="share-btn twitter-share">
            <?php echo jvbIcon('twitter', ['size' => 20]); ?>
            <?php echo jvbIcon('twitter-logo', ['size' => 20]); ?>
            Twitter
         </a>
      </div>
@@ -1130,27 +1199,52 @@
         return $content;
      }
      $settings = get_option(BASE . 'referral_settings', []);
      $reward_amount = $settings['referee_reward_amount'] ?? 20;
      $reward_type = $settings['referee_reward_type'] ?? 'percentage';
      $reward_amount = $this->settings['referee_reward_amount'] ?? 20;
      $reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
      $reward_text = $reward_type === 'percentage'
         ? $reward_amount . '% off'
         : '$' . number_format($reward_amount, 2) . ' off';
      $bonus_content = '<div style="background: #e7f5ff; padding: 20px; border-radius: 8px; margin: 20px 0;">';
      $bonus_content .= '<h3 style="margin-top: 0; color: #2271b1;">🎉 Welcome Bonus!</h3>';
      $bonus_content .= '<p>Since you were referred by a friend, you\'ve earned <strong>' . $reward_text . '</strong> your first booking!</p>';
      $bonus_content .= '<p>Your reward will be automatically applied when you book.</p>';
      $bonus_content .= '</div>';
      $bonus_content = sprintf(
         '
         <h3>Thanks for the ♡</h3>
         <p>Since you were referred by a friend, you\'ve earned <strong>%s</strong> your first booking!</p>
         <p>Your reward will be automatically applied when you book.</p>',
         $reward_text,
      );
      // Insert bonus content after the first paragraph
      $parts = explode('</p>', $content, 2);
      if (count($parts) === 2) {
         return $parts[0] . '</p>' . $bonus_content . $parts[1];
      $code = $this->getUserReferralCode($user->ID);
      $yourCode = '';
      if (!is_wp_error($code)) {
         $share_url = $this->getShareURL($code);
         $yourCode = sprintf(
            '<div class="callout">
         <h3>Share the ♡ with Friends</h3>
         <p>If you find you love what we can do for you, you can share your own code!</p>
         <p>Your Referral Code: <strong>%s</strong></p>
         <p>Or click the button below:</p>
         %s
         </div>',
            jvbEmailLink($code),
            jvbMailButton($share_url, 'Share Your Code')
         );
      }
      return $content . $bonus_content;
      return $content . $bonus_content . $yourCode;
   }
   public function getShareURL(string $code):string
   {
      return add_query_arg(
         [
            'ref' => $code,
            'action' => 'register'
         ],
         wp_login_url()
      );
   }
   /**
@@ -1198,10 +1292,9 @@
      }
      // Get reward text for email
      $settings = $this->getRewardSettings();
      $reward_text = $settings['referee_reward_type'] === 'percentage'
         ? "Get {$settings['referee_reward_amount']}% off your first treatment!"
         : "Get \${$settings['referee_reward_amount']} off your first treatment!";
      $reward_text = $this->settings['referee_reward_type'] === 'percentage'
         ? "Get {$this->settings['referee_reward_amount']}% off your first treatment!"
         : "Get \${$this->settings['referee_reward_amount']} off your first treatment!";
      // Record the invitation attempt (for tracking)
      $this->recordInvitationAttempt($user_id, $invitee_email, $invitee_name);
@@ -1463,5 +1556,345 @@
      return $csv_content;
   }
   /**
    * Add referral settings subpage to admin menu
    *
    * @param array $subpages
    * @return array
    */
   public function addSubpage(array $subpages): array
   {
      $subpages[] = [
         'page_title' => 'Referral Settings',
         'menu_title' => 'Referrals',
         'capability' => 'manage_options',
         'menu_slug'  => 'jvb-referrals',
         'callback'   => [$this, 'renderAdminPage'],
         'icon'       => 'users',
      ];
      return $subpages;
   }
   /**
    * Register settings
    */
   public function registerSettings(): void
   {
      register_setting(
         BASE . 'referral_settings',
         BASE . 'referral_page_id',
         [
            'type' => 'integer',
            'sanitize_callback' => 'absint',
            'default' => 0
         ]
      );
      register_setting(
         BASE . 'referral_settings',
         BASE . 'referral_reward_settings',
         [
            'type' => 'array',
            'sanitize_callback' => [$this, 'sanitizeRewardSettings'],
            'default' => $this->default_settings
         ]
      );
   }
   /**
    * Sanitize reward settings
    */
   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']
            : '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']
            : 'fixed',
         'referee_reward_type' => in_array($settings['referee_reward_type'] ?? '', ['percentage', 'fixed'])
            ? $settings['referee_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']
            : 'first_order',
      ];
   }
   /**
    * Render the admin settings page
    */
   public function renderAdminPage(): void
   {
      // Handle form submission
      if (isset($_POST['submit']) && check_admin_referer(BASE . 'referral_settings_nonce')) {
         update_option(BASE . 'referral_page_id', absint($_POST[BASE . 'referral_page_id'] ?? 0));
         $reward_settings = [
            'referrer_reward_applies_to' => sanitize_text_field($_POST['referrer_reward_applies_to'] ?? 'per_user'),
            'referrer_reward_amount' => floatval($_POST['referrer_reward_amount'] ?? 25.00),
            'referrer_reward_type' => sanitize_text_field($_POST['referrer_reward_type'] ?? 'fixed'),
            'referee_reward_type' => sanitize_text_field($_POST['referee_reward_type'] ?? 'percentage'),
            'referee_reward_amount' => floatval($_POST['referee_reward_amount'] ?? 20),
            'referee_reward_applies_to' => sanitize_text_field($_POST['referee_reward_applies_to'] ?? 'first_order'),
         ];
         update_option(BASE . 'referral_reward_settings', $this->sanitizeRewardSettings($reward_settings));
         echo '<div class="notice notice-success is-dismissible"><p>Settings saved successfully.</p></div>';
      }
      echo $this->renderAdminHTML();
   }
   protected function renderAdminHTML():string
   {
      ob_start();
      ?>
      <div class="wrap">
         <h1>Referral Settings</h1>
         <form method="post" action="">
            <?php wp_nonce_field(BASE . 'referral_settings_nonce'); ?>
            <div class="card">
               <h2>Referral Page</h2>
               <p>Select the page where users can access their referral dashboard.</p>
               <table class="form-table">
                  <tr>
                     <th scope="row">
                        <label for="<?= BASE ?>referral_page_id">Referral Page</label>
                     </th>
                     <td>
                        <?php
                        wp_dropdown_pages([
                           'name' => BASE . 'referral_page_id',
                           'id' => BASE . 'referral_page_id',
                           'selected' => $this->referralPage,
                           'show_option_none' => __('— Select —', 'jvbase'),
                           'option_none_value' => '0'
                        ]);
                        ?>
                        <p class="description">
                           This page will show "Referral Page" in the admin bar when editing.
                        </p>
                     </td>
                  </tr>
               </table>
            </div>
            <div class="card">
               <h2>Reward Settings</h2>
               <table class="form-table">
                  <tr>
                     <th colspan="2"><h3>Referrer Rewards</h3></th>
                  </tr>
                  <tr>
                     <th scope="row">
                        <label for="referrer_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>
                     </td>
                  </tr>
                  <tr>
                     <th scope="row">
                        <label for="referrer_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']) ?>"
                              step="0.01"
                              min="0">
                        <p class="description">Amount in dollars or percentage</p>
                     </td>
                  </tr>
                  <tr>
                     <th scope="row">
                        <label for="referrer_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>
                     </td>
                  </tr>
                  <tr>
                     <th colspan="2"><h3>Referee (New User) Rewards</h3></th>
                  </tr>
                  <tr>
                     <th scope="row">
                        <label for="referee_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>
                     </td>
                  </tr>
                  <tr>
                     <th scope="row">
                        <label for="referee_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']) ?>"
                              step="0.01"
                              min="0">
                        <p class="description">Amount in dollars or percentage</p>
                     </td>
                  </tr>
                  <tr>
                     <th scope="row">
                        <label for="referee_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>
                     </td>
                  </tr>
               </table>
            </div>
            <p class="submit">
               <button type="submit" name="submit" class="button button-primary">Save Settings</button>
            </p>
         </form>
         <?= $this->renderReferralStats(true) ?>
      </div>
      <?php
      return ob_get_clean();
   }
   /**
    * Render referral statistics
    */
   protected function renderReferralStats(bool $wrapCard = false):string
   {
      ob_start();
      global $wpdb;
      $total_referrals = $wpdb->get_var("SELECT COUNT(*) FROM {$this->referrals_table}");
      $pending_referrals = $wpdb->get_var("SELECT COUNT(*) FROM {$this->referrals_table} WHERE status = 'pending'");
      $treated_referrals = $wpdb->get_var("SELECT COUNT(*) FROM {$this->referrals_table} WHERE status = 'treated'");
      ?>
      <table class="widefat">
         <tr>
            <th>Total Referrals</th>
            <td><?= esc_html($total_referrals) ?></td>
         </tr>
         <tr>
            <th>Pending</th>
            <td><?= esc_html($pending_referrals) ?></td>
         </tr>
         <tr>
            <th>Treated</th>
            <td><?= esc_html($treated_referrals) ?></td>
         </tr>
      </table>
      <?php
      $table = ob_get_clean();
      if ($wrapCard) {
         $table = '<div class="card">
            <h2>Referral Statistics</h2>
            '.$table.'
         </div>';
      }
      return $table;
   }
   /**
    * Add "Referral Page" label to admin bar
    *
    * @param WP_Admin_Bar $wp_admin_bar
    */
   public function addReferralPageLabel($wp_admin_bar): void
   {
      if (!is_admin()) {
         return;
      }
      if (!$this->referralPage) {
         return;
      }
      global $pagenow, $post;
      // Check if we're editing the referral page
      if ('post.php' === $pagenow && $post && $post->ID === $this->referralPage) {
         $wp_admin_bar->add_node([
            'id' => 'referral-page',
            'parent' => 'top-secondary',
            'title' => __('Referral Page', 'jvbase'),
            'meta' => [
               'class' => 'referral-page-notice'
            ]
         ]);
      }
   }
   /**
    * Get the referral page ID
    *
    * @return int|null
    */
   public function getReferralPageId(): ?int
   {
      $page_id = get_option(BASE . 'referral_page_id');
      return $page_id ? (int) $page_id : null;
   }
   /**
    * Show admin notice on referral page edit screen
    */
   public function showReferralPageNotice(): void
   {
      global $pagenow, $post;
      if ('post.php' !== $pagenow || !$post) {
         return;
      }
      if ($post->ID === $this->referralPage) {
         echo '<div class="notice notice-info">';
         echo '<p>' . __('This page is designated as the <strong>Referral Page</strong>.', 'jvbase') . '</p>';
         echo '</div>';
      }
   }
   public function renderDashPage(string $content, string $page):string
   {
      if ($page !== 'referrals') {
         return $content;
      }
      $out = '';
      if (current_user_can('manage_options')) {
         $out .= $this->renderAdminHTML();
      } else {
         $out .= $this->renderReferralStats(true);
      }
      return ($out === '') ? $content : '<form id="referrals" class="col" data-save="referrals">'.$out.'</form>';
   }
}