Initial RS Learning Lab Project

This commit is contained in:
Gokula Krishnan 2026-05-27 14:29:58 +05:30
parent a4371fe7ea
commit 4db6f367a1
252 changed files with 28190 additions and 1165 deletions

View File

@ -1,18 +1,2 @@
DirectoryIndex index.php index.html
Options -Indexes
Options -MultiViews
RewriteEngine On
# 0) Serve existing files/directories as-is
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# 1) Internal map: /page or /page/ -> /page.php (if such PHP file exists)
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
# 2) Optional: strip trailing slash for non-directories (keeps .php links working)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ $1 [R=301,L]

18
.htaccess_old Normal file
View File

@ -0,0 +1,18 @@
DirectoryIndex index.php index.html
Options -Indexes
Options -MultiViews
RewriteEngine On
# 0) Serve existing files/directories as-is
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# 1) Internal map: /page or /page/ -> /page.php (if such PHP file exists)
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
# 2) Optional: strip trailing slash for non-directories (keeps .php links working)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ $1 [R=301,L]

18
activate_institution.php Normal file
View File

@ -0,0 +1,18 @@
<?php
$conn = new mysqli("localhost", "root", "", "rs_lab");
if ($conn->connect_error) {
die("DB Connection failed");
}
if (isset($_GET['id'])) {
$id = intval($_GET['id']);
$sql = "UPDATE institutions SET status='active' WHERE id=$id";
$conn->query($sql);
}
$conn->close();
// Redirect back to dashboard
header("Location: admin_dashboard.php");
exit();

116
active_learners.php Normal file
View File

@ -0,0 +1,116 @@
<?php
// DB Connection
$conn = new mysqli("localhost", "root", "", "rs_lab");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
/*
ACTIVE LEARNERS:
- Practiced at least once in last 7 days
*/
$sql = "
SELECT
pa.roll_no,
MAX(pa.last_attempt_at) AS last_practice_date,
DATEDIFF(NOW(), MAX(pa.last_attempt_at)) AS days_gap
FROM practice_attempts pa
GROUP BY pa.roll_no
HAVING
MAX(pa.last_attempt_at) >= NOW() - INTERVAL 7 DAY
ORDER BY last_practice_date DESC
";
$result = $conn->query($sql);
?>
<!DOCTYPE html>
<html>
<head>
<title>Active Learners</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background: #0f172a;
color: #e5e7eb;
}
h2 {
text-align: center;
margin-top: 40px;
color: #22c55e;
}
.context {
text-align: center;
color: #9ca3af;
margin-top: 10px;
}
table {
width: 60%;
margin: 30px auto;
border-collapse: collapse;
background: #020617;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 0 15px rgba(0,0,0,0.6);
}
th, td {
padding: 14px;
border-bottom: 1px solid #1e293b;
text-align: left;
}
th {
background: #14532d;
color: #bbf7d0;
}
td {
color: #e5e7eb;
}
.gap-good {
color: #22c55e;
font-weight: bold;
}
.empty {
text-align: center;
padding: 20px;
color: #9ca3af;
}
</style>
</head>
<body>
<h2>🟢 Active Learners (Last 7 Days)</h2>
<div class="context">
These students are maintaining learning continuity.
</div>
<table>
<tr>
<th>Roll No</th>
<th>Last Practice Date</th>
<th>Days Since Last Practice</th>
</tr>
<?php
if ($result && $result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo "<tr>
<td>{$row['roll_no']}</td>
<td>{$row['last_practice_date']}</td>
<td class='gap-good'>{$row['days_gap']} day(s)</td>
</tr>";
}
} else {
echo "<tr>
<td colspan='3' class='empty'>No active learners yet</td>
</tr>";
}
?>
</table>
</body>
</html>
<?php $conn->close(); ?>

147
admin/admin_dashboard.php Normal file
View File

