93 lines
3.6 KiB
PHP
93 lines
3.6 KiB
PHP
<?php
|
|
/**
|
|
* Calendar events for the training calendar (training_calendar.php).
|
|
* Returns FullCalendar event objects for the *current* training record per
|
|
* (employee, topic) that has a next_due_date, colored by computed status.
|
|
* HR-only.
|
|
*/
|
|
require_once(__DIR__ . '/../hr_auth_check.php');
|
|
header('Content-Type: application/json');
|
|
|
|
try {
|
|
// $pdo and $currentUserId provided by hr_auth_check.php
|
|
|
|
$start = $_GET['start'] ?? null;
|
|
$end = $_GET['end'] ?? null;
|
|
$fStatus = isset($_GET['status']) ? trim($_GET['status']) : '';
|
|
$fDept = isset($_GET['department_id']) && $_GET['department_id'] !== '' ? (int)$_GET['department_id'] : 0;
|
|
$fTopic = isset($_GET['topic_id']) && $_GET['topic_id'] !== '' ? (int)$_GET['topic_id'] : 0;
|
|
$fEmp = isset($_GET['employee_id']) && $_GET['employee_id'] !== '' ? (int)$_GET['employee_id'] : 0;
|
|
|
|
$where = [];
|
|
$params = [];
|
|
|
|
// Deadlines only (one-time trainings have no next_due_date)
|
|
$where[] = "et.next_due_date IS NOT NULL";
|
|
|
|
// Only the most recent record per (employee, topic)
|
|
$where[] = "NOT EXISTS (
|
|
SELECT 1 FROM employee_trainings et2
|
|
WHERE et2.employee_id = et.employee_id
|
|
AND et2.training_topic_id = et.training_topic_id
|
|
AND (et2.completed_date > et.completed_date
|
|
OR (et2.completed_date = et.completed_date AND et2.id > et.id))
|
|
)";
|
|
|
|
if ($start && $end) {
|
|
$where[] = "et.next_due_date >= :start AND et.next_due_date <= :end";
|
|
$params['start'] = $start;
|
|
$params['end'] = $end;
|
|
}
|
|
if ($fDept > 0) { $where[] = "e.department_id = :did"; $params['did'] = $fDept; }
|
|
if ($fTopic > 0) { $where[] = "et.training_topic_id = :tid"; $params['tid'] = $fTopic; }
|
|
if ($fEmp > 0) { $where[] = "et.employee_id = :eid"; $params['eid'] = $fEmp; }
|
|
|
|
$whereSql = 'WHERE ' . implode(' AND ', $where);
|
|
|
|
$stmt = $pdo->prepare("
|
|
SELECT et.id, et.employee_id, et.next_due_date, et.reminder_days,
|
|
tt.name AS topic_name, tt.default_reminder_days AS topic_default_rem,
|
|
e.first_name, e.last_name
|
|
FROM employee_trainings et
|
|
JOIN training_topics tt ON tt.id = et.training_topic_id
|
|
JOIN employees e ON e.id = et.employee_id
|
|
$whereSql
|
|
");
|
|
$stmt->execute($params);
|
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$today = new DateTime('today');
|
|
$events = [];
|
|
|
|
foreach ($rows as $r) {
|
|
$rem = $r['reminder_days'] !== null
|
|
? (int)$r['reminder_days']
|
|
: ($r['topic_default_rem'] !== null ? (int)$r['topic_default_rem'] : 30);
|
|
|
|
$due = DateTime::createFromFormat('Y-m-d', $r['next_due_date']);
|
|
if (!$due) continue;
|
|
$daysLeft = (int)$today->diff($due)->format('%r%a');
|
|
|
|
if ($daysLeft < 0) { $code = 'expired'; $color = '#dc3545'; }
|
|
elseif ($daysLeft <= $rem){ $code = 'due_soon'; $color = '#e8930c'; }
|
|
else { $code = 'compliant'; $color = '#198754'; }
|
|
|
|
if ($fStatus !== '' && $fStatus !== $code) continue;
|
|
|
|
$name = trim($r['first_name'] . ' ' . $r['last_name']);
|
|
$events[] = [
|
|
'id' => (int)$r['id'],
|
|
'title' => $name . ' — ' . $r['topic_name'],
|
|
'start' => $r['next_due_date'],
|
|
'allDay' => true,
|
|
'backgroundColor' => $color,
|
|
'borderColor' => $color,
|
|
'url' => 'employee-profile.php?id=' . (int)$r['employee_id'] . '#tab-training',
|
|
];
|
|
}
|
|
|
|
echo json_encode($events);
|
|
} catch (Exception $e) {
|
|
echo json_encode([]);
|
|
}
|