Update 34 files
- /nfc/README.md - /nfc/clean_old_logs.sh - /nfc/clockin.py - /nfc/emptycards.py - /nfc/sync_clocks.sh - /piservices/Dockerfile - /piservices/Portainer-lamp-stack.env - /piservices/Portainer-lamp-stack.yaml - /piservices/README.md - /piservices/avahi-alias.service - /piservices/clockpy.service - /piservices/emptycards.service - /piservices/nfc-sync.service - /piservices/test.sh - /www/404 - /www/get-table-data.php - /www/habiwan copy.png - /www/habiwan.png - /www/index.php - /www/log_swipe.php - /www/manage_data.php - /www/manifest.json - /www/names.csv - /www/network_audit.csv - /www/network-tracker.html - /www/nfc_debug.log - /www/scanner.html - /www/staff_names.php - /www/times.csv - /www/check_update.php - /www/dev.php - /www/favicon.png - /www/JsBarcode.all.min.js - /www/README.md
This commit is contained in:
1 parent
10824ab851
commit
a201d5d7ac
34 files changed
+9453
No files matched your search
+534
@@ -0,0 +1,534 @@
|
||||
<?php
|
||||
// Start a secure session
|
||||
session_start();
|
||||
|
||||
// 1. CHOOSE YOUR MANAGEMENT PASSWORD HERE:
|
||||
define('STAFF_PASSWORD', '1111');
|
||||
define('TIMEOUT_SECONDS', 86400); // Inactivity threshold
|
||||
|
||||
// Handle Explicit Logout
|
||||
if (isset($_GET['logout'])) {
|
||||
unset($_SESSION['authenticated']);
|
||||
unset($_SESSION['last_activity']);
|
||||
header("Location: " . $_SERVER['PHP_SELF']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle Login Form Submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['x_secure_token'])) {
|
||||
if ($_POST['x_secure_token'] === STAFF_PASSWORD) {
|
||||
$_SESSION['authenticated'] = true;
|
||||
$_SESSION['last_activity'] = time();
|
||||
header("Location: " . $_SERVER['PHP_SELF']);
|
||||
exit;
|
||||
} else {
|
||||
$login_error = "Incorrect password access denied.";
|
||||
}
|
||||
}
|
||||
|
||||
// Server-Side Inactivity Check
|
||||
if (isset($_SESSION['authenticated']) && $_SESSION['authenticated'] === true) {
|
||||
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > TIMEOUT_SECONDS)) {
|
||||
unset($_SESSION['authenticated']);
|
||||
unset($_SESSION['last_activity']);
|
||||
header("Location: " . $_SERVER['PHP_SELF'] . "?reason=timeout");
|
||||
exit;
|
||||
}
|
||||
$_SESSION['last_activity'] = time();
|
||||
}
|
||||
|
||||
if (isset($_GET['reason']) && $_GET['reason'] === 'timeout') {
|
||||
unset($_SESSION['authenticated']);
|
||||
unset($_SESSION['last_activity']);
|
||||
}
|
||||
|
||||
// If user is not authenticated, show the login gate
|
||||
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
|
||||
$display_msg = "Staff Access";
|
||||
if (isset($_GET['reason']) && $_GET['reason'] === 'timeout') {
|
||||
$login_error = "Logged out due to inactivity.";
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Staff Login</title>
|
||||
<link rel="icon" href="favicon.png" type="image/png">
|
||||
<style>
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
touch-action: manipulation; /* Improves touch response */
|
||||
}
|
||||
/* Prevent iOS zoom on input focus */
|
||||
input[type="text"],
|
||||
input[type="date"],
|
||||
input[type="password"] {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', sans-serif; }
|
||||
|
||||
/* 1. Change body to flex-column */
|
||||
body {
|
||||
background: linear-gradient(180deg, #1c3344, #102a33, #000000);
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center; /* Keeps it centered horizontally */
|
||||
}
|
||||
|
||||
/* 2. Push login box to the center of available space */
|
||||
.login-box {
|
||||
margin: auto;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 40px 30px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.5);
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 { margin-bottom: 25px; color: #0d47a1; font-size: 1.8rem; font-weight: 600; }
|
||||
input[type="password"] { width: 100%; padding: 15px; margin-bottom: 20px; border: 1px solid #ccc; border-radius: 8px; font-size: 1rem; transition: all 0.3s; }
|
||||
input[type="password"]:focus { outline: none; border-color: #1976d2; box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.15); }
|
||||
button { width: 100%; padding: 15px; background-color: #1976d2; color: white; border: none; border-radius: 8px; font-size: 1.1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 10px rgba(0,0,0,0.1); }
|
||||
button:hover { background-color: #1565c0; transform: translateY(-2px); box-shadow: 0 6px 15px rgba(25, 118, 210, 0.3); }
|
||||
.error { background: #ffebee; color: #c62828; padding: 12px; border-radius: 8px; font-weight: bold; margin-bottom: 20px; font-size: 0.9rem; border: 1px solid #ffcdd2; }
|
||||
|
||||
/* 3. Footer stays at the bottom */
|
||||
.footer { text-align: center; padding-bottom: 20px; color: rgba(255, 255, 255, 0.7); font-size: 0.9rem; }
|
||||
|
||||
/* Responsive adjustments for screens smaller than 600px */
|
||||
@media (max-width: 600px) {
|
||||
header h1 {
|
||||
/* Scales between 1.5rem and 2.8rem depending on screen width */
|
||||
font-size: clamp(1.5rem, 5vw, 2.8rem);
|
||||
margin-bottom: 15px;
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
header p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.employee-name {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.time-badge {
|
||||
font-size: 0.8rem;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
.search-box {
|
||||
max-width: 100%;
|
||||
}
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img src="favicon.png" alt="PiClock">
|
||||
<div class="login-box">
|
||||
<h2><?= htmlspecialchars($display_msg) ?></h2>
|
||||
<?php if (isset($login_error)): ?>
|
||||
<div class="error"><?= htmlspecialchars($login_error) ?></div>
|
||||
<?php endif; ?>
|
||||
<form method="POST" autocomplete="off" onsubmit="requestFS()">
|
||||
<input type="password" name="x_secure_token" placeholder="Enter Password" autocomplete="new-password" required autofocus>
|
||||
<button type="submit">Log In</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="footer">
|
||||
'PiClock' NFC Timestamp Viewer <span style="display: inline-block; transform: rotateY(180deg);">©</span> 2025-<?php echo date('Y');?>
|
||||
<br>made with ♥ by F.Javier "<a href="mailto:habiwan@me.com" style="color: #fff;">habiwan</a>" Puig Diaz
|
||||
<br><img src="habiwan.png" alt="habiwan">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>PiClock Dashboard</title>
|
||||
<link rel="icon" href="favicon.png" type="image/png">
|
||||
<style>
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
touch-action: manipulation; /* Improves touch response */
|
||||
}
|
||||
/* Prevent iOS zoom on input focus */
|
||||
input[type="text"],
|
||||
input[type="date"],
|
||||
input[type="password"] {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', sans-serif; }
|
||||
body { background: linear-gradient(180deg, #1c3344, #102a33, #000000); min-height: 100dvh; padding: 30px 20px; color: #fff; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
header { text-align: center; padding: 5px 0; margin-bottom: 1px; }
|
||||
header h1 {
|
||||
/* Scales between 1.5rem and 2.8rem depending on screen width */
|
||||
font-size: clamp(1.5rem, 5vw, 2.8rem);
|
||||
margin-bottom: 15px;
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
header p { font-size: 1.2rem; opacity: 0.9; }
|
||||
|
||||
.controls { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; flex-wrap: wrap; gap: 20px; }
|
||||
.search-box { flex: 1; min-width: 150px; max-width: 250px; }
|
||||
.search-box input { width: 100%; padding: 14px 20px; border-radius: 50px; border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.15); color: white; font-size: 1rem; }
|
||||
.search-box input:focus { outline: none; background: rgba(255, 255, 255, 0.25); border-color: #64b5f6; }
|
||||
|
||||
.date-filters { display: flex; gap: 15px; align-items: center; flex-wrap: wrap; }
|
||||
.date-group { display: flex; align-items: center; gap: 8px; }
|
||||
.date-group label { font-size: 0.9rem; color: #bbdefb; font-weight: 500; }
|
||||
.date-group input[type="date"] { padding: 11px 15px; border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.15); color: white; font-size: 0.95rem; font-family: inherit; color-scheme: dark; }
|
||||
.date-group input[type="date"]:focus { outline: none; background: rgba(255, 255, 255, 0.25); border-color: #64b5f6; }
|
||||
|
||||
.timeline-container { display: flex; flex-direction: column; gap: 25px; margin-bottom: 20px; }
|
||||
.day-card { background: rgba(255, 255, 255, 0.95); border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.3); }
|
||||
.day-header { display: flex; justify-content: space-between; align-items: center; padding: 15px 25px; background: #1976d2; color: white; }
|
||||
.day-title { font-size: 1.2rem; font-weight: 600; text-shadow: 0 1px 2px rgba(0,0,0,0.2); }
|
||||
.day-meta { display: flex; gap: 10px; align-items: center; }
|
||||
.day-meta .year, .day-meta .week { padding: 4px 10px; border-radius: 4px; font-size: 0.85rem; background: rgba(255, 255, 255, 0.2); color: white; font-weight: 500; }
|
||||
|
||||
.day-content { display: flex; flex-direction: column; }
|
||||
.employee-row { display: flex; justify-content: space-between; align-items: center; padding: 15px 25px; border-bottom: 1px solid #e0e0e0; gap: 20px; }
|
||||
.employee-row:last-child { border-bottom: none; }
|
||||
.employee-row:hover { background-color: #f5f9ff; }
|
||||
|
||||
.employee-info { display: flex; align-items: center; gap: 15px; }
|
||||
.avatar { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(45deg, #2196f3, #64b5f6); display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 1.1rem; }
|
||||
.employee-name { font-weight: 600; font-size: 1.05rem; color: #263238; }
|
||||
.card-id-sub { font-size: 0.85rem; color: #666; margin-top: 2px; }
|
||||
.badge-unknown { background-color: #ffebee; color: #c62828; padding: 4px 10px; border-radius: 12px; font-size: 0.85rem; font-weight: bold; border: 1px solid #ffcdd2; display: inline-block; }
|
||||
code { background-color: #f0f1f4; padding: 2px 6px; border-radius: 4px; font-family: monospace; color: #1976d2; font-size: 0.85rem; }
|
||||
|
||||
.punch-times { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; max-width: 60%; }
|
||||
.time-badge { background: #e3f2fd; color: #0d47a1; padding: 6px 14px; border-radius: 30px; font-size: 0.9rem; font-weight: 600; border: 1px solid #bbdefb; box-shadow: 0 2px 4px rgba(0,0,0,0.04); }
|
||||
|
||||
.table-header { display: flex; justify-content: space-between; padding: 15px 25px; background: #0d47a1; color: white; font-weight: 500; border-radius: 12px; margin-bottom: 15px; position: relative; z-index: 2; }
|
||||
.table-header a, .table-header a:link, .table-header a:visited { color: #ffffffff !important; text-decoration: none; cursor: default; }
|
||||
.footer { text-align: center; margin-top: 40px; color: rgba(255, 255, 255, 0.7); font-size: 0.9rem; }
|
||||
|
||||
/* ADDED: Visual Cues for Interactive filtering elements */
|
||||
.day-title, .year, .week, .employee-name, .badge-unknown {
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.day-title:hover, .year:hover, .week:hover, .employee-name:hover, .badge-unknown:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
/* Responsive adjustments for screens smaller than 600px */
|
||||
@media (max-width: 600px) {
|
||||
/* 1. Reclaim wasted screen space by reducing container padding */
|
||||
body {
|
||||
padding: 15px 10px;
|
||||
}
|
||||
|
||||
/* 2. Header and Search adjustments */
|
||||
header h1 {
|
||||
font-size: clamp(1.5rem, 6vw, 2.2rem) !important;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
header p { font-size: 1rem; }
|
||||
.search-box { max-width: 100%; }
|
||||
.controls { flex-direction: column; align-items: stretch; }
|
||||
.date-filters { justify-content: space-between; }
|
||||
|
||||
/* 3. Stack the Table Header */
|
||||
.table-header {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 4. Stack the Day Header so dates don't wrap aggressively */
|
||||
.day-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 12px 15px;
|
||||
gap: 8px;
|
||||
}
|
||||
.day-title { font-size: 1.1rem; }
|
||||
|
||||
/* 5. Stack the Employee Rows so time badges drop to a new line */
|
||||
.employee-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 15px;
|
||||
gap: 12px;
|
||||
}
|
||||
.employee-info { width: 100%; }
|
||||
|
||||
/* 6. Allow time punches to take full width and align left under the name */
|
||||
.punch-times {
|
||||
max-width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding-left: 55px; /* Indents them nicely under the avatar */
|
||||
}
|
||||
.time-badge {
|
||||
font-size: 0.85rem;
|
||||
padding: 5px 12px;
|
||||
}
|
||||
.employee-name { font-size: 1rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>'PiClock' NFC System</h1>
|
||||
<p>custom made Punchcard-System replacement</p>
|
||||
</header>
|
||||
<div class="controls">
|
||||
<div class="search-box">
|
||||
<input type="text" id="searchInput" placeholder="🔎 Search">
|
||||
</div>
|
||||
<div class="date-filters">
|
||||
<div class="date-group">
|
||||
<label for="startDate">From:</label>
|
||||
<input type="date" id="startDate">
|
||||
</div>
|
||||
<div class="date-group">
|
||||
<label for="endDate">To:</label>
|
||||
<input type="date" id="endDate">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-header">
|
||||
<div><a href="/staff_names.php" target="_blank">Grouped Shift Logs</a></div>
|
||||
<div id="recordCounter">Loading NFC Card swipes...</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline-container" id="live-data">
|
||||
</div>
|
||||
<div class="footer">
|
||||
'PiClock' NFC Timestamp Viewer <span style="display: inline-block; transform: rotateY(180deg);">©</span> 2025-<?php echo date('Y');?>
|
||||
<br>made with ♥ by F.Javier "<a href="mailto:habiwan@me.com" style="color: #fff;">habiwan</a>" Puig Diaz
|
||||
<br><img src="habiwan.png" alt="habiwan">
|
||||
</div>
|
||||
<script>
|
||||
// Safety net to re-enable fullscreen if lost
|
||||
window.addEventListener('load', function() {
|
||||
document.body.addEventListener('click', function() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen().catch(err => {});
|
||||
}
|
||||
}, { once: true });
|
||||
});
|
||||
|
||||
// --- INTERACTIVE CLICK LISTENERS ---
|
||||
document.getElementById('live-data').addEventListener('click', function(e) {
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const startDateInput = document.getElementById('startDate');
|
||||
const endDateInput = document.getElementById('endDate');
|
||||
|
||||
// 1. Employee Name or ID or Unknown Badge
|
||||
const clickedName = e.target.closest('.employee-name') || e.target.closest('.badge-unknown');
|
||||
if (clickedName) {
|
||||
let textToSearch = clickedName.textContent.trim();
|
||||
|
||||
// Toggle off if already searching this exact text
|
||||
if (searchInput.value === textToSearch) {
|
||||
searchInput.value = '';
|
||||
} else {
|
||||
searchInput.value = textToSearch;
|
||||
}
|
||||
filterDashboard();
|
||||
}
|
||||
|
||||
// 2. Day Title
|
||||
const clickedDay = e.target.closest('.day-title');
|
||||
if (clickedDay) {
|
||||
const d = clickedDay.closest('.day-card').dataset.date;
|
||||
|
||||
// Toggle off if already filtered to this exact day
|
||||
if (startDateInput.value === d && endDateInput.value === d) {
|
||||
startDateInput.value = '';
|
||||
endDateInput.value = '';
|
||||
} else {
|
||||
startDateInput.value = d;
|
||||
endDateInput.value = d;
|
||||
}
|
||||
filterDashboard();
|
||||
}
|
||||
|
||||
// 3. Year
|
||||
const clickedYear = e.target.closest('.year');
|
||||
if (clickedYear) {
|
||||
const y = clickedYear.dataset.year;
|
||||
const startY = y + '-01-01';
|
||||
const endY = y + '-12-31';
|
||||
|
||||
// Toggle off if already filtered to this exact year
|
||||
if (startDateInput.value === startY && endDateInput.value === endY) {
|
||||
startDateInput.value = '';
|
||||
endDateInput.value = '';
|
||||
} else {
|
||||
startDateInput.value = startY;
|
||||
endDateInput.value = endY;
|
||||
}
|
||||
filterDashboard();
|
||||
}
|
||||
|
||||
// 4. Week
|
||||
const clickedWeek = e.target.closest('.week');
|
||||
if (clickedWeek) {
|
||||
const startWk = clickedWeek.dataset.start;
|
||||
const endWk = clickedWeek.dataset.end;
|
||||
|
||||
// Toggle off if already filtered to this exact week
|
||||
if (startDateInput.value === startWk && endDateInput.value === endWk) {
|
||||
startDateInput.value = '';
|
||||
endDateInput.value = '';
|
||||
} else {
|
||||
startDateInput.value = startWk;
|
||||
endDateInput.value = endWk;
|
||||
}
|
||||
filterDashboard();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// --- FILTER LOGIC ---
|
||||
function filterDashboard() {
|
||||
const searchTerm = document.getElementById('searchInput').value.toLowerCase().trim();
|
||||
const startDate = document.getElementById('startDate').value;
|
||||
const endDate = document.getElementById('endDate').value;
|
||||
|
||||
const dayCards = document.querySelectorAll('.day-card');
|
||||
let totalVisiblePunches = 0;
|
||||
|
||||
dayCards.forEach(card => {
|
||||
const dayDate = card.getAttribute('data-date');
|
||||
|
||||
let dateMatches = true;
|
||||
if (startDate && dayDate < startDate) dateMatches = false;
|
||||
if (endDate && dayDate > endDate) dateMatches = false;
|
||||
|
||||
if (!dateMatches) {
|
||||
card.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
let visibleEmployeesInDay = 0;
|
||||
const rows = card.querySelectorAll('.employee-row');
|
||||
|
||||
rows.forEach(row => {
|
||||
const empName = row.getAttribute('data-name');
|
||||
|
||||
// Updated search logic
|
||||
const textMatches = empName.includes(searchTerm) ||
|
||||
(searchTerm === 'unknown card' && row.querySelector('.badge-unknown'));
|
||||
|
||||
if (textMatches) {
|
||||
row.style.display = '';
|
||||
visibleEmployeesInDay++;
|
||||
totalVisiblePunches += row.querySelectorAll('.time-badge').length;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
if (visibleEmployeesInDay > 0) {
|
||||
card.style.display = '';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('recordCounter').textContent = 'Total: ' + totalVisiblePunches + ' swipes';
|
||||
}
|
||||
|
||||
document.getElementById('searchInput').addEventListener('input', filterDashboard);
|
||||
document.getElementById('startDate').addEventListener('change', filterDashboard);
|
||||
document.getElementById('endDate').addEventListener('change', filterDashboard);
|
||||
|
||||
|
||||
// --- LIVE UPDATE LOGIC ---
|
||||
let currentVersion = '';
|
||||
|
||||
// Fetches the HTML from the backend and updates the container
|
||||
async function fetchTableData() {
|
||||
try {
|
||||
// Ensure this points to the new PHP script you created in the first step
|
||||
const response = await fetch('get-table-data.php?t=' + Date.now());
|
||||
if (!response.ok) return;
|
||||
const htmlRows = await response.text();
|
||||
|
||||
// Inject the new HTML
|
||||
document.getElementById('live-data').innerHTML = htmlRows;
|
||||
|
||||
// Re-run the filter immediately so new data obeys active search/dates
|
||||
filterDashboard();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching table data:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the database for new row counts
|
||||
async function checkServerForUpdates() {
|
||||
try {
|
||||
const response = await fetch('check_update.php?t=' + Date.now());
|
||||
if (!response.ok) {
|
||||
console.warn('Update background check failed with status:', response.status);
|
||||
return;
|
||||
}
|
||||
|
||||
const latestVersion = await response.text();
|
||||
|
||||
// Initial load scenario
|
||||
if (!currentVersion) {
|
||||
currentVersion = latestVersion;
|
||||
fetchTableData(); // Fetch the data for the first time
|
||||
return;
|
||||
}
|
||||
|
||||
// If a change happened in the MySQL Database
|
||||
if (latestVersion !== currentVersion) {
|
||||
console.log('Database change detected! Fetching new data...');
|
||||
currentVersion = latestVersion;
|
||||
fetchTableData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking for updates:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates every 3000 milliseconds (3 seconds)
|
||||
checkServerForUpdates();
|
||||
setInterval(checkServerForUpdates, 3000);
|
||||
</script>
|
||||
<script>
|
||||
// Pass the PHP timeout value into JavaScript to log out after inactivity
|
||||
const TIMEOUT_DURATION = <?php echo TIMEOUT_SECONDS; ?> * 1000;
|
||||
let logoutTimer;
|
||||
|
||||
function resetTimer() {
|
||||
clearTimeout(logoutTimer);
|
||||
logoutTimer = setTimeout(logoutUser, TIMEOUT_DURATION);
|
||||
}
|
||||
|
||||
function logoutUser() {
|
||||
window.location.href = "?logout=1";
|
||||
}
|
||||
|
||||
// Attach listeners
|
||||
window.onload = resetTimer;
|
||||
window.onmousemove = resetTimer;
|
||||
window.onmousedown = resetTimer;
|
||||
window.ontouchstart = resetTimer;
|
||||
window.onclick = resetTimer;
|
||||
window.onkeydown = resetTimer;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user