Auto commit: 2026-01-30T14:32:19.673Z

This commit is contained in:
Flatlogic Bot 2026-01-30 14:32:19 +00:00
parent e29ef2dff4
commit ef3a1cc12c
13 changed files with 978 additions and 311 deletions

View File

@ -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
View 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'; ?>

View 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;

View File

@ -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
View File

@ -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'; ?>

View File

@ -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
View 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
View File

@ -0,0 +1,5 @@
<?php
session_start();
session_destroy();
header('Location: index.php');
exit;

View File

@ -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
View 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'; ?>

View File

@ -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
View File

@ -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
View 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'; ?>