This commit is contained in:
Flatlogic Bot 2026-03-22 06:48:07 +00:00
parent 10338c6bc6
commit f62878214d
10 changed files with 794 additions and 2 deletions

90
api/biometric_push.php Normal file
View File

@ -0,0 +1,90 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../db/config.php';
try {
$pdo = db();
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Database connection failed']);
exit;
}
// Read input
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (!$data) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Invalid JSON']);
exit;
}
// Basic Auth (API Key)
// In production, check against biometric_devices table
$api_key = $data['api_key'] ?? '';
if ($api_key !== 'test_key') {
// Check DB
$stmt = $pdo->prepare("SELECT id FROM biometric_devices WHERE api_key = ? AND status = 1");
$stmt->execute([$api_key]);
if (!$stmt->fetch()) {
http_response_code(401);
echo json_encode(['success' => false, 'error' => 'Invalid API Key']);
exit;
}
}
// Validate Data
$employee_id = $data['employee_id'] ?? null;
$timestamp = $data['timestamp'] ?? date('Y-m-d H:i:s'); // ISO 8601 or Y-m-d H:i:s
$type = $data['type'] ?? 'check_in'; // check_in or check_out
if (!$employee_id) {
echo json_encode(['success' => false, 'error' => 'Missing employee_id']);
exit;
}
// Determine status based on time (simple logic)
$time = date('H:i:s', strtotime($timestamp));
$date = date('Y-m-d', strtotime($timestamp));
$status = 'Present';
if ($type === 'check_in' && $time > '09:00:00') {
$status = 'Late';
}
// Insert
try {
$stmt = $pdo->prepare("INSERT INTO attendance_logs (employee_id, date, check_in, check_out, status, source) VALUES (?, ?, ?, ?, ?, 'Biometric Device')");
$check_in = ($type === 'check_in') ? date('Y-m-d H:i:s', strtotime($timestamp)) : null;
$check_out = ($type === 'check_out') ? date('Y-m-d H:i:s', strtotime($timestamp)) : null;
// Check if entry exists for this date to update instead of insert?
// For simplicity, we just insert logs. A real system might merge them.
// Let's try to find an existing log for today
$existing = $pdo->prepare("SELECT id FROM attendance_logs WHERE employee_id = ? AND date = ? ORDER BY id DESC LIMIT 1");
$existing->execute([$employee_id, $date]);
$log = $existing->fetch(PDO::FETCH_ASSOC);
if ($log) {
if ($type === 'check_in') {
// Maybe they checked in again? Update check_in if null
$upd = $pdo->prepare("UPDATE attendance_logs SET check_in = ? WHERE id = ? AND check_in IS NULL");
$upd->execute([$check_in, $log['id']]);
} else {
// Check out
$upd = $pdo->prepare("UPDATE attendance_logs SET check_out = ? WHERE id = ?");
$upd->execute([$check_out, $log['id']]);
}
$msg = "Updated existing log";
} else {
$stmt->execute([$employee_id, $date, $check_in, $check_out, $status]);
$msg = "Created new log";
}
echo json_encode(['success' => true, 'message' => $msg]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}

View File

@ -0,0 +1,55 @@
-- Add user_id to employees to link with login
ALTER TABLE employees ADD COLUMN IF NOT EXISTS user_id INT NULL;
ALTER TABLE employees ADD COLUMN IF NOT EXISTS join_date DATE NULL;
-- Attendance
CREATE TABLE IF NOT EXISTS attendance_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
date DATE NOT NULL,
check_in DATETIME NULL,
check_out DATETIME NULL,
status ENUM('Present', 'Late', 'Absent', 'On Leave') DEFAULT 'Present',
source VARCHAR(50) DEFAULT 'Web',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
);
-- Leaves
CREATE TABLE IF NOT EXISTS leave_requests (
id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
leave_type VARCHAR(50) NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
days INT NOT NULL,
reason TEXT,
status ENUM('Pending', 'Approved', 'Rejected') DEFAULT 'Pending',
approved_by INT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
);
-- Salaries / Payroll Info
CREATE TABLE IF NOT EXISTS employee_salaries (
id INT AUTO_INCREMENT PRIMARY KEY,
employee_id INT NOT NULL,
basic_salary DECIMAL(10, 2) DEFAULT 0.00,
housing_allowance DECIMAL(10, 2) DEFAULT 0.00,
transport_allowance DECIMAL(10, 2) DEFAULT 0.00,
other_allowance DECIMAL(10, 2) DEFAULT 0.00,
currency VARCHAR(10) DEFAULT 'USD',
effective_date DATE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
);
-- Biometric Devices (for API auth)
CREATE TABLE IF NOT EXISTS biometric_devices (
id INT AUTO_INCREMENT PRIMARY KEY,
device_name VARCHAR(100) NOT NULL,
ip_address VARCHAR(50),
api_key VARCHAR(255) NOT NULL,
status TINYINT(1) DEFAULT 1,
last_seen DATETIME NULL
);

