Auto commit: 2026-01-30T14:32:19.673Z
This commit is contained in:
parent
e29ef2dff4
commit
ef3a1cc12c
97
admin.php
97
admin.php
@ -1,31 +1,43 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
// Auth Check
|
||||
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'Admin') {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$current_role = 'Admin';
|
||||
$pageTitle = 'Admin Dashboard | Township Schools Platform';
|
||||
$school_id = $_SESSION['school_id'];
|
||||
$pageTitle = 'Admin Dashboard | SOMS';
|
||||
|
||||
// Fetch Some Stats
|
||||
$total_learners = $db->query("SELECT COUNT(*) FROM learners")->fetchColumn();
|
||||
// Fetch Some Stats for this school
|
||||
$total_learners_stmt = $db->prepare("SELECT COUNT(*) FROM learners WHERE school_id = ?");
|
||||
$total_learners_stmt->execute([$school_id]);
|
||||
$total_learners = $total_learners_stmt->fetchColumn();
|
||||
|
||||
// Attendance for today
|
||||
$today = date('Y-m-d');
|
||||
$present_today = $db->prepare("SELECT COUNT(*) FROM attendance WHERE date = ? AND status = 'present'");
|
||||
$present_today->execute([$today]);
|
||||
$present_today = $db->prepare("SELECT COUNT(*) FROM attendance WHERE date = ? AND status = 'present' AND school_id = ?");
|
||||
$present_today->execute([$today, $school_id]);
|
||||
$present_today_count = $present_today->fetchColumn();
|
||||
|
||||
$presence_rate = $total_learners > 0 ? round(($present_today_count / $total_learners) * 100) : 0;
|
||||
|
||||
// Analytics: Attendance by Grade
|
||||
$grade_stats = $db->query("
|
||||
$grade_stats = $db->prepare("
|
||||
SELECT l.grade,
|
||||
COUNT(l.id) as total,
|
||||
SUM(CASE WHEN a.status = 'present' THEN 1 ELSE 0 END) as present
|
||||
FROM learners l
|
||||
LEFT JOIN attendance a ON l.id = a.learner_id AND a.date = '$today'
|
||||
LEFT JOIN attendance a ON l.id = a.learner_id AND a.date = :today
|
||||
WHERE l.school_id = :school_id
|
||||
GROUP BY l.grade
|
||||
ORDER BY l.grade
|
||||
")->fetchAll();
|
||||
");
|
||||
$grade_stats->execute(['today' => $today, 'school_id' => $school_id]);
|
||||
$grade_stats = $grade_stats->fetchAll();
|
||||
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
@ -34,45 +46,48 @@ include 'includes/header.php';
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h2 class="h4 mb-1">School Admin Dashboard</h2>
|
||||
<p class="text-muted small">Overview of school operations</p>
|
||||
<p class="text-muted small">Overview of operations for <strong>School ID: <?= $school_id ?></strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Row -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card">
|
||||
<p>Total Learners</p>
|
||||
<h3><?= $total_learners ?></h3>
|
||||
<div class="card p-3 stats-card shadow-sm">
|
||||
<p class="text-muted mb-1">Total Learners</p>
|
||||
<h3 class="mb-0"><?= $total_learners ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card" style="border-left-color: #2E7D32;">
|
||||
<p>Today's Presence</p>
|
||||
<h3><?= $presence_rate ?>%</h3>
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid #2E7D32;">
|
||||
<p class="text-muted mb-1">Today's Presence</p>
|
||||
<h3 class="mb-0"><?= $presence_rate ?>%</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card" style="border-left-color: var(--secondary-color);">
|
||||
<p>Total Staff</p>
|
||||
<h3>12</h3>
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid var(--secondary-color);">
|
||||
<p class="text-muted mb-1">Total Staff</p>
|
||||
<h3 class="mb-0">...</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card" style="border-left-color: #0288D1;">
|
||||
<p>SGB Meetings</p>
|
||||
<h3>1</h3>
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid #0288D1;">
|
||||
<p class="text-muted mb-1">Pending Tasks</p>
|
||||
<h3 class="mb-0">3</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card mb-4">
|
||||
<div class="card mb-4 shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0">Attendance Analytics by Grade (Today)</h5>
|
||||
<h5 class="mb-0 fw-bold">Attendance Analytics by Grade (Today)</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($grade_stats)): ?>
|
||||
<p class="text-center text-muted">No attendance data recorded today.</p>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($grade_stats as $stat): ?>
|
||||
<?php
|
||||
$rate = $stat['total'] > 0 ? round(($stat['present'] / $stat['total']) * 100) : 0;
|
||||
@ -80,8 +95,8 @@ include 'includes/header.php';
|
||||
?>
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span class="small fw-semibold"><?= htmlspecialchars($stat['grade']) ?></span>
|
||||
<span class="small text-muted"><?= $rate ?>% (<?= $stat['present'] ?>/<?= $stat['total'] ?>)</span>
|
||||
<span class="small fw-semibold">Grade <?= htmlspecialchars($stat['grade']) ?></span>
|
||||
<span class="small text-muted"><?= $rate ?>% (<?= (int)$stat['present'] ?>/<?= $stat['total'] ?>)</span>
|
||||
</div>
|
||||
<div class="progress" style="height: 8px;">
|
||||
<div class="progress-bar <?= $bar_color ?>" role="progressbar" style="width: <?= $rate ?>%"></div>
|
||||
@ -91,28 +106,19 @@ include 'includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0">Recent Activity</h5>
|
||||
<h5 class="mb-0 fw-bold">Recent Activity</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item px-0">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h6 class="mb-0">Daily Register Completed</h6>
|
||||
<small class="text-muted">Grade 10A - Mrs. Mdluli</small>
|
||||
<h6 class="mb-0 small fw-bold">System Online</h6>
|
||||
<small class="text-muted">School instance successfully initialized.</small>
|
||||
</div>
|
||||
<span class="text-muted small">10 mins ago</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item px-0">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h6 class="mb-0">New Learner Registered</h6>
|
||||
<small class="text-muted">Sipho Zulu - Grade 8B</small>
|
||||
</div>
|
||||
<span class="text-muted small">1 hour ago</span>
|
||||
<span class="text-muted small">Just now</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@ -120,14 +126,14 @@ include 'includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100">
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0">Quick Actions</h5>
|
||||
<h5 class="mb-0 fw-bold">Quick Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="learners.php" class="btn btn-outline-primary text-start">
|
||||
<i class="bi bi-person-plus me-2"></i> Register New Learner
|
||||
<i class="bi bi-person-plus me-2"></i> Manage Learners
|
||||
</a>
|
||||
<button class="btn btn-outline-primary text-start">
|
||||
<i class="bi bi-file-earmark-bar-graph me-2"></i> Generate Reports
|
||||
@ -135,9 +141,6 @@ include 'includes/header.php';
|
||||
<button class="btn btn-outline-primary text-start">
|
||||
<i class="bi bi-megaphone me-2"></i> Send Parent Notice
|
||||
</a>
|
||||
<button class="btn btn-outline-primary text-start">
|
||||
<i class="bi bi-calendar-event me-2"></i> Schedule Meeting
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -145,4 +148,4 @@ include 'includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
|
||||
250
assessments.php
Normal file
250
assessments.php
Normal file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
// Auth Check
|
||||
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'Teacher') {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$school_id = $_SESSION['school_id'];
|
||||
$pageTitle = 'Teacher Assessments | SOMS';
|
||||
$message = '';
|
||||
|
||||
// Handle Assessment Creation
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
if ($_POST['action'] === 'create') {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO assessments (name, type, total_marks, grade, school_id) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$_POST['name'],
|
||||
$_POST['type'],
|
||||
$_POST['total_marks'],
|
||||
$_POST['grade'],
|
||||
$school_id
|
||||
]);
|
||||
$message = "Assessment created successfully.";
|
||||
} catch (Exception $e) {
|
||||
$message = "Error: " . $e->getMessage();
|
||||
}
|
||||
} elseif ($_POST['action'] === 'save_marks') {
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
$assessment_id = $_POST['assessment_id'];
|
||||
$stmt = $db->prepare("INSERT INTO marks (assessment_id, learner_id, marks_obtained)
|
||||
VALUES (:aid, :lid, :marks)
|
||||
ON DUPLICATE KEY UPDATE marks_obtained = VALUES(marks_obtained)");
|
||||
|
||||
foreach ($_POST['marks'] as $learner_id => $marks) {
|
||||
if ($marks !== '') {
|
||||
$stmt->execute([
|
||||
'aid' => $assessment_id,
|
||||
'lid' => $learner_id,
|
||||
'marks' => $marks
|
||||
]);
|
||||
}
|
||||
}
|
||||
$db->commit();
|
||||
$message = "Marks saved successfully.";
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
$message = "Error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Assessments for this school
|
||||
$stmt = $db->prepare("SELECT * FROM assessments WHERE school_id = ? ORDER BY created_at DESC");
|
||||
$stmt->execute([$school_id]);
|
||||
$assessments = $stmt->fetchAll();
|
||||
|
||||
// If an assessment is selected for grading
|
||||
$selected_assessment = null;
|
||||
$learners_to_grade = [];
|
||||
if (isset($_GET['grade_id'])) {
|
||||
$stmt = $db->prepare("SELECT * FROM assessments WHERE id = ? AND school_id = ?");
|
||||
$stmt->execute([$_GET['grade_id'], $school_id]);
|
||||
$selected_assessment = $stmt->fetch();
|
||||
|
||||
if ($selected_assessment) {
|
||||
// Fetch learners in this grade and their existing marks
|
||||
$stmt = $db->prepare("
|
||||
SELECT l.*, m.marks_obtained
|
||||
FROM learners l
|
||||
LEFT JOIN marks m ON l.id = m.learner_id AND m.assessment_id = :aid
|
||||
WHERE l.school_id = :sid AND l.grade = :grade
|
||||
ORDER BY l.full_name ASC
|
||||
");
|
||||
$stmt->execute([
|
||||
'aid' => $selected_assessment['id'],
|
||||
'sid' => $school_id,
|
||||
'grade' => $selected_assessment['grade']
|
||||
]);
|
||||
$learners_to_grade = $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container pb-5">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h2 class="h4 mb-1">Assessment Tracking</h2>
|
||||
<p class="text-muted small">Record and monitor learner performance</p>
|
||||
</div>
|
||||
<button class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#addAssessmentModal">
|
||||
<i class="bi bi-plus-lg me-2"></i> New Assessment
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-info alert-dismissible fade show shadow-sm" role="alert">
|
||||
<?= htmlspecialchars($message) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-bold">Assessments</h5>
|
||||
</div>
|
||||
<div class="list-group list-group-flush overflow-auto" style="max-height: 500px;">
|
||||
<?php if (empty($assessments)): ?>
|
||||
<div class="p-4 text-center text-muted">No assessments created.</div>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($assessments as $a): ?>
|
||||
<a href="?grade_id=<?= $a['id'] ?>" class="list-group-item list-group-item-action p-3 <?= ($selected_assessment && $selected_assessment['id'] == $a['id']) ? 'active' : '' ?>">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0 fw-bold"><?= htmlspecialchars($a['name']) ?></h6>
|
||||
<span class="badge <?= ($selected_assessment && $selected_assessment['id'] == $a['id']) ? 'bg-light text-primary' : 'bg-primary' ?>"><?= htmlspecialchars($a['grade']) ?></span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<small><?= htmlspecialchars($a['type']) ?> (<?= $a['total_marks'] ?> pts)</small>
|
||||
<small><?= date('d M', strtotime($a['created_at'])) ?></small>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<?php if ($selected_assessment): ?>
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="mb-0 fw-bold">Capture Marks: <?= htmlspecialchars($selected_assessment['name']) ?></h5>
|
||||
<small class="text-muted">Grade <?= htmlspecialchars($selected_assessment['grade']) ?> | Max Marks: <?= $selected_assessment['total_marks'] ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="save_marks">
|
||||
<input type="hidden" name="assessment_id" value="<?= $selected_assessment['id'] ?>">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">Learner Name</th>
|
||||
<th style="width: 150px;" class="text-end pe-4">Score / <?= $selected_assessment['total_marks'] ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($learners_to_grade)): ?>
|
||||
<tr>
|
||||
<td colspan="2" class="text-center py-4 text-muted">No learners found for Grade <?= htmlspecialchars($selected_assessment['grade']) ?> in this school.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($learners_to_grade as $l): ?>
|
||||
<tr>
|
||||
<td class="ps-4">
|
||||
<strong><?= htmlspecialchars($l['full_name']) ?></strong>
|
||||
<br><small class="text-muted"><?= htmlspecialchars($l['student_id']) ?></small>
|
||||
</td>
|
||||
<td class="text-end pe-4">
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" name="marks[<?= $l['id'] ?>]" class="form-control text-end"
|
||||
max="<?= $selected_assessment['total_marks'] ?>" min="0"
|
||||
value="<?= $l['marks_obtained'] ?? '' ?>" placeholder="0">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-3 text-end border-top">
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">
|
||||
<i class="bi bi-save me-2"></i> Save Marks
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="h-100 d-flex flex-column align-items-center justify-content-center text-muted py-5 border rounded bg-white shadow-sm">
|
||||
<i class="bi bi-journal-check mb-3" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
<p>Select an assessment from the list to record marks.</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Assessment Modal -->
|
||||
<div class="modal fade" id="addAssessmentModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<form method="POST" class="modal-content border-0 shadow-lg">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title fw-bold">New Assessment</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<input type="hidden" name="action" value="create">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Assessment Name</label>
|
||||
<input type="text" name="name" class="form-control" required placeholder="e.g. Term 1 Math Test">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label small fw-bold">Type</label>
|
||||
<select name="type" class="form-select" required>
|
||||
<option value="Test">Test</option>
|
||||
<option value="Exam">Exam</option>
|
||||
<option value="Assignment">Assignment</option>
|
||||
<option value="Project">Project</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label small fw-bold">Total Marks</label>
|
||||
<input type="number" name="total_marks" class="form-control" required placeholder="e.g. 50">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Grade</label>
|
||||
<select name="grade" class="form-select" required>
|
||||
<option value="">Select Grade</option>
|
||||
<option value="8">Grade 8</option>
|
||||
<option value="9">Grade 9</option>
|
||||
<option value="10">Grade 10</option>
|
||||
<option value="11">Grade 11</option>
|
||||
<option value="12">Grade 12</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">Create Assessment</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
73
db/migrations/002_add_users_and_assessments.sql
Normal file
73
db/migrations/002_add_users_and_assessments.sql
Normal file
@ -0,0 +1,73 @@
|
||||
-- Migration: Add users, assessments and school association
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
role ENUM('Super Admin', 'Admin', 'Teacher', 'Parent') NOT NULL,
|
||||
school_id INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- Add school_id to learners if it doesn't exist
|
||||
SET @dbname = DATABASE();
|
||||
SET @tablename = 'learners';
|
||||
SET @columnname = 'school_id';
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @dbname
|
||||
AND TABLE_NAME = @tablename
|
||||
AND COLUMN_NAME = @columnname) > 0,
|
||||
'SELECT 1',
|
||||
'ALTER TABLE learners ADD COLUMN school_id INT, ADD FOREIGN KEY (school_id) REFERENCES schools(id)'
|
||||
));
|
||||
PREPARE stmt FROM @preparedStatement;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- Add school_id to attendance if it doesn't exist
|
||||
SET @tablename = 'attendance';
|
||||
SET @columnname = 'school_id';
|
||||
SET @preparedStatement = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @dbname
|
||||
AND TABLE_NAME = @tablename
|
||||
AND COLUMN_NAME = @columnname) > 0,
|
||||
'SELECT 1',
|
||||
'ALTER TABLE attendance ADD COLUMN school_id INT, ADD FOREIGN KEY (school_id) REFERENCES schools(id)'
|
||||
));
|
||||
PREPARE stmt FROM @preparedStatement;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS assessments (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type ENUM('Test', 'Exam', 'Assignment', 'Project') NOT NULL,
|
||||
total_marks INT NOT NULL,
|
||||
grade VARCHAR(20) NOT NULL,
|
||||
school_id INT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS marks (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
assessment_id INT NOT NULL,
|
||||
learner_id INT NOT NULL,
|
||||
marks_obtained INT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (assessment_id) REFERENCES assessments(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (learner_id) REFERENCES learners(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY unique_mark (assessment_id, learner_id)
|
||||
);
|
||||
|
||||
-- Seed a default user (password: password123)
|
||||
-- Admin for 'Soweto High' (assuming id 1 from previous seeding if it exists)
|
||||
INSERT IGNORE INTO users (email, password, role, school_id) VALUES
|
||||
('admin@sowetohigh.edu.za', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'Admin', 1),
|
||||
('teacher@sowetohigh.edu.za', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'Teacher', 1);
|
||||
|
||||
-- Assign existing learners to school 1 if they aren't assigned
|
||||
UPDATE learners SET school_id = 1 WHERE school_id IS NULL;
|
||||
UPDATE attendance SET school_id = 1 WHERE school_id IS NULL;
|
||||
@ -1,7 +1,14 @@
|
||||
<?php
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Education Ecosystem Platform for South African Schools';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
$current_role = $current_role ?? 'Teacher';
|
||||
|
||||
// Use session role if available, fallback to $current_role (for backward compatibility during transition)
|
||||
$user_role = $_SESSION['role'] ?? ($current_role ?? 'Guest');
|
||||
$is_logged_in = isset($_SESSION['user_id']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -23,30 +30,33 @@ $current_role = $current_role ?? 'Teacher';
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark mb-4">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark mb-4 shadow-sm">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.php">
|
||||
<a class="navbar-brand fw-bold" href="index.php">
|
||||
<i class="bi bi-book-half me-2"></i>
|
||||
Township Schools
|
||||
SOMS
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<?php if ($current_role === 'Super Admin'): ?>
|
||||
<?php if ($user_role === 'Super Admin'): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'super-admin.php' ? 'active' : '' ?>" href="super-admin.php">Platform Stats</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($current_role === 'Teacher'): ?>
|
||||
<?php if ($user_role === 'Teacher'): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'index.php' ? 'active' : '' ?>" href="index.php">Attendance</a>
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'teacher.php' ? 'active' : '' ?>" href="teacher.php">Attendance</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'assessments.php' ? 'active' : '' ?>" href="assessments.php">Assessments</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($current_role === 'Admin'): ?>
|
||||
<?php if ($user_role === 'Admin'): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'admin.php' ? 'active' : '' ?>" href="admin.php">Dashboard</a>
|
||||
</li>
|
||||
@ -55,26 +65,23 @@ $current_role = $current_role ?? 'Teacher';
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($current_role === 'Parent'): ?>
|
||||
<?php if ($user_role === 'Parent' || $user_role === 'Guest'): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'parent.php' ? 'active' : '' ?>" href="parent.php">My Child</a>
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'parent.php' ? 'active' : '' ?>" href="parent.php">Parent Portal</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="text-white me-3 d-none d-md-inline small">Role: <strong><?= $current_role ?></strong></span>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-outline-light btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
||||
Switch Role
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item <?= $current_role == 'Super Admin' ? 'active' : '' ?>" href="super-admin.php">Super Admin</a></li>
|
||||
<li><a class="dropdown-item <?= $current_role == 'Admin' ? 'active' : '' ?>" href="admin.php">School Admin</a></li>
|
||||
<li><a class="dropdown-item <?= $current_role == 'Teacher' ? 'active' : '' ?>" href="index.php">Teacher</a></li>
|
||||
<li><a class="dropdown-item <?= $current_role == 'Parent' ? 'active' : '' ?>" href="parent.php">Parent</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php if ($is_logged_in): ?>
|
||||
<span class="text-white me-3 d-none d-md-inline small">
|
||||
Logged in as: <strong><?= htmlspecialchars($_SESSION['email']) ?></strong> (<?= $user_role ?>)
|
||||
</span>
|
||||
<a href="logout.php" class="btn btn-outline-light btn-sm">Logout</a>
|
||||
<?php else: ?>
|
||||
<a href="login.php" class="btn btn-outline-light btn-sm me-2">Login</a>
|
||||
<a href="register.php" class="btn btn-warning btn-sm text-dark fw-bold">Register</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
||||
210
index.php
210
index.php
@ -1,164 +1,70 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$db = db();
|
||||
$date = date('Y-m-d');
|
||||
$message = '';
|
||||
$current_role = 'Teacher';
|
||||
$pageTitle = 'Teacher Dashboard | Township Schools Platform';
|
||||
|
||||
// Handle Attendance Submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['attendance'])) {
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
$stmt = $db->prepare("INSERT INTO attendance (learner_id, status, date)
|
||||
VALUES (:learner_id, :status, :date)
|
||||
ON DUPLICATE KEY UPDATE status = VALUES(status)");
|
||||
|
||||
foreach ($_POST['attendance'] as $learner_id => $status) {
|
||||
$stmt->execute([
|
||||
'learner_id' => $learner_id,
|
||||
'status' => $status,
|
||||
'date' => $date
|
||||
]);
|
||||
}
|
||||
$db->commit();
|
||||
$message = "Attendance saved successfully for $date.";
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
$message = "Error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Learners and their current attendance for today
|
||||
$query = "SELECT l.*, a.status as today_status
|
||||
FROM learners l
|
||||
LEFT JOIN attendance a ON l.id = a.learner_id AND a.date = :date
|
||||
ORDER BY l.full_name ASC";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute(['date' => $date]);
|
||||
$learners = $stmt->fetchAll();
|
||||
|
||||
// Stats
|
||||
$total_learners = count($learners);
|
||||
$present_count = 0;
|
||||
foreach ($learners as $l) {
|
||||
if ($l['today_status'] === 'present') $present_count++;
|
||||
}
|
||||
$presence_rate = $total_learners > 0 ? round(($present_count / $total_learners) * 100) : 0;
|
||||
|
||||
$pageTitle = 'Welcome | Township Schools Platform';
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container pb-5">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h2 class="h4 mb-1">Teacher Dashboard</h2>
|
||||
<p class="text-muted small">Daily attendance for <strong><?= date('l, d F Y') ?></strong></p>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.hero-section {
|
||||
height: 90vh;
|
||||
background: linear-gradient(rgba(0, 77, 64, 0.7), rgba(0, 77, 64, 0.7)),
|
||||
url('assets/images/landing-bg.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
.hero-content {
|
||||
max-width: 800px;
|
||||
padding: 2rem;
|
||||
}
|
||||
.feature-card {
|
||||
border: none;
|
||||
transition: transform 0.3s;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.feature-card:hover {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-info alert-dismissible fade show" role="alert">
|
||||
<?= htmlspecialchars($message) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Stats Row -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card p-3 stats-card">
|
||||
<p>Total Learners</p>
|
||||
<h3><?= $total_learners ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card p-3 stats-card" style="border-left-color: #2E7D32;">
|
||||
<p>Presence Rate</p>
|
||||
<h3><?= $presence_rate ?>%</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card p-3 stats-card" style="border-left-color: var(--secondary-color);">
|
||||
<p>Pending Reports</p>
|
||||
<h3>0</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attendance Table -->
|
||||
<div class="card">
|
||||
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Class Register</h5>
|
||||
<div class="input-group input-group-sm w-auto">
|
||||
<span class="input-group-text bg-white border-end-0"><i class="bi bi-search"></i></span>
|
||||
<input type="text" id="learnerSearch" class="form-control border-start-0" placeholder="Search learner...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0" id="learnersTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="ps-4">Learner Name</th>
|
||||
<th>Grade</th>
|
||||
<th>Student ID</th>
|
||||
<th class="text-end pe-4">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($learners as $learner): ?>
|
||||
<tr>
|
||||
<td class="ps-4">
|
||||
<strong><?= htmlspecialchars($learner['full_name']) ?></strong>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($learner['grade']) ?></td>
|
||||
<td><span class="badge bg-light text-dark"><?= htmlspecialchars($learner['student_id']) ?></span></td>
|
||||
<td class="text-end pe-4">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<input type="radio" class="btn-check" name="attendance[<?= $learner['id'] ?>]"
|
||||
id="pres_<?= $learner['id'] ?>" value="present"
|
||||
<?= $learner['today_status'] === 'present' ? 'checked' : '' ?> required>
|
||||
<label class="btn btn-outline-success" for="pres_<?= $learner['id'] ?>">Present</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="attendance[<?= $learner['id'] ?>]"
|
||||
id="abs_<?= $learner['id'] ?>" value="absent"
|
||||
<?= $learner['today_status'] === 'absent' ? 'checked' : '' ?>>
|
||||
<label class="btn btn-outline-danger" for="abs_<?= $learner['id'] ?>">Absent</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-3 text-end border-top">
|
||||
<button type="submit" class="btn btn-primary px-4">
|
||||
<i class="bi bi-cloud-upload me-2"></i> Save & Sync Register
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="hero-section">
|
||||
<div class="hero-content">
|
||||
<h1 class="display-3 fw-bold mb-4">Empowering Township Schools Through Digital Excellence</h1>
|
||||
<p class="lead mb-5">A comprehensive school operations and management system designed for resilience, efficiency, and student success.</p>
|
||||
<div class="d-flex justify-content-center gap-3">
|
||||
<a href="login.php" class="btn btn-warning btn-lg px-5 text-dark fw-bold">Login</a>
|
||||
<a href="parent.php" class="btn btn-outline-light btn-lg px-5">Parent Portal</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Simple Search Filter
|
||||
document.getElementById('learnerSearch').addEventListener('keyup', function() {
|
||||
let filter = this.value.toUpperCase();
|
||||
let rows = document.querySelector("#learnersTable tbody").rows;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let nameCol = rows[i].cells[0].textContent.toUpperCase();
|
||||
let idCol = rows[i].cells[2].textContent.toUpperCase();
|
||||
if (nameCol.indexOf(filter) > -1 || idCol.indexOf(filter) > -1) {
|
||||
rows[i].style.display = "";
|
||||
} else {
|
||||
rows[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div class="container py-5">
|
||||
<div class="row g-4 text-center">
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 p-4 feature-card shadow-sm">
|
||||
<i class="bi bi-person-check-fill display-4 text-primary mb-3"></i>
|
||||
<h3 class="h5">Teacher Productivity</h3>
|
||||
<p class="text-muted small">Streamlined attendance tracking and assessment management to save teachers valuable time.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 p-4 feature-card shadow-sm">
|
||||
<i class="bi bi-bar-chart-fill display-4 text-success mb-3"></i>
|
||||
<h3 class="h5">School Analytics</h3>
|
||||
<p class="text-muted small">Real-time data for administrators to monitor school-wide performance and attendance trends.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 p-4 feature-card shadow-sm">
|
||||
<i class="bi bi-cloud-slash-fill display-4 text-warning mb-3"></i>
|
||||
<h3 class="h5">Offline First</h3>
|
||||
<p class="text-muted small">Built to work in low-bandwidth environments with robust offline synchronization capabilities.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
|
||||
81
learners.php
81
learners.php
@ -1,20 +1,28 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
// Auth Check
|
||||
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'Admin') {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$current_role = 'Admin';
|
||||
$pageTitle = 'Manage Learners | Township Schools Platform';
|
||||
$school_id = $_SESSION['school_id'];
|
||||
$pageTitle = 'Manage Learners | SOMS';
|
||||
$message = '';
|
||||
|
||||
// Handle Add Learner
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
if ($_POST['action'] === 'add') {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO learners (full_name, grade, student_id) VALUES (?, ?, ?)");
|
||||
$stmt = $db->prepare("INSERT INTO learners (full_name, grade, student_id, school_id) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$_POST['full_name'],
|
||||
$_POST['grade'],
|
||||
$_POST['student_id']
|
||||
$_POST['student_id'],
|
||||
$school_id
|
||||
]);
|
||||
$message = "Learner registered successfully.";
|
||||
} catch (Exception $e) {
|
||||
@ -22,8 +30,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
}
|
||||
} elseif ($_POST['action'] === 'delete') {
|
||||
try {
|
||||
$stmt = $db->prepare("DELETE FROM learners WHERE id = ?");
|
||||
$stmt->execute([$_POST['id']]);
|
||||
// Ensure the learner belongs to this school before deleting
|
||||
$stmt = $db->prepare("DELETE FROM learners WHERE id = ? AND school_id = ?");
|
||||
$stmt->execute([$_POST['id'], $school_id]);
|
||||
$message = "Learner record deleted.";
|
||||
} catch (Exception $e) {
|
||||
$message = "Error: " . $e->getMessage();
|
||||
@ -31,8 +40,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Learners
|
||||
$learners = $db->query("SELECT * FROM learners ORDER BY full_name ASC")->fetchAll();
|
||||
// Fetch Learners for this specific school
|
||||
$stmt = $db->prepare("SELECT * FROM learners WHERE school_id = ? ORDER BY full_name ASC");
|
||||
$stmt->execute([$school_id]);
|
||||
$learners = $stmt->fetchAll();
|
||||
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
@ -41,26 +52,26 @@ include 'includes/header.php';
|
||||
<div class="row mb-4 align-items-center">
|
||||
<div class="col">
|
||||
<h2 class="h4 mb-1">Learner Management</h2>
|
||||
<p class="text-muted small">Total: <strong><?= count($learners) ?> learners</strong></p>
|
||||
<p class="text-muted small">Total: <strong><?= count($learners) ?> learners</strong> in this school</p>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addLearnerModal">
|
||||
<button class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#addLearnerModal">
|
||||
<i class="bi bi-person-plus me-2"></i> Register New Learner
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-info alert-dismissible fade show" role="alert">
|
||||
<div class="alert alert-info alert-dismissible fade show shadow-sm" role="alert">
|
||||
<?= htmlspecialchars($message) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">Full Name</th>
|
||||
<th>Grade</th>
|
||||
@ -69,13 +80,21 @@ include 'includes/header.php';
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($learners)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center py-5 text-muted">
|
||||
<i class="bi bi-people display-1 mb-3 d-block"></i>
|
||||
No learners registered yet.
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($learners as $l): ?>
|
||||
<tr>
|
||||
<td class="ps-4">
|
||||
<strong><?= htmlspecialchars($l['full_name']) ?></strong>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($l['grade']) ?></td>
|
||||
<td><span class="badge bg-light text-dark"><?= htmlspecialchars($l['student_id']) ?></span></td>
|
||||
<td><span class="badge bg-light text-dark border"><?= htmlspecialchars($l['student_id']) ?></span></td>
|
||||
<td class="text-end pe-4">
|
||||
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this learner?');">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
@ -96,39 +115,39 @@ include 'includes/header.php';
|
||||
<!-- Add Learner Modal -->
|
||||
<div class="modal fade" id="addLearnerModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<form method="POST" class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Register New Learner</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
<form method="POST" class="modal-content border-0 shadow-lg">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title fw-bold">Register New Learner</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-modal="modal" data-bs-target="#addLearnerModal" aria-label="Close" onclick="bootstrap.Modal.getInstance(document.getElementById('addLearnerModal')).hide()"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body p-4">
|
||||
<input type="hidden" name="action" value="add">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Full Name</label>
|
||||
<label class="form-label small fw-bold">Full Name</label>
|
||||
<input type="text" name="full_name" class="form-control" required placeholder="e.g. Sipho Zulu">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Grade</label>
|
||||
<label class="form-label small fw-bold">Grade</label>
|
||||
<select name="grade" class="form-select" required>
|
||||
<option value="">Select Grade</option>
|
||||
<option value="Grade 8">Grade 8</option>
|
||||
<option value="Grade 9">Grade 9</option>
|
||||
<option value="Grade 10">Grade 10</option>
|
||||
<option value="Grade 11">Grade 11</option>
|
||||
<option value="Grade 12">Grade 12</option>
|
||||
<option value="8">Grade 8</option>
|
||||
<option value="9">Grade 9</option>
|
||||
<option value="10">Grade 10</option>
|
||||
<option value="11">Grade 11</option>
|
||||
<option value="12">Grade 12</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Student ID / National ID</label>
|
||||
<label class="form-label small fw-bold">Student ID / National ID</label>
|
||||
<input type="text" name="student_id" class="form-control" required placeholder="e.g. STU10023">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Register Learner</button>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">Register Learner</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
95
login.php
Normal file
95
login.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($email && $password) {
|
||||
$db = db();
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE email = :email");
|
||||
$stmt->execute(['email' => $email]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
$_SESSION['school_id'] = $user['school_id'];
|
||||
$_SESSION['email'] = $user['email'];
|
||||
|
||||
// Redirect based on role
|
||||
switch ($user['role']) {
|
||||
case 'Super Admin':
|
||||
header('Location: super-admin.php');
|
||||
break;
|
||||
case 'Admin':
|
||||
header('Location: admin.php');
|
||||
break;
|
||||
case 'Teacher':
|
||||
header('Location: teacher.php');
|
||||
break;
|
||||
case 'Parent':
|
||||
header('Location: parent.php');
|
||||
break;
|
||||
default:
|
||||
header('Location: index.php');
|
||||
}
|
||||
exit;
|
||||
} else {
|
||||
$error = "Invalid email or password.";
|
||||
}
|
||||
} else {
|
||||
$error = "Please fill in all fields.";
|
||||
}
|
||||
}
|
||||
|
||||
$pageTitle = 'Login | SOMS Platform';
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card shadow-lg border-0">
|
||||
<div class="card-body p-5">
|
||||
<div class="text-center mb-4">
|
||||
<h2 class="fw-bold">Welcome Back</h2>
|
||||
<p class="text-muted">Sign in to manage your school</p>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger small"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Email Address</label>
|
||||
<input type="email" name="email" class="form-control" placeholder="e.g., admin@school.edu.za" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label small fw-bold">Password</label>
|
||||
<input type="password" name="password" class="form-control" placeholder="••••••••" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold">Sign In</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<p class="text-muted small">Don't have an account? <a href="register.php" class="text-primary fw-bold">Register School</a></p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 p-3 bg-light rounded border">
|
||||
<p class="mb-1 small fw-bold text-uppercase text-muted" style="letter-spacing: 1px;">Demo Credentials:</p>
|
||||
<p class="mb-0 small"><strong>Admin:</strong> admin@sowetohigh.edu.za</p>
|
||||
<p class="mb-0 small"><strong>Teacher:</strong> teacher@sowetohigh.edu.za</p>
|
||||
<p class="mb-0 small text-muted">Password: <code>password123</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
5
logout.php
Normal file
5
logout.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
45
parent.php
45
parent.php
@ -1,16 +1,20 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
$db = db();
|
||||
$current_role = 'Parent';
|
||||
$pageTitle = 'Parent Portal | Township Schools Platform';
|
||||
$pageTitle = 'Parent Portal | SOMS';
|
||||
$learner = null;
|
||||
$school = null;
|
||||
$attendance_history = [];
|
||||
$search_id = $_GET['student_id'] ?? '';
|
||||
|
||||
if ($search_id) {
|
||||
// Fetch Learner
|
||||
$stmt = $db->prepare("SELECT * FROM learners WHERE student_id = ?");
|
||||
// Fetch Learner and School
|
||||
$stmt = $db->prepare("SELECT l.*, s.name as school_name
|
||||
FROM learners l
|
||||
JOIN schools s ON l.school_id = s.id
|
||||
WHERE l.student_id = ?");
|
||||
$stmt->execute([$search_id]);
|
||||
$learner = $stmt->fetch();
|
||||
|
||||
@ -35,21 +39,21 @@ include 'includes/header.php';
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card p-4">
|
||||
<h5 class="mb-3">Find My Child</h5>
|
||||
<div class="card p-4 shadow-sm border-0">
|
||||
<h5 class="mb-3 fw-bold">Find My Child</h5>
|
||||
<form method="GET">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">Student ID / National ID</label>
|
||||
<label class="form-label small fw-bold">Student ID / National ID</label>
|
||||
<input type="text" name="student_id" class="form-control" placeholder="e.g. STU10023" value="<?= htmlspecialchars($search_id) ?>" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<button type="submit" class="btn btn-primary w-100 fw-bold">
|
||||
<i class="bi bi-search me-2"></i> View Progress
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if ($search_id && !$learner): ?>
|
||||
<div class="alert alert-warning mt-3">
|
||||
<div class="alert alert-warning mt-3 shadow-sm">
|
||||
<i class="bi bi-exclamation-triangle me-2"></i> No learner found with ID <strong><?= htmlspecialchars($search_id) ?></strong>. Please contact the school office.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
@ -57,25 +61,26 @@ include 'includes/header.php';
|
||||
|
||||
<div class="col-md-8">
|
||||
<?php if ($learner): ?>
|
||||
<div class="card mb-4">
|
||||
<div class="card mb-4 shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0">Learner Profile: <strong><?= htmlspecialchars($learner['full_name']) ?></strong></h5>
|
||||
<h5 class="mb-0 fw-bold"><?= htmlspecialchars($learner['full_name']) ?></h5>
|
||||
<small class="text-muted"><?= htmlspecialchars($learner['school_name']) ?></small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-4">
|
||||
<p class="text-muted small mb-1">Grade</p>
|
||||
<h6><?= htmlspecialchars($learner['grade']) ?></h6>
|
||||
<h6 class="fw-bold"><?= htmlspecialchars($learner['grade']) ?></h6>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p class="text-muted small mb-1">Attendance</p>
|
||||
<p class="text-muted small mb-1">Attendance (30d)</p>
|
||||
<?php
|
||||
$total = count($attendance_history);
|
||||
$present = 0;
|
||||
foreach ($attendance_history as $a) if ($a['status'] === 'present') $present++;
|
||||
$rate = $total > 0 ? round(($present / $total) * 100) : 0;
|
||||
?>
|
||||
<h6><?= $rate ?>%</h6>
|
||||
<h6 class="fw-bold"><?= $rate ?>%</h6>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p class="text-muted small mb-1">Status</p>
|
||||
@ -85,14 +90,14 @@ include 'includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0">Recent Attendance</h5>
|
||||
<h5 class="mb-0 fw-bold">Recent Attendance</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">Date</th>
|
||||
<th class="text-end pe-4">Status</th>
|
||||
@ -122,13 +127,13 @@ include 'includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="h-100 d-flex flex-column align-items-center justify-content-center text-muted py-5 border rounded bg-white">
|
||||
<div class="h-100 d-flex flex-column align-items-center justify-content-center text-muted py-5 border rounded bg-white shadow-sm">
|
||||
<i class="bi bi-person-badge mb-3" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
<p>Please enter a Student ID to view details.</p>
|
||||
<p>Please enter a Student ID to view your child's progress.</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
112
register.php
Normal file
112
register.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$school_name = $_POST['school_name'] ?? '';
|
||||
$province = $_POST['province'] ?? '';
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($school_name && $email && $password) {
|
||||
$db = db();
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
// 1. Create School
|
||||
$stmt = $db->prepare("INSERT INTO schools (name, province) VALUES (:name, :province)");
|
||||
$stmt->execute(['name' => $school_name, 'province' => $province]);
|
||||
$school_id = $db->lastInsertId();
|
||||
|
||||
// 2. Create Admin User
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("INSERT INTO users (email, password, role, school_id) VALUES (:email, :password, 'Admin', :school_id)");
|
||||
$stmt->execute([
|
||||
'email' => $email,
|
||||
'password' => $hashedPassword,
|
||||
'school_id' => $school_id
|
||||
]);
|
||||
|
||||
$db->commit();
|
||||
$success = "School registered successfully! You can now login.";
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
$error = "Registration failed: " . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = "Please fill in all required fields.";
|
||||
}
|
||||
}
|
||||
|
||||
$pageTitle = 'Register School | SOMS Platform';
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-lg border-0">
|
||||
<div class="card-body p-5">
|
||||
<div class="text-center mb-4">
|
||||
<h2 class="fw-bold">Register Your School</h2>
|
||||
<p class="text-muted">Join the SOMS digital ecosystem</p>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger small"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success small"><?= htmlspecialchars($success) ?></div>
|
||||
<div class="text-center"><a href="login.php" class="btn btn-primary">Go to Login</a></div>
|
||||
<?php else: ?>
|
||||
<form method="POST">
|
||||
<h5 class="mb-3 fw-bold text-primary">School Information</h5>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">School Name</label>
|
||||
<input type="text" name="school_name" class="form-control" placeholder="e.g., Thabong Secondary School" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label small fw-bold">Province</label>
|
||||
<select name="province" class="form-select">
|
||||
<option value="Gauteng">Gauteng</option>
|
||||
<option value="Western Cape">Western Cape</option>
|
||||
<option value="KwaZulu-Natal">KwaZulu-Natal</option>
|
||||
<option value="Limpopo">Limpopo</option>
|
||||
<option value="Mpumalanga">Mpumalanga</option>
|
||||
<option value="North West">North West</option>
|
||||
<option value="Eastern Cape">Eastern Cape</option>
|
||||
<option value="Free State">Free State</option>
|
||||
<option value="Northern Cape">Northern Cape</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<h5 class="mb-3 fw-bold text-primary">Admin Account</h5>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold">Admin Email</label>
|
||||
<input type="email" name="email" class="form-control" placeholder="e.g., principal@school.edu.za" required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label small fw-bold">Admin Password</label>
|
||||
<input type="password" name="password" class="form-control" placeholder="••••••••" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold">Register & Continue</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<p class="text-muted small">Already registered? <a href="login.php" class="text-primary fw-bold">Login here</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
@ -1,9 +1,15 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
// Auth Check
|
||||
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'Super Admin') {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$current_role = 'Super Admin';
|
||||
$pageTitle = 'Super Admin | Township Schools Platform';
|
||||
$pageTitle = 'Super Admin | SOMS';
|
||||
$message = '';
|
||||
|
||||
// Handle School Addition
|
||||
@ -34,14 +40,14 @@ include 'includes/header.php';
|
||||
<p class="text-muted small">Platform-wide oversight and school management</p>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addSchoolModal">
|
||||
<button class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#addSchoolModal">
|
||||
<i class="bi bi-plus-circle me-2"></i> Onboard New School
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-info alert-dismissible fade show" role="alert">
|
||||
<div class="alert alert-info alert-dismissible fade show shadow-sm" role="alert">
|
||||
<?= htmlspecialchars($message) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
@ -50,38 +56,38 @@ include 'includes/header.php';
|
||||
<!-- Platform Stats -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card">
|
||||
<p>Total Schools</p>
|
||||
<h3><?= $total_schools ?></h3>
|
||||
<div class="card p-3 stats-card shadow-sm">
|
||||
<p class="text-muted mb-1">Total Schools</p>
|
||||
<h3 class="mb-0"><?= $total_schools ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card" style="border-left-color: #0288D1;">
|
||||
<p>Total Learners</p>
|
||||
<h3><?= $total_learners ?></h3>
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid #0288D1;">
|
||||
<p class="text-muted mb-1">Total Learners</p>
|
||||
<h3 class="mb-0"><?= $total_learners ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card" style="border-left-color: #2E7D32;">
|
||||
<p>System Uptime</p>
|
||||
<h3>99.9%</h3>
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid #2E7D32;">
|
||||
<p class="text-muted mb-1">System Uptime</p>
|
||||
<h3 class="mb-0">99.9%</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card p-3 stats-card" style="border-left-color: #FFA000;">
|
||||
<p>Storage Used</p>
|
||||
<h3>12 MB</h3>
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid #FFA000;">
|
||||
<p class="text-muted mb-1">Storage Used</p>
|
||||
<h3 class="mb-0">12 MB</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0">Managed Schools</h5>
|
||||
<h5 class="mb-0 fw-bold">Managed Schools</h5>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">School Name</th>
|
||||
<th>Province</th>
|
||||
@ -113,19 +119,19 @@ include 'includes/header.php';
|
||||
<!-- Add School Modal -->
|
||||
<div class="modal fade" id="addSchoolModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<form method="POST" class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Onboard New School</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
<form method="POST" class="modal-content border-0 shadow-lg">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title fw-bold">Onboard New School</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body p-4">
|
||||
<input type="hidden" name="action" value="add_school">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">School Name</label>
|
||||
<label class="form-label small fw-bold">School Name</label>
|
||||
<input type="text" name="name" class="form-control" required placeholder="e.g. Luthuli High School">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Province</label>
|
||||
<label class="form-label small fw-bold">Province</label>
|
||||
<select name="province" class="form-select" required>
|
||||
<option value="Gauteng">Gauteng</option>
|
||||
<option value="Western Cape">Western Cape</option>
|
||||
@ -139,16 +145,16 @@ include 'includes/header.php';
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">District</label>
|
||||
<label class="form-label small fw-bold">District</label>
|
||||
<input type="text" name="district" class="form-control" required placeholder="e.g. Sedibeng East">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Onboard School</button>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">Onboard School</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
21
sw.js
21
sw.js
@ -1,9 +1,14 @@
|
||||
const CACHE_NAME = 'township-schools-v2';
|
||||
const CACHE_NAME = 'township-schools-v3';
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/index.php',
|
||||
'/login.php',
|
||||
'/register.php',
|
||||
'/teacher.php',
|
||||
'/admin.php',
|
||||
'/learners.php',
|
||||
'/assessments.php',
|
||||
'/parent.php',
|
||||
'/assets/css/custom.css',
|
||||
'/assets/js/main.js',
|
||||
'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css',
|
||||
@ -16,7 +21,7 @@ self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
console.log('Caching static assets');
|
||||
return cache.addAll(STATIC_ASSETS);
|
||||
return cache.addAll(STATIC_ASSETS).catch(err => console.warn('PWA Cache error:', err));
|
||||
})
|
||||
);
|
||||
self.skipWaiting();
|
||||
@ -37,17 +42,19 @@ self.addEventListener('activate', (event) => {
|
||||
|
||||
// Fetch Event (Stale-While-Revalidate)
|
||||
self.addEventListener('fetch', (event) => {
|
||||
// Only handle GET requests
|
||||
// Only handle GET requests and local/trusted domains
|
||||
if (event.request.method !== 'GET') return;
|
||||
|
||||
|
||||
event.respondWith(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
return cache.match(event.request).then((cachedResponse) => {
|
||||
const fetchedResponse = fetch(event.request).then((networkResponse) => {
|
||||
cache.put(event.request, networkResponse.clone());
|
||||
// Only cache successful GET responses
|
||||
if (networkResponse.ok) {
|
||||
cache.put(event.request, networkResponse.clone());
|
||||
}
|
||||
return networkResponse;
|
||||
}).catch(() => {
|
||||
// If network fails and no cache, maybe return a fallback page
|
||||
return cachedResponse;
|
||||
});
|
||||
|
||||
@ -55,4 +62,4 @@ self.addEventListener('fetch', (event) => {
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
179
teacher.php
Normal file
179
teacher.php
Normal file
@ -0,0 +1,179 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
session_start();
|
||||
|
||||
// Auth Check
|
||||
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'Teacher') {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$school_id = $_SESSION['school_id'];
|
||||
$date = date('Y-m-d');
|
||||
$message = '';
|
||||
$pageTitle = 'Teacher Dashboard | SOMS';
|
||||
|
||||
// Handle Attendance Submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['attendance'])) {
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
$stmt = $db->prepare("INSERT INTO attendance (learner_id, status, date, school_id)
|
||||
VALUES (:learner_id, :status, :date, :school_id)
|
||||
ON DUPLICATE KEY UPDATE status = VALUES(status)");
|
||||
|
||||
foreach ($_POST['attendance'] as $learner_id => $status) {
|
||||
$stmt->execute([
|
||||
'learner_id' => $learner_id,
|
||||
'status' => $status,
|
||||
'date' => $date,
|
||||
'school_id' => $school_id
|
||||
]);
|
||||
}
|
||||
$db->commit();
|
||||
$message = "Attendance saved successfully for $date.";
|
||||
} catch (Exception $e) {
|
||||
$db->rollBack();
|
||||
$message = "Error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Learners for this specific school and their current attendance for today
|
||||
$query = "SELECT l.*, a.status as today_status
|
||||
FROM learners l
|
||||
LEFT JOIN attendance a ON l.id = a.learner_id AND a.date = :date
|
||||
WHERE l.school_id = :school_id
|
||||
ORDER BY l.full_name ASC";
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute(['date' => $date, 'school_id' => $school_id]);
|
||||
$learners = $stmt->fetchAll();
|
||||
|
||||
// Stats
|
||||
$total_learners = count($learners);
|
||||
$present_count = 0;
|
||||
foreach ($learners as $l) {
|
||||
if ($l['today_status'] === 'present') $present_count++;
|
||||
}
|
||||
$presence_rate = $total_learners > 0 ? round(($present_count / $total_learners) * 100) : 0;
|
||||
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container pb-5">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h2 class="h4 mb-1">Teacher Dashboard</h2>
|
||||
<p class="text-muted small">Daily attendance for <strong><?= date('l, d F Y') ?></strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-info alert-dismissible fade show" role="alert">
|
||||
<?= htmlspecialchars($message) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Stats Row -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card p-3 stats-card shadow-sm">
|
||||
<p class="text-muted mb-1">Total Learners</p>
|
||||
<h3 class="mb-0"><?= $total_learners ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid #2E7D32;">
|
||||
<p class="text-muted mb-1">Presence Rate</p>
|
||||
<h3 class="mb-0"><?= $presence_rate ?>%</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card p-3 stats-card shadow-sm" style="border-left: 4px solid var(--secondary-color);">
|
||||
<p class="text-muted mb-1">Active Class</p>
|
||||
<h3 class="mb-0">Grade <?= $total_learners > 0 ? htmlspecialchars($learners[0]['grade']) : 'N/A' ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attendance Table -->
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 fw-bold">Class Register</h5>
|
||||
<div class="input-group input-group-sm w-auto">
|
||||
<span class="input-group-text bg-white border-end-0"><i class="bi bi-search"></i></span>
|
||||
<input type="text" id="learnerSearch" class="form-control border-start-0" placeholder="Search learner...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form method="POST">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0" id="learnersTable">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">Learner Name</th>
|
||||
<th>Grade</th>
|
||||
<th>Student ID</th>
|
||||
<th class="text-end pe-4">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($learners)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center py-4 text-muted">No learners found for your school. Please contact your Admin.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($learners as $learner): ?>
|
||||
<tr>
|
||||
<td class="ps-4">
|
||||
<strong><?= htmlspecialchars($learner['full_name']) ?></strong>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($learner['grade']) ?></td>
|
||||
<td><span class="badge bg-light text-dark border"><?= htmlspecialchars($learner['student_id']) ?></span></td>
|
||||
<td class="text-end pe-4">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<input type="radio" class="btn-check" name="attendance[<?= $learner['id'] ?>]"
|
||||
id="pres_<?= $learner['id'] ?>" value="present"
|
||||
<?= $learner['today_status'] === 'present' ? 'checked' : '' ?> required>
|
||||
<label class="btn btn-outline-success" for="pres_<?= $learner['id'] ?>">Present</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="attendance[<?= $learner['id'] ?>]"
|
||||
id="abs_<?= $learner['id'] ?>" value="absent"
|
||||
<?= $learner['today_status'] === 'absent' ? 'checked' : '' ?>>
|
||||
<label class="btn btn-outline-danger" for="abs_<?= $learner['id'] ?>">Absent</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="p-3 text-end border-top">
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">
|
||||
<i class="bi bi-cloud-upload me-2"></i> Save & Sync Register
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Simple Search Filter
|
||||
document.getElementById('learnerSearch').addEventListener('keyup', function() {
|
||||
let filter = this.value.toUpperCase();
|
||||
let rows = document.querySelector("#learnersTable tbody").rows;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
if (rows[i].cells.length < 3) continue;
|
||||
let nameCol = rows[i].cells[0].textContent.toUpperCase();
|
||||
let idCol = rows[i].cells[2].textContent.toUpperCase();
|
||||
if (nameCol.indexOf(filter) > -1 || idCol.indexOf(filter) > -1) {
|
||||
rows[i].style.display = "";
|
||||
} else {
|
||||
rows[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user