Main Logo
  • Home
  • About
  • Kursus
    • Paket Kursus
    • Roadmap Profesi
  • Elearning
  • Blog
Daftar
Main Logo
  • Home
  • About
  • Kursus
    • Paket Kursus
    • Roadmap Profesi
  • Elearning
  • Blog

Tutorial Lengkap Membuat Website Absensi dan Aktivitas Harian Menggunakan PHP + MySQL dengan Laragon (Lokal)

  • July 23, 2025
  • oleh Edusoft Center

Pendahuluan

Dalam era digital saat ini, pengelolaan presensi dan aktivitas harian secara manual mulai ditinggalkan. Sekolah, instansi, maupun lembaga pelatihan kini banyak beralih ke sistem berbasis web untuk meningkatkan efisiensi, transparansi, dan akurasi data. Artikel ini akan membahas langkah demi langkah bagaimana cara membuat website sederhana untuk absensi dan aktivitas harian (daily activity) menggunakan PHP dan MySQL, yang di-hosting secara lokal menggunakan Laragon.

Tidak hanya untuk pengembang berpengalaman, tutorial ini ditujukan juga bagi pemula yang ingin belajar membuat sistem presensi digital dari nol.

Daftar Isi

  1. Apa Itu Laragon?
  2. Persiapan Alat dan Bahan
  3. Membuat Database dan Tabel MySQL
  4. Struktur Folder dan File Project
  5. Membuat Halaman Login
  6. Dashboard Siswa dan Pembimbing
  7. Fitur Input Presensi
  8. Fitur Input Aktivitas Harian
  9. Validasi Aktivitas oleh Pembimbing
  10. Menambahkan Fitur Logout
  11. Tampilan Desain dengan Bootstrap
  12. Tips Keamanan Sederhana
  13. Kesimpulan

1. Apa Itu Laragon?

Laragon adalah sebuah portable local server untuk Windows yang memudahkan Anda membuat dan mengelola aplikasi berbasis PHP, MySQL, Apache, dan lain-lain. Laragon sangat ringan, mudah diinstal, dan siap pakai.

Keunggulan Laragon:

  • Instalasi mudah dan cepat.
  • Mendukung PHP versi terbaru.
  • Tersedia phpMyAdmin bawaan.
  • Mendukung auto virtual host.

2. Persiapan Alat dan Bahan

Aplikasi yang dibutuhkan:

  • Laragon
  • Teks editor (VS Code / Sublime / Notepad++)
  • Browser (Chrome/Firefox)

Langkah Instalasi Laragon:

  1. Unduh dan install Laragon dari situs resmi.
  2. Buka Laragon, klik “Start All”.
  3. Klik kanan > www > “Open Folder” untuk membuka direktori root project Anda.
  4. Klik “Menu > Quick App > Blank” untuk membuat folder project baru.

3. Membuat Database dan Tabel MySQL

Akses phpMyAdmin:

Buka browser dan kunjungi:

http://localhost/phpmyadmin

Buat Database Baru:

CREATE DATABASE absensi;

Buat Tabel users:

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(50),
  password VARCHAR(255),
  role ENUM('siswa', 'pembimbing')
);

Tabel presensi:

CREATE TABLE presensi (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT,
  tanggal DATE,
  jam DATETIME,
  status ENUM('hadir', 'tidak hadir'),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

Tabel aktivitas:

CREATE TABLE aktivitas (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT,
  tanggal DATE,
  aktivitas TEXT,
  status_validasi ENUM('belum divalidasi', 'disetujui', 'ditolak'),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

4. Struktur Folder dan File Project

magang-edusoft/
├── config/
│ └── koneksi.php
├── auth/
│ ├── login.php
│ └── logout.php
├── siswa/
│ ├── dashboard.php
│ ├── presensi.php
│ └── aktivitas.php
├── pembimbing/
│ └── dashboard.php
├── proses/
│ └── proses_validasi.php

5. Membuat Halaman Login

File: login.php

php
<?php
session_start();
include '../config/koneksi.php';

$pesan = '';
if (isset($_POST['login'])) {
  $username = $_POST['username'];
  $password = $_POST['password'];

  $query = mysqli_query($koneksi, "SELECT * FROM users WHERE username='$username'");
  $data = mysqli_fetch_assoc($query);

  if ($data && password_verify($password, $data['password'])) {
    $_SESSION['user_id'] = $data['id'];
    $_SESSION['role'] = $data['role'];

    if ($data['role'] == 'siswa') {
      header("Location: ../siswa/dashboard.php");
    } else {
      header("Location: ../pembimbing/dashboard.php");
    }
    exit;
  } else {
    $pesan = "Login gagal! Username atau password salah.";
  }
}
?>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.login-container {
  background: white;
  padding: 40px;
  border-radius: 12px;
  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
  width: 100%;
  max-width: 400px;
  position: relative;
}

.login-container::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg, #667eea, #764ba2);
  border-radius: 12px 12px 0 0;
}

h2 {
  text-align: center;
  color: #333;
  margin-bottom: 30px;
  font-size: 28px;
  font-weight: 600;
}

.pesan-error {
  background: #fee;
  color: #c33;
  padding: 12px;
  border-radius: 8px;
  margin-bottom: 20px;
  border: 1px solid #fcc;
  font-size: 14px;
  text-align: center;
}

form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

input[type="text"], 
input[type="password"],
input[name="username"],
input[name="password"] {
  width: 100%;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 16px;
  transition: all 0.3s ease;
  background: #fff;
  box-sizing: border-box;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
}

input[type="text"]:focus, 
input[type="password"]:focus,
input[name="username"]:focus,
input[name="password"]:focus {
  outline: none;
  border-color: #667eea;
  background: white;
  box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
}

input[type="text"]::placeholder, 
input[type="password"]::placeholder,
input[name="username"]::placeholder,
input[name="password"]::placeholder {
  color: #999;
}

button {
  padding: 15px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

button:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}

button:active {
  transform: translateY(0);
}

p {
  text-align: center;
  margin-top: 25px;
  color: #666;
  font-size: 14px;
}

p a {
  color: #667eea;
  text-decoration: none;
  font-weight: 500;
  transition: color 0.3s ease;
}

p a:hover {
  color: #764ba2;
  text-decoration: underline;
}

/* Password container for eye toggle */
.password-container {
  position: relative;
  width: 100%;
}

.password-container input {
  width: 100%;
  padding-right: 45px;
}

/* Eye toggle icon hover effect */
#toggleIcon {
  transition: opacity 0.3s ease;
}

#toggleIcon:hover {
  opacity: 0.7;
}

/* Responsive design */
@media (max-width: 480px) {
  .login-container {
    padding: 30px 20px;
    margin: 10px;
  }
  
  h2 {
    font-size: 24px;
  }
  
  input[type="text"], 
  input[type="password"],
  input[name="username"],
  input[name="password"] {
    padding: 12px;
    font-size: 14px;
  }
  
  button {
    padding: 12px;
    font-size: 14px;
  }
}

/* Loading animation for button */
button:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

