Files
2026-07-22 01:12:45 +02:00

439 lines
14 KiB
PHP

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
date_default_timezone_set("Europe/Berlin");
require_once "config/config.php";
include("Data.class.php");
$Data = new Data($pdo);
session_start();
// Login-Check
if (!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true) {
header("location: index.php");
exit;
}
// --- STATISTIKEN / DATEN HOLEN ---
// Status-Counts
$statusMap = [
0 => "Offen",
1 => "Bereit",
2 => "Erledigt",
3 => "Storniert"
];
$statusCounts = [
0 => 0,
1 => 0,
2 => 0,
3 => 0
];
$stmt = $pdo->query("SELECT status, COUNT(*) AS cnt FROM termine GROUP BY status");
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$s = (int)$row['status'];
if (isset($statusCounts[$s])) {
$statusCounts[$s] = (int)$row['cnt'];
}
}
// Nächste 5 Termine
$nowTs = time();
$nextStmt = $pdo->prepare("
SELECT t.*, u.name AS user_name
FROM termine t
LEFT JOIN users u ON u.id = t.user_id
WHERE t.startzeit >= :now
ORDER BY t.startzeit ASC
LIMIT 5
");
$nextStmt->execute([':now' => $nowTs]);
$nextEvents = $nextStmt->fetchAll(PDO::FETCH_ASSOC);
// Letzte Änderungen (Activity Log)
$changesStmt = $pdo->query("
SELECT c.*, u.name AS user_name
FROM changes c
LEFT JOIN users u ON u.id = c.user_id
ORDER BY c.timestamp DESC
LIMIT 5
");
$recentChanges = $changesStmt->fetchAll(PDO::FETCH_ASSOC);
// Einnahmen dieses Monats
$currentMonth = date('Y-m-01 00:00:00');
$monthStartTs = strtotime($currentMonth);
$nextMonthTs = strtotime('+1 month', $monthStartTs);
$gageStmt = $pdo->prepare("
SELECT
SUM(gage) AS sum_gage,
COUNT(*) AS count_terms
FROM termine
WHERE startzeit >= :start
AND startzeit < :end
AND status = 2 -- nur abgeschlossene Termine zählen!
");
$gageStmt->execute([
':start' => $monthStartTs,
':end' => $nextMonthTs
]);
$gageData = $gageStmt->fetch(PDO::FETCH_ASSOC);
$sumGage = (float)($gageData['sum_gage'] ?? 0);
$countGigs = (int)($gageData['count_terms'] ?? 0);
// Offene ToDos (Beispiele: Gage = 0, Endzeit = 0)
$todoGageStmt = $pdo->query("SELECT COUNT(*) FROM termine WHERE (gage = 0 OR gage IS NULL)");
$todoNoGage = (int)$todoGageStmt->fetchColumn();
$todoEndStmt = $pdo->query("SELECT COUNT(*) FROM termine WHERE (endzeit = 0 OR endzeit IS NULL)");
$todoNoEnd = (int)$todoEndStmt->fetchColumn();
?>
<style>
/* Allgemeines Dashboard-Layout */
.dashboard-section-title {
font-weight: 600;
margin-bottom: 0.5rem;
}
.dashboard-subtitle {
color: #6c757d;
font-size: 0.9rem;
}
/* Status-Kacheln */
.status-card {
border-radius: 12px;
padding: 14px 16px;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
box-shadow: 0 3px 8px rgba(0,0,0,0.15);
}
.status-card {
transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.status-card:hover {
transform: translateY(-3px);
box-shadow: 0 5px 14px rgba(0, 0, 0, 0.25);
cursor: pointer;
}
.status-label {
font-size: 0.9rem;
opacity: 0.9;
}
.status-value {
font-size: 1.6rem;
font-weight: 700;
}
/* Farben für Status */
.status-open { background: #a0a0a0; } /* blau */
.status-ready { background: #0d6efd; color: #212529; }
.status-done { background: #198754; }
.status-cancel { background: #dc3545; }
/* Karten fürs Dashboard */
.card-soft {
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
/* Nächste Termine */
.next-event-title {
font-weight: 600;
font-size: 0.95rem;
}
.next-event-meta {
font-size: 0.85rem;
color: #6c757d;
}
/* Activity Log im Dashboard */
.activity-entry {
font-size: 0.9rem;
padding: 8px 0;
border-bottom: 1px solid #f1f3f5;
}
.activity-entry:last-child {
border-bottom: none;
}
.activity-time {
font-size: 0.8rem;
color: #6c757d;
}
/* ToDo Badges */
.todo-item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 0.9rem;
padding: 6px 0;
}
.status-link {
text-decoration: none;
display: block;
}
.status-link:hover .status-card {
transform: scale(1.03);
box-shadow: 0 6px 16px rgba(0,0,0,0.15);
}
/* Mobile spacing */
@media (max-width: 768px){
.status-card {
margin-bottom: 10px;
}
}
</style>
<div class="container mt-4 mb-5">
<!-- Überschrift + Quick-Action -->
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-4">
<div>
<h3 class="mb-1">Dashboard</h3>
<div class="dashboard-subtitle">
Überblick über kommende Termine, Status & Änderungen.
</div>
</div>
<div class="mt-2 mt-md-0">
<a href="termin_create.php" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Neuer Termin
</a>
</div>
</div>
<!-- STATUS-KACHELN -->
<div class="row g-3 mb-4">
<div class="col-6 col-md-3">
<a href="termine.php?status=0" class="status-link">
<div class="status-card status-open">
<div class="status-label">Offen</div>
<div class="status-value"><?= $statusCounts[0]; ?></div>
</div>
</a>
</div>
<div class="col-6 col-md-3">
<a href="termine.php?status=1" class="status-link">
<div class="status-card status-ready">
<div class="status-label">Bereit</div>
<div class="status-value"><?= $statusCounts[1]; ?></div>
</div>
</a>
</div>
<div class="col-6 col-md-3">
<a href="termine.php?status=2" class="status-link">
<div class="status-card status-done">
<div class="status-label">Abgeschlossen</div>
<div class="status-value"><?= $statusCounts[2]; ?></div>
</div>
</a>
</div>
<div class="col-6 col-md-3">
<a href="termine.php?status=3" class="status-link">
<div class="status-card status-cancel">
<div class="status-label">Storniert</div>
<div class="status-value"><?= $statusCounts[3]; ?></div>
</div>
</a>
</div>
</div>
<!-- OBERE REIHE: Nächste Termine + ToDos -->
<div class="row g-4 mb-4">
<!-- Nächste Termine -->
<div class="col-12 col-lg-8">
<div class="card card-soft">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<div class="dashboard-section-title">
<i class="bi bi-calendar-event me-1"></i> Nächste Termine
</div>
<a href="termine.php" class="btn btn-sm btn-outline-secondary">
Alle Termine
</a>
</div>
<?php if (count($nextEvents) === 0): ?>
<p class="text-muted mb-0">Keine kommenden Termine eingetragen.</p>
<?php else: ?>
<div class="list-group list-group-flush">
<?php foreach ($nextEvents as $ev): ?>
<?php
$startStr = date('d.m.Y H:i', $ev['startzeit']);
$statusLabel = $statusMap[$ev['status']] ?? $ev['status'];
$badgeClass = match((int)$ev['status']) {
0 => 'bg-secondary',
1 => 'bg-primary',
2 => 'bg-success',
3 => 'bg-danger',
default => 'bg-secondary'
};
?>
<a href="termin_show.php?id=<?= $ev['id']; ?>"
class="list-group-item list-group-item-action d-flex justify-content-between align-items-start">
<div class="me-3">
<div class="next-event-title"><?= htmlspecialchars($ev['location']); ?></div>
<div class="next-event-meta">
<i class="bi bi-clock"></i> <?= $startStr; ?>
<?php if (!empty($ev['user_name'])): ?>
· <i class="bi bi-person"></i> <?= htmlspecialchars($ev['user_name']); ?>
<?php endif; ?>
</div>
</div>
<span class="badge rounded-pill <?= $badgeClass; ?>">
<?= $statusLabel; ?>
</span>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- ToDos / Quick Infos -->
<div class="col-12 col-lg-4">
<div class="card card-soft mb-3">
<div class="card-body">
<div class="dashboard-section-title mb-2">
<i class="bi bi-list-check me-1"></i> Offene Punkte
</div>
<div class="todo-item">
<span>Termine ohne Gage</span>
<span class="badge bg-warning text-dark"><?= $todoNoGage; ?></span>
</div>
<div class="todo-item">
<span>Termine ohne Endzeit</span>
<span class="badge bg-warning text-dark"><?= $todoNoEnd; ?></span>
</div>
<!-- ggf. weitere ToDos -->
</div>
</div>
<div class="card card-soft">
<div class="card-body">
<div class="dashboard-section-title mb-1">
<i class="bi bi-cash-stack me-1"></i> Einnahmen <?= date('m.Y'); ?>
</div>
<p class="mb-1">
<strong><?= number_format($sumGage, 2, ',', '.'); ?> €</strong> Gesamt
</p>
<p class="mb-0 text-muted">
<?= $countGigs; ?> Termin(e) in diesem Monat.
</p>
</div>
</div>
</div>
</div>
<!-- UNTERE REIHE: Letzte Änderungen -->
<div class="row g-4">
<div class="col-12 col-lg-8">
<div class="card card-soft">
<div class="card-body">
<div class="dashboard-section-title mb-2">
<i class="bi bi-clock-history me-1"></i> Letzte Änderungen
</div>
<?php if (count($recentChanges) === 0): ?>
<p class="text-muted mb-0">Noch keine Änderungen protokolliert.</p>
<?php else: ?>
<?php foreach ($recentChanges as $change): ?>
<?php
$timeStr = date('d.m.Y H:i', $change['timestamp']);
$userName = $change['user_name'] ?? 'Unbekannt';
$field = strtoupper($change['was']);
$termId = $change['termin_id'];
// --- ALT UND NEU LESEN ---
$alt = $change['alt'];
$neu = $change['neu'];
// Spezielle Formatierungen
if ($field === 'STATUS') {
$alt = $statusMap[$alt] ?? $alt;
$neu = $statusMap[$neu] ?? $neu;
}
if (in_array($field, ['STARTZEIT','ENDZEIT','REALENDZEIT'])) {
if ($alt > 0) $alt = date('d.m.Y H:i', $alt);
$neu = date('d.m.Y H:i', $neu);
}
if ($field === 'ANWESEND') {
$altArr = json_decode($alt, true) ?: [];
$neuArr = json_decode($neu, true) ?: [];
$alt = implode(', ', array_map(fn($id)=>$Data->getDataByID($id,"name"), $altArr));
$neu = implode(', ', array_map(fn($id)=>$Data->getDataByID($id,"name"), $neuArr));
}
// CREATE hat keinen alten Wert
$isCreate = ($field === 'CREATE');
$isDelete = ($field === 'DELETE');
?>
<!-- KLICKBARE ÄNDERUNG -->
<a href="termin_show.php?id=<?= $termId ?>"
class="text-decoration-none text-dark">
<div class="activity-entry">
<div class="activity-time">
<i class="bi bi-clock"></i> <?= $timeStr ?>
• <span class="text-primary">#<?= $termId ?></span>
</div>
<div>
<strong><?= htmlspecialchars($userName) ?></strong>
<?php if ($isCreate): ?>
hat einen neuen <strong>TERMIN</strong> erstellt.
<?php elseif ($isDelete): ?>
hat einen <strong>TERMIN</strong> gelöscht.
<?php else: ?>
hat <strong><?= htmlspecialchars($field) ?></strong> geändert:
<br>
<span class="text-danger fw-semibold"><?= htmlspecialchars($alt) ?></span>
<span class="text-success fw-semibold"><?= htmlspecialchars($neu) ?></span>
<?php endif; ?>
</div>
</div>
</a>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>