Files
intranet/web/intranet_repo/projects/seelsorge/content/visit.php
T

284 lines
16 KiB
PHP
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
/* Verwaltung der Besuchsstellen mit Haushalt-Gruppierung und Modals. */
require_once dirname(__DIR__) . '/util/seelsorge_helpers.php';
$current_user_id = (int) $_SESSION['user_id'];
$priests = seelsorge_load_priests($pdo);
$priestById = [];
foreach ($priests as $priest) {
$priestById[(int) $priest['user_id']] = $priest;
}
?>
<div class="visit-shell">
<div class="visit-card">
<div class="visit-list-header">
<div class="visit-list-title">Besuchsstellen</div>
<div class="visit-list-actions">
<button class="visit-action-btn" onclick="openVisitPickerModal()" title="Besuchsstelle auswählen und bearbeiten"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" style="vertical-align:middle;margin-right:4px;"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>Bearbeiten</button>
<button class="visit-action-btn visit-action-btn-primary" onclick="openVisitAddModal()" title="Neue Besuchsstelle anlegen">+ Neu</button>
</div>
</div>
<?php
// Daten einmal laden und im Speicher zu Haushalten gruppieren.
$visitRows = $pdo->query("SELECT * FROM seelsorge_besuchsstellen
ORDER BY CASE
WHEN reservation IS NOT NULL THEN 0
WHEN priority = 2 OR ((CURRENT_DATE - letzter_besuch) > 180) THEN 1
WHEN priority = 1 OR ((CURRENT_DATE - letzter_besuch) > 60) THEN 2
ELSE 3
END, last_name ASC")->fetchAll();
$visitEntriesForJs = array_map('seelsorge_visit_entry_client_data', $visitRows);
$households = seelsorge_group_households($visitRows);
// Reservierende in einer Abfrage laden statt pro Tabellenzeile.
$reservationIds = array_values(array_unique(array_filter(array_map(
static fn($row) => isset($row['reservation']) ? (int) $row['reservation'] : 0,
$visitRows
))));
$reservationUsers = [];
if ($reservationIds) {
$placeholders = implode(',', array_fill(0, count($reservationIds), '?'));
$stmtReservations = $pdo->prepare("SELECT user_id, first_name, last_name FROM user_general_information WHERE user_id IN ($placeholders)");
$stmtReservations->execute($reservationIds);
foreach ($stmtReservations->fetchAll() as $reservationUser) {
$reservationUsers[(int) $reservationUser['user_id']] = $reservationUser;
}
}
foreach ($households as &$household) {
$reservationId = (int) ($household['reservation'] ?? 0);
if ($reservationId > 0) {
$reservationUser = $reservationUsers[$reservationId] ?? [];
$household['reservation_name'] = trim(($reservationUser['first_name'] ?? '') . ' ' . ($reservationUser['last_name'] ?? ''));
$household['reservation_img'] = seelsorge_profile_image_url($reservationId);
}
}
unset($household);
?>
<table class="visit-table" id="visittable">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Letzter Besuch</th>
</tr>
</thead>
<tbody>
<?php if (empty($households)): ?>
<tr class="visit-empty-row"><td colspan="3" style="text-align:center;padding:24px;color:hsl(210,14%,50%);font-weight:600;">Keine Einträge vorhanden.</td></tr>
<?php else: ?>
<?php foreach ($households as $hKey => $household):
$members = $household['members'];
$firstMember = $members[0];
$isReserved = $household['reservation'] !== null;
$status = seelsorge_household_status($members, $isReserved);
$worstStatus = $status['key'];
$statusLabel = $status['label'];
// Name: Nachname, Vorname1 & Vorname2 ...
$lastName = $firstMember['last_name'] ?? '';
$firstNames = array_column($members, 'first_name');
$nameDisplay = htmlspecialchars($lastName) . ', ' . htmlspecialchars(implode(' & ', $firstNames));
// Letzter Besuch (ältester im Haushalt)
$visitDates = array_filter(array_column($members, 'letzter_besuch'));
$oldestVisit = $visitDates ? min($visitDates) : SEELSORGE_INITIAL_VISIT_DATE;
$visitText = seelsorge_relative_visit_text($oldestVisit);
// Reservierender
$reserverHtml = '';
if ($isReserved && (int) $household['reservation'] !== $current_user_id) {
$resName = $household['reservation_name'] ?: 'Geplant';
$resImgPath = $household['reservation_img'] ?? seelsorge_profile_image_url((int) $household['reservation']);
$reserverHtml = "<div class='visit-planned-by'><img src='" . htmlspecialchars($resImgPath) . "' alt=''>Geplant von " . htmlspecialchars($resName) . "</div>";
} elseif ($isReserved) {
$reserverHtml = "<div class='visit-planned-by' style='color:hsl(145,55%,34%);'><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor' width='14' height='14' style='vertical-align:middle;margin-right:4px;'><path d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/></svg>Von Ihnen geplant</div>";
}
// JSON data for modal
$householdJson = htmlspecialchars(json_encode([
'members' => array_map(function($m) use ($priestById) {
$dob = new DateTime($m['birth_date']);
$age = (new DateTime())->diff($dob)->y;
$priestId = (int) ($m['pr_bezirk'] ?? 0);
$priest = $priestById[$priestId] ?? [];
$priestName = trim(($priest['first_name'] ?? '') . ' ' . ($priest['last_name'] ?? ''));
$priestImg = seelsorge_profile_image_url($priestId);
return [
'besuch_id' => (int)$m['besuch_id'],
'last_name' => $m['last_name'],
'first_name' => $m['first_name'],
'birth_date' => $m['birth_date'],
'birth_formatted' => date('d.m.Y', strtotime($m['birth_date'])),
'age' => $age,
'phone' => $m['phone'] ?? '',
'comment' => $m['comment'] ?? '',
'priority' => (int)($m['priority'] ?? 0),
'pr_bezirk' => ($m['pr_bezirk'] === null ? '' : (int)$m['pr_bezirk']),
'priest_name' => $priestName,
'priest_id' => $priestId,
'priest_img' => $priestImg,
'address_street' => $m['address_street'] ?? '',
'address_plz' => $m['address_plz'] ?? '',
'address_link' => $m['address_link'] ?? '',
'letzter_besuch' => $m['letzter_besuch'] ?? '',
'reservation' => $m['reservation'],
];
}, $members),
'address_street' => $household['address_street'],
'address_plz' => $household['address_plz'],
'address_link' => $household['address_link'],
'phone' => $household['phone'],
'reservation' => $household['reservation'],
'reservation_name' => $household['reservation_name'] ?? '',
'reservation_img' => $household['reservation_img'] ?? '',
'status' => $worstStatus,
], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT), ENT_QUOTES);
?>
<tr class="visit-row" id="household_<?= md5($hKey) ?>" onclick='openHouseholdDetail(<?= $householdJson ?>)' data-household-key="<?= htmlspecialchars($hKey) ?>" tabindex="0" role="button" aria-label="<?= $nameDisplay ?> Details anzeigen">
<td>
<span class="visit-status-dot is-<?= $worstStatus ?>"></span>
</td>
<td>
<span class="visit-name-text"><?= $nameDisplay ?></span>
<div style="margin-top:6px;">
<span class="visit-status-badge is-<?= $worstStatus ?>"><?= $statusLabel ?></span>
</div>
</td>
<td>
<div class="visit-info-cell">
<div><?= $visitText ?></div>
<?= $reserverHtml ?>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Detail Modal -->
<div class="visit-detail-overlay" id="visitDetailOverlay" onclick="if(event.target===this)closeHouseholdDetail()">
<div class="visit-detail-card">
<button class="visit-detail-close" onclick="closeHouseholdDetail()">×</button>
<div class="visit-detail-hero">
<div class="visit-detail-hero-title" id="detailHeroTitle"></div>
<div class="visit-detail-hero-sub" id="detailHeroSub"></div>
</div>
<div class="visit-detail-body" id="detailBody"></div>
</div>
</div>
<!-- Picker Modal -->
<div class="visit-modal-overlay" id="visitPickerModalOverlay" aria-hidden="true">
<div class="visit-modal" role="dialog" aria-modal="true" aria-labelledby="visitPickerTitle">
<div class="visit-modal-header">
<h2 id="visitPickerTitle">Besuchsstelle auswählen</h2>
<button class="visit-modal-close" type="button" onclick="closeVisitPickerModal()" aria-label="Schließen">×</button>
</div>
<div class="visit-modal-body">
<input id="visitPickerSearch" class="visit-picker-search" type="text" placeholder="Suche nach Name…" oninput="onVisitPickerSearchInput()" autocomplete="off">
<div id="visitPickerList" class="visit-picker-list"></div>
</div>
</div>
</div>
<!-- Edit Modal -->
<div class="visit-modal-overlay" id="visitEntryModalOverlay" aria-hidden="true">
<div class="visit-modal" role="dialog" aria-modal="true" aria-labelledby="visitModalTitle" style="width:min(640px, 96vw);">
<div class="visit-modal-header">
<h2 id="visitModalTitle">Eintrag bearbeiten</h2>
<button class="visit-modal-close" type="button" onclick="closeVisitModal()" aria-label="Schließen">×</button>
</div>
<div class="visit-modal-body">
<form id="visitEntryForm" method="post" action="/intranet_repo/projects/seelsorge/seelsorge.php?tab=visit">
<input type="hidden" name="visit_command" value="save">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($seelsorgeCsrfToken, ENT_QUOTES, 'UTF-8') ?>">
<input type="hidden" name="besuch_id" id="modal_besuch_id" value="">
<div class="visit-form-error" id="visitFormError" role="alert" style="display:none;"></div>
<div class="visit-form-grid">
<div class="visit-form-field">
<label>Nachname *</label>
<input type="text" name="last_name" id="modal_last_name" required>
</div>
<div class="visit-form-field">
<label>Vorname *</label>
<input type="text" name="first_name" id="modal_first_name" required>
</div>
<div class="visit-form-field">
<label>Geburtsdatum *</label>
<input type="date" name="birth_date" id="modal_birth_date" required>
</div>
<div class="visit-form-field">
<label>Telefon</label>
<div class="visit-phone-row">
<div class="visit-input-prefix">+49</div>
<input class="visit-phone-input" type="tel" name="phone" id="modal_phone" inputmode="numeric" autocomplete="tel-national">
</div>
</div>
<div class="visit-form-field full">
<label>Straße *</label>
<input type="text" name="address_street" id="modal_address_street" required>
</div>
<div class="visit-form-field">
<label>PLZ *</label>
<input type="text" name="address_plz" id="modal_address_plz" required>
</div>
<div class="visit-form-field">
<label>Link zur Adresse *</label>
<input type="text" name="address_link" id="modal_address_link" required>
</div>
<div class="visit-form-field">
<label>Letzter Besuch</label>
<input type="date" name="letzter_besuch" id="modal_letzter_besuch" required>
</div>
<div class="visit-form-field">
<label>Priorität</label>
<select name="priority" id="modal_priority">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
</div>
<div class="visit-form-field full">
<label>Priester *</label>
<select name="pr_bezirk" id="modal_pr_bezirk" required>
<option value="">— auswählen —</option>
<?= seelsorge_priest_options($priests) ?>
</select>
</div>
<div class="visit-form-field full">
<label>Hinweis</label>
<input type="text" name="comment" id="modal_comment">
</div>
</div>
<div class="visit-modal-actions">
<button type="button" class="visit-btn-delete" id="modal_delete_btn" onclick="deleteVisitEntryFromModal()">Löschen</button>
<button type="button" class="visit-btn-cancel" onclick="closeVisitModal()">Abbrechen</button>
<button type="submit" class="visit-btn-save">Speichern</button>
</div>
</form>
</div>
</div>
</div>
<form id="visitActionForm" method="post" action="/intranet_repo/projects/seelsorge/seelsorge.php?tab=visit" hidden>
<input type="hidden" name="visit_command" id="visit_action_command" value="">
<input type="hidden" name="besuch_id" id="visit_action_id" value="">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($seelsorgeCsrfToken, ENT_QUOTES, 'UTF-8') ?>">
</form>
<script type="application/json" id="visit-config"><?= json_encode([
'entries' => $visitEntriesForJs,
'currentUserId' => $current_user_id,
'formError' => $visitFormError['message'] ?? '',
'formData' => $visitFormError['form'] ?? null,
], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?></script>