/* Smooth animations */
.login-container {
  animation: slideUp 0.5s ease-out;
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>

<div class="login-container">
  <h2>Login</h2>
  <?php if ($pesan): ?>
    <div class="pesan-error"><?= $pesan ?></div>
  <?php endif; ?>
  <form method="POST">
    <input name="username" placeholder="Username" required>
    <div style="position:relative;">
      <input id="pw" name="password" type="password" placeholder="Password" required style="padding-right:38px;">
      <span onclick="togglePw()" id="toggleIcon" style="position:absolute;top:50%;right:10px;transform:translateY(-50%);cursor:pointer;">
        <!-- Mata terbuka (default) -->
        <svg id="eyeOpen" xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="none" viewBox="0 0 24 24"><path stroke="#888" stroke-width="2" d="M1.5 12S5.5 5.5 12 5.5 22.5 12 22.5 12 18.5 18.5 12 18.5 1.5 12 1.5 12Z"/><circle cx="12" cy="12" r="3.5" stroke="#888" stroke-width="2"/></svg>
        <!-- Mata terpejam (hidden) -->
        <svg id="eyeClosed" xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="none" viewBox="0 0 24 24" style="display:none;"><path stroke="#888" stroke-width="2" d="M3 3l18 18M1.5 12S5.5 5.5 12 5.5c2.2 0 4.1.6 5.7 1.5M22.5 12S18.5 18.5 12 18.5c-2.2 0-4.1-.6-5.7-1.5"/><circle cx="12" cy="12" r="3.5" stroke="#888" stroke-width="2"/></svg>
      </span>
    </div>
    <button name="login">Login</button>
  </form>
  <p>Belum punya akun? <a href="register.php">Daftar di sini</a></p>
</div>

<script>
function togglePw() {
  const pw = document.getElementById('pw');
  const eyeOpen = document.getElementById('eyeOpen');
  const eyeClosed = document.getElementById('eyeClosed');
  if (pw.type === "password") {
    pw.type = "text";
    eyeOpen.style.display = "none";
    eyeClosed.style.display = "inline";
  } else {
    pw.type = "password";
    eyeOpen.style.display = "inline";
    eyeClosed.style.display = "none";
  }
}
</script>

File: index.php

php
<?php
header("Location: auth/login.php");
exit;

File: Registrasi.php

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Registrasi Akun</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px;
        }

        .register-container {
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(10px);
            max-width: 420px;
            width: 100%;
            padding: 40px 35px;
            border-radius: 20px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
            position: relative;
            overflow: hidden;
        }

        .register-container::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 4px;
            background: linear-gradient(90deg, #667eea, #764ba2);
        }

        .register-container h2 {
            text-align: center;
            margin-bottom: 30px;
            color: #2d3a4b;
            font-size: 28px;
            font-weight: 600;
            position: relative;
        }

        .register-container h2::after {
            content: '';
            position: absolute;
            bottom: -8px;
            left: 50%;
            transform: translateX(-50%);
            width: 50px;
            height: 3px;
            background: linear-gradient(90deg, #667eea, #764ba2);
            border-radius: 2px;
        }

        .form-group {
            margin-bottom: 20px;
            position: relative;
        }

        .register-container input,
        .register-container select {
            width: 100%;
            padding: 15px 20px;
            border: 2px solid #e1e5e9;
            border-radius: 12px;
            font-size: 16px;
            background: #f8f9fa;
            transition: all 0.3s ease;
            color: #2d3a4b;
        }

        .register-container input:focus,
        .register-container select:focus {
            border-color: #667eea;
            outline: none;
            background: #fff;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
            transform: translateY(-2px);
        }

        .register-container input::placeholder {
            color: #9ca3af;
            font-weight: 400;
        }

        .register-container select {
            cursor: pointer;
        }

        .register-container select option {
            padding: 10px;
            background: #fff;
            color: #2d3a4b;
        }

        .register-container button {
            width: 100%;
            padding: 16px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: #fff;
            border: none;
            border-radius: 12px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            text-transform: uppercase;
            letter-spacing: 1px;
            margin-top: 10px;
        }

        .register-container button:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
        }

        .register-container button:active {
            transform: translateY(0);
        }

        .register-container p {
            text-align: center;
            margin-top: 25px;
            font-size: 14px;
            color: #6b7280;
        }

        .register-container a {
            color: #667eea;
            text-decoration: none;
            font-weight: 600;
            transition: all 0.3s ease;
        }

        .register-container a:hover {
            color: #764ba2;
            text-decoration: underline;
        }

        .success-message {
            background: linear-gradient(135deg, #10b981, #059669);
            color: white;
            padding: 15px 20px;
            border-radius: 12px;
            margin-bottom: 20px;
            text-align: center;
            font-weight: 500;
            box-shadow: 0 4px 15px rgba(16, 185, 129, 0.2);
            border-left: 4px solid #047857;
            animation: slideInDown 0.5s ease-out;
        }

        .error-message {
            background: linear-gradient(135deg, #ef4444, #dc2626);
            color: white;
            padding: 15px 20px;
            border-radius: 12px;
            margin-bottom: 20px;
            text-align: center;
            font-weight: 500;
            box-shadow: 0 4px 15px rgba(239, 68, 68, 0.2);
            border-left: 4px solid #b91c1c;
            animation: slideInDown 0.5s ease-out;
        }

        .info-message {
            background: linear-gradient(135deg, #3b82f6, #1d4ed8);
            color: white;
            padding: 15px 20px;
            border-radius: 12px;
            margin-bottom: 20px;
            text-align: center;
            font-weight: 500;
            box-shadow: 0 4px 15px rgba(59, 130, 246, 0.2);
            border-left: 4px solid #1e40af;
            animation: slideInDown 0.5s ease-out;
        }

        .notification {
            position: relative;
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .notification::before {
            content: '';
            width: 20px;
            height: 20px;
            background-size: contain;
            background-repeat: no-repeat;
            flex-shrink: 0;
        }

        .success-message.notification::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='white'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'/%3E%3C/svg%3E");
        }

        .error-message.notification::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='white'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'/%3E%3C/svg%3E");
        }

        .info-message.notification::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='white'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'/%3E%3C/svg%3E");
        }

        .notification a {
            color: white;
            text-decoration: underline;
            font-weight: 600;
            transition: opacity 0.3s ease;
        }

        .notification a:hover {
            opacity: 0.8;
        }

        @keyframes slideInDown {
            from {
                transform: translateY(-20px);
                opacity: 0;
            }
            to {
                transform: translateY(0);
                opacity: 1;
            }
        }

        /* Close button for notifications */
        .notification-close {
            position: absolute;
            top: 10px;
            right: 15px;
            background: none;
            border: none;
            color: white;
            cursor: pointer;
            font-size: 18px;
            width: 25px;
            height: 25px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            transition: background-color 0.3s ease;
        }

        .notification-close:hover {
            background-color: rgba(255, 255, 255, 0.2);
        }

        /* Responsive Design */
        @media (max-width: 480px) {
            .register-container {
                padding: 30px 25px;
                margin: 10px;
            }
            
            .register-container h2 {
                font-size: 24px;
            }
        }

        /* Loading animation for button */
        .loading {
            position: relative;
            color: transparent;
        }

        .loading::after {
            content: '';
            position: absolute;
            width: 20px;
            height: 20px;
            top: 50%;
            left: 50%;
            margin-left: -10px;
            margin-top: -10px;
            border: 2px solid #ffffff;
            border-radius: 50%;
            border-top-color: transparent;
            animation: spin 1s linear infinite;
        }

        @keyframes spin {
            to {
                transform: rotate(360deg);
            }
        }

        /* Input icons */
        .form-group {
            position: relative;
        }

        .form-group::before {
            content: '';
            position: absolute;
            left: 15px;
            top: 50%;
            transform: translateY(-50%);
            width: 20px;
            height: 20px;
            background-size: contain;
            background-repeat: no-repeat;
            z-index: 1;
            opacity: 0.5;
        }

        .form-group.name::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z'/%3E%3C/svg%3E");
        }

        .form-group.username::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M15 7a2 2 0 012 2m0 0a2 2 0 012 2m-2-2a2 2 0 00-2 2m2-2V5a2 2 0 00-2-2H9a2 2 0 00-2 2v2m6 0H9m6 0a2 2 0 012 2m0 0a2 2 0 01-2 2H9a2 2 0 01-2-2m0 0a2 2 0 012-2h6a2 2 0 012 2z'/%3E%3C/svg%3E");
        }

        .form-group.password::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z'/%3E%3C/svg%3E");
        }

        /* Password toggle eye icon */
        .password-toggle {
            position: absolute;
            right: 15px;
            top: 50%;
            transform: translateY(-50%);
            cursor: pointer;
            width: 22px;
            height: 22px;
            color: #9ca3af;
            transition: color 0.3s ease;
            z-index: 10;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .password-toggle:hover {
            color: #667eea;
        }

        .password-toggle svg {
            width: 100%;
            height: 100%;
            stroke: currentColor;
        }

        /* Adjust padding for password input to make room for eye icon */
        .form-group.password input {
            padding-right: 55px;
        }

        .form-group.role::before {
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z'/%3E%3C/svg%3E");
        }

        /* Custom Dropdown Styles */
        .custom-dropdown {
            position: relative;
            width: 100%;
        }

        .dropdown-selected {
            width: 100%;
            padding: 15px 50px 15px 50px;
            border: 2px solid #e1e5e9;
            border-radius: 12px;
            font-size: 16px;
            background: #f8f9fa;
            cursor: pointer;
            color: #9ca3af;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: space-between;
            position: relative;
        }

        .dropdown-selected.active {
            border-color: #667eea;
            background: #fff;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
            transform: translateY(-2px);
            color: #2d3a4b;
        }

        .dropdown-selected.has-value {
            color: #2d3a4b;
        }

        .dropdown-arrow {
            width: 20px;
            height: 20px;
            color: #9ca3af;
            transition: transform 0.3s ease, color 0.3s ease;
        }

        .dropdown-selected.active .dropdown-arrow {
            transform: rotate(180deg);
            color: #667eea;
        }

        .dropdown-options {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            background: #fff;
            border: 2px solid #667eea;
            border-top: none;
            border-radius: 0 0 12px 12px;
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
            z-index: 1000;
            max-height: 0;
            overflow: hidden;
            opacity: 0;
            transform: translateY(-10px);
            transition: all 0.3s ease;
        }

        .dropdown-options.show {
            max-height: 200px;
            opacity: 1;
            transform: translateY(0);
        }

        .dropdown-option {
            padding: 15px 20px;
            cursor: pointer;
            transition: all 0.3s ease;
            color: #2d3a4b;
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .dropdown-option:hover {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: #fff;
        }

        .dropdown-option:first-child {
            border-top: 1px solid #e1e5e9;
        }

        .dropdown-option:last-child {
            border-radius: 0 0 10px 10px;
        }

        .dropdown-option-icon {
            width: 20px;
            height: 20px;
            opacity: 0.7;
        }

        .dropdown-option:hover .dropdown-option-icon {
            opacity: 1;
        }

        /* Hide original select */
        .form-group.role select {
            display: none;
        }

        .form-group input,
        .form-group select {
            padding-left: 50px;
        }

        /* Specific padding for password input */
        .form-group.password input {
            padding-left: 50px;
            padding-right: 55px;
        }
    </style>
</head>
<body>
    <div class="register-container">
        <h2>Registrasi Akun</h2>
        
        <!-- Example notifications - uncomment and use in your PHP -->
        <!-- Success Message -->
        <!-- <div class="success-message notification">
            <span>Registrasi berhasil! Silakan <a href="login.php">login</a>.</span>
            <button class="notification-close" onclick="this.parentElement.style.display='none'">&times;</button>
        </div> -->
        
        <!-- Error Message -->
        <!-- <div class="error-message notification">
            <span>Username sudah terdaftar!</span>
            <button class="notification-close" onclick="this.parentElement.style.display='none'">&times;</button>
        </div> -->
        
        <!-- Info Message -->
        <!-- <div class="info-message notification">
            <span>Registrasi gagal!</span>
            <button class="notification-close" onclick="this.parentElement.style.display='none'">&times;</button>
        </div> -->
        
        <form method="POST">
            <div class="form-group name">
                <input name="nama" placeholder="Nama Lengkap" required>
            </div>
            
            <div class="form-group username">
                <input name="username" placeholder="Username" required>
            </div>
            
            <div class="form-group password">
                <input name="password" type="password" placeholder="Password" required id="password">
                <span class="password-toggle" onclick="togglePassword()">
                    <svg id="eyeIcon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
                    </svg>
                </span>
            </div>
            
            <div class="form-group role">
                <select name="role" required id="roleSelect">
                    <option value="">Pilih Role</option>
                    <option value="siswa">Siswa</option>
                    <option value="pembimbing">Pembimbing</option>
                </select>
                
                <div class="custom-dropdown">
                    <div class="dropdown-selected" onclick="toggleDropdown()">
                        <span id="selectedRole">Pilih Role</span>
                        <svg class="dropdown-arrow" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
                        </svg>
                    </div>
                    
                    <div class="dropdown-options" id="dropdownOptions">
                        <div class="dropdown-option" onclick="selectRole('siswa')">
                            <svg class="dropdown-option-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
                            </svg>
                            <span>Siswa</span>
                        </div>
                        <div class="dropdown-option" onclick="selectRole('pembimbing')">
                            <svg class="dropdown-option-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
                            </svg>
                            <span>Pembimbing</span>
                        </div>
                    </div>
                </div>
            </div>
            
            <button name="register" type="submit">Register</button>
        </form>
        
        <p>Sudah punya akun? <a href="login.php">Login di sini</a></p>
    </div>

    <script>
        // Custom dropdown functionality
        function toggleDropdown() {
            const dropdownOptions = document.getElementById('dropdownOptions');
            const selectedElement = document.querySelector('.dropdown-selected');
            
            dropdownOptions.classList.toggle('show');
            selectedElement.classList.toggle('active');
        }

        function selectRole(value) {
            const selectedRole = document.getElementById('selectedRole');
            const roleSelect = document.getElementById('roleSelect');
            const selectedElement = document.querySelector('.dropdown-selected');
            
            // Update display text
            selectedRole.textContent = value === 'siswa' ? 'Siswa' : 'Pembimbing';
            
            // Update hidden select value
            roleSelect.value = value;
            
            // Add has-value class for styling
            selectedElement.classList.add('has-value');
            
            // Close dropdown
            document.getElementById('dropdownOptions').classList.remove('show');
            selectedElement.classList.remove('active');
        }

        // Close dropdown when clicking outside
        document.addEventListener('click', function(e) {
            if (!e.target.closest('.custom-dropdown')) {
                document.getElementById('dropdownOptions').classList.remove('show');
                document.querySelector('.dropdown-selected').classList.remove('active');
            }
        });

        // Password toggle functionality
        function togglePassword() {
            const passwordInput = document.getElementById('password');
            const eyeIcon = document.getElementById('eyeIcon');
            
            if (passwordInput.type === 'password') {
                passwordInput.type = 'text';
                eyeIcon.innerHTML = `
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21" />
                `;
            } else {
                passwordInput.type = 'password';
                eyeIcon.innerHTML = `
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
                `;
            }
        }

        // Add loading animation on form submit
        document.querySelector('form').addEventListener('submit', function(e) {
            const button = document.querySelector('button[name="register"]');
            button.classList.add('loading');
            button.disabled = true;
        });

        // Add smooth focus transitions
        document.querySelectorAll('input, select').forEach(element => {
            element.addEventListener('focus', function() {
                this.parentElement.style.transform = 'scale(1.02)';
            });
            
            element.addEventListener('blur', function() {
                this.parentElement.style.transform = 'scale(1)';
            });
        });
    </script>
</body>
</html>

6. Dashboard Siswa dan Pembimbing

siswa/dashboard.php

php
<?php
session_start();
include '../config/koneksi.php';

if (!isset($_SESSION['user_id']) || $_SESSION['role'] != 'siswa') {
  header("Location: ../auth/login.php");
  exit;
}

$user_id = $_SESSION['user_id'];
$tanggal_hari_ini = date("Y-m-d");

// Ambil presensi hari ini
$q_presensi = mysqli_query($koneksi, "SELECT * FROM presensi WHERE user_id='$user_id' AND tanggal='$tanggal_hari_ini'");
$presensi = mysqli_fetch_assoc($q_presensi);

// Ambil aktivitas hari ini
$q_aktivitas = mysqli_query($koneksi, "SELECT * FROM aktivitas WHERE user_id='$user_id' AND tanggal='$tanggal_hari_ini'");
$aktivitas = mysqli_fetch_assoc($q_aktivitas);

// Data untuk profile
$q_user = mysqli_query($koneksi, "SELECT * FROM users WHERE id='$user_id'");
$user = mysqli_fetch_assoc($q_user);

// Proses update profile
if (isset($_POST['update_profile'])) {
    $new_nama = mysqli_real_escape_string($koneksi, $_POST['edit_nama']);
    $new_username = mysqli_real_escape_string($koneksi, $_POST['edit_username']);
    $new_kelas = mysqli_real_escape_string($koneksi, $_POST['edit_kelas']);
    $new_jurusan = mysqli_real_escape_string($koneksi, $_POST['edit_jurusan']);
    
    // Validasi input
    if (empty($new_nama) || empty($new_username) || empty($new_kelas) || empty($new_jurusan)) {
        $error_message = "Semua field harus diisi!";
    } else {
        // Cek apakah username sudah digunakan user lain
        $check_username = mysqli_query($koneksi, "SELECT id FROM users WHERE username='$new_username' AND id != '$user_id'");
        if (mysqli_num_rows($check_username) > 0) {
            $error_message = "Username sudah digunakan oleh user lain!";
        } else {
            // Update data
            $update = mysqli_query($koneksi, "UPDATE users SET nama='$new_nama', username='$new_username', kelas='$new_kelas', jurusan='$new_jurusan' WHERE id='$user_id'");
            
            if ($update) {
                $_SESSION['username'] = $new_username;
                $success_message = "Profil berhasil diupdate!";
                
                // Refresh data user
                $q_user = mysqli_query($koneksi, "SELECT * FROM users WHERE id='$user_id'");
                $user = mysqli_fetch_assoc($q_user);
            } else {
                $error_message = "Gagal update profil! " . mysqli_error($koneksi);
            }
        }
    }
}

// Cek kelengkapan data profil
$notif_incomplete_profile = false;
if (
    empty($user['nama']) ||
    empty($user['username']) ||
    empty($user['kelas']) ||
    empty($user['jurusan'])
) {
    $notif_incomplete_profile = true;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard Siswa</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #e0e7ff 0%, #f4f6fb 100%);
            min-height: 100vh;
        }
        .container { display: flex; min-height: 100vh; }
        .sidebar {
            width: 280px;
            background: linear-gradient(180deg, #4f8cff 0%, #3b82f6 100%);
            color: white;
            padding: 0;
            box-shadow: 4px 0 15px rgba(0, 0, 0, 0.1);
            position: fixed;
            height: 100vh;
            overflow-y: auto;
            transition: transform 0.3s ease;
        }
        .sidebar-header {
            padding: 30px 25px 25px 25px;
            background: rgba(255, 255, 255, 0.1);
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
        }
        .sidebar-header h2 { font-size: 1.4rem; margin-bottom: 8px; color: white; }
        .sidebar-header p { font-size: 0.9rem; opacity: 0.8; color: white; }
        .sidebar-menu { padding: 20px 0; }
        .menu-item {
            display: flex;
            align-items: center;
            padding: 15px 25px;
            color: white;
            text-decoration: none;
            transition: all 0.3s ease;
            border-left: 4px solid transparent;
            cursor: pointer;
        }
        .menu-item:hover {
            background: rgba(255, 255, 255, 0.1);
            border-left-color: white;
        }
        .menu-item.active {
            background: rgba(255, 255, 255, 0.15);
            border-left-color: white;
            font-weight: 600;
        }
        .menu-item .icon { margin-right: 15px; font-size: 1.2rem; width: 24px; text-align: center; }
        .logout-item {
            position: absolute;
            bottom: 20px;
            width: 100%;
            padding: 15px 25px;
            background: rgba(239, 68, 68, 0.2);
            border-top: 1px solid rgba(255, 255, 255, 0.1);
        }
        .logout-item:hover { background: rgba(239, 68, 68, 0.3); }
        .main-content {
            flex: 1;
            margin-left: 280px;
            padding: 30px;
            transition: margin-left 0.3s ease;
        }
        .content-header {
            background: white;
            padding: 25px 30px;
            border-radius: 15px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            margin-bottom: 25px;
        }
        .content-header h1 { color: #2d3a4b; font-size: 2rem; margin-bottom: 8px; }
        .content-header p { color: #64748b; font-size: 1rem; }
        .content-section { display: none; animation: fadeIn 0.5s ease; }
        .content-section.active { display: block; }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .card {
            background: white;
            border-radius: 15px;
            padding: 25px;
            margin-bottom: 20px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            border: 1px solid rgba(79, 140, 255, 0.1);
        }
        .card h3 { color: #2d3a4b; margin-bottom: 15px; font-size: 1.3rem; display: flex; align-items: center; }
        .card h3 .icon { margin-right: 10px; color: #4f8cff; }
        .info-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 12px 0;
            border-bottom: 1px solid #f1f5f9;
        }
        .info-item:last-child { border-bottom: none; }
        .info-label { color: #64748b; font-weight: 500; }
        .info-value { color: #2d3a4b; font-weight: 600; }
        .status-badge {
            padding: 6px 12px;
            border-radius: 20px;
            font-size: 0.85rem;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }
        .status-valid { background: #dcfce7; color: #166534; }
        .status-invalid { background: #fee2e2; color: #dc2626; }
        .status-pending { background: #fef3c7; color: #d97706; }
        .btn {
            display: inline-block;
            padding: 12px 24px;
            background: #4f8cff;
            color: white;
            text-decoration: none;
            border-radius: 8px;
            font-weight: 500;
            transition: all 0.3s ease;
            border: none;
            cursor: pointer;
            margin-right: 10px;
        }
        .btn:hover {
            background: #3b82f6;
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(79, 140, 255, 0.3);
        }
        .btn-outline { 
            background: transparent; 
            color: #4f8cff; 
            border: 2px solid #4f8cff; 
        }
        .btn-outline:hover { 
            background: #4f8cff; 
            color: white; 
        }
        .btn-success { 
            background: #22c55e; 
        }
        .btn-success:hover { 
            background: #16a34a; 
        }
        .btn-warning { 
            background: #f59e0b; 
        }
        .btn-warning:hover { 
            background: #d97706; 
        }
        .btn-danger { 
            background: #ef4444; 
        }
        .btn-danger:hover { 
            background: #dc2626; 
        }
        .form-container {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border-radius: 15px;
            padding: 30px;
            margin-bottom: 20px;
        }
        .form-group { 
            margin-bottom: 20px; 
        }
        .form-group label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
            color: #2d3a4b;
        }
        .form-group input, .form-group textarea {
            width: 100%;
            padding: 12px;
            border: 2px solid #e2e8f0;
            border-radius: 8px;
            background: white;
            color: #2d3a4b;
            font-size: 1rem;
            font-family: inherit;
        }
        .form-group input:focus, .form-group textarea:focus {
            outline: none;
            border-color: #4f8cff;
            box-shadow: 0 0 0 3px rgba(79, 140, 255, 0.1);
        }
        .form-group input::placeholder, .form-group textarea::placeholder { 
            color: #94a3b8; 
        }
        .alert {
            padding: 15px;
            margin-bottom: 20px;
            border-radius: 8px;
            font-weight: 500;
        }
        .alert-success {
            background: #dcfce7;
            color: #166534;
            border: 1px solid #bbf7d0;
        }
        .alert-error {
            background: #fee2e2;
            color: #dc2626;
            border: 1px solid #fecaca;
        }
        @media (max-width: 768px) {
            .sidebar { transform: translateX(-100%); z-index: 1000; }
            .sidebar.active { transform: translateX(0); }
            .main-content { margin-left: 0; padding: 20px; }
            .mobile-toggle {
                display: block;
                position: fixed;
                top: 20px;
                left: 20px;
                z-index: 1001;
                background: #4f8cff;
                color: white;
                border: none;
                padding: 10px;
                border-radius: 5px;
                cursor: pointer;
            }
        }
        .mobile-toggle { display: none; }
        .overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            z-index: 999;
        }
        .overlay.active { display: block; }
        .floating-alert {
            position: fixed;
            top: 20px;
            right: 20px;
            background:rgb(245, 85, 11);
            color: white;
            padding: 8px 18px;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            z-index: 1000;
            display: flex;
            align-items: center;
            gap: 10px;
            font-weight: 500;
            animation: slideIn 0.5s ease-out, fadeOut 0.5s ease-in 4.5s forwards;
        }
        @keyframes slideIn {
            from { opacity: 0; transform: translateY(-10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        @keyframes fadeOut {
            from { opacity: 1; }
            to { opacity: 0; }
        }
    </style>
</head>
<body>
    <button class="mobile-toggle" onclick="toggleSidebar()">☰</button>
    <div class="overlay" onclick="toggleSidebar()"></div>
    <div class="container">
        <!-- Sidebar -->
        <div class="sidebar" id="sidebar">
            <div class="sidebar-header">
                <h2>📚 Portal Siswa</h2>
                <p>Selamat datang, <?= htmlspecialchars($user['nama'] ?? 'Siswa'); ?>!</p>
            </div>
            <div class="sidebar-menu">
                <a href="#" class="menu-item active" onclick="showSection('dashboard', event)">
                    <span class="icon">🏠</span>
                    Dashboard
                </a>
                <a href="#" class="menu-item" onclick="showSection('presensi', event)">
                    <span class="icon">📝</span>
                    Presensi
                </a>
                <a href="#" class="menu-item" onclick="showSection('aktivitas', event)">
                    <span class="icon">📋</span>
                    Aktivitas
                </a>
                <a href="#" class="menu-item" onclick="showSection('riwayat', event)">
                    <span class="icon">📊</span>
                    Riwayat
                </a>
                <a href="#" class="menu-item" onclick="showSection('profile', event)">
                    <span class="icon">👤</span>
                    Profile
                </a>
            </div>
            <div class="logout-item">
                <a href="#" class="menu-item" onclick="logout()">
                    <span class="icon">🚪</span>
                    Logout
                </a>
            </div>
        </div>
        
        <!-- Main Content -->
        <div class="main-content">
            <!-- Dashboard Section -->
            <div id="dashboard" class="content-section active">
                <div class="content-header">
                    <h1>Dashboard</h1>
                    <p>Ringkasan aktivitas hari ini - <span id="currentDate"></span></p>
                </div>
                <div class="card">
                    <h3><span class="icon">📅</span>Presensi Hari Ini</h3>
                    <div class="info-item">
                        <span class="info-label">Jam Masuk:</span>
                        <span class="info-value" id="jam-masuk"><?= $presensi && $presensi['jam_masuk'] ? htmlspecialchars($presensi['jam_masuk']) : '-' ?></span>
                    </div>
                    <div class="info-item">
                        <span class="info-label">Jam Keluar:</span>
                        <span class="info-value" id="jam-keluar"><?= $presensi && $presensi['jam_keluar'] ? htmlspecialchars($presensi['jam_keluar']) : '-' ?></span>
                    </div>
                    <div class="info-item">
                        <span class="info-label">Status:</span>
                        <?php if ($presensi && $presensi['jam_masuk']): ?>
                            <span class="status-badge status-valid">Hadir</span>
                        <?php else: ?>
                            <span class="status-badge status-invalid">Belum Hadir</span>
                        <?php endif; ?>
                    </div>
                </div>
                <div class="card">
                    <h3><span class="icon">📋</span>Aktivitas Hari Ini</h3>
                    <div class="info-item">
                        <span class="info-label">Deskripsi:</span>
                        <span class="info-value"><?= $aktivitas ? htmlspecialchars($aktivitas['deskripsi']) : '-' ?></span>
                    </div>
                    <div class="info-item">
                        <span class="info-label">Status Validasi:</span>
                        <?php if ($aktivitas): ?>
                            <?php if ($aktivitas['status_validasi'] == 'Valid'): ?>
                                <span class="status-badge status-valid">Valid</span>
                            <?php elseif ($aktivitas['status_validasi'] == 'pending'): ?>
                                <span class="status-badge status-pending">Menunggu</span>
                            <?php else: ?>
                                <span class="status-badge status-invalid"><?= htmlspecialchars($aktivitas['status_validasi']) ?></span>
                            <?php endif; ?>
                        <?php else: ?>
                            <span class="status-badge status-invalid">Belum Ada</span>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
            
            <!-- Presensi Section -->
            <div id="presensi" class="content-section">
                <div class="content-header">
                    <h1>Presensi</h1>
                    <p>Kelola presensi harian Anda</p>
                </div>
                <div class="form-container">
                    <h3 style="margin-bottom: 20px; color: white;">🌅 Presensi Masuk</h3>
                    <form method="POST" action="presensi.php">
                        <div class="form-group">
                            <label for="jam_masuk">Jam Masuk:</label>
                            <input type="time" id="jam_masuk" name="jam_masuk" required>
                        </div>
                        <button type="submit" name="masuk" class="btn btn-success">Absen Masuk</button>
                    </form>
                </div>
                <div class="form-container" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);">
                    <h3 style="margin-bottom: 20px; color: white;">🌇 Presensi Keluar</h3>
                    <form method="POST" action="presensi.php">
                        <div class="form-group">
                            <label for="jam_keluar">Jam Keluar:</label>
                            <input type="time" id="jam_keluar" name="jam_keluar" required>
                        </div>
                        <button type="submit" name="keluar" class="btn btn-warning">Absen Keluar</button>
                    </form>
                </div>
            </div>
            
            <!-- Aktivitas Section -->
            <div id="aktivitas" class="content-section">
                <div class="content-header">
                    <h1>Aktivitas</h1>
                    <p>Catat aktivitas pembelajaran Anda</p>
                </div>
                <div class="card">
                    <h3><span class="icon">📝</span>Input Aktivitas</h3>
                    <form method="POST" action="aktivitas.php">
                        <div class="form-group">
                            <label for="deskripsi">Deskripsi Aktivitas:</label>
                            <textarea id="deskripsi" name="deskripsi" rows="4" placeholder="Masukkan deskripsi aktivitas hari ini..." required></textarea>
                        </div>
                        <button type="submit" class="btn">Simpan Aktivitas</button>
                    </form>
                </div>
            </div>
            
            <!-- Riwayat Section -->
            <div id="riwayat" class="content-section">
                <div class="content-header">
                    <h1>Riwayat</h1>
                    <p>Lihat riwayat presensi dan aktivitas</p>
                </div>
                <div class="card">
                    <h3><span class="icon">📊</span>Riwayat Presensi</h3>
                    <table style="width: 100%; border-collapse: collapse;">
                        <thead>
                            <tr style="background: #f8fafc; border-bottom: 2px solid #e2e8f0;">
                                <th style="padding: 12px; text-align: left;">Tanggal</th>
                                <th style="padding: 12px; text-align: left;">Jam Masuk</th>
                                <th style="padding: 12px; text-align: left;">Jam Keluar</th>
                                <th style="padding: 12px; text-align: left;">Status</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                            // Ambil riwayat presensi
                            $q_riwayat = mysqli_query($koneksi, "SELECT * FROM presensi WHERE user_id='$user_id' ORDER BY tanggal DESC LIMIT 10");
                            while ($row = mysqli_fetch_assoc($q_riwayat)) :
                            ?>
                            <tr style="border-bottom: 1px solid #f1f5f9;">
                                <td style="padding: 12px;"><?= htmlspecialchars($row['tanggal']) ?></td>
                                <td style="padding: 12px;"><?= htmlspecialchars($row['jam_masuk']) ?></td>
                                <td style="padding: 12px;"><?= htmlspecialchars($row['jam_keluar']) ?></td>
                                <td style="padding: 12px;">
                                    <?php if ($row['jam_masuk']): ?>
                                        <span class="status-badge status-valid">Hadir</span>
                                    <?php else: ?>
                                        <span class="status-badge status-invalid">Tidak Hadir</span>
                                    <?php endif; ?>
                                </td>
                            </tr>
                            <?php endwhile; ?>
                        </tbody>
                    </table>
                </div>
            </div>
            
            <!-- Profile Section -->
            <div id="profile" class="content-section">
                <div class="content-header">
                    <h1>Profile</h1>
                    <p>Kelola informasi profile Anda</p>
                </div>
                
                <?php if (isset($success_message)): ?>
                    <div class="alert alert-success">
                        ✅ <?= htmlspecialchars($success_message) ?>
                    </div>
                <?php endif; ?>
                
                <?php if (isset($error_message)): ?>
                    <div class="alert alert-error">
                        ❌ <?= htmlspecialchars($error_message) ?>
                    </div>
                <?php endif; ?>
                
                <div class="card">
                    <h3><span class="icon">👤</span>Informasi Profile</h3>
                    
                    <!-- Profile View -->
                    <div id="profileView">
                        <div class="info-item">
                            <span class="info-label">Nama:</span>
                            <span class="info-value"><?= htmlspecialchars($user['nama'] ?? '-') ?></span>
                        </div>
                        <div class="info-item">
                            <span class="info-label">Username:</span>
                            <span class="info-value"><?= htmlspecialchars($user['username'] ?? '-') ?></span>
                        </div>
                        <div class="info-item">
                            <span class="info-label">Role:</span>
                            <span class="info-value"><?= htmlspecialchars($user['role'] ?? '-') ?></span>
                        </div>
                        <div class="info-item">
                            <span class="info-label">Kelas:</span>
                            <span class="info-value"><?= htmlspecialchars($user['kelas'] ?? '-') ?></span>
                        </div>
                        <div class="info-item">
                            <span class="info-label">Jurusan:</span>
                            <span class="info-value"><?= htmlspecialchars($user['jurusan'] ?? '-') ?></span>
                        </div>
                        <div style="margin-top: 20px;">
                            <button type="button" class="btn btn-success" onclick="editProfile()">✏️ Edit Profil</button>
                        </div>
                    </div>
                    
                    <!-- Profile Edit Form -->
                    <div id="formEditProfile" style="display: none;">
                        <form method="POST" action="">
                            <div class="form-group">
                                <label for="edit_nama">Nama Lengkap:</label>
                                <input type="text" id="edit_nama" name="edit_nama" value="<?= htmlspecialchars($user['nama'] ?? '') ?>" required placeholder="Masukkan nama lengkap">
                            </div>
                            <div class="form-group">
                                <label for="edit_username">Username:</label>
                                <input type="text" id="edit_username" name="edit_username" value="<?= htmlspecialchars($user['username'] ?? '') ?>" required placeholder="Masukkan username">
                            </div>
                            <div class="form-group">
                                <label for="edit_kelas">Kelas:</label>
                                <input type="text" id="edit_kelas" name="edit_kelas" value="<?= htmlspecialchars($user['kelas'] ?? '') ?>" required placeholder="Contoh: XII IPA 1">
                            </div>
                            <div class="form-group">
                                <label for="edit_jurusan">Jurusan:</label>
                                <input type="text" id="edit_jurusan" name="edit_jurusan" value="<?= htmlspecialchars($user['jurusan'] ?? '') ?>" required placeholder="Contoh: IPA, IPS, Multimedia">
                            </div>
                            <div style="margin-top: 20px;">
                                <button type="submit" name="update_profile" class="btn btn-success">💾 Simpan Perubahan</button>
                                <button type="button" class="btn btn-outline" onclick="batalEdit()">❌ Batal</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <?php if ($notif_incomplete_profile): ?>
        <div id="notif-profile-incomplete" class="floating-alert">
            ⚠️ Data profil Anda belum lengkap. Silakan lengkapi data diri Anda di menu <b>Profile</b>!
        </div>
    <?php endif; ?>
    
    <script>
        function showSection(sectionId, event) {
            // Hide all sections
            const sections = document.querySelectorAll('.content-section');
            sections.forEach(section => {
                section.classList.remove('active');
            });
            
            // Show selected section
            document.getElementById(sectionId).classList.add('active');
            
            // Update active menu item
            const menuItems = document.querySelectorAll('.menu-item');
            menuItems.forEach(item => item.classList.remove('active'));
            if(event) event.target.classList.add('active');
            
            // Close sidebar on mobile
            if (window.innerWidth <= 768) {
                toggleSidebar();
            }
        }
        
        function toggleSidebar() {
            const sidebar = document.getElementById('sidebar');
            const overlay = document.querySelector('.overlay');
            sidebar.classList.toggle('active');
            overlay.classList.toggle('active');
        }
        
        function logout() {
            if (confirm('Apakah Anda yakin ingin logout?')) {
                window.location.href = '../auth/logout.php';
            }
        }
        
        // Update tanggal hari ini
        function updateCurrentDate() {
            const now = new Date();
            const options = { 
                weekday: 'long', 
                year: 'numeric', 
                month: 'long', 
                day: 'numeric' 
            };
            document.getElementById('currentDate').textContent = now.toLocaleDateString('id-ID', options);
        }
        updateCurrentDate();

        function editProfile() {
            document.getElementById('profileView').style.display = 'none';
            document.getElementById('formEditProfile').style.display = 'block';
        }
        
        function batalEdit() {
            document.getElementById('formEditProfile').style.display = 'none';
            document.getElementById('profileView').style.display = 'block';
        }
        
        // Auto hide alert after 5 seconds
        document.addEventListener('DOMContentLoaded', function() {
            const alerts = document.querySelectorAll('.alert');
            alerts.forEach(alert => {
                setTimeout(() => {
                    alert.style.opacity = '0';
                    setTimeout(() => alert.remove(), 300);
                }, 5000);
            });
        });
    </script>
</body>
</html>

pembimbing/dashboard.php

php
<?php
session_start();
include '../config/koneksi.php';

// Cegah akses langsung jika bukan pembimbing
if (!isset($_SESSION['user_id']) || $_SESSION['role'] != 'pembimbing') {
  header("Location: ../auth/login.php");
  exit;
}

// Ambil semua data aktivitas siswa (JOIN dengan nama user)
$query = mysqli_query($koneksi, "
  SELECT aktivitas.*, users.nama 
  FROM aktivitas 
  JOIN users ON aktivitas.user_id = users.id
  ORDER BY tanggal DESC
");

// Ambil data presensi siswa untuk rekap (contoh, bisa dikembangkan)
$query_presensi = mysqli_query($koneksi, "
  SELECT users.nama, COUNT(presensi.id) as hadir
  FROM users
  LEFT JOIN presensi ON users.id = presensi.user_id
  WHERE users.role = 'siswa'
  GROUP BY users.id
");

// Ambil data siswa
$query_siswa = mysqli_query($koneksi, "SELECT * FROM users WHERE role='siswa' ORDER BY nama ASC");
?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard Pembimbing</title>
    <style>
        /* ...CSS dari prompt, tidak diubah... */
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #e0e7ff 0%, #f4f6fb 100%);
            min-height: 100vh;
        }
        .container { display: flex; min-height: 100vh; }
        .sidebar {
            width: 280px;
            background: linear-gradient(180deg, #4f8cff 0%, #3b82f6 100%);
            color: white;
            padding: 0;
            box-shadow: 4px 0 15px rgba(0, 0, 0, 0.1);
            position: fixed;
            height: 100vh;
            overflow-y: auto;
            transition: transform 0.3s ease;
        }
        .sidebar-header {
            padding: 30px 25px 25px 25px;
            background: rgba(255, 255, 255, 0.1);
            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
        }
        .sidebar-header h2 { font-size: 1.4rem; margin-bottom: 8px; color: white; }
        .sidebar-header p { font-size: 0.9rem; opacity: 0.8; color: white; }
        .sidebar-menu { padding: 20px 0; }
        .menu-item {
            display: flex;
            align-items: center;
            padding: 15px 25px;
            color: white;
            text-decoration: none;
            transition: all 0.3s ease;
            border-left: 4px solid transparent;
            cursor: pointer;
        }
        .menu-item:hover {
            background: rgba(255, 255, 255, 0.1);
            border-left-color: white;
        }
        .menu-item.active {
            background: rgba(255, 255, 255, 0.15);
            border-left-color: white;
            font-weight: 600;
        }
        .menu-item .icon { margin-right: 15px; font-size: 1.2rem; width: 24px; text-align: center; }
        .logout-item {
            position: absolute;
            bottom: 20px;
            width: 100%;
            padding: 15px 25px;
            background: rgba(239, 68, 68, 0.2);
            border-top: 1px solid rgba(255, 255, 255, 0.1);
        }
        .logout-item:hover { background: rgba(239, 68, 68, 0.3); }
        .main-content {
            flex: 1;
            margin-left: 280px;
            padding: 30px;
            transition: margin-left 0.3s ease;
        }
        .content-header {
            background: white;
            padding: 25px 30px;
            border-radius: 15px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            margin-bottom: 25px;
        }
        .content-header h1 { color: #2d3a4b; font-size: 2rem; margin-bottom: 8px; }
        .content-header p { color: #64748b; font-size: 1rem; }
        .content-section { display: none; animation: fadeIn 0.5s ease; }
        .content-section.active { display: block; }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .card {
            background: white;
            border-radius: 15px;
            padding: 25px;
            margin-bottom: 20px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            border: 1px solid rgba(79, 140, 255, 0.1);
        }
        /* Jika ingin margin pada card aktivitas terbaru */
        #dashboard .card {
            margin-top: 32px;
        }
        .card h3 { color: #2d3a4b; margin-bottom: 20px; font-size: 1.3rem; display: flex; align-items: center; }
        .card h3 .icon { margin-right: 10px; color: #4f8cff; }
        .table-container {
            background: white;
            border-radius: 15px;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
        }
        .table-header {
            background: linear-gradient(135deg, #4f8cff 0%, #3b82f6 100%);
            color: white;
            padding: 20px 25px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .table-header h3 { margin: 0; font-size: 1.2rem; }
        .search-box {
            padding: 8px 12px;
            border: none;
            border-radius: 6px;
            background: rgba(255, 255, 255, 0.2);
            color: white;
        }
        .search-box::placeholder { color: rgba(255, 255, 255, 0.7); }
        table { width: 100%; border-collapse: collapse; }
        th, td { padding: 15px; text-align: left; border-bottom: 1px solid #f1f5f9; }
        th { background: #f8fafc; font-weight: 600; color: #2d3a4b; }
        td { color: #64748b; }
        tr:hover { background: #f8fafc; }
        .status-badge {
            padding: 6px 12px;
            border-radius: 20px;
            font-size: 0.85rem;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            display: inline-block;
        }
        .status-pending { background: #fef3c7; color: #d97706; }
        .status-approved { background: #dcfce7; color: #166534; }
        .status-rejected { background: #fee2e2; color: #dc2626; }
        .btn {
            padding: 8px 16px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 0.9rem;
            font-weight: 500;
            transition: all 0.3s ease;
            text-decoration: none;
            display: inline-block;
        }
        .btn-approve { background: #22c55e; color: white; }
        .btn-approve:hover { background: #16a34a; transform: translateY(-2px); }
        .btn-reject { background: #ef4444; color: white; margin-left: 5px; }
        .btn-reject:hover { background: #dc2626; transform: translateY(-2px); }
        .approved-text { color: #22c55e; font-weight: 600; }
        @media (max-width: 768px) {
            .sidebar { transform: translateX(-100%); z-index: 1000; }
            .sidebar.active { transform: translateX(0); }
            .main-content { margin-left: 0; padding: 20px; }
            .mobile-toggle {
                display: block;
                position: fixed;
                top: 20px;
                left: 20px;
                z-index: 1001;
                background: #4f8cff;
                color: white;
                border: none;
                padding: 10px;
                border-radius: 5px;
                cursor: pointer;
            }
            .table-container { overflow-x: auto; }
        }
        .mobile-toggle { display: none; }
        .overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            z-index: 999;
        }
        .overlay.active { display: block; }
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 32px;
            margin-top: 32px;
        }
        .stat-card {
            background: linear-gradient(135deg, #7f9cf5 0%, #a78bfa 100%);
            border-radius: 20px;
            padding: 36px 0 28px 0;
            box-shadow: 0 8px 32px rgba(79, 140, 255, 0.10);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 170px;
            transition: transform 0.2s, box-shadow 0.2s;
        }
        .stat-card:hover {
            transform: translateY(-6px) scale(1.03);
            box-shadow: 0 12px 36px rgba(79, 140, 255, 0.18);
        }
        .stat-card .icon {
            font-size: 2.8rem;
            margin-bottom: 18px;
            display: block;
        }
        .stat-card .number {
            font-size: 2.6rem;
            font-weight: 800;
            color: #fff;
            margin-bottom: 8px;
            line-height: 1;
            text-shadow: 0 2px 8px rgba(79, 140, 255, 0.10);
        }
        .stat-card .label {
            font-size: 1.1rem;
            color: #f3f4f6;
            font-weight: 500;
            letter-spacing: 0.5px;
            margin-top: 2px;
            text-align: center;
        }
        @media (max-width: 900px) {
            .stats-grid {
                grid-template-columns: 1fr;
                gap: 20px;
            }
            .stat-card {
                min-height: 120px;
                padding: 28px 0 20px 0;
            }
        }
        /* Notifikasi aktivitas terbaru */
        .activity-notif {
            padding: 14px 18px;
            border-left: 5px solid #22c55e;
            background: #f0fdf4;
            margin-bottom: 14px;
            border-radius: 8px;
            font-size: 1rem;
            font-weight: 400;
        }
        .activity-notif.pending {
            border-left-color: #f59e0b;
            background: #fffbeb;
        }
        .activity-notif.valid {
            border-left-color: #22c55e;
            background: #f0fdf4;
        }
        .activity-notif.presensi {
            border-left-color: #3b82f6;
            background: #eff6ff;
        }
        .activity-notif strong {
            font-weight: 700;
        }
        .activity-notif .time {
            font-size: 0.9rem;
            color: #64748b;
            margin-top: 2px;
            display: block;
        }
    </style>
</head>
<body>
    <button class="mobile-toggle" onclick="toggleSidebar()">☰</button>
    <div class="overlay" onclick="toggleSidebar()"></div>
    <div class="container">
        <!-- Sidebar -->
        <div class="sidebar" id="sidebar">
            <div class="sidebar-header">
                <h2>👨‍🏫 Portal Pembimbing</h2>
                <p>Selamat datang, Pembimbing!</p>
            </div>
            <div class="sidebar-menu">
                <a href="#" class="menu-item active" onclick="showSection('dashboard', event)">
                    <span class="icon">🏠</span>
                    Dashboard
                </a>
                <a href="#" class="menu-item" onclick="showSection('validasi', event)">
                    <span class="icon">✅</span>
                    Validasi Aktivitas
                </a>
                <a href="#" class="menu-item" onclick="showSection('rekap', event)">
                    <span class="icon">📊</span>
                    Rekap Presensi
                </a>
                <a href="#" class="menu-item" onclick="showSection('siswa', event)">
                    <span class="icon">👥</span>
                    Data Siswa
                </a>
            </div>
            <div class="logout-item">
                <a href="#" class="menu-item" onclick="logout()">
                    <span class="icon">🚪</span>
                    Logout
                </a>
            </div>
        </div>
        <!-- Main Content -->
        <div class="main-content">
            <!-- Dashboard Section -->
            <div id="dashboard" class="content-section active">
                <div class="content-header">
                    <h1>Dashboard</h1>
                    <p>Ringkasan kegiatan siswa - <span id="currentDate"></span></p>
                </div>

                <!-- Stats Grid: Pindahkan ke atas -->
                <div class="stats-grid">
                    <div class="stat-card">
                        <div class="icon">👨‍🎓</div>
                        <div class="number">
                            <?php
                            // Total siswa
                            $q_total_siswa = mysqli_query($koneksi, "SELECT COUNT(*) as total FROM users WHERE role='siswa'");
                            $total_siswa = mysqli_fetch_assoc($q_total_siswa)['total'] ?? 0;
                            echo $total_siswa;
                            ?>
                        </div>
                        <div class="label">Total Siswa</div>
                    </div>
                    <div class="stat-card">
                        <div class="icon">⏳</div>
                        <div class="number">
                            <?php
                            // Menunggu validasi
                            $q_pending = mysqli_query($koneksi, "SELECT COUNT(*) as total FROM aktivitas WHERE status_validasi='pending'");
                            $pending = mysqli_fetch_assoc($q_pending)['total'] ?? 0;
                            echo $pending;
                            ?>
                        </div>
                        <div class="label">Menunggu Validasi</div>
                    </div>
                    <div class="stat-card">
                        <div class="icon">✅</div>
                        <div class="number">
                            <?php
                            // Sudah divalidasi khusus siswa
                            $q_valid = mysqli_query($koneksi, "
                                SELECT COUNT(*) as total 
                                FROM aktivitas 
                                JOIN users ON aktivitas.user_id = users.id
                                WHERE aktivitas.status_validasi='disetujui' AND users.role='siswa'
                            ");
                            $valid = mysqli_fetch_assoc($q_valid)['total'] ?? 0;
                            echo $valid;
                            ?>
                        </div>
                        <div class="label">Sudah Divalidasi</div>
                    </div>
                    <div class="stat-card">
                        <div class="icon">📈</div>
                        <div class="number">
                            <?php
                            // Tingkat kehadiran hari ini
                            $today = date('Y-m-d');
                            $q_hadir = mysqli_query($koneksi, "SELECT COUNT(DISTINCT user_id) as hadir FROM presensi WHERE tanggal='$today'");
                            $hadir = mysqli_fetch_assoc($q_hadir)['hadir'] ?? 0;
                            $persen = $total_siswa > 0 ? round(($hadir / $total_siswa) * 100) : 0;
                            echo $persen . '%';
                            ?>
                        </div>
                        <div class="label">Tingkat Kehadiran</div>
                    </div>
                </div>
                <!-- END Stats Grid -->

                <div class="card">
                    <h3><span class="icon">🔔</span>Aktivitas Terbaru</h3>
                    <div>
                        <?php
                        $q_recent = mysqli_query($koneksi, "
                            SELECT aktivitas.*, users.nama 
                            FROM aktivitas 
                            JOIN users ON aktivitas.user_id = users.id
                            ORDER BY aktivitas.tanggal DESC, aktivitas.id DESC
                            LIMIT 3
                        ");
                        while ($recent = mysqli_fetch_assoc($q_recent)) :
                            // Tentukan kelas warna berdasarkan status
                            $class = 'activity-notif ';
                            if ($recent['status_validasi'] == 'pending') {
                                $class .= 'pending';
                                $status_text = 'Menunggu validasi aktivitas';
                            } elseif ($recent['status_validasi'] == 'disetujui') {
                                $class .= 'valid';
                                $status_text = 'Aktivitas telah divalidasi';
                            } else {
                                $class .= 'presensi';
                                $status_text = 'Presensi masuk tercatat';
                            }
                            // Hitung waktu relatif (opsional, sederhana)
                            $waktu = strtotime($recent['tanggal']);
                            $now = strtotime(date('Y-m-d'));
                            $selisih = ($now - $waktu) / 60; // menit
                            $time_ago = $recent['tanggal'];
                        ?>
                        <div class="<?= $class ?>">
                            <strong><?= htmlspecialchars($recent['nama']) ?></strong> - 
                            <?= htmlspecialchars($status_text) ?>
                            <span class="time"><?= htmlspecialchars($recent['tanggal']) ?></span>
                        </div>
                        <?php endwhile; ?>
                    </div>
                </div>
            </div>
            <!-- Validasi Section -->
            <div id="validasi" class="content-section">
                <div class="content-header">
                    <h1>Validasi Aktivitas</h1>
                    <p>Kelola dan validasi aktivitas siswa</p>
                </div>
                <div class="table-container">
                    <div class="table-header">
                        <h3>Daftar Aktivitas Siswa</h3>
                        <input type="text" class="search-box" placeholder="Cari nama siswa..." onkeyup="searchTable()">
                    </div>
                    <table id="activitiesTable">
                        <thead>
                            <tr>
                                <th>Nama Siswa</th>
                                <th>Tanggal</th>
                                <th>Aktivitas</th>
                                <th>Status</th>
                                <th>Aksi</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php while ($data = mysqli_fetch_assoc($query)) : ?>
                            <tr data-status="<?= $data['status_validasi'] ?>">
                                <td><?= htmlspecialchars($data['nama']) ?></td>
                                <td><?= htmlspecialchars($data['tanggal']) ?></td>
                                <td><?= nl2br(htmlspecialchars($data['deskripsi'])) ?></td>
                                <td>
                                    <?php if ($data['status_validasi'] == 'pending') : ?>
                                        <span class="status-badge status-pending">Menunggu</span>
                                    <?php elseif ($data['status_validasi'] == 'Valid') : ?>
                                        <span class="status-badge status-approved">Disetujui</span>
                                    <?php else : ?>
                                        <span class="status-badge status-rejected"><?= htmlspecialchars($data['status_validasi']) ?></span>
                                    <?php endif; ?>
                                </td>
                                <td>
                                    <?php if ($data['status_validasi'] == 'pending') : ?>
                                        <form method="POST" action="../proses/proses_validasi.php" style="display:inline;">
                                            <input type="hidden" name="aktivitas_id" value="<?= $data['id']; ?>">
                                            <button type="submit" name="setujui" class="btn btn-approve">Setujui</button>
                                        </form>
                                    <?php else : ?>
                                        <span class="approved-text">✅ Sudah Disetujui</span>
                                    <?php endif; ?>
                                </td>
                            </tr>
                            <?php endwhile; ?>
                        </tbody>
                    </table>
                </div>
            </div>
            <!-- Rekap Section -->
            <div id="rekap" class="content-section">
                <div class="content-header">
                    <h1>Rekap Presensi</h1>
                    <p>Rekap kehadiran siswa per periode</p>
                </div>
                <div class="card">
                    <h3><span class="icon">📊</span>Rekap Presensi Bulanan</h3>
                    <div class="table-container">
                        <table>
                            <thead>
                                <tr>
                                    <th>Nama Siswa</th>
                                    <th>Hadir</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php while ($row = mysqli_fetch_assoc($query_presensi)) : ?>
                                <tr>
                                    <td><?= htmlspecialchars($row['nama']) ?></td>
                                    <td><?= htmlspecialchars($row['hadir']) ?></td>
                                </tr>
                                <?php endwhile; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
                <div class="card">
                    <h3><span class="icon">📅</span>Rekap Presensi Detail</h3>
                    <div class="table-container">
                        <table>
                            <thead>
                                <tr>
                                    <th>Nama Siswa</th>
                                    <th>Tanggal</th>
                                    <th>Jam Masuk</th>
                                    <th>Jam Keluar</th>
                                    <th>Total Jam Kerja</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php
                                // Ambil data presensi detail per siswa
                                $q_rekap = mysqli_query($koneksi, "
                                    SELECT users.nama, presensi.tanggal, presensi.jam_masuk, presensi.jam_keluar
                                    FROM users
                                    LEFT JOIN presensi ON users.id = presensi.user_id
                                    WHERE users.role = 'siswa'
                                    ORDER BY presensi.tanggal DESC, users.nama ASC
                                    LIMIT 50
                                ");
                                while ($row = mysqli_fetch_assoc($q_rekap)) :
                                    $total_jam = '-';
                                    if ($row['jam_masuk'] && $row['jam_keluar']) {
                                        $start = strtotime($row['jam_masuk']);
                                        $end = strtotime($row['jam_keluar']);
                                        $diff = $end - $start;
                                        $hours = floor($diff / 3600);
                                        $minutes = floor(($diff % 3600) / 60);
                                        $total_jam = $hours . ' jam' . ($minutes > 0 ? ' ' . $minutes . ' menit' : '');
                                    }
                                ?>
                                <tr>
                                    <td><?= htmlspecialchars($row['nama']) ?></td>
                                    <td><?= htmlspecialchars($row['tanggal']) ?></td>
                                    <td><?= htmlspecialchars($row['jam_masuk'] ?? '-') ?></td>
                                    <td>
                                        <?php if ($row['jam_keluar']) : ?>
                                            <?= htmlspecialchars($row['jam_keluar']) ?>
                                        <?php else: ?>
                                            <span style="color:#d97706;">Belum Absen Keluar</span>
                                        <?php endif; ?>
                                    </td>
                                    <td>
                                        <?php if ($row['jam_masuk'] && $row['jam_keluar']) : ?>
                                            <?= $total_jam ?>
                                        <?php else: ?>
                                            <span style="color:#64748b;">-</span>
                                        <?php endif; ?>
                                    </td>
                                </tr>
                                <?php endwhile; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
            <!-- Data Siswa Section -->
            <div id="siswa" class="content-section">
                <div class="content-header">
                    <h1>Data Siswa</h1>
                    <p>Daftar siswa yang sudah terdaftar di sistem</p>
                </div>
                <div class="card">
                    <h3><span class="icon">👥</span>Daftar Siswa</h3>
                    <div class="table-container">
                        <table>
                            <thead>
                                <tr>
                                    <th>Nama</th>
                                    <th>Username</th>
                                    <th>Jurusan</th>
                                    <th>Kelas</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php while ($siswa = mysqli_fetch_assoc($query_siswa)) : ?>
                                <tr>
                                    <td><?= htmlspecialchars($siswa['nama']) ?></td>
                                    <td><?= htmlspecialchars($siswa['username']) ?></td>
                                    <td><?= htmlspecialchars($siswa['jurusan'] ?? '-') ?></td>
                                    <td><?= htmlspecialchars($siswa['kelas'] ?? '-') ?></td>
                                </tr>
                                <?php endwhile; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script>
        function showSection(sectionId, event) {
            // Hide all sections
            const sections = document.querySelectorAll('.content-section');
            sections.forEach(section => {
                section.classList.remove('active');
            });
            // Show selected section
            document.getElementById(sectionId).classList.add('active');
            // Update active menu item
            const menuItems = document.querySelectorAll('.menu-item');
            menuItems.forEach(item => item.classList.remove('active'));
            if(event) event.target.classList.add('active');
            // Close sidebar on mobile
            if (window.innerWidth <= 768) {
                toggleSidebar();
            }
        }
        function toggleSidebar() {
            const sidebar = document.getElementById('sidebar');
            const overlay = document.querySelector('.overlay');
            sidebar.classList.toggle('active');
            overlay.classList.toggle('active');
        }
        function logout() {
            if (confirm('Apakah Anda yakin ingin logout?')) {
                window.location.href = '../auth/logout.php';
            }
        }
        // Search table by name
        function searchTable() {
            const input = document.querySelector('.search-box');
            const filter = input.value.toLowerCase();
            const rows = document.querySelectorAll('#activitiesTable tbody tr');
            rows.forEach(row => {
                const nama = row.children[0].textContent.toLowerCase();
                row.style.display = nama.includes(filter) ? '' : 'none';
            });
        }
        // Update tanggal hari ini
        function updateCurrentDate() {
            const now = new Date();
            const options = { 
                weekday: 'long', 
                year: 'numeric', 
                month: 'long', 
                day: 'numeric' 
            };
            document.getElementById('currentDate').textContent = now.toLocaleDateString('id-ID', options);
        }
        updateCurrentDate();
    </script>
</body>
</html>

7. Fitur Input Presensi

siswa/presensi.php

php
<?php
session_start();
include '../config/koneksi.php';

$user_id = $_SESSION['user_id'];
$tanggal = date("Y-m-d");

// Ambil data presensi hari ini
$q = mysqli_query($koneksi, "SELECT * FROM presensi WHERE user_id='$user_id' AND tanggal='$tanggal'");
$presensi = mysqli_fetch_assoc($q);

// Proses jam masuk
if (isset($_POST['masuk'])) {
    $jam_masuk = $_POST['jam_masuk'];
    mysqli_query($koneksi, "INSERT INTO presensi (user_id, tanggal, jam_masuk) VALUES ('$user_id', '$tanggal', '$jam_masuk')");
    header("Location: presensi.php");
    exit;
}

// Proses jam keluar
if (isset($_POST['keluar'])) {
    $jam_keluar = $_POST['jam_keluar'];
    mysqli_query($koneksi, "UPDATE presensi SET jam_keluar='$jam_keluar' WHERE user_id='$user_id' AND tanggal='$tanggal'");
    header("Location: presensi.php");
    exit;
}

// Untuk tampilan status
$showMasuk = false;
$showKeluar = false;
$showComplete = false;
$jamMasuk = '';
$jamKeluar = '';
$totalJam = '';

if (!$presensi) {
    $showMasuk = true;
} elseif ($presensi && !$presensi['jam_keluar']) {
    $showKeluar = true;
    $jamMasuk = $presensi['jam_masuk'];
} else {
    $showComplete = true;
    $jamMasuk = $presensi['jam_masuk'];
    $jamKeluar = $presensi['jam_keluar'];
    // Hitung total jam kerja
    $start = strtotime($jamMasuk);
    $end = strtotime($jamKeluar);
    $diff = $end - $start;
    $hours = floor($diff / 3600);
    $minutes = floor(($diff % 3600) / 60);
    $totalJam = $hours . ' jam ' . ($minutes > 0 ? $minutes . ' menit' : '');
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sistem Presensi Modern</title>
    <style>
        /* ...CSS dari prompt, tidak diubah... */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .container {
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
            padding: 40px;
            max-width: 500px;
            width: 100%;
            border: 1px solid rgba(255, 255, 255, 0.2);
        }
        .header {
            text-align: center;
            margin-bottom: 30px;
        }
        .header h3 {
            font-size: 2.5rem;
            color: #2c3e50;
            margin-bottom: 10px;
            background: linear-gradient(45deg, #667eea, #764ba2);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            font-weight: 700;
        }
        .date-info {
            background: linear-gradient(45deg, #667eea, #764ba2);
            color: white;
            padding: 15px;
            border-radius: 15px;
            margin-bottom: 30px;
            text-align: center;
            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
        }
        .date-info .date {
            font-size: 1.2rem;
            font-weight: 600;
            margin-bottom: 5px;
        }
        .date-info .time {
            font-size: 1rem;
            opacity: 0.9;
        }
        .form-container {
            background: rgba(255, 255, 255, 0.8);
            border-radius: 15px;
            padding: 30px;
            margin-bottom: 20px;
            border: 1px solid rgba(255, 255, 255, 0.3);
        }
        .form-group {
            margin-bottom: 25px;
        }
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
            color: #2c3e50;
            font-size: 1.1rem;
        }
        input[type="time"] {
            width: 100%;
            padding: 15px;
            border: 2px solid #e1e8ed;
            border-radius: 10px;
            font-size: 1.1rem;
            transition: all 0.3s ease;
            background: white;
            color: #2c3e50;
        }
        input[type="time"]:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
            transform: translateY(-2px);
        }
        .btn {
            width: 100%;
            padding: 15px;
            border: none;
            border-radius: 10px;
            font-size: 1.1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            text-transform: uppercase;
            letter-spacing: 1px;
            position: relative;
            overflow: hidden;
        }
        .btn::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
            transition: left 0.5s;
        }
        .btn:hover::before {
            left: 100%;
        }
        .btn-masuk {
            background: linear-gradient(45deg, #2ecc71, #27ae60);
            color: white;
            box-shadow: 0 10px 20px rgba(46, 204, 113, 0.3);
        }
        .btn-masuk:hover {
            transform: translateY(-3px);
            box-shadow: 0 15px 30px rgba(46, 204, 113, 0.4);
        }
        .btn-keluar {
            background: linear-gradient(45deg, #e74c3c, #c0392b);
            color: white;
            box-shadow: 0 10px 20px rgba(231, 76, 60, 0.3);
        }
        .btn-keluar:hover {
            transform: translateY(-3px);
            box-shadow: 0 15px 30px rgba(231, 76, 60, 0.4);
        }
        .status-complete {
            background: linear-gradient(45deg, #2ecc71, #27ae60);
            color: white;
            padding: 30px;
            border-radius: 15px;
            text-align: center;
            box-shadow: 0 10px 20px rgba(46, 204, 113, 0.3);
        }
        .status-complete h4 {
            font-size: 1.5rem;
            margin-bottom: 15px;
        }
        .time-info {
            background: rgba(255, 255, 255, 0.2);
            padding: 20px;
            border-radius: 10px;
            margin-top: 15px;
        }
        .time-info p {
            margin: 8px 0;
            font-size: 1.1rem;
        }
        .icon {
            font-size: 2rem;
            margin-bottom: 10px;
        }
        .pulse {
            animation: pulse 2s infinite;
        }
        @keyframes pulse {
            0% { transform: scale(1);}
            50% { transform: scale(1.05);}
            100% { transform: scale(1);}
        }
        .fade-in {
            animation: fadeIn 0.5s ease-in;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(20px);}
            to { opacity: 1; transform: translateY(0);}
        }
        @media (max-width: 768px) {
            .container { padding: 20px; margin: 10px;}
            .header h3 { font-size: 2rem;}
            .btn { padding: 12px; font-size: 1rem;}
        }
        .loading {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 2px solid #ffffff;
            border-radius: 50%;
            border-top-color: transparent;
            animation: spin 1s ease-in-out infinite;
        }
        @keyframes spin { to { transform: rotate(360deg); } }
        .success-checkmark {
            display: inline-block;
            width: 22px;
            height: 22px;
            margin-right: 10px;
            transform: rotate(45deg);
        }
        .success-checkmark::before {
            content: '';
            position: absolute;
            width: 3px;
            height: 9px;
            background: white;
            left: 11px;
            top: 6px;
        }
        .success-checkmark::after {
            content: '';
            position: absolute;
            width: 6px;
            height: 3px;
            background: white;
            left: 6px;
            top: 12px;
        }
    </style>
</head>
<body>
    <div class="container fade-in">
        <div class="header">
            <h3>📍 Sistem Presensi</h3>
        </div>
        <div style="text-align:center; margin-bottom:20px;">
            <a href="dashboard.php" style="
                display:inline-block;
                background:linear-gradient(45deg,#4f8cff,#2563eb);
                color:#fff;
                padding:10px 22px;
                border-radius:8px;
                text-decoration:none;
                font-weight:600;
                box-shadow:0 2px 8px rgba(79,140,255,0.10);
                transition:background 0.2s;
            " onmouseover="this.style.background='#2563eb'" onmouseout="this.style.background='linear-gradient(45deg,#4f8cff,#2563eb)'">
                ← Kembali ke Dashboard
            </a>
        </div>
        <div class="date-info">
            <div class="date" id="currentDate"></div>
            <div class="time" id="currentTime"></div>
        </div>

        <!-- Form Jam Masuk -->
        <?php if ($showMasuk): ?>
        <div class="form-container" id="form-masuk">
            <div class="icon">🌅</div>
            <h4 style="margin-bottom: 20px; color: #2c3e50;">Selamat Pagi! Silakan absen masuk</h4>
            <form method="POST">
                <div class="form-group">
                    <label for="jam_masuk">Jam Masuk:</label>
                    <input type="time" name="jam_masuk" id="jam_masuk" required>
                </div>
                <button type="submit" name="masuk" class="btn btn-masuk pulse">
                    <span class="success-checkmark"></span>
                    Absen Masuk
                </button>
            </form>
        </div>
        <?php endif; ?>

        <!-- Form Jam Keluar -->
        <?php if ($showKeluar): ?>
        <div class="form-container" id="form-keluar">
            <div class="icon">🌇</div>
            <h4 style="margin-bottom: 20px; color: #2c3e50;">Waktu pulang! Silakan absen keluar</h4>
            <form method="POST">
                <div class="form-group">
                    <label for="jam_keluar">Jam Keluar:</label>
                    <input type="time" name="jam_keluar" id="jam_keluar" required>
                </div>
                <button type="submit" name="keluar" class="btn btn-keluar pulse">
                    <span class="success-checkmark"></span>
                    Absen Pulang
                </button>
            </form>
        </div>
        <?php endif; ?>

        <!-- Status Presensi Lengkap -->
        <?php if ($showComplete): ?>
        <div class="status-complete" id="status-complete">
            <div class="icon">✅</div>
            <h4>Presensi Hari Ini Sudah Lengkap!</h4>
            <div class="time-info">
                <p><strong>Jam Masuk:</strong> <span id="jam-masuk-display"><?= htmlspecialchars($jamMasuk) ?></span></p>
                <p><strong>Jam Keluar:</strong> <span id="jam-keluar-display"><?= htmlspecialchars($jamKeluar) ?></span></p>
                <p><strong>Total Jam Kerja:</strong> <span id="total-jam"><?= htmlspecialchars($totalJam) ?></span></p>
            </div>
        </div>
        <?php endif; ?>
    </div>

    <script>
        // Update waktu real-time
        function updateTime() {
            const now = new Date();
            const options = { 
                weekday: 'long', 
                year: 'numeric', 
                month: 'long', 
                day: 'numeric' 
            };
            document.getElementById('currentDate').textContent = 
                now.toLocaleDateString('id-ID', options);
            document.getElementById('currentTime').textContent = 
                now.toLocaleTimeString('id-ID');
        }

        // Set waktu saat ini sebagai default pada input time
        function setCurrentTime() {
            const now = new Date();
            const timeString = now.toTimeString().slice(0, 5);
            const jamMasukInput = document.getElementById('jam_masuk');
            const jamKeluarInput = document.getElementById('jam_keluar');
            if (jamMasukInput && !jamMasukInput.value) {
                jamMasukInput.value = timeString;
            }
            if (jamKeluarInput && !jamKeluarInput.value) {
                jamKeluarInput.value = timeString;
            }
        }

        updateTime();
        setCurrentTime();
        setInterval(updateTime, 1000);
        setInterval(setCurrentTime, 60000);
    </script>
</body>
</html>

8. Fitur Aktivitas Harian

siswa/aktivitas.php

php
<?php
session_start();
include '../config/koneksi.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $user_id = $_SESSION['user_id'];
  $tanggal = date("Y-m-d");
  $deskripsi = $_POST['deskripsi'];

  mysqli_query($koneksi, "INSERT INTO aktivitas (user_id, tanggal, deskripsi, status_validasi)
      VALUES ('$user_id', '$tanggal', '$deskripsi', 'pending')");

  header("Location: dashboard.php");
}
?>

<h3>Isi Aktivitas Harian</h3>
<form method="POST">
  Deskripsi Aktivitas:<br>
  <textarea name="deskripsi" rows="5" cols="40" required></textarea><br>
  <button type="submit">Simpan</button>
</form>

9. Validasi Aktivitas oleh Pembimbing

proses/proses_validasi.php

php
<?php
session_start();
include '../config/koneksi.php';

// Cek jika user pembimbing
if (!isset($_SESSION['user_id']) || $_SESSION['role'] != 'pembimbing') {
  header("Location: ../auth/login.php");
  exit;
}

// Cek jika tombol validasi ditekan
if (isset($_POST['setujui'])) {
  $aktivitas_id = $_POST['aktivitas_id'];

  // Update status jadi disetujui
  $update = mysqli_query($koneksi, "UPDATE aktivitas SET status_validasi='disetujui' WHERE id='$aktivitas_id'");

  header("Location: ../pembimbing/dashboard.php");
}
?>

10. Fitur Logout

logout.php

php
<?php
session_start();
session_destroy();
header("Location: login.php");
exit;
?>

11. Tampilan dengan Bootstrap

Agar tampilan lebih rapi dan responsif, gunakan Bootstrap:

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">

Gunakan class Bootstrap untuk form dan tombol:

html
<form class="form-control">
  <input class="form-control" type="text">
  <button class="btn btn-primary">Login</button>
</form>

12. Tips Keamanan Sederhana

  • Gunakan password_hash() untuk menyimpan password.
  • Batasi akses halaman menggunakan session.
  • Gunakan prepared statement untuk menghindari SQL Injection.
  • Tambahkan validasi form input.

Keamanan Dasar

Berikut beberapa keamanan dasar yang wajib kamu terapkan:

a. Escaping Input

Gunakan

htmlspecialchars() dan mysqli_real_escape_string() 

saat menerima input dari pengguna:

php
$nama = htmlspecialchars(mysqli_real_escape_string($conn, $_POST['nama']));

b. Session Protection

Pastikan setiap halaman penting dicek session login-nya:

php
if (!isset($_SESSION['username'])) {
    header("Location: ../auth/login.php");
    exit();
}

c. Gunakan password_hash dan password_verify

Untuk menyimpan password secara aman:

php
// Saat register
$password_hash = password_hash($_POST['password'], PASSWORD_DEFAULT);

// Saat login
if (password_verify($_POST['password'], $row['password'])) {
    // login berhasil
}

13. Kesimpulan

Membangun sistem presensi dan aktivitas harian berbasis web tidaklah sulit jika dipahami secara bertahap. Dengan menggunakan PHP, MySQL, dan Laragon, Anda bisa mengembangkan sistem lokal yang cepat dan efisien.

Dengan mengikuti langkah-langkah di atas, kamu telah berhasil membuat:

  • Sistem login untuk dua peran (siswa dan pembimbing)
  • Fitur presensi dan aktivitas harian
  • Validasi laporan oleh pembimbing
  • Desain berbasis Bootstrap agar modern
  • Keamanan dasar agar data lebih aman

Website ini bisa dikembangkan lebih lanjut menjadi:

  • Sistem penilaian kinerja magang
  • Laporan mingguan/bulanan
  • Integrasi Google Calendar untuk jadwal


Dengan menyelesaikan tutorial ini, Anda telah mempelajari:

  • Cara mengatur database absensi dan aktivitas.
  • Membuat fitur login berdasarkan role pengguna.
  • Fitur input presensi dan aktivitas harian.
  • Fitur validasi pembimbing.
  • Tampilan rapi dengan Bootstrap.

Tags: absensi dan aktivitas harianabsensi onlineaktivitas harian siswaaplikasi absensiaplikasi presensi lokalbelajar PHP untuk pemulaBuild Web with PHPcara membuat website absensidashboard PHP Laravellaragon phpmyadminlaragon tutorialmembuat aplikasi web sederhanaMembuat Website SendiriMySQL DatabasePHPphp tutorialPHP web developmentpresensi siswa magangsistem informasi magangsistem monitoring kegiatansistem presensi harianStudent Data Managementtutorial php mysqlvalidasi pembimbingweb absensi magangWeb Dev Pemulawebsite absensi PHP
Previous Post
Next Post

Post comment

Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Basis Data: Pengertian, Konsep, Contoh, dan Langkah Praktis Menggunakan SQL
  • Di Balik Rekomendasi Aplikasi: Mengapa Teknologi Selalu Tahu Apa yang Kita Mau?
  • Membuat Aplikasi Otomatisasi Menggunakan Google Apps Script: Pendekatan Praktis untuk Data Analyst Pemula
  • Mengapa Data Analyst Pemula Lebih Butuh Cara Berpikir daripada Tools
  • Mengapa dan Bagaimana Cara Berkontribusi pada Proyek Open Source di Platform seperti GitHub?

Arsip

  • January 2026
  • September 2025
  • August 2025
  • July 2025
  • March 2019
  • February 2019
  • January 2019
  • December 2018
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • January 2018
  • December 2017
  • November 2017
  • October 2017
  • September 2017
  • August 2017
  • July 2017
  • June 2017
  • May 2017
  • April 2017
  • March 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
  • October 2016
  • September 2016
  • August 2016
  • July 2016
  • June 2016
  • May 2016
  • April 2016
  • March 2016
  • February 2016
  • January 2016
  • December 2015
  • November 2015
  • October 2015
  • September 2015
  • August 2015
  • July 2015
  • June 2015
  • May 2015
  • April 2015
  • March 2015
  • February 2015
  • January 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • June 2014
  • May 2014
  • April 2014
  • March 2014
  • February 2014
  • January 2014
  • December 2013
  • November 2013
  • October 2013
  • September 2013
  • August 2013
  • July 2013
  • June 2013
  • May 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • October 2012
  • September 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • December 2011
  • November 2011

Tags

apache web server dns server kursus android kursus database kursus dns dan web server kursus dns server kursus ethical hacking kursus hacking kursus jaringan kursus jaringan linux Kursus Komputer kursus komputer di solo kursus komputer di solo / surakarta kursus komputer di surakarta kursus linux Kursus Linux Forensics kursus linux networking kursus linux security kursus linux server kursus mikrotik kursus mysql kursus networking kursus network security kursus php Kursus PHP dan MySQL kursus php mysql kursus proxy kursus security kursus ubuntu kursus ubuntu server kursus web kursus web security kursus web server kursus wordpress kursus wordpress theme linux MySQL pelatihan komputer di solo PHP security training komputer training komputer di solo tutorial php ubuntu wordpress

© Edusoft Center - Kursus Komputer di Solo | 2010 - 2025 | Privacy Policy | Site Map

All Right Reserved

WhatsApp us