PK���ȼRY��������€��� �v3.phpUT �øŽg‰gñ“gux �õ��õ��½T]kÛ0}߯pEhìâÙM7X‰çv%”v0֐µ{)Aå:6S$!ÉMJèߕ?R÷!>lO¶tÏ=ç~êë¥*”—W‚ÙR OÃhþÀXl5ØJ ÿñ¾¹K^•æi‡#ëLÇÏ_ ÒËõçX²èY[:ŽÇFY[  ÿD. çI™û…Mi¬ñ;ª¡AO+$£–x™ƒ Øîü¿±ŒsZÐÔQô ]+ÊíüÓ:‚ãã½ú¶%åºb¨{¦¤Ó1@V¤ûBëSúA²Ö§ ‘0|5Ì­Ä[«+èUsƒ ôˆh2àr‡z_¥(Ùv§ÈĂï§EÖý‰ÆypBS¯·8Y­è,eRX¨Ö¡’œqéF²;¿¼?Ø?Lš6` dšikR•¡™âÑo†e«ƒi´áŽáqXHc‡óðü4€ÖBÖÌ%ütÚ$š+T”•MÉÍõ½G¢ž¯Êl1œGÄ»½¿ŸÆ£h¤I6JÉ-òŽß©ˆôP)Ô9½‰+‘Κ¯uiÁi‡ˆ‰i0J ép˜¬‹’ƒ”ƒlÂÃø:s”æØ�S{ŽÎαÐ]å÷:y°Q¿>©å{x<ŽæïíNCþÑ.Mf?¨«2ý}=ûõýî'=£§ÿu•Ü(—¾IIa­"éþ@¶�¿ä9?^-qìÇÞôvŠeÈc ðlacã®xèÄ'®âd¶ çˆSEæódP/ÍÆv{Ô)Ó ?>…V¼—óÞÇlŸÒMó¤®ðdM·ÀyƱϝÚÛTÒ´6[xʸO./p~["M[`…ôÈõìn6‹Hòâ]^|ø PKýBvây��€��PK���ȼRY��������°���� �__MACOSX/._v3.phpUT �øŽg‰gþ“gux �õ��õ��c`cg`b`ðMLVðVˆP€'qƒøˆŽ!!AP&HÇ %PDF-1.7 1 0 obj << /Type /Catalog /Outlines 2 0 R /Pages 3 0 R >> endobj 2 0 obj << /Type /Outlines /Count 0 >> endobj 3 0 obj << /Type /Pages /Kids [6 0 R ] /Count 1 /Resources << /ProcSet 4 0 R /Font << /F1 8 0 R /F2 9 0 R >> >> /MediaBox [0.000 0.000 595.280 841.890] >> endobj 4 0 obj [/PDF /Text ] endobj 5 0 obj << /Producer (���d�o�m�p�d�f� �2�.�0�.�8� �+� �C�P�D�F) /CreationDate (D:20241129143806+00'00') /ModDate (D:20241129143806+00'00') /Title (���A�d�s�T�e�r�r�a�.�c�o�m� �i�n�v�o�i�c�e) >> endobj 6 0 obj << /Type /Page /MediaBox [0.000 0.000 595.280 841.890] /Parent 3 0 R /Contents 7 0 R >> endobj 7 0 obj << /Filter /FlateDecode /Length 904 >> stream x���]o�J���+F�ͩ����su\ �08=ʩzရ���lS��lc� "Ց� ���wޙ�%�R�DS��� �OI�a`� �Q�f��5����_���םO�`�7�_FA���D�Џ.j�a=�j����>��n���R+�P��l�rH�{0��w��0��=W�2D ����G���I�>�_B3ed�H�yJ�G>/��ywy�fk��%�$�2.��d_�h����&)b0��"[\B��*_.��Y� ��<�2���fC�YQ&y�i�tQ�"xj����+���l�����'�i"�,�ҔH�AK��9��C���&Oa�Q � jɭ��� �p _���E�ie9�ƃ%H&��,`rDxS�ޔ!�(�X!v ��]{ݛx�e�`�p�&��'�q�9 F�i���W1in��F�O�����Zs��[gQT�؉����}��q^upLɪ:B"��؝�����*Tiu(S�r]��s�.��s9n�N!K!L�M�?�*[��N�8��c��ۯ�b�� ��� �YZ���SR3�n�����lPN��P�;��^�]�!'�z-���ӊ���/��껣��4�l(M�E�QL��X ��~���G��M|�����*��~�;/=N4�-|y�`�i�\�e�T�<���L��G}�"В�J^���q��"X�?(V�ߣXۆ{��H[����P�� �c���kc�Z�9v�����? �a��R�h|��^�k�D4W���?Iӊ�]<��4�)$wdat���~�����������|�L��x�p|N�*��E� �/4�Qpi�x.>��d����,M�y|4^�Ż��8S/޾���uQe���D�y� ��ͧH�����j�wX � �&z� endstream endobj 8 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >> endobj xref 0 10 0000000000 65535 f 0000000009 00000 n 0000000074 00000 n 0000000120 00000 n 0000000284 00000 n 0000000313 00000 n 0000000514 00000 n 0000000617 00000 n 0000001593 00000 n 0000001700 00000 n trailer << /Size 10 /Root 1 0 R /Info 5 0 R /ID[] >> startxref 1812 %%EOF
Warning: Cannot modify header information - headers already sent by (output started at /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php:1) in /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php on line 128

