Compare commits

...

3 Commits

Author SHA1 Message Date
Borgal
9782304e61 Layout angepasst 2025-10-31 15:04:16 +01:00
Borgal
8804920129 Layout angepasst 2025-10-31 15:03:58 +01:00
Borgal
bbce9776e1 Geburtstagslogik implementiert 2025-10-31 15:03:43 +01:00
4 changed files with 164 additions and 98 deletions

View File

@@ -151,9 +151,26 @@ include('inc/header.php');
<?php else: ?>
<?php foreach ($all_meetings as $meeting_id => $meeting): ?>
<div class="card shadow mb-4">
<?php
// Wochentag abkürzen (Mo., Di., Mi., ...)
$weekday_short = date('D', strtotime($meeting['date']));
$german_weekdays = [
'Mon' => 'Mo.',
'Tue' => 'Di.',
'Wed' => 'Mi.',
'Thu' => 'Do.',
'Fri' => 'Fr.',
'Sat' => 'Sa.',
'Sun' => 'So.'
];
$weekday = $german_weekdays[$weekday_short] ?? '';
$formatted_date = date('d.m.y', strtotime($meeting['date']));
$formatted_time = date('H:i', strtotime($meeting['date']));
?>
<div class="card-header d-flex justify-content-between bg-secondary bg-opacity-25 text-dark align-items-center">
<div class="mb-0 fs-6 fs-md-5 fw-bold">
<span class="fw-normal me-2">am</span><?= date('d.m.y', strtotime($meeting['date'])) ?>
Treffen am <?= $weekday ?> <?= $formatted_date ?> um <?= $formatted_time ?> Uhr
</div>
<?php if ($_SESSION['role'] === 'admin'): ?>
<div class="dropdown">
@@ -177,7 +194,11 @@ include('inc/header.php');
</div>
<div class="card-body">
<div class="d-flex flex-column text-muted fst-italic mb-2">
<span>Farbe: <span class="fw-bold"><?= htmlspecialchars($meeting['color_name']) ?></span></span>
<div class="d-flex align-items-center mb-1">
<span>Farbe:</span>
<div class="ms-2" style="background-color: <?= htmlspecialchars($meeting['hex_code']); ?>; width: 20px; height: 20px; border: 1px solid #ccc; border-radius: 2px;"></div>
<span class="fw-bold ms-2"><?= htmlspecialchars($meeting['color_name']) ?></span>
</div>
<span>Grund: <?= htmlspecialchars($meeting['reason']) ?></span>
</div>
<h6 class="mb-2">Teilnehmer:</h6>

View File