@ -0,0 +1,147 @@
<?php
session_start();
require_once("../config/db.php");
/*
ASSUMPTION:
- Founder already logged in
- institutions table has:
id, institution_name, email, username, password, institution_type, status
*/
// 🔒 BASIC ADMIN PROTECTION (simple)
if (!isset($_SESSION['admin_logged_in'])) {
header("Location: admin_login.php");
exit;
}
$message = "";
// 🔑 GENERATE CREDENTIALS
if (isset($_POST['generate_credentials'])) {
$institution_id = $_POST['institution_id'];
// fetch institution
$stmt = $pdo->prepare("
SELECT institution_name, institution_type
FROM institutions
WHERE id = ?
");
$stmt->execute([$institution_id]);
$inst = $stmt->fetch(PDO::FETCH_ASSOC);
if ($inst) {
// username pattern
$username = "inst_" . $institution_id;
// temporary password
$temp_password = strtoupper(substr(md5(time()), 0, 6));
$hashed_password = password_hash($temp_password, PASSWORD_DEFAULT);
// update institution record
$update = $pdo->prepare("
UPDATE institutions
SET username = ?, password = ?, status = 'active'
WHERE id = ?
");
$update->execute([$username, $hashed_password, $institution_id]);
// login URL (FINAL)
$login_url = "http://localhost/rs_lab/institution/login.php";
// activation message
$message = "
Hello 👋
Your institution has been activated on RS Learning Lab.
🏫 Institution Type: {$inst['institution_type']}
🔐 Institution Login Details:
🌐 Login URL: {$login_url}
👤 Username: {$username}
🔑 Temporary Password: {$temp_password}
Please log in and change your password immediately after first login.
RS Learning Lab Team
";
}
}
// fetch all institutions
$list = $pdo->query("
SELECT id, institution_name, email, institution_type, status
FROM institutions
ORDER BY id DESC
")->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html>
<head>
<title>Founder Dashboard | RS Learning Lab</title>
<style>
body { font-family: Arial; background:#f4f6f8; }
table { border-collapse: collapse; width: 100%; background:#fff; }
th, td { border:1px solid #ddd; padding:10px; text-align:left; }
th { background:#0b8c9f; color:white; }
.btn { padding:6px 12px; cursor:pointer; }
.btn-gen { background:#ff7a00; color:white; border:none; }
textarea { width:100%; height:180px; }
</style>
</head>
<body>
<h2>🧠 RS Learning Lab Founder Dashboard</h2>
<table>
<tr>
<th>ID</th>
<th>Institution</th>
<th>Email</th>
<th>Type</th>
<th>Status</th>
<th>Action</th>
</tr>
<?php foreach ($list as $row): ?>
<tr>
<td><?= $row['id'] ?></td>
<td><?= htmlspecialchars($row['institution_name']) ?></td>
<td><?= htmlspecialchars($row['email']) ?></td>
<td><?= ucfirst($row['institution_type']) ?></td>
<td><?= $row['status'] ?></td>
<td>
<form method="POST" style="margin:0;">
<input type="hidden" name="institution_id" value="<?= $row['id'] ?>">
<button class="btn btn-gen" name="generate_credentials">
Generate Credentials
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</table>
<?php if ($message): ?>
<h3>📩 Activation Message</h3>
<textarea readonly><?= trim($message) ?></textarea><br><br>
<button onclick="copyMsg()">Copy Message</button>
<?php endif; ?>
<script>
function copyMsg() {
const ta = document.querySelector("textarea");
ta.select();
document.execCommand("copy");
alert("Message copied!");
}
</script>
</body>
</html>

53
admin/admin_login.php Normal file
View File

@ -0,0 +1,53 @@
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// demo login no DB
$_SESSION['institution_logged_in'] = true;
$_SESSION['institution_name'] = "Demo Institution";
header("Location: ../dashboard.php");
exit;
}
include __DIR__ . '/../includes/header.php';
?>
<section class="section">
<div class="container">
<div class="grid" style="grid-template-columns: 1fr; max-width:420px; margin:0 auto;">
<div class="card">
<h2 style="text-align:center; margin-bottom:6px;">Institution Admin Login</h2>
<p style="text-align:center; color:var(--muted); margin-bottom:22px;">
Secure access for registered schools and colleges
</p>
<form method="post" action="">
<div class="form-group">
<label>Official Email ID</label>
<input type="email" name="email" placeholder="admin@institution.edu" required>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" placeholder="Enter your password" required>
</div>
<div style="margin-top:18px;">
<button class="btn btn-primary" style="width:100%;">
Login to Dashboard
</button>
</div>
</form>
<p style="text-align:center; margin-top:18px; font-size:13px; color:var(--muted);">
Only registered institutions can access the dashboard.
</p>
</div>
</div>
</div>
</section>
<?php
include __DIR__ . '/../includes/footer.php';
?>

View File

@ -0,0 +1,229 @@
<?php
// admin/computer_quiz_setup.php
session_start();
require_once __DIR__ . '/../config.php';
// 🔐 Auth guard
if (!isset($_SESSION['institution_id']) || $_SESSION['user_role'] !== 'admin') {
header("Location: login.php");
exit;
}
$institutionId = $_SESSION['institution_id'];
$institutionName = $_SESSION['institution_name'] ?? 'Your Institution';
$message = '';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$quiz_name = trim($_POST['quiz_name']);
$total_questions = 20; // FIXED for learning style
if ($quiz_name === '') {
$error = "Quiz name is required.";
} else {
try {
// 1⃣ Deactivate existing quizzes (only one active)
$stmt = $conn->prepare(
"UPDATE computer_quizzes
SET is_active = 0
WHERE institution_id = ?"
);
$stmt->bind_param("i", $institutionId);
$stmt->execute();
$stmt->close();
// 2⃣ Create new active quiz
$stmt = $conn->prepare(
"INSERT INTO computer_quizzes
(institution_id, quiz_name, total_questions, is_active, created_at)
VALUES (?, ?, ?, 1, NOW())"
);
$stmt->bind_param("isi", $institutionId, $quiz_name, $total_questions);
$stmt->execute();
$stmt->close();
$message = "Computer quiz started successfully.";
} catch (Throwable $e) {
$error = "Something went wrong. Please try again.";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Start Computer Quiz | RS Learning Lab</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;font-family:'Inter',system-ui}
body{margin:0;background:#0b1220;color:#e5e7eb}
.wrapper{display:flex;min-height:100vh}
/* Sidebar */
.sidebar{
width:240px;
background:#0f172a;
padding:24px 18px;
}
.logo{
font-size:20px;
font-weight:700;
color:#22d3ee;
margin-bottom:32px;
}
.nav a{
display:block;
padding:12px 14px;
margin-bottom:6px;
border-radius:10px;
color:#cbd5f5;
text-decoration:none;
font-size:14px;
}
.nav a.active,
.nav a:hover{
background:#1e293b;
color:#22d3ee;
}
/* Main */
.main{flex:1;display:flex;flex-direction:column}
/* Topbar */
.topbar{
background:#0f172a;
padding:18px 28px;
display:flex;
justify-content:space-between;
align-items:center;
border-bottom:1px solid #1e293b;
}
.topbar .title{font-size:18px;font-weight:600}
.topbar .right{font-size:13px;color:#94a3b8}
.logout{
margin-left:16px;
padding:8px 12px;
background:#1e293b;
border-radius:8px;
text-decoration:none;
color:#e5e7eb;
}
/* Content */
.content{padding:36px;max-width:720px}
/* Card */
.card{
background:#0f172a;
border:1px solid #1e293b;
border-radius:16px;
padding:32px;
}
.card h2{margin-top:0;margin-bottom:6px}
.card p{color:#94a3b8;margin-bottom:26px}
label{display:block;margin-top:18px;color:#cbd5f5}
input{
width:100%;
padding:14px;
border-radius:12px;
border:1px solid #1e293b;
background:#020617;
color:#e5e7eb;
margin-top:6px;
}
.note{
margin-top:10px;
font-size:13px;
color:#64748b;
}
button{
margin-top:28px;
padding:14px 20px;
border-radius:14px;
border:0;
background:#22d3ee;
color:#001018;
font-weight:700;
cursor:pointer;
}
.success{margin-top:16px;color:#86efac}
.error{margin-top:16px;color:#fca5a5}
</style>
</head>
<body>
<div class="wrapper">
<!-- Sidebar -->
<aside class="sidebar">
<div class="logo">RS Learning Lab</div>
<nav class="nav">
<a href="index.php">Dashboard</a>
<a class="active" href="#">Computer Quiz</a>
<a href="#">Manual Quiz</a>
<a href="#">Students</a>
<a href="#">Reports</a>
<a href="#">Coding Challenges</a>
</nav>
</aside>
<!-- Main -->
<div class="main">
<!-- Topbar -->
<div class="topbar">
<div class="title">Start Computer Quiz</div>
<div class="right">
<?= htmlspecialchars($institutionName) ?> · Admin
<a class="logout" href="logout.php">Logout</a>
</div>
</div>
<!-- Content -->
<div class="content">
<div class="card">
<h2>Create Learning Style Quiz</h2>
<p>
This quiz identifies learning patterns and behaviour.<br>
No marks. No answer key. No subject.
</p>
<?php if ($message): ?>
<div class="success"><?= htmlspecialchars($message) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="post">
<label>Quiz Name</label>
<input name="quiz_name" placeholder="Learning Style Assessment Class 8" required>
<div class="note">
Total questions fixed at 20<br>
Only one active quiz per institution
</div>
<button type="submit">Start Computer Quiz</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,107 @@
<?php
session_start();
require_once '../db_config.php';
if (!isset($_SESSION['admin_logged_in'])) {
header("Location: admin_login.php");
exit;
}
if (!isset($_GET['id'])) {
die("Invalid request");
}
$institution_id = (int) $_GET['id'];
/* Fetch institution */
$stmt = $pdo->prepare("SELECT institution_name, institution_type FROM institutions WHERE id = ?");
$stmt->execute([$institution_id]);
$inst = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$inst) {
die("Institution not found");
}
/* Generate credentials */
$username = strtoupper(substr($inst['institution_type'], 0, 3)) . "_RS_" . $institution_id;
$password_plain = substr(str_shuffle("ABCDEFGHJKLMNPQRSTUVWXYZ23456789"), 0, 8);
$password_hash = password_hash($password_plain, PASSWORD_DEFAULT);
/* Save credentials */
$update = $pdo->prepare("
UPDATE institutions
SET username = ?, password_hash = ?, credentials_created = 1
WHERE id = ?
");
$update->execute([$username, $password_hash, $institution_id]);
?>
<!DOCTYPE html>
<html>
<head>
<title>Credentials Generated</title>
< style >
body {
background: #050b14;
color: #eaf2ff;
font-family: Inter, sans-serif;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.card {
background: rgba(255,255,255,0.05);
padding: 30px 40px;
border-radius: 14px;
text-align: center;
width: 420px;
}
h2 {
margin-bottom: 10px;
}
p {
color: #b8c6de;
font-size: 14px;
}
.cred {
margin-top: 20px;
font-size: 15px;
background: rgba(255,255,255,0.08);
padding: 14px;
border-radius: 10px;
}
a {
display: inline-block;
margin-top: 20px;
color: #7fd4ff;
text-decoration: none;
}
</ style >
</head>
<body>
<div class="card">
<h2>Credentials Created</h2>
<p>Send these credentials to the institution</p>
<div class="cred">
<strong>Username:</strong><br><?= $username ?><br><br>
<strong>Password:</strong><br><?= $password_plain ?>
</div>
<p style="margin-top:14px;font-size:12px;">
Institution will be forced to change password on first login.
</p>
<a href="admin_dashboard.php"> Back to Dashboard</a>
</div>
</body>
</html>

View File

@ -1,66 +1,70 @@
<?php
session_start();
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
header('Location: login.php');
exit;
}
include __DIR__ . '/../includes/header.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="index.php">Admin Panel</a>
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="logout.php">Logout</a>
</li>
</ul>
</div>
</nav>
<section class="section">
<div class="container">
<div class="container py-5">
<h1 class="display-4 fw-bold">Admin Dashboard</h1>
<p class="fs-5 text-muted">Welcome to the admin panel. Here you can manage the content of the application.</p>
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">Challenges</h5>
<p class="card-text">Manage coding challenges.</p>
<a href="challenges.php" class="btn btn-primary">Go to Challenges</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">Competitions</h5>
<p class="card-text">Manage coding competitions.</p>
<a href="competitions.php" class="btn btn-primary">Go to Competitions</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">Users</h5>
<p class="card-text">Manage users.</p>
<a href="users.php" class="btn btn-primary">Go to Users</a>
</div>
</div>
</div>
</div>
<!-- Dashboard Header -->
<div style="margin-bottom:28px;">
<h1 style="font-size:28px; font-weight:800; margin-bottom:6px;">
Institution Dashboard
</h1>
<p style="color:var(--muted);">
Manage learning insights, coding challenges, and certifications from one place.
</p>
</div>
</body>
</html>
<!-- Dashboard Cards -->
<div class="grid">
<!-- Learning Insight -->
<div class="card">
<h3>Learning Style Assessment</h3>
<p>
Understand how students learn through pattern-based assessments
without marks or exam pressure.
</p>
<div style="margin-top:16px;">
<a href="../manual_mode_setup.php" class="btn btn-primary">
Start Assessment
</a>
</div>
</div>
<!-- Coding Challenges -->
<div class="card">
<h3>Coding Challenges</h3>
<p>
Structured coding tracks designed to build logical thinking
and real-world problem-solving skills.
</p>
<div style="margin-top:16px;">
<a href="../coding/tracks.php" class="btn btn-primary">
View Coding Tracks
</a>
</div>
</div>
<!-- Certificates -->
<div class="card">
<h3>Certificates</h3>
<p>
Generate professional participation certificates
recognizing student effort and demonstrated skills.
</p>
<div style="margin-top:16px;">
<a href="../certificates/generate_certificate_pdf.php" class="btn btn-primary">
Preview Certificate
</a>
</div>
</div>
</div>
</div>
</section>
<?php
include __DIR__ . '/../includes/footer.php';
?>

View File

@ -1,58 +0,0 @@
<?php
session_start();
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if ($username === 'admin' && $password === 'password') {
$_SESSION['admin_logged_in'] = true;
header('Location: index.php');
exit;
} else {
$errors[] = 'Invalid username or password';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-4">
<div class="card mt-5">
<div class="card-header">Admin Login</div>
<div class="card-body">
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p><?php echo $error; ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="login.php" method="post">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

196
admin_dashboard.php Normal file
View File

@ -0,0 +1,196 @@
<?php
$conn = new mysqli("localhost", "root", "", "rs_lab");
if ($conn->connect_error) {
die("DB Connection failed");
}
/* GET VIEW FILTER */
$view = $_GET['view'] ?? 'active';
/* COUNTS */
$total = $conn->query("SELECT COUNT(*) c FROM institutions WHERE deleted_at IS NULL")->fetch_assoc()['c'];
$active = $conn->query("SELECT COUNT(*) c FROM institutions WHERE status='active' AND deleted_at IS NULL")->fetch_assoc()['c'];
$inactive = $conn->query("SELECT COUNT(*) c FROM institutions WHERE status='inactive' AND deleted_at IS NULL")->fetch_assoc()['c'];
$deleted = $conn->query("SELECT COUNT(*) c FROM institutions WHERE deleted_at IS NOT NULL")->fetch_assoc()['c'];
/* FILTER QUERY */
if($view == 'active'){
$result = $conn->query("SELECT * FROM institutions WHERE status='active' AND deleted_at IS NULL");
}
elseif($view == 'inactive'){
$result = $conn->query("SELECT * FROM institutions WHERE status='inactive' AND deleted_at IS NULL");
}
elseif($view == 'deleted'){
$result = $conn->query("SELECT * FROM institutions WHERE deleted_at IS NOT NULL");
}
else{
$result = $conn->query("SELECT * FROM institutions WHERE deleted_at IS NULL");
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Founder Dashboard | RS Learning Lab</title>
<style>
body {
margin: 0;
font-family: Arial;
background: radial-gradient(circle at top, #020617, #0f172a);
color: #e5e7eb;
}
.container {
max-width: 1200px;
margin: 40px auto;
}
.cards {
display: flex;
gap: 20px;
margin-bottom: 40px;
}
.card {
flex: 1;
padding: 25px;
border-radius: 16px;
background: #020617;
cursor: pointer;
text-align:center;
}
.total { border-left: 4px solid #60a5fa; }
.active { border-left: 4px solid #22c55e; }
.inactive { border-left: 4px solid #facc15; }
.deleted { border-left: 4px solid #ef4444; }
table {
width:100%;
border-collapse: collapse;
}
th,td {
padding:12px;
border-bottom:1px solid #1e293b;
}
.badge {
cursor:pointer;
font-weight:bold;
}
.badge.active { color:#22c55e; }
.badge.inactive { color:#ef4444; }
.delete-btn{
background:red;
color:white;
border:none;
padding:6px 10px;
border-radius:6px;
cursor:pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>Founder Dashboard</h1>
<div class="cards">
<a href="?view=all" style="flex:1;text-decoration:none;color:white;">
<div class="card total">
<h2><?= $total ?></h2>
<p>Total</p>
</div>
</a>
<a href="?view=active" style="flex:1;text-decoration:none;color:white;">
<div class="card active">
<h2><?= $active ?></h2>
<p>Active</p>
</div>
</a>
<a href="?view=inactive" style="flex:1;text-decoration:none;color:white;">
<div class="card inactive">
<h2><?= $inactive ?></h2>
<p>Inactive</p>
</div>
</a>
<a href="?view=deleted" style="flex:1;text-decoration:none;color:white;">
<div class="card deleted">
<h2><?= $deleted ?></h2>
<p>Deleted</p>
</div>
</a>
</div>
<table>
<tr>
<th>Name</th>
<th>Contact</th>
<th>Phone</th>
<th>Email</th>
<th>Status</th>
<th>Action</th>
</tr>
<?php while($row = $result->fetch_assoc()): ?>
<tr>
<td>
<a href="institution_detail.php?id=<?= $row['id'] ?>"
style="color:#60a5fa;text-decoration:none;">
<?= htmlspecialchars($row['institution_name']) ?>
</a>
</td>
<td><?= $row['contact_person'] ?></td>
<td><?= $row['phone'] ?></td>
<td><?= $row['email'] ?></td>
<td>
<?php if($row['deleted_at']): ?>
<span style="color:red;">Deleted</span>
<?php else: ?>
<a href="toggle_institution_status.php?id=<?= $row['id'] ?>&view=<?= $view ?>">
<span class="badge <?= $row['status'] ?>">
<?= ucfirst($row['status']) ?>
</span>
</a>
<?php endif; ?>
</td>
<td>
<?php if(!$row['deleted_at']): ?>
<form method="POST" action="delete_institution.php">
<input type="hidden" name="id" value="<?= $row['id'] ?>">
<input type="hidden" name="view" value="<?= $view ?>">
<button class="delete-btn">Delete</button>
</form>
<?php else: ?>
<?php endif; ?>
</td>
</tr>
<?php endwhile; ?>
</table>
</div>
</body>
</html>
<?php $conn->close(); ?>

187
admin_login.php Normal file
View File

@ -0,0 +1,187 @@
<?php
// admin_login.php
session_start();
$error = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
// Demo credentials
if ($email === "admin@rslearninglab.in" && $password === "admin123") {
$_SESSION['admin_logged_in'] = true;
header("Location: dashboard.php");
exit;
} else {
$error = "Invalid admin credentials";
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Admin Login | RS Learning Lab</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
margin: 0;
font-family: 'Segoe UI', system-ui, sans-serif;
background: radial-gradient(circle at top, #0b1a2a, #05080f);
color: #fff;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-wrapper {
background: rgba(255,255,255,0.04);
backdrop-filter: blur(14px);
border-radius: 18px;
padding: 40px;
width: 100%;
max-width: 420px;
box-shadow: 0 30px 80px rgba(0,0,0,0.5);
}
.brand {
text-align: center;
margin-bottom: 28px;
}
.brand img {
width: 64px;
margin-bottom: 10px;
}
.brand h1 {
font-size: 28px;
margin: 0;
}
.brand p {
font-size: 14px;
opacity: 0.75;
}
.form-group {
margin-bottom: 18px;
}
label {
display: block;
font-size: 13px;
opacity: 0.8;
margin-bottom: 6px;
}
input {
width: 100%;
padding: 12px 14px;
border-radius: 10px;
border: none;
background: rgba(255,255,255,0.08);
color: #fff;
outline: none;
}
input::placeholder {
color: rgba(255,255,255,0.4);
}
button {
width: 100%;
padding: 14px;
border-radius: 12px;
border: none;
background: linear-gradient(135deg, #0b8c9f, #1fd1f9);
color: #000;
font-weight: 600;
font-size: 15px;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(31,209,249,0.4);
}
.error {
background: rgba(255,0,0,0.15);
padding: 10px;
border-radius: 8px;
font-size: 13px;
margin-bottom: 14px;
text-align: center;
}
.demo-box {
margin-top: 24px;
background: rgba(255,255,255,0.06);
border-radius: 12px;
padding: 14px;
font-size: 13px;
opacity: 0.85;
}
.demo-box strong {
color: #1fd1f9;
}
.footer {
text-align: center;
margin-top: 24px;
font-size: 12px;
opacity: 0.6;
}
</style>
</head>
<body>
<div class="login-wrapper">
<div class="brand">
<!-- Optional logo -->
<!-- <img src="assets/logo.png" alt="RS Learning Lab"> -->
<h1>RS Learning Lab</h1>
<p>Institution Admin Login</p>
</div>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<form method="post">
<div class="form-group">
<label>Email Address</label>
<input type="email" name="email" placeholder="admin@rslearninglab.in" required>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" placeholder="••••••••" required>
</div>
<button type="submit">Login as Admin</button>
</form>
<div class="demo-box">
<strong>Demo Credentials</strong><br>
Email: admin@rslearninglab.in<br>
Password: admin123
</div>
<div class="footer">
© RS Learning Lab · Learning Behaviour Platform
</div>
</div>
</body>
</html>

71
api_scan_omr.php Normal file
View File

@ -0,0 +1,71 @@
<?php
// api_scan_omr.php
// RS Learning Lab OMR Scan API (Silent Mode)
$pythonPath = "python";
$scriptPath = __DIR__ . "/omr_read_sheet.py";
$uploadDir = __DIR__ . "/uploads/";
// Create uploads folder
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
// Student context
$roll_no = $_GET['roll'] ?? $_POST['roll'] ?? null;
$student_name = $_GET['name'] ?? $_POST['name'] ?? null;
// Validate upload
if (!isset($_FILES['omr_image'])) {
return ""; // 🔥 changed
}
$tmpName = $_FILES['omr_image']['tmp_name'];
$originalName = basename($_FILES['omr_image']['name']);
$ext = pathinfo($originalName, PATHINFO_EXTENSION);
// Allow only images
$allowed = ['jpg', 'jpeg', 'png'];
if (!in_array(strtolower($ext), $allowed)) {
return ""; // 🔥 changed
}
// Save image
$filename = time() . "_" . uniqid() . "." . $ext;
$targetPath = $uploadDir . $filename;
if (!move_uploaded_file($tmpName, $targetPath)) {
return ""; // 🔥 changed
}
// Call Python script
$command = escapeshellcmd("$pythonPath \"$scriptPath\" \"$targetPath\"");
$raw_output = shell_exec($command);
// Clean output
if (!$raw_output) {
return ""; // 🔥 changed
}
$raw_output = trim($raw_output);
$raw_output = str_replace(["\n", "\r", " "], "", $raw_output);
$clean_answers = preg_replace("/[^A-D,]/", "", strtoupper($raw_output));
$clean_answers = rtrim($clean_answers, ",");
// Logging (optional)
$logEntry = [
'timestamp' => date("Y-m-d H:i:s"),
'roll_no' => $roll_no,
'student_name' => $student_name,
'answers' => $clean_answers
];
file_put_contents(
__DIR__ . "/omr_log.txt",
json_encode($logEntry) . PHP_EOL,
FILE_APPEND
);
// 🔥 FINAL CHANGE: RETURN instead of echo
return $clean_answers;

View File

@ -1,13 +0,0 @@
body {
background-color: #f8f9fa;
}
.login-container {
max-width: 400px;
margin: 100px auto;
padding: 30px;
border: 1px solid #dee2e6;
border-radius: 5px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

View File

@ -1,74 +0,0 @@
:root {
--primary-color: #20C997;
--secondary-color: #FD7E14;
--dark-color: #212529;
--light-color: #F8F9FA;
}
body {
font-family: 'Poppins', sans-serif;
color: var(--dark-color);
}
.btn-primary, .btn-primary:hover, .btn-primary:active, .btn-primary:visited {
background: var(--primary-color) !important;
border-color: var(--primary-color) !important;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(32, 201, 151, 0.2);
}
.card {
border: none;
border-radius: 0.5rem;
box-shadow: 0 4px 25px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
}
.card-img-container {
position: relative;
}
.card-img-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0));
padding: 1.5rem;
}
.card-img-overlay .card-title a {
color: white;
text-decoration: none;
}
.card-img-overlay .badge {
position: absolute;
top: 1rem;
right: 1rem;
}
.navbar-brand {
letter-spacing: -0.5px;
}
.logo-img {
vertical-align: middle;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 700;
}
.hero-section {
background: linear-gradient(45deg, var(--primary-color), #5be3c2);
}

View File

@ -1,56 +0,0 @@
document.addEventListener('DOMContentLoaded', function() {
const difficultyFilter = document.getElementById('difficulty-filter');
const learningStyleFilter = document.getElementById('learning-style-filter');
const sortBy = document.getElementById('sort-by');
const challengeList = document.querySelector('.challenge-list');
if (difficultyFilter) {
function fetchChallenges() {
const difficulty = difficultyFilter.value;
const learningStyle = learningStyleFilter.value;
const sort = sortBy.value;
const formData = new FormData();
formData.append('difficulty', difficulty);
formData.append('learning_style', learningStyle);
formData.append('sort_by', sort);
fetch('api/filter_challenges.php', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => {
challengeList.innerHTML = data;
});
}
difficultyFilter.addEventListener('change', fetchChallenges);
learningStyleFilter.addEventListener('change', fetchChallenges);
sortBy.addEventListener('change', fetchChallenges);
}
const runCodeBtn = document.getElementById('run-code');
if (runCodeBtn) {
runCodeBtn.addEventListener('click', function() {
const solution = document.getElementById('solution').value;
const language = document.getElementById('language').value;
const challengeId = document.querySelector('input[name="challenge_id"]').value;
const formData = new FormData();
formData.append('solution', solution);
formData.append('language', language);
formData.append('challenge_id', challengeId);
fetch('api/run_code.php', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => {
const outputDiv = document.getElementById('output');
outputDiv.innerHTML = data;
});
});
}
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

72
auto_route_engine.php Normal file
View File

@ -0,0 +1,72 @@
<?php
// auto_route_engine.php
// RS Learning Lab Momentum-based Auto Routing Engine
// Decides next learning track automatically
session_start();
/* ===============================
DB CONFIG
================================ */
require_once "db_config.php";
/* ===============================
INPUT
================================ */
$roll_no = $_GET['roll'] ?? $_SESSION['roll'] ?? null;
$name = $_GET['name'] ?? $_SESSION['name'] ?? null;
$concept_id = $_GET['concept'] ?? 1;
if (!$roll_no || !$name) {
die("Student context missing");
}
$_SESSION['roll'] = $roll_no;
$_SESSION['name'] = $name;
/* ===============================
FETCH MOMENTUM
================================ */
$stmt = $pdo->prepare("
SELECT momentum_state
FROM learning_momentum
WHERE roll_no = :roll
LIMIT 1
");
$stmt->execute([':roll' => $roll_no]);
$data = $stmt->fetch();
$momentum_state = $data['momentum_state'] ?? "Building";
/* ===============================
ROUTING RULE ENGINE
================================ */
switch ($momentum_state) {
case "Applying":
$next_track = "final_track.php";
$reason = "You are ready to apply what youve learned calmly.";
break;
case "Stabilizing":
$next_track = "reinforcement_track.php";
$reason = "Lets stabilize your understanding before final application.";
break;
case "Reset":
case "Building":
default:
$next_track = "practice_track.php";
$reason = "Practice builds confidence. Lets continue calmly.";
break;
}
/* ===============================
AUTO REDIRECT
================================ */
header("Location: $next_track?roll=" . urlencode($roll_no) .
"&name=" . urlencode($name) .
"&concept=" . urlencode($concept_id));
exit;

70
bulk_download.php Normal file
View File

@ -0,0 +1,70 @@
<?php
$folder = "generated_pdfs/";
$files = glob($folder . "*.pdf");
?>
<!DOCTYPE html>
<html>
<head>
<title>Download Reports</title>
<style>
body{
background:#0b1020;
color:white;
font-family:Arial;
padding:30px;
}
.card{
background:#111827;
padding:25px;
border-radius:10px;
max-width:700px;
margin:auto;
}
a{
display:block;
margin:10px 0;
padding:10px;
background:#0ea5e9;
color:white;
text-decoration:none;
border-radius:6px;
width:300px;
text-align:center;
}
</style>
</head>
<body>
<div class="card">
<h2>Learning Style Reports</h2>
<?php
if(count($files)==0){
echo "No reports generated yet.";
}
foreach($files as $file){
$name = basename($file);
echo "<a href='$file' download>$name</a>";
}
?>
</div>
</body>
</html>

48
certificate.html Normal file
View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial;
border: 3px solid teal;
padding: 40px;
}
.center { text-align: center; }
.footer {
display: flex;
justify-content: space-between;
margin-top: 60px;
}
</style>
</head>
<body>
<div class="center">
<img src="../assets/logo.png" width="120"><br><br>
<h1>CERTIFICATE OF PARTICIPATION</h1>
</div>
<p class="center">This is to certify that</p>
<h2 class="center">STUDENT NAME</h2>
<p class="center">
has successfully completed the coding challenge track
<b>Python Foundations</b><br>
conducted by <b>RS Learning Lab</b>
</p>
<div class="footer">
<div>
<img src="../assets/signature.png" width="150"><br>
<b>Gokula Krishnan</b><br>
Founder RS Learning Lab
</div>
<div>
Verified Learning Certificate<br>
www.rslearninglab.in
</div>
</div>
</body>
</html>

137
certificate_verify.php Normal file
View File

@ -0,0 +1,137 @@
<?php
// ============================================
// RS Learning Lab - Certificate Verification
// ============================================
require_once 'db_config.php';
$verified = false;
$error = '';
$data = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$roll_no = $_POST['roll_no'] ?? '';
$concept_id = isset($_POST['concept_id']) ? (int)$_POST['concept_id'] : 0;
if ($roll_no === '' || $concept_id === 0) {
$error = "Please enter valid details.";
} else {
$stmt = $pdo->prepare("
SELECT
qa.roll_no,
SUM(qa.is_correct) AS correct_count,
COUNT(*) AS total_count
FROM question_attempts qa
WHERE qa.roll_no = ?
AND qa.track = 'final'
");
$stmt->execute([$roll_no]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row && $row['total_count'] > 0) {
$percent = round(($row['correct_count'] / $row['total_count']) * 100);
if ($percent >= 60) {
$verified = true;
$data = [
'roll_no' => $roll_no,
'concept' => $concept_id,
'score' => $percent,
'date' => date("d M Y")
];
} else {
$error = "Certificate not eligible (final not passed).";
}
} else {
$error = "No final assessment found.";
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Certificate Verification</title>
<style>
body {
background:#020617;
color:#e5e7eb;
font-family:Arial;
}
.box {
max-width:520px;
margin:80px auto;
background:#020617;
padding:30px;
border-radius:16px;
border:1px solid #1e293b;
}
h2 { color:#38bdf8; text-align:center; }
input {
width:100%;
padding:10px;
margin:10px 0;
border-radius:8px;
border:none;
}
button {
width:100%;
padding:12px;
background:#0b8c9f;
color:white;
border:none;
border-radius:8px;
font-weight:bold;
cursor:pointer;
}
.success {
margin-top:20px;
padding:15px;
background:#022c22;
border-radius:10px;
color:#22c55e;
}
.error {
margin-top:20px;
padding:15px;
background:#3f1d1d;
border-radius:10px;
color:#f87171;
}
</style>
</head>
<body>
<div class="box">
<h2>Certificate Verification</h2>
<form method="post">
<input type="text" name="roll_no" placeholder="Student Roll No" required>
<input type="number" name="concept_id" placeholder="Concept ID" required>
<button type="submit">Verify Certificate</button>
</form>
<?php if ($verified): ?>
<div class="success">
Certificate Verified<br><br>
Roll No: <strong><?=htmlspecialchars($data['roll_no'])?></strong><br>
Concept: <strong><?=htmlspecialchars($data['concept'])?></strong><br>
Score: <strong><?=$data['score']?>%</strong><br>
Status: <strong>VALID</strong><br>
Date: <strong><?=$data['date']?></strong>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error">
<?=$error?>
</div>
<?php endif; ?>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1,130 @@
<?php
function base64img($path) {
$type = pathinfo($path, PATHINFO_EXTENSION);
$data = file_get_contents($path);
return 'data:image/' . $type . ';base64,' . base64_encode($data);
}
$logo = base64img(__DIR__ . '/../assets/logo.png');
$sign = base64img(__DIR__ . '/../assets/signature.png');
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4 landscape;
margin: 12mm;
}
body {
font-family: Georgia, serif;
margin: 0;
padding: 0;
}
.certificate {
border: 2px solid #0b8c9f;
padding: 15mm;
}
.header {
text-align: center;
}
.header img {
height: 90px;
}
.title {
font-size: 26px;
letter-spacing: 2px;
margin: 8mm 0;
}
.content {
text-align: center;
}
.student {
font-size: 30px;
font-weight: bold;
margin: 8px 0;
}
.track {
font-size: 22px;
font-weight: bold;
}
.footer {
margin-top: 15mm;
display: table;
width: 100%;
font-size: 13px;
}
.footer-left {
display: table-cell;
}
.footer-right {
display: table-cell;
text-align: right;
}
.signature img {
height: 75px;
}
.verify {
text-align: center;
margin-top: 6mm;
font-size: 11px;
color: #555;
}
</style>
</head>
<body>
<div class="certificate">
<div class="header">
<img src="<?= $logo ?>">
<div class="title">CERTIFICATE OF PARTICIPATION</div>
</div>
<div class="content">
<p>This is to certify that</p>
<div class="student"><?= htmlspecialchars($student_name) ?></div>
<p>has successfully participated in the</p>
<div class="track"><?= htmlspecialchars($track_name) ?></div>
<p>conducted by <b>RS Learning Lab</b></p>
</div>
<div class="footer">
<div class="footer-left signature">
<img src="<?= $sign ?>"><br>
<b>Gokula Krishnan</b><br>
Founder RS Learning Lab
</div>
<div class="footer-right">
Certificate ID: <?= htmlspecialchars($certificate_id) ?><br>
Issued on: <?= date("d M Y", strtotime($issued_at)) ?><br>
<small>Verify at: rslearninglab.com/verify</small>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4;
margin: 0;
}
body {
margin: 0;
}
.page {
position: relative;
width: 210mm;
height: 297mm;
}
/* FULL BACKGROUND IMAGE */
.bg {
position: absolute;
top: 0;
left: 0;
width: 210mm;
height: 297mm;
}
/* STUDENT NAME */
.name {
position: absolute;
top: 150mm; /* 🔥 adjust small up/down if needed */
width: 100%;
text-align: center;
font-size: 32px;
font-weight: bold;
color: #0f172a;
}
/* CERTIFICATE ID */
.cert-id {
position: absolute;
top: 35mm;
right: 30mm;
font-size: 12px;
color: #334155;
}
/* ISSUE DATE */
.issue-date {
position: absolute;
top: 42mm;
right: 30mm;
font-size: 12px;
color: #334155;
}
</style>
</head>
<body>
<div class="page">
<img class="bg" src="assets/certificate_bg.png">
<div class="cert-id">Certificate ID: {{CERT_ID}}</div>
<div class="issue-date">Issue Date: {{ISSUE_DATE}}</div>
<div class="name">{{NAME}}</div>
</div>
</body>
</html>

View File

@ -0,0 +1,25 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use Dompdf\Dompdf;
use Dompdf\Options;
$name = $_GET['name'] ?? 'STUDENT NAME';
$certId = 'RSL-' . date('Y') . '-' . rand(10000,99999);
$issueDate = date('F Y');
$html = file_get_contents(__DIR__ . '/certificate_tmp.html');
$html = str_replace('{{NAME}}', htmlspecialchars($name), $html);
$html = str_replace('{{CERT_ID}}', $certId, $html);
$html = str_replace('{{ISSUE_DATE}}', $issueDate, $html);
$options = new Options();
$options->set('isRemoteEnabled', true);
$options->set('chroot', realpath(__DIR__));
$dompdf = new Dompdf($options);
$dompdf->loadHtml($html);
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
$dompdf->stream("certificate_$name.pdf", ["Attachment" => false]);

View File

@ -0,0 +1,53 @@
<?php
include __DIR__ . '/../includes/header.php';
?>
<section class="section">
<div class="container">
<div style="margin-bottom:26px;">
<h1 style="font-size:26px; font-weight:800;">Certificate Preview</h1>
<p style="color:var(--muted);">
This is how the official certificate will appear for students.
</p>
</div>
<div class="certificate">
<img src="/rs_lab/assets/rs_logo.png" style="height:60px; margin-bottom:20px;">
<h2 style="letter-spacing:1px;">Certificate of Participation</h2>
<p style="margin-top:16px;">This is to certify that</p>
<div class="student-name">Gokul</div>
<p>
has actively participated in the RS Learning Lab Coding Challenge Program
and demonstrated consistent learning effort.
</p>
<h4 style="margin-top:24px;">Demonstrated Skills</h4>
<p>Logical Thinking · Problem Solving · Coding Fundamentals</p>
<div style="margin-top:40px; text-align:left;">
<img src="/rs_lab/assets/signature.png" style="height:50px;">
<div class="signature-name">Gokula Krishnan</div>
<div style="font-size:13px; color:var(--muted);">
Founder RS Learning Lab
</div>
</div>
</div>
<div style="margin-top:26px;">
<a href="generate_certificate_pdf.php" class="btn btn-primary">
Download Official Certificate (PDF)
</a>
</div>
</div>
</section>
<?php
include __DIR__ . '/../includes/footer.php';
?>

59
certificates/verify.php Normal file
View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<title>Certificate Verification | RS Learning Lab</title>
<style>
body {
background:#0f172a;
color:#ffffff;
font-family: Arial, sans-serif;
}
.card {
width:420px;
margin:120px auto;
background:#020617;
padding:30px;
border-radius:14px;
box-shadow:0 0 25px rgba(0,0,0,0.6);
}
h2 {
text-align:center;
}
input {
width:100%;
padding:12px;
margin-top:15px;
border-radius:8px;
border:none;
font-size:15px;
}
button {
width:100%;
padding:12px;
margin-top:20px;
background:#0b8c9f;
color:#fff;
border:none;
border-radius:8px;
font-size:16px;
cursor:pointer;
}
button:hover {
background:#0aa0b5;
}
</style>
</head>
<body>
<div class="card">
<h2>Certificate Verification</h2>
<p style="text-align:center;">Enter Certificate ID</p>
<form action="verify_action.php" method="POST">
<input type="text" name="certificate_id" placeholder="Eg: RSL-2025-00123" required>
<button type="submit">Verify Certificate</button>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,31 @@
<?php
session_start();
include_once __DIR__ . '/../includes/db.php';
// Allow only POST request
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
die("Invalid Request");
}
// Check required input
if (!isset($_POST['certificate_id']) || empty($_POST['certificate_id'])) {
die("Invalid Request");
}
$certificate_id = trim($_POST['certificate_id']);
$stmt = $pdo->prepare("SELECT * FROM certificates WHERE certificate_id = ?");
$stmt->execute([$certificate_id]);
$cert = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$cert) {
echo "Certificate Not Found";
exit;
}
?>
<h2> Certificate Verified</h2>
<p><strong>Student:</strong> <?= htmlspecialchars($cert['student_name']) ?></p>
<p><strong>Course:</strong> <?= htmlspecialchars($cert['course']) ?></p>
<p><strong>Date:</strong> <?= htmlspecialchars($cert['issued_on']) ?></p>

View File

@ -1,164 +0,0 @@
<?php
include 'includes/header.php';
require_once 'db/config.php';
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: challenges.php');
exit();
}
$challenge_id = $_GET['id'];
$pdo = db();
$stmt = $pdo->prepare('SELECT * FROM challenges WHERE id = ? AND deleted_at IS NULL');
$stmt->execute([$challenge_id]);
$challenge = $stmt->fetch();
if (!$challenge) {
header('Location: challenges.php');
exit();
}
$user_id = $_SESSION['user_id'] ?? null;
$submissions = [];
if ($user_id) {
$stmt = $pdo->prepare('SELECT * FROM challenge_submissions WHERE user_id = ? AND challenge_id = ? ORDER BY submitted_at DESC');
$stmt->execute([$user_id, $challenge_id]);
$submissions = $stmt->fetchAll();
}
?>
<div class="container py-5">
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-body">
<h5 class="card-title">Submit Your Solution</h5>
<form action="submit_solution.php" method="post">
<input type="hidden" name="challenge_id" value="<?php echo $challenge['id']; ?>">
<div class="mb-3">
<label for="language" class="form-label">Language</label>
<select class="form-select" id="language" name="language" required>
<option value="python">Python</option>
<option value="javascript">JavaScript</option>
<option value="php">PHP</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
</select>
</div>
<div class="mb-3">
<label for="solution" class="form-label">Your Code</label>
<div id="editor"></div>
<textarea class="form-control d-none" id="solution" name="solution" rows="10" required></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Solution</button>
<button type="button" class="btn btn-secondary" id="run-code">Run Code</button>
</form>
<div id="output" class="mt-3"></div>
</div>
</div>
</div>
<div class="col-md-4">
<h1 class="display-4 fw-bold"><?php echo htmlspecialchars($challenge['title']); ?></h1>
<p class="fs-5 text-muted"><strong>Difficulty:</strong> <?php echo htmlspecialchars($challenge['difficulty']); ?> | <strong>Learning Style:</strong> <?php echo htmlspecialchars($challenge['learning_style']); ?></p>
<hr>
<ul class="nav nav-tabs" id="challengeTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="description-tab" data-bs-toggle="tab" data-bs-target="#description" type="button" role="tab" aria-controls="description" aria-selected="true">Description</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="submissions-tab" data-bs-toggle="tab" data-bs-target="#submissions" type="button" role="tab" aria-controls="submissions" aria-selected="false">My Submissions</button>
</li>
</ul>
<div class="tab-content" id="challengeTabContent">
<div class="tab-pane fade show active" id="description" role="tabpanel" aria-labelledby="description-tab">
<div class="pt-4">
<h2>Description</h2>
<p><?php echo nl2br(htmlspecialchars($challenge['description'])); ?></p>
<?php if (!empty($challenge['sample_cases_json'])) : ?>
<h2>Sample Cases</h2>
<pre class="bg-light p-3 rounded"><code><?php
$samples = json_decode($challenge['sample_cases_json'], true);
foreach ($samples as $sample) {
echo "<strong>Input:</strong>\n" . htmlspecialchars($sample['input']) . "\n";
echo "<strong>Output:</strong>\n" . htmlspecialchars($sample['output']) . "\n\n";
}
?></code></pre>
<?php endif; ?>
</div>
</div>
<div class="tab-pane fade" id="submissions" role="tabpanel" aria-labelledby="submissions-tab">
<div class="pt-4">
<h2>My Submissions</h2>
<table class="table">
<thead>
<tr>
<th>Attempt</th>
<th>Language</th>
<th>Status</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php if (count($submissions) > 0) : ?>
<?php foreach ($submissions as $submission) : ?>
<tr>
<td><?php echo $submission['attempt_number']; ?></td>
<td><?php echo htmlspecialchars($submission['language']); ?></td>
<td><span class="badge bg-<?php echo get_status_badge_class($submission['status']); ?>"><?php echo htmlspecialchars($submission['status']); ?></span></td>
<td><?php echo $submission['submitted_at']; ?></td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr>
<td colspan="4" class="text-center">You have not made any submissions for this challenge yet.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<?php
function get_status_badge_class($status) {
switch ($status) {
case 'passed':
return 'success';
case 'failed':
return 'danger';
case 'pending':
return 'warning';
default:
return 'secondary';
}
}
include 'includes/footer.php';
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
var editor = CodeMirror(document.getElementById('editor'), {
lineNumbers: true,
mode: 'python',
theme: 'default'
});
var solutionTextarea = document.getElementById('solution');
editor.on('change', function() {
solutionTextarea.value = editor.getValue();
});
var languageSelect = document.getElementById('language');
languageSelect.addEventListener('change', function() {
var mode = this.value;
if (mode === 'html' || mode === 'xml') {
mode = 'htmlmixed';
}
editor.setOption('mode', mode);
});
});
</script>

View File

@ -1,114 +1,103 @@
<?php
include 'includes/header.php';
require_once 'includes/pexels.php';
require_once __DIR__ . '/includes/auth_check.php';
session_start();
/*
Progress State
(default everything NOT started)
*/
$language = $_SESSION['language'] ?? null;
$practice_status = $_SESSION['practice_status'] ?? 'not_started';
$reinforcement_status = $_SESSION['reinforcement_status'] ?? 'locked';
$final_status = $_SESSION['final_status'] ?? 'locked';
$class = $_SESSION['class'] ?? 'college';
$applied_allowed = in_array($class, ['11', '12']);
?>
<div class="container py-5">
<h1 class="display-4 fw-bold">Coding Challenges</h1>
<p class="fs-5 text-muted">Test your skills with our coding challenges.</p>
<!DOCTYPE html>
<html>
<head>
<title>Coding Challenges | RS Learning Lab</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<?php if (isset($_SESSION['success_message'])) : ?>
<div class="alert alert-success">
<?php
echo $_SESSION['success_message'];
unset($_SESSION['success_message']);
?>
</div>
<h1>🏆 Coding Challenges</h1>
<p>Base to advanced learning path.</p>
<!-- LANGUAGE SELECTION -->
<?php if (!$language): ?>
<div class="card">
<h2>Select Programming Language</h2>
<form method="post" action="select_language.php">
<select name="language" required>
<option value="">-- Choose Language --</option>
<option value="python">Python</option>
<option value="c">C</option>
<option value="java">Java</option>
</select>
<button class="btn">Start Practice Track</button>
</form>
</div>
<?php endif; ?>
<!-- PRACTICE TRACK -->
<div class="card">
<h2>Track 1: Practice Track</h2>
<p>Focus: Syntax, basics, confidence building.</p>
<?php if ($practice_status === 'not_started'): ?>
<span class="pending">Not Started</span><br><br>
<a href="practice_track.php" class="btn">Start Practice</a>
<?php elseif ($practice_status === 'completed'): ?>
<span class="completed">Completed</span>
<?php endif; ?>
<?php if (isset($_SESSION['error_message'])) : ?>
<div class="alert alert-danger">
<?php
echo $_SESSION['error_message'];
unset($_SESSION['error_message']);
?>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['info_message'])) : ?>
<div class="alert alert-info">
<?php
echo $_SESSION['info_message'];
unset($_SESSION['info_message']);
?>
</div>
<?php endif; ?>
<div class="row mb-4">
<div class="col-md-4">
<div class="input-group">
<label class="input-group-text" for="difficulty-filter">Difficulty</label>
<select class="form-select" id="difficulty-filter">
<option value="all">All</option>
<option value="easy">Easy</option>
<option value="medium">Medium</option>
<option value="advanced">Advanced</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="input-group">
<label class="input-group-text" for="learning-style-filter">Learning Style</label>
<select class="form-select" id="learning-style-filter">
<option value="all">All</option>
<option value="kinesthetic">Kinesthetic</option>
<option value="visual">Visual</option>
<option value="auditory">Auditory</option>
<option value="reading/writing">Reading/Writing</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="input-group">
<label class="input-group-text" for="sort-by">Sort By</label>
<select class="form-select" id="sort-by">
<option value="date_desc">Newest</option>
<option value="date_asc">Oldest</option>
<option value="popularity">Popularity</option>
</select>
</div>
</div>
</div>
<div class="row">
<?php
require_once 'db/config.php';
$pdo = db();
$stmt = $pdo->query('SELECT * FROM challenges WHERE deleted_at IS NULL ORDER BY created_at DESC');
$challenges = $stmt->fetchAll();
if (count($challenges) > 0) {
foreach ($challenges as $challenge) {
$image_data = pexels_get('https://api.pexels.com/v1/search?query=' . urlencode($challenge['title']) . '&per_page=1');
if ($image_data && isset($image_data['photos'][0])) {
$image_url = $image_data['photos'][0]['src']['large'];
} else {
$image_url = 'https://via.placeholder.com/600x400'; // Default placeholder
}
echo '<div class="col-md-4 mb-4">';
echo '<div class="card h-100">';
echo '<div class="card-img-container">';
echo '<img src="' . $image_url . '" class="card-img-top" alt="' . htmlspecialchars($challenge['title']) . '">';
echo '<div class="card-img-overlay d-flex flex-column justify-content-end">';
echo '<h5 class="card-title text-white"><a href="challenge.php?id=' . $challenge['id'] . '">' . htmlspecialchars($challenge['title']) . '</a></h5>';
echo '<span class="badge bg-primary">' . htmlspecialchars($challenge['difficulty']) . '</span>';
echo '</div>';
echo '</div>';
echo '<div class="card-body d-flex flex-column">';
echo '<p class="card-text flex-grow-1">' . htmlspecialchars(substr($challenge['description'], 0, 100)) . '...</p>';
echo '<div class="d-flex justify-content-between align-items-center">';
echo '<small class="text-muted">' . htmlspecialchars($challenge['learning_style']) . '</small>';
echo '</div>';
echo '</div>';
echo '</div>';
echo '</div>';
}
} else {
echo '<div class="alert alert-info">No challenges available yet. Please check back later.</div>';
}
?>
</div>
</div>
<?php include 'includes/footer.php'; ?>
<!-- REINFORCEMENT TRACK -->
<div class="card">
<h2>Track 2: Reinforcement Track</h2>
<p>Focus: Logic strengthening.</p>
<?php if ($reinforcement_status === 'locked'): ?>
<span class="locked">Locked</span>
<?php elseif ($reinforcement_status === 'active'): ?>
<a href="practice_reinforcement.php" class="btn">Start Reinforcement</a>
<?php elseif ($reinforcement_status === 'completed'): ?>
<span class="completed">Completed</span><br>
🏅 Learning Badge Earned
<?php endif; ?>
</div>
<!-- FINAL TRACK -->
<div class="card">
<h2>Track 3: Final Track</h2>
<p>Focus: End-to-end problem solving.</p>
<?php if ($final_status === 'locked'): ?>
<span class="locked">Locked</span>
<?php elseif ($final_status === 'active'): ?>
<a href="practice_final.php" class="btn">Start Final Track</a>
<?php elseif ($final_status === 'completed'): ?>
<span class="completed">Completed</span><br>
📜 Track Completion Certificate Unlocked
<?php endif; ?>
</div>
<!-- APPLIED THINKING -->
<div class="card">
<h2>Applied Thinking (Class 1112 CS)</h2>
<?php if ($applied_allowed): ?>
<a href="applied_thinking.php" class="btn">Start Applied Thinking</a>
<?php else: ?>
<span class="locked">Only for Class 1112 CS</span>
<?php endif; ?>
</div>
<a href="dashboard.php" class="btn"> Back to Dashboard</a>
</body>
</html>

63
change_password.php Normal file
View File

@ -0,0 +1,63 @@
<?php
session_start();
require_once("config/db.php");
if (!isset($_SESSION['institution_id'])) {
header("Location: /rs_lab/institution/login.php");
exit;
}
$error = "";
$success = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$new = $_POST['new_password'];
$confirm = $_POST['confirm_password'];
if ($new === "" || $confirm === "") {
$error = "All fields required";
} elseif ($new !== $confirm) {
$error = "Passwords do not match";
} else {
$hash = password_hash($new, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("
UPDATE institutions
SET password = ?
WHERE id = ?
");
$stmt->execute([$hash, $_SESSION['institution_id']]);
$success = "Password updated successfully";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Change Password | RS Learning Lab</title>
</head>
<body>
<h2>Change Password</h2>
<?php if ($error): ?>
<p style="color:red;"><?php echo $error; ?></p>
<?php endif; ?>
<?php if ($success): ?>
<p style="color:green;"><?php echo $success; ?></p>
<a href="/rs_lab/institution/dashboard.php">Go to Dashboard</a>
<?php endif; ?>
<form method="POST">
<input type="password" name="new_password" placeholder="New Password" required><br><br>
<input type="password" name="confirm_password" placeholder="Confirm Password" required><br><br>
<button type="submit">Update Password</button>
</form>
</body>
</html>

View File

@ -0,0 +1,40 @@
<?php
require_once "../config.php";
$student_id = $_SESSION['student_id'];
$track = "Python Basics Track";
// Count completed challenges
$stmt = $conn->prepare("
SELECT COUNT(DISTINCT challenge_id) AS completed
FROM challenge_submissions
WHERE student_id = ? AND is_correct = 1
");
$stmt->bind_param("i", $student_id);
$stmt->execute();
$result = $stmt->get_result()->fetch_assoc();
$completed = $result['completed'];
// Rule: 5 challenges
if ($completed >= 5) {
// Check already issued?
$check = $conn->prepare("
SELECT id FROM certificates
WHERE student_id = ? AND certificate_type = 'Participation'
");
$check->bind_param("i", $student_id);
$check->execute();
$checkResult = $check->get_result();
if ($checkResult->num_rows == 0) {
// Issue certificate
$insert = $conn->prepare("
INSERT INTO certificates (student_id, certificate_type, track_name)
VALUES (?, 'Participation', ?)
");
$insert->bind_param("is", $student_id, $track);
$insert->execute();
}
}

View File

@ -0,0 +1,24 @@
<?php
// ❌ session_start() remove pannrom
// session_start();
if (!isset($_SESSION)) {
session_start();
}
// DEFAULT
$eligible = false;
// DEMO LOGIC (ippo)
$class = $_SESSION['class'] ?? null;
$stream = $_SESSION['stream'] ?? null;
$applied_thinking_completed = $_SESSION['track3_completed'] ?? false;
// REAL RULE
if (
in_array($class, [11, 12]) &&
$stream === 'CS' &&
$applied_thinking_completed === true
) {
$eligible = true;
}

4
check_session.php Normal file
View File

@ -0,0 +1,4 @@
<?php
session_start();
echo "<pre>";
print_r($_SESSION);

103
choose_assessment_mode.php Normal file
View File

@ -0,0 +1,103 @@
<?php
session_start();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Choose Assessment Mode | RS Learning Lab</title>
<style>
body{
margin:0;
min-height:100vh;
background:radial-gradient(circle at top,#0f172a,#020617);
font-family:Segoe UI,sans-serif;
color:#e5e7eb;
}
.wrap{
max-width:1100px;
margin:80px auto;
padding:20px;
}
h1{
font-size:34px;
margin-bottom:6px;
}
.sub{
opacity:.7;
margin-bottom:45px;
}
.cards{
display:grid;
grid-template-columns:1fr 1fr;
gap:30px;
}
.card{
background:linear-gradient(180deg,#111827,#020617);
border:1px solid #1f2937;
border-radius:20px;
padding:32px;
transition:.3s;
}
.card:hover{
border-color:#0ea5e9;
transform:translateY(-6px);
}
.title{
font-size:22px;
margin-bottom:10px;
}
.desc{
opacity:.8;
line-height:1.6;
}
a{
text-decoration:none;
color:inherit;
}
.back{
display:inline-block;
margin-top:40px;
color:#38bdf8;
}
</style>
</head>
<body>
<div class="wrap">
<h1>Choose Assessment Mode</h1>
<div class="sub">
Select how you want to conduct the learning style assessment.
</div>
<div class="cards">
<a href="manual.php">
<div class="card">
<div class="title">👨‍🏫 Facilitated Mode</div>
<div class="desc">
Teacher conducts the assessment.
Upload class list and guide students
in classroom or lab.
</div>
</div>
</a>
<a href="self_assessment_start.php">
<div class="card">
<div class="title">💻 Self-Assessment Mode</div>
<div class="desc">
Students attempt the assessment independently
using name or roll number.
</div>
</div>
</a>
</div>
<a href="dashboard.php" class="back"> Back to Dashboard</a>
</div>
</body>
</html>

116
class_report.php Normal file
View File

@ -0,0 +1,116 @@
<?php
session_start();
require_once __DIR__ . '/config/db.php';
if (!isset($_SESSION['user_id'])) {
header("Location: teacher_login.php");
exit();
}
/* -------------------------------
FETCH CLASS REPORT DATA
-------------------------------- */
$stmt = $pdo->prepare("
SELECT student_roll, student_name, primary_style, secondary_style, pdf_path
FROM learning_style_results
ORDER BY student_roll ASC
");
$stmt->execute();
$students = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<?php require_once 'includes/header.php'; ?>
<div class="dashboard-wrap">
<h2>Class Learning Style Report</h2>
<p class="sub">
Teacher: <?php echo htmlspecialchars($_SESSION['teacher_name'] ?? ''); ?> |
Class: <?php echo htmlspecialchars($_SESSION['class_name'] ?? ''); ?>
</p>
<table style="width:100%; margin-top:30px; border-collapse:collapse;">
<thead>
<tr>
<th align="left">Roll No</th>
<th align="left">Student Name</th>
<th align="left">Primary Style</th>
<th align="left">Secondary Style</th>
<th align="left">Report</th>
</tr>
</thead>
<tbody>
<?php if(count($students) > 0): ?>
<?php foreach ($students as $s): ?>
<tr>
<td><?php echo htmlspecialchars($s['student_roll']); ?></td>
<td><?php echo htmlspecialchars($s['student_name']); ?></td>
<td><?php echo htmlspecialchars($s['primary_style']); ?></td>
<td><?php echo htmlspecialchars($s['secondary_style']); ?></td>
<td>
<?php if(!empty($s['pdf_path'])): ?>
<a href="<?php echo $s['pdf_path']; ?>" target="_blank" style="color:#38bdf8;">
Download PDF
</a>
<?php else: ?>
<span style="color:#ef4444;">Not Generated</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5">No assessment data available</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<form action="generate_class_pdf.php" method="POST" style="margin-top:30px;">
<button type="submit">
Download Class Report (PDF)
</button>
</form>
<a href="dashboard.php" class="back-link">
Back to Dashboard
</a>
</div>
</body>
</html>

44
class_summary.php Normal file
View File

@ -0,0 +1,44 @@
<?php
session_start();
require_once __DIR__ . '/config/db.php';
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
$stmt = $pdo->prepare("
SELECT learning_style, COUNT(*) as count
FROM learning_styles
WHERE user_id = ?
GROUP BY learning_style
");
$stmt->execute([$_SESSION['user_id']]);
$summary = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<?php require_once 'includes/header.php'; ?>
<div class="dashboard-wrap">
<h2>Class Learning Style Summary</h2>
<p class="sub">
Overview of how students in your class prefer to learn.
</p>
<div class="dashboard-cards">
<?php foreach ($summary as $row): ?>
<div class="dash-card">
<h3><?php echo $row['learning_style']; ?></h3>
<p><?php echo $row['count']; ?> students</p>
</div>
<?php endforeach; ?>
</div>
<a href="dashboard.php" class="back-link"> Back to Dashboard</a>
</div>
</body>
</html>

9
code_arena/.env.example Normal file
View File

@ -0,0 +1,9 @@
# GEMINI_API_KEY: Required for Gemini AI API calls.
# AI Studio automatically injects this at runtime from user secrets.
# Users configure this via the Secrets panel in the AI Studio UI.
GEMINI_API_KEY="MY_GEMINI_API_KEY"
# APP_URL: The URL where this applet is hosted.
# AI Studio automatically injects this at runtime with the Cloud Run service URL.
# Used for self-referential links, OAuth callbacks, and API endpoints.
APP_URL="MY_APP_URL"

8
code_arena/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
node_modules/
build/
dist/
coverage/
.DS_Store
*.log
.env*
!.env.example

20
code_arena/README.md Normal file
View File

@ -0,0 +1,20 @@
<div align="center">
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
</div>
# Run and deploy your AI Studio app
This contains everything you need to run your app locally.
View your app in AI Studio: https://ai.studio/apps/d9855fda-dc07-40f0-9cd7-f15e32e565e5
## Run Locally
**Prerequisites:** Node.js
1. Install dependencies:
`npm install`
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
3. Run the app:
`npm run dev`

14
code_arena/index.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Playfair+Display:wght@700&family=Dancing+Script:wght@700&display=swap" rel="stylesheet">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RS LEARNING LAB</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

5
code_arena/metadata.json Normal file
View File

@ -0,0 +1,5 @@
{
"name": "RS Learning Lab - Coding Challenge System",
"description": "A gamified coding challenge platform with levels, tracks, mini-projects, and dynamic certificate generation.",
"requestFramePermissions": []
}

4795
code_arena/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

45
code_arena/package.json Normal file
View File

@ -0,0 +1,45 @@
{
"name": "react-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"clean": "rm -rf dist",
"lint": "tsc --noEmit"
},
"dependencies": {
"@google/genai": "^1.29.0",
"@tailwindcss/vite": "^4.1.14",
"@types/canvas-confetti": "^1.9.0",
"@vitejs/plugin-react": "^5.0.4",
"canvas-confetti": "^1.9.4",
"clsx": "^2.1.1",
"docx": "^9.6.1",
"dotenv": "^17.2.3",
"express": "^4.21.2",
"file-saver": "^2.0.5",
"framer-motion": "^12.38.0",
"html2canvas": "^1.4.1",
"jspdf": "^4.2.1",
"lucide-react": "^0.546.0",
"motion": "^12.23.24",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.0"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.14.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.0"
}
}

179
code_arena/server.ts Normal file
View File

@ -0,0 +1,179 @@
import express from "express";
import path from "path";
import { createServer as createViteServer } from "vite";
async function startServer() {
const app = express();
const PORT = 3000;
app.use(express.json());
// In-memory "database"
const db = {
users: [
{ id: "admin1", username: "admin", password: "password", role: "admin", name: "Administrator" },
{ id: "teacher1", username: "teacher1", password: "password", role: "teacher", name: "Gokula Krishnan", assignedGrade: "12" },
{ id: "teacher2", username: "teacher2", password: "password", role: "teacher", name: "Arun Kumar", assignedGrade: "10" },
{ id: "teacher3", username: "teacher3", password: "password", role: "teacher", name: "Meera Devi", assignedGrade: "11" },
{ id: "teacher4", username: "teacher4", password: "password", role: "teacher", name: "Senthil Nathan", assignedGrade: "9" }
],
submissions: [] as any[],
students: [] as any[],
studentStats: {} as Record<string, any> // rollNumber -> latest LEI stats
};
// Auth
app.post("/api/login", (req, res) => {
const { username, password } = req.body;
const user = db.users.find(u => u.username === username && u.password === password);
if (user) {
res.json({ id: user.id, username: user.username, role: user.role, name: user.name, assignedGrade: (user as any).assignedGrade });
} else {
res.status(401).json({ success: false, message: "Invalid credentials" });
}
});
// Submissions
app.post("/api/submissions", (req, res) => {
const submission = {
id: Date.now().toString(),
...req.body,
status: "pending",
submittedAt: new Date().toISOString(),
documentationMarks: 0,
vivaScore: 0,
understandingLevel: 0,
explanationAbility: 0,
teacherRemarks: "",
lei: req.body.lei || 0,
leiCategory: req.body.leiCategory || 'Stalled Learner',
leiInsight: req.body.leiInsight || '',
retries: req.body.retries || 0,
streak: req.body.streak || 0,
completionRate: req.body.completionRate || 0
};
db.submissions.push(submission);
res.json({ success: true, submission });
});
app.get("/api/submissions", (req, res) => {
const { grade } = req.query;
let filtered = db.submissions;
if (grade) {
filtered = filtered.filter(s => s.grade.includes(grade as string));
}
res.json(filtered);
});
app.post("/api/student/lei", (req, res) => {
const { rollNumber, lei, leiCategory, leiInsight, studentName, grade } = req.body;
if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" });
db.studentStats[rollNumber] = {
lei,
leiCategory,
leiInsight,
studentName,
grade,
updatedAt: new Date().toISOString()
};
res.json({ success: true });
});
app.get("/api/students/lei", (req, res) => {
const { grade } = req.query;
let stats = Object.values(db.studentStats);
if (grade) {
stats = stats.filter(s => s.grade.includes(grade as string));
}
res.json(stats);
});
app.post("/api/student/viva", (req, res) => {
const { rollNumber, session, vivaScore } = req.body;
if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" });
if (db.studentStats[rollNumber]) {
if (!db.studentStats[rollNumber].vivaSessions) {
db.studentStats[rollNumber].vivaSessions = [];
}
db.studentStats[rollNumber].vivaSessions.push(session);
db.studentStats[rollNumber].vivaSummary = session.summary;
db.studentStats[rollNumber].vivaScore = vivaScore;
}
res.json({ success: true });
});
app.patch("/api/submissions/:id", (req, res) => {
const { id } = req.params;
const {
documentationMarks, vivaScore, teacherVivaMarks, vivaRequested,
teacherRemarks, status, marks, totalScore
} = req.body;
const index = db.submissions.findIndex(s => s.id === id);
if (index !== -1) {
if (documentationMarks !== undefined) db.submissions[index].documentationMarks = documentationMarks;
if (vivaScore !== undefined) db.submissions[index].vivaScore = vivaScore;
if (teacherVivaMarks !== undefined) db.submissions[index].teacherVivaMarks = teacherVivaMarks;
if (vivaRequested !== undefined) db.submissions[index].vivaRequested = vivaRequested;
if (teacherRemarks !== undefined) db.submissions[index].teacherRemarks = teacherRemarks;
if (marks !== undefined) db.submissions[index].marks = marks;
if (totalScore !== undefined) db.submissions[index].totalScore = totalScore;
db.submissions[index].status = status || "reviewed";
res.json({ success: true, submission: db.submissions[index] });
} else {
res.status(404).json({ success: false, message: "Submission not found" });
}
});
// Leaderboard
app.get("/api/leaderboard", (req, res) => {
const { grade } = req.query;
let filtered = db.submissions;
if (grade) {
filtered = filtered.filter(s => s.grade.includes(grade as string));
}
const leaderboard = filtered
.sort((a, b) => (b.lei || 0) - (a.lei || 0))
.slice(0, 10)
.map(s => {
let label = "Active Learner";
if (s.lei > 100) label = "Top Contributor";
else if (s.lei > 50) label = "Most Consistent";
else if (s.lei > 20) label = "Rising Star";
return {
name: s.studentName,
lei: s.lei || 0,
grade: s.grade,
rollNumber: s.rollNumber,
label: label
};
});
res.json(leaderboard);
});
// Vite middleware for development
if (process.env.NODE_ENV !== "production") {
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "spa",
});
app.use(vite.middlewares);
} else {
const distPath = path.join(process.cwd(), 'dist');
app.use(express.static(distPath));
app.get('*', (req, res) => {
res.sendFile(path.join(distPath, 'index.html'));
});
}
app.listen(PORT, "0.0.0.0", () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
startServer();

2563
code_arena/src/App.tsx Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -0,0 +1,185 @@
import React from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { AlertTriangle, Lightbulb, CheckCircle2, ChevronRight, RefreshCw, Code2, Brain, Target } from 'lucide-react';
import { ErrorAnalysis } from '../lib/errorAnalyzer';
import { cn } from '../lib/utils';
interface ErrorAnalyzerViewProps {
analysis: ErrorAnalysis;
onRetry: () => void;
currentHintLevel: number;
onShowNextHint: () => void;
}
export function ErrorAnalyzerView({ analysis, onRetry, currentHintLevel, onShowNextHint }: ErrorAnalyzerViewProps) {
const [activeStep, setActiveStep] = React.useState(1);
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="mt-8 space-y-6"
>
{/* Error Header */}
<div className="bg-red-500/10 border border-red-500/20 rounded-2xl p-6">
<div className="flex items-center gap-3 mb-4">
<div className="p-2 bg-red-500/20 rounded-lg text-red-500">
<AlertTriangle size={24} />
</div>
<div>
<h3 className="text-lg font-bold text-white">{analysis.errorType} Error Detected</h3>
<p className="text-sm text-red-400/80">Don't worry! Mistakes are the best way to learn.</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="p-4 bg-black/20 rounded-xl border border-white/5">
<div className="text-[10px] font-bold text-slate-500 uppercase tracking-widest mb-1">What happened?</div>
<p className="text-xs text-slate-300 leading-relaxed">{analysis.explanation.what}</p>
</div>
<div className="p-4 bg-black/20 rounded-xl border border-white/5">
<div className="text-[10px] font-bold text-slate-500 uppercase tracking-widest mb-1">Why is it wrong?</div>
<p className="text-xs text-slate-300 leading-relaxed">{analysis.explanation.why}</p>
</div>
<div className="p-4 bg-black/20 rounded-xl border border-white/5">
<div className="text-[10px] font-bold text-slate-500 uppercase tracking-widest mb-1">Where is it?</div>
<p className="text-xs text-slate-300 leading-relaxed font-medium text-[#0b8c9f]">{analysis.explanation.where}</p>
</div>
</div>
</div>
{/* Step-by-Step Learning */}
<div className="bg-dark-card border border-dark-border rounded-2xl overflow-hidden">
<div className="p-4 bg-slate-800/50 border-b border-dark-border flex items-center justify-between">
<div className="flex items-center gap-2">
<Brain size={18} className="text-purple-400" />
<span className="text-xs font-bold text-white uppercase tracking-widest">Step-by-Step Learning</span>
</div>
<div className="flex gap-1">
{[1, 2, 3].map(step => (
<button
key={step}
onClick={() => setActiveStep(step)}
className={cn(
"w-8 h-8 rounded-lg text-xs font-bold transition-all",
activeStep === step
? "bg-[#0b8c9f] text-white"
: "bg-slate-800 text-slate-500 hover:text-slate-300"
)}
>
{step}
</button>
))}
</div>
</div>
<div className="p-6">
<AnimatePresence mode="wait">
<motion.div
key={activeStep}
initial={{ opacity: 0, x: 10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
className="space-y-4"
>
<div className="flex items-start gap-4">
<div className="w-10 h-10 rounded-xl bg-[#0b8c9f]/10 flex items-center justify-center text-[#0b8c9f] shrink-0">
{activeStep === 1 ? <Target size={20} /> : activeStep === 2 ? <Brain size={20} /> : <CheckCircle2 size={20} />}
</div>
<div>
<h4 className="font-bold text-white mb-1">
{activeStep === 1 ? "Identify the Mistake" : activeStep === 2 ? "Understand the Concept" : "The Correct Approach"}
</h4>
<p className="text-sm text-slate-400 leading-relaxed">
{activeStep === 1 ? analysis.steps.step1 : activeStep === 2 ? analysis.steps.step2 : analysis.steps.step3}
</p>
</div>
</div>
{activeStep < 3 && (
<button
onClick={() => setActiveStep(prev => prev + 1)}
className="text-xs font-bold text-[#0b8c9f] hover:underline flex items-center gap-1 ml-14"
>
Next Step <ChevronRight size={14} />
</button>
)}
</motion.div>
</AnimatePresence>
</div>
</div>
{/* Hint System */}
<div className="bg-yellow-500/5 border border-yellow-500/20 rounded-2xl p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<Lightbulb size={20} className="text-yellow-500" />
<span className="text-xs font-bold text-yellow-500 uppercase tracking-widest">Hint System</span>
</div>
{currentHintLevel < 3 && (
<button
onClick={onShowNextHint}
className="text-[10px] font-bold text-yellow-500 hover:underline uppercase tracking-widest"
>
{currentHintLevel === 0 ? "Get a Hint" : "Need more help?"}
</button>
)}
</div>
<div className="space-y-3">
{currentHintLevel >= 1 && (
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
className="p-3 bg-yellow-500/10 rounded-xl border border-yellow-500/20 text-xs text-yellow-200/80 italic"
>
<span className="font-bold mr-2">Hint 1:</span> {analysis.hints[0]}
</motion.div>
)}
{currentHintLevel >= 2 && (
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
className="p-3 bg-yellow-500/10 rounded-xl border border-yellow-500/20 text-xs text-yellow-200/80 italic"
>
<span className="font-bold mr-2">Hint 2:</span> {analysis.hints[1]}
</motion.div>
)}
{currentHintLevel >= 3 && (
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
className="p-4 bg-green-500/10 rounded-xl border border-green-500/20 space-y-3"
>
<div className="text-xs text-green-400 font-bold uppercase tracking-widest">Final Explanation</div>
<p className="text-xs text-slate-300 leading-relaxed">{analysis.hints[2]}</p>
<div className="pt-3 border-t border-green-500/20">
<div className="text-[10px] font-bold text-slate-500 uppercase mb-2">Corrected Code Snippet</div>
<pre className="text-[10px] font-mono text-green-400 bg-black/40 p-3 rounded-lg overflow-x-auto">
{analysis.correctedCode}
</pre>
</div>
</motion.div>
)}
</div>
</div>
{/* Suggested Fix & Retry */}
<div className="flex flex-col sm:flex-row items-center gap-4">
<div className="flex-1 p-4 bg-[#0b8c9f]/5 border border-[#0b8c9f]/20 rounded-xl flex items-center gap-3">
<div className="p-2 bg-[#0b8c9f]/10 rounded-lg text-[#0b8c9f]">
<Code2 size={20} />
</div>
<div>
<div className="text-[10px] font-bold text-slate-500 uppercase tracking-widest mb-0.5">Quick Fix</div>
<p className="text-sm font-bold text-white">{analysis.suggestedFix}</p>
</div>
</div>
<button
onClick={onRetry}
className="w-full sm:w-auto bg-white text-black px-8 py-4 rounded-xl font-bold hover:bg-slate-200 transition-all flex items-center justify-center gap-2 shadow-xl"
>
<RefreshCw size={20} /> Try Again
</button>
</div>
</motion.div>
);
}

View File

@ -0,0 +1,156 @@
import React from 'react';
import { motion } from 'motion/react';
import { Trophy, Medal, Star, ArrowRight, Search, Filter } from 'lucide-react';
import { type LeaderboardEntry, type User } from '../types';
import { cn } from '../lib/utils';
interface LeaderboardProps {
user?: User | null;
}
export function Leaderboard({ user }: LeaderboardProps) {
const [entries, setEntries] = React.useState<LeaderboardEntry[]>([]);
const [loading, setLoading] = React.useState(true);
const [filter, setFilter] = React.useState<string>('all');
const [search, setSearch] = React.useState('');
const fetchLeaderboard = async () => {
try {
const res = await fetch('/api/leaderboard');
const data = await res.json();
setEntries(data);
} catch (error) {
console.error('Failed to fetch leaderboard:', error);
} finally {
setLoading(false);
}
};
React.useEffect(() => {
fetchLeaderboard();
}, []);
const filteredEntries = entries.filter(e => {
const matchesFilter = filter === 'all' || e.grade === filter;
const matchesSearch = e.name.toLowerCase().includes(search.toLowerCase()) ||
e.rollNumber.toLowerCase().includes(search.toLowerCase());
return matchesFilter && matchesSearch;
});
const grades = Array.from(new Set(entries.map(e => e.grade))).sort();
return (
<div className="space-y-10">
<div className="text-center max-w-2xl mx-auto">
<h2 className="text-4xl font-black text-white mb-4">Class Leaderboard 🏆</h2>
<p className="text-slate-400">Celebrate the top performers of RS Learning Lab. Keep learning to climb the ranks!</p>
</div>
<div className="bg-dark-card rounded-3xl border border-dark-border overflow-hidden shadow-2xl">
<div className="p-6 border-b border-dark-border flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 bg-slate-800/30">
<div className="relative w-full sm:w-64">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500" size={18} />
<input
type="text"
placeholder="Search students..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full pl-10 pr-4 py-2 rounded-xl bg-dark-bg border border-dark-border text-white text-sm focus:ring-2 focus:ring-[#0b8c9f] outline-none"
/>
</div>
<div className="flex items-center gap-2">
<Filter size={18} className="text-slate-500" />
<select
value={filter}
onChange={(e) => setFilter(e.target.value)}
className="bg-dark-bg border border-dark-border text-white text-sm rounded-xl px-3 py-2 outline-none focus:ring-2 focus:ring-[#0b8c9f]"
>
<option value="all">All Classes</option>
{grades.map(g => <option key={g} value={g}>{g}</option>)}
</select>
</div>
</div>
<div className="overflow-x-auto">
<table className="w-full text-left">
<thead>
<tr className="bg-slate-800/50 border-b border-dark-border">
<th className="px-8 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest">Rank</th>
<th className="px-8 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest">Student</th>
<th className="px-8 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest">Class</th>
<th className="px-8 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest">Recognition</th>
{user?.role === 'teacher' && (
<th className="px-8 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest text-right">Learning Effective Index (LEI)</th>
)}
</tr>
</thead>
<tbody className="divide-y divide-dark-border">
{loading ? (
<tr><td colSpan={user?.role === 'teacher' ? 5 : 4} className="px-8 py-12 text-center text-slate-500">Loading leaderboard...</td></tr>
) : filteredEntries.length === 0 ? (
<tr><td colSpan={user?.role === 'teacher' ? 5 : 4} className="px-8 py-12 text-center text-slate-500">No entries found.</td></tr>
) : (
filteredEntries.map((entry, index) => (
<motion.tr
key={entry.rollNumber}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.05 }}
className={cn(
"group hover:bg-slate-800/50 transition-all",
index === 0 && "bg-yellow-500/5",
index === 1 && "bg-slate-400/5",
index === 2 && "bg-orange-500/5"
)}
>
<td className="px-8 py-6">
<div className="flex items-center gap-3">
{index === 0 ? (
<div className="w-8 h-8 bg-yellow-500 rounded-lg flex items-center justify-center text-white shadow-lg shadow-yellow-500/20">
<Trophy size={16} />
</div>
) : index === 1 ? (
<div className="w-8 h-8 bg-slate-400 rounded-lg flex items-center justify-center text-white shadow-lg shadow-slate-400/20">
<Medal size={16} />
</div>
) : index === 2 ? (
<div className="w-8 h-8 bg-orange-500 rounded-lg flex items-center justify-center text-white shadow-lg shadow-orange-500/20">
<Medal size={16} />
</div>
) : (
<span className="w-8 h-8 flex items-center justify-center font-bold text-slate-500">{index + 1}</span>
)}
</div>
</td>
<td className="px-8 py-6">
<div className="flex items-center gap-4">
<div className="w-10 h-10 bg-slate-800 rounded-full flex items-center justify-center text-white font-bold text-sm">
{entry.name.charAt(0)}
</div>
<span className="font-bold text-white group-hover:text-[#0b8c9f] transition-colors">{entry.name}</span>
</div>
</td>
<td className="px-8 py-6 text-slate-400 font-medium">{entry.grade}</td>
<td className="px-8 py-6">
<span className="px-3 py-1 rounded-full bg-[#0b8c9f]/10 text-[#0b8c9f] text-[10px] font-bold uppercase tracking-widest border border-[#0b8c9f]/20">
{entry.label || 'Active Learner'}
</span>
</td>
{user?.role === 'teacher' && (
<td className="px-8 py-6 text-right">
<div className="flex items-center justify-end gap-2">
<Star size={16} className="text-[#ff7a00]" fill="#ff7a00" />
<span className="text-xl font-black text-white">{entry.lei}</span>
</div>
</td>
)}
</motion.tr>
))
)}
</tbody>
</table>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,695 @@
import React from 'react';
import { motion } from 'motion/react';
import {
Users, FileText, CheckCircle2, Clock, AlertCircle,
Search, Filter, ArrowRight, MessageSquare, Award, Download, Trophy
} from 'lucide-react';
import { type Submission, type User } from '../types';
import { cn } from '../lib/utils';
interface TeacherDashboardProps {
user: User;
onLogout: () => void;
}
export function TeacherDashboard({ user, onLogout }: TeacherDashboardProps) {
const [submissions, setSubmissions] = React.useState<Submission[]>([]);
const [loading, setLoading] = React.useState(true);
const [filter, setFilter] = React.useState<'all' | 'pending' | 'reviewed'>('all');
const [search, setSearch] = React.useState('');
const [selectedSubmission, setSelectedSubmission] = React.useState<Submission | null>(null);
const [vivaScore, setVivaScore] = React.useState<number>(0);
const [teacherVivaMarks, setTeacherVivaMarks] = React.useState<number>(0);
const [docMarks, setDocMarks] = React.useState({
objective: 0,
concepts: 0,
explanation: 0,
learningOutcome: 0
});
const [codeMarks, setCodeMarks] = React.useState({
logic: 0,
output: 0,
concepts: 0
});
const [teacherRemarks, setTeacherRemarks] = React.useState<string>('');
const [isUpdating, setIsUpdating] = React.useState(false);
const [showSettings, setShowSettings] = React.useState(false);
const [showReport, setShowReport] = React.useState(false);
const [schoolName, setSchoolName] = React.useState(() => localStorage.getItem('rs_school_name') || '');
const [principalName, setPrincipalName] = React.useState(() => localStorage.getItem('rs_principal_name') || '');
const fetchSubmissions = async () => {
try {
const res = await fetch('/api/submissions');
const data = await res.json();
// Filter by teacher's assigned grade if applicable
const filtered = user.assignedGrade
? data.filter((s: Submission) => s.grade === user.assignedGrade)
: data;
setSubmissions(filtered);
} catch (error) {
console.error('Failed to fetch submissions:', error);
} finally {
setLoading(false);
}
};
React.useEffect(() => {
fetchSubmissions();
}, []);
const handleUpdateScore = async (submissionId: string) => {
setIsUpdating(true);
const docTotal = docMarks.objective + docMarks.concepts + docMarks.explanation + docMarks.learningOutcome;
const codeTotal = codeMarks.logic + codeMarks.output + codeMarks.concepts;
const vivaTotal = vivaScore + teacherVivaMarks;
const totalScore = docTotal + codeTotal + vivaTotal;
try {
const res = await fetch(`/api/submissions/${submissionId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
marks: {
documentation: { ...docMarks, total: docTotal },
code: { ...codeMarks, total: codeTotal },
viva: { aiScore: vivaScore, teacherScore: teacherVivaMarks, total: vivaTotal },
total: totalScore
},
documentationMarks: docTotal,
vivaScore: vivaScore,
teacherVivaMarks: teacherVivaMarks,
teacherRemarks,
totalScore,
status: 'reviewed'
})
});
if (res.ok) {
await fetchSubmissions();
setSelectedSubmission(null);
}
} catch (error) {
console.error('Failed to update score:', error);
} finally {
setIsUpdating(false);
}
};
const filteredSubmissions = submissions.filter(s => {
const matchesFilter = filter === 'all' || s.status === filter;
const matchesSearch = s.studentName.toLowerCase().includes(search.toLowerCase()) ||
s.rollNumber.toLowerCase().includes(search.toLowerCase());
return matchesFilter && matchesSearch;
});
return (
<div className="space-y-8">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-6">
<div>
<h2 className="text-3xl font-black text-white">Teacher Dashboard 👩🏫</h2>
<p className="text-slate-400 mt-1">Reviewing submissions for {user.assignedGrade || 'All Classes'}</p>
</div>
<div className="flex items-center gap-4">
<button
onClick={() => setShowReport(true)}
className="bg-orange-500/10 text-orange-500 px-4 py-2 rounded-xl font-bold border border-orange-500/20 hover:bg-orange-500 hover:text-white transition-all flex items-center gap-2"
>
<Award size={18} /> Class Report
</button>
<button
onClick={() => setShowSettings(true)}
className="bg-[#0b8c9f]/10 text-[#0b8c9f] px-4 py-2 rounded-xl font-bold border border-[#0b8c9f]/20 hover:bg-[#0b8c9f] hover:text-white transition-all flex items-center gap-2"
>
<FileText size={18} /> School Settings
</button>
<div className="bg-dark-card px-4 py-2 rounded-xl border border-dark-border flex items-center gap-3">
<Users size={20} className="text-[#0b8c9f]" />
<span className="font-bold text-white">{submissions.length} Students</span>
</div>
<button
onClick={onLogout}
className="bg-red-500/10 text-red-500 px-4 py-2 rounded-xl font-bold border border-red-500/20 hover:bg-red-500 hover:text-white transition-all"
>
Logout
</button>
</div>
</div>
{showReport && (
<div className="fixed inset-0 bg-dark-bg/90 backdrop-blur-xl z-[100] flex items-center justify-center p-6 overflow-y-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="bg-dark-card rounded-3xl p-10 border border-dark-border max-w-4xl w-full shadow-2xl my-auto"
>
<div className="flex items-center justify-between mb-8">
<div>
<h3 className="text-3xl font-black text-white">Class Performance Report 📊</h3>
<p className="text-slate-400">Comprehensive analysis for {user.assignedGrade || 'All Classes'}</p>
</div>
<button onClick={() => setShowReport(false)} className="p-2 hover:bg-slate-800 rounded-xl text-slate-500 transition-colors">
<X size={24} />
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-10">
<div className="bg-dark-bg p-6 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-2">Avg LEI</div>
<div className="text-3xl font-black text-[#0b8c9f]">
{submissions.length > 0
? Math.round(submissions.reduce((acc, s) => acc + (s.lei || 0), 0) / submissions.length)
: 0}
</div>
</div>
<div className="bg-dark-bg p-6 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-2">Total Submissions</div>
<div className="text-3xl font-black text-white">{submissions.length}</div>
</div>
<div className="bg-dark-bg p-6 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-2">Reviewed</div>
<div className="text-3xl font-black text-green-500">
{submissions.filter(s => s.status === 'reviewed').length}
</div>
</div>
<div className="bg-dark-bg p-6 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-2">Avg Time</div>
<div className="text-3xl font-black text-orange-500">
{submissions.length > 0
? Math.round(submissions.reduce((acc, s) => acc + (s.timeSpent || 0), 0) / submissions.length)
: 0}m
</div>
</div>
</div>
<div className="space-y-6">
<h4 className="text-lg font-bold text-white flex items-center gap-2">
<Trophy size={20} className="text-yellow-500" /> Top Performers (by LEI)
</h4>
<div className="bg-dark-bg rounded-2xl border border-dark-border overflow-hidden">
<table className="w-full text-left text-sm">
<thead className="bg-slate-800/50">
<tr>
<th className="px-6 py-4 font-bold text-slate-400">Student</th>
<th className="px-6 py-4 font-bold text-slate-400">Roll No</th>
<th className="px-6 py-4 font-bold text-slate-400">Level</th>
<th className="px-6 py-4 font-bold text-slate-400 text-right">LEI Score</th>
</tr>
</thead>
<tbody className="divide-y divide-dark-border">
{[...submissions]
.sort((a, b) => (b.lei || 0) - (a.lei || 0))
.slice(0, 5)
.map((s, i) => (
<tr key={s.id} className="hover:bg-slate-800/30 transition-colors">
<td className="px-6 py-4 text-white font-medium">{s.studentName}</td>
<td className="px-6 py-4 text-slate-400">{s.rollNumber}</td>
<td className="px-6 py-4 text-slate-400">Level {s.level}</td>
<td className="px-6 py-4 text-right font-black text-[#0b8c9f]">{s.lei}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="mt-10 pt-8 border-t border-dark-border flex justify-between items-center">
<div className="flex items-center gap-4">
<div className="bg-white p-1 rounded-lg">
<img src="https://raw.githubusercontent.com/RS-Learning-Lab/assets/main/logo.png" alt="Logo" className="h-6" />
</div>
<div className="text-[10px] text-slate-500 uppercase tracking-widest font-bold">
RS Learning Lab Official Report
</div>
</div>
<button
onClick={() => window.print()}
className="flex items-center gap-2 text-sm font-bold text-[#0b8c9f] hover:underline"
>
<Download size={16} /> Print Report
</button>
</div>
</motion.div>
</div>
)}
{showSettings && (
<div className="fixed inset-0 bg-dark-bg/80 backdrop-blur-md z-[100] flex items-center justify-center p-6">
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
className="bg-dark-card rounded-3xl p-8 border border-dark-border max-w-md w-full shadow-2xl"
>
<div className="flex items-center justify-between mb-6">
<h3 className="text-2xl font-bold text-white">School Settings</h3>
<button onClick={() => setShowSettings(false)} className="text-slate-500 hover:text-white">
<X size={24} />
</button>
</div>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-300 mb-1">School Name</label>
<input
type="text"
value={schoolName}
onChange={(e) => setSchoolName(e.target.value)}
placeholder="Enter school name"
className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] outline-none"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-300 mb-1">Principal Name</label>
<input
type="text"
value={principalName}
onChange={(e) => setPrincipalName(e.target.value)}
placeholder="Enter principal name"
className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white focus:ring-2 focus:ring-[#0b8c9f] outline-none"
/>
</div>
<button
onClick={() => {
// In a real app, we'd save this to the backend
localStorage.setItem('rs_school_name', schoolName);
localStorage.setItem('rs_principal_name', principalName);
setShowSettings(false);
}}
className="w-full bg-[#0b8c9f] text-white py-4 rounded-xl font-bold hover:bg-[#097a8a] transition-all shadow-lg shadow-[#0b8c9f]/20"
>
Save Settings
</button>
</div>
</motion.div>
</div>
)}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Submissions List */}
<div className="lg:col-span-2 space-y-6">
<div className="bg-dark-card rounded-3xl border border-dark-border overflow-hidden">
<div className="p-6 border-b border-dark-border flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div className="relative w-full sm:w-64">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500" size={18} />
<input
type="text"
placeholder="Search students..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full pl-10 pr-4 py-2 rounded-xl bg-dark-bg border border-dark-border text-white text-sm focus:ring-2 focus:ring-[#0b8c9f] outline-none"
/>
</div>
<div className="flex items-center gap-2">
<Filter size={18} className="text-slate-500" />
<select
value={filter}
onChange={(e) => setFilter(e.target.value as any)}
className="bg-dark-bg border border-dark-border text-white text-sm rounded-xl px-3 py-2 outline-none focus:ring-2 focus:ring-[#0b8c9f]"
>
<option value="all">All Status</option>
<option value="pending">Pending</option>
<option value="reviewed">Reviewed</option>
</select>
</div>
</div>
<div className="divide-y divide-dark-border">
{loading ? (
<div className="p-12 text-center text-slate-500">Loading submissions...</div>
) : filteredSubmissions.length === 0 ? (
<div className="p-12 text-center text-slate-500">No submissions found.</div>
) : (
filteredSubmissions.map((s) => (
<button
key={s.id}
onClick={() => {
setSelectedSubmission(s);
setDocMarks({
objective: s.marks?.documentation?.objective || 0,
concepts: s.marks?.documentation?.concepts || 0,
explanation: s.marks?.documentation?.explanation || 0,
learningOutcome: s.marks?.documentation?.learningOutcome || 0
});
setCodeMarks({
logic: s.marks?.code?.logic || 0,
output: s.marks?.code?.output || 0,
concepts: s.marks?.code?.concepts || 0
});
setVivaScore(s.vivaScore || 0);
setTeacherVivaMarks(s.teacherVivaMarks || 0);
setTeacherRemarks(s.teacherRemarks || '');
}}
className={cn(
"w-full p-6 flex items-center justify-between hover:bg-slate-800/50 transition-all text-left group",
selectedSubmission?.id === s.id && "bg-slate-800/80"
)}
>
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-slate-800 rounded-xl flex items-center justify-center text-white font-bold">
{s.studentName.charAt(0)}
</div>
<div>
<h4 className="font-bold text-white group-hover:text-[#0b8c9f] transition-colors">{s.studentName}</h4>
<div className="flex items-center gap-3 text-xs text-slate-500 mt-1">
<span>Roll: {s.rollNumber}</span>
<span></span>
<span>{s.grade}</span>
<span></span>
<span className="text-[#0b8c9f] font-bold">Lvl {s.level}</span>
<span></span>
<span className="text-orange-500">{s.language}</span>
</div>
</div>
</div>
<div className="flex items-center gap-6">
<div className="text-right hidden sm:block">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest">Total Score</div>
<div className="text-lg font-black text-white">{s.marks?.total || 0} <span className="text-[10px] text-slate-500">/ 50</span></div>
<div className="text-[10px] font-bold text-[#0b8c9f] uppercase">LEI: {s.lei || 0}</div>
</div>
<div className="text-right hidden sm:block">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest">Effort</div>
<div className="text-lg font-black text-white">{s.timeSpent}m / {s.attempts}a</div>
</div>
<div className={cn(
"px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest",
s.status === 'reviewed' ? "bg-green-500/10 text-green-500" : "bg-yellow-500/10 text-yellow-500"
)}>
{s.status}
</div>
<ArrowRight size={20} className="text-slate-700 group-hover:text-white transition-all" />
</div>
</button>
))
)}
</div>
</div>
</div>
{/* Evaluation Panel */}
<div className="space-y-6">
{selectedSubmission ? (
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
className="bg-dark-card rounded-3xl border border-dark-border p-8 sticky top-28"
>
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-bold text-white">Evaluation</h3>
<button onClick={() => setSelectedSubmission(null)} className="text-slate-500 hover:text-white">
<X size={20} />
</button>
</div>
<div className="space-y-6">
<div className="p-4 bg-slate-800/50 rounded-2xl border border-dark-border">
<div className="flex justify-between items-center mb-2">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest">Performance Metrics</div>
<div className="text-sm font-black text-[#0b8c9f]">LEI: {selectedSubmission.lei}</div>
</div>
<div className="grid grid-cols-3 gap-2">
<div className="p-2 bg-dark-bg rounded-lg text-center">
<div className="text-[10px] text-slate-500 uppercase">Time</div>
<div className="text-sm font-bold text-white">{selectedSubmission.timeSpent}m</div>
</div>
<div className="p-2 bg-dark-bg rounded-lg text-center">
<div className="text-[10px] text-slate-500 uppercase">Attempts</div>
<div className="text-sm font-bold text-white">{selectedSubmission.attempts}</div>
</div>
<div className="p-2 bg-dark-bg rounded-lg text-center">
<div className="text-[10px] text-slate-500 uppercase">Hints</div>
<div className="text-sm font-bold text-white">{selectedSubmission.hintsUsed}</div>
</div>
</div>
</div>
<div className="p-4 bg-slate-800/50 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-2">Project Details</div>
<div className="text-sm font-bold text-white mb-1">{selectedSubmission.track === 'project' ? 'Final Project' : 'Track Challenge'}</div>
<div className="text-xs text-[#0b8c9f] font-mono">{selectedSubmission.language}</div>
</div>
<div className="p-4 bg-[#0b8c9f]/5 rounded-2xl border border-[#0b8c9f]/20">
<div className="text-xs font-bold text-[#0b8c9f] uppercase tracking-widest mb-2">Project Documentation</div>
<div className="text-xs text-slate-300 leading-relaxed whitespace-pre-wrap max-h-40 overflow-y-auto custom-scrollbar">
{selectedSubmission.docText}
</div>
</div>
<div className="p-4 bg-[#0b8c9f]/5 rounded-2xl border border-[#0b8c9f]/20">
<div className="text-xs font-bold text-[#0b8c9f] uppercase tracking-widest mb-2">AI Feedback</div>
<p className="text-xs text-slate-300 leading-relaxed italic">"{selectedSubmission.aiFeedback}"</p>
</div>
{selectedSubmission.vivaSummary && (
<div className="p-4 bg-yellow-500/5 rounded-2xl border border-yellow-500/20">
<div className="text-xs font-bold text-yellow-500 uppercase tracking-widest mb-4">AI Viva Practice Summary</div>
<div className="space-y-4">
<div>
<div className="text-[10px] font-bold text-slate-500 uppercase mb-1">Overall Rating</div>
<p className="text-xs text-white font-bold">{selectedSubmission.vivaSummary.finalQualitative}</p>
</div>
<div>
<div className="text-[10px] font-bold text-slate-500 uppercase mb-1">Strengths</div>
<ul className="space-y-1">
{selectedSubmission.vivaSummary.strengths.map((s, i) => (
<li key={i} className="text-[10px] text-slate-300 flex items-center gap-2">
<div className="w-1 h-1 bg-green-500 rounded-full" /> {s}
</li>
))}
</ul>
</div>
<div>
<div className="text-[10px] font-bold text-slate-500 uppercase mb-1">Areas to Improve</div>
<ul className="space-y-1">
{selectedSubmission.vivaSummary.improvements.map((s, i) => (
<li key={i} className="text-[10px] text-slate-300 flex items-center gap-2">
<div className="w-1 h-1 bg-orange-500 rounded-full" /> {s}
</li>
))}
</ul>
</div>
</div>
</div>
)}
{selectedSubmission.plagiarismReport && (
<div className={cn(
"p-4 rounded-2xl border",
selectedSubmission.plagiarismReport.status === 'Safe' ? "bg-green-500/5 border-green-500/20" :
selectedSubmission.plagiarismReport.status === 'Needs Review' ? "bg-yellow-500/5 border-yellow-500/20" :
"bg-red-500/5 border-red-500/20"
)}>
<div className="flex items-center justify-between mb-4">
<div className={cn(
"text-xs font-bold uppercase tracking-widest",
selectedSubmission.plagiarismReport.status === 'Safe' ? "text-green-500" :
selectedSubmission.plagiarismReport.status === 'Needs Review' ? "text-yellow-500" :
"text-red-500"
)}>
Anti-Copy Report: {selectedSubmission.plagiarismReport.status}
</div>
<div className="text-xl font-black text-white">
{selectedSubmission.plagiarismReport.score}%
</div>
</div>
<p className="text-xs text-slate-300 leading-relaxed mb-4 italic">
"{selectedSubmission.plagiarismReport.explanation}"
</p>
{selectedSubmission.plagiarismReport.status !== 'Safe' && (
<button
onClick={async () => {
try {
await fetch(`/api/submissions/${selectedSubmission.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ vivaRequested: true })
});
alert('Viva explanation requested from student.');
} catch (err) {
console.error("Failed to request viva:", err);
}
}}
className="w-full mb-4 py-2 bg-yellow-500/10 hover:bg-yellow-500/20 text-yellow-500 rounded-xl text-[10px] font-bold border border-yellow-500/20 transition-all flex items-center justify-center gap-2"
>
<MessageSquare size={14} /> Request Viva Explanation
</button>
)}
{selectedSubmission.plagiarismReport.similarSubmissions.length > 0 && (
<div className="space-y-2">
<div className="text-[10px] font-bold text-slate-500 uppercase">Similar Submissions</div>
{selectedSubmission.plagiarismReport.similarSubmissions.map((s, i) => (
<div key={i} className="p-2 bg-black/20 rounded-lg flex items-center justify-between">
<div>
<div className="text-[10px] font-bold text-white">{s.studentName} ({s.rollNumber})</div>
<div className="text-[8px] text-slate-500">{s.reason}</div>
</div>
<div className="text-[10px] font-bold text-slate-400">{s.similarityScore}%</div>
</div>
))}
</div>
)}
</div>
)}
{selectedSubmission.code && (
<div className="p-4 bg-slate-950 rounded-2xl border border-dark-border">
<div className="flex items-center justify-between mb-2">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest">Submitted Code</div>
<button
onClick={() => {
const blob = new Blob([selectedSubmission.code || ''], { type: 'text/javascript' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `solution_${selectedSubmission.studentName.replace(/\s+/g, '_')}.js`;
a.click();
}}
className="text-[10px] text-[#0b8c9f] hover:underline flex items-center gap-1"
>
<Download size={12} /> Download Code
</button>
</div>
<pre className="text-[10px] text-slate-400 font-mono bg-black/30 p-3 rounded-lg overflow-x-auto max-h-40 custom-scrollbar">
{selectedSubmission.code}
</pre>
</div>
)}
<div className="space-y-4">
<div className="p-4 bg-slate-800/50 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-4">Documentation (Max 20)</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Objective (5)</label>
<input
type="number" max={5} value={docMarks.objective}
onChange={(e) => setDocMarks({...docMarks, objective: Number(e.target.value)})}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Concepts (5)</label>
<input
type="number" max={5} value={docMarks.concepts}
onChange={(e) => setDocMarks({...docMarks, concepts: Number(e.target.value)})}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Explanation (5)</label>
<input
type="number" max={5} value={docMarks.explanation}
onChange={(e) => setDocMarks({...docMarks, explanation: Number(e.target.value)})}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Outcome (5)</label>
<input
type="number" max={5} value={docMarks.learningOutcome}
onChange={(e) => setDocMarks({...docMarks, learningOutcome: Number(e.target.value)})}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
</div>
</div>
<div className="p-4 bg-slate-800/50 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-4">Code Implementation (Max 15)</div>
<div className="grid grid-cols-3 gap-2">
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Logic (5)</label>
<input
type="number" max={5} value={codeMarks.logic}
onChange={(e) => setCodeMarks({...codeMarks, logic: Number(e.target.value)})}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Output (5)</label>
<input
type="number" max={5} value={codeMarks.output}
onChange={(e) => setCodeMarks({...codeMarks, output: Number(e.target.value)})}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Concept (5)</label>
<input
type="number" max={5} value={codeMarks.concepts}
onChange={(e) => setCodeMarks({...codeMarks, concepts: Number(e.target.value)})}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
</div>
</div>
<div className="p-4 bg-slate-800/50 rounded-2xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-4">Viva (Max 15)</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">AI Viva (5)</label>
<input
type="number" max={5} value={vivaScore}
onChange={(e) => setVivaScore(Number(e.target.value))}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
<div>
<label className="block text-[10px] text-slate-500 uppercase mb-1">Teacher (10)</label>
<input
type="number" max={10} value={teacherVivaMarks}
onChange={(e) => setTeacherVivaMarks(Number(e.target.value))}
className="w-full px-3 py-2 rounded-lg bg-dark-bg border border-dark-border text-white text-sm"
/>
</div>
</div>
</div>
<div>
<label className="block text-xs font-bold text-slate-500 uppercase tracking-widest mb-2">Teacher Remarks</label>
<textarea
value={teacherRemarks}
onChange={(e) => setTeacherRemarks(e.target.value)}
placeholder="Enter qualitative feedback..."
className="w-full px-4 py-3 rounded-xl bg-dark-bg border border-dark-border text-white outline-none focus:ring-2 focus:ring-[#0b8c9f] min-h-[80px]"
/>
</div>
</div>
<div className="pt-4">
<button
disabled={isUpdating}
onClick={() => handleUpdateScore(selectedSubmission.id)}
className="w-full bg-[#0b8c9f] text-white py-4 rounded-xl font-bold hover:bg-[#097a8a] transition-all flex items-center justify-center gap-2 shadow-lg shadow-[#0b8c9f]/20"
>
{isUpdating ? 'Updating...' : 'Save Evaluation'} <Award size={20} />
</button>
</div>
</div>
</motion.div>
) : (
<div className="bg-dark-card rounded-3xl border border-dark-border p-12 text-center flex flex-col items-center justify-center h-full min-h-[400px]">
<div className="w-20 h-20 bg-slate-800 rounded-full flex items-center justify-center text-slate-600 mb-6">
<MessageSquare size={40} />
</div>
<h3 className="text-lg font-bold text-white mb-2">Select a Submission</h3>
<p className="text-sm text-slate-500 max-w-[200px]">Click on a student to start the evaluation process.</p>
</div>
)}
</div>
</div>
</div>
);
}
function X({ size }: { size: number }) {
return (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
);
}

View File

@ -0,0 +1,343 @@
import React from 'react';
import { motion, AnimatePresence } from 'motion/react';
import {
MessageSquare, Send, CheckCircle2, AlertCircle,
ArrowRight, Trophy, Brain, Target, RefreshCw,
ChevronRight, Sparkles, User, Award
} from 'lucide-react';
import { VivaQuestion, VivaEvaluation, VivaSession, UserProgress } from '../types';
import { generateVivaQuestions, evaluateVivaAnswer, generateVivaSummary } from '../lib/vivaSimulator';
import { cn } from '../lib/utils';
interface VivaSimulatorProps {
progress: UserProgress;
level: number;
onComplete: (session: VivaSession) => void;
onClose: () => void;
}
export function VivaSimulator({ progress, level, onComplete, onClose }: VivaSimulatorProps) {
const [step, setStep] = React.useState<'start' | 'questioning' | 'feedback' | 'summary'>('start');
const [questions, setQuestions] = React.useState<VivaQuestion[]>([]);
const [currentQuestionIndex, setCurrentQuestionIndex] = React.useState(0);
const [answer, setAnswer] = React.useState('');
const [isEvaluating, setIsEvaluating] = React.useState(false);
const [lastEvaluation, setLastEvaluation] = React.useState<VivaEvaluation | null>(null);
const [session, setSession] = React.useState<Partial<VivaSession>>({
id: Date.now().toString(),
level,
language: progress.language || 'JavaScript',
questions: []
});
const [loading, setLoading] = React.useState(false);
const startViva = async () => {
setLoading(true);
try {
const qs = await generateVivaQuestions(progress, level, 5);
setQuestions(qs);
setStep('questioning');
} catch (error) {
console.error("Failed to start viva:", error);
} finally {
setLoading(false);
}
};
const handleSubmitAnswer = async () => {
if (!answer.trim() || isEvaluating) return;
setIsEvaluating(true);
try {
const evaluation = await evaluateVivaAnswer(
questions[currentQuestionIndex],
answer,
progress.language || 'JavaScript'
);
setLastEvaluation(evaluation);
setSession(prev => ({
...prev,
questions: [
...(prev.questions || []),
{
question: questions[currentQuestionIndex],
answer,
evaluation
}
]
}));
setStep('feedback');
} catch (error) {
console.error("Failed to evaluate answer:", error);
} finally {
setIsEvaluating(false);
}
};
const nextQuestion = async () => {
if (currentQuestionIndex < questions.length - 1) {
setCurrentQuestionIndex(prev => prev + 1);
setAnswer('');
setLastEvaluation(null);
setStep('questioning');
} else {
setLoading(true);
try {
const summary = await generateVivaSummary(session);
const finalSession: VivaSession = {
...session as VivaSession,
summary,
completedAt: new Date().toISOString()
};
setSession(finalSession);
setStep('summary');
} catch (error) {
console.error("Failed to generate summary:", error);
} finally {
setLoading(false);
}
}
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/90 backdrop-blur-sm">
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
className="bg-dark-card w-full max-w-2xl rounded-3xl border border-dark-border shadow-2xl overflow-hidden flex flex-col max-h-[90vh]"
>
{/* Header */}
<div className="p-6 border-b border-dark-border flex items-center justify-between bg-dark-card/50">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-[#0b8c9f]/10 rounded-xl flex items-center justify-center text-[#0b8c9f]">
<MessageSquare size={20} />
</div>
<div>
<h3 className="text-xl font-black text-white">AI Viva Simulator</h3>
<p className="text-xs text-slate-400 font-bold uppercase tracking-widest">Level {level} {progress.language}</p>
</div>
</div>
<button
onClick={onClose}
className="text-slate-500 hover:text-white transition-colors"
>
<AlertCircle size={24} />
</button>
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-8 custom-scrollbar">
<AnimatePresence mode="wait">
{step === 'start' && (
<motion.div
key="start"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="text-center space-y-8 py-8"
>
<div className="w-20 h-20 bg-[#0b8c9f]/10 rounded-3xl flex items-center justify-center text-[#0b8c9f] mx-auto">
<Brain size={40} />
</div>
<div>
<h4 className="text-2xl font-black text-white mb-2">Ready for your Viva?</h4>
<p className="text-slate-400 max-w-md mx-auto">
I'll ask you 5 questions about what you've learned. Practice explaining your logic clearly and confidently!
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 max-w-lg mx-auto">
{[
{ icon: Target, label: "Conceptual Clarity" },
{ icon: MessageSquare, label: "Logical Explanation" },
{ icon: Award, label: "Confidence Building" }
].map((item, i) => (
<div key={i} className="p-4 bg-slate-900/50 rounded-2xl border border-dark-border">
<item.icon className="mx-auto mb-2 text-[#0b8c9f]" size={20} />
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">{item.label}</div>
</div>
))}
</div>
<button
onClick={startViva}
disabled={loading}
className="bg-[#0b8c9f] hover:bg-[#0b8c9f]/90 text-white px-8 py-4 rounded-2xl font-black shadow-lg shadow-[#0b8c9f]/20 transition-all flex items-center gap-2 mx-auto disabled:opacity-50"
>
{loading ? <RefreshCw className="animate-spin" /> : <Sparkles size={20} />}
Start Viva Session
</button>
</motion.div>
)}
{step === 'questioning' && (
<motion.div
key="questioning"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
className="space-y-8"
>
{/* Progress */}
<div className="flex items-center justify-between">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest">Question {currentQuestionIndex + 1} of {questions.length}</div>
<div className="flex gap-1">
{questions.map((_, i) => (
<div
key={i}
className={cn(
"h-1.5 rounded-full transition-all duration-500",
i === currentQuestionIndex ? "w-8 bg-[#0b8c9f]" :
i < currentQuestionIndex ? "w-4 bg-green-500/50" : "w-4 bg-slate-800"
)}
/>
))}
</div>
</div>
{/* Question */}
<div className="bg-slate-900/50 p-8 rounded-3xl border border-dark-border relative overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-[#0b8c9f]" />
<div className="flex items-center gap-2 mb-4">
<span className="px-2 py-1 bg-[#0b8c9f]/10 text-[#0b8c9f] text-[10px] font-bold rounded uppercase tracking-widest">
{questions[currentQuestionIndex].type}
</span>
</div>
<h4 className="text-xl font-bold text-white leading-relaxed">
{questions[currentQuestionIndex].text}
</h4>
{questions[currentQuestionIndex].context && (
<div className="mt-6 p-4 bg-slate-950 rounded-xl border border-dark-border font-mono text-sm text-slate-300 overflow-x-auto">
<pre>{questions[currentQuestionIndex].context}</pre>
</div>
)}
</div>
{/* Answer Input */}
<div className="space-y-4">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest">Your Explanation</div>
<textarea
value={answer}
onChange={(e) => setAnswer(e.target.value)}
placeholder="Type your answer here... Be as descriptive as possible!"
className="w-full h-32 bg-slate-900 border border-dark-border rounded-2xl p-4 text-white placeholder:text-slate-600 focus:outline-none focus:border-[#0b8c9f] transition-colors resize-none custom-scrollbar"
/>
<button
onClick={handleSubmitAnswer}
disabled={!answer.trim() || isEvaluating}
className="w-full bg-[#0b8c9f] hover:bg-[#0b8c9f]/90 text-white py-4 rounded-2xl font-black shadow-lg shadow-[#0b8c9f]/20 transition-all flex items-center justify-center gap-2 disabled:opacity-50"
>
{isEvaluating ? <RefreshCw className="animate-spin" /> : <Send size={20} />}
Submit Answer
</button>
</div>
</motion.div>
)}
{step === 'feedback' && lastEvaluation && (
<motion.div
key="feedback"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
className="space-y-8"
>
<div className="text-center">
<div className={cn(
"w-16 h-16 rounded-2xl flex items-center justify-center mx-auto mb-4 shadow-lg",
lastEvaluation.isCorrect ? "bg-green-500/10 text-green-500 shadow-green-500/10" : "bg-orange-500/10 text-orange-500 shadow-orange-500/10"
)}>
{lastEvaluation.isCorrect ? <CheckCircle2 size={32} /> : <Brain size={32} />}
</div>
<h4 className="text-xl font-black text-white">{lastEvaluation.qualitativeScore}</h4>
</div>
<div className="space-y-4">
<div className="p-6 bg-green-500/5 rounded-2xl border border-green-500/20">
<div className="text-[10px] font-bold text-green-500 uppercase tracking-widest mb-2">What was correct</div>
<p className="text-sm text-slate-300 leading-relaxed">{lastEvaluation.feedback.correct}</p>
</div>
<div className="p-6 bg-orange-500/5 rounded-2xl border border-orange-500/20">
<div className="text-[10px] font-bold text-orange-500 uppercase tracking-widest mb-2">What was missing</div>
<p className="text-sm text-slate-300 leading-relaxed">{lastEvaluation.feedback.missing}</p>
</div>
<div className="p-6 bg-[#0b8c9f]/5 rounded-2xl border border-[#0b8c9f]/20">
<div className="text-[10px] font-bold text-[#0b8c9f] uppercase tracking-widest mb-2">How to improve</div>
<p className="text-sm text-slate-300 leading-relaxed">{lastEvaluation.feedback.improvement}</p>
</div>
</div>
<button
onClick={nextQuestion}
className="w-full bg-slate-800 hover:bg-slate-700 text-white py-4 rounded-2xl font-black transition-all flex items-center justify-center gap-2"
>
{currentQuestionIndex < questions.length - 1 ? "Next Question" : "View Final Summary"}
<ArrowRight size={20} />
</button>
</motion.div>
)}
{step === 'summary' && session.summary && (
<motion.div
key="summary"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="space-y-8 py-4"
>
<div className="text-center">
<div className="w-20 h-20 bg-yellow-500/10 rounded-3xl flex items-center justify-center text-yellow-500 mx-auto mb-6 shadow-xl shadow-yellow-500/10">
<Trophy size={40} />
</div>
<h4 className="text-3xl font-black text-white mb-2">Viva Completed!</h4>
<div className="inline-block px-4 py-1.5 bg-yellow-500/10 text-yellow-500 rounded-full text-sm font-black uppercase tracking-widest border border-yellow-500/20">
{session.summary.finalQualitative}
</div>
</div>
<div className="bg-slate-900/50 p-8 rounded-3xl border border-dark-border">
<div className="text-xs font-bold text-slate-500 uppercase tracking-widest mb-4">Overall Performance</div>
<p className="text-slate-300 leading-relaxed italic">"{session.summary.overall}"</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="p-6 bg-green-500/5 rounded-2xl border border-green-500/20">
<div className="text-[10px] font-bold text-green-500 uppercase tracking-widest mb-4 flex items-center gap-2">
<Award size={14} /> Key Strengths
</div>
<ul className="space-y-3">
{session.summary.strengths.map((s, i) => (
<li key={i} className="flex items-start gap-2 text-xs text-slate-300">
<CheckCircle2 size={14} className="text-green-500 mt-0.5 flex-shrink-0" />
{s}
</li>
))}
</ul>
</div>
<div className="p-6 bg-orange-500/5 rounded-2xl border border-orange-500/20">
<div className="text-[10px] font-bold text-orange-500 uppercase tracking-widest mb-4 flex items-center gap-2">
<Target size={14} /> Areas to Improve
</div>
<ul className="space-y-3">
{session.summary.improvements.map((s, i) => (
<li key={i} className="flex items-start gap-2 text-xs text-slate-300">
<ChevronRight size={14} className="text-orange-500 mt-0.5 flex-shrink-0" />
{s}
</li>
))}
</ul>
</div>
</div>
<button
onClick={() => onComplete(session as VivaSession)}
className="w-full bg-[#0b8c9f] hover:bg-[#0b8c9f]/90 text-white py-4 rounded-2xl font-black shadow-lg shadow-[#0b8c9f]/20 transition-all"
>
Save & Finish Session
</button>
</motion.div>
)}
</AnimatePresence>
</div>
</motion.div>
</div>
);
}

65
code_arena/src/index.css Normal file
View File

@ -0,0 +1,65 @@
@import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Root Variables */
:root {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-serif: "Playfair Display", serif;
--font-cursive: "Dancing Script", cursive;
/* Brand Colors */
--color-primary: #0b8c9f;
--color-secondary: #ff7a00;
/* Dark Theme */
--color-dark-bg: #020617;
--color-dark-card: #0f172a;
--color-dark-border: #1e293b;
/* Slate Colors */
--color-slate-50: #f8fafc;
--color-slate-100: #f1f5f9;
--color-slate-200: #e2e8f0;
--color-slate-300: #cbd5e1;
--color-slate-400: #94a3b8;
--color-slate-500: #64748b;
--color-slate-600: #475569;
--color-slate-700: #334155;
--color-slate-800: #1e293b;
--color-slate-900: #0f172a;
--color-slate-950: #020617;
}
/* Apply Base Styles */
body {
font-family: var(--font-sans);
background-color: var(--color-dark-bg);
color: var(--color-slate-50);
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--color-dark-bg);
}
::-webkit-scrollbar-thumb {
background: var(--color-dark-border);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-primary);
}
/* Glass Card */
.glass-card {
background: rgba(15, 23, 42, 0.5);
backdrop-filter: blur(10px);
border: 1px solid var(--color-dark-border);
}

View File

@ -0,0 +1,111 @@
import { jsPDF } from 'jspdf';
import html2canvas from 'html2canvas';
export const generateCertificate = async (
studentName: string,
level: number,
track: string,
skills: string[],
isPremium: boolean,
grade: string,
rollNumber: string,
projectName?: string,
effortLevel?: string,
schoolName?: string,
principalName?: string
) => {
const element = document.createElement('div');
element.style.width = '800px';
element.style.padding = '40px';
element.style.background = '#fff';
element.style.position = 'absolute';
element.style.left = '-9999px';
element.style.fontFamily = "'Inter', sans-serif";
const certificateId = `RS-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
const date = new Date().toLocaleDateString();
const logoUrl = "https://raw.githubusercontent.com/RS-Learning-Lab/assets/main/logo.png";
element.innerHTML = `
<div style="border: 15px solid #0b8c9f; padding: 40px; position: relative; background: #fff; box-sizing: border-box; height: 560px;">
<!-- Watermark -->
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0.05; pointer-events: none; width: 400px;">
<img src="${logoUrl}" style="width: 100%;" />
</div>
<div style="text-align: center; position: relative; z-index: 1;">
<div style="display: flex; justify-content: center; margin-bottom: 20px;">
<img src="${logoUrl}" style="height: 60px;" />
</div>
<div style="color: #0b8c9f; font-weight: 800; font-size: 28px; margin-bottom: 5px; letter-spacing: 2px;">RS LEARNING LAB</div>
<div style="height: 2px; width: 100px; background: #ff7a00; margin: 0 auto 20px;"></div>
<h1 style="font-size: 36px; margin: 10px 0; color: #1a1a1a; text-transform: uppercase; letter-spacing: 1px;">Certificate of Achievement</h1>
<p style="font-size: 16px; color: #666; margin-bottom: 10px;">This prestigious award is presented to</p>
<h2 style="font-size: 42px; color: #ff7a00; margin: 5px 0; font-family: 'Playfair Display', serif; font-weight: 700; border-bottom: 2px solid #f0f0f0; display: inline-block; padding: 0 40px;">${studentName}</h2>
<div style="margin: 5px 0; font-size: 14px; color: #444; font-weight: 600;">
Roll No: ${rollNumber} | Class: ${grade}
</div>
<div style="margin: 5px 0; font-size: 14px; color: #0b8c9f; font-weight: 700; text-transform: uppercase;">
${schoolName || 'RS Learning Lab'}
</div>
<p style="font-size: 15px; color: #666; max-width: 600px; margin: 15px auto;">
for outstanding performance and successful completion of <br/>
<strong style="color: #0b8c9f;">Level ${level} - ${track}</strong>
${projectName ? `<br/>and the <strong style="color: #0b8c9f;">${projectName}</strong> project` : ''}
${effortLevel ? `<br/><span style="font-size: 13px; color: #ff7a00; font-weight: bold;">Effort Recognition: ${effortLevel} Learner</span>` : ''}
</p>
<div style="margin-top: 20px; text-align: left; display: flex; justify-content: space-between; align-items: flex-end;">
<div style="flex: 1;">
<p style="font-size: 11px; color: #888; margin-bottom: 6px; font-weight: bold; text-transform: uppercase;">Skills Mastered:</p>
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
${skills.map(s => `<span style="background: #f0fdfa; color: #0b8c9f; padding: 3px 8px; border-radius: 4px; font-size: 10px; font-weight: 600; border: 1px solid #0b8c9f;">${s}</span>`).join('')}
</div>
<div style="margin-top: 20px;">
<p style="font-size: 10px; color: #aaa; margin: 0;">Certificate ID: ${certificateId}</p>
<p style="font-size: 10px; color: #aaa; margin: 0;">Issued on: ${date}</p>
</div>
</div>
<div style="text-align: right; flex: 1;">
<div style="display: flex; gap: 40px; justify-content: flex-end;">
<div style="text-align: center;">
<div style="font-family: 'Dancing Script', cursive; font-size: 24px; color: #1a1a1a; border-bottom: 2px solid #0b8c9f; padding: 0 15px 3px;">Gokula Krishnan</div>
<p style="font-size: 10px; color: #0b8c9f; font-weight: bold; margin-top: 6px; text-transform: uppercase; letter-spacing: 1px;">Founder</p>
</div>
<div style="text-align: center;">
<div style="width: 120px; border-bottom: 2px solid #ccc; padding: 0 15px 3px; height: 30px;"></div>
<p style="font-size: 10px; color: #888; font-weight: bold; margin-top: 6px; text-transform: uppercase; letter-spacing: 1px;">School Signature</p>
</div>
</div>
</div>
</div>
</div>
${isPremium ? `
<div style="position: absolute; top: 30px; right: 30px; width: 100px; height: 100px; background: #ff7a00; border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: white; font-weight: 900; font-size: 11px; text-align: center; transform: rotate(15deg); box-shadow: 0 6px 15px rgba(255, 122, 0, 0.4); border: 4px double white;">
<span style="font-size: 14px;">PREMIUM</span>
<span>EXCELLENCE</span>
</div>
` : ''}
</div>
`;
document.body.appendChild(element);
const canvas = await html2canvas(element, { scale: 3, useCORS: true });
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'landscape',
unit: 'px',
format: [800, 560]
});
pdf.addImage(imgData, 'PNG', 0, 0, 800, 560);
pdf.save(`RS_Certificate_${studentName.replace(/\s+/g, '_')}.pdf`);
document.body.removeChild(element);
};

View File

@ -0,0 +1,402 @@
import { Document, Packer, Paragraph, TextRun, AlignmentType, HeadingLevel, ImageRun } from 'docx';
import { saveAs } from 'file-saver';
export const generateProjectDoc = async (
studentName: string,
grade: string,
rollNumber: string,
projectTitle: string,
projectId: string,
schoolName?: string,
principalName?: string,
docContent?: {
objective: string;
conceptsUsed: string;
howItWorks: string;
whatILearned: string;
}
) => {
const logoUrl = "https://raw.githubusercontent.com/RS-Learning-Lab/assets/main/logo.png";
let logoImage;
try {
const response = await fetch(logoUrl);
const buffer = await response.arrayBuffer();
logoImage = new ImageRun({
data: new Uint8Array(buffer),
transformation: {
width: 100,
height: 100,
},
} as any);
} catch (error) {
console.error("Failed to load logo for document:", error);
}
const doc = new Document({
sections: [
{
properties: {},
children: [
// Front Page Logo
...(logoImage ? [
new Paragraph({
alignment: AlignmentType.CENTER,
children: [logoImage],
}),
new Paragraph({ spacing: { before: 200 } }),
] : []),
// Front Page
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({
text: "RS LEARNING LAB",
bold: true,
size: 56,
color: "0b8c9f",
font: "Calibri",
}),
],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({
text: "Innovation through Code",
italics: true,
size: 24,
color: "666666",
}),
],
}),
new Paragraph({ spacing: { before: 1200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({
text: "PROJECT DOCUMENTATION",
bold: true,
size: 40,
underline: {},
}),
],
}),
new Paragraph({ spacing: { before: 800 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({
text: projectTitle.toUpperCase(),
bold: true,
size: 32,
color: "333333",
}),
],
}),
new Paragraph({ spacing: { before: 1200 } }),
// Front Page Details (Centered)
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "Project ID: ", bold: true, size: 24 }),
new TextRun({ text: `RS-PROJ-${projectId}`, size: 24 }),
],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "Student Name: ", bold: true, size: 24 }),
new TextRun({ text: studentName, size: 24 }),
],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "Class: ", bold: true, size: 24 }),
new TextRun({ text: grade, size: 24 }),
],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "School Name: ", bold: true, size: 24 }),
new TextRun({ text: schoolName || "N/A", size: 24 }),
],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "Founder Name: ", bold: true, size: 24 }),
new TextRun({ text: "Gokula Krishnan", size: 24 }),
],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "Date: ", bold: true, size: 24 }),
new TextRun({ text: new Date().toLocaleDateString(), size: 24 }),
],
}),
// Page Break
new Paragraph({ children: [new TextRun({ text: "", break: 1 })] }),
// Acknowledgement Page
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "ACKNOWLEDGEMENT", bold: true, size: 36, color: "0b8c9f" }),
],
}),
new Paragraph({ spacing: { before: 800 } }),
new Paragraph({
alignment: AlignmentType.JUSTIFIED,
children: [
new TextRun({
text: "I would like to extend my heartfelt gratitude to my school for their unwavering support and for providing the necessary resources to undertake this project. I am particularly grateful to RS Learning Lab and my mentor, Mr. Gokula Krishnan, for his exceptional guidance, technical expertise, and constant encouragement throughout the project's lifecycle. His mentorship has been instrumental in the successful completion of this endeavor. Furthermore, I would like to express my sincere appreciation to my parents and friends for their continuous motivation and support, which played a vital role in my academic journey.",
size: 24,
}),
],
}),
new Paragraph({ spacing: { before: 1200 } }),
new Paragraph({
alignment: AlignmentType.RIGHT,
children: [
new TextRun({ text: "__________________________", bold: true }),
],
}),
new Paragraph({
alignment: AlignmentType.RIGHT,
children: [
new TextRun({ text: "Student Signature", size: 18, color: "666666" }),
],
}),
// Page Break
new Paragraph({ children: [new TextRun({ text: "", break: 1 })] }),
// Certificate
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "CERTIFICATE OF COMPLETION", bold: true, size: 36, color: "0b8c9f" }),
],
}),
new Paragraph({ spacing: { before: 600 } }),
new Paragraph({
alignment: AlignmentType.JUSTIFIED,
children: [
new TextRun({
text: `This is to formally certify that `,
size: 24,
}),
new TextRun({
text: studentName,
bold: true,
size: 24,
}),
new TextRun({
text: ` of `,
size: 24,
}),
new TextRun({
text: grade,
bold: true,
size: 24,
}),
new TextRun({
text: `, `,
size: 24,
}),
new TextRun({
text: schoolName || "N/A",
bold: true,
size: 24,
}),
new TextRun({
text: ` has successfully designed, developed, and documented the project titled `,
size: 24,
}),
new TextRun({
text: `"${projectTitle}"`,
bold: true,
size: 24,
}),
new TextRun({
text: `. This project was completed as part of the curriculum at RS Learning Lab, demonstrating proficiency in logical reasoning and technical implementation.`,
size: 24,
}),
],
}),
new Paragraph({ spacing: { before: 1600 } }),
// Signatures
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "__________________________", bold: true }),
new TextRun({ text: "\t\t\t\t\t__________________________", bold: true }),
],
}),
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "Gokula Krishnan", bold: true, size: 22 }),
new TextRun({ text: "\t\t\t\t\t", bold: true, size: 22 }),
],
}),
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "Founder, RS Learning Lab", size: 18, color: "666666" }),
new TextRun({ text: "\t\t\t\t\tSchool Signature", size: 18, color: "666666" }),
],
}),
// Page Break
new Paragraph({ children: [new TextRun({ text: "", break: 1 })] }),
// Project Technical Content
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "PROJECT CONTENT", bold: true, size: 36, color: "0b8c9f" }),
],
}),
new Paragraph({ spacing: { before: 600 } }),
new Paragraph({
children: [new TextRun({ text: "1. Objective", bold: true, size: 28 })],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.JUSTIFIED,
children: [
new TextRun({
text: docContent?.objective || "The primary objective of this project is to develop a functional application that demonstrates the practical application of programming logic in a real-world context.",
size: 22,
color: "444444"
}),
],
}),
new Paragraph({ spacing: { before: 400 } }),
new Paragraph({
children: [new TextRun({ text: "2. Concepts Used", bold: true, size: 28 })],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.JUSTIFIED,
children: [
new TextRun({
text: docContent?.conceptsUsed || "Core logic, variables, and algorithmic thinking were applied to build this solution.",
size: 22,
color: "444444"
}),
],
}),
new Paragraph({ spacing: { before: 400 } }),
new Paragraph({
children: [new TextRun({ text: "3. How It Works", bold: true, size: 28 })],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.JUSTIFIED,
children: [
new TextRun({
text: docContent?.howItWorks || "The implementation follows a structured approach to process input and generate the desired output.",
size: 22,
color: "444444"
}),
],
}),
new Paragraph({ spacing: { before: 400 } }),
new Paragraph({
children: [new TextRun({ text: "4. What I Learned", bold: true, size: 28 })],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.JUSTIFIED,
children: [
new TextRun({
text: docContent?.whatILearned || "Through this project, I have gained significant insights into problem-solving and technical implementation.",
size: 22,
color: "444444"
}),
],
}),
new Paragraph({ spacing: { before: 1200 } }),
// Declaration
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({
text: "DECLARATION",
bold: true,
size: 24,
color: "333333",
}),
],
}),
new Paragraph({ spacing: { before: 200 } }),
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({
text: "I hereby declare that this project is a result of my own efforts and has been completed with integrity under the academic guidance of RS Learning Lab. This is my own work.",
italics: true,
size: 20,
color: "666666",
}),
],
}),
new Paragraph({ spacing: { before: 1200 } }),
// Evaluation Section
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "OFFICIAL EVALUATION", bold: true, size: 24, color: "0b8c9f" }),
],
}),
new Paragraph({ spacing: { before: 400 } }),
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "Performance Score: ", bold: true, size: 22 }),
new TextRun({ text: "__________ / 100", size: 22 }),
],
}),
new Paragraph({ spacing: { before: 400 } }),
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "Instructor Remarks: ", bold: true, size: 22 }),
],
}),
new Paragraph({
alignment: AlignmentType.LEFT,
children: [
new TextRun({ text: "__________________________________________________________________________", size: 18, color: "999999" }),
],
}),
],
},
],
});
const blob = await Packer.toBlob(doc);
saveAs(blob, `${projectTitle.replace(/\s+/g, '_')}_Documentation.docx`);
};

View File

@ -0,0 +1,120 @@
import { GoogleGenAI, Type } from "@google/genai";
import { ProgrammingLanguage } from "../types";
export interface ErrorAnalysis {
errorType: 'Syntax' | 'Logic' | 'Runtime' | 'Conceptual';
explanation: {
what: string;
why: string;
where: string;
};
steps: {
step1: string; // Identify mistake
step2: string; // Explain concept
step3: string; // Show correct approach
};
suggestedFix: string;
correctLogic: string;
correctedCode?: string;
hints: string[]; // [Small clue, More guidance, Full explanation]
}
export async function analyzeError(
studentCode: string,
expectedOutput: string,
actualOutput: string,
language: ProgrammingLanguage,
problemDescription: string
): Promise<ErrorAnalysis> {
const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
throw new Error("Gemini API key is missing. Please configure it in the settings.");
}
const ai = new GoogleGenAI({ apiKey });
try {
const response = await ai.models.generateContent({
model: "gemini-3-flash-preview",
contents: `You are an expert AI Coding Tutor for school students (Classes 6-12).
Analyze the following incorrect code submission and provide a student-friendly explanation.
Problem Description: ${problemDescription}
Programming Language: ${language}
Student Code:
\`\`\`${language.toLowerCase()}
${studentCode}
\`\`\`
Expected Output: ${expectedOutput}
Actual Output: ${actualOutput}
Rules:
1. Keep it simple and encouraging. No complex jargon.
2. Identify the error type: Syntax, Logic, Runtime, or Conceptual.
3. Provide a 3-step learning breakdown.
4. Generate 3 progressive hints:
- Hint 1: A very small clue to nudge them.
- Hint 2: More specific guidance.
- Hint 3: A full explanation of how to fix it.
Return a JSON object with the following structure:
{
"errorType": "Syntax" | "Logic" | "Runtime" | "Conceptual",
"explanation": {
"what": "What exactly went wrong in simple terms",
"why": "Why this is a mistake in this language",
"where": "Where the mistake is (e.g., 'on the line where you defined the loop')"
},
"steps": {
"step1": "Identify the mistake clearly",
"step2": "Explain the underlying concept simply",
"step3": "Show the correct approach without necessarily giving the full code yet"
},
"suggestedFix": "A short, actionable instruction to fix the code",
"correctLogic": "Explain the correct logic that should have been used",
"correctedCode": "The full corrected code snippet",
"hints": ["Hint 1", "Hint 2", "Hint 3"]
}`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: Type.OBJECT,
properties: {
errorType: { type: Type.STRING },
explanation: {
type: Type.OBJECT,
properties: {
what: { type: Type.STRING },
why: { type: Type.STRING },
where: { type: Type.STRING }
},
required: ["what", "why", "where"]
},
steps: {
type: Type.OBJECT,
properties: {
step1: { type: Type.STRING },
step2: { type: Type.STRING },
step3: { type: Type.STRING }
},
required: ["step1", "step2", "step3"]
},
suggestedFix: { type: Type.STRING },
correctLogic: { type: Type.STRING },
correctedCode: { type: Type.STRING },
hints: {
type: Type.ARRAY,
items: { type: Type.STRING }
}
},
required: ["errorType", "explanation", "steps", "suggestedFix", "correctLogic", "correctedCode", "hints"]
}
}
});
return JSON.parse(response.text) as ErrorAnalysis;
} catch (error) {
console.error("Error Analysis failed:", error);
throw new Error("Failed to analyze the error. Please try again.");
}
}

124
code_arena/src/lib/lei.ts Normal file
View File

@ -0,0 +1,124 @@
import { GoogleGenAI, Type } from "@google/genai";
import { UserProgress } from "../types";
export interface LEIResult {
score: number;
category: 'Active Learner' | 'Consistent Learner' | 'Needs Support' | 'Stalled Learner';
insight: string;
}
export async function calculateLEI(progress: UserProgress): Promise<LEIResult> {
const apiKey = process.env.GEMINI_API_KEY;
if (!apiKey) {
// Fallback if no API key
const score = calculateManualLEI(progress);
return {
score,
category: getLEICategory(score),
insight: "AI insight unavailable. Manual calculation used."
};
}
const ai = new GoogleGenAI({ apiKey });
const completedTracksCount = Object.values(progress.completedTracks).filter(Boolean).length;
const metrics = {
attempts: progress.totalAttempts,
timeSpent: progress.timeSpent,
completedTracks: completedTracksCount,
retries: progress.retries || 0,
hintsUsed: progress.hintsUsed,
streak: progress.streak,
xp: progress.xp
};
try {
const response = await ai.models.generateContent({
model: "gemini-3-flash-preview",
contents: `Analyze the following student learning metrics and calculate a Learning Efficiency Index (LEI) score (0-100).
Metrics:
- Total Coding Attempts: ${metrics.attempts}
- Total Time Spent (minutes): ${metrics.timeSpent}
- Completed Learning Tracks: ${metrics.completedTracks} (out of 12)
- Retry Count: ${metrics.retries}
- Hints Used: ${metrics.hintsUsed}
- Daily Activity Streak: ${metrics.streak}
Calculation Rules:
- Completion (Basics + Tracks) -> 30% weight
- Effort (Attempts + Retries) -> 25% weight
- Consistency (Daily usage / streak) -> 20% weight
- Time Spent -> 15% weight
- Hint usage -> -10% penalty
Categories:
- 80-100: Active Learner
- 60-79: Consistent Learner
- 40-59: Needs Support
- 0-39: Stalled Learner
Return a JSON object with:
{
"score": number (0-100),
"category": "Active Learner" | "Consistent Learner" | "Needs Support" | "Stalled Learner",
"insight": "A short (1-2 sentence) insight about the student's learning behavior"
}`,
config: {
responseMimeType: "application/json",
responseSchema: {
type: Type.OBJECT,
properties: {
score: { type: Type.NUMBER },
category: { type: Type.STRING },
insight: { type: Type.STRING }
},
required: ["score", "category", "insight"]
}
}
});
const result = JSON.parse(response.text);
return {
score: Math.min(100, Math.max(0, result.score)),
category: result.category,
insight: result.insight
};
} catch (error) {
console.error("LEI Calculation failed:", error);
const score = calculateManualLEI(progress);
return {
score,
category: getLEICategory(score),
insight: "Error generating AI insight. Manual calculation used."
};
}
}
function calculateManualLEI(progress: UserProgress): number {
const completedTracksCount = Object.values(progress.completedTracks).filter(Boolean).length;
// Normalization factors (estimated)
const completionScore = (completedTracksCount / 12) * 100;
const effortScore = Math.min(100, ((progress.totalAttempts + (progress.retries || 0)) / 40) * 100);
const consistencyScore = Math.min(100, (progress.streak / 7) * 100);
const timeScore = Math.min(100, (progress.timeSpent / 300) * 100);
const hintPenalty = Math.min(100, (progress.hintsUsed / 10) * 100);
const finalScore = (
(completionScore * 0.3) +
(effortScore * 0.25) +
(consistencyScore * 0.2) +
(timeScore * 0.15) -
(hintPenalty * 0.1)
);
return Math.round(Math.min(100, Math.max(0, finalScore)));
}
function getLEICategory(score: number): LEIResult['category'] {
if (score >= 80) return 'Active Learner';
if (score >= 60) return 'Consistent Learner';
if (score >= 40) return 'Needs Support';
return 'Stalled Learner';
}

View File

@ -0,0 +1,9 @@
import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
export const getLogoUrl = async (base64Image: string) => {
// In a real app, you might upload this to a storage service.
// For this environment, we'll just return the base64 string as the URL.
return `data:image/png;base64,${base64Image}`;
};

View File

@ -0,0 +1,62 @@
import { PlagiarismReport } from "../types";
export async function detectPlagiarism(
newCode: string,
existingSubmissions: { studentName: string; rollNumber: string; code: string }[],
problemStatement: string,
language: string
): Promise<PlagiarismReport> {
if (existingSubmissions.length === 0) {
return {
score: 0,
status: 'Safe',
explanation: "No other submissions found for comparison.",
similarSubmissions: []
};
}
// 🔥 SAFE MODE (NO AI)
// Basic dummy comparison logic
let highestScore = 0;
let similarSubmissions: any[] = [];
existingSubmissions.forEach(sub => {
// simple similarity check (length + includes)
let similarity = 0;
if (newCode.trim() === sub.code.trim()) {
similarity = 90;
} else if (newCode.includes(sub.code.substring(0, 20))) {
similarity = 50;
} else {
similarity = 10;
}
if (similarity > highestScore) {
highestScore = similarity;
}
if (similarity > 30) {
similarSubmissions.push({
studentName: sub.studentName,
rollNumber: sub.rollNumber,
similarityScore: similarity,
reason: "Basic similarity detected (AI disabled mode)"
});
}
});
let status: 'Safe' | 'Needs Review' | 'High Similarity' = 'Safe';
if (highestScore >= 60) status = 'High Similarity';
else if (highestScore >= 30) status = 'Needs Review';
return {
score: highestScore,
status,
explanation: "Basic plagiarism check (AI disabled mode)",
similarSubmissions
};
}

View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View File

@ -0,0 +1,79 @@
// AI DISABLED VERSION (SAFE)
import { UserProgress, VivaQuestion, VivaEvaluation, VivaSession } from "../types";
// ------------------ DOCUMENT ANALYSIS ------------------
export async function analyzeDocumentation(submission: any, language: string): Promise<any> {
return {
marks: {
documentation: {
objective: 4,
concepts: 4,
explanation: 4,
learningOutcome: 4,
total: 16
},
code: {
logic: 4,
output: 4,
concepts: 4,
total: 12
}
},
aiFeedback: "Good effort. AI disabled mode."
};
}
// ------------------ VIVA QUESTIONS ------------------
export async function generateVivaQuestions(
progress: UserProgress,
level: number,
count: number = 5
): Promise<VivaQuestion[]> {
return [
{
id: "q1",
text: "Explain your project.",
type: "Conceptual"
},
{
id: "q2",
text: "What concepts did you use?",
type: "Conceptual"
},
{
id: "q3",
text: "What is the output of your program?",
type: "Output Prediction"
}
];
}
// ------------------ ANSWER EVALUATION ------------------
export async function evaluateVivaAnswer(
question: VivaQuestion,
answer: string,
language: string
): Promise<VivaEvaluation> {
return {
isCorrect: true,
feedback: {
correct: "Good explanation",
missing: "Could improve clarity",
improvement: "Practice more"
},
qualitativeScore: "Good understanding"
};
}
// ------------------ SUMMARY ------------------
export async function generateVivaSummary(
session: Partial<VivaSession>
): Promise<VivaSession['summary']> {
return {
overall: "Good performance",
strengths: ["Basic understanding", "Effort"],
improvements: ["More clarity needed"],
finalQualitative: "Good"
};
}

10
code_arena/src/main.tsx Normal file
View File

@ -0,0 +1,10 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

1931
code_arena/src/types.ts Normal file

File diff suppressed because it is too large Load Diff

26
code_arena/tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

24
code_arena/vite.config.ts Normal file
View File

@ -0,0 +1,24 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});

39
coding.php Normal file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>RS Learning Lab - Coding Challenge</title>
<style>
body {
margin: 0;
background: #020617;
font-family: Arial, sans-serif;
}
.header {
background: #0b8c9f;
color: white;
padding: 12px;
text-align: center;
font-size: 18px;
font-weight: bold;
}
iframe {
width: 100%;
height: calc(100vh - 50px);
border: none;
}
</style>
</head>
<body>
<div class="header">
RS Learning Lab - Coding Challenge
</div>
<iframe src="https://ai.studio/apps/d9855fda-dc07-40f0-9cd7-f15e32e565e5"></iframe>
</body>
</html>

30
coding/attempt.php Normal file
View File

@ -0,0 +1,30 @@
<?php
if (!isset($_GET['track_id'], $_GET['challenge_no'])) {
die('Invalid access');
}
$track_id = (int)$_GET['track_id'];
$challenge_no = (int)$_GET['challenge_no'];
?>
<!DOCTYPE html>
<html>
<head>
<title>Challenge <?= $challenge_no ?></title>
</head>
<body>
<h2>Challenge <?= $challenge_no ?></h2>
<p><b>Question:</b> Write code to complete this task.</p>
<form method="post" action="submit.php">
<input type="hidden" name="track_id" value="<?= $track_id ?>">
<input type="hidden" name="challenge_no" value="<?= $challenge_no ?>">
<input type="text" name="answer" placeholder="Your Answer" required>
<br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>

53
coding/challenges.php Normal file
View File

@ -0,0 +1,53 @@
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['student_name'] = $_POST['student_name'];
$_SESSION['class'] = $_POST['class'];
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Coding Challenges</title>
<style>
body { font-family: Arial; background:#f4f6f8; }
.card {
background:#fff;
padding:20px;
margin:20px auto;
width:500px;
border-radius:8px;
}
a {
text-decoration:none;
color:#0b8c9f;
font-weight:bold;
}
</style>
</head>
<body>
<h2 style="text-align:center;">Coding Challenges</h2>
<?php
$challenges = [
1 => "Identify the Output",
2 => "Logical Sequence",
3 => "Condition Based Thinking",
4 => "Pattern Recognition",
5 => "Step-by-Step Logic"
];
foreach ($challenges as $id => $title) {
echo "
<div class='card'>
<h3>$title</h3>
<a href='attempt.php?id=$id'>Start Challenge</a>
</div>";
}
?>
</body>
</html>

View File

@ -0,0 +1,44 @@
<?php
require_once(__DIR__ . "/../config/db.php");
$student_id = $_SESSION['student_id'];
$topic_id = $_GET['topic_id'];
$class = $_GET['class'];
/* CHECK COMPLETED TRACKS */
$stmt = $pdo->prepare("
SELECT COUNT(*) as completed_tracks
FROM student_progress
WHERE student_id = ?
AND topic_id = ?
AND completed = 1
");
$stmt->execute([$student_id, $topic_id]);
$result = $stmt->fetch();
if ($result['completed_tracks'] == 3 && $class >= 11) {
/* CHECK ALREADY UNLOCKED */
$check = $pdo->prepare("
SELECT * FROM project_unlock
WHERE student_id=? AND topic_id=?
");
$check->execute([$student_id, $topic_id]);
if ($check->rowCount() == 0) {
/* INSERT UNLOCK */
$insert = $pdo->prepare("
INSERT INTO project_unlock
(student_id, topic_id, class, unlocked)
VALUES (?, ?, ?, 1)
");
$insert->execute([$student_id, $topic_id, $class]);
}
echo "unlocked";
} else {
echo "locked";
}
?>

204
coding/mini_project.php Normal file
View File

@ -0,0 +1,204 @@
<?php
session_start();
$name = $_SESSION['student_name'] ?? 'Student';
$class = $_SESSION['student_class'] ?? '';
$topic_id = $_GET['topic_id'] ?? 1;
/* 🔥 PROJECT QUESTIONS (TOPIC BASED) */
$projects = [
1 => [
"title" => "Student Grade Calculator",
"desc" => "Take 3 subject marks and print grade based on average.",
"rules" => [
"Take 3 inputs",
"Find average",
">=90 → A",
">=75 → B",
">=50 → C",
"<50 → Fail"
],
"example" => "Input: 80 90 70\nOutput: Grade B"
],
2 => [
"title" => "Even or Odd Analyzer",
"desc" => "Take a number and print whether it is Even or Odd.",
"rules" => [
"Take input",
"Use condition",
"Print result"
],
"example" => "Input: 5\nOutput: Odd"
]
];
$project = $projects[$topic_id] ?? $projects[1];
?>
<!DOCTYPE html>
<html>
<head>
<title>Mini Project | RS Learning Lab</title>
<style>
body{
background: radial-gradient(circle at top,#020617,#0f172a);
color:#e5e7eb;
font-family:'Segoe UI', Arial;
margin:0;
}
/* 🔥 HEADER */
.header{
width:85%;
margin:20px auto;
color:#94a3b8;
font-size:14px;
}
.container{
width:85%;
margin:20px auto;
display:flex;
gap:30px;
}
.left{
width:42%;
background: rgba(15,23,42,0.7);
backdrop-filter: blur(10px);
padding:28px;
border-radius:18px;
box-shadow:0 0 25px rgba(0,0,0,0.5);
}
.right{
width:58%;
background: rgba(2,6,23,0.8);
padding:28px;
border-radius:18px;
box-shadow:0 0 25px rgba(0,0,0,0.6);
}
h2{
margin-top:0;
font-size:24px;
}
textarea{
width:100%;
height:320px;
background:#020617;
color:#22c55e;
border:1px solid #1f2937;
padding:15px;
border-radius:12px;
font-family:monospace;
font-size:14px;
outline:none;
}
textarea:focus{
border:1px solid #22c55e;
box-shadow:0 0 10px rgba(34,197,94,0.3);
}
button{
margin-top:20px;
padding:14px 40px;
border:none;
border-radius:999px;
font-weight:bold;
font-size:15px;
background:linear-gradient(135deg,#22c55e,#06b6d4);
color:#020617;
cursor:pointer;
transition:0.3s;
}
button:hover{
transform:scale(1.05);
box-shadow:0 0 20px rgba(34,197,94,0.4);
}
.rule{
margin-bottom:10px;
}
/* 🔥 TIMER */
.timer{
color:#facc15;
margin-bottom:10px;
}
</style>
</head>
<body>
<!-- 🔥 STUDENT HEADER -->
<div class="header">
👤 <?= htmlspecialchars($name) ?> | 🎓 Class <?= htmlspecialchars($class) ?>
</div>
<div class="container">
<!-- 🔥 LEFT SIDE -->
<div class="left">
<h2>🚀 <?= $project['title'] ?></h2>
<p><?= $project['desc'] ?></p>
<h3>📌 Requirements:</h3>
<?php foreach($project['rules'] as $r): ?>
<div class="rule"> <?= $r ?></div>
<?php endforeach; ?>
<h3>🧪 Example:</h3>
<pre><?= $project['example'] ?></pre>
</div>
<!-- 🔥 RIGHT SIDE -->
<div class="right">
<h2>💻 Write Your Code</h2>
<div class="timer" id="timer">
Time Left: 10:00
</div>
<div style="margin-bottom:10px;color:#22c55e;">
💻 Language: Python
</div>
<input type="hidden" name="start_time" id="start_time">
<form method="POST" action="/rs_lab/coding/submit_project.php">
<textarea name="code" placeholder="Write your code here..." required></textarea>
<input type="hidden" name="topic_id" value="<?= $topic_id ?>">
<button type="submit">🚀 Submit Project</button>
</form>
</div>
</div>
<!-- 🔥 TIMER SCRIPT -->
<script>
let time = 600;
setInterval(()=>{
let m = Math.floor(time/60);
let s = time%60;
document.getElementById("timer").innerText =
"⏳ Time Left: " + m + ":" + (s<10?"0":"") + s;
time--;
},1000);
</script>
<script>
document.getElementById("start_time").value = Date.now();
</script>
</body>
</html>

19
coding/submit.php Normal file
View File

@ -0,0 +1,19 @@
<?php
session_start();
require_once "../config.php";
$student_name = $_SESSION['student_name'];
$class = $_SESSION['class'];
$track_id = $_POST['track_id'];
$challenge_no = $_POST['challenge_no'];
$stmt = $conn->prepare(
"INSERT INTO coding_submissions
(student_name, class, track_id, challenge_no)
VALUES (?, ?, ?, ?)"
);
$stmt->bind_param("ssii", $student_name, $class, $track_id, $challenge_no);
$stmt->execute();
header("Location: track_challenges.php?track_id=$track_id");
exit;

90
coding/submit_project.php Normal file
View File

@ -0,0 +1,90 @@
<?php
session_start();
require_once(__DIR__ . "/../config/db.php");
$name = $_SESSION['student_name'] ?? 'Student';
$roll = $_SESSION['student_id'] ?? 'NA';
$class = $_SESSION['student_class'] ?? '';
$code = $_POST['code'] ?? '';
/* 🔥 TIME TRACKING */
$start_time = $_POST['start_time'] ?? round(microtime(true) * 1000);
$end_time = round(microtime(true) * 1000);
$time_taken = ($end_time - $start_time) / 1000; // seconds
$topic_id = $_POST['topic_id'] ?? 1;
if(strlen(trim($code)) < 20){
die("❌ Write proper logic (too short)");
}
/* 🔥 SMART CHECK */
$passed = false;
$score = 0;
if($topic_id == 1){
if(strpos($code, 'if') !== false && strpos($code, 'average') !== false){
$passed = true;
$score = 90;
} else {
$score = 40;
}
}
/* 🔥 THINKING TYPE LOGIC */
if($time_taken < 30){
$thinking_type = "⚡ Quick Thinker";
}
elseif($time_taken > 120){
$thinking_type = "🧠 Deep Thinker";
}
else{
$thinking_type = "🚶 Balanced Learner";
}
/* SAVE TO DB */
$status = $passed ? 'approved' : 'rejected';
$stmt = $pdo->prepare("
INSERT INTO project_submissions
(student_name, student_id, class, topic_id, score, status, thinking_type)
VALUES (?,?,?,?,?,?,?)
");
$stmt->execute([$name, $roll, $class, $topic_id, $score, $status, $thinking_type]);
/* RESULT UI */
if($passed){
echo "
<div style='text-align:center;margin-top:100px'>
<h2 style='color:#22c55e'> Project Approved 🎉</h2>
<h3 style='color:#facc15'>$thinking_type</h3>
<p style='color:#94a3b8'>
You solved this in ".round($time_taken)." seconds
</p>
<a href='/rs_lab/student/leaderboard.php'
style='display:inline-block;margin-top:20px;padding:12px 30px;
background:#facc15;color:#020617;border-radius:999px;
text-decoration:none;font-weight:bold'>
🏆 View Leaderboard
</a>
</div>
";
}else{
echo "
<div style='text-align:center;margin-top:100px'>
<h2 style='color:#f87171'> Improve your logic</h2>
<h3 style='color:#38bdf8'>$thinking_type</h3>
<p style='color:#94a3b8'>
Try again. You spent ".round($time_taken)." seconds
</p>
</div>
";
}
?>

View File

@ -0,0 +1,52 @@
<?php
include __DIR__ . '/../includes/header.php';
$track_id = $_GET['track_id'] ?? 0;
$trackNames = [
1 => "Logic & Problem Solving",
2 => "Python Foundations",
3 => "Applied Thinking"
];
$trackTitle = $trackNames[$track_id] ?? "Coding Challenges";
?>
<section class="section">
<div class="container">
<!-- Header -->
<div style="margin-bottom:28px;">
<h1 style="font-size:26px; font-weight:800; margin-bottom:6px;">
<?php echo htmlspecialchars($trackTitle); ?>
</h1>
<p style="color:var(--muted); max-width:720px;">
Complete the challenges in sequence to unlock participation
recognition and skill validation.
</p>
</div>
<!-- Challenges -->
<div class="grid">
<?php for ($i = 1; $i <= 5; $i++): ?>
<div class="card">
<h3>Challenge <?php echo $i; ?></h3>
<p>
This challenge focuses on strengthening core concepts
and applying logical reasoning step by step.
</p>
<div style="margin-top:14px;">
<button class="btn btn-outline">Start Challenge</button>
</div>
</div>
<?php endfor; ?>
</div>
</div>
</section>
<?php
include __DIR__ . '/../includes/footer.php';
?>

71
coding/tracks.php Normal file
View File

@ -0,0 +1,71 @@
<?php
include __DIR__ . '/../includes/header.php';
?>
<section class="section">
<div class="container">
<!-- Page Header -->
<div style="margin-bottom:28px;">
<h1 style="font-size:28px; font-weight:800; margin-bottom:6px;">
Coding Challenge Tracks
</h1>
<p style="color:var(--muted); max-width:720px;">
Professionally designed learning tracks to develop logical thinking,
programming fundamentals, and real-world problem-solving skills.
</p>
</div>
<!-- Tracks Grid -->
<div class="grid">
<!-- Track 1 -->
<div class="card feature-card">
<h3>Logic & Problem Solving</h3>
<p>
Strengthen reasoning skills using puzzles, patterns,
and step-by-step thinking exercises.
</p>
<div style="margin-top:16px;">
<a href="track_challenges.php?track_id=1" class="btn btn-primary">
View Challenges
</a>
</div>
</div>
<!-- Track 2 -->
<div class="card feature-card">
<h3>Python Foundations</h3>
<p>
Learn programming basics with beginner-friendly
challenges focused on logic and clarity.
</p>
<div style="margin-top:16px;">
<a href="track_challenges.php?track_id=2" class="btn btn-primary">
View Challenges
</a>
</div>
</div>
<!-- Track 3 -->
<div class="card feature-card">
<h3>Applied Thinking</h3>
<p>
Apply concepts to simple real-life problems
and scenario-based coding tasks.
</p>
<div style="margin-top:16px;">
<a href="track_challenges.php?track_id=3" class="btn btn-primary">
View Challenges
</a>
</div>
</div>
</div>
</div>
</section>
<?php
include __DIR__ . '/../includes/footer.php';
?>

View File

@ -1,46 +1,57 @@
<?php
session_start();
include 'includes/header.php';
require_once 'includes/pexels.php';
// competitions.php
// Ensure DB config loaded BEFORE using db()
require_once __DIR__ . '/db/config.php';
// Start session if not already (header will also try, but safe to ensure)
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// get PDO connection via helper
$pdo = db();
$stmt = $pdo->query('SELECT * FROM competitions WHERE deleted_at IS NULL ORDER BY start_date DESC');
$competitions = $stmt->fetchAll();
// fetch competitions - prepared statement, safe
try {
$stmt = $pdo->prepare("SELECT id, title, description, start_date, end_date, registration_link FROM competitions ORDER BY start_date DESC");
$stmt->execute();
$competitions = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
// If query fails, show friendly message (we enabled display_errors during debug)
die("Database query failed: " . htmlspecialchars($e->getMessage()));
}
// include header (will not start a new session because header uses safe check)
include __DIR__ . '/includes/header.php';
?>
<div class="container py-5">
<h1 class="display-4 fw-bold">Coding Competitions</h1>
<p class="fs-5 text-muted">Join our coding competitions and win exciting prizes.</p>
<h2>Competitions</h2>
<div class="row">
<?php if (count($competitions) > 0) : ?>
<?php foreach ($competitions as $competition) : ?>
<?php
$image_url = 'https://via.placeholder.com/600x400'; // Default placeholder
$query = preg_replace('/[^a-zA-Z0-9\s]/', '', $competition['title']);
$pexels_data = pexels_get('https://api.pexels.com/v1/search?query=' . urlencode($query) . '&per_page=1');
if ($pexels_data && !empty($pexels_data['photos'])) {
$image_url = $pexels_data['photos'][0]['src']['large'];
}
?>
<div class="col-md-4 mb-4">
<div class="card h-100">
<img src="<?php echo $image_url; ?>" class="card-img-top" alt="<?php echo htmlspecialchars($competition['title']); ?>">
<div class="card-body d-flex flex-column">
<h5 class="card-title"><a href="competition.php?id=<?php echo $competition['id']; ?>"><?php echo htmlspecialchars($competition['title']); ?></a></h5>
<p class="card-text flex-grow-1"><?php echo htmlspecialchars(substr($competition['description'], 0, 100)); ?>...</p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Starts: <?php echo date('M d, Y', strtotime($competition['start_date'])); ?></small>
<small class="text-muted">Ends: <?php echo date('M d, Y', strtotime($competition['end_date'])); ?></small>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="alert alert-info">No competitions available yet. Please check back later.</div>
<?php endif; ?>
</div>
</div>
<?php if (empty($competitions)): ?>
<p>No competitions right now. Check back later.</p>
<?php else: ?>
<table style="width:100%; border-collapse:collapse;" border="1" cellpadding="8">
<thead style="background:#0b8c9f; color:#fff;">
<tr>
<th style="text-align:left; width:30%;">Title</th>
<th style="text-align:left; width:40%;">Description</th>
<th style="text-align:center; width:15%;">Start</th>
<th style="text-align:center; width:15%;">End</th>
</tr>
</thead>
<tbody>
<?php foreach ($competitions as $c): ?>
<tr>
<td><?php echo htmlspecialchars($c['title']); ?></td>
<td><?php echo nl2br(htmlspecialchars($c['description'])); ?></td>
<td style="text-align:center;"><?php echo htmlspecialchars(date('Y-m-d', strtotime($c['start_date']))); ?></td>
<td style="text-align:center;"><?php echo htmlspecialchars(date('Y-m-d', strtotime($c['end_date']))); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php include 'includes/footer.php'; ?>
<?php
// optional footer close (if you want to keep layout)
echo "</div></body></html>";

5
composer.json Normal file
View File

@ -0,0 +1,5 @@
{
"require": {
"dompdf/dompdf": "^3.1"
}
}

307
composer.lock generated Normal file
View File

@ -0,0 +1,307 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "00da199c24f69d4e39903c3676bfe248",
"packages": [
{
"name": "dompdf/dompdf",
"version": "v3.1.4",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "db712c90c5b9868df3600e64e68da62e78a34623"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623",
"reference": "db712c90c5b9868df3600e64e68da62e78a34623",
"shasum": ""
},
"require": {
"dompdf/php-font-lib": "^1.0.0",
"dompdf/php-svg-lib": "^1.0.0",
"ext-dom": "*",
"ext-mbstring": "*",
"masterminds/html5": "^2.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"ext-gd": "*",
"ext-json": "*",
"ext-zip": "*",
"mockery/mockery": "^1.3",
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0"
},
"suggest": {
"ext-gd": "Needed to process images",
"ext-gmagick": "Improves image processing performance",
"ext-imagick": "Improves image processing performance",
"ext-zlib": "Needed for pdf stream compression"
},
"type": "library",
"autoload": {
"psr-4": {
"Dompdf\\": "src/"
},
"classmap": [
"lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "The Dompdf Community",
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
}
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v3.1.4"
},
"time": "2025-10-29T12:43:30+00:00"
},
{
"name": "dompdf/php-font-lib",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-font-lib.git",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"FontLib\\": "src/FontLib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "The FontLib Community",
"homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/dompdf/php-font-lib",
"support": {
"issues": "https://github.com/dompdf/php-font-lib/issues",
"source": "https://github.com/dompdf/php-font-lib/tree/1.0.1"
},
"time": "2024-12-02T14:37:59+00:00"
},
{
"name": "dompdf/php-svg-lib",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-svg-lib.git",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0",
"sabberworm/php-css-parser": "^8.4"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Svg\\": "src/Svg"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The SvgLib Community",
"homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/dompdf/php-svg-lib",
"support": {
"issues": "https://github.com/dompdf/php-svg-lib/issues",
"source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0"
},
"time": "2024-04-29T13:26:35+00:00"
},
{
"name": "masterminds/html5",
"version": "2.10.0",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
"reference": "fcf91eb64359852f00d921887b219479b4f21251"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251",
"reference": "fcf91eb64359852f00d921887b219479b4f21251",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Masterminds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Butcher",
"email": "technosophos@gmail.com"
},
{
"name": "Matt Farina",
"email": "matt@mattfarina.com"
},
{
"name": "Asmir Mustafic",
"email": "goetas@gmail.com"
}
],
"description": "An HTML5 parser and serializer.",
"homepage": "http://masterminds.github.io/html5-php",
"keywords": [
"HTML5",
"dom",
"html",
"parser",
"querypath",
"serializer",
"xml"
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
"source": "https://github.com/Masterminds/html5-php/tree/2.10.0"
},
"time": "2025-07-25T09:04:22+00:00"
},
{
"name": "sabberworm/php-css-parser",
"version": "v8.9.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9",
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41",
"rawr/cross-data-providers": "^2.0.0"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "9.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Sabberworm\\CSS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raphael Schweikert"
},
{
"name": "Oliver Klee",
"email": "github@oliverklee.de"
},
{
"name": "Jake Hotson",
"email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
"keywords": [
"css",
"parser",
"stylesheet"
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0"
},
"time": "2025-07-11T13:20:48+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.9.0"
}

156
computer_quiz.php Normal file
View File

@ -0,0 +1,156 @@
<?php
// computer_quiz.php
session_start();
require_once __DIR__ . '/config.php';
// 1⃣ Find active quiz
$quiz = null;
$result = $conn->query(
"SELECT * FROM computer_quizzes
WHERE is_active = 1
ORDER BY created_at DESC
LIMIT 1"
);
if ($result && $result->num_rows > 0) {
$quiz = $result->fetch_assoc();
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!$quiz) {
$error = "Quiz not started yet.";
} else {
$name = trim($_POST['student_name']);
$class = trim($_POST['class']);
$roll = trim($_POST['roll_number']);
if ($name === '' || $class === '' || $roll === '') {
$error = "All fields are required.";
} else {
// Save attempt
$stmt = $conn->prepare(
"INSERT INTO computer_quiz_attempts
(quiz_id, student_name, class, roll_number)
VALUES (?, ?, ?, ?)"
);
$stmt->bind_param(
"isss",
$quiz['id'],
$name,
$class,
$roll
);
$stmt->execute();
$attempt_id = $stmt->insert_id;
$stmt->close();
// Store attempt in session
$_SESSION['quiz_attempt_id'] = $attempt_id;
$_SESSION['quiz_id'] = $quiz['id'];
header("Location: computer_quiz_questions.php");
exit;
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Computer Quiz | RS Learning Lab</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;font-family:'Inter',system-ui}
body{
margin:0;
background:#0b1220;
color:#e5e7eb;
display:flex;
align-items:center;
justify-content:center;
min-height:100vh;
}
.card{
width:100%;
max-width:420px;
background:#0f172a;
border:1px solid #1e293b;
border-radius:18px;
padding:32px;
}
h1{margin-top:0;font-size:22px}
p{color:#94a3b8}
label{display:block;margin-top:18px}
input{
width:100%;
margin-top:6px;
padding:14px;
border-radius:12px;
border:1px solid #1e293b;
background:#020617;
color:#e5e7eb;
}
button{
margin-top:26px;
width:100%;
padding:14px;
border-radius:14px;
border:0;
background:#22d3ee;
color:#001018;
font-weight:700;
cursor:pointer;
}
.error{
margin-top:14px;
color:#fca5a5;
font-size:14px;
}
</style>
</head>
<body>
<div class="card">
<h1>Learning Style Quiz</h1>
<?php if ($quiz): ?>
<p>Please enter your details to begin.</p>
<?php else: ?>
<p>No active quiz at the moment.</p>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<?php if ($quiz): ?>
<form method="post">
<label>Student Name</label>
<input name="student_name" required>
<label>Class</label>
<input name="class" required>
<label>Roll Number</label>
<input name="roll_number" required>
<button type="submit">Start Quiz</button>
</form>
<?php endif; ?>
</div>
</body>
</html>

147
computer_quiz_questions.php Normal file
View File

@ -0,0 +1,147 @@
<?php
// computer_quiz_questions.php
session_start();
require_once __DIR__ . '/config.php';
// 🔐 Guard: must come from entry page
if (!isset($_SESSION['quiz_attempt_id']) || !isset($_SESSION['quiz_id'])) {
header("Location: computer_quiz.php");
exit;
}
$attemptId = $_SESSION['quiz_attempt_id'];
// 🔹 20 FIXED QUESTIONS (Learning-style based)
$questions = [
1 => "When learning something new, what helps you most?",
2 => "If you dont understand a topic, what do you do first?",
3 => "You remember things best when you…",
4 => "In exams, you usually prefer questions that are…",
5 => "While studying, you are more comfortable with…",
6 => "When solving problems, you usually…",
7 => "You feel confident when learning involves…",
8 => "During group study, you usually…",
9 => "You understand faster when lessons include…",
10 => "When instructions are given, you prefer them to be…",
11 => "You feel bored quickly if learning is…",
12 => "You prefer teachers who…",
13 => "While revising, you usually…",
14 => "You feel learning is effective when it is…",
15 => "When facing a difficult question, you…",
16 => "You like assignments that involve…",
17 => "You stay focused when tasks are…",
18 => "You learn better from mistakes when…",
19 => "You feel confident if learning materials are…",
20 => "Overall, you feel learning is best when it is…"
];
// Options (same pattern for all)
$options = [
'A' => 'Using diagrams, images or videos',
'B' => 'Understanding logic and steps',
'C' => 'Practicing with real examples',
'D' => 'Thinking deeply and quietly'
];
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
foreach ($questions as $qNo => $qText) {
if (!isset($_POST['q'][$qNo])) {
$error = "Please answer all questions.";
break;
}
}
if ($error === '') {
foreach ($_POST['q'] as $qNo => $answer) {
$stmt = $conn->prepare(
"INSERT INTO computer_quiz_answers
(attempt_id, question_no, selected_option)
VALUES (?, ?, ?)"
);
$stmt->bind_param("iis", $attemptId, $qNo, $answer);
$stmt->execute();
$stmt->close();
}
header("Location: generate_learning_pdf.php");
exit;
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Learning Style Quiz</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;font-family:'Inter',system-ui}
body{margin:0;background:#0b1220;color:#e5e7eb}
.container{max-width:860px;margin:40px auto;padding:0 20px}
h1{text-align:center;margin-bottom:26px}
.question{
background:#0f172a;
border:1px solid #1e293b;
border-radius:16px;
padding:22px;
margin-bottom:18px;
}
.question h3{margin-top:0;font-size:16px}
.option{margin-top:10px}
.option label{cursor:pointer}
button{
margin-top:28px;
width:100%;
padding:16px;
border-radius:16px;
border:0;
background:#22d3ee;
color:#001018;
font-weight:700;
font-size:16px;
cursor:pointer;
}
.error{text-align:center;color:#fca5a5;margin-bottom:14px}
</style>
</head>
<body>
<div class="container">
<h1>Learning Style Assessment</h1>
<?php if ($error): ?>
<div class="error"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="post">
<?php foreach ($questions as $no => $text): ?>
<div class="question">
<h3><?= $no ?>. <?= htmlspecialchars($text) ?></h3>
<?php foreach ($options as $key => $label): ?>
<div class="option">
<label>
<input type="radio" name="q[<?= $no ?>]" value="<?= $key ?>" required>
<?= $key ?>. <?= htmlspecialchars($label) ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
<button type="submit">Submit Quiz</button>
</form>
</div>
</body>
</html>

19
config.php Normal file
View File

@ -0,0 +1,19 @@
<?php
// RS Learning Lab - single DB config
$host = "localhost";
$user = "root";
$pass = ""; // XAMPP default empty password
$db = "rs_lab"; // IMPORTANT: your phpMyAdmin DB name
// Create connection
$conn = new mysqli($host, $user, $pass, $db);
// Check connection
if ($conn->connect_error) {
die("DB Connection Failed: " . $conn->connect_error);
}
// Optional: show errors while developing
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
?>

19
config/db.php Normal file
View File

@ -0,0 +1,19 @@
<?php
$host = "localhost";
$db = "rs_lab";
$user = "root";
$pass = "";
try {
$pdo = new PDO(
"mysql:host=$host;dbname=$db;charset=utf8mb4",
$user,
$pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
die("Database connection failed: " . $e->getMessage());
}

29
dashboard.php Normal file
View File

@ -0,0 +1,29 @@
<?php
session_start();
if (!isset($_SESSION['role'])) {
echo "Unauthorized access";
exit;
}
switch ($_SESSION['role']) {
case 'student':
header("Location: /rs_lab/student/student_dashboard.php");
break;
case 'teacher':
header("Location: /rs_lab/teacher/dashboard.php");
break;
case 'school':
header("Location: /rs_lab/school/dashboard.php");
break;
case 'institution':
header("Location: /rs_lab/institution/dashboard.php");
break;
default:
echo "Invalid role";
}
exit;

View File

@ -0,0 +1,292 @@
<?php
// dashboard_learning_passport.php
// RS Learning Lab Student Dashboard with Momentum Card
/* ===============================
DB CONFIG
================================ */
$db_host = "localhost";
$db_name = "rs_lab";
$db_user = "root";
$db_pass = "";
try {
$pdo = new PDO(
"mysql:host=$db_host;dbname=$db_name;charset=utf8mb4",
$db_user,
$db_pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
die("DB connection failed");
}
/* ===============================
INPUT
================================ */
$roll_no = $_GET['roll'] ?? null;
if (!$roll_no) {
die("Student not specified");
}
/* ===============================
FETCH LEARNING PROFILE
================================ */
$stmt = $pdo->prepare("
SELECT *
FROM learning_profiles
WHERE roll_no = :roll
LIMIT 1
");
$stmt->execute([':roll' => $roll_no]);
$profile = $stmt->fetch();
if (!$profile) {
die("Learning profile not found");
}
$style_scores = json_decode($profile['style_scores'], true) ?? [];
$learning_signals = json_decode($profile['learning_signals'], true) ?? [];
/* ===============================
FETCH MOMENTUM
================================ */
$stmt = $pdo->prepare("
SELECT momentum_score, momentum_state, updated_at
FROM learning_momentum
WHERE roll_no = :roll
LIMIT 1
");
$stmt->execute([':roll' => $roll_no]);
$momentum = $stmt->fetch();
$momentum_score = $momentum['momentum_score'] ?? 50;
$momentum_state = $momentum['momentum_state'] ?? "Building";
$momentum_updated = $momentum['updated_at'] ?? null;
/* ===============================
MOMENTUM UI MAPPING
================================ */
$momentum_color = [
"Applying" => "#22c55e",
"Stabilizing" => "#38bdf8",
"Building" => "#facc15",
"Reset" => "#ef4444"
];
$momentum_text = [
"Applying" => "Applying concepts confidently",
"Stabilizing" => "Understanding is settling steadily",
"Building" => "Learning habit is forming",
"Reset" => "Needs calm practice reinforcement"
];
$bar_color = $momentum_color[$momentum_state] ?? "#38bdf8";
?>
<!DOCTYPE html>
<html>
<head>
<title>Student Dashboard RS Learning Lab</title>
<style>
body {
background:#0b1020;
color:#e5e7eb;
font-family:Arial, sans-serif;
padding:30px;
}
.container {
max-width:1100px;
margin:auto;
}
h1 {
color:#38bdf8;
}
.grid {
display:grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap:20px;
}
.card {
background:#111827;
padding:22px;
border-radius:14px;
}
.label {
color:#93c5fd;
font-size:14px;
}
.value {
font-size:16px;
margin-bottom:8px;
}
.badge {
display:inline-block;
padding:6px 14px;
border-radius:20px;
background:#0b8c9f;
color:#fff;
font-size:14px;
}
.badge.secondary {
background:#334155;
}
.badge.lei {
background:#022c22;
color:#6ee7b7;
}
.progress {
height:10px;
background:#020617;
border-radius:8px;
overflow:hidden;
margin-top:10px;
}
.progress-fill {
height:100%;
}
ul {
padding-left:20px;
}
.footer-note {
margin-top:30px;
font-size:13px;
color:#94a3b8;
}
.btn {
display:inline-block;
background:#0ea5e9;
padding:10px 16px;
border-radius:8px;
color:#fff;
text-decoration:none;
font-size:14px;
margin-top:15px;
}
</style>
</head>
<body>
<div class="container">
<h1>Student Learning Dashboard</h1>
<div class="grid">
<!-- STUDENT INFO -->
<div class="card">
<p class="label">Student Name</p>
<p class="value"><?php echo htmlspecialchars($profile['student_name']); ?></p>
<p class="label">Roll No</p>
<p class="value"><?php echo htmlspecialchars($profile['roll_no']); ?></p>
<p class="label">Class</p>
<p class="value"><?php echo htmlspecialchars($profile['class']); ?></p>
</div>
<!-- LEARNING STYLE -->
<div class="card">
<h3>Learning Style</h3>
<span class="badge"><?php echo $profile['primary_style']; ?></span>
<span class="badge secondary"><?php echo $profile['secondary_style']; ?></span>
</div>
<!-- LEI -->
<div class="card">
<h3>Learning Effectiveness Index</h3>
<span class="badge lei"><?php echo $profile['lei_signal']; ?></span>
<p class="footer-note">
LEI reflects effort, improvement, and consistency.
It is not a score or rank.
</p>
</div>
<!-- 🚀 MOMENTUM CARD (NEW) -->
<div class="card">
<h3>Learning Momentum</h3>
<p>
<strong><?php echo $momentum_state; ?></strong><br>
<span class="label"><?php echo $momentum_text[$momentum_state]; ?></span>
</p>
<div class="progress">
<div class="progress-fill"
style="width: <?php echo $momentum_score; ?>%;
background: <?php echo $bar_color; ?>;">
</div>
</div>
<p class="footer-note">
Momentum shows persistence and recovery during learning.
<?php if ($momentum_updated): ?>
<br>Last updated: <?php echo date("d M Y, h:i A", strtotime($momentum_updated)); ?>
<?php endif; ?>
</p>
<a class="btn"
href="momentum_timeline.php?roll=<?php echo urlencode($roll_no); ?>&name=<?php echo urlencode($profile['student_name']); ?>">
View Momentum Timeline
</a>
</div>
</div>
<!-- LEARNING SIGNALS -->
<div class="card" style="margin-top:20px;">
<h3>Learning Signals</h3>
<ul>
<?php foreach ($learning_signals as $signal): ?>
<li><?php echo htmlspecialchars($signal); ?></li>
<?php endforeach; ?>
</ul>
</div>
<!-- STYLE PATTERN -->
<div class="card" style="margin-top:20px;">
<h3>Learning Pattern Overview</h3>
<div class="grid">
<?php foreach ($style_scores as $style => $score): ?>
<div class="card">
<strong><?php echo $style; ?></strong><br>
<span><?php echo $score; ?> signal units</span>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- PASSPORT -->
<div class="card" style="margin-top:20px;">
<h3>Learning Passport</h3>
<p class="footer-note">
This passport evolves as the student practices, reinforces,
and applies learning over time.
</p>
<a class="btn"
href="learning_passport.php?
name=<?php echo urlencode($profile['student_name']); ?>&
roll=<?php echo urlencode($profile['roll_no']); ?>&
class=<?php echo urlencode($profile['class']); ?>&
primary=<?php echo urlencode($profile['primary_style']); ?>&
secondary=<?php echo urlencode($profile['secondary_style']); ?>&
lei=<?php echo urlencode($profile['lei_signal']); ?>&
signals=<?php echo urlencode(implode(',', $learning_signals)); ?>">
View Learning Passport PDF
</a>
</div>
<div class="footer-note">
© 2026 RS Learning Lab · Learning · Momentum · Growth
</div>
</div>
</body>
</html>

View File

@ -1,17 +1,20 @@
<?php
// Generated by setup_mariadb_project.sh — edit as needed.
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'app_36459');
define('DB_USER', 'app_36459');
define('DB_PASS', 'dcb62e1f-58c2-4988-8f8c-4094d3b743c0');
// RS Learning Lab - basic DB config
// NOTE: change db_name, username, password based on your XAMPP setup
function db() {
static $pdo;
if (!$pdo) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
return $pdo;
$host = 'localhost';
$db_name = 'rs_lab'; // create this DB in phpMyAdmin
$db_user = 'root'; // default XAMPP user
$db_pass = ''; // default XAMPP password (empty)
// Create connection
$conn = new mysqli($host, $db_user, $db_pass, $db_name);
// Check connection
if ($conn->connect_error) {
die("Database connection failed: " . $conn->connect_error);
}
// Set charset
$conn->set_charset("utf8mb4");
?>

17
db/config.php.bak Normal file
View File

@ -0,0 +1,17 @@
<?php
// Generated by setup_mariadb_project.sh — edit as needed.
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'app_36459');
define('DB_USER', 'app_36459');
define('DB_PASS', 'dcb62e1f-58c2-4988-8f8c-4094d3b743c0');
function db() {
static $pdo;
if (!$pdo) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
return $pdo;
}

43
db/config.php.bak.txt Normal file
View File

@ -0,0 +1,43 @@
<?php
// db/config.php
// RS Learning Lab — database configuration for XAMPP (MySQL root, blank password)
// Database connection settings
$DB_HOST = 'localhost';
$DB_USER = 'root';
$DB_PASS = '';
$DB_NAME = 'rs_lab';
$DB_PORT = 3306; // usually 3306
// Create MySQLi connection and check
$mysqli = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME, $DB_PORT);
// Check connection
if ($mysqli->connect_errno) {
// If you enabled display_errors (see below) you'll see this on the page.
error_log("MySQL connection failed: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error);
die("Database connection failed. Please check config.php settings.");
}
// Set charset to UTF-8
$mysqli->set_charset("utf8mb4");
// Optional: a PDO connection if any part of app expects PDO (safe to have both)
try {
$dsn = "mysql:host={$DB_HOST};dbname={$DB_NAME};port={$DB_PORT};charset=utf8mb4";
$pdo = new PDO($dsn, $DB_USER, $DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
error_log("PDO connection failed: " . $e->getMessage());
// don't reveal raw error to users in production
// but during debugging it's okay to show:
// die("PDO DB connection failed: " . $e->getMessage());
}
// If your app expects a single $db or $conn variable, create alias:
$db = $mysqli; // mysqli object
$conn = $mysqli; // some files may use $conn
// End of config.php

6
db/students.csv Normal file
View File

@ -0,0 +1,6 @@
roll_number,student_name
2173,Gokul Krishnan
2174,Arun Kumar
2175,Karthik Raja
2176,Manoj Kumar
2177,Suresh Babu
1 roll_number student_name
2 2173 Gokul Krishnan
3 2174 Arun Kumar
4 2175 Karthik Raja
5 2176 Manoj Kumar
6 2177 Suresh Babu

21
db_config.php Normal file
View File

@ -0,0 +1,21 @@
<?php
// db_config.php Common DB connection for RS Learning Lab
$db_host = "localhost";
$db_name = "rs_lab"; // ✅ CONFIRMED DB NAME
$db_user = "root";
$db_pass = "";
try {
$pdo = new PDO(
"mysql:host=$db_host;dbname=$db_name;charset=utf8mb4",
$db_user,
$db_pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
die("DB connection failed");
}

View File

@ -0,0 +1,137 @@
<?php
// db_save_learning_profile.php
// RS Learning Lab Save Learning Profile (Production Ready)
// ------------------------------------
// DB CONFIG (CHANGE AS PER YOUR SETUP)
// ------------------------------------
$db_host = "localhost";
$db_name = "rs_learning_lab";
$db_user = "root";
$db_pass = "";
// ------------------------------------
// CONNECT TO DATABASE (PDO)
// ------------------------------------
try {
$pdo = new PDO(
"mysql:host=$db_host;dbname=$db_name;charset=utf8mb4",
$db_user,
$db_pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode([
"status" => "error",
"message" => "Database connection failed"
]);
exit;
}
// ------------------------------------
// INPUT DATA (POST / SESSION / API)
// ------------------------------------
$student_name = $_POST['name'] ?? null;
$roll_no = $_POST['roll'] ?? null;
$class = $_POST['class'] ?? null;
$primary_style = $_POST['primary'] ?? null;
$secondary_style = $_POST['secondary'] ?? null;
$lei_signal = $_POST['lei'] ?? "Baseline";
// Scores JSON (internal, no marks)
$scores_json = $_POST['scores'] ?? null;
// Signals array → JSON
$signals = $_POST['signals'] ?? [];
$signals_json = json_encode($signals);
// ------------------------------------
// BASIC VALIDATION
// ------------------------------------
if (
!$student_name ||
!$roll_no ||
!$primary_style ||
!$scores_json
) {
http_response_code(400);
echo json_encode([
"status" => "error",
"message" => "Missing required learning profile data"
]);
exit;
}
// ------------------------------------
// INSERT OR UPDATE LOGIC
// ------------------------------------
// One active learning profile per student
$sql = "
INSERT INTO learning_profiles
(
student_name,
roll_no,
class,
primary_style,
secondary_style,
lei_signal,
style_scores,
learning_signals,
created_at
)
VALUES
(
:student_name,
:roll_no,
:class,
:primary_style,
:secondary_style,
:lei_signal,
:style_scores,
:learning_signals,
NOW()
)
ON DUPLICATE KEY UPDATE
primary_style = VALUES(primary_style),
secondary_style = VALUES(secondary_style),
lei_signal = VALUES(lei_signal),
style_scores = VALUES(style_scores),
learning_signals = VALUES(learning_signals),
updated_at = NOW()
";
$stmt = $pdo->prepare($sql);
// ------------------------------------
// EXECUTE
// ------------------------------------
try {
$stmt->execute([
':student_name' => $student_name,
':roll_no' => $roll_no,
':class' => $class,
':primary_style' => $primary_style,
':secondary_style' => $secondary_style,
':lei_signal' => $lei_signal,
':style_scores' => $scores_json,
':learning_signals'=> $signals_json
]);
echo json_encode([
"status" => "success",
"message" => "Learning profile saved successfully"
]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode([
"status" => "error",
"message" => "Failed to save learning profile"
]);
}

View File

@ -0,0 +1,17 @@
<?php
$conn = new mysqli("localhost", "root", "", "rs_lab");
if ($conn->connect_error) {
die("DB Connection failed");
}
if (isset($_GET['id'])) {
$id = intval($_GET['id']);
$sql = "UPDATE institutions SET status='inactive' WHERE id=$id";
$conn->query($sql);
}
$conn->close();
header("Location: admin_dashboard.php");
exit();

9
delete_institution.php Normal file
View File

@ -0,0 +1,9 @@
<?php
$conn = new mysqli("localhost","root","","rs_lab");
$id = $_POST['id'];
$conn->query("UPDATE institutions SET deleted_at = NOW() WHERE id=$id");
header("Location: admin_dashboard.php");
exit;

57
export_teacher_excel.php Normal file
View File

@ -0,0 +1,57 @@
<?php
// export_teacher_excel.php
// RS Learning Lab Teacher Dashboard Excel Export
$db_host = "localhost";
$db_name = "rs_learning_lab";
$db_user = "root";
$db_pass = "";
$class = $_GET['class'] ?? '10-A';
try {
$pdo = new PDO(
"mysql:host=$db_host;dbname=$db_name;charset=utf8mb4",
$db_user,
$db_pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
die("DB connection failed");
}
$stmt = $pdo->prepare("
SELECT
lp.roll_no,
lp.student_name,
lp.class,
lm.momentum_state,
lm.momentum_score,
lm.updated_at
FROM learning_profiles lp
LEFT JOIN learning_momentum lm
ON lp.roll_no = lm.roll_no
WHERE lp.class = :class
ORDER BY lp.roll_no ASC
");
$stmt->execute([':class' => $class]);
$data = $stmt->fetchAll();
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=Class_{$class}_Momentum_Report.xls");
echo "Roll No\tStudent Name\tClass\tMomentum State\tMomentum Score\tLast Activity\n";
foreach ($data as $row) {
echo
$row['roll_no'] . "\t" .
$row['student_name'] . "\t" .
$row['class'] . "\t" .
($row['momentum_state'] ?? 'Building') . "\t" .
($row['momentum_score'] ?? 50) . "\t" .
($row['updated_at'] ?? '-') . "\n";
}
exit;

76
export_teacher_pdf.php Normal file
View File

@ -0,0 +1,76 @@
<?php
// export_teacher_pdf.php
// RS Learning Lab Teacher Dashboard PDF Export (wkhtmltopdf)
$db_host = "localhost";
$db_name = "rs_learning_lab";
$db_user = "root";
$db_pass = "";
$class = $_GET['class'] ?? '10-A';
try {
$pdo = new PDO(
"mysql:host=$db_host;dbname=$db_name;charset=utf8mb4",
$db_user,
$db_pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
die("DB connection failed");
}
$stmt = $pdo->prepare("
SELECT
lp.roll_no,
lp.student_name,
lm.momentum_state,
lm.momentum_score
FROM learning_profiles lp
LEFT JOIN learning_momentum lm
ON lp.roll_no = lm.roll_no
WHERE lp.class = :class
ORDER BY lp.roll_no ASC
");
$stmt->execute([':class' => $class]);
$students = $stmt->fetchAll();
$html = "
<h2>RS Learning Lab Class Momentum Report</h2>
<p>Class: {$class}</p>
<table border='1' cellpadding='8' cellspacing='0' width='100%'>
<tr>
<th>Roll No</th>
<th>Student Name</th>
<th>Momentum State</th>
<th>Momentum Score</th>
</tr>
";
foreach ($students as $s) {
$html .= "
<tr>
<td>{$s['roll_no']}</td>
<td>{$s['student_name']}</td>
<td>".($s['momentum_state'] ?? 'Building')."</td>
<td>".($s['momentum_score'] ?? 50)."</td>
</tr>
";
}
$html .= "</table>";
$tmp = __DIR__ . "/teacher_report.html";
$pdf = __DIR__ . "/Class_{$class}_Momentum_Report.pdf";
file_put_contents($tmp, $html);
shell_exec("wkhtmltopdf \"$tmp\" \"$pdf\"");
header("Content-Type: application/pdf");
header("Content-Disposition: inline; filename=Class_Momentum_Report.pdf");
readfile($pdf);
exit;

170
facilitated_assessment.php Normal file
View File

@ -0,0 +1,170 @@
<?php
session_start();
require_once __DIR__ . "/config/db.php"; // 🔥 ADDED
$csvFile = __DIR__ . "/uploads/student_list.csv";
$students = [];
$teacher = $_SESSION["teacher_name"] ?? "";
$class = $_SESSION["class"] ?? "";
$section = $_SESSION["section"] ?? "";
/* 🔥 FETCH PDF STATUS FROM DB */
$stmt = $pdo->prepare("SELECT student_roll, pdf_path FROM learning_style_results");
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
$pdfMap = [];
foreach ($results as $row) {
$pdfMap[$row['student_roll']] = $row['pdf_path'];
}
if (file_exists($csvFile)) {
if (($handle = fopen($csvFile, "r")) !== FALSE) {
$header = fgetcsv($handle);
while (($row = fgetcsv($handle)) !== FALSE) {
$students[] = [
"roll" => $row[0],
"name" => $row[1]
];
}
fclose($handle);
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Assessment Session | RS Learning Lab</title>
<style>
body{
margin:0;
background:radial-gradient(circle at top,#0f172a,#020617);
font-family:Segoe UI,sans-serif;
color:#e5e7eb;
}
.wrap{
max-width:1100px;
margin:60px auto;
padding:20px;
}
h1{
margin-bottom:6px;
}
.sub{
opacity:.7;
margin-bottom:30px;
}
table{
width:100%;
border-collapse:collapse;
background:#020617;
border-radius:14px;
overflow:hidden;
}
th,td{
padding:16px;
border-bottom:1px solid #1f2937;
}
th{
text-align:left;
color:#9ca3af;
font-size:14px;
}
tr:hover{
background:#020617;
}
.btn{
padding:8px 14px;
background:#0ea5e9;
color:#000;
font-size:13px;
border-radius:8px;
text-decoration:none;
font-weight:600;
}
.btn:hover{
background:#38bdf8;
}
.success{
color:#22c55e;
font-weight:bold;
}
.empty{
opacity:.6;
padding:30px;
text-align:center;
}
</style>
</head>
<body>
<div class="wrap">
<h1>📋 Assessment Session</h1>
<div class="sub">
Class <?= htmlspecialchars($class) ?> ·
Section <?= htmlspecialchars($section) ?> ·
Facilitator: <?= htmlspecialchars($teacher) ?>
</div>
<table>
<thead>
<tr>
<th width="15%">Roll No</th>
<th>Student Name</th>
<th width="25%">Action</th>
</tr>
</thead>
<tbody>
<?php if (count($students) > 0): ?>
<?php foreach ($students as $s): ?>
<tr>
<td><?= htmlspecialchars($s["roll"]) ?></td>
<td><?= htmlspecialchars($s["name"]) ?></td>
<td>
<?php if(isset($pdfMap[$s['roll']]) && !empty($pdfMap[$s['roll']])): ?>
<span class="success"> PDF Generated</span><br><br>
<a href="<?= $pdfMap[$s['roll']] ?>" target="_blank" class="btn">
View PDF
</a>
<?php else: ?>
<a href="omr_test.php?roll=<?= urlencode($s['roll']) ?>&name=<?= urlencode($s['name']) ?>" class="btn">
Scan OMR
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="3" class="empty">
No student list found. Please upload CSV to begin.
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<br><br>
<a href="bulk_download.php" class="btn">
Download All Reports
</a>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More