From 463712fc0cd7fdebb8d1165f6460c72c410b1aa1 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 7 Mar 2026 09:22:35 +0000 Subject: [PATCH] SMS --- api/students.php | 33 ++ assets/css/custom.css | 493 ++++++------------------ assets/js/main.js | 64 ++- attendance.php | 44 +++ attendance_detail.php | 71 ++++ attendance_recap.php | 48 +++ db/migrations/001_school_core.sql | 22 ++ db/migrations/002_attendance_module.sql | 12 + includes/db_init.php | 51 +++ index.php | 271 +++++++------ student.php | 139 +++++++ students.php | 236 ++++++++++++ 12 files changed, 952 insertions(+), 532 deletions(-) create mode 100644 api/students.php create mode 100644 attendance.php create mode 100644 attendance_detail.php create mode 100644 attendance_recap.php create mode 100644 db/migrations/001_school_core.sql create mode 100644 db/migrations/002_attendance_module.sql create mode 100644 includes/db_init.php create mode 100644 student.php create mode 100644 students.php diff --git a/api/students.php b/api/students.php new file mode 100644 index 0000000..d0bde94 --- /dev/null +++ b/api/students.php @@ -0,0 +1,33 @@ + 200) { + $limit = 50; +} + +$stmt = $db->prepare(" + SELECT s.id, s.student_code, s.full_name, s.status, s.gender, + c.name AS class_name, c.grade_level + FROM students s + LEFT JOIN classes c ON c.id = s.class_id + ORDER BY s.created_at DESC + LIMIT :limit +"); +$stmt->bindValue(':limit', $limit, PDO::PARAM_INT); +$stmt->execute(); +$students = $stmt->fetchAll(); + +echo json_encode([ + 'data' => $students, + 'count' => count($students), +]); diff --git a/assets/css/custom.css b/assets/css/custom.css index 789132e..defdbb7 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,403 +1,150 @@ -body { - background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); - background-size: 400% 400%; - animation: gradient 15s ease infinite; - color: #212529; - font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - font-size: 14px; - margin: 0; - min-height: 100vh; +:root { + --bg: #f6f7f9; + --surface: #ffffff; + --border: #e3e6ea; + --text: #0f172a; + --muted: #6b7280; + --accent: #1d4ed8; + --accent-soft: rgba(29, 78, 216, 0.08); } -.main-wrapper { - display: flex; - align-items: center; - justify-content: center; - min-height: 100vh; - width: 100%; - padding: 20px; +* { box-sizing: border-box; - position: relative; - z-index: 1; } -@keyframes gradient { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } -} - -.chat-container { - width: 100%; - max-width: 600px; - background: rgba(255, 255, 255, 0.85); - border: 1px solid rgba(255, 255, 255, 0.3); - border-radius: 20px; - display: flex; - flex-direction: column; - height: 85vh; - box-shadow: 0 20px 40px rgba(0,0,0,0.2); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - overflow: hidden; -} - -.chat-header { - padding: 1.5rem; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); - background: rgba(255, 255, 255, 0.5); - font-weight: 700; - font-size: 1.1rem; - display: flex; - justify-content: space-between; - align-items: center; -} - -.chat-messages { - flex: 1; - overflow-y: auto; - padding: 1.5rem; - display: flex; - flex-direction: column; - gap: 1.25rem; -} - -/* Custom Scrollbar */ -::-webkit-scrollbar { - width: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 10px; -} - -::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.5); -} - -.message { - max-width: 85%; - padding: 0.85rem 1.1rem; - border-radius: 16px; - line-height: 1.5; - font-size: 0.95rem; - box-shadow: 0 4px 15px rgba(0,0,0,0.05); - animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(20px) scale(0.95); } - to { opacity: 1; transform: translateY(0) scale(1); } -} - -.message.visitor { - align-self: flex-end; - background: linear-gradient(135deg, #212529 0%, #343a40 100%); - color: #fff; - border-bottom-right-radius: 4px; -} - -.message.bot { - align-self: flex-start; - background: #ffffff; - color: #212529; - border-bottom-left-radius: 4px; -} - -.chat-input-area { - padding: 1.25rem; - background: rgba(255, 255, 255, 0.5); - border-top: 1px solid rgba(0, 0, 0, 0.05); -} - -.chat-input-area form { - display: flex; - gap: 0.75rem; -} - -.chat-input-area input { - flex: 1; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - padding: 0.75rem 1rem; - outline: none; - background: rgba(255, 255, 255, 0.9); - transition: all 0.3s ease; -} - -.chat-input-area input:focus { - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2); -} - -.chat-input-area button { - background: #212529; - color: #fff; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - transition: all 0.3s ease; -} - -.chat-input-area button:hover { - background: #000; - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(0,0,0,0.2); -} - -/* Background Animations */ -.bg-animations { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; - overflow: hidden; - pointer-events: none; -} - -.blob { - position: absolute; - width: 500px; - height: 500px; - background: rgba(255, 255, 255, 0.2); - border-radius: 50%; - filter: blur(80px); - animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1); -} - -.blob-1 { - top: -10%; - left: -10%; - background: rgba(238, 119, 82, 0.4); -} - -.blob-2 { - bottom: -10%; - right: -10%; - background: rgba(35, 166, 213, 0.4); - animation-delay: -7s; - width: 600px; - height: 600px; -} - -.blob-3 { - top: 40%; - left: 30%; - background: rgba(231, 60, 126, 0.3); - animation-delay: -14s; - width: 450px; - height: 450px; -} - -@keyframes move { - 0% { transform: translate(0, 0) rotate(0deg) scale(1); } - 33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); } - 66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); } - 100% { transform: translate(0, 0) rotate(360deg) scale(1); } -} - -.header-link { +body { + background: var(--bg); + color: var(--text); + font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif; font-size: 14px; - color: #fff; - text-decoration: none; - background: rgba(0, 0, 0, 0.2); - padding: 0.5rem 1rem; - border-radius: 8px; - transition: all 0.3s ease; + line-height: 1.5; } -.header-link:hover { - background: rgba(0, 0, 0, 0.4); +a { + color: var(--accent); text-decoration: none; } -/* Admin Styles */ -.admin-container { - max-width: 900px; - margin: 3rem auto; - padding: 2.5rem; - background: rgba(255, 255, 255, 0.85); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-radius: 24px; - box-shadow: 0 20px 50px rgba(0,0,0,0.15); - border: 1px solid rgba(255, 255, 255, 0.4); - position: relative; - z-index: 1; +a:hover { + color: #153eae; } -.admin-container h1 { - margin-top: 0; - color: #212529; - font-weight: 800; +.app-shell { + min-height: 100vh; + display: flex; + flex-direction: column; } -.table { - width: 100%; - border-collapse: separate; - border-spacing: 0 8px; - margin-top: 1.5rem; +.navbar { + border-bottom: 1px solid var(--border); + background: var(--surface); } -.table th { - background: transparent; - border: none; - padding: 1rem; - color: #6c757d; +.navbar-brand { font-weight: 600; + letter-spacing: -0.2px; +} + +.badge-role { + background: var(--accent-soft); + color: var(--accent); + font-weight: 600; + border: 1px solid rgba(29, 78, 216, 0.2); +} + +.hero { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 10px; + padding: 24px; +} + +.module-card, +.card-soft { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 10px; + box-shadow: 0 2px 12px rgba(15, 23, 42, 0.04); +} + +.module-card .card-body { + padding: 18px; +} + +.module-title { + font-size: 15px; + font-weight: 600; +} + +.module-meta { + color: var(--muted); + font-size: 12px; +} + +.table thead th { + font-weight: 600; + font-size: 12px; text-transform: uppercase; - font-size: 0.75rem; - letter-spacing: 1px; + letter-spacing: 0.4px; + color: var(--muted); + border-bottom: 1px solid var(--border); } .table td { - background: #fff; - padding: 1rem; - border: none; + vertical-align: middle; } -.table tr td:first-child { border-radius: 12px 0 0 12px; } -.table tr td:last-child { border-radius: 0 12px 12px 0; } - -.form-group { - margin-bottom: 1.25rem; -} - -.form-group label { - display: block; - margin-bottom: 0.5rem; - font-weight: 600; - font-size: 0.9rem; -} - -.form-control { - width: 100%; - padding: 0.75rem 1rem; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - background: #fff; - transition: all 0.3s ease; - box-sizing: border-box; -} - -.form-control:focus { - outline: none; - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1); -} - -.header-container { - display: flex; - justify-content: space-between; - align-items: center; -} - -.header-links { - display: flex; - gap: 1rem; -} - -.admin-card { - background: rgba(255, 255, 255, 0.6); - padding: 2rem; - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.5); - margin-bottom: 2.5rem; - box-shadow: 0 10px 30px rgba(0,0,0,0.05); -} - -.admin-card h3 { - margin-top: 0; - margin-bottom: 1.5rem; - font-weight: 700; -} - -.btn-delete { - background: #dc3545; - color: white; - border: none; - padding: 0.25rem 0.5rem; - border-radius: 4px; - cursor: pointer; -} - -.btn-add { - background: #212529; - color: white; - border: none; - padding: 0.5rem 1rem; - border-radius: 4px; - cursor: pointer; - margin-top: 1rem; -} - -.btn-save { - background: #0088cc; - color: white; - border: none; - padding: 0.8rem 1.5rem; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - width: 100%; - transition: all 0.3s ease; -} - -.webhook-url { - font-size: 0.85em; - color: #555; - margin-top: 0.5rem; -} - -.history-table-container { - overflow-x: auto; - background: rgba(255, 255, 255, 0.4); - padding: 1rem; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.3); -} - -.history-table { - width: 100%; -} - -.history-table-time { - width: 15%; - white-space: nowrap; - font-size: 0.85em; - color: #555; -} - -.history-table-user { - width: 35%; - background: rgba(255, 255, 255, 0.3); +.form-control, +.form-select { border-radius: 8px; - padding: 8px; + border-color: var(--border); + font-size: 14px; } -.history-table-ai { - width: 50%; - background: rgba(255, 255, 255, 0.5); +.form-control:focus, +.form-select:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.12); +} + +.btn-primary { + background: var(--accent); + border-color: var(--accent); border-radius: 8px; - padding: 8px; + font-weight: 600; } -.no-messages { - text-align: center; - color: #777; -} \ No newline at end of file +.btn-outline-secondary { + border-radius: 8px; +} + +.section-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 12px; +} + +.footer { + margin-top: auto; + padding: 24px 0; + color: var(--muted); + font-size: 12px; +} + +.toast { + border-radius: 10px; + border: 1px solid var(--border); + box-shadow: 0 6px 20px rgba(15, 23, 42, 0.08); +} + +.search-input { + max-width: 280px; +} + +.empty-state { + border: 1px dashed var(--border); + border-radius: 10px; + padding: 18px; + color: var(--muted); + background: #fafbfc; +} diff --git a/assets/js/main.js b/assets/js/main.js index d349598..6e3f248 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,39 +1,31 @@ document.addEventListener('DOMContentLoaded', () => { - const chatForm = document.getElementById('chat-form'); - const chatInput = document.getElementById('chat-input'); - const chatMessages = document.getElementById('chat-messages'); - - const appendMessage = (text, sender) => { - const msgDiv = document.createElement('div'); - msgDiv.classList.add('message', sender); - msgDiv.textContent = text; - chatMessages.appendChild(msgDiv); - chatMessages.scrollTop = chatMessages.scrollHeight; - }; - - chatForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const message = chatInput.value.trim(); - if (!message) return; - - appendMessage(message, 'visitor'); - chatInput.value = ''; - - try { - const response = await fetch('api/chat.php', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ message }) - }); - const data = await response.json(); - - // Artificial delay for realism - setTimeout(() => { - appendMessage(data.reply, 'bot'); - }, 500); - } catch (error) { - console.error('Error:', error); - appendMessage("Sorry, something went wrong. Please try again.", 'bot'); + const toastEl = document.getElementById('liveToast'); + if (toastEl) { + const toast = new bootstrap.Toast(toastEl); + if (toastEl.dataset.autoshow === '1') { + toast.show(); } - }); + } + + const roleSelect = document.getElementById('roleSelect'); + if (roleSelect) { + roleSelect.addEventListener('change', () => { + const role = roleSelect.value; + const url = new URL(window.location.href); + url.searchParams.set('role', role); + window.location.href = url.toString(); + }); + } + + const searchInput = document.getElementById('studentSearch'); + const tableRows = document.querySelectorAll('[data-student-row]'); + if (searchInput) { + searchInput.addEventListener('input', () => { + const query = searchInput.value.toLowerCase(); + tableRows.forEach((row) => { + const hay = row.dataset.studentRow || ''; + row.style.display = hay.includes(query) ? '' : 'none'; + }); + }); + } }); diff --git a/attendance.php b/attendance.php new file mode 100644 index 0000000..641db1d --- /dev/null +++ b/attendance.php @@ -0,0 +1,44 @@ +query("SELECT * FROM classes ORDER BY name ASC")->fetchAll(); +?> + + + + + + Absensi Harian — Dashboard Kelas + + + + +
+ +