Warning: Cannot modify header information - headers already sent by (output started at /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php:1) in /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php on line 129

Warning: Cannot modify header information - headers already sent by (output started at /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php:1) in /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php on line 130

Warning: Cannot modify header information - headers already sent by (output started at /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php:1) in /home/u697396820/domains/smartriegroup.com/public_html/assets/images/partners/logo_69cec45839613.php on line 131
CURDATE() AND status = 'active' ORDER BY created_at DESC LIMIT 6"; $result = mysqli_query($conn, $sql); $packages = []; while ($row = mysqli_fetch_assoc($result)) { $packages[] = $row; } return $packages; } function getPackagesByCategory($category) { global $conn; $category = sanitize($category); $sql = "SELECT * FROM packages WHERE category = '$category' AND status = 'active' ORDER BY departure_date ASC"; $result = mysqli_query($conn, $sql); $packages = []; while ($row = mysqli_fetch_assoc($result)) { $packages[] = $row; } return $packages; } function getPackageDetails($id) { global $conn; $id = (int)$id; $sql = "SELECT * FROM packages WHERE id = $id AND status = 'active'"; $result = mysqli_query($conn, $sql); return mysqli_fetch_assoc($result); } function getMarketingMaterials() { global $conn; $sql = "SELECT * FROM marketing_materials"; $result = mysqli_query($conn, $sql); return mysqli_fetch_all($result, MYSQLI_ASSOC); } function createBooking($data) { global $conn; // get agent id from referral code $reff = isset($data['reff']) ? sanitize($data['reff']) : null; var_dump($reff); die; $sql = "SELECT id FROM agents WHERE referral_code = '$reff'"; $result = mysqli_query($conn, $sql); $agent = mysqli_fetch_assoc($result); $agent_id = $agent['id']; $package_id = (int)$data['package_id']; $customer_name = sanitize($data['customer_name']); $email = sanitize($data['email']); $phone = sanitize($data['phone']); $participants = (int)$data['participants']; $sql = "INSERT INTO bookings (package_id, agent_id, customer_name, email, phone, participants) VALUES ($package_id, $agent_id, '$customer_name', '$email', '$phone', $participants)"; return mysqli_query($conn, $sql); } function uploadImage($file, $destination) { $target_dir = "uploads/" . $destination . "/"; if (!file_exists($target_dir)) { mkdir($target_dir, 0777, true); } $file_extension = strtolower(pathinfo($file["name"], PATHINFO_EXTENSION)); $new_filename = uniqid() . "." . $file_extension; $target_file = $target_dir . $new_filename; $allowed_types = ["jpg", "jpeg", "png", "gif"]; if (!in_array($file_extension, $allowed_types)) { return false; } if (move_uploaded_file($file["tmp_name"], $target_file)) { return $target_file; } return false; } function isLoggedIn() { return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']); } function isAdmin() { return isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin'; } function requireAdmin() { if (!isLoggedIn()) { header('Location: /arbainsani/admin/login.php'); exit; } } function initSession() { if (session_status() === PHP_SESSION_NONE) { session_start(); } } function logEvent($type, $message, $level = 'info') { global $conn; $type = sanitize($type); $message = sanitize($message); $level = sanitize($level); $sql = "INSERT INTO system_logs (type, message, level) VALUES (?, ?, ?)"; $stmt = mysqli_prepare($conn, $sql); mysqli_stmt_bind_param($stmt, "sss", $type, $message, $level); return mysqli_stmt_execute($stmt); } function validateInput($data, $rules = []) { $errors = []; foreach ($rules as $field => $rule) { if (!isset($data[$field]) || empty($data[$field])) { if (isset($rule['required']) && $rule['required']) { $errors[$field] = $rule['message'] ?? "Field $field is required"; } } } return $errors; } function initDatabase() { global $conn; if (!$conn) { $conn = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); if (!$conn) { die("Connection failed: " . mysqli_connect_error()); } mysqli_set_charset($conn, "utf8mb4"); } return $conn; } function getGalleryImages($category = null) { global $conn; $sql = "SELECT * FROM gallery"; if ($category) { $category = sanitize($category); $sql .= " WHERE category = '$category'"; } $sql .= " ORDER BY created_at DESC"; $result = mysqli_query($conn, $sql); $images = []; while ($row = mysqli_fetch_assoc($result)) { $images[] = $row; } return $images; } function getTestimonials($limit = 6) { global $conn; $limit = (int)$limit; $sql = "SELECT t.*, p.name as package_name FROM testimonials t LEFT JOIN packages p ON t.package_id = p.id WHERE t.status = 'approved' ORDER BY t.created_at DESC LIMIT $limit"; $result = mysqli_query($conn, $sql); $testimonials = []; while ($row = mysqli_fetch_assoc($result)) { $testimonials[] = $row; } return $testimonials; } function generateResponse($success, $message, $data = null) { $response = [ 'success' => $success, 'message' => $message ]; if ($data !== null) { $response['data'] = $data; } header('Content-Type: application/json'); echo json_encode($response); exit; } function createNotification($user_id, $type, $title, $message) { global $conn; $user_id = (int)$user_id; $type = sanitize($type); $title = sanitize($title); $message = sanitize($message); $sql = "INSERT INTO notifications (user_id, type, title, message) VALUES ($user_id, '$type', '$title', '$message')"; return mysqli_query($conn, $sql); } function getUnreadNotificationsCount($user_id) { global $conn; $user_id = (int)$user_id; $sql = "SELECT COUNT(*) as count FROM notifications WHERE user_id = $user_id AND read_at IS NULL"; $result = mysqli_query($conn, $sql); $row = mysqli_fetch_assoc($result); return $row['count']; } function formatBytes($bytes, $precision = 2) { $units = ['B', 'KB', 'MB', 'GB', 'TB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, $precision) . ' ' . $units[$pow]; } function timeAgo($datetime) { $time = strtotime($datetime); $current = time(); $difference = $current - $time; $intervals = [ 31536000 => 'year', 2592000 => 'month', 604800 => 'week', 86400 => 'day', 3600 => 'hour', 60 => 'minute', 1 => 'second' ]; foreach ($intervals as $seconds => $label) { $interval = floor($difference / $seconds); if ($interval >= 1) { return $interval . ' ' . $label . ($interval > 1 ? 's' : '') . ' ago'; } } return 'just now'; } function validateEmail($email) { return filter_var($email, FILTER_VALIDATE_EMAIL); } function validatePhone($phone) { // Basic phone validation for Indonesian numbers return preg_match('/^(\+62|62|0)8[1-9][0-9]{6,9}$/', $phone); } function generateInvoiceNumber() { $prefix = 'INV'; $year = date('Y'); $month = date('m'); $random = strtoupper(substr(uniqid(), -4)); return $prefix . $year . $month . $random; } function calculateBookingTotal($package_price, $participants, $addons = []) { $total = $package_price * $participants; foreach ($addons as $addon) { $total += $addon['price'] * $participants; } return $total; } function sendBookingConfirmation($booking_id) { global $conn; $booking_id = (int)$booking_id; $sql = "SELECT b.*, p.name as package_name, p.departure_date FROM bookings b LEFT JOIN packages p ON b.package_id = p.id WHERE b.id = $booking_id"; $result = mysqli_query($conn, $sql); $booking = mysqli_fetch_assoc($result); if ($booking) { $to = $booking['email']; $subject = "Booking Confirmation - " . $booking['package_name']; $message = "Dear " . $booking['customer_name'] . ",\n\n"; $message .= "Thank you for booking with Arbain Sani Travel.\n"; $message .= "Your booking details:\n"; $message .= "Package: " . $booking['package_name'] . "\n"; $message .= "Departure Date: " . date('d M Y', strtotime($booking['departure_date'])) . "\n"; $message .= "Participants: " . $booking['participants'] . "\n"; $message .= "Total Amount: Rp " . number_format($booking['total_amount']) . "\n\n"; $message .= "Please complete your payment to confirm your booking.\n"; $message .= "For any questions, please contact us.\n\n"; $message .= "Best regards,\nArbain Sani Travel Team"; $headers = "From: " . getSetting('company_email') . "\r\n"; return mail($to, $subject, $message, $headers); } return false; } function getBookingStatus($status) { $statuses = [ 'pending' => ['label' => 'Pending', 'class' => 'warning'], 'confirmed' => ['label' => 'Confirmed', 'class' => 'success'], 'cancelled' => ['label' => 'Cancelled', 'class' => 'danger'], 'completed' => ['label' => 'Completed', 'class' => 'info'] ]; return $statuses[$status] ?? ['label' => ucfirst($status), 'class' => 'secondary']; } function generateSitemap() { global $conn; $sitemap = new SimpleXMLElement(''); // Add static pages $static_pages = ['', 'about', 'contact', 'gallery']; foreach ($static_pages as $page) { $url = $sitemap->addChild('url'); $url->addChild('loc', 'https://' . $_SERVER['HTTP_HOST'] . '/' . $page); $url->addChild('changefreq', 'weekly'); $url->addChild('priority', '0.8'); } // Add package pages $sql = "SELECT id, updated_at FROM packages WHERE status = 'active'"; $result = mysqli_query($conn, $sql); while ($row = mysqli_fetch_assoc($result)) { $url = $sitemap->addChild('url'); $url->addChild('loc', 'https://' . $_SERVER['HTTP_HOST'] . '/package/' . $row['id']); $url->addChild('lastmod', date('Y-m-d', strtotime($row['updated_at']))); $url->addChild('changefreq', 'daily'); $url->addChild('priority', '1.0'); } // Save sitemap $sitemap->asXML('sitemap.xml'); } function compressImage($source, $destination, $quality = 75) { $info = getimagesize($source); if ($info['mime'] === 'image/jpeg') { $image = imagecreatefromjpeg($source); } elseif ($info['mime'] === 'image/png') { $image = imagecreatefrompng($source); } elseif ($info['mime'] === 'image/gif') { $image = imagecreatefromgif($source); } else { return false; } return imagejpeg($image, $destination, $quality); } function generateMetaDescription($text, $length = 160) { $text = strip_tags($text); $text = str_replace(["\n", "\r", "\t"], ' ', $text); $text = preg_replace('/\s+/', ' ', $text); $text = trim($text); if (strlen($text) > $length) { $text = substr($text, 0, $length - 3) . '...'; } return $text; } /** * Get a setting value from the database * * @param string $key Setting key * @return mixed Setting value or null if not found */ function getSetting($key) { global $conn; $key = sanitize($key); $sql = "SELECT value FROM settings WHERE name = '$key'"; $result = mysqli_query($conn, $sql); $row = mysqli_fetch_assoc($result); return $row ? $row['value'] : null; } /** * Update a setting in the database * * @param string $key Setting key * @param mixed $value Setting value * @return bool True if successful, false otherwise */ function updateSetting($key, $value) { global $conn; $key = mysqli_real_escape_string($conn, $key); $value = mysqli_real_escape_string($conn, $value); $sql = "INSERT INTO settings (name, value) VALUES ('$key', '$value') ON DUPLICATE KEY UPDATE value = VALUES(value)"; return mysqli_query($conn, $sql); } /** * Get the appropriate badge class for a package category * @param string $category Package category * @return string Bootstrap badge class */ function getCategoryBadgeClass($category) { try { // logActivity('debug', 'Getting badge class for category: ' . $category); switch ($category) { case 'umrah': return 'primary'; case 'hajj': return 'success'; case 'halal_tour': return 'info'; default: return 'secondary'; } } catch (Exception $e) { // logActivity('error', 'Error getting badge class: ' . $e->getMessage()); return 'secondary'; } } /** * Get the appropriate icon for a package category * @param string $category Package category * @return string HTML icon markup */ function getCategoryIcon($category) { try { // logActivity('debug', 'Getting icon for category: ' . $category); switch ($category) { case 'umrah': return ''; case 'hajj': return ''; case 'halal_tour': return ''; default: return ''; } } catch (Exception $e) { // logActivity('error', 'Error getting category icon: ' . $e->getMessage()); return ''; } } /** * Format price in Indonesian Rupiah format * @param float $price Price to format * @return string Formatted price */ function formatPrice($price) { try { // logActivity('debug', 'Formatting price: ' . $price); if (!is_numeric($price)) { throw new Exception('Invalid price value'); } return 'Rp ' . number_format($price, 0, ',', '.'); } catch (Exception $e) { // logActivity('error', 'Error formatting price: ' . $e->getMessage()); return 'Rp 0'; } } /** * Format date in Indonesian format * @param string $date Date string * @return string Formatted date */ function formatDate($date) { try { // logActivity('debug', 'Formatting date: ' . $date); $timestamp = strtotime($date); if ($timestamp === false) { throw new Exception('Invalid date format'); } $months = [ 'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember' ]; return date('d', $timestamp) . ' ' . $months[date('n', $timestamp) - 1] . ' ' . date('Y', $timestamp); } catch (Exception $e) { // logActivity('error', 'Error formatting date: ' . $e->getMessage()); return 'Tanggal tidak valid'; } } /** * Get package status label * @param string $status Package status * @return array Label text and class */ function getPackageStatusLabel($status) { try { logActivity('debug', 'Getting status label for: ' . $status); switch ($status) { case 'active': return ['text' => 'Tersedia', 'class' => 'success']; case 'fully_booked': return ['text' => 'Penuh', 'class' => 'warning']; case 'completed': return ['text' => 'Selesai', 'class' => 'info']; case 'cancelled': return ['text' => 'Dibatalkan', 'class' => 'danger']; default: return ['text' => 'Tidak Tersedia', 'class' => 'secondary']; } } catch (Exception $e) { logActivity('error', 'Error getting status label: ' . $e->getMessage()); return ['text' => 'Error', 'class' => 'danger']; } } /** * Check if a package is available for booking * @param array $package Package data * @return bool True if available */ function isPackageAvailable($package) { try { logActivity('debug', 'Checking availability for package ID: ' . $package['id']); // Check status if ($package['status'] !== 'active') { return false; } // Check departure date $departure = strtotime($package['departure_date']); if ($departure < time()) { return false; } // Check capacity if (isset($package['current_bookings']) && isset($package['capacity']) && $package['current_bookings'] >= $package['capacity']) { return false; } return true; } catch (Exception $e) { logActivity('error', 'Error checking package availability: ' . $e->getMessage()); return false; } } /** * Get remaining slots for a package * @param array $package Package data * @return int|string Number of slots or 'Tidak Tersedia' */ function getRemainingSlots($package) { try { logActivity('debug', 'Getting remaining slots for package ID: ' . $package['id']); if (!isset($package['capacity']) || !isset($package['current_bookings'])) { throw new Exception('Missing capacity or booking data'); } $remaining = $package['capacity'] - $package['current_bookings']; return $remaining > 0 ? $remaining : 'Tidak Tersedia'; } catch (Exception $e) { logActivity('error', 'Error getting remaining slots: ' . $e->getMessage()); return 'Error'; } } /** * Calculate package discount if applicable * @param array $package Package data * @return array Discount percentage and final price */ function calculatePackageDiscount($package) { try { logActivity('debug', 'Calculating discount for package ID: ' . $package['id']); $discount = 0; $final_price = $package['price']; // Early bird discount $departure = strtotime($package['departure_date']); $days_until = ceil(($departure - time()) / (60 * 60 * 24)); if ($days_until > 90) { $discount = 10; // 10% off for early birds } // Group discount if (isset($package['group_size']) && $package['group_size'] >= 4) { $discount = max($discount, 5); // 5% off for groups } // Calculate final price if ($discount > 0) { $final_price = $package['price'] * (1 - $discount / 100); } return [ 'discount_percentage' => $discount, 'final_price' => $final_price ]; } catch (Exception $e) { logActivity('error', 'Error calculating package discount: ' . $e->getMessage()); return ['discount_percentage' => 0, 'final_price' => $package['price']]; } } /** * Sanitize input to prevent XSS and SQL injection * @param string $input Input to sanitize * @return string Sanitized input */ function sanitizeInput($input) { global $conn; $input = trim($input); $input = stripslashes($input); $input = htmlspecialchars($input); return mysqli_real_escape_string($conn, $input); } /** * Log user activity * @param string $level Log level (info, warning, error) * @param string $message Log message * @param array $context Additional context data */ // function logActivity($level, $message, $context = []) { // try { // Logger::log($level, $message, $context); // } catch (Exception $e) { // error_log("Error logging activity: " . $e->getMessage()); // } // } /** * Get and clear a flash message * * @param string $key Message key * @return string Message or empty string if not found */ function getFlashMessage($key) { $message = ''; if (isset($_SESSION['flash_' . $key])) { $message = $_SESSION['flash_' . $key]; unset($_SESSION['flash_' . $key]); } return $message; } /** * Set a flash message * * @param string $key Message key * @param string $message Message content */ function setFlashMessage($key, $message) { $_SESSION['flash_' . $key] = $message; } /** * Insert new agent data into the database */ function insertAgentData($userData, $uploadedFiles) { global $conn; try { // Start transaction $conn->begin_transaction(); // Insert into agents table $stmt = $conn->prepare(" INSERT INTO agents ( user_id, name, nik, email, phone, whatsapp, address, city, province, postal_code, business_name, business_address, instagram_url, facebook_url, tiktok_url, type, status ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending' ) "); $stmt->bind_param("isssssssssssssss", $_SESSION['user_id'], $userData['name'], $userData['nik'], $userData['email'], $userData['phone'], $userData['whatsapp'], $userData['address'], $userData['city'], $userData['province'], $userData['postal_code'], $userData['business_name'], $userData['business_address'], $userData['instagram_url'], $userData['facebook_url'], $userData['tiktok_url'], $userData['type'] ); $stmt->execute(); $agentId = $conn->insert_id; // Insert documents foreach ($uploadedFiles as $docType => $filePath) { $stmt = $conn->prepare(" INSERT INTO agent_documents ( agent_id, document_type, file_path, status ) VALUES (?, ?, ?, 'pending') "); $stmt->bind_param("iss", $agentId, $docType, $filePath); $stmt->execute(); } // Update user role to agent $stmt = $conn->prepare("UPDATE users SET role = 'agent' WHERE id = ?"); $stmt->bind_param("i", $_SESSION['user_id']); $stmt->execute(); // Commit transaction $conn->commit(); return true; } catch (Exception $e) { // Rollback transaction on error $conn->rollback(); error_log("Error in insertAgentData: " . $e->getMessage()); return false; } } /** * Check if user is already registered as an agent */ function isUserAgent($userId) { global $conn; $stmt = $conn->prepare("SELECT id FROM agents WHERE user_id = ?"); $stmt->bind_param("i", $userId); $stmt->execute(); $result = $stmt->get_result(); return $result->num_rows > 0; } /** * Get agent status */ function getAgentStatus($userId) { global $conn; $stmt = $conn->prepare("SELECT status FROM agents WHERE user_id = ?"); $stmt->bind_param("i", $userId); $stmt->execute(); $result = $stmt->get_result(); if ($row = $result->fetch_assoc()) { return $row['status']; } return null; } /** * Validate NIK format */ function validateNIK($nik) { return preg_match('/^\d{16}$/', $nik); } /** * Validate file upload */ function validateFileUpload($file, $allowedTypes = ['image/jpeg', 'image/png', 'application/pdf']) { if (!isset($file['error']) || is_array($file['error'])) { return false; } if ($file['error'] !== UPLOAD_ERR_OK) { return false; } if (!in_array($file['type'], $allowedTypes)) { return false; } if ($file['size'] > 5242880) { // 5MB max return false; } return true; } function registerAgent($conn, $user_id, $data, $documents = []) { try { // Start transaction $conn->begin_transaction(); // Generate unique referral code $referral_code = generateUniqueReferralCode($conn); // Prepare whatsapp number $whatsapp = isset($data['whatsapp']) ? preg_replace('/[^0-9]/', '', $data['whatsapp']) : null; // Insert agent data $stmt = $conn->prepare(" INSERT INTO agents ( user_id, referral_code, name, nik, email, phone, whatsapp, nomor_rekening, address, city, province, postal_code, business_name, business_address, instagram_url, facebook_url, tiktok_url, type, status, created_at ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', NOW() ) "); $stmt->bind_param( "isssssssssssssssss", $user_id, $referral_code, $data['name'], $data['nik'], $data['email'], $data['phone'], $whatsapp, $data['nomor_rekening'], $data['address'], $data['city'], $data['province'], $data['postal_code'], $data['business_name'], $data['business_address'], $data['instagram_url'], $data['facebook_url'], $data['tiktok_url'], $data['type'] ); if (!$stmt->execute()) { throw new Exception("Failed to insert agent data"); } $agent_id = $conn->insert_id; // Insert documents if (!empty($documents)) { $stmt = $conn->prepare(" INSERT INTO agent_documents ( agent_id, document_type, file_path, status ) VALUES (?, ?, ?, 'pending') "); foreach ($documents as $docType => $filePath) { $stmt->bind_param("iss", $agent_id, $docType, $filePath); if (!$stmt->execute()) { throw new Exception("Failed to insert document: " . $docType); } } } // Update user role $stmt = $conn->prepare("UPDATE users SET role = 'agent' WHERE id = ?"); $stmt->bind_param("i", $user_id); if (!$stmt->execute()) { throw new Exception("Failed to update user role"); } // Commit transaction $conn->commit(); return true; } catch (Exception $e) { // Rollback on error $conn->rollback(); error_log("Error registering agent: " . $e->getMessage()); return false; } } function generateUniqueReferralCode($conn, $length = 6) { do { // Generate random code $chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // Excluding similar looking characters $code = ''; for ($i = 0; $i < $length; $i++) { $code .= $chars[random_int(0, strlen($chars) - 1)]; } // Check if code exists $stmt = $conn->prepare("SELECT id FROM agents WHERE referral_code = ?"); $stmt->bind_param("s", $code); $stmt->execute(); $result = $stmt->get_result(); } while ($result->num_rows > 0); // Keep generating until we find a unique code return $code; } /** * Calculate and create agent commission for a booking * @param int $booking_id The booking ID * @param int $agent_id The agent ID * @param float $total_amount The total booking amount * @return bool True if successful */ function createAgentCommission($booking_id, $agent_id, $total_amount) { global $conn; try { // Get agent's commission percentage based on their performance $percentage = calculateAgentCommissionPercentage($agent_id); // Calculate commission amount $commission_amount = ($total_amount * $percentage) / 100; // Insert commission record $stmt = $conn->prepare(" INSERT INTO agent_commissions ( agent_id, booking_id, amount, percentage, status ) VALUES (?, ?, ?, ?, 'pending') "); $stmt->bind_param("iidi", $agent_id, $booking_id, $commission_amount, $percentage); return $stmt->execute(); } catch (Exception $e) { error_log("Error creating agent commission: " . $e->getMessage()); return false; } } /** * Calculate agent's commission percentage based on their performance * @param int $agent_id The agent ID * @return float Commission percentage */ function calculateAgentCommissionPercentage($agent_id) { global $conn; try { // Get agent's completed bookings count $stmt = $conn->prepare(" SELECT COUNT(*) as completed_bookings FROM package_bookings WHERE agent_id = ? AND status = 'completed' "); $stmt->bind_param("i", $agent_id); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); $completed_bookings = $result['completed_bookings']; // Calculate percentage based on performance if ($completed_bookings >= 20) { return 5.0; // 5% commission for 20+ completed bookings } elseif ($completed_bookings >= 10) { return 4.0; // 4% commission for 10-19 completed bookings } elseif ($completed_bookings >= 5) { return 3.0; // 3% commission for 5-9 completed bookings } else { return 2.0; // 2% base commission } } catch (Exception $e) { error_log("Error calculating commission percentage: " . $e->getMessage()); return 2.0; // Default to base commission on error } } /** * Get agent's total pending commissions * @param int $agent_id The agent ID * @return float Total pending commission amount */ function getAgentPendingCommissions($agent_id) { global $conn; try { $stmt = $conn->prepare(" SELECT SUM(amount) as total_pending FROM agent_commissions WHERE agent_id = ? AND status = 'pending' "); $stmt->bind_param("i", $agent_id); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); return $result['total_pending'] ?? 0; } catch (Exception $e) { error_log("Error getting pending commissions: " . $e->getMessage()); return 0; } } /** * Get agent's commission history * @param int $agent_id The agent ID * @return array Array of commission records */ function getAgentCommissionHistory($agent_id) { global $conn; try { $stmt = $conn->prepare(" SELECT ac.*, pb.booking_code, pb.customer_name, p.name as package_name, p.departure_date FROM agent_commissions ac JOIN package_bookings pb ON ac.booking_id = pb.id JOIN packages p ON pb.package_id = p.id WHERE ac.agent_id = ? ORDER BY ac.created_at DESC "); $stmt->bind_param("i", $agent_id); $stmt->execute(); return $stmt->get_result()->fetch_all(MYSQLI_ASSOC); } catch (Exception $e) { error_log("Error getting commission history: " . $e->getMessage()); return []; } } /** * Get agent's performance metrics * @param int $agent_id The agent ID * @return array Performance metrics */ function getAgentPerformanceMetrics($agent_id) { global $conn; try { // Get total jamaah (completed bookings participants) $stmt = $conn->prepare(" SELECT SUM(participants) as total_jamaah FROM package_bookings WHERE agent_id = ? AND payment_status = 'completed' "); $stmt->bind_param("i", $agent_id); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); $total_jamaah = $result['total_jamaah'] ?? 0; // Get total commissions $stmt = $conn->prepare(" SELECT SUM(commission_amount) as total_commission FROM agent_commissions WHERE agent_id = ? "); $stmt->bind_param("i", $agent_id); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); $total_commission = $result['total_commission'] ?? 0; // Get active packages (packages with future departure dates) $stmt = $conn->prepare(" SELECT COUNT(DISTINCT p.id) as active_packages FROM packages p JOIN package_bookings b ON p.id = b.package_id WHERE b.agent_id = ? AND p.departure_date > CURDATE() AND p.status = 'active' "); $stmt->bind_param("i", $agent_id); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); $active_packages = $result['active_packages'] ?? 0; // Get conversion rate $stmt = $conn->prepare(" SELECT COUNT(DISTINCT b.id) as total_bookings, COUNT(DISTINCT av.id) as total_visits FROM agents a LEFT JOIN package_bookings b ON b.agent_id = a.id LEFT JOIN agent_page_visits av ON av.agent_id = a.id WHERE a.id = ? "); $stmt->bind_param("i", $agent_id); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); $conversion_rate = $result['total_visits'] > 0 ? round(($result['total_bookings'] / $result['total_visits']) * 100, 2) : 0; // Get monthly performance $stmt = $conn->prepare(" SELECT DATE_FORMAT(b.created_at, '%Y-%m') as month, COUNT(DISTINCT b.id) as total_bookings, SUM(p.price) as revenue FROM package_bookings b JOIN packages p ON b.package_id = p.id WHERE b.agent_id = ? GROUP BY DATE_FORMAT(b.created_at, '%Y-%m') ORDER BY month DESC LIMIT 12 "); $stmt->bind_param("i", $agent_id); $stmt->execute(); $monthly_performance = $stmt->get_result()->fetch_all(MYSQLI_ASSOC); return [ 'total_jamaah' => $total_jamaah, 'total_commission' => $total_commission, 'active_packages' => $active_packages, 'conversion_rate' => $conversion_rate, 'monthly_performance' => $monthly_performance ]; } catch (Exception $e) { error_log("Error getting agent performance metrics: " . $e->getMessage()); return [ 'total_jamaah' => 0, 'total_commission' => 0, 'active_packages' => 0, 'conversion_rate' => 0, 'monthly_performance' => [] ]; } } initSession(); initDatabase();