|
|
|
|
|
|
|
|
class AttendanceTracker { |
|
|
constructor() { |
|
|
this.isMonitoring = false; |
|
|
this.updateInterval = null; |
|
|
this.employees = []; |
|
|
this.events = []; |
|
|
this.summary = []; |
|
|
this.searchQuery = ''; |
|
|
|
|
|
this.init(); |
|
|
} |
|
|
|
|
|
init() { |
|
|
this.setupEventListeners(); |
|
|
this.loadInitialData(); |
|
|
this.startAutoUpdate(); |
|
|
this.updateCurrentTime(); |
|
|
|
|
|
|
|
|
const today = new Date().toISOString().split('T')[0]; |
|
|
document.getElementById('summaryDate').value = today; |
|
|
} |
|
|
|
|
|
setupEventListeners() { |
|
|
|
|
|
document.getElementById('startBtn').addEventListener('click', () => this.startMonitoring()); |
|
|
document.getElementById('stopBtn').addEventListener('click', () => this.stopMonitoring()); |
|
|
document.getElementById('refreshBtn').addEventListener('click', () => this.refreshData()); |
|
|
document.getElementById('exportBtn').addEventListener('click', () => this.exportCSV()); |
|
|
|
|
|
|
|
|
document.getElementById('searchBtn').addEventListener('click', () => this.performSearch()); |
|
|
document.getElementById('clearSearchBtn').addEventListener('click', () => this.clearSearch()); |
|
|
document.getElementById('searchInput').addEventListener('keypress', (e) => { |
|
|
if (e.key === 'Enter') this.performSearch(); |
|
|
}); |
|
|
document.getElementById('searchInput').addEventListener('input', (e) => { |
|
|
if (e.target.value === '') this.clearSearch(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('addEmployeeBtn').addEventListener('click', () => this.openModal('addEmployeeModal')); |
|
|
document.getElementById('settingsBtn').addEventListener('click', () => this.openModal('settingsModal')); |
|
|
|
|
|
|
|
|
document.getElementById('addEmployeeForm').addEventListener('submit', (e) => this.handleAddEmployee(e)); |
|
|
document.getElementById('changePasswordForm').addEventListener('submit', (e) => this.handleChangePassword(e)); |
|
|
document.getElementById('deleteEmployeeForm').addEventListener('submit', (e) => this.handleDeleteEmployee(e)); |
|
|
document.getElementById('modifyEmployeeForm').addEventListener('submit', (e) => this.handleModifyEmployee(e)); |
|
|
|
|
|
|
|
|
document.getElementById('summaryDate').addEventListener('change', () => this.loadDailySummary()); |
|
|
document.getElementById('summaryRefreshBtn').addEventListener('click', () => this.loadDailySummary()); |
|
|
|
|
|
|
|
|
document.getElementById('eventDateFilter').addEventListener('change', () => this.loadEvents()); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.modal-close').forEach(btn => { |
|
|
btn.addEventListener('click', (e) => { |
|
|
const modal = e.target.closest('.modal'); |
|
|
this.closeModal(modal.id); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.modal').forEach(modal => { |
|
|
modal.addEventListener('click', (e) => { |
|
|
if (e.target === modal) { |
|
|
this.closeModal(modal.id); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
async loadInitialData() { |
|
|
await Promise.all([ |
|
|
this.loadSystemStatus(), |
|
|
this.loadEmployees(), |
|
|
this.loadEvents(), |
|
|
this.loadDailySummary(), |
|
|
this.loadSummaryStats() |
|
|
]); |
|
|
} |
|
|
|
|
|
async loadSystemStatus() { |
|
|
try { |
|
|
const response = await fetch('/api/status'); |
|
|
const status = await response.json(); |
|
|
|
|
|
document.getElementById('systemStatus').textContent = status.is_monitoring ? 'Monitoring' : 'Stopped'; |
|
|
document.getElementById('systemStatus').className = `status-value ${status.is_monitoring ? 'monitoring' : 'stopped'}`; |
|
|
document.getElementById('employeeCount').textContent = status.employee_count; |
|
|
document.getElementById('scanInterval').textContent = `${status.scan_interval} seconds`; |
|
|
document.getElementById('officeTimeout').textContent = status.office_timeout; |
|
|
|
|
|
this.isMonitoring = status.is_monitoring; |
|
|
this.updateControlButtons(); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Error loading system status:', error); |
|
|
this.showNotification('Error loading system status', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadEmployees() { |
|
|
try { |
|
|
const response = await fetch('/api/employees'); |
|
|
this.employees = await response.json(); |
|
|
this.renderEmployees(); |
|
|
} catch (error) { |
|
|
console.error('Error loading employees:', error); |
|
|
this.showNotification('Error loading employees', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadEvents() { |
|
|
try { |
|
|
const dateFilter = document.getElementById('eventDateFilter').value; |
|
|
const url = dateFilter ? `/api/attendance_events?date=${dateFilter}&limit=50` : '/api/attendance_events?limit=50'; |
|
|
const response = await fetch(url); |
|
|
this.events = await response.json(); |
|
|
this.renderEvents(); |
|
|
} catch (error) { |
|
|
console.error('Error loading events:', error); |
|
|
this.showNotification('Error loading events', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadDailySummary() { |
|
|
try { |
|
|
const date = document.getElementById('summaryDate').value; |
|
|
const response = await fetch(`/api/daily_summary?date=${date}`); |
|
|
this.summary = await response.json(); |
|
|
this.renderDailySummary(); |
|
|
} catch (error) { |
|
|
console.error('Error loading daily summary:', error); |
|
|
this.showNotification('Error loading daily summary', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async loadSummaryStats() { |
|
|
try { |
|
|
const date = document.getElementById('summaryDate').value; |
|
|
const response = await fetch(`/api/summary_stats?date=${date}`); |
|
|
const stats = await response.json(); |
|
|
|
|
|
document.getElementById('presentCount').textContent = stats.present_count || 0; |
|
|
document.getElementById('absentCount').textContent = stats.absent_count || 0; |
|
|
document.getElementById('breakCount').textContent = stats.on_break_count || 0; |
|
|
document.getElementById('timeoutCount').textContent = stats.timed_out_count || 0; |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Error loading summary stats:', error); |
|
|
} |
|
|
} |
|
|
|
|
|
renderEmployees() { |
|
|
const container = document.getElementById('employeeList'); |
|
|
|
|
|
if (this.employees.length === 0) { |
|
|
container.innerHTML = '<div class="loading">No employees found</div>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const filteredEmployees = this.searchQuery |
|
|
? this.employees.filter(emp => |
|
|
emp.name.toLowerCase().includes(this.searchQuery.toLowerCase()) || |
|
|
emp.mac.toLowerCase().includes(this.searchQuery.toLowerCase()) |
|
|
) |
|
|
: this.employees; |
|
|
|
|
|
if (filteredEmployees.length === 0) { |
|
|
container.innerHTML = '<div class="loading">No employees match your search</div>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
container.innerHTML = filteredEmployees.map(employee => ` |
|
|
<div class="employee-card" onclick="attendanceTracker.showEmployeeDetails('${employee.mac}')"> |
|
|
<div class="employee-avatar"> |
|
|
${employee.picture ? |
|
|
`<img src="${employee.picture}" alt="${employee.name}" onerror="this.style.display='none'; this.parentNode.textContent='${employee.name.charAt(0).toUpperCase()}'">` : |
|
|
employee.name.charAt(0).toUpperCase() |
|
|
} |
|
|
</div> |
|
|
<div class="employee-info"> |
|
|
<div class="employee-name">${employee.name}</div> |
|
|
<div class="employee-mac">${employee.mac}</div> |
|
|
</div> |
|
|
<div class="employee-status"> |
|
|
<span class="status-badge ${employee.status.toLowerCase().replace(' ', '')}">${employee.status}</span> |
|
|
<div class="employee-time"> |
|
|
${employee.time_in !== 'N/A' ? `In: ${employee.time_in}` : 'Not checked in'} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
`).join(''); |
|
|
} |
|
|
|
|
|
renderEvents() { |
|
|
const container = document.getElementById('eventsList'); |
|
|
|
|
|
if (this.events.length === 0) { |
|
|
container.innerHTML = '<div class="loading">No recent events</div>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
container.innerHTML = this.events.map(event => ` |
|
|
<div class="event-item ${event.event_type}"> |
|
|
<div class="event-icon"> |
|
|
<i class="fas ${this.getEventIcon(event.event_type)}"></i> |
|
|
</div> |
|
|
<div class="event-content"> |
|
|
<div class="event-name">${event.employee_name}</div> |
|
|
<div class="event-type">${this.formatEventType(event.event_type)}</div> |
|
|
</div> |
|
|
<div class="event-time">${event.time_ago}</div> |
|
|
</div> |
|
|
`).join(''); |
|
|
} |
|
|
|
|
|
renderDailySummary() { |
|
|
const container = document.getElementById('summaryTable'); |
|
|
|
|
|
if (this.summary.length === 0) { |
|
|
container.innerHTML = '<div class="loading">No summary data for selected date</div>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
container.innerHTML = ` |
|
|
<table> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>Employee</th> |
|
|
<th>Time In</th> |
|
|
<th>Time Out</th> |
|
|
<th>Work Duration</th> |
|
|
<th>Break Duration</th> |
|
|
<th>Status</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
${this.summary.map(emp => ` |
|
|
<tr> |
|
|
<td> |
|
|
<div style="display: flex; align-items: center; gap: 8px;"> |
|
|
<div class="employee-avatar" style="width: 32px; height: 32px; font-size: 12px;"> |
|
|
${emp.name.charAt(0).toUpperCase()} |
|
|
</div> |
|
|
<div> |
|
|
<div style="font-weight: 600;">${emp.name}</div> |
|
|
<div style="font-size: 11px; color: var(--text-muted);">${emp.mac_address}</div> |
|
|
</div> |
|
|
</div> |
|
|
</td> |
|
|
<td>${emp.time_in || 'N/A'}</td> |
|
|
<td>${emp.time_out || 'N/A'}</td> |
|
|
<td>${emp.total_work_formatted}</td> |
|
|
<td>${emp.total_break_formatted}</td> |
|
|
<td><span class="status-badge ${emp.status.toLowerCase().replace(' ', '')}">${emp.status}</span></td> |
|
|
</tr> |
|
|
`).join('')} |
|
|
</tbody> |
|
|
</table> |
|
|
`; |
|
|
} |
|
|
|
|
|
getEventIcon(eventType) { |
|
|
const icons = { |
|
|
'time_in': 'fa-sign-in-alt', |
|
|
'time_out': 'fa-sign-out-alt', |
|
|
'break_start': 'fa-coffee', |
|
|
'break_end': 'fa-play', |
|
|
'timeout_5pm': 'fa-clock' |
|
|
}; |
|
|
return icons[eventType] || 'fa-circle'; |
|
|
} |
|
|
|
|
|
formatEventType(eventType) { |
|
|
const formats = { |
|
|
'time_in': 'Time In', |
|
|
'time_out': 'Time Out', |
|
|
'break_start': 'Break Start', |
|
|
'break_end': 'Break End', |
|
|
'timeout_5pm': '5 PM Timeout' |
|
|
}; |
|
|
return formats[eventType] || eventType; |
|
|
} |
|
|
|
|
|
async startMonitoring() { |
|
|
try { |
|
|
const response = await fetch('/api/start_monitoring', { method: 'POST' }); |
|
|
const result = await response.json(); |
|
|
|
|
|
if (result.success) { |
|
|
this.isMonitoring = true; |
|
|
this.updateControlButtons(); |
|
|
this.showNotification('Monitoring started successfully', 'success'); |
|
|
await this.loadSystemStatus(); |
|
|
} else { |
|
|
this.showNotification(result.message, 'error'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error starting monitoring:', error); |
|
|
this.showNotification('Error starting monitoring', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async stopMonitoring() { |
|
|
try { |
|
|
const response = await fetch('/api/stop_monitoring', { method: 'POST' }); |
|
|
const result = await response.json(); |
|
|
|
|
|
if (result.success) { |
|
|
this.isMonitoring = false; |
|
|
this.updateControlButtons(); |
|
|
this.showNotification('Monitoring stopped', 'warning'); |
|
|
await this.loadSystemStatus(); |
|
|
} else { |
|
|
this.showNotification(result.message, 'error'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error stopping monitoring:', error); |
|
|
this.showNotification('Error stopping monitoring', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async refreshData() { |
|
|
document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString(); |
|
|
await this.loadInitialData(); |
|
|
this.showNotification('Data refreshed', 'success'); |
|
|
} |
|
|
|
|
|
async exportCSV() { |
|
|
try { |
|
|
const date = document.getElementById('summaryDate').value; |
|
|
const response = await fetch(`/api/export_csv?date=${date}`); |
|
|
const result = await response.json(); |
|
|
|
|
|
if (result.success) { |
|
|
this.showNotification('CSV exported successfully', 'success'); |
|
|
} else { |
|
|
this.showNotification(result.message, 'error'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error exporting CSV:', error); |
|
|
this.showNotification('Error exporting CSV', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
performSearch() { |
|
|
this.searchQuery = document.getElementById('searchInput').value.trim(); |
|
|
this.renderEmployees(); |
|
|
|
|
|
if (this.searchQuery) { |
|
|
this.showNotification(`Searching for: ${this.searchQuery}`, 'info'); |
|
|
} |
|
|
} |
|
|
|
|
|
clearSearch() { |
|
|
this.searchQuery = ''; |
|
|
document.getElementById('searchInput').value = ''; |
|
|
this.renderEmployees(); |
|
|
} |
|
|
|
|
|
updateControlButtons() { |
|
|
const startBtn = document.getElementById('startBtn'); |
|
|
const stopBtn = document.getElementById('stopBtn'); |
|
|
|
|
|
startBtn.disabled = this.isMonitoring; |
|
|
stopBtn.disabled = !this.isMonitoring; |
|
|
} |
|
|
|
|
|
startAutoUpdate() { |
|
|
|
|
|
this.updateInterval = setInterval(() => { |
|
|
if (this.isMonitoring) { |
|
|
this.loadEmployees(); |
|
|
this.loadEvents(); |
|
|
this.loadSummaryStats(); |
|
|
} |
|
|
}, 10000); |
|
|
} |
|
|
|
|
|
updateCurrentTime() { |
|
|
const updateTime = () => { |
|
|
const now = new Date(); |
|
|
document.getElementById('currentTime').textContent = now.toLocaleTimeString(); |
|
|
}; |
|
|
|
|
|
updateTime(); |
|
|
setInterval(updateTime, 1000); |
|
|
} |
|
|
|
|
|
openModal(modalId) { |
|
|
const modal = document.getElementById(modalId); |
|
|
modal.classList.add('show'); |
|
|
|
|
|
|
|
|
if (modalId === 'settingsModal') { |
|
|
this.loadSystemStatus(); |
|
|
} |
|
|
} |
|
|
|
|
|
closeModal(modalId) { |
|
|
const modal = document.getElementById(modalId); |
|
|
modal.classList.remove('show'); |
|
|
|
|
|
|
|
|
const forms = modal.querySelectorAll('form'); |
|
|
forms.forEach(form => form.reset()); |
|
|
} |
|
|
|
|
|
async handleAddEmployee(e) { |
|
|
e.preventDefault(); |
|
|
|
|
|
const formData = new FormData(e.target); |
|
|
const employeeData = { |
|
|
name: formData.get('name'), |
|
|
mac: formData.get('mac').toLowerCase(), |
|
|
picture: formData.get('picture'), |
|
|
password: formData.get('password') |
|
|
}; |
|
|
|
|
|
try { |
|
|
const response = await fetch('/api/add_employee', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify(employeeData) |
|
|
}); |
|
|
|
|
|
const result = await response.json(); |
|
|
|
|
|
if (result.success) { |
|
|
this.showNotification('Employee added successfully', 'success'); |
|
|
this.closeModal('addEmployeeModal'); |
|
|
await this.loadEmployees(); |
|
|
await this.loadSystemStatus(); |
|
|
} else { |
|
|
this.showNotification(result.message, 'error'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error adding employee:', error); |
|
|
this.showNotification('Error adding employee', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async handleChangePassword(e) { |
|
|
e.preventDefault(); |
|
|
|
|
|
const formData = new FormData(e.target); |
|
|
const newPassword = formData.get('newPassword'); |
|
|
const confirmPassword = formData.get('confirmPassword'); |
|
|
|
|
|
if (newPassword !== confirmPassword) { |
|
|
this.showNotification('Passwords do not match', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const passwordData = { |
|
|
currentPassword: formData.get('currentPassword'), |
|
|
newPassword: newPassword |
|
|
}; |
|
|
|
|
|
try { |
|
|
const response = await fetch('/api/change_password', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify(passwordData) |
|
|
}); |
|
|
|
|
|
const result = await response.json(); |
|
|
|
|
|
if (result.success) { |
|
|
this.showNotification('Password changed successfully', 'success'); |
|
|
this.closeModal('settingsModal'); |
|
|
} else { |
|
|
this.showNotification(result.message, 'error'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error changing password:', error); |
|
|
this.showNotification('Error changing password', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
showEmployeeDetails(mac) { |
|
|
const employee = this.employees.find(emp => emp.mac === mac); |
|
|
if (!employee) return; |
|
|
|
|
|
const modal = document.getElementById('employeeDetailsModal'); |
|
|
const content = document.getElementById('employeeDetailsContent'); |
|
|
|
|
|
content.innerHTML = ` |
|
|
<div style="text-align: center; margin-bottom: 20px;"> |
|
|
<div class="employee-avatar" style="width: 80px; height: 80px; font-size: 32px; margin: 0 auto 12px;"> |
|
|
${employee.picture ? |
|
|
`<img src="${employee.picture}" alt="${employee.name}" style="width: 100%; height: 100%; object-fit: cover;">` : |
|
|
employee.name.charAt(0).toUpperCase() |
|
|
} |
|
|
</div> |
|
|
<h3>${employee.name}</h3> |
|
|
<p style="color: var(--text-muted); font-family: monospace;">${employee.mac}</p> |
|
|
</div> |
|
|
|
|
|
<div class="info-grid"> |
|
|
<div class="info-item"> |
|
|
<span class="info-label">Current Status:</span> |
|
|
<span class="status-badge ${employee.status.toLowerCase().replace(' ', '')}">${employee.status}</span> |
|
|
</div> |
|
|
<div class="info-item"> |
|
|
<span class="info-label">Time In:</span> |
|
|
<span class="info-value">${employee.time_in}</span> |
|
|
</div> |
|
|
<div class="info-item"> |
|
|
<span class="info-label">Last Seen:</span> |
|
|
<span class="info-value">${employee.last_seen}</span> |
|
|
</div> |
|
|
<div class="info-item"> |
|
|
<span class="info-label">Is Present:</span> |
|
|
<span class="info-value">${employee.is_present ? 'Yes' : 'No'}</span> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
this.openModal('employeeDetailsModal'); |
|
|
|
|
|
|
|
|
this.currentEmployee = employee; |
|
|
|
|
|
|
|
|
document.getElementById('deleteEmployeeBtn').onclick = () => this.openDeleteEmployeeModal(); |
|
|
document.getElementById('modifyEmployeeBtn').onclick = () => this.openModifyEmployeeModal(); |
|
|
} |
|
|
|
|
|
openDeleteEmployeeModal() { |
|
|
if (!this.currentEmployee) return; |
|
|
|
|
|
|
|
|
document.getElementById('deleteEmployeeInfo').innerHTML = ` |
|
|
<div style="text-align: center; padding: 15px; background: #fee; border: 1px solid #fcc; border-radius: 8px;"> |
|
|
<h4>${this.currentEmployee.name}</h4> |
|
|
<p style="font-family: monospace; color: #666;">${this.currentEmployee.mac}</p> |
|
|
<p style="color: #999;">Status: ${this.currentEmployee.status}</p> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
document.getElementById('deleteAdminPassword').value = ''; |
|
|
|
|
|
this.closeModal('employeeDetailsModal'); |
|
|
this.openModal('deleteEmployeeModal'); |
|
|
} |
|
|
|
|
|
openModifyEmployeeModal() { |
|
|
if (!this.currentEmployee) return; |
|
|
|
|
|
|
|
|
document.getElementById('modifyEmployeeName').value = this.currentEmployee.name; |
|
|
document.getElementById('modifyEmployeeMac').value = this.currentEmployee.mac; |
|
|
document.getElementById('modifyEmployeePicture').value = this.currentEmployee.picture || ''; |
|
|
document.getElementById('modifyAdminPassword').value = ''; |
|
|
|
|
|
this.closeModal('employeeDetailsModal'); |
|
|
this.openModal('modifyEmployeeModal'); |
|
|
} |
|
|
|
|
|
async handleDeleteEmployee(e) { |
|
|
e.preventDefault(); |
|
|
|
|
|
if (!this.currentEmployee) { |
|
|
this.showNotification('No employee selected', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const formData = new FormData(e.target); |
|
|
const password = formData.get('password'); |
|
|
|
|
|
if (!password) { |
|
|
this.showNotification('Please enter admin password', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
const response = await fetch('/api/delete_employee', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
employee_id: this.getEmployeeIdByMac(this.currentEmployee.mac), |
|
|
password: password |
|
|
}) |
|
|
}); |
|
|
|
|
|
const result = await response.json(); |
|
|
|
|
|
if (result.success) { |
|
|
this.showNotification(result.message, 'success'); |
|
|
this.closeModal('deleteEmployeeModal'); |
|
|
await this.loadEmployees(); |
|
|
await this.loadSummaryStats(); |
|
|
} else { |
|
|
this.showNotification(result.message, 'error'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error deleting employee:', error); |
|
|
this.showNotification('Error deleting employee', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
async handleModifyEmployee(e) { |
|
|
e.preventDefault(); |
|
|
|
|
|
if (!this.currentEmployee) { |
|
|
this.showNotification('No employee selected', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const formData = new FormData(e.target); |
|
|
const name = formData.get('name'); |
|
|
const mac_address = formData.get('mac_address'); |
|
|
const picture_path = formData.get('picture_path'); |
|
|
const password = formData.get('password'); |
|
|
|
|
|
if (!password) { |
|
|
this.showNotification('Please enter admin password', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!name || !mac_address) { |
|
|
this.showNotification('Please fill in all required fields', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
const response = await fetch('/api/modify_employee', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
employee_id: this.getEmployeeIdByMac(this.currentEmployee.mac), |
|
|
name: name, |
|
|
mac_address: mac_address, |
|
|
picture_path: picture_path, |
|
|
password: password |
|
|
}) |
|
|
}); |
|
|
|
|
|
const result = await response.json(); |
|
|
|
|
|
if (result.success) { |
|
|
this.showNotification(result.message, 'success'); |
|
|
this.closeModal('modifyEmployeeModal'); |
|
|
await this.loadEmployees(); |
|
|
await this.loadSummaryStats(); |
|
|
} else { |
|
|
this.showNotification(result.message, 'error'); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error('Error modifying employee:', error); |
|
|
this.showNotification('Error modifying employee', 'error'); |
|
|
} |
|
|
} |
|
|
|
|
|
getEmployeeIdByMac(mac) { |
|
|
|
|
|
|
|
|
return mac; |
|
|
} |
|
|
|
|
|
showNotification(message, type = 'info') { |
|
|
const container = document.getElementById('notifications'); |
|
|
const notification = document.createElement('div'); |
|
|
notification.className = `notification ${type}`; |
|
|
|
|
|
const title = type.charAt(0).toUpperCase() + type.slice(1); |
|
|
notification.innerHTML = ` |
|
|
<div class="notification-title">${title}</div> |
|
|
<div class="notification-message">${message}</div> |
|
|
`; |
|
|
|
|
|
container.appendChild(notification); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
if (notification.parentNode) { |
|
|
notification.parentNode.removeChild(notification); |
|
|
} |
|
|
}, 5000); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function closeModal(modalId) { |
|
|
if (window.attendanceTracker) { |
|
|
window.attendanceTracker.closeModal(modalId); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
window.attendanceTracker = new AttendanceTracker(); |
|
|
}); |
|
|
|
|
|
|