36716-vm/profile.php
2025-12-07 05:00:42 +00:00

406 lines
22 KiB
PHP

<?php
session_start();
require_once 'db/config.php';
// Fetch settings
$settings_stmt = db()->query('SELECT setting_key, setting_value FROM settings');
$settings = $settings_stmt->fetchAll(PDO::FETCH_KEY_PAIR);
$coach_mode = $settings['coach_mode'] ?? 'multi';
$coach_id = $_GET['id'] ?? null;
$coach = null;
$error_message = '';
$reviews = [];
$average_rating = 0;
if ($coach_id) {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM coaches WHERE id = ?");
$stmt->execute([$coach_id]);
$coach = $stmt->fetch();
if (!$coach) {
$error_message = 'Coach not found.';
} else {
$stmt = $pdo->prepare("SELECT r.*, c.name as client_name FROM reviews r JOIN clients c ON r.client_id = c.id WHERE r.coach_id = ? ORDER BY r.created_at DESC");
$stmt->execute([$coach_id]);
$reviews = $stmt->fetchAll();
$portfolio_stmt = $pdo->prepare("SELECT * FROM coach_portfolio_items WHERE coach_id = ? ORDER BY created_at DESC");
$portfolio_stmt->execute([$coach_id]);
$portfolio_items = $portfolio_stmt->fetchAll();
if (count($reviews) > 0) {
$total_rating = 0;
foreach ($reviews as $review) {
$total_rating += $review['rating'];
}
$average_rating = round($total_rating / count($reviews), 1);
}
}
} catch (PDOException $e) {
$error_message = 'Error fetching coach details: ' . $e->getMessage();
}
} else {
$error_message = 'No coach ID specified.';
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $coach ? htmlspecialchars($coach['name']) : 'Coach Profile'; ?> - CoachConnect</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vanilla-calendar-pro@2.9.6/build/vanilla-calendar.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body class="bg-gray-50">
<header class="bg-white shadow-md">
<nav class="container mx-auto px-6 py-4 flex justify-between items-center">
<a href="index.php" class="text-2xl font-bold text-gray-800">CoachConnect</a>
<div class="flex space-x-4">
<a href="index.php" class="text-gray-600 hover:text-blue-500">Home</a>
<?php if ($coach_mode === 'multi'): ?>
<a href="coaches.php" class="text-gray-600 hover:text-blue-500">Coaches</a>
<?php endif; ?>
<?php if (isset($_SESSION['user_id'])): ?>
<a href="dashboard.php" class="text-gray-600 hover:text-blue-500">Dashboard</a>
<a href="messages.php" class="text-gray-600 hover:text-blue-500">Messages</a>
<a href="logout.php" class="text-gray-600 hover:text-blue-500">Logout</a>
<?php else: ?>
<a href="login.php" class="text-gray-600 hover:text-blue-500">Login</a>
<a href="register-client.php" class="text-gray-600 hover:text-blue-500">Sign Up</a>
<a href="register-coach.php" class="text-gray-600 hover:text-blue-500">Become a Coach</a>
<?php endif; ?>
</div>
</nav>
</header>
<main class="container mx-auto px-6 py-12">
<?php if (isset($_GET['error']) && $_GET['error'] === 'no_package'): ?>
<div class="max-w-4xl mx-auto bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg shadow-md mt-8" role="alert">
<h3 class="font-bold">Booking Error</h3>
<p>You must purchase a package with this coach before you can book a session. Please select one of the packages below.</p>
</div>
<?php endif; ?>
<?php if ($coach): ?>
<section class="max-w-4xl mx-auto bg-white p-8 rounded-lg shadow-md">
<div class="flex flex-col md:flex-row items-center">
<div class="md:w-1/3 text-center mb-6 md:mb-0">
<img class="w-48 h-48 rounded-full mx-auto shadow-lg" src="<?php echo htmlspecialchars($coach['photo_url'] ?: 'https://i.pravatar.cc/300?img=' . $coach['id']); ?>" alt="<?php echo htmlspecialchars($coach['name']); ?>">
</div>
<div class="md:w-2/3 md:pl-8">
<h1 class="text-4xl font-bold text-gray-800 mb-2"><?php echo htmlspecialchars($coach['name']); ?></h1>
<p class="text-xl text-blue-600 font-semibold mb-4"><?php echo htmlspecialchars($coach['specialties']); ?></p>
<div class="text-gray-600 leading-relaxed"><?php echo nl2br(htmlspecialchars($coach['bio'])); ?></div>
<?php if (isset($_SESSION['user_id']) && isset($_SESSION['user_type']) && $_SESSION['user_type'] === 'client'): ?>
<a href="messages.php?user_id=<?php echo $coach['id']; ?>&user_type=coach" class="mt-4 inline-block bg-blue-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-blue-700">Message</a>
<?php endif; ?>
</div>
</div>
</section>
<?php if (!empty($portfolio_items)):
<section class="max-w-4xl mx-auto bg-white p-8 rounded-lg shadow-md mt-8">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Portfolio</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<?php foreach ($portfolio_items as $item): ?>
<div class="rounded-lg overflow-hidden shadow-lg">
<img src="<?php echo htmlspecialchars($item['url']); ?>" alt="<?php echo htmlspecialchars($item['caption']); ?>" class="w-full h-48 object-cover">
<?php if (!empty($item['caption'])) : ?>
<div class="p-4">
<p class="text-gray-700"><?php echo htmlspecialchars($item['caption']); ?></p>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php
$package_stmt = db()->prepare('SELECT * FROM service_packages WHERE coach_id = ?');
$package_stmt->execute([$coach_id]);
$packages = $package_stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<?php if (!empty($packages)) : ?>
<section class="max-w-4xl mx-auto bg-white p-8 rounded-lg shadow-md mt-8">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Coaching Packages</h2>
<div class="space-y-4">
<?php foreach ($packages as $package): ?>
<?php
$items_stmt = db()->prepare('SELECT SUM(quantity) as total_sessions FROM package_service_items WHERE package_id = ? AND service_type IN ("one_on_one", "group_session")');
$items_stmt->execute([$package['id']]);
$items_result = $items_stmt->fetch();
$total_sessions = $items_result['total_sessions'] ?? 0;
?>
<div class="border rounded-lg p-4">
<h4 class="font-bold text-lg"><?= htmlspecialchars($package['name']) ?></h4>
<p class="text-gray-600 text-sm mb-2"><?= htmlspecialchars($package['description']) ?></p>
<div class="flex justify-between items-center">
<div>
<span class="font-bold text-blue-600">
<?php if ($package['payment_type'] === 'one_time'): ?>
$<?= htmlspecialchars(number_format($package['price'], 2)) ?>
<?php else: ?>
$<?= htmlspecialchars(number_format($package['price'], 2)) ?> / <?= htmlspecialchars($package['payment_plan_interval']) ?> for <?= htmlspecialchars($package['payment_plan_installments']) ?> installments
<?php endif; ?>
</span>
<?php if ($total_sessions > 0): ?>
<span class="text-gray-600 text-sm">for <?= htmlspecialchars($total_sessions) ?> sessions</span>
<?php endif; ?>
</div>
<a href="purchase-package.php?package_id=<?= $package['id'] ?>" class="inline-block bg-green-500 text-white font-bold py-2 px-4 rounded hover:bg-green-600 transition duration-300">Purchase</a>
</div>
<div class="text-sm text-gray-500 mt-2">
<span>Type: <?= htmlspecialchars(ucfirst(str_replace('_', ' ', $package['type']))) ?></span>
<?php if($package['client_limit']): ?>
| <span>Client Limit: <?= htmlspecialchars($package['client_limit']) ?></span>
<?php endif; ?>
<?php if($package['start_date']): ?>
| <span>Starts: <?= htmlspecialchars(date('M j, Y', strtotime($package['start_date']))) ?></span>
<?php endif; ?>
<?php if($package['end_date']): ?>
| <span>Ends: <?= htmlspecialchars(date('M j, Y', strtotime($package['end_date']))) ?></span>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php
if (isset($_SESSION['user_id']) && $_SESSION['user_role'] === 'client') {
$client_id = $_SESSION['user_id'];
$packages_stmt = $pdo->prepare("SELECT cp.*, sp.name FROM client_packages cp JOIN service_packages sp ON cp.package_id = sp.id WHERE cp.client_id = ? AND sp.coach_id = ? AND cp.sessions_remaining > 0");
$packages_stmt->execute([$client_id, $coach_id]);
$client_packages = $packages_stmt->fetchAll();
if ($client_packages) {
echo '<section class="max-w-4xl mx-auto bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 rounded-lg shadow-md mt-8" role="alert">';
echo '<h3 class="font-bold">You have active packages!</h3>';
foreach ($client_packages as $pkg) {
echo '<p>You have ' . htmlspecialchars($pkg['sessions_remaining']) . ' sessions remaining for the \'' . htmlspecialchars($pkg['name']) . '\' package. These will be used automatically when you book a session.</p>';
}
echo '</section>';
}
}
?>
<?php if (isset($_SESSION['user_id']) && $_SESSION['user_role'] === 'client'): ?>
<section class="max-w-4xl mx-auto bg-white p-8 rounded-lg shadow-md mt-8">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Select a Date</h2>
<div id="calendar" class="mb-4"></div>
<h3 class="text-xl font-bold text-gray-800 mb-2">Available Time Slots</h3>
<div id="time-slots" class="flex flex-wrap gap-2"></div>
<form action="book-session.php" method="POST" id="booking-form" class="hidden mt-4">
<input type="hidden" name="coach_id" value="<?php echo $coach['id']; ?>">
<div class="mb-4">
<label for="booking_time" class="block text-gray-700 font-bold mb-2">Selected Date and Time:</label>
<input type="datetime-local" id="booking_time" name="booking_time" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" required readonly>
</div>
<div class="mb-4">
<label class="block text-gray-700 font-bold mb-2">
<input type="checkbox" id="recurring" name="recurring" class="mr-2 leading-tight">Book recurring sessions
</label>
</div>
<div id="recurring-options" class="hidden mb-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label for="frequency" class="block text-gray-700 font-bold mb-2">Frequency:</label>
<select id="frequency" name="frequency" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline">
<option value="weekly">Weekly</option>
<option value="bi-weekly">Bi-weekly</option>
</select>
</div>
<div>
<label for="recurrences" class="block text-gray-700 font-bold mb-2">Number of sessions:</label>
<input type="number" id="recurrences" name="recurrences" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" min="1" value="1">
</div>
</div>
</div>
<button type="submit" class="bg-blue-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-blue-700">Request Booking</button>
</form>
</section>
<?php endif; ?>
<section class="max-w-4xl mx-auto bg-white p-8 rounded-lg shadow-md mt-8">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Reviews</h2>
<?php if (count($reviews) > 0): ?>
<div class="mb-6">
<h3 class="text-xl font-bold text-gray-800">Average Rating: <?php echo $average_rating; ?> / 5</h3>
<div class="flex items-center mt-2">
<?php for ($i = 1; $i <= 5; $i++): ?>
<svg class="w-6 h-6 <?php echo $i <= $average_rating ? 'text-yellow-400' : 'text-gray-300'; ?>" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z"/>
</svg>
<?php endfor; ?>
</div>
</div>
<div class="space-y-6">
<?php foreach ($reviews as $review): ?>
<div class="border-t border-gray-200 pt-6">
<div class="flex items-center mb-2">
<p class="font-bold text-gray-800"><?php echo htmlspecialchars($review['client_name']); ?></p>
<span class="mx-2 text-gray-400">&bull;</span>
<div class="flex items-center">
<?php for ($i = 1; $i <= 5; $i++): ?>
<svg class="w-5 h-5 <?php echo $i <= $review['rating'] ? 'text-yellow-400' : 'text-gray-300'; ?>" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z"/>
</svg>
<?php endfor; ?>
</div>
</div>
<p class="text-gray-600"><?php echo nl2br(htmlspecialchars($review['review'])); ?></p>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p>No reviews yet.</p>
<?php endif; ?>
</section>
<?php else: ?>
<section class="text-center">
<h1 class="text-3xl font-bold text-red-500">Error</h1>
<p class="text-xl text-gray-600 mt-4"><?php echo htmlspecialchars($error_message); ?></p>
<a href="index.php" class="bg-blue-600 text-white font-bold py-3 px-8 rounded-lg hover:bg-blue-700 mt-8 inline-block">Back to Home</a>
</section>
<?php endif; ?>
</main>
<footer class="bg-gray-800 text-white py-6 mt-12">
<div class="container mx-auto px-6 text-center">
<p>&copy; <?php echo date("Y"); ?> CoachConnect. All rights reserved.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/vanilla-calendar-pro@2.9.6/build/vanilla-calendar.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const coachId = <?php echo $coach['id']; ?>;
const calendarElement = document.getElementById('calendar');
const timeSlotsElement = document.getElementById('time-slots');
const bookingForm = document.getElementById('booking-form');
const bookingTimeInput = document.getElementById('booking_time');
if (!calendarElement) return;
let availableSlots = [];
const calendar = new VanillaCalendar(calendarElement, {
settings: {
selection: {
day: 'single',
},
visibility: {
daysOutside: false,
},
},
actions: {
clickDay(e, self) {
updateAvailableTimes(self.selectedDates[0]);
},
update(self) {
const year = self.viewYear;
const month = self.viewMonth;
const startDate = new Date(year, month, 1);
const endDate = new Date(year, month + 1, 0);
fetchAvailability(startDate, endDate);
}
}
});
calendar.init();
async function fetchAvailability(startDate, endDate) {
const startStr = startDate.toISOString().split('T')[0];
const endStr = endDate.toISOString().split('T')[0];
const response = await fetch(`api/availability.php?coach_id=${coachId}&start=${startStr}&end=${endStr}`);
const data = await response.json();
const { availability, bookings } = data;
const allSlots = [];
availability.forEach(slot => {
const startTime = new Date(slot.start_time);
const endTime = new Date(slot.end_time);
let currentTime = startTime;
while (currentTime < endTime) {
allSlots.push(new Date(currentTime));
currentTime.setHours(currentTime.getHours() + 1);
}
});
const bookedPeriods = bookings.map(b => ({
start: new Date(b.start_time).getTime(),
end: new Date(b.end_time).getTime()
}));
availableSlots = allSlots.filter(slot => {
const slotTime = slot.getTime();
// Check if the slot is within any of the booked periods
return !bookedPeriods.some(period => slotTime >= period.start && slotTime < period.end);
});
const availableDates = availableSlots.map(slot => slot.toISOString().split('T')[0]);
calendar.settings.selected.dates = [];
calendar.settings.selected.month = startDate.getMonth();
calendar.settings.selected.year = startDate.getFullYear();
const highlightedDates = [...new Set(availableDates)];
calendar.settings.visibility.highlightedDates = highlightedDates;
calendar.update();
}
function updateAvailableTimes(selectedDate) {
const slotsForDay = availableSlots.filter(slot => slot.toISOString().split('T')[0] === selectedDate);
timeSlotsElement.innerHTML = '';
if (slotsForDay.length > 0) {
slotsForDay.forEach(slot => {
const button = document.createElement('button');
button.classList.add('bg-gray-200', 'hover:bg-blue-500', 'hover:text-white', 'text-gray-800', 'font-bold', 'py-2', 'px-4', 'rounded');
button.textContent = slot.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
button.addEventListener('click', () => {
const year = slot.getFullYear();
const month = (`0` + (slot.getMonth() + 1)).slice(-2);
const day = (`0` + slot.getDate()).slice(-2);
const hours = (`0` + slot.getHours()).slice(-2);
const minutes = (`0` + slot.getMinutes()).slice(-2);
bookingTimeInput.value = `${year}-${month}-${day}T${hours}:${minutes}`;
bookingForm.classList.remove('hidden');
});
timeSlotsElement.appendChild(button);
});
} else {
timeSlotsElement.innerHTML = '<p>No available time slots for this day.</p>';
}
}
const initialDate = new Date();
const startDate = new Date(initialDate.getFullYear(), initialDate.getMonth(), 1);
const endDate = new Date(initialDate.getFullYear(), initialDate.getMonth() + 1, 0);
fetchAvailability(startDate, endDate);
const recurringCheckbox = document.getElementById('recurring');
const recurringOptions = document.getElementById('recurring-options');
recurringCheckbox.addEventListener('change', () => {
if (recurringCheckbox.checked) {
recurringOptions.classList.remove('hidden');
} else {
recurringOptions.classList.add('hidden');
}
});
});
</script>
</body>
</html>