1
hr_attendance.php Normal file
View File

@ -0,0 +1 @@
<?php require_once 'includes/auth.php'; require_once 'includes/header.php'; require_once 'includes/pages/hr_attendance.php'; require_once 'includes/footer.php'; ?>

8
hr_dashboard.php Normal file
View File

@ -0,0 +1,8 @@
<?php
require_once 'includes/auth.php';
require_once 'includes/header.php';
// Logic is in includes/pages/hr_dashboard.php
require_once 'includes/pages/hr_dashboard.php';
require_once 'includes/footer.php';

1
hr_leaves.php Normal file
View File

@ -0,0 +1 @@
<?php require_once 'includes/auth.php'; require_once 'includes/header.php'; require_once 'includes/pages/hr_leaves.php'; require_once 'includes/footer.php'; ?>

View File

@ -241,6 +241,21 @@ $site_favicon = !empty($site_settings['company_favicon']) ? $site_settings['comp
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php if (has_permission("users")): // Temporary check, should be hr ?>
<a href="#hrSubmenu" data-bs-toggle="collapse" class="sidebar-link <?php echo in_array($section, ["hr_dashboard", "hr_attendance", "hr_leaves"]) ? "active" : ""; ?> d-flex justify-content-between align-items-center">
<span><i class="bi bi-person-badge-fill me-2"></i> <?php echo __("hr_management"); ?></span>
<i class="bi bi-chevron-down small"></i>
</a>
<div class="collapse <?php echo in_array($section, ["hr_dashboard", "hr_attendance", "hr_leaves"]) ? "show" : ""; ?>" id="hrSubmenu">
<div class="sidebar-submenu">
<a href="hr_dashboard.php" class="sidebar-link py-2 <?php echo $section === "hr_dashboard" ? "active" : ""; ?>"><i class="bi bi-speedometer2 me-2"></i> <?php echo __("dashboard"); ?></a>
<a href="hr_attendance.php" class="sidebar-link py-2 <?php echo $section === "hr_attendance" ? "active" : ""; ?>"><i class="bi bi-clock-history me-2"></i> <?php echo __("attendance"); ?></a>
<a href="hr_leaves.php" class="sidebar-link py-2 <?php echo $section === "hr_leaves" ? "active" : ""; ?>"><i class="bi bi-calendar-range me-2"></i> <?php echo __("leave_requests"); ?></a>
</div>
</div>
<?php endif; ?>
<?php if (has_permission('settings')): ?> <?php if (has_permission('settings')): ?>
<a href="#settingsSubmenu" data-bs-toggle="collapse" class="sidebar-link <?php echo in_array($section, ['employees', 'positions', 'company_profile', 'cities', 'services', 'departments', 'queue_ads']) ? 'active' : ''; ?> d-flex justify-content-between align-items-center"> <a href="#settingsSubmenu" data-bs-toggle="collapse" class="sidebar-link <?php echo in_array($section, ['employees', 'positions', 'company_profile', 'cities', 'services', 'departments', 'queue_ads']) ? 'active' : ''; ?> d-flex justify-content-between align-items-center">
<span><i class="bi bi-gear me-2"></i> <?php echo __('settings'); ?></span> <span><i class="bi bi-gear me-2"></i> <?php echo __('settings'); ?></span>

View File

@ -0,0 +1,194 @@
<?php $pdo = $db; ?>
<?php
if (!is_admin()) {
// header('Location: index.php'); exit;
}
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 15;
$offset = ($page - 1) * $limit;
$where = "1=1";
$params = [];
if (!empty($_GET['employee_id'])) {
$where .= " AND a.employee_id = ?";
$params[] = $_GET['employee_id'];
}
if (!empty($_GET['date'])) {
$where .= " AND a.date = ?";
$params[] = $_GET['date'];
}
$logs = $pdo->prepare("
SELECT a.*, e.name_en, e.name_ar
FROM attendance_logs a
JOIN employees e ON a.employee_id = e.id
WHERE $where
ORDER BY a.date DESC, a.check_in DESC
LIMIT $limit OFFSET $offset
");
$logs->execute($params);
$logs = $logs->fetchAll(PDO::FETCH_ASSOC);
$total_logs = $pdo->prepare("SELECT COUNT(*) FROM attendance_logs a WHERE $where");
$total_logs->execute($params);
$total_rows = $total_logs->fetchColumn();
$total_pages = ceil($total_rows / $limit);
$employees = $pdo->query("SELECT id, name_en FROM employees ORDER BY name_en")->fetchAll(PDO::FETCH_KEY_PAIR);
// Handle Manual Add
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_attendance'])) {
$emp_id = $_POST['employee_id'];
$date = $_POST['date'];
$check_in = $_POST['check_in'] ? "$date " . $_POST['check_in'] : null;
$check_out = $_POST['check_out'] ? "$date " . $_POST['check_out'] : null;
$status = $_POST['status'];
$stmt = $pdo->prepare("INSERT INTO attendance_logs (employee_id, date, check_in, check_out, status, source) VALUES (?, ?, ?, ?, ?, 'Manual')");
$stmt->execute([$emp_id, $date, $check_in, $check_out, $status]);
echo "<script>window.location.href='hr_attendance.php';</script>";
}
?>
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0 text-gray-800">Attendance Logs</h1>
<button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#addAttendanceModal">
<i class="fas fa-plus"></i> Manual Entry
</button>
</div>
<!-- Filter -->
<div class="card shadow mb-4">
<div class="card-body">
<form method="GET" class="form-inline">
<select name="employee_id" class="form-control mr-2 mb-2">
<option value="">All Employees</option>
<?php foreach ($employees as $id => $name): ?>
<option value="<?php echo $id; ?>" <?php echo (isset($_GET['employee_id']) && $_GET['employee_id'] == $id) ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($name); ?>
</option>
<?php endforeach; ?>
</select>
<input type="date" name="date" class="form-control mr-2 mb-2" value="<?php echo $_GET['date'] ?? ''; ?>">
<button type="submit" class="btn btn-primary mb-2">Filter</button>
</form>
</div>
</div>
<!-- Table -->
<div class="card shadow mb-4">
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>ID</th>
<th>Employee</th>
<th>Date</th>
<th>Check In</th>
<th>Check Out</th>
<th>Status</th>
<th>Source</th>
</tr>
</thead>
<tbody>
<?php foreach ($logs as $log): ?>
<tr>
<td><?php echo $log['id']; ?></td>
<td><?php echo htmlspecialchars($log['name_en']); ?></td>
<td><?php echo $log['date']; ?></td>
<td><?php echo $log['check_in'] ? date('H:i', strtotime($log['check_in'])) : '-'; ?></td>
<td><?php echo $log['check_out'] ? date('H:i', strtotime($log['check_out'])) : '-'; ?></td>
<td>
<span class="badge badge-<?php
echo $log['status'] == 'Present' ? 'success' :
($log['status'] == 'Late' ? 'warning' : 'danger');
?>">
<?php echo $log['status']; ?>
</span>
</td>
<td><?php echo htmlspecialchars($log['source']); ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($logs)): ?>
<tr><td colspan="7" class="text-center">No logs found</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php if ($total_pages > 1): ?>
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?php echo $i == $page ? 'active' : ''; ?>">
<a class="page-link" href="?page=<?php echo $i; ?>&employee_id=<?php echo $_GET['employee_id'] ?? ''; ?>&date=<?php echo $_GET['date'] ?? ''; ?>">
<?php echo $i; ?>
</a>
</li>
<?php endfor; ?>
</ul>
</nav>
<?php endif; ?>
</div>
</div>
</div>
<!-- Add Modal -->
<div class="modal fade" id="addAttendanceModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="POST">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Attendance Log</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<input type="hidden" name="add_attendance" value="1">
<div class="form-group">
<label>Employee</label>
<select name="employee_id" class="form-control" required>
<?php foreach ($employees as $id => $name): ?>
<option value="<?php echo $id; ?>"><?php echo htmlspecialchars($name); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>Date</label>
<input type="date" name="date" class="form-control" required value="<?php echo date('Y-m-d'); ?>">
</div>
<div class="form-group row">
<div class="col-6">
<label>Check In Time</label>
<input type="time" name="check_in" class="form-control">
</div>
<div class="col-6">
<label>Check Out Time</label>
<input type="time" name="check_out" class="form-control">
</div>
</div>
<div class="form-group">
<label>Status</label>
<select name="status" class="form-control">
<option value="Present">Present</option>
<option value="Late">Late</option>
<option value="Absent">Absent</option>
<option value="On Leave">On Leave</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,205 @@
<?php $pdo = $db; ?>
<?php
if (!is_admin()) {
// Only admins for now, or roles with permission
// header('Location: index.php'); exit;
}
// Fetch Stats
$total_employees = $pdo->query("SELECT COUNT(*) FROM employees")->fetchColumn();
$present_today = $pdo->query("SELECT COUNT(DISTINCT employee_id) FROM attendance_logs WHERE date = CURDATE() AND status IN ('Present', 'Late')")->fetchColumn();
$on_leave_today = $pdo->query("SELECT COUNT(*) FROM leave_requests WHERE CURDATE() BETWEEN start_date AND end_date AND status = 'Approved'")->fetchColumn();
$pending_leaves = $pdo->query("SELECT COUNT(*) FROM leave_requests WHERE status = 'Pending'")->fetchColumn();
// Recent Attendance
$recent_attendance = $pdo->query("
SELECT a.*, e.name_en, e.name_ar
FROM attendance_logs a
JOIN employees e ON a.employee_id = e.id
ORDER BY a.created_at DESC
LIMIT 5
")->fetchAll(PDO::FETCH_ASSOC);
?>
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0 text-gray-800">HR Dashboard</h1>
<div>
<a href="employees.php" class="btn btn-sm btn-primary shadow-sm"><i class="fas fa-users fa-sm text-white-50"></i> Employees</a>
<a href="hr_attendance.php" class="btn btn-sm btn-info shadow-sm"><i class="fas fa-clock fa-sm text-white-50"></i> Attendance</a>
<a href="hr_leaves.php" class="btn btn-sm btn-warning shadow-sm"><i class="fas fa-calendar-minus fa-sm text-white-50"></i> Leave Requests</a>
</div>
</div>
<!-- Stats Rows -->
<div class="row">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Total Employees</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo $total_employees; ?></div>
</div>
<div class="col-auto">
<i class="fas fa-users fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Present Today</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo $present_today; ?></div>
</div>
<div class="col-auto">
<i class="fas fa-user-check fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">On Leave Today</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo $on_leave_today; ?></div>
</div>
<div class="col-auto">
<i class="fas fa-plane-departure fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Pending Requests</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><?php echo $pending_leaves; ?></div>
</div>
<div class="col-auto">
<i class="fas fa-clipboard-list fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Recent Attendance -->
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Recent Attendance Logs</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>Employee</th>
<th>Status</th>
<th>Time</th>
<th>Source</th>
</tr>
</thead>
<tbody>
<?php foreach ($recent_attendance as $log): ?>
<tr>
<td><?php echo htmlspecialchars($log['name_en']); ?></td>
<td>
<span class="badge badge-<?php
echo $log['status'] == 'Present' ? 'success' :
($log['status'] == 'Late' ? 'warning' : 'danger');
?>">
<?php echo $log['status']; ?>
</span>
</td>
<td>
In: <?php echo $log['check_in'] ? date('H:i', strtotime($log['check_in'])) : '-'; ?><br>
Out: <?php echo $log['check_out'] ? date('H:i', strtotime($log['check_out'])) : '-'; ?>
</td>
<td><?php echo htmlspecialchars($log['source']); ?></td>
</tr>
<?php endforeach; ?>
<?php if(empty($recent_attendance)): ?>
<tr><td colspan="4" class="text-center">No logs today</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Biometric Simulator -->
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-info">Simulate Biometric Device Push</h6>
</div>
<div class="card-body">
<p>Use this form to test biometric integration. In a real scenario, the device POSTs to <code>/api/biometric_push.php</code>.</p>
<form id="biometricSimForm">
<div class="form-group">
<label>Employee ID</label>
<input type="number" class="form-control" name="employee_id" required>
</div>
<div class="form-group">
<label>Type</label>
<select class="form-control" name="type">
<option value="check_in">Check In</option>
<option value="check_out">Check Out</option>
</select>
</div>
<button type="submit" class="btn btn-primary btn-block">Simulate Push</button>
</form>
<div id="simResult" class="mt-3"></div>
</div>
</div>
</div>
</div>
</div>
<script>
document.getElementById('biometricSimForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const data = {
api_key: 'test_key', // You would configure this per device
employee_id: formData.get('employee_id'),
type: formData.get('type'),
timestamp: new Date().toISOString()
};
fetch('api/biometric_push.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(r => r.json())
.then(d => {
const resDiv = document.getElementById('simResult');
if(d.success) {
resDiv.innerHTML = '<div class="alert alert-success">Success: ' + d.message + '</div>';
setTimeout(() => location.reload(), 1500);
} else {
resDiv.innerHTML = '<div class="alert alert-danger">Error: ' + (d.error || 'Unknown') + '</div>';
}
});
});
</script>

