// ════════════════════════════════════════════════════════════════════ require_once __DIR__ . '/../inc/config.php'; $_alerts_path = __DIR__ . '/../inc/agent_alerts.php'; if (file_exists($_alerts_path)) require_once $_alerts_path; if (!is_logged_in()) { header('Location: ' . $url . 'login.php?redirect=agent_alerts.php'); exit; } $uid = current_uid(); $username = h($_SESSION['username'] ?? ''); $user = db_query("SELECT * FROM users WHERE id=? LIMIT 1", 'i', $uid); $user = $user ? $user[0] : []; $user_phone = $user['phone'] ?? ''; // ── Self-heal: ensure alert tables exist ──────────────────────────── if ($conn) { try { $conn->query("CREATE TABLE IF NOT EXISTS `agent_alert_prefs` ( `user_id` INT NOT NULL PRIMARY KEY, `sms_enabled` TINYINT(1) DEFAULT 0, `alert_phone` VARCHAR(30) DEFAULT NULL, `service_locations` TEXT NULL, `alert_types` VARCHAR(60) DEFAULT 'all', `min_fee` DECIMAL(10,2) DEFAULT '0.00', `quiet_hours` VARCHAR(20) DEFAULT NULL, `verified` TINYINT(1) DEFAULT 0, `verified_at` DATETIME NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX `idx_alert_enabled` (`sms_enabled`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); $conn->query("CREATE TABLE IF NOT EXISTS `sms_alert_queue` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `user_id` INT NOT NULL, `phone` VARCHAR(30) NOT NULL, `message` TEXT NOT NULL, `opp_type` VARCHAR(20) DEFAULT NULL, `opp_id` INT DEFAULT NULL, `status` TINYINT DEFAULT 0, `provider_id` VARCHAR(100) DEFAULT NULL, `error` TEXT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `sent_at` DATETIME NULL, INDEX `idx_sms_status` (`status`), INDEX `idx_sms_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); } catch (\Throwable $e) {} } // ── POST: save preferences ───────────────────────────────────────── $msg = ''; $err = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_alert_prefs']) && csrf_verify()) { $sms_on = !empty($_POST['alert_sms_enabled']) ? 1 : 0; $phone_in = trim((string)($_POST['alert_phone'] ?? '')); $phone_clean = preg_replace('/[^\d+]/', '', $phone_in); $locs = trim((string)($_POST['service_locations'] ?? '')); if (strlen($locs) > 4000) $locs = substr($locs, 0, 4000); $type_in = $_POST['alert_types'] ?? []; if (is_string($type_in)) $type_in = [$type_in]; $type_in = array_filter($type_in, function($t){return in_array($t, ['return','usher','scout'], true);}); $alert_types = empty($type_in) || count($type_in) === 3 ? 'all' : implode(',', $type_in); $min_fee = (float)preg_replace('/[^\d.]/', '', (string)($_POST['alert_min_fee'] ?? '0')); if ($sms_on && $phone_clean === '') { $err = 'A phone number is required to receive SMS alerts.'; } else { try { $ep = $conn->real_escape_string($phone_clean); $el = $conn->real_escape_string($locs); $et = $conn->real_escape_string($alert_types); $conn->query("INSERT INTO agent_alert_prefs (user_id, sms_enabled, alert_phone, service_locations, alert_types, min_fee) VALUES ($uid, $sms_on, '$ep', '$el', '$et', $min_fee) ON DUPLICATE KEY UPDATE sms_enabled=$sms_on, alert_phone='$ep', service_locations='$el', alert_types='$et', min_fee=$min_fee"); $msg = $sms_on ? "SMS alerts enabled. You'll be texted when matching opportunities are posted." : 'Alert preferences saved.'; } catch (\Throwable $e) { $err = 'Could not save: ' . $e->getMessage(); } } } // ── Load current prefs ───────────────────────────────────────────── $prefs = ['sms_enabled'=>0,'alert_phone'=>'','service_locations'=>'','alert_types'=>'all','min_fee'=>0,'verified'=>0]; try { $r = $conn->query("SELECT * FROM agent_alert_prefs WHERE user_id=$uid LIMIT 1"); if ($r && $r->num_rows > 0) { $row = $r->fetch_assoc(); $prefs = [ 'sms_enabled' => (int)$row['sms_enabled'], 'alert_phone' => (string)($row['alert_phone'] ?? ''), 'service_locations' => (string)($row['service_locations'] ?? ''), 'alert_types' => (string)($row['alert_types'] ?? 'all'), 'min_fee' => (float)($row['min_fee'] ?? 0), 'verified' => (int)($row['verified'] ?? 0), ]; } else { $prefs['alert_phone'] = $user_phone; } } catch (\Throwable $e) {} // ── Recent alert log for this agent ──────────────────────────────── $recent_alerts = []; try { $r = $conn->query("SELECT id, phone, message, opp_type, opp_id, status, provider_id, error, created_at, sent_at FROM sms_alert_queue WHERE user_id = $uid ORDER BY created_at DESC LIMIT 25"); if ($r) while ($row = $r->fetch_assoc()) $recent_alerts[] = $row; } catch (\Throwable $e) {} $sel_types = $prefs['alert_types'] === 'all' ? ['return','usher','scout'] : explode(',', $prefs['alert_types']); $status_disp = [0 => ['Queued','#b8860b'], 1 => ['Sent','#1a7f37'], 2 => ['Failed','#e53935']]; $type_icon = ['return'=>'📦','usher'=>'🚗','scout'=>'🔍']; ?> Opportunity Alerts | B* DYNA

Agent Settings

Opportunity Alerts

Get a text the moment a return, vehicle usher, or drive scout job posts in your service area. Configure your phone, locations, and filters below.

📱 Subscription
Enter any combination of cities, states, ZIPs, or counties — one per line. Opportunities are matched if their location contains any term from a line. Leave blank to receive alerts for every opportunity nationwide.