Pilih Kelas untuk Absensi

+
+ +
+
+
+

Guru:

+ Buka Absensi +
+
+ +
+
+ + diff --git a/attendance_detail.php b/attendance_detail.php new file mode 100644 index 0000000..99aff35 --- /dev/null +++ b/attendance_detail.php @@ -0,0 +1,71 @@ + $status) { + $stmt = $db->prepare("INSERT INTO attendance (student_id, class_id, status, attendance_date) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE status = ?"); + $stmt->execute([(int)$student_id, $class_id, $status, $date, $status]); + } + $message = "Absensi berhasil disimpan untuk tanggal $date."; +} + +$class = $db->prepare("SELECT * FROM classes WHERE id = ?"); +$class->execute([$class_id]); +$classData = $class->fetch(); + +$students = $db->prepare("SELECT s.id, s.full_name, a.status FROM students s LEFT JOIN attendance a ON s.id = a.student_id AND a.attendance_date = ? WHERE s.class_id = ?"); +$students->execute([$date, $class_id]); +$studentList = $students->fetchAll(); +?> + + + + + + Absensi — <?= htmlspecialchars($classData['name'] ?? 'Kelas') ?> + + + + +
+ +

Absensi Kelas:

+ +
+ +
+ + + + + + + + + + + + + + + +
Nama SiswaStatus
+ +
+ +
+
+ + diff --git a/attendance_recap.php b/attendance_recap.php new file mode 100644 index 0000000..b633999 --- /dev/null +++ b/attendance_recap.php @@ -0,0 +1,48 @@ +prepare("SELECT c.name as class_name, + SUM(CASE WHEN a.status = 'hadir' THEN 1 ELSE 0 END) as hadir, + SUM(CASE WHEN a.status = 'sakit' THEN 1 ELSE 0 END) as sakit, + SUM(CASE WHEN a.status = 'izin' THEN 1 ELSE 0 END) as izin, + SUM(CASE WHEN a.status = 'alpa' THEN 1 ELSE 0 END) as alpa + FROM classes c + LEFT JOIN attendance a ON c.id = a.class_id AND a.attendance_date = ? + GROUP BY c.id"); +$recap->execute([$date]); +$data = $recap->fetchAll(); +?> + + + + + + Rekap Absensi — <?= htmlspecialchars($date) ?> + + + + +

