Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -1,151 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
color: #264653;
|
|
||||||
background-color: #F4F4F4;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #2A9D8F !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
color: #264653 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link:hover {
|
|
||||||
color: #2A9D8F !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero {
|
|
||||||
background: linear-gradient(45deg, #2A9D8F, #2de0c9);
|
|
||||||
color: white;
|
|
||||||
padding: 100px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero h1 {
|
|
||||||
font-size: 3.5rem;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero p {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background-color: #E9C46A;
|
|
||||||
border-color: #E9C46A;
|
|
||||||
color: #264653;
|
|
||||||
padding: 12px 30px;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
font-weight: 700;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
transition: background-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
|
||||||
background-color: #f0d28a;
|
|
||||||
border-color: #f0d28a;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
padding: 80px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
section h2 {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 60px;
|
|
||||||
font-size: 2.5rem;
|
|
||||||
color: #264653;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 30px;
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
|
||||||
transition: transform 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card:hover {
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-icon {
|
|
||||||
font-size: 3rem;
|
|
||||||
color: #2A9D8F;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.testimonial-card {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 30px;
|
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
#contact {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
border-color: #2A9D8F;
|
|
||||||
box-shadow: 0 0 0 0.25rem rgba(42, 157, 143, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
background-color: #264653;
|
|
||||||
color: white;
|
|
||||||
padding: 40px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer a {
|
|
||||||
color: #E9C46A;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dashboard Styles */
|
|
||||||
.sidebar {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 280px;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-right: 1px solid #dee2e6;
|
|
||||||
}
|
|
||||||
.sidebar .nav-link {
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
.sidebar .nav-link.active {
|
|
||||||
color: #0d6efd;
|
|
||||||
}
|
|
||||||
.sidebar .nav-link .bi {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.main-content {
|
|
||||||
margin-left: 280px;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
125
certificate.php
125
certificate.php
@ -1,125 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
if (!isset($_SESSION['user_id'])) {
|
|
||||||
header('Location: login.php');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
require_once 'db/config.php';
|
|
||||||
$db = db();
|
|
||||||
|
|
||||||
$skill_id = $_GET['skill_id'] ?? 0;
|
|
||||||
$user_id = $_SESSION['user_id'];
|
|
||||||
|
|
||||||
// Fetch user name
|
|
||||||
$user_stmt = $db->prepare("SELECT name FROM users WHERE id = ?");
|
|
||||||
$user_stmt->execute([$user_id]);
|
|
||||||
$user_name = $user_stmt->fetchColumn();
|
|
||||||
|
|
||||||
// Fetch skill title
|
|
||||||
$skill_stmt = $db->prepare("SELECT title FROM skills WHERE id = ?");
|
|
||||||
$skill_stmt->execute([$skill_id]);
|
|
||||||
$skill_title = $skill_stmt->fetchColumn();
|
|
||||||
|
|
||||||
// Fetch latest quiz attempt with a score of 70% or higher
|
|
||||||
$attempt_stmt = $db->prepare("SELECT score, completed_at FROM quiz_attempts WHERE user_id = ? AND skill_id = ? AND score >= 70 ORDER BY completed_at DESC LIMIT 1");
|
|
||||||
$attempt_stmt->execute([$user_id, $skill_id]);
|
|
||||||
$attempt = $attempt_stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$user_name || !$skill_title || !$attempt) {
|
|
||||||
// Redirect or show an error if the user is not eligible for a certificate
|
|
||||||
header('Location: dashboard.php?error=not_eligible');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$completion_date = date("F j, Y", strtotime($attempt['completed_at']));
|
|
||||||
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Certificate of Completion</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #f0f2f5;
|
|
||||||
}
|
|
||||||
.certificate-container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 50px auto;
|
|
||||||
padding: 40px;
|
|
||||||
background: white;
|
|
||||||
border: 10px solid #0d6efd;
|
|
||||||
border-radius: 15px;
|
|
||||||
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
|
||||||
text-align: center;
|
|
||||||
font-family: serif;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.certificate-container::before, .certificate-container::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: -15px;
|
|
||||||
top: -15px;
|
|
||||||
right: -15px;
|
|
||||||
bottom: -15px;
|
|
||||||
border: 2px solid #0d6efd;
|
|
||||||
border-radius: 15px;
|
|
||||||
}
|
|
||||||
.certificate-title {
|
|
||||||
font-size: 48px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #0d6efd;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.certificate-subtitle {
|
|
||||||
font-size: 24px;
|
|
||||||
color: #6c757d;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
.user-name {
|
|
||||||
font-size: 40px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #343a40;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-bottom: 2px solid #dee2e6;
|
|
||||||
display: inline-block;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
.completion-text {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
.skill-title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-style: italic;
|
|
||||||
color: #495057;
|
|
||||||
}
|
|
||||||
.completion-date {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #6c757d;
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
.actions {
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="certificate-container">
|
|
||||||
<div class="certificate-title">Certificate of Completion</div>
|
|
||||||
<div class="certificate-subtitle">This is to certify that</div>
|
|
||||||
<div class="user-name"><?php echo htmlspecialchars($user_name); ?></div>
|
|
||||||
<div class="completion-text">has successfully completed the skill</div>
|
|
||||||
<div class="skill-title">"<?php echo htmlspecialchars($skill_title); ?>"</div>
|
|
||||||
<div class="completion-date">on <?php echo $completion_date; ?></div>
|
|
||||||
<div class="actions">
|
|
||||||
<a href="dashboard.php" class="btn btn-secondary"><i class="bi bi-arrow-left"></i> Back to Dashboard</a>
|
|
||||||
<button onclick="window.print()" class="btn btn-primary"><i class="bi bi-printer"></i> Print Certificate</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
49
contact.php
49
contact.php
@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/db/config.php';
|
|
||||||
require_once __DIR__ . '/mail/MailService.php';
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$name = trim($_POST['name'] ?? '');
|
|
||||||
$email = trim($_POST['email'] ?? '');
|
|
||||||
$message = trim($_POST['message'] ?? '');
|
|
||||||
|
|
||||||
if (empty($name) || empty($email) || empty($message) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
http_response_code(400);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Invalid input.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO contact_submissions (name, email, message) VALUES (?, ?, ?)");
|
|
||||||
$stmt->execute([$name, $email, $message]);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
// In a real app, you would log this error.
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Database error.']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$to = 'usmanabdulhamidsaleh4@gmail.com';
|
|
||||||
$subject = 'New Contact Form Submission';
|
|
||||||
|
|
||||||
$emailBody = "<p>You have a new contact form submission:</p>"
|
|
||||||
. "<ul>"
|
|
||||||
. "<li><strong>Name:</strong> " . htmlspecialchars($name) . "</li>"
|
|
||||||
. "<li><strong>Email:</strong> " . htmlspecialchars($email) . "</li>"
|
|
||||||
. "<li><strong>Message:</strong><br>" . nl2br(htmlspecialchars($message)) . "</li>"
|
|
||||||
. "</ul>";
|
|
||||||
|
|
||||||
$result = MailService::sendMail($to, $subject, $emailBody, strip_tags($emailBody), ['reply_to' => $email]);
|
|
||||||
|
|
||||||
if ($result['success']) {
|
|
||||||
echo json_encode(['success' => true, 'message' => 'Message sent successfully!']);
|
|
||||||
} else {
|
|
||||||
// In a real app, you would log this error.
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Failed to send email.']);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
http_response_code(405);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Method not allowed.']);
|
|
||||||
}
|
|
||||||
233
dashboard.php
233
dashboard.php
@ -1,233 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
require_once 'db/config.php';
|
|
||||||
|
|
||||||
if (!isset($_SESSION['user_id'])) {
|
|
||||||
header("Location: login.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$userId = $_SESSION['user_id'];
|
|
||||||
$message = '';
|
|
||||||
$message_type = '';
|
|
||||||
|
|
||||||
// Handle Enrollment
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['enroll_skill_id'])) {
|
|
||||||
$skillId = $_POST['enroll_skill_id'];
|
|
||||||
|
|
||||||
// Check if already enrolled
|
|
||||||
$stmt = db()->prepare("SELECT id FROM enrollments WHERE user_id = ? AND skill_id = ?");
|
|
||||||
$stmt->execute([$userId, $skillId]);
|
|
||||||
if ($stmt->fetch()) {
|
|
||||||
$_SESSION['message'] = "You are already enrolled in this skill.";
|
|
||||||
$_SESSION['message_type'] = 'warning';
|
|
||||||
} else {
|
|
||||||
// Enroll user
|
|
||||||
$stmt = db()->prepare("INSERT INTO enrollments (user_id, skill_id, progress, date_enrolled) VALUES (?, ?, 0, NOW())");
|
|
||||||
if ($stmt->execute([$userId, $skillId])) {
|
|
||||||
$_SESSION['message'] = "Enrollment successful! You can now continue learning from your dashboard.";
|
|
||||||
$_SESSION['message_type'] = 'success';
|
|
||||||
} else {
|
|
||||||
$_SESSION['message'] = "An error occurred. Please try again.";
|
|
||||||
$_SESSION['message_type'] = 'danger';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
header("Location: dashboard.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_SESSION['message'])) {
|
|
||||||
$message = $_SESSION['message'];
|
|
||||||
$message_type = $_SESSION['message_type'];
|
|
||||||
unset($_SESSION['message']);
|
|
||||||
unset($_SESSION['message_type']);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
|
|
||||||
$stmt->execute([$userId]);
|
|
||||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$user) {
|
|
||||||
session_destroy();
|
|
||||||
header("Location: login.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch all skills, excluding those the user is already enrolled in for the explorer
|
|
||||||
$enrolled_skill_ids = [];
|
|
||||||
$stmt_enrolled_ids = db()->prepare("SELECT skill_id FROM enrollments WHERE user_id = ?");
|
|
||||||
$stmt_enrolled_ids->execute([$userId]);
|
|
||||||
$enrolled_skill_ids = $stmt_enrolled_ids->fetchAll(PDO::FETCH_COLUMN);
|
|
||||||
|
|
||||||
$skills_by_category = [];
|
|
||||||
$sql = "SELECT * FROM skills";
|
|
||||||
if (!empty($enrolled_skill_ids)) {
|
|
||||||
$placeholders = implode(',', array_fill(0, count($enrolled_skill_ids), '?'));
|
|
||||||
$sql .= " WHERE id NOT IN ($placeholders)";
|
|
||||||
}
|
|
||||||
$sql .= " ORDER BY category, title";
|
|
||||||
|
|
||||||
$stmt = db()->prepare($sql);
|
|
||||||
if (!empty($enrolled_skill_ids)) {
|
|
||||||
$stmt->execute($enrolled_skill_ids);
|
|
||||||
} else {
|
|
||||||
$stmt->execute();
|
|
||||||
}
|
|
||||||
$all_skills = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
|
|
||||||
foreach ($all_skills as $skill) {
|
|
||||||
$skills_by_category[$skill['category']][] = $skill;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch enrolled skills for the current user
|
|
||||||
$stmt = db()->prepare("
|
|
||||||
SELECT s.id, s.title, s.category, s.thumbnail, e.progress
|
|
||||||
FROM enrollments e
|
|
||||||
JOIN skills s ON e.skill_id = s.id
|
|
||||||
WHERE e.user_id = ?
|
|
||||||
");
|
|
||||||
$stmt->execute([$userId]);
|
|
||||||
$enrolled_skills = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Dashboard - TAP2SKILL</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
|
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="sidebar">
|
|
||||||
<a class="navbar-brand" href="index.php">TAP2SKILL</a>
|
|
||||||
<hr>
|
|
||||||
<ul class="nav flex-column">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link active" href="dashboard.php">
|
|
||||||
<i class="bi bi-grid-1x2-fill"></i>
|
|
||||||
Dashboard
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#">
|
|
||||||
<i class="bi bi-person-circle"></i>
|
|
||||||
Profile
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#">
|
|
||||||
<i class="bi bi-gear-fill"></i>
|
|
||||||
Settings
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<hr>
|
|
||||||
<a href="logout.php" class="btn btn-outline-primary">Logout</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="main-content">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
|
||||||
<h1 class="h2">Welcome, <?php echo htmlspecialchars($user['username']); ?>!</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if ($message): ?>
|
|
||||||
<div class="alert alert-<?php echo $message_type; ?> alert-dismissible fade show" role="alert">
|
|
||||||
<?php echo htmlspecialchars($message); ?>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<!-- Quick Access Buttons -->
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<a href="#skill-explorer" class="btn btn-primary btn-lg w-100">Explore Skills</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<a href="#enrolled-skills" class="btn btn-success btn-lg w-100">Continue Learning</a>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<a href="#" class="btn btn-info btn-lg w-100">Community</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Enrolled Skills Section -->
|
|
||||||
<section id="enrolled-skills" class="mb-5">
|
|
||||||
<h2 class="h4">My Enrolled Skills</h2>
|
|
||||||
<div class="row">
|
|
||||||
<?php if (empty($enrolled_skills)): ?>
|
|
||||||
<div class="col">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<p class="card-text">You haven’t enrolled in any skills yet. Explore new skills to start learning!</p>
|
|
||||||
<a href="#skill-explorer" class="btn btn-primary">Explore Skills</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($enrolled_skills as $skill): ?>
|
|
||||||
<div class="col-md-6 col-lg-4 mb-3">
|
|
||||||
<div class="card">
|
|
||||||
<img src="<?php echo htmlspecialchars($skill['thumbnail']); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($skill['title']); ?>">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title"><?php echo htmlspecialchars($skill['title']); ?></h5>
|
|
||||||
<p class="card-text text-muted"><?php echo htmlspecialchars($skill['category']); ?></p>
|
|
||||||
<div class="progress mb-2">
|
|
||||||
<div class="progress-bar" role="progressbar" style="width: <?php echo $skill['progress']; ?>%;" aria-valuenow="<?php echo $skill['progress']; ?>" aria-valuemin="0" aria-valuemax="100"><?php echo $skill['progress']; ?>%</div>
|
|
||||||
</div>
|
|
||||||
<a href="learn.php?skill_id=<?php echo $skill['id']; ?>" class="btn btn-primary mt-3">Continue Learning</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Skill Explorer Section -->
|
|
||||||
<section id="skill-explorer">
|
|
||||||
<h2 class="h4">Skill Explorer</h2>
|
|
||||||
<?php if (empty($all_skills)): ?>
|
|
||||||
<div class="col">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<p class="card-text">You've enrolled in all available skills. Great job!</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($skills_by_category as $category => $skills): ?>
|
|
||||||
<h3 class="h5 mt-4"><?php echo htmlspecialchars($category); ?></h3>
|
|
||||||
<div class="row">
|
|
||||||
<?php foreach ($skills as $skill): ?>
|
|
||||||
<div class="col-md-6 col-lg-3 mb-3">
|
|
||||||
<div class="card h-100">
|
|
||||||
<img src="<?php echo htmlspecialchars($skill['thumbnail']); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($skill['title']); ?>">
|
|
||||||
<div class="card-body d-flex flex-column">
|
|
||||||
<h5 class="card-title"><?php echo htmlspecialchars($skill['title']); ?></h5>
|
|
||||||
<p class="card-text flex-grow-1"><?php echo htmlspecialchars($skill['description']); ?></p>
|
|
||||||
<form method="POST" action="dashboard.php" class="mt-auto">
|
|
||||||
<input type="hidden" name="enroll_skill_id" value="<?php echo $skill['id']; ?>">
|
|
||||||
<button type="submit" class="btn btn-outline-primary">Enroll</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/config.php';
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$migration_files = glob(__DIR__ . '/migrations/*.sql');
|
|
||||||
sort($migration_files);
|
|
||||||
|
|
||||||
foreach ($migration_files as $file) {
|
|
||||||
echo "Running migration: " . basename($file) . "...\n";
|
|
||||||
$sql = file_get_contents($file);
|
|
||||||
$pdo->exec($sql);
|
|
||||||
echo "Success.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "\nAll migrations completed successfully!\n";
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
die("Database migration failed: " . $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS contact_submissions (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
email VARCHAR(255) NOT NULL,
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `users` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`username` VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
`email` VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
`password` VARCHAR(255) NOT NULL,
|
|
||||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `skills` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`title` VARCHAR(255) NOT NULL,
|
|
||||||
`category` VARCHAR(100) NOT NULL,
|
|
||||||
`description` TEXT,
|
|
||||||
`thumbnail` VARCHAR(255)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `user_skills` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`user_id` int(11) unsigned NOT NULL,
|
|
||||||
`skill_id` int(11) NOT NULL,
|
|
||||||
`progress` INT DEFAULT 0,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
||||||
FOREIGN KEY (skill_id) REFERENCES skills(id)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
INSERT INTO `skills` (`title`, `category`, `description`, `thumbnail`) VALUES
|
|
||||||
('Introduction to Python', 'Tech', 'Learn the fundamentals of Python programming.', 'assets/images/python.png'),
|
|
||||||
('Web Development Basics', 'Tech', 'Understand HTML, CSS, and JavaScript.', 'assets/images/webdev.png'),
|
|
||||||
('Data Analysis with Pandas', 'Tech', 'Analyze data effectively using the Pandas library.', 'assets/images/pandas.png'),
|
|
||||||
('Basic Carpentry', 'Trade', 'Learn to build simple wooden furniture.', 'assets/images/carpentry.png'),
|
|
||||||
('Electrical Wiring 101', 'Trade', 'Understand the basics of home electrical wiring.', 'assets/images/wiring.png'),
|
|
||||||
('Digital Painting', 'Art', 'Create stunning digital art with this introductory course.', 'assets/images/digital_painting.png'),
|
|
||||||
('Music Production Fundamentals', 'Art', 'Learn to produce your own music from scratch.', 'assets/images/music_production.png'),
|
|
||||||
('Introduction to Business', 'Business', 'Learn the basics of starting and running a business.', 'assets/images/business.png'),
|
|
||||||
('Marketing for Beginners', 'Business', 'Understand the fundamentals of modern marketing.', 'assets/images/marketing.png'),
|
|
||||||
('Personal Finance Management', 'Business', 'Take control of your finances.', 'assets/images/finance.png');
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
-- This migration cleans up previous attempts and creates the enrollments table correctly.
|
|
||||||
DROP TABLE IF EXISTS `user_skills`;
|
|
||||||
DROP TABLE IF EXISTS `enrollments`;
|
|
||||||
|
|
||||||
CREATE TABLE `enrollments` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`user_id` int(11) unsigned NOT NULL,
|
|
||||||
`skill_id` int(11) NOT NULL,
|
|
||||||
`progress` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`date_enrolled` DATETIME NOT NULL DEFAULT current_timestamp(),
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `user_id` (`user_id`),
|
|
||||||
KEY `skill_id` (`skill_id`),
|
|
||||||
CONSTRAINT `enrollments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT `enrollments_ibfk_2` FOREIGN KEY (`skill_id`) REFERENCES `skills` (`id`) ON DELETE CASCADE
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS modules (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
skill_id INT NOT NULL,
|
|
||||||
title VARCHAR(255) NOT NULL,
|
|
||||||
`order` INT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS lessons (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
module_id INT NOT NULL,
|
|
||||||
title VARCHAR(255) NOT NULL,
|
|
||||||
content TEXT,
|
|
||||||
`order` INT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (module_id) REFERENCES modules(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
INSERT INTO modules (skill_id, title, `order`) VALUES
|
|
||||||
(1, 'Module 1: Getting Started', 1),
|
|
||||||
(1, 'Module 2: Core Concepts', 2);
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
INSERT INTO lessons (module_id, title, content, `order`) VALUES
|
|
||||||
(1, 'Lesson 1.1: Introduction to the topic', 'This is the content for the introductory lesson.', 1),
|
|
||||||
(1, 'Lesson 1.2: Setting up your environment', 'Here is how you set up your environment to get started.', 2),
|
|
||||||
(2, 'Lesson 2.1: Understanding the fundamentals', 'This lesson covers the fundamental principles.', 1),
|
|
||||||
(2, 'Lesson 2.2: Advanced techniques', 'This lesson delves into more advanced techniques.', 2);
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS lesson_completion (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
user_id INT(11) UNSIGNED NOT NULL,
|
|
||||||
lesson_id INT(11) NOT NULL,
|
|
||||||
completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (lesson_id) REFERENCES lessons(id) ON DELETE CASCADE,
|
|
||||||
UNIQUE KEY (user_id, lesson_id)
|
|
||||||
);
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS quizzes (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
skill_id INT NOT NULL,
|
|
||||||
question TEXT NOT NULL,
|
|
||||||
option_a VARCHAR(255) NOT NULL,
|
|
||||||
option_b VARCHAR(255) NOT NULL,
|
|
||||||
option_c VARCHAR(255) NOT NULL,
|
|
||||||
option_d VARCHAR(255) NOT NULL,
|
|
||||||
correct_option CHAR(1) NOT NULL,
|
|
||||||
FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS quiz_attempts (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
user_id INT(11) UNSIGNED NOT NULL,
|
|
||||||
skill_id INT NOT NULL,
|
|
||||||
score INT NOT NULL,
|
|
||||||
completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
INSERT INTO quizzes (skill_id, question, option_a, option_b, option_c, option_d, correct_option) VALUES
|
|
||||||
(1, 'What does HTML stand for?', 'Hyper Text Markup Language', 'High Tech Modern Language', 'Hyperlink and Text Markup Language', 'Home Tool Markup Language', 'a'),
|
|
||||||
(1, 'Who is making the Web standards?', 'Mozilla', 'The World Wide Web Consortium', 'Google', 'Microsoft', 'b'),
|
|
||||||
(1, 'Choose the correct HTML element for the largest heading:', '<h1>', '<heading>', '<h6>', '<head>', 'a'),
|
|
||||||
(1, 'What is the correct HTML element for inserting a line break?', '<lb>', '<br>', '<break>', '<linebreak>', 'b'),
|
|
||||||
(1, 'What is the correct HTML for creating a hyperlink?', '<a href="http://www.example.com">Example</a>', '<a>http://www.example.com</a>', '<a url="http://www.example.com">Example</a>', '<link>http://www.example.com</link>', 'a');
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/config.php';
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW CREATE TABLE users");
|
|
||||||
$user_table_def = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
echo "Users table definition:\n";
|
|
||||||
print_r($user_table_def);
|
|
||||||
|
|
||||||
$stmt = $pdo->query("SHOW CREATE TABLE lessons");
|
|
||||||
$lesson_table_def = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
echo "\nLessons table definition:\n";
|
|
||||||
print_r($lesson_table_def);
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
die("Database query failed: " . $e->getMessage());
|
|
||||||
}
|
|
||||||
439
index.php
439
index.php
@ -1,299 +1,150 @@
|
|||||||
<!DOCTYPE html>
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
@ini_set('display_errors', '1');
|
||||||
|
@error_reporting(E_ALL);
|
||||||
|
@date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
$phpVersion = PHP_VERSION;
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>TAP2SKILL - Learn. Hustle. Thrive.</title>
|
<title>New Style</title>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<?php
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
// Read project preview data from environment
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Lato:wght@400;700&display=swap" rel="stylesheet">
|
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||||
<style>
|
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||||
:root {
|
?>
|
||||||
--primary-color: #2A9D8F;
|
<?php if ($projectDescription): ?>
|
||||||
--secondary-color: #E9C46A;
|
<!-- Meta description -->
|
||||||
--background-color: #F4F4F4;
|
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||||
--surface-color: #FFFFFF;
|
<!-- Open Graph meta tags -->
|
||||||
--text-color: #264653;
|
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||||
--gradient-start: #2A9D8F;
|
<!-- Twitter meta tags -->
|
||||||
--gradient-end: #3aafa9;
|
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||||
}
|
<?php endif; ?>
|
||||||
body {
|
<?php if ($projectImageUrl): ?>
|
||||||
font-family: 'Lato', sans-serif;
|
<!-- Open Graph image -->
|
||||||
margin: 0;
|
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||||
background-color: var(--background-color);
|
<!-- Twitter image -->
|
||||||
color: var(--text-color);
|
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||||
}
|
<?php endif; ?>
|
||||||
.container {
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
max-width: 1200px;
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
margin: 0 auto;
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||||
padding: 0 24px;
|
<style>
|
||||||
}
|
:root {
|
||||||
header {
|
--bg-color-start: #6a11cb;
|
||||||
background-color: var(--surface-color);
|
--bg-color-end: #2575fc;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
--text-color: #ffffff;
|
||||||
padding: 16px 0;
|
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||||
position: sticky;
|
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||||
top: 0;
|
}
|
||||||
z-index: 100;
|
body {
|
||||||
}
|
margin: 0;
|
||||||
nav {
|
font-family: 'Inter', sans-serif;
|
||||||
display: flex;
|
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||||
justify-content: space-between;
|
color: var(--text-color);
|
||||||
align-items: center;
|
display: flex;
|
||||||
}
|
justify-content: center;
|
||||||
.logo {
|
align-items: center;
|
||||||
font-family: 'Poppins', sans-serif;
|
min-height: 100vh;
|
||||||
font-size: 24px;
|
text-align: center;
|
||||||
font-weight: 700;
|
overflow: hidden;
|
||||||
color: var(--primary-color);
|
position: relative;
|
||||||
text-decoration: none;
|
}
|
||||||
}
|
body::before {
|
||||||
nav ul {
|
content: '';
|
||||||
list-style: none;
|
position: absolute;
|
||||||
margin: 0;
|
top: 0;
|
||||||
padding: 0;
|
left: 0;
|
||||||
display: flex;
|
width: 100%;
|
||||||
}
|
height: 100%;
|
||||||
nav ul li {
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||||
margin-left: 32px;
|
animation: bg-pan 20s linear infinite;
|
||||||
}
|
z-index: -1;
|
||||||
nav ul li a {
|
}
|
||||||
text-decoration: none;
|
@keyframes bg-pan {
|
||||||
color: var(--text-color);
|
0% { background-position: 0% 0%; }
|
||||||
font-weight: 600;
|
100% { background-position: 100% 100%; }
|
||||||
transition: color 0.3s ease;
|
}
|
||||||
}
|
main {
|
||||||
nav ul li a:hover {
|
padding: 2rem;
|
||||||
color: var(--primary-color);
|
}
|
||||||
}
|
.card {
|
||||||
.hero {
|
background: var(--card-bg-color);
|
||||||
background: linear-gradient(45deg, var(--gradient-start), var(--gradient-end));
|
border: 1px solid var(--card-border-color);
|
||||||
color: white;
|
border-radius: 16px;
|
||||||
padding: 120px 0;
|
padding: 2rem;
|
||||||
text-align: center;
|
backdrop-filter: blur(20px);
|
||||||
}
|
-webkit-backdrop-filter: blur(20px);
|
||||||
.hero h1 {
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||||
font-family: 'Poppins', sans-serif;
|
}
|
||||||
font-size: 56px;
|
.loader {
|
||||||
margin: 0 0 16px;
|
margin: 1.25rem auto 1.25rem;
|
||||||
}
|
width: 48px;
|
||||||
.hero p {
|
height: 48px;
|
||||||
font-size: 20px;
|
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||||
margin: 0 0 32px;
|
border-top-color: #fff;
|
||||||
}
|
border-radius: 50%;
|
||||||
.cta-button {
|
animation: spin 1s linear infinite;
|
||||||
font-family: 'Poppins', sans-serif;
|
}
|
||||||
background-color: var(--secondary-color);
|
@keyframes spin {
|
||||||
color: var(--text-color);
|
from { transform: rotate(0deg); }
|
||||||
padding: 16px 32px;
|
to { transform: rotate(360deg); }
|
||||||
border-radius: 8px;
|
}
|
||||||
text-decoration: none;
|
.hint {
|
||||||
font-weight: 600;
|
opacity: 0.9;
|
||||||
transition: transform 0.3s ease;
|
}
|
||||||
display: inline-block;
|
.sr-only {
|
||||||
}
|
position: absolute;
|
||||||
.cta-button:hover {
|
width: 1px; height: 1px;
|
||||||
transform: translateY(-2px);
|
padding: 0; margin: -1px;
|
||||||
}
|
overflow: hidden;
|
||||||
section {
|
clip: rect(0, 0, 0, 0);
|
||||||
padding: 80px 0;
|
white-space: nowrap; border: 0;
|
||||||
}
|
}
|
||||||
.section-title {
|
h1 {
|
||||||
font-family: 'Poppins', sans-serif;
|
font-size: 3rem;
|
||||||
font-size: 40px;
|
font-weight: 700;
|
||||||
text-align: center;
|
margin: 0 0 1rem;
|
||||||
margin-bottom: 48px;
|
letter-spacing: -1px;
|
||||||
}
|
}
|
||||||
.grid {
|
p {
|
||||||
display: grid;
|
margin: 0.5rem 0;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
font-size: 1.1rem;
|
||||||
gap: 32px;
|
}
|
||||||
}
|
code {
|
||||||
.card {
|
background: rgba(0,0,0,0.2);
|
||||||
background-color: var(--surface-color);
|
padding: 2px 6px;
|
||||||
border-radius: 8px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
padding: 32px;
|
}
|
||||||
text-align: center;
|
footer {
|
||||||
}
|
position: absolute;
|
||||||
.testimonial-card {
|
bottom: 1rem;
|
||||||
background-color: var(--surface-color);
|
font-size: 0.8rem;
|
||||||
border-radius: 8px;
|
opacity: 0.7;
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
}
|
||||||
padding: 32px;
|
</style>
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.testimonial-card p {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.testimonial-card .author {
|
|
||||||
margin-top: 16px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
#contact {
|
|
||||||
background-color: var(--surface-color);
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
form input, form textarea {
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
padding: 16px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
form button {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
padding: 16px 32px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: none;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
form button:hover {
|
|
||||||
background-color: #228b80;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
background-color: var(--text-color);
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
padding: 24px 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<main>
|
||||||
<header>
|
<div class="card">
|
||||||
<nav class="container">
|
<h1>Analyzing your requirements and generating your website…</h1>
|
||||||
<a href="#" class="logo">TAP2SKILL</a>
|
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||||
<ul>
|
<span class="sr-only">Loading…</span>
|
||||||
<li><a href="#about">About</a></li>
|
</div>
|
||||||
<li><a href="#features">Features</a></li>
|
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||||
<li><a href="#testimonials">Testimonials</a></li>
|
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||||
<li><a href="#contact">Contact</a></li>
|
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||||
<li><a href="login.php">Login</a></li>
|
</div>
|
||||||
<li><a href="register.php">Register</a></li>
|
</main>
|
||||||
</ul>
|
<footer>
|
||||||
</nav>
|
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||||
</header>
|
</footer>
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="hero">
|
|
||||||
<div class="container">
|
|
||||||
<h1>Learn. Hustle. Thrive.</h1>
|
|
||||||
<p>Your journey to mastery starts here.</p>
|
|
||||||
<a href="#features" class="cta-button">Explore Features</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="about">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="section-title">About TAP2SKILL</h2>
|
|
||||||
<p style="text-align: center; max-width: 800px; margin: 0 auto 48px;">TAP2SKILL is a modern learning platform designed to help you acquire new skills efficiently and effectively. We believe in a hands-on, project-based approach to learning that prepares you for real-world challenges.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="features">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="section-title">Core Features</h2>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="card">
|
|
||||||
<h3>Skill Explorer</h3>
|
|
||||||
<p>Discover new skills and learning paths tailored to your interests and career goals.</p>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<h3>Video-Based Learning</h3>
|
|
||||||
<p>Engage with high-quality video content from industry experts.</p>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<h3>Project-Based Tasks</h3>
|
|
||||||
<p>Apply what you learn with hands-on projects and real-world scenarios.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="testimonials">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="section-title">What Our Users Say</h2>
|
|
||||||
<div class="grid">
|
|
||||||
<div class="testimonial-card">
|
|
||||||
<p>"TAP2SKILL helped me land my dream job. The project-based learning was a game-changer."</p>
|
|
||||||
<p class="author">- Alex Doe</p>
|
|
||||||
</div>
|
|
||||||
<div class="testimonial-card">
|
|
||||||
<p>"I love the flexibility of the platform. I can learn at my own pace, anytime, anywhere."</p>
|
|
||||||
<p class="author">- Jane Smith</p>
|
|
||||||
</div>
|
|
||||||
<div class="testimonial-card">
|
|
||||||
<p>"The instructors are top-notch. I've learned so much in such a short time."</p>
|
|
||||||
<p class="author">- Sam Wilson</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="contact">
|
|
||||||
<div class="container">
|
|
||||||
<h2 class="section-title">Contact Us</h2>
|
|
||||||
<form id="contact-form" action="contact.php" method="POST">
|
|
||||||
<input type="text" name="name" placeholder="Your Name" required>
|
|
||||||
<input type="email" name="email" placeholder="Your Email" required>
|
|
||||||
<textarea name="message" rows="5" placeholder="Your Message" required></textarea>
|
|
||||||
<button type="submit">Send Message</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<div class="container">
|
|
||||||
<p>© 2025 TAP2SKILL. All Rights Reserved.</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.getElementById('contact-form').addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const form = e.target;
|
|
||||||
const button = form.querySelector('button');
|
|
||||||
const buttonText = button.textContent;
|
|
||||||
button.textContent = 'Sending...';
|
|
||||||
button.disabled = true;
|
|
||||||
|
|
||||||
const formData = new FormData(form);
|
|
||||||
|
|
||||||
fetch('contact.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
form.reset();
|
|
||||||
alert('Thank you for your message! We will get back to you shortly.');
|
|
||||||
} else {
|
|
||||||
alert('An error occurred: ' + data.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
|
||||||
alert('An unexpected error occurred. Please try again later.');
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
button.textContent = buttonText;
|
|
||||||
button.disabled = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
189
learn.php
189
learn.php
@ -1,189 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
if (!isset($_SESSION['user_id'])) {
|
|
||||||
header('Location: login.php');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
require_once 'db/config.php';
|
|
||||||
$db = db();
|
|
||||||
|
|
||||||
$skill_id = $_GET['skill_id'] ?? 0;
|
|
||||||
$user_id = $_SESSION['user_id'];
|
|
||||||
|
|
||||||
// Handle marking a lesson as complete
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['complete_lesson'])) {
|
|
||||||
$lesson_id = $_POST['lesson_id'];
|
|
||||||
|
|
||||||
// Check if already completed
|
|
||||||
$stmt = $db->prepare("SELECT id FROM lesson_completion WHERE user_id = ? AND lesson_id = ?");
|
|
||||||
$stmt->execute([$user_id, $lesson_id]);
|
|
||||||
if ($stmt->fetch()) {
|
|
||||||
// Already completed, do nothing
|
|
||||||
} else {
|
|
||||||
// Insert into lesson_completion
|
|
||||||
$stmt = $db->prepare("INSERT INTO lesson_completion (user_id, lesson_id) VALUES (?, ?)");
|
|
||||||
$stmt->execute([$user_id, $lesson_id]);
|
|
||||||
|
|
||||||
// Recalculate progress
|
|
||||||
$total_lessons_stmt = $db->prepare("SELECT COUNT(*) FROM lessons l JOIN modules m ON l.module_id = m.id WHERE m.skill_id = ?");
|
|
||||||
$total_lessons_stmt->execute([$skill_id]);
|
|
||||||
$total_lessons = $total_lessons_stmt->fetchColumn();
|
|
||||||
|
|
||||||
$completed_lessons_stmt = $db->prepare("SELECT COUNT(*) FROM lesson_completion lc JOIN lessons l ON lc.lesson_id = l.id JOIN modules m ON l.module_id = m.id WHERE lc.user_id = ? AND m.skill_id = ?");
|
|
||||||
$completed_lessons_stmt->execute([$user_id, $skill_id]);
|
|
||||||
$completed_lessons = $completed_lessons_stmt->fetchColumn();
|
|
||||||
|
|
||||||
$progress = ($total_lessons > 0) ? round(($completed_lessons / $total_lessons) * 100) : 0;
|
|
||||||
|
|
||||||
// Update enrollments table
|
|
||||||
$update_stmt = $db->prepare("UPDATE enrollments SET progress = ? WHERE user_id = ? AND skill_id = ?");
|
|
||||||
$update_stmt->execute([$progress, $user_id, $skill_id]);
|
|
||||||
}
|
|
||||||
header("Location: learn.php?skill_id=$skill_id");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Fetch skill details
|
|
||||||
$stmt = $db->prepare("SELECT * FROM skills WHERE id = ?");
|
|
||||||
$stmt->execute([$skill_id]);
|
|
||||||
$skill = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$skill) {
|
|
||||||
die('Skill not found!');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch completed lesson IDs for the user
|
|
||||||
$completed_lessons_stmt = $db->prepare("SELECT lesson_id FROM lesson_completion WHERE user_id = ?");
|
|
||||||
$completed_lessons_stmt->execute([$user_id]);
|
|
||||||
$completed_lesson_ids = $completed_lessons_stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
||||||
|
|
||||||
// Fetch modules and lessons
|
|
||||||
$modules_stmt = $db->prepare("SELECT * FROM modules WHERE skill_id = ? ORDER BY `order` ASC");
|
|
||||||
$modules_stmt->execute([$skill_id]);
|
|
||||||
$modules = $modules_stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
$lessons_stmt = $db->prepare("SELECT * FROM lessons WHERE module_id = ? ORDER BY `order` ASC");
|
|
||||||
|
|
||||||
// Calculate progress
|
|
||||||
$total_lessons_stmt = $db->prepare("SELECT COUNT(*) FROM lessons l JOIN modules m ON l.module_id = m.id WHERE m.skill_id = ?");
|
|
||||||
$total_lessons_stmt->execute([$skill_id]);
|
|
||||||
$total_lessons = $total_lessons_stmt->fetchColumn();
|
|
||||||
$completed_lessons = count($completed_lesson_ids);
|
|
||||||
$progress = ($total_lessons > 0) ? round(($completed_lessons / $total_lessons) * 100) : 0;
|
|
||||||
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Learning: <?php echo htmlspecialchars($skill['title']); ?></title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container mt-5">
|
|
||||||
<a href="dashboard.php" class="btn btn-secondary mb-4">Back to Dashboard</a>
|
|
||||||
<h1><?php echo htmlspecialchars($skill['title']); ?></h1>
|
|
||||||
<p class="text-muted"><?php echo htmlspecialchars($skill['category']); ?></p>
|
|
||||||
|
|
||||||
<!-- Progress Bar -->
|
|
||||||
<div class="progress my-4" style="height: 25px;">
|
|
||||||
<div class="progress-bar bg-success" role="progressbar" style="width: <?php echo $progress; ?>%;" aria-valuenow="<?php echo $progress; ?>" aria-valuemin="0" aria-valuemax="100"><?php echo $progress; ?>% Complete</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<p><?php echo nl2br(htmlspecialchars($skill['description'])); ?></p>
|
|
||||||
|
|
||||||
<div class="mt-4">
|
|
||||||
<h3>Course Content</h3>
|
|
||||||
<div class="accordion" id="modulesAccordion">
|
|
||||||
<?php if (empty($modules)): ?>
|
|
||||||
<p><em>(Content for this course will be added soon.)</em></p>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php foreach ($modules as $module): ?>
|
|
||||||
<div class="accordion-item">
|
|
||||||
<h2 class="accordion-header" id="heading<?php echo $module['id']; ?>">
|
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse<?php echo $module['id']; ?>" aria-expanded="false" aria-controls="collapse<?php echo $module['id']; ?>">
|
|
||||||
<?php echo htmlspecialchars($module['title']); ?>
|
|
||||||
</button>
|
|
||||||
</h2>
|
|
||||||
<div id="collapse<?php echo $module['id']; ?>" class="accordion-collapse collapse" aria-labelledby="heading<?php echo $module['id']; ?>" data-bs-parent="#modulesAccordion">
|
|
||||||
<div class="accordion-body">
|
|
||||||
<?php
|
|
||||||
$lessons_stmt->execute([$module['id']]);
|
|
||||||
$lessons = $lessons_stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
?>
|
|
||||||
<?php if (empty($lessons)): ?>
|
|
||||||
<p>No lessons in this module yet.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<ul class="list-group">
|
|
||||||
<?php foreach ($lessons as $lesson):
|
|
||||||
$is_completed = in_array($lesson['id'], $completed_lesson_ids);
|
|
||||||
?>
|
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center <?php echo $is_completed ? 'list-group-item-light text-muted' : ''; ?>">
|
|
||||||
<div>
|
|
||||||
<h5>
|
|
||||||
<?php if ($is_completed): ?>
|
|
||||||
<i class="bi bi-check-circle-fill text-success"></i>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php echo htmlspecialchars($lesson['title']); ?>
|
|
||||||
</h5>
|
|
||||||
<p><?php echo nl2br(htmlspecialchars($lesson['content'])); ?></p>
|
|
||||||
</div>
|
|
||||||
<?php if (!$is_completed): ?>
|
|
||||||
<form method="POST" action="learn.php?skill_id=<?php echo $skill_id; ?>">
|
|
||||||
<input type="hidden" name="lesson_id" value="<?php echo $lesson['id']; ?>">
|
|
||||||
<button type="submit" name="complete_lesson" class="btn btn-sm btn-success">Mark as Complete</button>
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
</li>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</ul>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// Fetch last quiz attempt
|
|
||||||
$quiz_attempt_stmt = $db->prepare("SELECT * FROM quiz_attempts WHERE user_id = ? AND skill_id = ? ORDER BY completed_at DESC LIMIT 1");
|
|
||||||
$quiz_attempt_stmt->execute([$user_id, $skill_id]);
|
|
||||||
$quiz_attempt = $quiz_attempt_stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if ($progress >= 100): ?>
|
|
||||||
<div class="card my-4">
|
|
||||||
<div class="card-body text-center">
|
|
||||||
<?php if ($quiz_attempt): ?>
|
|
||||||
<h4 class="card-title">Quiz Result</h4>
|
|
||||||
<p class="card-text">You scored <strong><?php echo $quiz_attempt['score']; ?>%</strong> on your last attempt.</p>
|
|
||||||
<?php if ($quiz_attempt['score'] >= 70): ?>
|
|
||||||
<a href="certificate.php?skill_id=<?php echo $skill_id; ?>" class="btn btn-success">
|
|
||||||
<i class="bi bi-patch-check-fill"></i> View Certificate
|
|
||||||
</a>
|
|
||||||
<?php else: ?>
|
|
||||||
<p class="text-danger">You need a score of 70% or higher to get a certificate.</p>
|
|
||||||
<a href="quiz.php?skill_id=<?php echo $skill_id; ?>" class="btn btn-primary">
|
|
||||||
<i class="bi bi-arrow-clockwise"></i> Retake Quiz
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php else: ?>
|
|
||||||
<h4 class="card-title">Ready to test your knowledge?</h4>
|
|
||||||
<p class="card-text">You have completed all the lessons. It's time to take the quiz!</p>
|
|
||||||
<a href="quiz.php?skill_id=<?php echo $skill_id; ?>" class="btn btn-primary btn-lg">Take Quiz</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
232
login.php
232
login.php
@ -1,232 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
require_once __DIR__ . '/db/config.php';
|
|
||||||
|
|
||||||
$error = '';
|
|
||||||
|
|
||||||
if (isset($_SESSION['user_id'])) {
|
|
||||||
header("Location: dashboard.php");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
|
||||||
$email = trim($_POST['email']);
|
|
||||||
$password = $_POST['password'];
|
|
||||||
|
|
||||||
if (empty($email) || empty($password)) {
|
|
||||||
$error = 'Please fill in all fields.';
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
|
|
||||||
$stmt->execute(['email' => $email]);
|
|
||||||
$user = $stmt->fetch();
|
|
||||||
|
|
||||||
if ($user && password_verify($password, $user['password'])) {
|
|
||||||
$_SESSION['user_id'] = $user['id'];
|
|
||||||
$_SESSION['username'] = $user['username'];
|
|
||||||
header("Location: dashboard.php");
|
|
||||||
exit;
|
|
||||||
} else {
|
|
||||||
$error = 'Invalid email or password.';
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
$error = "Database error: " . $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Login - TAP2SKILL</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Lato:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--primary-color: #2A9D8F;
|
|
||||||
--secondary-color: #E9C46A;
|
|
||||||
--background-color: #F4F4F4;
|
|
||||||
--surface-color: #FFFFFF;
|
|
||||||
--text-color: #264653;
|
|
||||||
--error-color: #e74c3c;
|
|
||||||
--success-color: #2ecc71;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
background-color: var(--background-color);
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
header {
|
|
||||||
background-color: var(--surface-color);
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
padding: 16px 0;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
nav {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.logo {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
nav ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
nav ul li {
|
|
||||||
margin-left: 32px;
|
|
||||||
}
|
|
||||||
nav ul li a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--text-color);
|
|
||||||
font-weight: 600;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
nav ul li a:hover {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 80px 0;
|
|
||||||
}
|
|
||||||
.form-container {
|
|
||||||
background-color: var(--surface-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
||||||
padding: 48px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 480px;
|
|
||||||
}
|
|
||||||
.form-title {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
font-size: 32px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
form input {
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
padding: 16px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
form button {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
padding: 16px 32px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: none;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
form button:hover {
|
|
||||||
background-color: #228b80;
|
|
||||||
}
|
|
||||||
.alert-message {
|
|
||||||
color: white;
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.alert-message.error {
|
|
||||||
background-color: var(--error-color);
|
|
||||||
}
|
|
||||||
.alert-message.success {
|
|
||||||
background-color: var(--success-color);
|
|
||||||
}
|
|
||||||
.form-footer {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
.form-footer a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
background-color: var(--text-color);
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
padding: 24px 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<nav class="container">
|
|
||||||
<a href="index.php" class="logo">TAP2SKILL</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="index.php#about">About</a></li>
|
|
||||||
<li><a href="index.php#features">Features</a></li>
|
|
||||||
<li><a href="index.php#testimonials">Testimonials</a></li>
|
|
||||||
<li><a href="index.php#contact">Contact</a></li>
|
|
||||||
<li><a href="register.php">Register</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="form-container">
|
|
||||||
<h2 class="form-title">Login</h2>
|
|
||||||
|
|
||||||
<?php if (isset($_GET['registered'])): ?>
|
|
||||||
<div class="alert-message success">Registration successful! Please log in.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert-message error"><?php echo htmlspecialchars($error); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<form action="login.php" method="post">
|
|
||||||
<input type="email" name="email" placeholder="Email Address" required>
|
|
||||||
<input type="password" name="password" placeholder="Password" required>
|
|
||||||
<button type="submit">Login</button>
|
|
||||||
</form>
|
|
||||||
<div class="form-footer">
|
|
||||||
<p>Don't have an account? <a href="register.php">Register here</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<div class="container">
|
|
||||||
<p>© 2025 TAP2SKILL. All Rights Reserved.</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
session_unset();
|
|
||||||
session_destroy();
|
|
||||||
header("Location: login.php");
|
|
||||||
exit;
|
|
||||||
108
quiz.php
108
quiz.php
@ -1,108 +0,0 @@
|
|||||||
<?php
|
|
||||||
session_start();
|
|
||||||
if (!isset($_SESSION['user_id'])) {
|
|
||||||
header('Location: login.php');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
require_once 'db/config.php';
|
|
||||||
$db = db();
|
|
||||||
|
|
||||||
$skill_id = $_GET['skill_id'] ?? 0;
|
|
||||||
$user_id = $_SESSION['user_id'];
|
|
||||||
|
|
||||||
// Fetch skill details
|
|
||||||
$stmt = $db->prepare("SELECT title FROM skills WHERE id = ?");
|
|
||||||
$stmt->execute([$skill_id]);
|
|
||||||
$skill_title = $stmt->fetchColumn();
|
|
||||||
|
|
||||||
if (!$skill_title) {
|
|
||||||
die('Skill not found!');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch quiz questions
|
|
||||||
$questions_stmt = $db->prepare("SELECT * FROM quizzes WHERE skill_id = ?");
|
|
||||||
$questions_stmt->execute([$skill_id]);
|
|
||||||
$questions = $questions_stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
||||||
$score = 0;
|
|
||||||
$total_questions = count($questions);
|
|
||||||
|
|
||||||
foreach ($questions as $question) {
|
|
||||||
$question_id = $question['id'];
|
|
||||||
$user_answer = $_POST['question_' . $question_id] ?? '';
|
|
||||||
if ($user_answer === $question['correct_option']) {
|
|
||||||
$score++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$percentage_score = ($total_questions > 0) ? round(($score / $total_questions) * 100) : 0;
|
|
||||||
|
|
||||||
// Save quiz attempt
|
|
||||||
$insert_stmt = $db->prepare("INSERT INTO quiz_attempts (user_id, skill_id, score) VALUES (?, ?, ?)");
|
|
||||||
$insert_stmt->execute([$user_id, $skill_id, $percentage_score]);
|
|
||||||
|
|
||||||
header("Location: learn.php?skill_id=$skill_id");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Quiz: <?php echo htmlspecialchars($skill_title); ?></title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container mt-5">
|
|
||||||
<a href="learn.php?skill_id=<?php echo $skill_id; ?>" class="btn btn-secondary mb-4">Back to Learning</a>
|
|
||||||
<h1>Quiz: <?php echo htmlspecialchars($skill_title); ?></h1>
|
|
||||||
|
|
||||||
<?php if (empty($questions)): ?>
|
|
||||||
<div class="alert alert-warning">No quiz questions available for this skill yet.</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<form method="POST">
|
|
||||||
<?php foreach ($questions as $index => $question): ?>
|
|
||||||
<div class="card my-4">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="mb-0">Question <?php echo $index + 1; ?></h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p class="card-text"><?php echo htmlspecialchars($question['question']); ?></p>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="radio" name="question_<?php echo $question['id']; ?>" id="q<?php echo $question['id']; ?>_a" value="a" required>
|
|
||||||
<label class="form-check-label" for="q<?php echo $question['id']; ?>_a">
|
|
||||||
<?php echo htmlspecialchars($question['option_a']); ?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="radio" name="question_<?php echo $question['id']; ?>" id="q<?php echo $question['id']; ?>_b" value="b">
|
|
||||||
<label class="form-check-label" for="q<?php echo $question['id']; ?>_b">
|
|
||||||
<?php echo htmlspecialchars($question['option_b']); ?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="radio" name="question_<?php echo $question['id']; ?>" id="q<?php echo $question['id']; ?>_c" value="c">
|
|
||||||
<label class="form-check-label" for="q<?php echo $question['id']; ?>_c">
|
|
||||||
<?php echo htmlspecialchars($question['option_c']); ?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="radio" name="question_<?php echo $question['id']; ?>" id="q<?php echo $question['id']; ?>_d" value="d">
|
|
||||||
<label class="form-check-label" for="q<?php echo $question['id']; ?>_d">
|
|
||||||
<?php echo htmlspecialchars($question['option_d']); ?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<button type="submit" class="btn btn-primary btn-lg">Submit Quiz</button>
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
226
register.php
226
register.php
@ -1,226 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/db/config.php';
|
|
||||||
|
|
||||||
$error = '';
|
|
||||||
$success = '';
|
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
|
||||||
$username = trim($_POST['username']);
|
|
||||||
$email = trim($_POST['email']);
|
|
||||||
$password = $_POST['password'];
|
|
||||||
|
|
||||||
if (empty($username) || empty($email) || empty($password)) {
|
|
||||||
$error = 'Please fill in all fields.';
|
|
||||||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
$error = 'Invalid email format.';
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username OR email = :email");
|
|
||||||
$stmt->execute(['username' => $username, 'email' => $email]);
|
|
||||||
if ($stmt->fetch()) {
|
|
||||||
$error = 'Username or email already exists.';
|
|
||||||
} else {
|
|
||||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
|
||||||
$insert_stmt = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (:username, :email, :password)");
|
|
||||||
$insert_stmt->execute([
|
|
||||||
'username' => $username,
|
|
||||||
'email' => $email,
|
|
||||||
'password' => $hashed_password
|
|
||||||
]);
|
|
||||||
|
|
||||||
header("Location: login.php?registered=true");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
$error = "Database error: " . $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Register - TAP2SKILL</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Lato:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--primary-color: #2A9D8F;
|
|
||||||
--secondary-color: #E9C46A;
|
|
||||||
--background-color: #F4F4F4;
|
|
||||||
--surface-color: #FFFFFF;
|
|
||||||
--text-color: #264653;
|
|
||||||
--error-color: #e74c3c;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
background-color: var(--background-color);
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
header {
|
|
||||||
background-color: var(--surface-color);
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
padding: 16px 0;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
nav {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.logo {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
nav ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
nav ul li {
|
|
||||||
margin-left: 32px;
|
|
||||||
}
|
|
||||||
nav ul li a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--text-color);
|
|
||||||
font-weight: 600;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
nav ul li a:hover {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 80px 0;
|
|
||||||
}
|
|
||||||
.form-container {
|
|
||||||
background-color: var(--surface-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
||||||
padding: 48px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 480px;
|
|
||||||
}
|
|
||||||
.form-title {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
font-size: 32px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
form input {
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
padding: 16px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 16px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
form button {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
padding: 16px 32px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: none;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
form button:hover {
|
|
||||||
background-color: #228b80;
|
|
||||||
}
|
|
||||||
.error-message {
|
|
||||||
background-color: var(--error-color);
|
|
||||||
color: white;
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.form-footer {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
.form-footer a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
background-color: var(--text-color);
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
padding: 24px 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<nav class="container">
|
|
||||||
<a href="index.php" class="logo">TAP2SKILL</a>
|
|
||||||
<ul>
|
|
||||||
<li><a href="index.php#about">About</a></li>
|
|
||||||
<li><a href="index.php#features">Features</a></li>
|
|
||||||
<li><a href="index.php#testimonials">Testimonials</a></li>
|
|
||||||
<li><a href="index.php#contact">Contact</a></li>
|
|
||||||
<li><a href="login.php">Login</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="form-container">
|
|
||||||
<h2 class="form-title">Create Account</h2>
|
|
||||||
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="error-message"><?php echo htmlspecialchars($error); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<form action="register.php" method="post">
|
|
||||||
<input type="text" name="username" placeholder="Username" required>
|
|
||||||
<input type="email" name="email" placeholder="Email Address" required>
|
|
||||||
<input type="password" name="password" placeholder="Password" required>
|
|
||||||
<button type="submit">Register</button>
|
|
||||||
</form>
|
|
||||||
<div class="form-footer">
|
|
||||||
<p>Already have an account? <a href="login.php">Login here</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<div class="container">
|
|
||||||
<p>© 2025 TAP2SKILL. All Rights Reserved.</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user