diff --git a/db/migrations/09_unmatch_and_block.sql b/db/migrations/09_unmatch_and_block.sql new file mode 100644 index 0000000..8254ee5 --- /dev/null +++ b/db/migrations/09_unmatch_and_block.sql @@ -0,0 +1,13 @@ +-- Migration: Unmatch and Block Features +CREATE TABLE IF NOT EXISTS blocked_users ( + id INT AUTO_INCREMENT PRIMARY KEY, + blocker_id INT NOT NULL, + blocked_id INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (blocker_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (blocked_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE KEY unique_block (blocker_id, blocked_id) +); + +-- Add status to matches to allow unmatching without deleting history +ALTER TABLE matches ADD COLUMN status ENUM('active', 'unmatched') DEFAULT 'active'; diff --git a/discover.php b/discover.php index c7b50b3..0021e12 100644 --- a/discover.php +++ b/discover.php @@ -20,39 +20,55 @@ if (!$user) { $user_role = $user['role']; +// Blocked users filter +$stmt = db()->prepare("SELECT blocked_id FROM blocked_users WHERE blocker_id = ?"); +$stmt->execute([$current_user_id]); +$blocked_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + +$stmt = db()->prepare("SELECT blocker_id FROM blocked_users WHERE blocked_id = ?"); +$stmt->execute([$current_user_id]); +$blocked_by_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + +$all_blocked = array_unique(array_merge($blocked_ids, $blocked_by_ids)); +$placeholders = empty($all_blocked) ? "0" : implode(',', array_fill(0, count($all_blocked), '?')); + // Leaderboard: Most Followed This Week -$stmt = db()->prepare(" +$sql = " SELECT s.id, s.name, s.description, u.full_name as founder_name, COUNT(sf.id) as followers_count FROM startups s JOIN users u ON s.founder_id = u.id JOIN startup_followers sf ON s.id = sf.startup_id WHERE sf.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) + AND u.id NOT IN ($placeholders) GROUP BY s.id ORDER BY followers_count DESC LIMIT 10 -"); -$stmt->execute(); +"; +$stmt = db()->prepare($sql); +$stmt->execute($all_blocked ?: []); $mostFollowed = $stmt->fetchAll(); // Leaderboard: Most Funded This Week -$stmt = db()->prepare(" +$sql = " SELECT s.id, s.name, s.description, u.full_name as founder_name, SUM(i.amount) as funded_amount FROM startups s JOIN users u ON s.founder_id = u.id JOIN investments i ON s.id = i.startup_id WHERE i.status = 'approved' AND i.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) + AND u.id NOT IN ($placeholders) GROUP BY s.id ORDER BY funded_amount DESC LIMIT 10 -"); -$stmt->execute(); +"; +$stmt = db()->prepare($sql); +$stmt->execute($all_blocked ?: []); $mostFunded = $stmt->fetchAll(); // Search logic $q = $_GET['q'] ?? ''; $browseStartups = []; -$where = "s.status = 'public'"; -$params = []; +$where = "s.status = 'public' AND u.id NOT IN ($placeholders)"; +$params = $all_blocked ?: []; if ($q) { $where .= " AND (s.name LIKE ? OR s.description LIKE ? OR u.full_name LIKE ?)"; $params[] = "%$q%"; @@ -85,10 +101,8 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby'; .hub-header { padding: 60px 0 40px; text-align: center; } .hub-header h1 { font-size: 48px; font-weight: 800; background: var(--gradient-primary); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 12px; } .hub-header p { color: var(--text-secondary); font-size: 18px; } - .leaderboard-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-bottom: 60px; } @media (max-width: 900px) { .leaderboard-grid { grid-template-columns: 1fr; } } - .lb-card { background: var(--card-bg); border: 1px solid var(--border-color); border-radius: 24px; padding: 30px; backdrop-filter: blur(20px); } .lb-title { display: flex; align-items: center; gap: 12px; margin-bottom: 25px; font-size: 20px; font-weight: 700; } .lb-item { display: flex; align-items: center; gap: 15px; padding: 15px; background: rgba(255,255,255,0.03); border-radius: 16px; margin-bottom: 12px; transition: all 0.3s; text-decoration: none; color: inherit; } @@ -97,7 +111,6 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby'; .rank-1 { background: gold; color: #000; } .rank-2 { background: silver; color: #000; } .rank-3 { background: #cd7f32; color: #000; } - .startup-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 25px; } .startup-card { background: var(--card-bg); border: 1px solid var(--border-color); border-radius: 24px; padding: 30px; transition: all 0.3s; } .startup-card:hover { transform: translateY(-5px); border-color: var(--accent-blue); } @@ -210,4 +223,4 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby'; - + \ No newline at end of file diff --git a/messages.php b/messages.php index 6f96d09..3d72d83 100644 --- a/messages.php +++ b/messages.php @@ -14,7 +14,7 @@ $user = $stmt->fetch(); $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby'; -// Handle Accept/Reject Actions +// Handle Actions (Accept/Reject/Unmatch/Block) if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && isset($_POST['other_user_id'])) { $other_id = (int)$_POST['other_user_id']; $action = $_POST['action']; @@ -22,11 +22,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && isset($_ if ($action === 'accept') { $stmt = db()->prepare("UPDATE messages SET status = 'accepted' WHERE sender_id = ? AND receiver_id = ? AND status = 'pending'"); $stmt->execute([$other_id, $user_id]); - - // Notify the sender $stmt = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $stmt->execute([$other_id, "Your message request to " . $user['full_name'] . " has been accepted."]); - header("Location: messages.php?chat_with=$other_id"); exit; } elseif ($action === 'reject') { @@ -34,13 +31,38 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && isset($_ $stmt->execute([$other_id, $user_id]); header("Location: messages.php"); exit; + } elseif ($action === 'unmatch') { + $stmt = db()->prepare("UPDATE matches SET status = 'unmatched' WHERE (user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?)"); + $stmt->execute([$user_id, $other_id, $other_id, $user_id]); + header("Location: messages.php"); + exit; + } elseif ($action === 'block') { + $stmt = db()->prepare("INSERT IGNORE INTO blocked_users (blocker_id, blocked_id) VALUES (?, ?)"); + $stmt->execute([$user_id, $other_id]); + // Also unmatch if they were matched + $stmt = db()->prepare("UPDATE matches SET status = 'unmatched' WHERE (user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?)"); + $stmt->execute([$user_id, $other_id, $other_id, $user_id]); + header("Location: messages.php"); + exit; } } -// Fetch Matches for Founders +// Fetch Blocked Users to filter them out +$stmt = db()->prepare("SELECT blocked_id FROM blocked_users WHERE blocker_id = ?"); +$stmt->execute([$user_id]); +$blocked_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + +$stmt = db()->prepare("SELECT blocker_id FROM blocked_users WHERE blocked_id = ?"); +$stmt->execute([$user_id]); +$blocked_by_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + +$all_blocked = array_unique(array_merge($blocked_ids, $blocked_by_ids)); +$placeholders = empty($all_blocked) ? "0" : implode(',', array_fill(0, count($all_blocked), '?')); + +// Fetch Matches for Founders (active only, not blocked) $matches = []; if ($user['role'] === 'founder') { - $stmt = db()->prepare(" + $sql = " SELECT m.*, u.full_name as match_name, u.id as match_id, @@ -50,21 +72,28 @@ if ($user['role'] === 'founder') { JOIN users u ON (m.user1_id = u.id OR m.user2_id = u.id) WHERE (m.user1_id = ? OR m.user2_id = ?) AND u.id != ? - "); - $stmt->execute([$user_id, $user_id, $user_id]); + AND m.status = 'active' + AND u.id NOT IN ($placeholders) + "; + $stmt = db()->prepare($sql); + $params = array_merge([$user_id, $user_id, $user_id], $all_blocked ?: []); + $stmt->execute($params); $matches = $stmt->fetchAll(); } -// Fetch Conversations (accepted or pending from others) -$stmt = db()->prepare(" +// Fetch Conversations (accepted or pending from others, excluding blocked) +$sql = " SELECT DISTINCT u.id as other_user_id, u.full_name as other_user_name, u.role as other_role FROM messages m JOIN users u ON (m.sender_id = u.id OR m.receiver_id = u.id) WHERE ((m.sender_id = ? OR m.receiver_id = ?) AND u.id != ?) AND (m.status = 'accepted' OR (m.status = 'pending' AND m.receiver_id = ?)) + AND u.id NOT IN ($placeholders) ORDER BY (SELECT MAX(created_at) FROM messages WHERE (sender_id = m.sender_id AND receiver_id = m.receiver_id) OR (sender_id = m.receiver_id AND receiver_id = m.sender_id)) DESC -"); -$stmt->execute([$user_id, $user_id, $user_id, $user_id]); +"; +$stmt = db()->prepare($sql); +$params = array_merge([$user_id, $user_id, $user_id, $user_id], $all_blocked ?: []); +$stmt->execute($params); $conversations = $stmt->fetchAll(); $active_chat_id = isset($_GET['chat_with']) ? (int)$_GET['chat_with'] : null; @@ -73,14 +102,20 @@ $active_chat_user = null; $chat_messages = []; $can_reply = true; $needs_acceptance = false; +$is_currently_matched = false; if ($active_chat_id) { + // Check if blocked + if (in_array($active_chat_id, $all_blocked)) { + header("Location: messages.php"); + exit; + } + $stmt = db()->prepare("SELECT id, full_name, role FROM users WHERE id = ?"); $stmt->execute([$active_chat_id]); $active_chat_user = $stmt->fetch(); if ($active_chat_user) { - // Fetch messages (only show accepted or pending ones) $stmt = db()->prepare(" SELECT * FROM messages WHERE ((sender_id = ? AND receiver_id = ?) @@ -91,21 +126,19 @@ if ($active_chat_id) { $stmt->execute([$user_id, $active_chat_id, $active_chat_id, $user_id]); $chat_messages = $stmt->fetchAll(); - // Check if there are any accepted messages or a match $stmt = db()->prepare("SELECT 1 FROM messages WHERE ((sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)) AND status = 'accepted'"); $stmt->execute([$user_id, $active_chat_id, $active_chat_id, $user_id]); $has_accepted = (bool)$stmt->fetchColumn(); - $stmt = db()->prepare("SELECT 1 FROM matches WHERE (user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?)"); + $stmt = db()->prepare("SELECT 1 FROM matches WHERE ((user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?)) AND status = 'active'"); $stmt->execute([$user_id, $active_chat_id, $active_chat_id, $user_id]); - $has_match = (bool)$stmt->fetchColumn(); + $is_currently_matched = (bool)$stmt->fetchColumn(); - // If receiver of pending messages, can't reply yet $stmt = db()->prepare("SELECT 1 FROM messages WHERE sender_id = ? AND receiver_id = ? AND status = 'pending'"); $stmt->execute([$active_chat_id, $user_id]); $has_pending_from_other = (bool)$stmt->fetchColumn(); - if (!$has_accepted && !$has_match && $has_pending_from_other) { + if (!$has_accepted && !$is_currently_matched && $has_pending_from_other) { $can_reply = false; $needs_acceptance = true; } @@ -116,29 +149,22 @@ if ($active_chat_id) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && $active_chat_id && $can_reply) { $content = trim($_POST['content']); if (!empty($content)) { - // Determine status $status = 'accepted'; - - // Check if a match exists - $stmt = db()->prepare("SELECT 1 FROM matches WHERE (user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?)"); + $stmt = db()->prepare("SELECT 1 FROM matches WHERE ((user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?)) AND status = 'active'"); $stmt->execute([$user_id, $active_chat_id, $active_chat_id, $user_id]); $is_match = (bool)$stmt->fetchColumn(); - // Check if previous accepted exists $stmt = db()->prepare("SELECT 1 FROM messages WHERE ((sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)) AND status = 'accepted'"); $stmt->execute([$user_id, $active_chat_id, $active_chat_id, $user_id]); $is_accepted = (bool)$stmt->fetchColumn(); if (!$is_match && !$is_accepted) { - // Check startup status if ($startup_id) { $stmt = db()->prepare("SELECT status, name FROM startups WHERE id = ?"); $stmt->execute([$startup_id]); $s = $stmt->fetch(); if ($s && $s['status'] === 'private') { $status = 'pending'; - - // Notify receiver about the request $stmt = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $stmt->execute([$active_chat_id, "New message request from " . $user['full_name'] . " regarding " . $s['name'] . "."]); } @@ -199,7 +225,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && $active width: 40px; height: 40px; border-radius: 12px; background: var(--gradient-primary); display: flex; align-items: center; justify-content: center; font-weight: 700; color: #fff; } - .chat-header { padding: 15px 25px; border-bottom: 1px solid var(--border-color); display: flex; align-items: center; gap: 15px; } + .chat-header { padding: 15px 25px; border-bottom: 1px solid var(--border-color); display: flex; align-items: center; justify-content: space-between; } .chat-messages { flex: 1; overflow-y: auto; padding: 25px; display: flex; flex-direction: column; gap: 15px; } .msg-bubble { max-width: 70%; padding: 12px 18px; border-radius: 18px; font-size: 14px; line-height: 1.5; @@ -219,6 +245,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && $active padding: 20px; background: rgba(255, 204, 0, 0.1); border-top: 1px solid rgba(255, 204, 0, 0.2); text-align: center; } + + /* Action Menu */ + .action-menu { position: relative; } + .action-menu-content { + display: none; position: absolute; right: 0; top: 100%; + background: var(--surface-color); border: 1px solid var(--border-color); + border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); + z-index: 100; min-width: 150px; + } + .action-menu:hover .action-menu-content { display: block; } + .action-link { + display: block; padding: 10px 15px; color: var(--text-secondary); + text-decoration: none; font-size: 13px; transition: all 0.2s; + background: none; border: none; width: 100%; text-align: left; cursor: pointer; + } + .action-link:hover { background: rgba(255,255,255,0.05); color: #fff; } + .action-link.danger { color: #ff3b30; } + .action-link.danger:hover { background: rgba(255, 59, 48, 0.1); } @@ -253,7 +297,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && $active
New Matches
prepare("SELECT 1 FROM messages WHERE sender_id = ? AND receiver_id = ? AND status = 'pending'"); $stmt->execute([$conv['other_user_id'], $user_id]); $isPending = (bool)$stmt->fetchColumn(); @@ -298,10 +340,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && $active
-
-
-
-
+
+
+
+
+
+
+
+
+ +
+
+ + + +
+
+ + + +
+
@@ -309,9 +368,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && $active

This is the start of your conversation with .

- -

Regarding their private startup. Your first message will be a request.

-
diff --git a/partners.php b/partners.php index 07f6d4d..8724c5c 100644 --- a/partners.php +++ b/partners.php @@ -17,39 +17,38 @@ if (!$user || $user['role'] !== 'founder') { exit; } -// Onboarding check if (!$user['onboarding_completed']) { header("Location: founder_onboarding.php"); exit; } -// Skill-based Matching Helper +// Blocked users filter +$stmt = db()->prepare("SELECT blocked_id FROM blocked_users WHERE blocker_id = ?"); +$stmt->execute([$current_user_id]); +$blocked_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + +$stmt = db()->prepare("SELECT blocker_id FROM blocked_users WHERE blocked_id = ?"); +$stmt->execute([$current_user_id]); +$blocked_by_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + +$all_blocked = array_unique(array_merge($blocked_ids, $blocked_by_ids)); +$placeholders = empty($all_blocked) ? "0" : implode(',', array_fill(0, count($all_blocked), '?')); + function calculateMatchScore($me, $them) { $score = 0; - - // 1. My preferred skills vs Their skills $myNeeds = array_filter(array_map('trim', explode(',', strtolower($me['preferred_co_founder_skills'] ?? '')))); $theirSkills = array_filter(array_map('trim', explode(',', strtolower($them['skills'] ?? '')))); $skillMatches = array_intersect($myNeeds, $theirSkills); $score += count($skillMatches) * 10; - - // 2. Their preferred skills vs My skills $theirNeeds = array_filter(array_map('trim', explode(',', strtolower($them['preferred_co_founder_skills'] ?? '')))); $mySkills = array_filter(array_map('trim', explode(',', strtolower($me['skills'] ?? '')))); $reciprocalMatches = array_intersect($theirNeeds, $mySkills); $score += count($reciprocalMatches) * 10; - - // 3. Industry overlap $myIndustries = array_filter(array_map('trim', explode(',', strtolower($me['startup_industries'] ?? '')))); $theirIndustries = array_filter(array_map('trim', explode(',', strtolower($them['startup_industries'] ?? '')))); $industryMatches = array_intersect($myIndustries, $theirIndustries); $score += count($industryMatches) * 5; - - // 4. University match (bonus) - if (!empty($me['university']) && $me['university'] === $them['university']) { - $score += 5; - } - + if (!empty($me['university']) && $me['university'] === $them['university']) { $score += 5; } return [ 'total' => $score, 'skillMatches' => array_values($skillMatches), @@ -57,27 +56,23 @@ function calculateMatchScore($me, $them) { ]; } -// Handle Swipe Logic if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'swipe') { header('Content-Type: application/json'); $swiped_id = (int)$_POST['swiped_id']; - $direction = $_POST['direction']; // 'like' or 'dislike' - + $direction = $_POST['direction']; if ($swiped_id > 0 && in_array($direction, ['like', 'dislike'])) { $stmt = db()->prepare("INSERT IGNORE INTO swipes (swiper_id, swiped_id, direction) VALUES (?, ?, ?)"); $stmt->execute([$current_user_id, $swiped_id, $direction]); - $isMatch = false; if ($direction === 'like') { $stmt = db()->prepare("SELECT id FROM swipes WHERE swiper_id = ? AND swiped_id = ? AND direction = 'like'"); $stmt->execute([$swiped_id, $current_user_id]); if ($stmt->fetch()) { $isMatch = true; - $stmt = db()->prepare("INSERT IGNORE INTO matches (user1_id, user2_id) VALUES (?, ?)"); + $stmt = db()->prepare("INSERT INTO matches (user1_id, user2_id, status) VALUES (?, ?, 'active') ON DUPLICATE KEY UPDATE status = 'active'"); $u1 = min($current_user_id, $swiped_id); $u2 = max($current_user_id, $swiped_id); $stmt->execute([$u1, $u2]); - $stmt = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $stmt->execute([$swiped_id, "You have a new match! Start a conversation now."]); $stmt->execute([$current_user_id, "You have a new match! Start a conversation now."]); @@ -90,26 +85,32 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' exit; } -// Fetch matches -$stmt = db()->prepare(" +// Fetch active matches not blocked +$sql = " SELECT u.*, m.created_at as matched_at FROM matches m JOIN users u ON (m.user1_id = u.id OR m.user2_id = u.id) - WHERE (m.user1_id = ? OR m.user2_id = ?) AND u.id != ? + WHERE (m.user1_id = ? OR m.user2_id = ?) + AND u.id != ? + AND m.status = 'active' + AND u.id NOT IN ($placeholders) ORDER BY m.created_at DESC -"); -$stmt->execute([$current_user_id, $current_user_id, $current_user_id]); +"; +$stmt = db()->prepare($sql); +$stmt->execute(array_merge([$current_user_id, $current_user_id, $current_user_id], $all_blocked ?: [])); $matches = $stmt->fetchAll(); -// Fetch swipe candidates with scoring -$stmt = db()->prepare(" +// Fetch swipe candidates +$sql = " SELECT * FROM users WHERE role = 'founder' AND id != ? AND onboarding_completed = 1 AND id NOT IN (SELECT swiped_id FROM swipes WHERE swiper_id = ?) + AND id NOT IN ($placeholders) LIMIT 50 -"); -$stmt->execute([$current_user_id, $current_user_id]); +"; +$stmt = db()->prepare($sql); +$stmt->execute(array_merge([$current_user_id, $current_user_id], $all_blocked ?: [])); $allCandidates = $stmt->fetchAll(); foreach ($allCandidates as &$c) { @@ -121,8 +122,8 @@ $swipeCandidates = array_slice($allCandidates, 0, 10); // Fetch partners for Browse view $search = $_GET['q'] ?? ''; -$where = "role = 'founder' AND id != ? AND onboarding_completed = 1 AND id NOT IN (SELECT swiped_id FROM swipes WHERE swiper_id = ?)"; -$params = [$current_user_id, $current_user_id]; +$where = "role = 'founder' AND id != ? AND onboarding_completed = 1 AND id NOT IN (SELECT swiped_id FROM swipes WHERE swiper_id = ?) AND id NOT IN ($placeholders)"; +$params = array_merge([$current_user_id, $current_user_id], $all_blocked ?: []); if ($search) { $where .= " AND (full_name LIKE ? OR skills LIKE ? OR university LIKE ? OR startup_industries LIKE ?)"; $params[] = "%$search%"; @@ -154,138 +155,25 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby'; @@ -514,72 +368,38 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby'; document.getElementById('tab-swipe').classList.toggle('active', tab === 'swipe'); document.getElementById('tab-browse').classList.toggle('active', tab === 'browse'); } - function handleSwipe(direction) { const stack = document.getElementById('card-stack'); const cards = stack.querySelectorAll('.swipe-card'); if (cards.length === 0) return; - const topCard = cards[cards.length - 1]; const swipedId = topCard.getAttribute('data-id'); - - // Animation const rotate = direction === 'like' ? 30 : -30; const x = direction === 'like' ? 1000 : -1000; topCard.style.transform = `translateX(${x}px) rotate(${rotate}deg)`; topCard.style.opacity = '0'; - - // POST request const fd = new FormData(); fd.append('action', 'swipe'); fd.append('swiped_id', swipedId); fd.append('direction', direction); - fetch('partners.php', { method: 'POST', body: fd }) .then(r => r.json()) .then(data => { - if (data.match) { - document.getElementById('match-modal').style.display = 'flex'; - } + if (data.match) { document.getElementById('match-modal').style.display = 'flex'; } setTimeout(() => { topCard.remove(); - if (stack.querySelectorAll('.swipe-card').length === 0) { - location.reload(); - } + if (stack.querySelectorAll('.swipe-card').length === 0) { location.reload(); } }, 500); }); } - - function closeMatch() { - document.getElementById('match-modal').style.display = 'none'; - } + function closeMatch() { document.getElementById('match-modal').style.display = 'none'; } diff --git a/startup_details.php b/startup_details.php index 37023e3..8e829d4 100644 --- a/startup_details.php +++ b/startup_details.php @@ -23,6 +23,14 @@ if (!$startup) { exit; } +// Check if blocked or blocker +$stmt = db()->prepare("SELECT 1 FROM blocked_users WHERE (blocker_id = ? AND blocked_id = ?) OR (blocker_id = ? AND blocked_id = ?)"); +$stmt->execute([$user_id, $startup['founder_id'], $startup['founder_id'], $user_id]); +if ($stmt->fetchColumn()) { + header('Location: discover.php'); + exit; +} + // Check if current user is following $stmt = db()->prepare("SELECT 1 FROM startup_followers WHERE startup_id = ? AND user_id = ?"); $stmt->execute([$startup_id, $user_id]); @@ -40,7 +48,7 @@ $success = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { $action = $_POST['action']; - // Follow/Unfollow Actions (Available to all logged in users except the founder of this startup) + // Follow/Unfollow Actions if ($action === 'follow' && $startup['founder_id'] != $user_id) { $stmt = db()->prepare("INSERT IGNORE INTO startup_followers (startup_id, user_id) VALUES (?, ?)"); $stmt->execute([$startup_id, $user_id]); @@ -56,37 +64,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { // Founder Actions if ($user['role'] === 'founder' && $startup['founder_id'] == $user_id) { $round_id = isset($_POST['round_id']) ? (int)$_POST['round_id'] : 0; - if ($action === 'finish_round' && $activeRound && $activeRound['id'] == $round_id) { $stmt = db()->prepare("UPDATE funding_rounds SET status = 'Closed' WHERE id = ?"); $stmt->execute([$round_id]); $success = "Funding round finished early. No new investments allowed."; - $activeRound = null; // Refresh for UI + $activeRound = null; } elseif ($action === 'cancel_round' && $activeRound && $activeRound['id'] == $round_id) { db()->beginTransaction(); try { - // 1. Cancel round $stmt = db()->prepare("UPDATE funding_rounds SET status = 'Cancelled' WHERE id = ?"); $stmt->execute([$round_id]); - - // 2. Refund all investors for this round $stmt = db()->prepare("SELECT * FROM investments WHERE funding_round_id = ? AND status = 'approved'"); $stmt->execute([$round_id]); $investmentsToRefund = $stmt->fetchAll(); - foreach ($investmentsToRefund as $inv) { - // Mark investment as refunded $upd = db()->prepare("UPDATE investments SET status = 'Refunded' WHERE id = ?"); $upd->execute([$inv['id']]); - - // Notify investor $notif = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $notif->execute([$inv['investor_id'], "The funding round for " . $startup['name'] . " has been cancelled. Your investment of £" . number_format($inv['amount']) . " has been refunded."]); } - db()->commit(); $success = "Funding round cancelled and investors refunded."; - $activeRound = null; // Refresh for UI + $activeRound = null; } catch (Exception $e) { db()->rollBack(); $error = "Cancellation failed: " . $e->getMessage(); @@ -101,7 +100,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { $stmt = db()->prepare("INSERT INTO funding_rounds (startup_id, funding_goal, status) VALUES (?, ?, 'Active')"); $stmt->execute([$startup_id, $newTarget]); $success = "New funding round launched!"; - // Refresh active round $stmt = db()->prepare("SELECT * FROM funding_rounds WHERE startup_id = ? AND status = 'Active' LIMIT 1"); $stmt->execute([$startup_id]); $activeRound = $stmt->fetch(); @@ -109,17 +107,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { } elseif ($action === 'post_update') { $title = trim($_POST['update_title'] ?? ''); $content = trim($_POST['update_content'] ?? ''); - if (empty($title) || empty($content)) { $error = "Update title and content are required."; } else { db()->beginTransaction(); try { - // 1. Insert Update $stmt = db()->prepare("INSERT INTO startup_updates (startup_id, founder_id, title, content) VALUES (?, ?, ?, ?)"); $stmt->execute([$startup_id, $user_id, $title, $content]); - - // 2. Identify All Unique Investors AND Followers for this startup $stmt = db()->prepare(" SELECT DISTINCT u.id, u.email, u.full_name FROM users u @@ -129,20 +123,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { "); $stmt->execute([$startup_id, $startup_id]); $peopleToNotify = $stmt->fetchAll(); - - // 3. Notify them foreach ($peopleToNotify as $targetUser) { - // DB Notification $notif = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $notif->execute([$targetUser['id'], "New progress update from " . $startup['name'] . ": " . $title]); - - // Email Notification $subject = "New Update: " . $startup['name']; $emailHtml = "

New Update: " . htmlspecialchars($title) . "

Hi " . htmlspecialchars($targetUser['full_name']) . ", " . htmlspecialchars($startup['name']) . " has posted a new progress update:


" . nl2br(htmlspecialchars($content)) . "

"; $emailText = "New Update: " . $title . "\n\nHi " . $targetUser['full_name'] . ", " . $startup['name'] . " has posted a new progress update: " . $content; MailService::sendMail($targetUser['email'], $subject, $emailHtml, $emailText); } - db()->commit(); $success = "Update posted and " . count($peopleToNotify) . " backers/followers notified."; } catch (Exception $e) { @@ -166,53 +154,33 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { } else { db()->beginTransaction(); try { - // Use funding_round_id for the investment $stmt = db()->prepare("INSERT INTO investments (investor_id, startup_id, funding_round_id, amount, status) VALUES (?, ?, ?, ?, 'approved')"); $stmt->execute([$_SESSION['user_id'], $startup_id, $activeRound['id'], $amount]); - - // Update funding_raised in funding_rounds (the dedicated pot) $stmt = db()->prepare("UPDATE funding_rounds SET funding_raised = funding_raised + ? WHERE id = ?"); $stmt->execute([$amount, $activeRound['id']]); - - // Update legacy field for backward compatibility in listing pages if needed $stmt = db()->prepare("UPDATE startups SET funding_raised = funding_raised + ? WHERE id = ?"); $stmt->execute([$amount, $startup_id]); - $stmt = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $stmt->execute([$startup['founder_id'], "New investment of £" . number_format($amount) . " in " . $startup['name'] . "!"]); - - // Check if goal reached for automated notification system $stmt = db()->prepare("SELECT * FROM funding_rounds WHERE id = ?"); $stmt->execute([$activeRound['id']]); $updatedRound = $stmt->fetch(); - if ($updatedRound['funding_raised'] >= $updatedRound['funding_goal']) { - // Update status to 'Closed' $stmt = db()->prepare("UPDATE funding_rounds SET status = 'Closed' WHERE id = ?"); $stmt->execute([$updatedRound['id']]); - - // Notify Founder (DB) $stmt = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $stmt->execute([$startup['founder_id'], "Congratulations! The funding round for " . $startup['name'] . " has reached its goal of £" . number_format($updatedRound['funding_goal']) . "!"]); - - // Notify All Investors for this round (DB + Email) $stmt = db()->prepare("SELECT DISTINCT u.id, u.email, u.full_name FROM investments i JOIN users u ON i.investor_id = u.id WHERE i.funding_round_id = ? AND i.status = 'approved'"); $stmt->execute([$updatedRound['id']]); $investorsToNotify = $stmt->fetchAll(); - foreach ($investorsToNotify as $invUser) { - // DB Notification $stmt = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); $stmt->execute([$invUser['id'], "Great news! The funding round for " . $startup['name'] . " that you invested in has reached its goal!"]); - - // Email Notification $subject = "Funding Goal Reached for " . $startup['name']; $html = "

Goal Reached!

Hi " . htmlspecialchars($invUser['full_name']) . ", the funding round for " . htmlspecialchars($startup['name']) . " has successfully reached its goal. Thank you for being a part of this journey!

"; $text = "Goal Reached! Hi " . $invUser['full_name'] . ", the funding round for " . $startup['name'] . " has successfully reached its goal. Thank you for being a part of this journey!"; MailService::sendMail($invUser['email'], $subject, $html, $text); } - - // Email Founder $stmt = db()->prepare("SELECT email, full_name FROM users WHERE id = ?"); $stmt->execute([$startup['founder_id']]); $founderUser = $stmt->fetch(); @@ -223,10 +191,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { MailService::sendMail($founderUser['email'], $subject, $html, $text); } } - db()->commit(); $success = "Investment successful! You've backed the current round."; - // Refresh active round data $stmt = db()->prepare("SELECT * FROM funding_rounds WHERE id = ?"); $stmt->execute([$activeRound['id']]); $activeRound = $stmt->fetch(); @@ -314,7 +280,6 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby';

-

Public Updates

@@ -322,7 +287,6 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby';
- - prepare("SELECT * FROM startup_updates WHERE startup_id = ? ORDER BY created_at DESC"); $stmt->execute([$startup_id]); $updates = $stmt->fetchAll(); ?> -

No updates have been posted yet.

@@ -367,9 +329,7 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby';

Active Funding Round

- 0) ? min(100, ($activeRound['funding_raised'] / $activeRound['funding_goal']) * 100) : 0; - ?> + 0) ? min(100, ($activeRound['funding_raised'] / $activeRound['funding_goal']) * 100) : 0; ?>
@@ -378,7 +338,6 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby'; £ raised Target: £
-
@@ -400,7 +359,6 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby';
-

Minimum investment is £50. Your support helps students grow.

@@ -408,18 +366,15 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby';

No Active Funding Round

-

This startup is not currently raising funds, or the previous round has closed.

- -
-
- -
+
Send Message - -
Founder account deleted. Venture is community-managed.
@@ -490,26 +441,9 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby';