diff --git a/db/migrations/06_startup_updates.sql b/db/migrations/06_startup_updates.sql new file mode 100644 index 0000000..4978875 --- /dev/null +++ b/db/migrations/06_startup_updates.sql @@ -0,0 +1,11 @@ +-- Migration: Add startup_updates table for founder progress reports +CREATE TABLE IF NOT EXISTS startup_updates ( + id INT AUTO_INCREMENT PRIMARY KEY, + startup_id INT NOT NULL, + founder_id INT NOT NULL, + title VARCHAR(255) NOT NULL, + content TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (startup_id) REFERENCES startups(id) ON DELETE CASCADE, + FOREIGN KEY (founder_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/startup_details.php b/startup_details.php index 145731c..5af3208 100644 --- a/startup_details.php +++ b/startup_details.php @@ -27,146 +27,188 @@ $stmt = db()->prepare("SELECT * FROM funding_rounds WHERE startup_id = ? AND sta $stmt->execute([$startup_id]); $activeRound = $stmt->fetch(); -// Handle Founder Actions +// Handle Actions $error = ''; $success = ''; -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $user['role'] === 'founder' && $startup['founder_id'] == $user_id) { +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { $action = $_POST['action']; - $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 - } 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 = ?"); + // 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]); - - // 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."; + $success = "Funding round finished early. No new investments allowed."; $activeRound = null; // Refresh for UI - } catch (Exception $e) { - db()->rollBack(); - $error = "Cancellation failed: " . $e->getMessage(); - } - } elseif ($action === 'start_new_round') { - $newTarget = (float)($_POST['new_target'] ?? 0); - if ($newTarget < 50) { - $error = "Minimum target for a new round is £50."; - } elseif ($activeRound) { - $error = "An active round already exists."; - } else { - $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(); - } - } -} - -// Handle Investment (Investor Only) -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'invest') { - if ($user['role'] !== 'investor') { - $error = "Founders cannot make investments."; - } elseif (!$activeRound) { - $error = "There is no active funding round for this startup."; - } else { - $amount = (float)($_POST['amount'] ?? 0); - if ($amount < 50) { - $error = "Minimum investment is £50."; - } else { + } elseif ($action === 'cancel_round' && $activeRound && $activeRound['id'] == $round_id) { 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]); + // 1. Cancel round + $stmt = db()->prepare("UPDATE funding_rounds SET status = 'Cancelled' WHERE id = ?"); + $stmt->execute([$round_id]); - // 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']]); + // 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(); - // 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 = "
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); - } + foreach ($investmentsToRefund as $inv) { + // Mark investment as refunded + $upd = db()->prepare("UPDATE investments SET status = 'Refunded' WHERE id = ?"); + $upd->execute([$inv['id']]); - // Email Founder - $stmt = db()->prepare("SELECT email, full_name FROM users WHERE id = ?"); - $stmt->execute([$startup['founder_id']]); - $founderUser = $stmt->fetch(); - if ($founderUser) { - $subject = "Funding Goal Reached: " . $startup['name']; - $html = "Your funding round for " . htmlspecialchars($startup['name']) . " has reached its goal of £" . number_format($updatedRound['funding_goal']) . ".
"; - $text = "Congratulations! Your funding round for " . $startup['name'] . " has reached its goal of £" . number_format($updatedRound['funding_goal']) . "."; - MailService::sendMail($founderUser['email'], $subject, $html, $text); - } + // 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 = "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(); + $success = "Funding round cancelled and investors refunded."; + $activeRound = null; // Refresh for UI } catch (Exception $e) { db()->rollBack(); - $error = "Investment failed: " . $e->getMessage(); + $error = "Cancellation failed: " . $e->getMessage(); + } + } elseif ($action === 'start_new_round') { + $newTarget = (float)($_POST['new_target'] ?? 0); + if ($newTarget < 50) { + $error = "Minimum target for a new round is £50."; + } elseif ($activeRound) { + $error = "An active round already exists."; + } else { + $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(); + } + } 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 for this startup + $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.startup_id = ? AND i.status = 'approved'"); + $stmt->execute([$startup_id]); + $investorsToNotify = $stmt->fetchAll(); + + // 3. Notify them + foreach ($investorsToNotify as $invUser) { + // DB Notification + $notif = db()->prepare("INSERT INTO notifications (user_id, content) VALUES (?, ?)"); + $notif->execute([$invUser['id'], "New progress update from " . $startup['name'] . ": " . $title]); + + // Email Notification + $subject = "New Update: " . $startup['name']; + $emailHtml = "Hi " . htmlspecialchars($invUser['full_name']) . ", " . htmlspecialchars($startup['name']) . " has posted a new progress update:
" . nl2br(htmlspecialchars($content)) . "
"; + $emailText = "New Update: " . $title . "\n\nHi " . $invUser['full_name'] . ", " . $startup['name'] . " has posted a new progress update: " . $content; + MailService::sendMail($invUser['email'], $subject, $emailHtml, $emailText); + } + + db()->commit(); + $success = "Update posted and " . count($investorsToNotify) . " investors notified."; + } catch (Exception $e) { + db()->rollBack(); + $error = "Failed to post update: " . $e->getMessage(); + } + } + } + } + + // Investor Actions + if ($action === 'invest') { + if ($user['role'] !== 'investor') { + $error = "Founders cannot make investments."; + } elseif (!$activeRound) { + $error = "There is no active funding round for this startup."; + } else { + $amount = (float)($_POST['amount'] ?? 0); + if ($amount < 50) { + $error = "Minimum investment is £50."; + } 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 = "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(); + if ($founderUser) { + $subject = "Funding Goal Reached: " . $startup['name']; + $html = "Your funding round for " . htmlspecialchars($startup['name']) . " has reached its goal of £" . number_format($updatedRound['funding_goal']) . ".
"; + $text = "Congratulations! Your funding round for " . $startup['name'] . " has reached its goal of £" . number_format($updatedRound['funding_goal']) . "."; + 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(); + } catch (Exception $e) { + db()->rollBack(); + $error = "Investment failed: " . $e->getMessage(); + } } } } @@ -235,6 +277,55 @@ $platformName = defined('PLATFORM_NAME') ? PLATFORM_NAME : 'Gatsby';= htmlspecialchars($startup['description']) ?>
+ +No updates have been posted yet.
+ + += htmlspecialchars($upd['content']) ?>
+