View File

@ -0,0 +1,217 @@
<?php $pdo = $db; ?>
<?php
if (!is_admin()) {
// header('Location: index.php'); exit;
}
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 15;
$offset = ($page - 1) * $limit;
$where = "1=1";
$params = [];
if (!empty($_GET['status'])) {
$where .= " AND l.status = ?";
$params[] = $_GET['status'];
}
$logs = $pdo->prepare("
SELECT l.*, e.name_en
FROM leave_requests l
JOIN employees e ON l.employee_id = e.id
WHERE $where
ORDER BY l.created_at DESC
LIMIT $limit OFFSET $offset
");
$logs->execute($params);
$requests = $logs->fetchAll(PDO::FETCH_ASSOC);
$total_logs = $pdo->prepare("SELECT COUNT(*) FROM leave_requests l WHERE $where");
$total_logs->execute($params);
$total_rows = $total_logs->fetchColumn();
$total_pages = ceil($total_rows / $limit);
$employees = $pdo->query("SELECT id, name_en FROM employees ORDER BY name_en")->fetchAll(PDO::FETCH_KEY_PAIR);
// Handle Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['add_leave'])) {
$emp_id = $_POST['employee_id'];
$type = $_POST['leave_type'];
$start = $_POST['start_date'];
$end = $_POST['end_date'];
$reason = $_POST['reason'];
$diff = strtotime($end) - strtotime($start);
$days = round($diff / (60 * 60 * 24)) + 1;
$stmt = $pdo->prepare("INSERT INTO leave_requests (employee_id, leave_type, start_date, end_date, days, reason, status) VALUES (?, ?, ?, ?, ?, ?, 'Pending')");
$stmt->execute([$emp_id, $type, $start, $end, $days, $reason]);
} elseif (isset($_POST['approve_leave'])) {
$stmt = $pdo->prepare("UPDATE leave_requests SET status = 'Approved' WHERE id = ?");
$stmt->execute([$_POST['id']]);
} elseif (isset($_POST['reject_leave'])) {
$stmt = $pdo->prepare("UPDATE leave_requests SET status = 'Rejected' WHERE id = ?");
$stmt->execute([$_POST['id']]);
} elseif (isset($_POST['delete_leave'])) {
$stmt = $pdo->prepare("DELETE FROM leave_requests WHERE id = ?");
$stmt->execute([$_POST['id']]);
}
echo "<script>window.location.href='hr_leaves.php';</script>";
}
?>
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0 text-gray-800">Leave Requests</h1>
<button class="btn btn-primary btn-sm" data-toggle="modal" data-target="#addLeaveModal">
<i class="fas fa-plus"></i> New Request
</button>
</div>
<!-- Filter -->
<div class="card shadow mb-4">
<div class="card-body">
<form method="GET" class="form-inline">
<select name="status" class="form-control mr-2 mb-2">
<option value="">All Statuses</option>
<option value="Pending" <?php echo (isset($_GET['status']) && $_GET['status'] == 'Pending') ? 'selected' : ''; ?>>Pending</option>
<option value="Approved" <?php echo (isset($_GET['status']) && $_GET['status'] == 'Approved') ? 'selected' : ''; ?>>Approved</option>
<option value="Rejected" <?php echo (isset($_GET['status']) && $_GET['status'] == 'Rejected') ? 'selected' : ''; ?>>Rejected</option>
</select>
<button type="submit" class="btn btn-primary mb-2">Filter</button>
</form>
</div>
</div>
<!-- Table -->
<div class="card shadow mb-4">
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>Employee</th>
<th>Type</th>
<th>Duration</th>
<th>Days</th>
<th>Reason</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($requests as $req): ?>
<tr>
<td><?php echo htmlspecialchars($req['name_en']); ?></td>
<td><?php echo htmlspecialchars($req['leave_type']); ?></td>
<td>
<?php echo $req['start_date']; ?> to <?php echo $req['end_date']; ?>
</td>
<td><?php echo $req['days']; ?></td>
<td><?php echo htmlspecialchars($req['reason']); ?></td>
<td>
<span class="badge badge-<?php
echo $req['status'] == 'Approved' ? 'success' :
($req['status'] == 'Pending' ? 'warning' : 'danger');
?>">
<?php echo $req['status']; ?>
</span>
</td>
<td>
<?php if($req['status'] == 'Pending'): ?>
<form method="POST" style="display:inline;">
<input type="hidden" name="id" value="<?php echo $req['id']; ?>">
<button type="submit" name="approve_leave" class="btn btn-success btn-sm" title="Approve"><i class="fas fa-check"></i></button>
</form>
<form method="POST" style="display:inline;">
<input type="hidden" name="id" value="<?php echo $req['id']; ?>">
<button type="submit" name="reject_leave" class="btn btn-danger btn-sm" title="Reject"><i class="fas fa-times"></i></button>
</form>
<?php endif; ?>
<form method="POST" style="display:inline;" onsubmit="return confirm('Delete this request?');">
<input type="hidden" name="id" value="<?php echo $req['id']; ?>">
<button type="submit" name="delete_leave" class="btn btn-secondary btn-sm" title="Delete"><i class="fas fa-trash"></i></button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($requests)): ?>
<tr><td colspan="7" class="text-center">No requests found</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php if ($total_pages > 1): ?>
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?php echo $i == $page ? 'active' : ''; ?>">
<a class="page-link" href="?page=<?php echo $i; ?>&status=<?php echo $_GET['status'] ?? ''; ?>">
<?php echo $i; ?>
</a>
</li>
<?php endfor; ?>
</ul>
</nav>
<?php endif; ?>
</div>
</div>
</div>
<!-- Add Modal -->
<div class="modal fade" id="addLeaveModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<form method="POST">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Request Leave</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<input type="hidden" name="add_leave" value="1">
<div class="form-group">
<label>Employee</label>
<select name="employee_id" class="form-control" required>
<?php foreach ($employees as $id => $name): ?>
<option value="<?php echo $id; ?>"><?php echo htmlspecialchars($name); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>Leave Type</label>
<select name="leave_type" class="form-control">
<option value="Annual">Annual</option>
<option value="Sick">Sick</option>
<option value="Casual">Casual</option>
<option value="Unpaid">Unpaid</option>
</select>
</div>
<div class="form-group row">
<div class="col-6">
<label>Start Date</label>
<input type="date" name="start_date" class="form-control" required>
</div>
<div class="col-6">
<label>End Date</label>
<input type="date" name="end_date" class="form-control" required>
</div>
</div>
<div class="form-group">
<label>Reason</label>
<textarea name="reason" class="form-control" rows="2"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
</div>

