Doctype HTML
Doctype HTML
DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<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 {
min-height: 100vh;
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
.header {
background: white;
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 {
border-radius: 25px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
.logout-btn {
background: #ef4444;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
.logout-btn:hover {
background: #dc2626;
transform: translateY(-1px);
.dashboard-grid {
display: grid;
gap: 20px;
margin-bottom: 30px;
.card {
background: white;
border-radius: 15px;
padding: 25px;
.card:hover {
transform: translateY(-5px);
.stat-card {
text-align: center;
color: white;
.stat-number {
font-size: 48px;
font-weight: bold;
margin-bottom: 10px;
.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;
border: none;
background: none;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
min-width: 120px;
.tab.active {
background: white;
color: #667eea;
.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;
.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%;
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;
.controls {
display: flex;
justify-content: between;
align-items: center;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
border-radius: 8px;
font-size: 14px;
.search-box:focus, .date-filter:focus {
outline: none;
border-color: #667eea;
.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;
text-align: left;
.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%;
.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-radius: 50%;
@keyframes spin {
0% { transform: rotate(0deg); }
</style>
</head>
<body>
<div class="container">
<div class="header">
<div>
</div>
<div class="user-info">
</div>
</div>
</div>
<div class="dashboard-grid">
</div>
<div class="stat-label">Terlambat</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="tabs">
</div>
<div class="chart-container">
<canvas id="attendanceChart"></canvas>
</div>
</div>
<div class="controls">
</button>
</div>
</div>
<div class="controls">
📋 Export Periode
</button>
📅 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>Persentase Kehadiran</th>
</tr>
</thead>
<tbody id="reportsTable">
</tbody>
</table>
</div>
<div class="controls">
</button>
</div>
<table class="data-table">
<thead>
<tr>
<th>Nama</th>
<th>NIP</th>
<th>Mata Pelajaran</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
<tbody id="teachersTable">
</tbody>
</table>
</div>
</div>
</div>
<script>
const currentUser = {
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" },
];
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 roleClasses = {
'kepala': 'role-kepala',
'guru': 'role-guru',
'operator': 'role-operator'
};
const roleTexts = {
'guru': 'Guru',
'operator': 'Operator'
};
roleElement.textContent = roleTexts[currentUser.role];
document.getElementById('userName').textContent = currentUser.name;
}
function setCurrentDate() {
const options = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
};
document.getElementById('currentDate').textContent = today.toLocaleDateString('id-ID',
options);
function setDefaultDates() {
document.getElementById('startDate').valueAsDate = firstDay;
document.getElementById('endDate').valueAsDate = today;
function switchTab(tabName) {
document.querySelectorAll('.tab-content').forEach(content =>
content.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(tabName).classList.add('active');
}
function populateAttendanceList() {
listContainer.innerHTML = '';
todayAttendance.forEach(attendance => {
item.className = 'attendance-item';
const statusClass = {
'hadir': 'status-hadir',
'terlambat': 'status-terlambat',
'tidak-hadir': 'status-tidak-hadir'
};
const statusText = {
'hadir': 'Hadir',
'terlambat': 'Terlambat',
};
item.innerHTML = `
<div class="teacher-info">
<div class="teacher-avatar">${attendance.name.charAt(0)}</div>
<div class="teacher-details">
<h4>${attendance.name}</h4>
</div>
</div>
${statusText[attendance.status]}
</div>
`;
listContainer.appendChild(item);
});
function populateReportsTable() {
tbody.innerHTML = '';
teachers.forEach(teacher => {
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() {
tbody.innerHTML = '';
teachers.forEach(teacher => {
row.innerHTML = `
<td>${teacher.name}</td>
<td>${teacher.nip}</td>
<td>${teacher.subject}</td>
<td><code>${teacher.rfidId}</code></td>
<td>
</td>
`;
tbody.appendChild(row);
});
function createAttendanceChart() {
date.setDate(date.getDate() - i);
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',
tension: 0.4
},
label: 'Terlambat',
data: terlambatData,
borderColor: '#f59e0b',
tension: 0.4
},
{
label: 'Tidak Hadir',
data: tidakHadirData,
borderColor: '#ef4444',
tension: 0.4
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
},
scales: {
y: {
beginAtZero: true
});
function simulateRealTime() {
setInterval(() => {
const lastSyncEl = document.getElementById('lastSync');
}, 5000);
// Export functions
function exportTodayAttendance() {
'Status': att.status === 'hadir' ? 'Hadir' : att.status === 'terlambat' ? 'Terlambat' : 'Tidak
Hadir'
}));
exportToExcel(data, 'Absensi_Hari_Ini');
function exportPeriodReport() {
if (!startDate || !endDate) {
return;
return {
'NIP': teacher.nip,
};
});
exportToExcel(data, `Laporan_Periode_${startDate}_${endDate}`);
function exportMonthlyReport() {
teachers.forEach(teacher => {
const status = Math.random() > 0.1 ? (Math.random() > 0.15 ? 'Hadir' : 'Terlambat') :
'Tidak Hadir';
data.push({
'Tanggal': `${day}/${new Date().getMonth() + 1}/${new Date().getFullYear()}`,
'NIP': teacher.nip,
'Status': status
});
});
function exportTeachersData() {
'Nama': teacher.name,
'NIP': teacher.nip,
'Status': teacher.status
}));
exportToExcel(data, 'Data_Guru_Lengkap');
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(data);
// Auto-width columns
header.length,
);
});
ws['!cols'] = colWidths;
XLSX.writeFile(wb, `${filename}_${today}.xlsx`);
// Search functions
document.getElementById('searchTeacher')?.addEventListener('input', function(e) {
items.forEach(item => {
});
});
document.getElementById('searchAllTeachers')?.addEventListener('input', function(e) {
nip.includes(searchTerm) ||
subject.includes(searchTerm);
});
});
function editTeacher(teacherId) {
if (!teacher) return;
teacher.name = newName;
teacher.subject = newSubject;
teacher.rfidId = newRfidId;
populateTeachersTable();
function deleteTeacher(teacherId) {
teachers.splice(index, 1);
populateTeachersTable();
function logout() {
setTimeout(() => {
location.reload();
}, 1000);
// Notification system
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 8px;
color: white;
font-weight: 500;
z-index: 1000;
max-width: 300px;
`;
const colors = {
success: '#10b981',
error: '#ef4444',
info: '#3b82f6',
warning: '#f59e0b'
};
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
style.textContent = `
@keyframes slideIn {
@keyframes slideOut {
`;
document.head.appendChild(style);
function simulateRFIDScan() {
setTimeout(() => {
hour: '2-digit',
minute: '2-digit'
});
if (!existingAttendance) {
teacherId: randomTeacher.id,
name: randomTeacher.name,
checkIn: currentTime,
status: status,
subject: randomTeacher.subject
});
populateAttendanceList();
updateStats();
}, randomDelay);
function updateStats() {
document.getElementById('todayPresent').textContent = present;
document.getElementById('todayLate').textContent = late;
document.getElementById('todayAbsent').textContent = absent;
document.addEventListener('DOMContentLoaded', function() {
setTimeout(() => {
simulateRFIDScan();
}, 3000);
});
function simulateConnectionStatus() {
if (isConnected) {
rfidStatus.innerHTML = `
`;
} else {
rfidStatus.innerHTML = `
`;
document.addEventListener('DOMContentLoaded', function() {
setTimeout(simulateConnectionStatus, 10000);
});
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
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;
border-radius: 6px;
font-size: 12px;
opacity: 0.7;
z-index: 1000;
`;
document.body.appendChild(helpText);
setInterval(() => {
updateStats();
}, 30000);
</script>
</body>
</html>