adding attendence
This commit is contained in:
parent
999d73eacf
commit
b98ef1276a
125
admin/attendance.php
Normal file
125
admin/attendance.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
$pdo = db();
|
||||
require_permission('all');
|
||||
|
||||
$date_from = $_GET['date_from'] ?? date('Y-m-01');
|
||||
$date_to = $_GET['date_to'] ?? date('Y-m-d');
|
||||
$user_id = $_GET['user_id'] ?? '';
|
||||
|
||||
$query = "SELECT l.*, u.full_name, u.username
|
||||
FROM attendance_logs l
|
||||
LEFT JOIN users u ON l.user_id = u.id
|
||||
WHERE DATE(l.log_timestamp) BETWEEN ? AND ?";
|
||||
$params = [$date_from, $date_to];
|
||||
|
||||
if ($user_id) {
|
||||
$query .= " AND l.user_id = ?";
|
||||
$params[] = $user_id;
|
||||
}
|
||||
|
||||
$query .= " ORDER BY l.log_timestamp DESC";
|
||||
$stmt = $pdo->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$logs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$users = $pdo->query("SELECT id, full_name, username FROM users ORDER BY full_name")->fetchAll();
|
||||
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="fw-bold mb-0">Attendance Sheet</h2>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-outline-primary rounded-pill px-3" onclick="window.print()">
|
||||
<i class="bi bi-printer me-1"></i> Print
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm rounded-4 mb-4">
|
||||
<div class="card-body p-4">
|
||||
<form method="GET" class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small fw-bold text-muted">FROM DATE</label>
|
||||
<input type="date" name="date_from" class="form-control" value="<?= $date_from ?>">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small fw-bold text-muted">TO DATE</label>
|
||||
<input type="date" name="date_to" class="form-control" value="<?= $date_to ?>">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label small fw-bold text-muted">USER</label>
|
||||
<select name="user_id" class="form-select">
|
||||
<option value="">All Users</option>
|
||||
<?php foreach ($users as $u): ?>
|
||||
<option value="<?= $u['id'] ?>" <?= $user_id == $u['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($u['full_name'] ?: $u['username']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100 rounded-pill fw-bold">Filter</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm rounded-4">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">Timestamp</th>
|
||||
<th>User</th>
|
||||
<th>Employee ID</th>
|
||||
<th>Type</th>
|
||||
<th>Device</th>
|
||||
<th class="pe-4">IP Address</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($logs)): ?>
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-5 text-muted">No attendance logs found for the selected period.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($logs as $log): ?>
|
||||
<tr>
|
||||
<td class="ps-4">
|
||||
<div class="fw-bold"><?= date('d M Y', strtotime($log['log_timestamp'])) ?></div>
|
||||
<div class="small text-muted"><?= date('H:i:s', strtotime($log['log_timestamp'])) ?></div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($log['user_id']): ?>
|
||||
<div class="fw-bold text-primary"><?= htmlspecialchars($log['full_name'] ?: $log['username']) ?></div>
|
||||
<?php else: ?>
|
||||
<span class="text-danger small">Unmapped Device ID: <?= htmlspecialchars($log['employee_id']) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><code><?= htmlspecialchars($log['employee_id']) ?></code></td>
|
||||
<td>
|
||||
<?php if ($log['log_type'] === 'IN'): ?>
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle rounded-pill">CHECK-IN</span>
|
||||
<?php elseif ($log['log_type'] === 'OUT'): ?>
|
||||
<span class="badge bg-danger-subtle text-danger border border-danger-subtle rounded-pill">CHECK-OUT</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary-subtle text-secondary border border-secondary-subtle rounded-pill"><?= $log['log_type'] ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><span class="small"><?= htmlspecialchars($log['device_id']) ?></span></td>
|
||||
<td class="pe-4 small text-muted"><?= htmlspecialchars($log['ip_address']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'includes/footer.php'; ?>
|
||||
@ -450,7 +450,7 @@ function can_view($module) {
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
$userGroupPages = ['users.php', 'user_edit.php', 'user_groups.php', 'user_group_edit.php'];
|
||||
$userGroupPages = ['users.php', 'user_edit.php', 'user_groups.php', 'user_group_edit.php', 'attendance.php'];
|
||||
$canViewUserGroup = can_view('users') || can_view('user_groups');
|
||||
if ($canViewUserGroup):
|
||||
?>
|
||||
@ -472,6 +472,11 @@ function can_view($module) {
|
||||
<?php if (can_view('user_groups')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= isActive('user_groups.php') ?>" href="user_groups.php">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= isActive('attendance.php') ?>" href="attendance.php">
|
||||
<i class="bi bi-calendar-check me-2"></i> Attendance
|
||||
</a>
|
||||
</li>
|
||||
<i class="bi bi-shield-lock me-2"></i> Roles / Groups
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -118,6 +118,8 @@ include 'includes/header.php';
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small fw-bold text-muted">ROLE / GROUP</label>
|
||||
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($user['group_name']) ?>" readonly>
|
||||
<label class="form-label small fw-bold text-muted mt-3">EMPLOYEE / BIOMETRIC ID</label>
|
||||
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($user['employee_id'] ?? 'Not assigned') ?>" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'];
|
||||
$email = $_POST['email'];
|
||||
$group_id = $_POST['group_id'];
|
||||
$employee_id = $_POST['employee_id'] ?? null;
|
||||
$is_active = isset($_POST['is_active']) ? 1 : 0;
|
||||
$assigned_outlets = $_POST['outlets'] ?? [];
|
||||
|
||||
@ -41,8 +42,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (!$message) {
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$sql = "UPDATE users SET full_name = ?, username = ?, email = ?, group_id = ?, is_active = ? WHERE id = ?";
|
||||
$params = [$full_name, $username, $email, $group_id, $is_active, $id];
|
||||
$sql = "UPDATE users SET full_name = ?, username = ?, email = ?, group_id = ?, is_active = ?, employee_id = ? WHERE id = ?";
|
||||
$params = [$full_name, $username, $email, $group_id, $is_active, $employee_id, $id];
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
@ -144,6 +145,8 @@ include 'includes/header.php';
|
||||
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($user['email']) ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small fw-bold text-muted">EMPLOYEE / BIOMETRIC ID</label>
|
||||
<input type="text" name="employee_id" class="form-control" value="<?= htmlspecialchars($user['employee_id'] ?? '') ?>" placeholder="e.g. 101">
|
||||
<label class="form-label small fw-bold text-muted">USER GROUP / ROLE</label>
|
||||
<select name="group_id" class="form-select" required>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
|
||||
@ -27,8 +27,8 @@ $query = "SELECT u.*, g.name as group_name
|
||||
LEFT JOIN user_groups g ON u.group_id = g.id";
|
||||
|
||||
if ($search) {
|
||||
$query .= " WHERE u.username LIKE ? OR u.full_name LIKE ? OR u.email LIKE ?";
|
||||
$params = ["%$search%", "%$search%", "%$search%"];
|
||||
$query .= " WHERE u.username LIKE ? OR u.full_name LIKE ? OR u.email LIKE ? OR u.employee_id LIKE ?";
|
||||
$params = ["%$search%", "%$search%", "%$search%", "%$search%"];
|
||||
}
|
||||
|
||||
$query .= " ORDER BY u.id DESC";
|
||||
@ -74,6 +74,7 @@ include 'includes/header.php';
|
||||
<tr>
|
||||
<th class="ps-4">User</th>
|
||||
<th>Role / Group</th>
|
||||
<th>Employee ID</th>
|
||||
<th>Email</th>
|
||||
<th>Status</th>
|
||||
<th>Joined</th>
|
||||
|
||||
67
api/attendance_sync.php
Normal file
67
api/attendance_sync.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// Simple API Key check (Optional but recommended)
|
||||
// In a real scenario, you'd want a more secure way to authenticate the device
|
||||
$api_key = $_GET['api_key'] ?? '';
|
||||
$expected_key = getenv('ATTENDANCE_API_KEY') ?: 'secret_device_key';
|
||||
|
||||
if ($api_key !== $expected_key && !empty($expected_key)) {
|
||||
// http_response_code(401);
|
||||
// echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
// exit;
|
||||
}
|
||||
|
||||
$input = file_get_contents('php://input');
|
||||
$data = json_decode($input, true);
|
||||
|
||||
if (!$data) {
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid JSON input']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Normalize to array of logs
|
||||
if (!isset($data[0])) {
|
||||
$logs = [$data];
|
||||
} else {
|
||||
$logs = $data;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$inserted = 0;
|
||||
$errors = [];
|
||||
|
||||
foreach ($logs as $log) {
|
||||
$emp_id = $log['employee_id'] ?? null;
|
||||
$timestamp = $log['timestamp'] ?? date('Y-m-d H:i:s');
|
||||
$type = strtoupper($log['type'] ?? 'IN');
|
||||
$device_id = $log['device_id'] ?? 'Biometric Device';
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
|
||||
if (!$emp_id) {
|
||||
$errors[] = "Missing employee_id for a log entry";
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Find user by employee_id
|
||||
$stmt = $pdo->prepare("SELECT id FROM users WHERE employee_id = ?");
|
||||
$stmt->execute([$emp_id]);
|
||||
$user = $stmt->fetch();
|
||||
$user_id = $user ? $user['id'] : null;
|
||||
|
||||
// Insert log
|
||||
$stmt = $pdo->prepare("INSERT INTO attendance_logs (user_id, employee_id, log_timestamp, log_type, device_id, ip_address) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $emp_id, $timestamp, $type, $device_id, $ip]);
|
||||
$inserted++;
|
||||
} catch (Exception $e) {
|
||||
$errors[] = "Error inserting log for $emp_id: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'inserted' => $inserted,
|
||||
'errors' => $errors
|
||||
]);
|
||||
BIN
assets/images/users/user_1_699c5922f2b0b.jpg
Normal file
BIN
assets/images/users/user_1_699c5922f2b0b.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
15
db/migrations/017_attendance_system.sql
Normal file
15
db/migrations/017_attendance_system.sql
Normal file
@ -0,0 +1,15 @@
|
||||
-- Add employee_id to users to map biometric device IDs
|
||||
ALTER TABLE users ADD COLUMN employee_id VARCHAR(50) UNIQUE AFTER full_name;
|
||||
|
||||
-- Table to store raw attendance logs from biometric device
|
||||
CREATE TABLE IF NOT EXISTS attendance_logs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT,
|
||||
employee_id VARCHAR(50), -- Mapping from the device
|
||||
log_timestamp DATETIME,
|
||||
log_type ENUM('IN', 'OUT', 'OTHER') DEFAULT 'IN',
|
||||
device_id VARCHAR(100),
|
||||
ip_address VARCHAR(45),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
||||
Loading…
x
Reference in New Issue
Block a user