Files

234 lines
7.6 KiB
PHP

<?php
session_start();
require_once('inc/config.inc.php');
require_once('inc/functions.inc.php');
require_once __DIR__ . '/inc/vacation_absence.inc.php';
$user = check_user();
if (!is_admin_user()) {
die('Zugriff verweigert. Nur Chefs dürfen den Leitungskalender sehen.');
}
$schemaWarning = '';
try {
vacationAbsenceEnsureSchema($pdo);
} catch (Throwable $e) {
$schemaWarning = 'Das Abwesenheitsschema konnte nicht automatisch aktualisiert werden: ' . $e->getMessage();
}
function adminAbsenceStatusLabel(?string $status): string
{
$status = trim((string)$status);
if ($status === '' || $status === 'beantragt') {
return 'Beantragt';
}
if ($status === 'genehmigt') {
return 'Genehmigt';
}
if ($status === 'abgelehnt') {
return 'Abgelehnt';
}
return ucfirst($status);
}
function adminAbsenceColor(string $reason, string $status): string
{
$reason = vacationAbsenceNormalizeReason($reason);
$status = trim(strtolower($status));
$palette = [
'urlaub' => '#2f855a',
'krankheit_mit_atest' => '#c53030',
'krankheit_ohne_atest' => '#9b2c2c',
'berufsschule' => '#dd6b20',
'weiterbildung' => '#6b46c1',
'persoenliche_gruende' => '#4a5568',
'sonstiges' => '#718096',
];
$color = $palette[$reason] ?? '#2d3748';
if ($status === 'beantragt') {
return $color;
}
return $color;
}
function adminAbsenceEventTitle(array $row): string
{
$employee = trim(($row['vorname'] ?? '') . ' ' . ($row['nachname'] ?? ''));
$reasonLabel = vacationAbsenceReasonLabel($row['absence_reason'] ?? 'urlaub');
$statusLabel = adminAbsenceStatusLabel($row['status'] ?? '');
if ($employee === '') {
return $reasonLabel . ' (' . $statusLabel . ')';
}
if ($statusLabel !== 'Genehmigt') {
return $employee . ' · ' . $reasonLabel . ' (' . $statusLabel . ')';
}
return $employee . ' · ' . $reasonLabel;
}
$events = [];
$vacStmt = $pdo->prepare("
SELECT
v.id,
v.user_id,
v.start_date,
v.end_date,
v.days,
v.status,
v.comment_user,
v.absence_reason,
u.vorname,
u.nachname,
u.email
FROM vacations v
JOIN users u ON u.id = v.user_id
WHERE LOWER(TRIM(COALESCE(v.status, ''))) != 'abgelehnt'
ORDER BY v.start_date ASC, v.end_date ASC, u.nachname ASC, u.vorname ASC
");
$vacStmt->execute();
$vacations = $vacStmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($vacations as $row) {
$endInclusive = (new DateTime($row['end_date']))->modify('+1 day')->format('Y-m-d');
$reason = vacationAbsenceNormalizeReason($row['absence_reason'] ?? 'urlaub');
$status = trim((string)($row['status'] ?? ''));
$statusLabel = adminAbsenceStatusLabel($status);
$events[] = [
'id' => 'vac_' . $row['id'],
'title' => adminAbsenceEventTitle($row),
'start' => $row['start_date'],
'end' => $endInclusive,
'allDay' => true,
'backgroundColor' => adminAbsenceColor($reason, $status),
'borderColor' => adminAbsenceColor($reason, $status),
'extendedProps' => [
'type' => 'absence',
'user_id' => $row['user_id'],
'employee_name' => trim(($row['vorname'] ?? '') . ' ' . ($row['nachname'] ?? '')),
'email' => $row['email'] ?? '',
'status' => $status,
'status_label' => $statusLabel,
'reason' => $reason,
'reason_label' => vacationAbsenceReasonLabel($reason),
'days' => (int)($row['days'] ?? 0),
'comment' => $row['comment_user'] ?? '',
],
];
}
$companyStmt = $pdo->prepare("
SELECT id, start_date, end_date, description
FROM company_holidays
ORDER BY start_date ASC, end_date ASC
");
$companyStmt->execute();
$companyHolidays = $companyStmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($companyHolidays as $row) {
$endInclusive = (new DateTime($row['end_date']))->modify('+1 day')->format('Y-m-d');
$events[] = [
'id' => 'com_' . $row['id'],
'title' => $row['description'] ?: 'Betriebsurlaub',
'start' => $row['start_date'],
'end' => $endInclusive,
'allDay' => true,
'backgroundColor' => '#005f73',
'borderColor' => '#005f73',
'extendedProps' => [
'type' => 'company',
'description' => $row['description'] ?: 'Betriebsurlaub',
],
];
}
include 'header.php';
?>
<div class="container">
<h2>Leitungskalender</h2>
<?php if (!empty($schemaWarning)): ?>
<div class="alert alert-warning"><?php echo htmlspecialchars($schemaWarning); ?></div>
<?php endif; ?>
<div class="alert alert-info">
<strong>Hinweis:</strong> Dieser Kalender zeigt alle Abwesenheiten aller Personen sowie Betriebsurlaub.
</div>
<div id="calendar"></div>
<br>
<div class="d-flex flex-wrap" style="gap: 0.5rem;">
<span class="badge badge-success">Urlaub</span>
<span class="badge badge-danger">Krankheit mit Attest</span>
<span class="badge badge-dark">Krankheit ohne Attest</span>
<span class="badge badge-warning">Berufsschule</span>
<span class="badge badge-primary">Weiterbildung</span>
<span class="badge badge-secondary">Persönliche Gründe</span>
<span class="badge badge-light">Sonstiges</span>
<span class="badge badge-info">Betriebsurlaub</span>
</div>
<br>
<div id="eventDetails" style="display:none;">
<h4>Details</h4>
<div id="detailsContent"></div>
</div>
</div>
<link href='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css' rel='stylesheet' />
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js'></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var events = <?php echo json_encode($events, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
function formatAllDayRange(start, end) {
var startDate = new Date(start);
var endDate = end ? new Date(end) : null;
var startLabel = startDate.toLocaleDateString('de-DE');
if (!endDate) {
return startLabel;
}
var inclusiveEnd = new Date(endDate.getTime() - 24 * 60 * 60 * 1000);
return startLabel + ' - ' + inclusiveEnd.toLocaleDateString('de-DE');
}
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
firstDay: 1,
height: 700,
events: events,
eventClick: function(info) {
var ev = info.event;
var props = ev.extendedProps || {};
var html = '<strong>' + ev.title + '</strong><br>' + formatAllDayRange(ev.start, ev.end) + '<br>';
if (props.type === 'absence') {
html += 'Mitarbeiter: ' + (props.employee_name || '') + '<br>';
html += 'Grund: ' + (props.reason_label || '') + '<br>';
html += 'Status: ' + (props.status_label || '') + '<br>';
html += 'Tage: ' + (props.days || 0) + '<br>';
if (props.comment) {
html += 'Kommentar: ' + props.comment + '<br>';
}
} else if (props.type === 'company') {
html += 'Beschreibung: ' + (props.description || 'Betriebsurlaub') + '<br>';
}
document.getElementById('detailsContent').innerHTML = html;
document.getElementById('eventDetails').style.display = 'block';
}
});
calendar.render();
});
</script>
<?php include 'footer.php'; ?>