[go: up one dir, main page]

0% found this document useful (0 votes)
24 views36 pages

Doctype HTML

Uploaded by

Bagas Rahmadani
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views36 pages

Doctype HTML

Uploaded by

Bagas Rahmadani
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 36

<!

DOCTYPE html>

<html lang="id">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Sistem Absensi Guru - SMK Teknologi</title>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>

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

.container {

max-width: 1400px;

margin: 0 auto;

padding: 20px;

.header {

background: white;

padding: 20px 30px;

border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);

margin-bottom: 30px;

display: flex;

justify-content: space-between;

align-items: center;

flex-wrap: wrap;

.header h1 {

color: #2d3748;

font-size: 28px;

margin-bottom: 5px;

.header .subtitle {

color: #718096;

font-size: 14px;

.user-info {

display: flex;

align-items: center;

gap: 15px;

flex-wrap: wrap;

.role-badge {

padding: 8px 16px;

border-radius: 25px;

font-size: 12px;

font-weight: 600;
text-transform: uppercase;

.role-kepala { background: #ffd700; color: #8b6914; }

.role-guru { background: #e3f2fd; color: #1976d2; }

.role-operator { background: #e8f5e8; color: #2e7d32; }

.logout-btn {

background: #ef4444;

color: white;

border: none;

padding: 10px 20px;

border-radius: 8px;

cursor: pointer;

transition: all 0.3s;

.logout-btn:hover {

background: #dc2626;

transform: translateY(-1px);

.dashboard-grid {

display: grid;

grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));

gap: 20px;

margin-bottom: 30px;

.card {

background: white;
border-radius: 15px;

padding: 25px;

box-shadow: 0 10px 30px rgba(0,0,0,0.1);

transition: transform 0.3s ease;

.card:hover {

transform: translateY(-5px);

.stat-card {

text-align: center;

background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

color: white;

.stat-number {

font-size: 48px;

font-weight: bold;

margin-bottom: 10px;

text-shadow: 0 2px 4px rgba(0,0,0,0.3);

.stat-label {

font-size: 16px;

opacity: 0.9;

.section-title {

color: #2d3748;

font-size: 20px;
margin-bottom: 20px;

display: flex;

align-items: center;

gap: 10px;

.tabs {

display: flex;

background: #f7fafc;

border-radius: 10px;

padding: 5px;

margin-bottom: 20px;

flex-wrap: wrap;

.tab {

flex: 1;

padding: 12px 20px;

border: none;

background: none;

border-radius: 8px;

cursor: pointer;

transition: all 0.3s;

font-weight: 500;

min-width: 120px;

.tab.active {

background: white;

color: #667eea;

box-shadow: 0 2px 10px rgba(0,0,0,0.1);


}

.tab-content {

display: none;

.tab-content.active {

display: block;

.attendance-list {

max-height: 400px;

overflow-y: auto;

.attendance-item {

display: flex;

justify-content: space-between;

align-items: center;

padding: 15px;

border-bottom: 1px solid #e2e8f0;

transition: background 0.3s;

.attendance-item:hover {

background: #f7fafc;

.attendance-item:last-child {

border-bottom: none;

}
.teacher-info {

display: flex;

align-items: center;

gap: 15px;

.teacher-avatar {

width: 45px;

height: 45px;

border-radius: 50%;

background: linear-gradient(135deg, #667eea, #764ba2);

display: flex;

align-items: center;

justify-content: center;

color: white;

font-weight: bold;

font-size: 18px;

.teacher-details h4 {

color: #2d3748;

margin-bottom: 2px;

.teacher-details p {

color: #718096;

font-size: 14px;

.status-badge {
padding: 6px 12px;

border-radius: 20px;

font-size: 12px;

font-weight: 600;

.status-hadir { background: #dcfce7; color: #166534; }

.status-terlambat { background: #fef3c7; color: #92400e; }

.status-tidak-hadir { background: #fee2e2; color: #991b1b; }

.controls {

display: flex;

justify-content: between;

align-items: center;

gap: 15px;

margin-bottom: 20px;

flex-wrap: wrap;

.search-box, .date-filter, .export-btn {

padding: 10px 15px;

border: 2px solid #e2e8f0;

border-radius: 8px;

font-size: 14px;

transition: all 0.3s;

.search-box:focus, .date-filter:focus {

outline: none;

border-color: #667eea;

box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);


}

.export-btn {

background: #10b981;

color: white;

border: none;

cursor: pointer;

display: flex;

align-items: center;

gap: 8px;

.export-btn:hover {

background: #059669;

transform: translateY(-1px);

.data-table {

width: 100%;

border-collapse: collapse;

margin-top: 20px;

.data-table th, .data-table td {

padding: 12px 15px;

text-align: left;

border-bottom: 1px solid #e2e8f0;

.data-table th {

background: #f7fafc;
color: #2d3748;

font-weight: 600;

.data-table tr:hover {

background: #f7fafc;

.chart-container {

position: relative;

height: 300px;

margin-top: 20px;

.rfid-status {

display: flex;

align-items: center;

gap: 10px;

padding: 15px;

border-radius: 10px;

margin-bottom: 20px;

.rfid-connected {

background: #dcfce7;

color: #166534;

.rfid-disconnected {

background: #fee2e2;

color: #991b1b;
}

.status-dot {

width: 12px;

height: 12px;

border-radius: 50%;

.dot-green { background: #22c55e; }

.dot-red { background: #ef4444; }

@media (max-width: 768px) {

.header {

flex-direction: column;

text-align: center;

gap: 15px;

.dashboard-grid {

grid-template-columns: 1fr;

.controls {

flex-direction: column;

align-items: stretch;

.tabs {

flex-direction: column;

}
.tab {

min-width: auto;

.loading {

display: inline-block;

width: 20px;

height: 20px;

border: 3px solid #f3f3f3;

border-top: 3px solid #667eea;

border-radius: 50%;

animation: spin 1s linear infinite;

@keyframes spin {

0% { transform: rotate(0deg); }

100% { transform: rotate(360deg); }

</style>

</head>

<body>

<div class="container">

<!-- Header -->

<div class="header">

<div>

<h1>Sistem Absensi Guru</h1>

<p class="subtitle">SMK Teknologi Pekanbaru</p>

</div>

<div class="user-info">

<span class="role-badge role-kepala" id="userRole">Kepala Sekolah</span>


<span id="userName">Budi Santoso, S.Pd</span>

<button class="logout-btn" onclick="logout()">Keluar</button>

</div>

</div>

<!-- RFID Status -->

<div class="rfid-status rfid-connected" id="rfidStatus">

<div class="status-dot dot-green"></div>

<span>ESP8266 & MFRC522 Connected</span>

<span style="margin-left: auto; font-size: 12px;">Last sync: <span id="lastSync">2 detik


lalu</span></span>

</div>

<!-- Dashboard Stats -->

<div class="dashboard-grid">

<div class="card stat-card">

<div class="stat-number" id="todayPresent">24</div>

<div class="stat-label">Hadir Hari Ini</div>

</div>

<div class="card stat-card">

<div class="stat-number" id="todayLate">3</div>

<div class="stat-label">Terlambat</div>

</div>

<div class="card stat-card">

<div class="stat-number" id="todayAbsent">1</div>

<div class="stat-label">Tidak Hadir</div>

</div>

<div class="card stat-card">

<div class="stat-number" id="totalTeachers">28</div>

<div class="stat-label">Total Guru</div>

</div>
</div>

<!-- Main Content -->

<div class="card">

<div class="tabs">

<button class="tab active" onclick="switchTab('dashboard')">Dashboard</button>

<button class="tab" onclick="switchTab('attendance')">Kehadiran Hari Ini</button>

<button class="tab" onclick="switchTab('reports')">Laporan & Rekap</button>

<button class="tab" onclick="switchTab('teachers')">Data Guru</button>

</div>

<!-- Dashboard Tab -->

<div id="dashboard" class="tab-content active">

<h3 class="section-title">📊 Dashboard Kehadiran</h3>

<div class="chart-container">

<canvas id="attendanceChart"></canvas>

</div>

</div>

<!-- Attendance Tab -->

<div id="attendance" class="tab-content">

<h3 class="section-title">📅 Kehadiran Hari Ini - <span id="currentDate"></span></h3>

<div class="controls">

<input type="text" class="search-box" placeholder="Cari nama guru..."


id="searchTeacher">

<button class="export-btn" onclick="exportTodayAttendance()">

📊 Export Excel Hari Ini

</button>

</div>

<div class="attendance-list" id="attendanceList">

<!-- Attendance items will be populated by JavaScript -->


</div>

</div>

<!-- Reports Tab -->

<div id="reports" class="tab-content">

<h3 class="section-title">📈 Laporan & Rekap Absensi</h3>

<div class="controls">

<input type="date" class="date-filter" id="startDate">

<input type="date" class="date-filter" id="endDate">

<button class="export-btn" onclick="exportPeriodReport()">

📋 Export Periode

</button>

<button class="export-btn" onclick="exportMonthlyReport()" style="background:


#3b82f6;">

📅 Rekap Bulanan

</button>

</div>

<table class="data-table">

<thead>

<tr>

<th>Nama Guru</th>

<th>NIP</th>

<th>Total Hadir</th>

<th>Total Terlambat</th>

<th>Total Tidak Hadir</th>

<th>Persentase Kehadiran</th>

</tr>

</thead>

<tbody id="reportsTable">

<!-- Data will be populated by JavaScript -->

</tbody>
</table>

</div>

<!-- Teachers Tab -->

<div id="teachers" class="tab-content">

<h3 class="section-title">👥 Data Guru</h3>

<div class="controls">

<input type="text" class="search-box" placeholder="Cari guru..." id="searchAllTeachers">

<button class="export-btn" onclick="exportTeachersData()" style="background:


#8b5cf6;">

👥 Export Data Guru

</button>

</div>

<table class="data-table">

<thead>

<tr>

<th>Nama</th>

<th>NIP</th>

<th>Mata Pelajaran</th>

<th>RFID Card ID</th>

<th>Status</th>

<th>Aksi</th>

</tr>

</thead>

<tbody id="teachersTable">

<!-- Data will be populated by JavaScript -->

</tbody>

</table>

</div>

</div>

</div>
<script>

// Sample data - replace with real API calls

const currentUser = {

name: "Budi Santoso, S.Pd",

role: "kepala", // kepala, guru, operator

id: "001"

};

const teachers = [

{ id: "001", name: "Budi Santoso, S.Pd", nip: "196801012000031001", subject: "Matematika",
rfidId: "A1B2C3D4", status: "aktif" },

{ id: "002", name: "Sari Dewi, S.Kom", nip: "198503152010122002", subject: "Pemrograman",
rfidId: "E5F6G7H8", status: "aktif" },

{ id: "003", name: "Ahmad Rahman, S.T", nip: "197912052005021003", subject: "Jaringan",
rfidId: "I9J0K1L2", status: "aktif" },

{ id: "004", name: "Maya Sari, S.Pd", nip: "198207102008012004", subject: "Bahasa
Indonesia", rfidId: "M3N4O5P6", status: "aktif" },

{ id: "005", name: "Dedi Kurniawan, S.Kom", nip: "199001152012011005", subject:


"Database", rfidId: "Q7R8S9T0", status: "aktif" }

];

const todayAttendance = [

{ teacherId: "001", name: "Budi Santoso, S.Pd", checkIn: "07:15", status: "hadir", subject:
"Matematika" },

{ teacherId: "002", name: "Sari Dewi, S.Kom", checkIn: "07:45", status: "terlambat", subject:
"Pemrograman" },

{ teacherId: "003", name: "Ahmad Rahman, S.T", checkIn: "07:10", status: "hadir", subject:
"Jaringan" },

{ teacherId: "004", name: "Maya Sari, S.Pd", checkIn: "-", status: "tidak-hadir", subject:
"Bahasa Indonesia" },

{ teacherId: "005", name: "Dedi Kurniawan, S.Kom", checkIn: "07:20", status: "hadir", subject:
"Database" }

];
// Initialize page

document.addEventListener('DOMContentLoaded', function() {

setupUserRole();

setCurrentDate();

populateAttendanceList();

populateReportsTable();

populateTeachersTable();

createAttendanceChart();

simulateRealTime();

setDefaultDates();

});

function setupUserRole() {

const roleElement = document.getElementById('userRole');

const roleClasses = {

'kepala': 'role-kepala',

'guru': 'role-guru',

'operator': 'role-operator'

};

const roleTexts = {

'kepala': 'Kepala Sekolah',

'guru': 'Guru',

'operator': 'Operator'

};

roleElement.className = `role-badge ${roleClasses[currentUser.role]}`;

roleElement.textContent = roleTexts[currentUser.role];

document.getElementById('userName').textContent = currentUser.name;

}
function setCurrentDate() {

const today = new Date();

const options = {

weekday: 'long',

year: 'numeric',

month: 'long',

day: 'numeric'

};

document.getElementById('currentDate').textContent = today.toLocaleDateString('id-ID',
options);

function setDefaultDates() {

const today = new Date();

const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);

document.getElementById('startDate').valueAsDate = firstDay;

document.getElementById('endDate').valueAsDate = today;

function switchTab(tabName) {

// Remove active class from all tabs and content

document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));

document.querySelectorAll('.tab-content').forEach(content =>
content.classList.remove('active'));

// Add active class to clicked tab and corresponding content

event.target.classList.add('active');

document.getElementById(tabName).classList.add('active');

}
function populateAttendanceList() {

const listContainer = document.getElementById('attendanceList');

listContainer.innerHTML = '';

todayAttendance.forEach(attendance => {

const item = document.createElement('div');

item.className = 'attendance-item';

const statusClass = {

'hadir': 'status-hadir',

'terlambat': 'status-terlambat',

'tidak-hadir': 'status-tidak-hadir'

};

const statusText = {

'hadir': 'Hadir',

'terlambat': 'Terlambat',

'tidak-hadir': 'Tidak Hadir'

};

item.innerHTML = `

<div class="teacher-info">

<div class="teacher-avatar">${attendance.name.charAt(0)}</div>

<div class="teacher-details">

<h4>${attendance.name}</h4>

<p>${attendance.subject} • ${attendance.checkIn !== '-' ? 'Masuk: ' +


attendance.checkIn : 'Belum absen'}</p>

</div>

</div>

<div class="status-badge ${statusClass[attendance.status]}">

${statusText[attendance.status]}
</div>

`;

listContainer.appendChild(item);

});

function populateReportsTable() {

const tbody = document.getElementById('reportsTable');

tbody.innerHTML = '';

teachers.forEach(teacher => {

const row = document.createElement('tr');

const hadir = Math.floor(Math.random() * 20) + 15;

const terlambat = Math.floor(Math.random() * 3);

const tidakHadir = Math.floor(Math.random() * 2);

const total = hadir + terlambat + tidakHadir;

const persentase = ((hadir / total) * 100).toFixed(1);

row.innerHTML = `

<td>${teacher.name}</td>

<td>${teacher.nip}</td>

<td>${hadir}</td>

<td>${terlambat}</td>

<td>${tidakHadir}</td>

<td><strong>${persentase}%</strong></td>

`;

tbody.appendChild(row);

});

}
function populateTeachersTable() {

const tbody = document.getElementById('teachersTable');

tbody.innerHTML = '';

teachers.forEach(teacher => {

const row = document.createElement('tr');

row.innerHTML = `

<td>${teacher.name}</td>

<td>${teacher.nip}</td>

<td>${teacher.subject}</td>

<td><code>${teacher.rfidId}</code></td>

<td><span class="status-badge status-hadir">Aktif</span></td>

<td>

<button onclick="editTeacher('${teacher.id}')" style="background: #3b82f6; color:


white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; margin-right:
5px;">Edit</button>

<button onclick="deleteTeacher('${teacher.id}')" style="background: #ef4444; color:


white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer;">Hapus</button>

</td>

`;

tbody.appendChild(row);

});

function createAttendanceChart() {

const ctx = document.getElementById('attendanceChart').getContext('2d');

// Sample data for the last 7 days

const labels = [];

const hadirData = [];

const terlambatData = [];

const tidakHadirData = [];


for (let i = 6; i >= 0; i--) {

const date = new Date();

date.setDate(date.getDate() - i);

labels.push(date.toLocaleDateString('id-ID', { weekday: 'short', day: 'numeric' }));

hadirData.push(Math.floor(Math.random() * 5) + 20);

terlambatData.push(Math.floor(Math.random() * 3) + 1);

tidakHadirData.push(Math.floor(Math.random() * 2));

new Chart(ctx, {

type: 'line',

data: {

labels: labels,

datasets: [

label: 'Hadir',

data: hadirData,

borderColor: '#10b981',

backgroundColor: 'rgba(16, 185, 129, 0.1)',

tension: 0.4

},

label: 'Terlambat',

data: terlambatData,

borderColor: '#f59e0b',

backgroundColor: 'rgba(245, 158, 11, 0.1)',

tension: 0.4

},

{
label: 'Tidak Hadir',

data: tidakHadirData,

borderColor: '#ef4444',

backgroundColor: 'rgba(239, 68, 68, 0.1)',

tension: 0.4

},

options: {

responsive: true,

maintainAspectRatio: false,

plugins: {

legend: {

position: 'top',

},

title: {

display: true,

text: 'Tren Kehadiran 7 Hari Terakhir'

},

scales: {

y: {

beginAtZero: true

});

function simulateRealTime() {

setInterval(() => {
const lastSyncEl = document.getElementById('lastSync');

const randomSeconds = Math.floor(Math.random() * 30) + 1;

lastSyncEl.textContent = `${randomSeconds} detik lalu`;

}, 5000);

// Export functions

function exportTodayAttendance() {

const data = todayAttendance.map(att => ({

'Nama Guru': att.name,

'Mata Pelajaran': att.subject,

'Waktu Masuk': att.checkIn,

'Status': att.status === 'hadir' ? 'Hadir' : att.status === 'terlambat' ? 'Terlambat' : 'Tidak
Hadir'

}));

exportToExcel(data, 'Absensi_Hari_Ini');

function exportPeriodReport() {

const startDate = document.getElementById('startDate').value;

const endDate = document.getElementById('endDate').value;

if (!startDate || !endDate) {

alert('Mohon pilih tanggal mulai dan akhir');

return;

// Simulate period report data

const data = teachers.map(teacher => {

const hadir = Math.floor(Math.random() * 20) + 15;


const terlambat = Math.floor(Math.random() * 3);

const tidakHadir = Math.floor(Math.random() * 2);

const total = hadir + terlambat + tidakHadir;

return {

'Nama Guru': teacher.name,

'NIP': teacher.nip,

'Mata Pelajaran': teacher.subject,

'Total Hadir': hadir,

'Total Terlambat': terlambat,

'Total Tidak Hadir': tidakHadir,

'Persentase Kehadiran': `${((hadir / total) * 100).toFixed(1)}%`

};

});

exportToExcel(data, `Laporan_Periode_${startDate}_${endDate}`);

function exportMonthlyReport() {

const currentMonth = new Date().toLocaleDateString('id-ID', { year: 'numeric', month:


'long' });

// Simulate monthly data with daily breakdown

const data = [];

teachers.forEach(teacher => {

for (let day = 1; day <= 30; day++) {

const status = Math.random() > 0.1 ? (Math.random() > 0.15 ? 'Hadir' : 'Terlambat') :
'Tidak Hadir';

const waktu = status === 'Tidak Hadir' ? '-' : `07:${String(Math.floor(Math.random() *


45)).padStart(2, '0')}`;

data.push({
'Tanggal': `${day}/${new Date().getMonth() + 1}/${new Date().getFullYear()}`,

'Nama Guru': teacher.name,

'NIP': teacher.nip,

'Waktu Masuk': waktu,

'Status': status

});

});

exportToExcel(data, `Rekap_Bulanan_${currentMonth.replace(' ', '_')}`);

function exportTeachersData() {

const data = teachers.map(teacher => ({

'Nama': teacher.name,

'NIP': teacher.nip,

'Mata Pelajaran': teacher.subject,

'RFID Card ID': teacher.rfidId,

'Status': teacher.status

}));

exportToExcel(data, 'Data_Guru_Lengkap');

function exportToExcel(data, filename) {

const wb = XLSX.utils.book_new();

const ws = XLSX.utils.json_to_sheet(data);

// Auto-width columns

const colWidths = [];

const headers = Object.keys(data[0] || {});


headers.forEach(header => {

const maxWidth = Math.max(

header.length,

...data.map(row => String(row[header] || '').length)

);

colWidths.push({ width: Math.min(maxWidth + 2, 50) });

});

ws['!cols'] = colWidths;

XLSX.utils.book_append_sheet(wb, ws, 'Data');

const today = new Date().toISOString().split('T')[0];

XLSX.writeFile(wb, `${filename}_${today}.xlsx`);

showNotification('File Excel berhasil didownload!', 'success');

// Search functions

document.getElementById('searchTeacher')?.addEventListener('input', function(e) {

const searchTerm = e.target.value.toLowerCase();

const items = document.querySelectorAll('.attendance-item');

items.forEach(item => {

const teacherName = item.querySelector('.teacher-details h4').textContent.toLowerCase();

item.style.display = teacherName.includes(searchTerm) ? 'flex' : 'none';

});

});

document.getElementById('searchAllTeachers')?.addEventListener('input', function(e) {

const searchTerm = e.target.value.toLowerCase();

const rows = document.querySelectorAll('#teachersTable tr');


rows.forEach(row => {

const teacherName = row.cells[0]?.textContent.toLowerCase() || '';

const nip = row.cells[1]?.textContent.toLowerCase() || '';

const subject = row.cells[2]?.textContent.toLowerCase() || '';

const matches = teacherName.includes(searchTerm) ||

nip.includes(searchTerm) ||

subject.includes(searchTerm);

row.style.display = matches ? 'table-row' : 'none';

});

});

// Teacher management functions

function editTeacher(teacherId) {

const teacher = teachers.find(t => t.id === teacherId);

if (!teacher) return;

// Simple prompt-based editing (in real app, use modal)

const newName = prompt('Nama Guru:', teacher.name);

const newSubject = prompt('Mata Pelajaran:', teacher.subject);

const newRfidId = prompt('RFID Card ID:', teacher.rfidId);

if (newName && newSubject && newRfidId) {

teacher.name = newName;

teacher.subject = newSubject;

teacher.rfidId = newRfidId;

populateTeachersTable();

showNotification('Data guru berhasil diupdate!', 'success');


}

function deleteTeacher(teacherId) {

if (confirm('Apakah Anda yakin ingin menghapus data guru ini?')) {

const index = teachers.findIndex(t => t.id === teacherId);

if (index > -1) {

teachers.splice(index, 1);

populateTeachersTable();

showNotification('Data guru berhasil dihapus!', 'success');

function logout() {

if (confirm('Apakah Anda yakin ingin keluar?')) {

showNotification('Logout berhasil!', 'info');

// In real app, redirect to login page

setTimeout(() => {

location.reload();

}, 1000);

// Notification system

function showNotification(message, type = 'info') {

const notification = document.createElement('div');

notification.style.cssText = `

position: fixed;

top: 20px;

right: 20px;
padding: 15px 20px;

border-radius: 8px;

color: white;

font-weight: 500;

z-index: 1000;

animation: slideIn 0.3s ease;

max-width: 300px;

box-shadow: 0 4px 12px rgba(0,0,0,0.2);

`;

const colors = {

success: '#10b981',

error: '#ef4444',

info: '#3b82f6',

warning: '#f59e0b'

};

notification.style.backgroundColor = colors[type] || colors.info;

notification.textContent = message;

document.body.appendChild(notification);

setTimeout(() => {

notification.style.animation = 'slideOut 0.3s ease';

setTimeout(() => {

document.body.removeChild(notification);

}, 300);

}, 3000);

// Add CSS animations


const style = document.createElement('style');

style.textContent = `

@keyframes slideIn {

from { transform: translateX(100%); opacity: 0; }

to { transform: translateX(0); opacity: 1; }

@keyframes slideOut {

from { transform: translateX(0); opacity: 1; }

to { transform: translateX(100%); opacity: 0; }

`;

document.head.appendChild(style);

// Simulate real-time RFID scanning

function simulateRFIDScan() {

const rfidStatus = document.getElementById('rfidStatus');

const randomDelay = Math.random() * 10000 + 5000; // 5-15 seconds

setTimeout(() => {

// Simulate random teacher scanning card

const randomTeacher = teachers[Math.floor(Math.random() * teachers.length)];

const currentTime = new Date().toLocaleTimeString('id-ID', {

hour: '2-digit',

minute: '2-digit'

});

// Update attendance list if teacher not already checked in

const existingAttendance = todayAttendance.find(att => att.teacherId ===


randomTeacher.id);

if (!existingAttendance) {

const status = Math.random() > 0.8 ? 'terlambat' : 'hadir';


todayAttendance.push({

teacherId: randomTeacher.id,

name: randomTeacher.name,

checkIn: currentTime,

status: status,

subject: randomTeacher.subject

});

populateAttendanceList();

updateStats();

showNotification(`${randomTeacher.name} baru saja absen masuk (${currentTime})`,


'success');

simulateRFIDScan(); // Continue simulation

}, randomDelay);

function updateStats() {

const present = todayAttendance.filter(att => att.status === 'hadir').length;

const late = todayAttendance.filter(att => att.status === 'terlambat').length;

const absent = teachers.length - todayAttendance.length;

document.getElementById('todayPresent').textContent = present;

document.getElementById('todayLate').textContent = late;

document.getElementById('todayAbsent').textContent = absent;

// Start RFID simulation when page loads

document.addEventListener('DOMContentLoaded', function() {

setTimeout(() => {
simulateRFIDScan();

}, 3000);

});

// Simulate ESP8266 connection status

function simulateConnectionStatus() {

const rfidStatus = document.getElementById('rfidStatus');

const isConnected = Math.random() > 0.05; // 95% uptime

if (isConnected) {

rfidStatus.className = 'rfid-status rfid-connected';

rfidStatus.innerHTML = `

<div class="status-dot dot-green"></div>

<span>ESP8266 & MFRC522 Connected</span>

<span style="margin-left: auto; font-size: 12px;">Last sync: <span id="lastSync">2 detik


lalu</span></span>

`;

} else {

rfidStatus.className = 'rfid-status rfid-disconnected';

rfidStatus.innerHTML = `

<div class="status-dot dot-red"></div>

<span>ESP8266 Connection Lost</span>

<span style="margin-left: auto; font-size: 12px;">Reconnecting...</span>

`;

setTimeout(simulateConnectionStatus, Math.random() * 30000 + 30000); // Check every 30-


60 seconds

// Start connection monitoring

document.addEventListener('DOMContentLoaded', function() {
setTimeout(simulateConnectionStatus, 10000);

});

// Add keyboard shortcuts

document.addEventListener('keydown', function(e) {

if (e.ctrlKey || e.metaKey) {

switch(e.key) {

case '1':

e.preventDefault();

switchTab('dashboard');

document.querySelector('[onclick="switchTab(\'dashboard\')"]').classList.add('active');

break;

case '2':

e.preventDefault();

switchTab('attendance');

document.querySelector('[onclick="switchTab(\'attendance\')"]').classList.add('active');

break;

case '3':

e.preventDefault();

switchTab('reports');

document.querySelector('[onclick="switchTab(\'reports\')"]').classList.add('active');

break;

case '4':

e.preventDefault();

switchTab('teachers');

document.querySelector('[onclick="switchTab(\'teachers\')"]').classList.add('active');

break;

});
// Add help tooltip

const helpText = document.createElement('div');

helpText.innerHTML = '💡 Tips: Gunakan Ctrl+1,2,3,4 untuk navigasi cepat antar tab';

helpText.style.cssText = `

position: fixed;

bottom: 20px;

left: 20px;

background: rgba(0,0,0,0.8);

color: white;

padding: 8px 12px;

border-radius: 6px;

font-size: 12px;

opacity: 0.7;

z-index: 1000;

`;

document.body.appendChild(helpText);

// Auto-refresh data every 30 seconds

setInterval(() => {

if (document.visibilityState === 'visible') {

// In real app, fetch fresh data from API

updateStats();

showNotification('Data diperbarui', 'info');

}, 30000);

</script>

</body>

</html>

You might also like