@@ -23,7 +23,7 @@ function is_user_on_vacation($conn, $user_id, $meeting_date)
return $found !== null;
}
// 🔹 Funktion: Automatisch ablehnen, wenn im Urlaub (außer bei expliziter Zusage)
// 🔹 Funktion: Automatisch ablehnen, wenn im Urlaub
function auto_decline_if_on_vacation($conn, $meeting_id, $user_id, $meeting_date)
{
if (!is_user_on_vacation($conn, $user_id, $meeting_date)) {
@@ -39,12 +39,10 @@ function auto_decline_if_on_vacation($conn, $meeting_id, $user_id, $meeting_date
$current_status = $existing ? $existing['rsvp_status'] : null;
// Wenn explizit "accepted" oder "maybe" gewählt wurde → NICHT überschreiben!
if ($current_status === 'accepted' || $current_status === 'maybe') {
return $current_status;
}
// Nur wenn Status "declined" oder NULL → auf "declined" setzen
if ($existing) {
$upd = mysqli_prepare($conn, "UPDATE meeting_teilnehmer SET rsvp_status = 'declined', attended = 0 WHERE meeting_id = ? AND user_id = ?");
mysqli_stmt_bind_param($upd, "ii", $meeting_id, $user_id);
@@ -181,36 +179,66 @@ if ($row) {
mysqli_stmt_close($attendees_stmt);
}
// --- ZAHLENDE PERSON BESTIMMEN ---
// --- ZAHLENDE PERSON BESTIMMEN (MIT GEBURTSTAGS-REGEL) ---
$next_payer_username = null;
if ($total_accepted > 0) {
$sql_next_payer = "
$current_year = date('Y');
$meeting_date = $row['meeting_date'];
// Kandidaten holen
$sql_candidates = "
SELECT
u.id,
u.username,
u.birthday,
u.last_birthday_year,
(SELECT COUNT(*) FROM meeting_teilnehmer WHERE user_id = u.id AND paid = 1) AS paid_count
FROM meeting_teilnehmer mt
JOIN users u ON mt.user_id = u.id
WHERE mt.meeting_id = ? AND mt.rsvp_status = 'accepted'
ORDER BY paid_count ASC
ORDER BY u.username
";
$stmt_next_payer = mysqli_prepare($conn, $sql_next_payer);
mysqli_stmt_bind_param($stmt_next_payer, "i", $meeting_id);
mysqli_stmt_execute($stmt_next_payer);
$result_next_payer = mysqli_stmt_get_result($stmt_next_payer);
$payer_candidates = [];
$min_paid_count = -1;
while ($row_payer = mysqli_fetch_assoc($result_next_payer)) {
if ($min_paid_count == -1 || $row_payer['paid_count'] < $min_paid_count) {
$min_paid_count = $row_payer['paid_count'];
$payer_candidates = [$row_payer['username']];
} elseif ($row_payer['paid_count'] == $min_paid_count) {
$payer_candidates[] = $row_payer['username'];
$stmt_candidates = mysqli_prepare($conn, $sql_candidates);
mysqli_stmt_bind_param($stmt_candidates, "i", $meeting_id);
mysqli_stmt_execute($stmt_candidates);
$candidates = mysqli_fetch_all(mysqli_stmt_get_result($stmt_candidates), MYSQLI_ASSOC);
mysqli_stmt_close($stmt_candidates);
$birthday_payers = [];
$regular_payers = [];
foreach ($candidates as $c) {
if (empty($c['birthday'])) {
$regular_payers[] = $c;
continue;
}
$birth_month = (int)date('m', strtotime($c['birthday']));
$birth_day = (int)date('d', strtotime($c['birthday']));
$birthday_this_year = "$current_year-$birth_month-$birth_day";
$already_paid_this_year = ($c['last_birthday_year'] == $current_year);
$birthday_passed = (strtotime($birthday_this_year) <= strtotime($meeting_date));
if (!$already_paid_this_year && $birthday_passed) {
$birthday_payers[] = $c;
} else {
$regular_payers[] = $c;
}
}
mysqli_stmt_close($stmt_next_payer);
if (!empty($payer_candidates)) {
sort($payer_candidates);
$next_payer_username = $payer_candidates[0];
// Priorität: Geburtstagskinder zuerst
if (!empty($birthday_payers)) {
usort($birthday_payers, function ($a, $b) {
return $a['paid_count'] <=> $b['paid_count'];
});
$next_payer_username = $birthday_payers[0]['username'];
} elseif (!empty($regular_payers)) {
usort($regular_payers, function ($a, $b) {
return $a['paid_count'] <=> $b['paid_count'];
});
$next_payer_username = $regular_payers[0]['username'];
}
}
@@ -283,7 +311,6 @@ $german_weekdays = [
<p class="text-muted">nächster Termin:</p>
<p class="text-muted h3 fw-bold mb-2"><?= $german_weekday . ' ' . date('d.m.Y H:i', strtotime($row['meeting_date'])) ?></p>
<?php
// Prüfen, ob der aktuelle Benutzer bereits einen aktiven Vorschlag hat
$has_active_proposal = false;
if ($row) {
$check_proposal = mysqli_prepare($conn, "
@@ -332,9 +359,7 @@ $german_weekdays = [
</div>
</div>
<!-- TODO: Fehler, wenn Kommentar weggelassen wird-->
<div class="d-flex justify-content-center pt-2 pb-3" style="max-width: 500px; margin-left: auto; margin-right: auto; flex-direction: column; align-items: center;" <!-- inline-css-ok -->
<div class="d-flex justify-content-center pt-2 pb-3" style="max-width: 500px; margin-left: auto; margin-right: auto; flex-direction: column; align-items: center;">
<?php if ($user_attendance_status === 'accepted'): ?>
<p class="text-success fw-bold mb-4">Du hast zugesagt!</p>
<div class="d-flex justify-content-center">

View File

@@ -1,13 +1,8 @@
<?php
// Fehleranzeige für Entwicklung (optional)
// error_reporting(E_ALL);
// ini_set('display_errors', 1);
include('inc/check_login.php');
include('inc/db.php');
require_once 'inc/helpers.php';
// Meeting-ID prüfen
if (!isset($_GET['id'])) {
$_SESSION['error_message'] = "Keine Meeting-ID angegeben.";
header("Location: index.php");
@@ -15,12 +10,9 @@ if (!isset($_GET['id'])) {
}
$meeting_id = intval($_GET['id']);
// Quelle merken (für Weiterleitung)
$source_page = isset($_GET['source']) && $_GET['source'] === 'history' ? 'history' : 'index';
$cancel_link = $source_page === 'history' ? 'history.php' : 'index.php';
// Meeting-Daten laden
$stmt = mysqli_prepare($conn, "SELECT meeting_date, color_id, reason FROM meetings WHERE id = ?");
mysqli_stmt_bind_param($stmt, "i", $meeting_id);
mysqli_stmt_execute($stmt);
@@ -33,7 +25,6 @@ if (!$meeting) {
exit;
}
// Farben und Benutzer laden
$colors = [];
$colors_result = mysqli_query($conn, "SELECT id, name FROM colors ORDER BY name");
while ($row = mysqli_fetch_assoc($colors_result)) {
@@ -46,7 +37,6 @@ while ($row = mysqli_fetch_assoc($users_result)) {
$users[] = $row;
}
// Bestehende Teilnehmerdaten laden
$existing_feedback = [];
$stmt = mysqli_prepare($conn, "SELECT user_id, attended, wore_color, paid FROM meeting_teilnehmer WHERE meeting_id = ?");
mysqli_stmt_bind_param($stmt, "i", $meeting_id);
@@ -60,9 +50,7 @@ mysqli_stmt_close($stmt);
$message = '';
$message_type = '';
// POST-Verarbeitung
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// Meeting-Daten aktualisieren (nur im History-Modus)
if ($source_page === 'history') {
$meeting_date = $_POST['meeting_date'] ?? '';
$color_id = intval($_POST['color_id'] ?? 0);
@@ -97,7 +85,24 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
}
mysqli_stmt_close($stmt_insert);
// Meeting als abgeschlossen markieren (nur im Index-Modus)
// 🔹 GEBURTSTAGS-REGEL: last_birthday_year setzen
foreach ($_POST['user_id'] as $user_id) {
$user_id = (int)$user_id;
$paid = isset($_POST['paid'][$user_id]) ? 1 : 0;
if ($paid) {
$stmt_bday = mysqli_prepare($conn, "
UPDATE users
SET last_birthday_year = YEAR(CURDATE())
WHERE id = ? AND birthday IS NOT NULL
");
mysqli_stmt_bind_param($stmt_bday, "i", $user_id);
mysqli_stmt_execute($stmt_bday);
mysqli_stmt_close($stmt_bday);
}
}
// Meeting abschließen (nur im Index-Modus)
if ($source_page === 'index') {
$stmt_complete = mysqli_prepare($conn, "UPDATE meetings SET is_completed = 1 WHERE id = ?");
mysqli_stmt_bind_param($stmt_complete, "i", $meeting_id);
@@ -112,7 +117,6 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$message_type = 'warning';
}
// 🔁 Zurück zur ursprünglichen Quelle
header("Location: " . $cancel_link);
exit;
}

View File

@@ -1,21 +1,17 @@
<?php
// vacation.php
// Korrektur: Die Pfade gehen jetzt direkt in den 'inc' Ordner.
include('inc/check_login.php');
include('inc/db.php');
require_once 'inc/helpers.php';
$message = '';
$message_type = '';
$logged_in_user_id = intval($_SESSION['user_id'] ?? 1); // Stellen Sie sicher, dass die User-ID verfügbar ist
$logged_in_user_id = (int)($_SESSION['user_id'] ?? 1);
// Aktion verarbeiten (Hinzufügen oder Löschen)
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (isset($_POST['action']) && $_POST['action'] == 'add_vacation') {
$start_date = $_POST['start_date'];
$end_date = $_POST['end_date'];
// --- Hinzufügen ---
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'add_vacation') {
$start_date = $_POST['start_date'] ?? '';
$end_date = $_POST['end_date'] ?? '';
// Eingabe validieren
if (empty($start_date) || empty($end_date)) {
$message = "Bitte geben Sie ein Start- und Enddatum an.";
$message_type = "danger";
@@ -23,7 +19,6 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
$message = "Das Enddatum kann nicht vor dem Startdatum liegen.";
$message_type = "danger";
} else {
// Termin in die Datenbank einfügen
$stmt = mysqli_prepare($conn, "INSERT INTO vacations (user_id, start_date, end_date) VALUES (?, ?, ?)");
if ($stmt) {
mysqli_stmt_bind_param($stmt, "iss", $logged_in_user_id, $start_date, $end_date);
@@ -31,19 +26,17 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
$message = "Urlaub erfolgreich hinzugefügt.";
$message_type = "success";
} else {
$message = "Fehler beim Hinzufügen des Urlaubs: " . mysqli_error($conn);
$message = "Fehler beim Hinzufügen des Urlaubs.";
$message_type = "danger";
}
mysqli_stmt_close($stmt);
}
}
}
}
// Löschen-Aktion
// --- Löschen ---
if (isset($_GET['action']) && $_GET['action'] == 'delete' && isset($_GET['id'])) {
$vacation_id = intval($_GET['id']);
// Überprüfen, ob die ID zum eingeloggten Benutzer gehört
$vacation_id = (int)$_GET['id'];
$stmt = mysqli_prepare($conn, "DELETE FROM vacations WHERE id = ? AND user_id = ?");
if ($stmt) {
mysqli_stmt_bind_param($stmt, "ii", $vacation_id, $logged_in_user_id);
@@ -58,7 +51,7 @@ if (isset($_GET['action']) && $_GET['action'] == 'delete' && isset($_GET['id']))
}
}
// Alle bestehenden Urlaube des Benutzers abrufen
// --- Daten laden ---
$vacations = [];
$stmt = mysqli_prepare($conn, "SELECT id, start_date, end_date FROM vacations WHERE user_id = ? ORDER BY start_date DESC");
if ($stmt) {
@@ -75,33 +68,35 @@ require_once 'inc/header.php';
?>
<div class="container mt-5">
<h2 class="mb-4">Abwesenheitsassistent</h2>
<?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" aria-label="Close"></button>
<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">Abwesenheitsassistent</h2>
</div>
<div class="card shadow mb-4">
<div class="card-header bg-light">
<h5 class="mb-0">Urlaub eintragen</h5>
<div class="card-header bg-primary-subtle text-secondary">
<h4 class="mb-0">Urlaub eintragen</h4>
</div>
<div class="card-body">
<form action="vacation.php" method="post">
<input type="hidden" name="action" value="add_vacation">
<div class="row g-3">
<div class="col-md-5">
<label for="start_date" class="form-label">Startdatum</label>
<input type="date" class="form-control" id="start_date" name="start_date" required>
<label class="form-label">Startdatum</label>
<input type="date" class="form-control" name="start_date" required>
</div>
<div class="col-md-5">
<label for="end_date" class="form-label">Enddatum</label>
<input type="date" class="form-control" id="end_date" name="end_date" required>
<label class="form-label">Enddatum</label>
<input type="date" class="form-control" name="end_date" required>
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Hinzufügen</button>
<button type="submit" class="btn btn-sm btn-outline-primary w-100">Hinzufügen</button>
</div>
</div>
</form>
@@ -109,25 +104,46 @@ require_once 'inc/header.php';
</div>
<div class="card shadow">
<div class="card-header bg-light">
<h5 class="mb-0">Eingetragene Urlaube</h5>
<div class="card-header bg-secondary bg-opacity-50 text-secondary">
<h4 class="mb-0">Eingetragene Urlaube</h4>
</div>
<div class="card-body">
<?php if (empty($vacations)): ?>
<p class="text-muted text-center">Es sind keine Urlaube eingetragen.</p>
<?php else: ?>
<ul class="list-group list-group-flush">
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Zeitraum</th>
<th class="text-end">Aktionen</th>
</tr>
</thead>
<tbody>
<?php foreach ($vacations as $vacation): ?>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>
<tr>
<td>
Vom <?= date('d.m.Y', strtotime($vacation['start_date'])) ?> bis <?= date('d.m.Y', strtotime($vacation['end_date'])) ?>
</span>
<a href="vacation.php?action=delete&id=<?= htmlspecialchars($vacation['id']) ?>" class="btn btn-sm btn-danger" onclick="return confirm('Sind Sie sicher, dass Sie diesen Urlaub löschen möchten?');">
Löschen
</td>
<td class="text-end align-middle">
<div class="dropdown">
<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 text-danger" href="vacation.php?action=delete&id=<?= htmlspecialchars($vacation['id']) ?>" onclick="return confirm('Wirklich löschen?')">
<span class="material-icons me-2">delete_outline</span> Löschen
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>