Rekap Absensi:

+ + + + + + + + + + + + + + + +
KelasHadirSakitIzinAlpa
+ + diff --git a/db/migrations/001_school_core.sql b/db/migrations/001_school_core.sql new file mode 100644 index 0000000..390c0ef --- /dev/null +++ b/db/migrations/001_school_core.sql @@ -0,0 +1,22 @@ +-- Core schema for School Management MVP slice (idempotent) +CREATE TABLE IF NOT EXISTS classes ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(120) NOT NULL, + grade_level VARCHAR(20) NOT NULL, + homeroom_teacher VARCHAR(120) DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS students ( + id INT AUTO_INCREMENT PRIMARY KEY, + student_code VARCHAR(30) NOT NULL UNIQUE, + full_name VARCHAR(160) NOT NULL, + gender VARCHAR(12) DEFAULT NULL, + class_id INT DEFAULT NULL, + guardian_name VARCHAR(160) DEFAULT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'aktif', + notes TEXT DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX idx_students_class (class_id), + INDEX idx_students_status (status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/db/migrations/002_attendance_module.sql b/db/migrations/002_attendance_module.sql new file mode 100644 index 0000000..903de42 --- /dev/null +++ b/db/migrations/002_attendance_module.sql @@ -0,0 +1,12 @@ +-- Attendance migration +CREATE TABLE IF NOT EXISTS attendance ( + id INT AUTO_INCREMENT PRIMARY KEY, + student_id INT NOT NULL, + class_id INT NOT NULL, + status ENUM('hadir', 'sakit', 'izin', 'alpa') NOT NULL DEFAULT 'alpa', + attendance_date DATE NOT NULL, + recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE, + FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE, + UNIQUE KEY unique_attendance (student_id, attendance_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/includes/db_init.php b/includes/db_init.php new file mode 100644 index 0000000..7b1d223 --- /dev/null +++ b/includes/db_init.php @@ -0,0 +1,51 @@ +exec(" + CREATE TABLE IF NOT EXISTS classes ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(120) NOT NULL, + grade_level VARCHAR(20) NOT NULL, + homeroom_teacher VARCHAR(120) DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + "); + + $db->exec(" + CREATE TABLE IF NOT EXISTS students ( + id INT AUTO_INCREMENT PRIMARY KEY, + student_code VARCHAR(30) NOT NULL UNIQUE, + full_name VARCHAR(160) NOT NULL, + gender VARCHAR(12) DEFAULT NULL, + class_id INT DEFAULT NULL, + guardian_name VARCHAR(160) DEFAULT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'aktif', + notes TEXT DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX idx_students_class (class_id), + INDEX idx_students_status (status) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + "); +} + +function seed_classes(PDO $db): void { + $count = (int)$db->query("SELECT COUNT(*) FROM classes")->fetchColumn(); + if ($count > 0) { + return; + } + $defaults = [ + ['X IPA 1', '10', 'Ibu Fitri'], + ['X IPA 2', '10', 'Bapak Dedi'], + ['XI IPS 1', '11', 'Ibu Rina'], + ['XII IPA 1', '12', 'Bapak Arif'], + ]; + $stmt = $db->prepare("INSERT INTO classes (name, grade_level, homeroom_teacher) VALUES (:name, :grade, :teacher)"); + foreach ($defaults as $row) { + $stmt->execute([ + ':name' => $row[0], + ':grade' => $row[1], + ':teacher' => $row[2], + ]); + } +} diff --git a/index.php b/index.php index 7205f3d..2b95821 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,175 @@ 'Admin Sekolah', + 'guru' => 'Guru', + 'orang_tua' => 'Orang Tua', + 'siswa' => 'Siswa', + 'bendahara' => 'Bendahara', +]; + +$navItems = [ + 'admin' => ['Dashboard', 'Siswa', 'Kelas', 'Absensi', 'Ujian', 'Keuangan', 'Laporan'], + 'guru' => ['Dashboard', 'Jadwal', 'Kelas', 'Absensi', 'Tugas', 'Nilai'], + 'orang_tua' => ['Dashboard', 'Perkembangan', 'Tagihan', 'Pengumuman'], + 'siswa' => ['Dashboard', 'Jadwal', 'Tugas', 'Ujian', 'Nilai'], + 'bendahara' => ['Dashboard', 'Tagihan', 'Pembayaran', 'Rekonsiliasi'], +]; + +$studentCount = (int)$db->query("SELECT COUNT(*) FROM students")->fetchColumn(); +$classCount = (int)$db->query("SELECT COUNT(*) FROM classes")->fetchColumn(); +$latestStudent = $db->query("SELECT full_name, created_at FROM students ORDER BY created_at DESC LIMIT 1")->fetch(); + +$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; +$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; +$projectName = $_SERVER['PROJECT_NAME'] ?? 'School Management System'; ?> - + - New Style - + <?= htmlspecialchars($projectName) ?> — Dashboard - - - + - - - - - + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… +
+ + +
+
+
+
+

Dashboard Operasional Sekolah

+

Pantau data siswa, kelas, dan modul harian dari satu panel. Gunakan menu sesuai hak akses role.

+
+ +
+
+ +
+
+
+
Ringkasan Data
+
+
+
Total Siswa
+
+
+
+
Total Kelas
+
+
+
+
+ Terakhir ditambah: +
+
+
+
+
+
Menu Cepat ()
+
+ + + +
+

Menu ditampilkan sesuai RBAC. Modul yang belum tersedia akan segera ditambahkan.

+
+
+
+ +
+
+
+
+
Master Data Siswa
+
Registrasi, kelas, wali, status
+

Input data siswa baru, lihat daftar, dan detail profil.

+ Buka Modul +
+
+
+
+
+
+
Absensi Harian
+
QR/manual + rekap
+

Tandai hadir/sakit/izin dan lihat ringkasan kelas.

+ Buka Modul + Lihat Rekap +
+
+
+
+
+
+
Keuangan & Tagihan
+
Pembayaran + verifikasi
+

Kelola tagihan SPP dan konfirmasi pembayaran.

+ Buka Modul +
+
+
+
+ +
+
Endpoint API Mobile (Preview)
+
Gunakan endpoint berikut untuk aplikasi Android/iOS hybrid:
+
    +
  • /api/students.php — daftar siswa (GET)
  • +
-
- Page updated: (UTC) + +
+ © EduCore CMS • Role aktif:
+
+ + + diff --git a/student.php b/student.php new file mode 100644 index 0000000..c369cfa --- /dev/null +++ b/student.php @@ -0,0 +1,139 @@ + 0) { + $stmt = $db->prepare(" + SELECT s.*, c.name AS class_name, c.grade_level, c.homeroom_teacher + FROM students s + LEFT JOIN classes c ON c.id = s.class_id + WHERE s.id = :id + LIMIT 1 + "); + $stmt->execute([':id' => $studentId]); + $student = $stmt->fetch(); +} + +$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; +$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; +$projectName = $_SERVER['PROJECT_NAME'] ?? 'School Management System'; +?> + + + + + + <?= htmlspecialchars($projectName) ?> — Detail Siswa + + + + + + + + + + + + + + +
+ + +
+ +
Data siswa tidak ditemukan.
+ +
+
+
+
Profil Siswa
+
+
+
Kode Siswa
+
+
+
+
Status
+ +
+
+
Nama Lengkap
+
+
+
+
Jenis Kelamin
+
+
+
+
Kelas
+
()
+
+
+
Wali Kelas
+
+
+
+
Nama Wali
+
+
+
+
Tanggal Input
+
+
+
+ +
+
Catatan
+
+ +
+
+
+
+
Aksi Cepat
+
+ + + +
+
+ Modul lanjutan (absensi, ujian, keuangan) akan ditambahkan pada iterasi berikutnya. +
+
+
+
+ +
+ +
+ © EduCore CMS • Detail Siswa +
+
+ + + + + diff --git a/students.php b/students.php new file mode 100644 index 0000000..bad33f2 --- /dev/null +++ b/students.php @@ -0,0 +1,236 @@ +prepare("INSERT INTO students (student_code, full_name, gender, class_id, guardian_name, status, notes) + VALUES (:code, :name, :gender, :class_id, :guardian, :status, :notes)"); + $stmt->execute([ + ':code' => $studentCode, + ':name' => $fullName, + ':gender' => $gender !== '' ? $gender : null, + ':class_id' => $classId, + ':guardian' => $guardianName !== '' ? $guardianName : null, + ':status' => $status, + ':notes' => $notes !== '' ? $notes : null, + ]); + header('Location: students.php?role=' . urlencode($role) . '&success=1'); + exit; + } catch (PDOException $e) { + if ((int)$e->getCode() === 23000) { + $errors[] = 'Kode siswa sudah digunakan. Gunakan kode lain.'; + } else { + $errors[] = 'Gagal menyimpan data siswa.'; + } + } + } +} + +$classes = $db->query("SELECT id, name, grade_level FROM classes ORDER BY grade_level, name")->fetchAll(); +$students = $db->query(" + SELECT s.id, s.student_code, s.full_name, s.status, s.created_at, + c.name AS class_name, c.grade_level + FROM students s + LEFT JOIN classes c ON c.id = s.class_id + ORDER BY s.created_at DESC + LIMIT 50 +")->fetchAll(); + +$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; +$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; +$projectName = $_SERVER['PROJECT_NAME'] ?? 'School Management System'; +?> + + + + + + <?= htmlspecialchars($projectName) ?> — Data Siswa + + + + + + + + + + + + + + +
+ + +
+
+
+
+
Registrasi Siswa Baru
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
    + +
  • + +
+
+ +
+
+
+
+
+
Daftar Siswa Terbaru
+ +
+ +
Belum ada siswa yang terdaftar. Tambahkan data pertama di form.
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
KodeNamaKelasStatus
+ Detail +
+
+ +
+
+
+
+ +
+ © EduCore CMS • Modul Data Siswa +
+
+ +
+ +
+ + + + +