Files
domili/planning.php
2026-02-02 12:10:30 +01:00

329 lines
15 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
include('inc/check_login.php');
require_once('inc/db.php');
$is_admin = ($_SESSION['role'] === 'admin');
$message = '';
$message_type = '';
$edit_mode = false;
$edit_meeting = null;
// --- Funktion: Zufallsfarbe mit inverser Gewichtung OHNE übersprungene Termine ---
function get_weighted_random_color($conn)
{
$colors = [];
// 🔥 Wichtig: Nur nicht-übersprungene Termine zählen
$result = mysqli_query($conn, "
SELECT
c.id,
c.name,
COUNT(m.id) AS usage_count
FROM colors c
LEFT JOIN meetings m ON c.id = m.color_id AND m.is_skipped = 0
WHERE c.is_special = 0
GROUP BY c.id, c.name
");
if (!$result || mysqli_num_rows($result) === 0) {
return null;
}
while ($row = mysqli_fetch_assoc($result)) {
$colors[] = [
'id' => (int)$row['id'],
'usage' => (int)$row['usage_count']
];
}
$max_usage = max(array_column($colors, 'usage'));
$color_pool = [];
foreach ($colors as $color) {
$weight = ($max_usage - $color['usage'] + 1) ** 2;
for ($i = 0; $i < $weight; $i++) {
$color_pool[] = $color['id'];
}
}
if (empty($color_pool)) {
return null;
}
return $color_pool[array_rand($color_pool)];
}
// --- Nur Admins: Löschen ---
if ($is_admin && isset($_GET['action']) && $_GET['action'] == 'delete' && isset($_GET['id'])) {
$id = (int)$_GET['id'];
$stmt = mysqli_prepare($conn, "DELETE FROM meetings WHERE id = ?");
mysqli_stmt_bind_param($stmt, "i", $id);
if (mysqli_stmt_execute($stmt)) {
$message = "Termin erfolgreich gelöscht!";
$message_type = 'success';
} else {
$message = "Fehler beim Löschen des Termins.";
$message_type = 'danger';
}
mysqli_stmt_close($stmt);
header("Location: planning.php");
exit();
}
// --- Nur Admins: Bearbeiten ---
if ($is_admin && isset($_GET['action']) && $_GET['action'] == 'edit' && isset($_GET['id'])) {
$id = (int)$_GET['id'];
// 🔥 is_skipped mit abfragen
$stmt = mysqli_prepare($conn, "SELECT id, meeting_date, color_id, reason, is_skipped FROM meetings WHERE id = ?");
mysqli_stmt_bind_param($stmt, "i", $id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$edit_meeting = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
$edit_mode = true;
if ($edit_meeting) {
$edit_dt = new DateTime($edit_meeting['meeting_date']);
$edit_meeting['meeting_date_only'] = $edit_dt->format('Y-m-d');
$edit_meeting['meeting_time_only'] = $edit_dt->format('H:i');
} else {
$message = "Termin nicht gefunden.";
$message_type = 'warning';
}
}
// --- Nur Admins: Speichern (POST) ---
if ($is_admin && $_SERVER["REQUEST_METHOD"] == "POST") {
$meeting_date_only = $_POST['meeting_date'] ?? '';
$meeting_time_only = $_POST['meeting_time'] ?? '12:00';
$color_id = (int)($_POST['color_id'] ?? 0);
$reason = trim($_POST['reason'] ?? 'Zufallsfarbe');
$id = !empty($_POST['id']) ? (int)$_POST['id'] : null;
$is_skipped = !empty($_POST['is_skipped']) ? 1 : 0;
if (empty($meeting_date_only) || !$color_id) {
$message = "Datum und Farbe sind erforderlich.";
$message_type = 'danger';
} else {
$meeting_date = $meeting_date_only . ' ' . $meeting_time_only;
if ($id) {
$stmt = mysqli_prepare($conn, "UPDATE meetings SET meeting_date = ?, color_id = ?, reason = ?, is_skipped = ? WHERE id = ?");
mysqli_stmt_bind_param($stmt, "sisis", $meeting_date, $color_id, $reason, $is_skipped, $id);
} else {
$stmt = mysqli_prepare($conn, "INSERT INTO meetings (meeting_date, color_id, reason, is_skipped) VALUES (?, ?, ?, ?)");
mysqli_stmt_bind_param($stmt, "sisi", $meeting_date, $color_id, $reason, $is_skipped);
}
if (mysqli_stmt_execute($stmt)) {
$message = $id ? "Termin aktualisiert!" : "Neuer Termin hinzugefügt!";
$message_type = 'success';
} else {
$message = "Fehler beim Speichern.";
$message_type = 'danger';
}
mysqli_stmt_close($stmt);
header("Location: planning.php");
exit();
}
}
// --- 🔁 Automatische Planung: 4 Donnerstage, max. 1 pro Woche für ALLE Nutzer ---
$today = new DateTime('today');
// Nur nicht-übersprungene Termine zählen
$existing_weeks = [];
$result_existing = mysqli_query($conn, "SELECT meeting_date FROM meetings WHERE is_skipped = 0");
while ($row = mysqli_fetch_assoc($result_existing)) {
$dt = new DateTime($row['meeting_date']);
$year = $dt->format('o');
$week = $dt->format('W');
$existing_weeks["$year-$week"] = true;
}
$date = clone $today;
for ($i = 0; $i < 4; $i++) {
$date->modify($i === 0 ? 'next thursday' : 'next thursday');
$year = $date->format('o');
$week = $date->format('W');
$week_key = "$year-$week";
if (!isset($existing_weeks[$week_key])) {
$new_date = $date->format('Y-m-d') . ' 12:00:00';
$color_id = get_weighted_random_color($conn);
if ($color_id) {
// Prüfen, ob überhaupt ein Termin (auch übersprungener) existiert → keine Duplikate
$check = mysqli_prepare($conn, "SELECT id FROM meetings WHERE meeting_date = ?");
mysqli_stmt_bind_param($check, "s", $new_date);
mysqli_stmt_execute($check);
$exists = mysqli_fetch_assoc(mysqli_stmt_get_result($check));
mysqli_stmt_close($check);
if (!$exists) {
$default_reason = "Zufallsfarbe";
$ins = mysqli_prepare($conn, "INSERT INTO meetings (meeting_date, color_id, reason, is_skipped) VALUES (?, ?, ?, 0)");
mysqli_stmt_bind_param($ins, "sis", $new_date, $color_id, $default_reason);
mysqli_stmt_execute($ins);
mysqli_stmt_close($ins);
$existing_weeks[$week_key] = true;
}
}
}
}
// --- Daten für Anzeige laden ---
$all_colors = [];
if ($is_admin) {
$result_colors = mysqli_query($conn, "SELECT id, name, hex_code FROM colors ORDER BY name");
while ($row = mysqli_fetch_assoc($result_colors)) {
$all_colors[] = $row;
}
}
// 🔥 Alle Termine laden (auch übersprungene), für alle Nutzer
$meetings = [];
$result_meetings = mysqli_query($conn, "
SELECT m.id, m.meeting_date, m.reason, m.is_skipped, c.name AS color_name, c.hex_code
FROM meetings m
JOIN colors c ON m.color_id = c.id
WHERE m.is_completed = 0
ORDER BY m.meeting_date
");
while ($row = mysqli_fetch_assoc($result_meetings)) {
$meetings[] = $row;
}
require_once 'inc/header.php';
?>
<div class="container mt-5">
<?php if ($message): ?>
<div class="alert alert-<?= htmlspecialchars($message_type) ?> alert-dismissible fade show" role="alert">
<?= htmlspecialchars($message) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="mb-0">Terminübersicht</h2>
</div>
<?php if ($is_admin): ?>
<div class="collapse <?= $edit_mode ? 'show' : '' ?>" id="planningFormCollapse">
<div class="card shadow mb-4">
<div class="card-header bg-primary-subtle text-secondary">
<h4 class="mb-0"><?= $edit_mode ? 'Termin bearbeiten' : 'Neuen Termin hinzufügen'; ?></h4>
</div>
<div class="card-body">
<form action="planning.php" method="post">
<?php if ($edit_mode): ?>
<input type="hidden" name="id" value="<?= htmlspecialchars($edit_meeting['id']); ?>">
<?php endif; ?>
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Datum</label>
<input type="date" class="form-control" name="meeting_date" value="<?= htmlspecialchars($edit_meeting['meeting_date_only'] ?? ''); ?>" required>
</div>
<div class="col-md-4">
<label class="form-label">Uhrzeit</label>
<input type="time" class="form-control" name="meeting_time" value="<?= htmlspecialchars($edit_meeting['meeting_time_only'] ?? '12:00'); ?>" required>
</div>
<div class="col-md-4">
<label class="form-label">Farbe</label>
<select class="form-select" name="color_id" required>
<?php foreach ($all_colors as $color): ?>
<option value="<?= (int)$color['id'] ?>" <?= (($edit_meeting['color_id'] ?? 0) == $color['id']) ? 'selected' : '' ?>>
<?= htmlspecialchars($color['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12">
<label class="form-label">Grund für die Farbe (optional)</label>
<input type="text" class="form-control" name="reason" value="<?= htmlspecialchars($edit_meeting['reason'] ?? ''); ?>">
<div class="form-text">Wenn leer, wird „Zufallsfarbe“ eingetragen.</div>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_skipped" name="is_skipped" value="1"
<?= (!empty($edit_meeting['is_skipped'])) ? 'checked' : '' ?>>
<label class="form-check-label" for="is_skipped">
Termin bewusst aussetzen (wird in Grau angezeigt und nicht für Farbstatistik genutzt)
</label>
</div>
</div>
<div class="col-12 d-flex justify-content-start">
<button type="submit" class="btn btn-sm btn-outline-<?= $edit_mode ? 'success' : 'primary' ?> me-2">
<?= $edit_mode ? 'Speichern' : 'Hinzufügen' ?>
</button>
<a href="planning.php" class="btn btn-sm btn-outline-secondary">Abbrechen</a>
</div>
</div>
</form>
</div>
</div>
</div>
<?php endif; ?>
<div class="card shadow">
<div class="card-header bg-primary-subtle text-secondary d-flex justify-content-between align-items-center">
<h4 class="mb-0">Anstehende Termine</h4>
<?php if ($is_admin): ?>
<a class="btn btn-sm d-flex align-items-center justify-content-center" data-bs-toggle="collapse" href="#planningFormCollapse" role="button" aria-expanded="false" aria-controls="planningFormCollapse">Add
<span class="material-symbols-outlined">add</span>
</a>
<?php endif; ?>
</div>
<div class="card-body">
<?php if (empty($meetings)): ?>
<p class="text-muted text-center">Keine anstehenden Termine.</p>
<?php else: ?>
<div class="list-group list-group-flush">
<?php foreach ($meetings as $m): ?>
<?php
$is_skipped = (bool)$m['is_skipped'];
$date_str = date('d.m.Y H:i', strtotime($m['meeting_date']));
?>
<div class="list-group-item <?= $is_skipped ? 'opacity-75' : '' ?>">
<div class="d-flex justify-content-between align-items-start">
<div>
<p class="fw-bold mb-1 <?= $is_skipped ? 'text-muted text-decoration-line-through' : '' ?>">
<?= htmlspecialchars($date_str) ?>
<?php if ($is_skipped): ?>
<span class="badge bg-light text-dark ms-2">übersprungen</span>
<?php endif; ?>
</p>
<div class="d-flex align-items-center mb-1">
<div class="me-2" style="background-color: <?= htmlspecialchars($m['hex_code']); ?>; width: 20px; height: 20px; border: 1px solid #ccc; border-radius: 2px;"></div>
<span><?= htmlspecialchars($m['color_name']); ?></span>
</div>
<p class="text-muted small mb-0">Grund: <?= htmlspecialchars($m['reason']); ?></p>
</div>
<?php if ($is_admin): ?>
<div class="dropdown ms-2">
<a href="#" class="text-secondary" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="material-icons">more_vert</span>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item d-flex align-items-center" href="planning.php?action=edit&id=<?= (int)$m['id'] ?>">
<span class="material-icons me-2">mode_edit_outline</span> Bearbeiten
</a>
</li>
<li>
<a class="dropdown-item d-flex align-items-center text-danger" href="planning.php?action=delete&id=<?= (int)$m['id'] ?>" onclick="return confirm('Wirklich löschen?')">
<span class="material-icons me-2">delete_outline</span> Löschen
</a>
</li>
</ul>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php include('inc/footer.php'); ?>