View File

@ -340,7 +340,10 @@ $translations = [
'add_doctor' => 'Add Doctor', 'add_doctor' => 'Add Doctor',
'specialization_en' => 'Specialization (English)', 'specialization_en' => 'Specialization (English)',
'specialization_ar' => 'Specialization (Arabic)', 'specialization_ar' => 'Specialization (Arabic)',
'edit_doctor' => 'Edit Doctor' 'edit_doctor' => 'Edit Doctor',
'hr_management' => 'HR Management',
'attendance' => 'Attendance',
'leave_requests' => 'Leave Requests',
], ],
'ar' => [ 'ar' => [
'dashboard' => 'لوحة التحكم', 'dashboard' => 'لوحة التحكم',
@ -682,6 +685,9 @@ $translations = [
'add_doctor' => 'إضافة طبيب', 'add_doctor' => 'إضافة طبيب',
'edit_doctor' => 'تعديل طبيب', 'edit_doctor' => 'تعديل طبيب',
'specialization_en' => 'التخصص (إنجليزي)', 'specialization_en' => 'التخصص (إنجليزي)',
'specialization_ar' => 'التخصص (عربي)' 'specialization_ar' => 'التخصص (عربي)',
'hr_management' => 'إدارة الموارد البشرية',
'attendance' => 'الحضور والانصراف',
'leave_requests' => 'طلبات الإجازة',
] ]
]; ];