284 lines
16 KiB
PHP
284 lines
16 KiB
PHP
<?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>
|