diff --git a/assets/css/custom.css b/assets/css/custom.css index 42bf88b..81c2ced 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,8 +1,256 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; +/** + * Custom Styles for Compañía + */ + +:root { + --brand-primary: #C05761; /* Deep Rose */ + --brand-secondary: #E3C16F; /* Champagne Gold */ + --brand-bg: #FDFBF7; /* Cream */ + --brand-text: #333333; /* Charcoal */ + --brand-text-muted: #6c757d; + --brand-white: #ffffff; + --brand-like: #28a745; + --brand-dislike: #dc3545; + + --border-radius-lg: 16px; + --border-radius-md: 10px; + --font-serif: 'Playfair Display', serif; + --font-sans: 'Inter', sans-serif; + --shadow-soft: 0 4px 20px rgba(0,0,0,0.05); + --shadow-card: 0 10px 30px rgba(0,0,0,0.08); + --shadow-hover: 0 12px 35px rgba(192, 87, 97, 0.15); } -.hero { - padding: 6rem 1rem; - background-color: #f8f9fa; +body { + background-color: var(--brand-bg); + color: var(--brand-text); + font-family: var(--font-sans); + line-height: 1.6; + padding-top: 80px; } + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-serif); + font-weight: 700; + color: var(--brand-primary); +} + +.navbar { + background-color: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + box-shadow: 0 2px 10px rgba(0,0,0,0.03); + padding: 0.75rem 0; +} + +.navbar-brand { + font-family: var(--font-serif); + font-size: 1.5rem; + color: var(--brand-primary) !important; + font-weight: 700; +} + +.nav-link { + font-weight: 500; + color: var(--brand-text) !important; + transition: color 0.2s; +} + +.nav-link:hover, .nav-link.active { + color: var(--brand-primary) !important; +} + +.btn { + border-radius: 50px; + font-weight: 600; + padding: 0.5rem 1.5rem; + transition: all 0.3s ease; +} + +.btn-primary { + background-color: var(--brand-primary); + border-color: var(--brand-primary); +} + +.btn-primary:hover { + background-color: #a6444d; + border-color: #a6444d; + transform: translateY(-2px); + box-shadow: 0 4px 10px rgba(192, 87, 97, 0.2); +} + +.btn-outline-primary { + color: var(--brand-primary); + border-color: var(--brand-primary); +} + +.btn-outline-primary:hover { + background-color: var(--brand-primary); + color: var(--brand-white); +} + + +/* --- Swiping UI --- */ +#swipe-container { + perspective: 1000px; +} + +.card-swipe { + position: absolute; + width: 100%; + height: 100%; + border-radius: var(--border-radius-lg); + background: var(--brand-white); + box-shadow: var(--shadow-card); + will-change: transform, opacity; + transition: all 0.3s ease-in-out; + display: flex; + flex-direction: column; +} + +@keyframes swipe-left-anim { + from { transform: translateX(0) rotate(0); opacity: 1; } + to { transform: translateX(-150%) rotate(-20deg); opacity: 0; } +} + +@keyframes swipe-right-anim { + from { transform: translateX(0) rotate(0); opacity: 1; } + to { transform: translateX(150%) rotate(20deg); opacity: 0; } +} + +.swipe-left { + animation: swipe-left-anim 0.5s forwards ease-in; +} + +.swipe-right { + animation: swipe-right-anim 0.5s forwards ease-in; +} + +.companion-card .card-body { + flex-grow: 1; + padding-bottom: 0; +} + +.companion-img-wrapper { + position: relative; + padding-top: 100%; + background-color: #f0f0f0; + border-top-left-radius: var(--border-radius-lg); + border-top-right-radius: var(--border-radius-lg); + overflow: hidden; +} + +.companion-img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +.verified-badge { + position: absolute; + top: 12px; + right: 12px; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(5px); + color: var(--brand-primary); + padding: 5px 10px; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 700; + display: flex; + align-items: center; + gap: 4px; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); +} + +.companion-name { + font-size: 1.5rem; + margin-bottom: 0.25rem; +} + +.companion-meta { + font-size: 0.9rem; + color: var(--brand-text-muted); + margin-bottom: 1rem; +} + +.rate-badge { + font-weight: 700; + color: var(--brand-primary); + font-size: 1.1rem; +} + +.card-footer-swipe { + border-top: none; + background: transparent; + padding: 1rem; +} + +.btn-swipe { + width: 100%; + height: 60px; + border: 1px solid #eee; + border-radius: 0; + background: var(--brand-white); + color: #ccc; + font-size: 1.5rem; + transition: all 0.3s ease; +} + +.btn-swipe:hover { + background-color: #f7f7f7; + transform: scale(1.1); +} + +.btn-dislike { + border-bottom-left-radius: var(--border-radius-lg); +} + +.btn-dislike:hover { + color: var(--brand-dislike); +} + +.btn-like { + border-left: none; + border-bottom-right-radius: var(--border-radius-lg); +} + +.btn-like:hover { + color: var(--brand-like); +} + +/* Match Notification Modal */ +#matchNotificationModal .modal-content { + border-radius: var(--border-radius-lg); + background: linear-gradient(135deg, var(--brand-primary), #a6444d); + color: var(--brand-white); +} + +#matchNotificationModal .modal-title { + color: var(--brand-white); + font-size: 2rem; +} + +#matchNotificationModal .btn-primary { + background-color: var(--brand-white); + color: var(--brand-primary); + border-color: var(--brand-white); +} + +/* Matches Modal */ +#matchesModal .modal-content { + background-color: var(--brand-bg); + border-radius: var(--border-radius-lg); +} + +/* Footer */ +footer { + background-color: #fff; + border-top: 1px solid #eee; + padding: 2rem 0; + color: var(--brand-text-muted); + font-size: 0.9rem; +} \ No newline at end of file diff --git a/assets/images/avatar_placeholder.svg b/assets/images/avatar_placeholder.svg new file mode 100644 index 0000000..a3a56db --- /dev/null +++ b/assets/images/avatar_placeholder.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/js/main.js b/assets/js/main.js index e69de29..c3f1236 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -0,0 +1,50 @@ +function handleSwipe(action, swiped_user_id) { + const card = document.querySelector('.card-swipe'); + if (!card) return; + + // 1. Add class to trigger animation + const animationClass = action === 'like' ? 'swipe-right' : 'swipe-left'; + card.classList.add(animationClass); + + // 2. Wait for animation to end, then fetch + card.addEventListener('animationend', () => { + fetch('swipe.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + body: JSON.stringify({ + action: action, + swiped_user_id: swiped_user_id + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + // 3. Replace the card with the next one + const swipeContainer = document.getElementById('swipe-container'); + swipeContainer.innerHTML = data.next_companion_html; + + // 4. If it's a match, show the notification modal + if (data.match) { + const matchModal = new bootstrap.Modal(document.getElementById('matchNotificationModal')); + const matchBody = document.getElementById('match-notification-body'); + // Note: This relies on a global `translations` object or a dedicated function. + // For simplicity, we hardcode a message here, but a robust solution would use the `t()` function. + matchBody.textContent = `You and ${data.matched_user_name} have liked each other.`; + matchModal.show(); + } + } else { + // Handle errors, e.g., show a message to the user + console.error('Swipe failed:', data.error); + // Re-enable card if swipe failed + card.classList.remove(animationClass); + } + }) + .catch(error => { + console.error('Error during swipe:', error); + card.classList.remove(animationClass); + }); + }, { once: true }); +} diff --git a/db/setup.php b/db/setup.php new file mode 100644 index 0000000..6cdbbe8 --- /dev/null +++ b/db/setup.php @@ -0,0 +1,113 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // 1. Users Table + $sql_users = "CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + role ENUM('client', 'companion') NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )"; + $pdo->exec($sql_users); + echo "- Table 'users' is ready.\n"; + + // 2. User Profiles Table + $sql_profiles = "CREATE TABLE IF NOT EXISTS user_profiles ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL UNIQUE, + name VARCHAR(100) NOT NULL, + age INT, + city VARCHAR(100), + bio TEXT, + profile_photo_path VARCHAR(255) DEFAULT 'assets/images/avatar_placeholder.svg', + hourly_rate DECIMAL(10, 2), + is_verified TINYINT(1) DEFAULT 0, + rating DECIMAL(3, 2) DEFAULT 5.00, + activities TEXT, -- Can store comma-separated values or JSON + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + )"; + $pdo->exec($sql_profiles); + echo "- Table 'user_profiles' is ready.\n"; + + // 3. Swipes Table + $sql_swipes = "CREATE TABLE IF NOT EXISTS swipes ( + id INT AUTO_INCREMENT PRIMARY KEY, + swiper_user_id INT NOT NULL, + swiped_user_id INT NOT NULL, + swipe_type ENUM('like', 'dislike') NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (swiper_user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (swiped_user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE KEY (swiper_user_id, swiped_user_id) + )"; + $pdo->exec($sql_swipes); + echo "- Table 'swipes' is ready.\n"; + + // 4. Matches Table + $sql_matches = "CREATE TABLE IF NOT EXISTS matches ( + id INT AUTO_INCREMENT PRIMARY KEY, + user1_id INT NOT NULL, + user2_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user1_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (user2_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE KEY (user1_id, user2_id) + )"; + $pdo->exec($sql_matches); + echo "- Table 'matches' is ready.\n"; + + + // -- SEEDING DATA (if tables are empty) -- + $stmt = $pdo->query("SELECT COUNT(*) FROM users"); + if ($stmt->fetchColumn() == 0) { + echo "Seeding initial data...\n"; + + // Hash for a default password, e.g., 'password123' + $default_password_hash = password_hash('password123', PASSWORD_DEFAULT); + + // Companions + $companions_to_seed = [ + [1, 'sofia@email.com', 'Sofia', 28, 'San José', 45.00, 'Art lover and coffee enthusiast. I know the best hidden cafes in Amón.', 1, 4.9, 'assets/images/avatar_placeholder.svg'], + [2, 'mateo@email.com', 'Mateo', 32, 'San José', 50.00, 'Local history buff. Let\'s walk through the city center.', 1, 4.8, 'assets/images/avatar_placeholder.svg'], + [3, 'elena@email.com', 'Elena', 25, 'Escazú', 60.00, 'Foodie and wine taster. Available for elegant dinners.', 1, 5.0, 'assets/images/avatar_placeholder.svg'], + [4, 'alejandro@email.com', 'Alejandro', 29, 'San Pedro', 40.00, 'Musician and movie buff. Great for concerts.', 0, 4.7, 'assets/images/avatar_placeholder.svg'] + ]; + + $user_sql = "INSERT INTO users (id, email, password_hash, role) VALUES (?, ?, ?, 'companion')"; + $profile_sql = "INSERT INTO user_profiles (user_id, name, age, city, hourly_rate, bio, is_verified, rating, profile_photo_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + $user_stmt = $pdo->prepare($user_sql); + $profile_stmt = $pdo->prepare($profile_sql); + + foreach ($companions_to_seed as $c) { + $user_stmt->execute([$c[0], $c[1], $default_password_hash]); + $profile_stmt->execute([$c[0], $c[2], $c[3], $c[4], $c[5], $c[6], $c[7], $c[8], $c[9]]); + } + + // A client user for testing + $client_user_sql = "INSERT INTO users (email, password_hash, role) VALUES ('client@email.com', ?, 'client')"; + $pdo->prepare($client_user_sql)->execute([$default_password_hash]); + $client_user_id = $pdo->lastInsertId(); + + $client_profile_sql = "INSERT INTO user_profiles (user_id, name, age, city, bio) VALUES (?, 'Test Client', 30, 'San José', 'Looking for great company.')"; + $pdo->prepare($client_profile_sql)->execute([$client_user_id]); + + echo "- Seeded companion and client users.\n"; + + } else { + echo "- Users table already has data. Skipping seed.\n"; + } + + echo "Database setup completed successfully!\n"; + +} catch (PDOException $e) { + die("DB Setup Error: " . $e->getMessage() . "\n"); +} \ No newline at end of file diff --git a/includes/helpers.php b/includes/helpers.php new file mode 100644 index 0000000..69e5ccc --- /dev/null +++ b/includes/helpers.php @@ -0,0 +1,126 @@ + [ + 'page_title' => 'Compañía — Platonic Companion Booking', + 'nav_companions' => 'Companions', + 'nav_how_it_works' => 'How it Works', + 'nav_matches' => 'My Matches', + 'nav_logout' => 'Logout', + 'hero_title_1' => 'Find your perfect', + 'hero_title_2' => 'platonic', + 'hero_title_3' => 'companion.', + 'hero_lead' => 'Book vetted companions for dinner dates, movie nights, or city tours. Safe, secure, and strictly platonic.', + 'search_where' => 'Where?', + 'search_city_any' => 'Any City', + 'search_activity' => 'Activity / Keyword', + 'search_placeholder' => 'e.g. Dinner, Coffee, Casual Dates...', + 'search_button' => 'Search Companions', + 'featured_companions' => 'Discover Companions', + 'no_companions_found' => 'No new companions found. Check back later!', + 'clear_filters' => 'Clear Filters', + 'verified' => 'Verified', + 'rate_per_hour' => '/hr', + 'like_button' => 'Like', + 'dislike_button' => 'Dislike', + 'matches_title' => 'Your Matches', + 'matches_empty' => 'No matches yet. Keep swiping!', + 'match_modal_title' => 'It\'s a Match!', + 'match_modal_body' => 'You and {name} have liked each other.', + 'match_modal_button' => 'Keep Swiping', + 'footer_copyright' => 'Compañía. All rights reserved.', + 'footer_coc' => 'Strict Code of Conduct: Sexual services are strictly prohibited.', + 'footer_safety' => 'Read our Safety Guidelines', + ], + 'es' => [ + 'page_title' => 'Compañía — Reserva de Compañeros Platónicos', + 'nav_companions' => 'Compañeros', + 'nav_how_it_works' => 'Cómo Funciona', + 'nav_matches' => 'Mis Matches', + 'nav_logout' => 'Cerrar Sesión', + 'hero_title_1' => 'Encuentra tu compañero', + 'hero_title_2' => 'platónico', + 'hero_title_3' => 'perfecto.', + 'hero_lead' => 'Reserva compañeros verificados para cenas, noches de cine o tours por la ciudad. Seguro, protegido y estrictamente platónico.', + 'search_where' => '¿Dónde?', + 'search_city_any' => 'Cualquier ciudad', + 'search_activity' => 'Actividad / Palabra Clave', + 'search_placeholder' => 'Ej: Cena, Café, Citas Casuales...', + 'search_button' => 'Buscar Compañeros', + 'featured_companions' => 'Descubre Compañeros', + 'no_companions_found' => 'No se encontraron nuevos compañeros. ¡Vuelve más tarde!', + 'clear_filters' => 'Limpiar Filtros', + 'verified' => 'Verificado', + 'rate_per_hour' => '/hora', + 'like_button' => 'Me gusta', + 'dislike_button' => 'No me gusta', + 'matches_title' => 'Tus Matches', + 'matches_empty' => 'Aún no tienes matches. ¡Sigue deslizando!', + 'match_modal_title' => '¡Es un Match!', + 'match_modal_body' => 'A ti y a {name} se gustaron.', + 'match_modal_button' => 'Seguir Deslizando', + 'footer_copyright' => 'Compañía. Todos los derechos reservados.', + 'footer_coc' => 'Código de Conducta Estricto: Los servicios sexuales están estrictamente prohibidos.', + 'footer_safety' => 'Lee nuestras Guías de Seguridad', + ], +]; + +function t($key, $replacements = []) { + global $lang, $translations; + // Fallback to session language if not passed explicitly + $current_lang = $lang ?? $_SESSION['lang'] ?? 'en'; + $text = $translations[$current_lang][$key] ?? $key; + foreach ($replacements as $k => $v) { + $text = str_replace('{'.$k.'}', $v, $text); + } + return $text; +} + +function render_companion_card($companion) { + if (!$companion) { + return '

' . t('no_companions_found') . '

'; + } + + $companion['img'] = $companion['profile_photo_path'] ?? 'assets/images/avatar_placeholder.svg'; + $verified_badge = $companion['is_verified'] ? '
' . t('verified') . '
' : ''; + + return ' +
+
+ '.htmlspecialchars($companion['name']).' + '.$verified_badge.' +
+
+
+
+

'.htmlspecialchars($companion['name']).', '.$companion['age'].'

+
+ + '.htmlspecialchars($companion['city']).' +
+
+
+
'.number_format((float)($companion['hourly_rate'] ?? 0), 0).t('rate_per_hour').'
+ ★ '.$companion['rating'].' +
+
+

'.htmlspecialchars($companion['bio']).'

+
+ +
'; +} + diff --git a/index.php b/index.php index 7205f3d..543c700 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,188 @@ $current_user_id]; + +if ($filterCity) { + $sql .= " AND p.city LIKE :city"; + $params[':city'] = '%' . $filterCity . '%'; +} +if ($filterActivity) { + $sql .= " AND p.bio LIKE :activity"; + $params[':activity'] = '%' . $filterActivity . '%'; +} + +$sql .= " ORDER BY RAND() LIMIT 1"; + +$stmt = $pdo->prepare($sql); +$stmt->execute($params); +$companion = $stmt->fetch(PDO::FETCH_ASSOC); + +// Fetch matches for the logged-in user +$stmt = $pdo->prepare(" + SELECT p.* + FROM matches m + JOIN user_profiles p ON (m.user1_id = p.user_id OR m.user2_id = p.user_id) + WHERE (m.user1_id = :current_user_id OR m.user2_id = :current_user_id) + AND p.user_id != :current_user_id +"); +$stmt->execute(['current_user_id' => $current_user_id]); +$matches = $stmt->fetchAll(PDO::FETCH_ASSOC); +foreach ($matches as &$m) { + $m['img'] = $m['profile_photo_path'] ?? 'assets/images/avatar_placeholder.svg'; +} +unset($m); + +$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Find your perfect vetted companion for platonic experiences.'; +$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; + ?> - + - - - New Style - - - - - - - - - - - - - - - - - - - + + + <?= t('page_title') ?> + + + + + + + + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+ + + + +
+
+

+ +
+ +
+
+
+ + + -
- + + + + + + + + + + - + \ No newline at end of file diff --git a/login.php b/login.php new file mode 100644 index 0000000..a6c06df --- /dev/null +++ b/login.php @@ -0,0 +1,67 @@ +prepare('SELECT * FROM users WHERE email = ?'); + $stmt->execute([$email]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password_hash'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['user_role'] = $user['role']; + header('Location: index.php'); + exit; + } else { + $error = 'Invalid email or password.'; + } + } +} +?> + + + + + + Login + + + + +
+
+
+

Login

+ +
+ +
+
+ + +
+
+ + +
+
+ +
+
+
+

Don't have an account? Register here

+
+
+
+
+ + diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..95db42c --- /dev/null +++ b/logout.php @@ -0,0 +1,6 @@ +prepare('SELECT id FROM users WHERE email = ?'); + $stmt->execute([$email]); + if ($stmt->fetch()) { + $error = 'An account with this email already exists.'; + } else { + $password_hash = password_hash($password, PASSWORD_DEFAULT); + $pdo->prepare('INSERT INTO users (email, password_hash, role) VALUES (?, ?, ?)') + ->execute([$email, $password_hash, $role]); + + $user_id = $pdo->lastInsertId(); + + // Create a basic profile + $pdo->prepare('INSERT INTO user_profiles (user_id, name, bio) VALUES (?, ?, ?)') + ->execute([$user_id, 'New User', 'Please update your bio.']); + + $success = 'Registration successful! You can now log in.'; + } + } +} +?> + + + + + + Register + + + + +
+
+
+

Register

+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+

Already have an account? Login here

+
+
+
+
+ + diff --git a/swipe.php b/swipe.php new file mode 100644 index 0000000..8e63e1d --- /dev/null +++ b/swipe.php @@ -0,0 +1,109 @@ + false, 'match' => false, 'error' => null]; + +if (!isset($_SESSION['user_id'])) { + $response['error'] = 'User not authenticated.'; + echo json_encode($response); + exit; +} + +$current_user_id = $_SESSION['user_id']; +$data = json_decode(file_get_contents('php://input'), true); + +$swiped_user_id = $data['swiped_user_id'] ?? null; +$action = $data['action'] ?? null; // 'like' or 'dislike' + +if (!$swiped_user_id || !in_array($action, ['like', 'dislike'])) { + $response['error'] = 'Invalid input.'; + echo json_encode($response); + exit; +} + +try { + $pdo = db(); + + // 1. Record the swipe action + $stmt = $pdo->prepare( + "INSERT INTO swipes (swiper_user_id, swiped_user_id, swipe_type) + VALUES (:swiper_user_id, :swiped_user_id, :swipe_type)" + ); + $stmt->execute([ + ':swiper_user_id' => $current_user_id, + ':swiped_user_id' => $swiped_user_id, + ':swipe_type' => $action + ]); + + // 2. If it was a 'like', check for a mutual match + if ($action === 'like') { + $stmt = $pdo->prepare( + "SELECT COUNT(*) FROM swipes + WHERE swiper_user_id = :swiped_user_id AND swiped_user_id = :current_user_id AND swipe_type = 'like'" + ); + $stmt->execute([ + ':swiped_user_id' => $swiped_user_id, + ':current_user_id' => $current_user_id + ]); + $is_mutual = (int)$stmt->fetchColumn() > 0; + + if ($is_mutual) { + // 3. It's a match! Insert into matches table. + $user1 = min($current_user_id, $swiped_user_id); + $user2 = max($current_user_id, $swiped_user_id); + + $stmt = $pdo->prepare( + "INSERT INTO matches (user1_id, user2_id) + SELECT :user1_id, :user2_id + WHERE NOT EXISTS ( + SELECT 1 FROM matches + WHERE (user1_id = :user1_id AND user2_id = :user2_id) OR (user1_id = :user2_id AND user2_id = :user1_id) + )" + ); + $stmt->execute([ + ':user1_id' => $user1, + ':user2_id' => $user2 + ]); + + $response['match'] = true; + + // Fetch the matched user's name for the notification + $stmt = $pdo->prepare("SELECT name FROM user_profiles WHERE user_id = :swiped_user_id"); + $stmt->execute(['swiped_user_id' => $swiped_user_id]); + $matched_user = $stmt->fetch(PDO::FETCH_ASSOC); + $response['matched_user_name'] = $matched_user['name'] ?? 'Someone'; + } + } + + $response['success'] = true; + +} catch (PDOException $e) { + $response['error'] = "Database error: " . $e->getMessage(); +} + +// Finally, get the next companion to show +$stmt = $pdo->prepare( + "SELECT p.* + FROM user_profiles p + JOIN users u ON p.user_id = u.id + WHERE u.role = 'companion' + AND p.user_id != :current_user_id + AND p.user_id NOT IN ( + SELECT swiped_user_id FROM swipes WHERE swiper_user_id = :current_user_id + ) + ORDER BY RAND() + LIMIT 1" +); +$stmt->execute(['current_user_id' => $current_user_id]); +$next_companion = $stmt->fetch(PDO::FETCH_ASSOC); + +$response['next_companion_html'] = render_companion_card($next_companion); + +echo json_encode($response); \ No newline at end of file