Files
Workshop-Wahlen/includes/admin-teilnehmer.php
ProgrammGamer 7fc12ba7bb
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
Sync: Inhalte aus origin/main in develop übernommen; .gitea und .github unverändert belassen (falls vorhanden).
2026-01-30 21:30:23 +01:00

403 lines
21 KiB
PHP
Raw Blame History

<?php
function kc_teilnehmer_page() {
global $wpdb;
$prefix = $wpdb->prefix;
kc_admin_tabs('kc_teilnehmer');
// Alle Wahlen + Workshops für Dropdowns und Validierung
$all_wahlen = $wpdb->get_results("SELECT id, name, anzahl_einheiten FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY id DESC");
$all_workshops = $wpdb->get_results("SELECT id, name FROM {$prefix}kc_workshops ORDER BY name");
// Map of workshop id => name for quick lookup in overview
$workshops_map = [];
if (!empty($all_workshops)) {
foreach ($all_workshops as $ws) {
$workshops_map[intval($ws->id)] = $ws->name;
}
}
// Build map of wahl -> phases + workshops (for JS)
$wahl_map = [];
foreach($all_wahlen as $w) {
$wahl_map[intval($w->id)] = ['phases' => max(1,intval($w->anzahl_einheiten)), 'workshops' => []];
}
if (!empty($all_workshops)) {
foreach($all_workshops as $ws) {
$ww = $wpdb->get_col($wpdb->prepare("SELECT wahl_id FROM {$prefix}kc_wahl_workshops WHERE workshop_id=%d", $ws->id));
if (!empty($ww)) {
foreach($ww as $wid) {
if (isset($wahl_map[intval($wid)])) {
$wahl_map[intval($wid)]['workshops'][] = ['id'=>intval($ws->id),'name'=>$ws->name];
}
}
}
}
}
// Teilnehmer l<>schen
if (isset($_GET['delete_teilnehmer'])) {
$tid = intval($_GET['delete_teilnehmer']);
$wpdb->delete("{$prefix}kc_teilnehmer", ['id' => $tid]);
echo '<div class="notice notice-success">Teilnehmer gel<65>scht!</div>';
}
// Teilnehmer speichern (neu/<2F>ndern)
if (isset($_POST['kc_teilnehmer_save'])) {
// sanitize inputs
$vorname = sanitize_text_field($_POST['vorname']);
$nachname = sanitize_text_field($_POST['nachname']);
$wahl_id_post = intval($_POST['wahl_id']);
$phase_post = intval($_POST['phase']);
$w1 = intval($_POST['wunsch1']);
$w2 = intval($_POST['wunsch2']);
$w3 = intval($_POST['wunsch3']);
// Server-side validation: duplicate name in same Wahl (exclude self on edit)
$norm_v = mb_strtolower(trim($vorname));
$norm_n = mb_strtolower(trim($nachname));
if (!empty($_POST['tid'])) {
$exclude_id = intval($_POST['tid']);
$exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d AND id<>%d", $norm_v, $norm_n, $wahl_id_post, $exclude_id));
} else {
$exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d", $norm_v, $norm_n, $wahl_id_post));
}
if ($exists && $exists > 0) {
echo '<div class="notice notice-error">Diese Kombination aus Vorname und Nachname existiert bereits für diese Wahl.</div>';
} else {
// validate phase within wahl
$wahl_row = $wpdb->get_row($wpdb->prepare("SELECT anzahl_einheiten FROM {$prefix}kc_wahlen WHERE id=%d", $wahl_id_post));
$max_ph = $wahl_row ? max(1,intval($wahl_row->anzahl_einheiten)) : 1;
if ($phase_post < 1 || $phase_post > $max_ph) {
echo '<div class="notice notice-error">Ungültige Phase für die gewählte Wahl.</div>';
} else {
// validate that selected workshops belong to the chosen wahl (if mapping exists)
$valid_ws = [];
$ww_rows = $wpdb->get_col($wpdb->prepare("SELECT workshop_id FROM {$prefix}kc_wahl_workshops WHERE wahl_id=%d", $wahl_id_post));
if (!empty($ww_rows)) foreach($ww_rows as $r) $valid_ws[] = intval($r);
// if mapping exists, enforce membership
$check_membership = function($wid) use ($valid_ws) {
if (empty($valid_ws)) return true; // no mapping -> allow
return in_array(intval($wid), $valid_ws);
};
if (!$check_membership($w1) || !$check_membership($w2) || !$check_membership($w3)) {
echo '<div class="notice notice-error">Einer oder mehrere ausgewählte Workshops gehören nicht zur gewählten Wahl.</div>';
} else {
$data = [
'vorname' => $vorname,
'nachname' => $nachname,
'wahl_id' => $wahl_id_post,
'phase' => $phase_post,
'wunsch1' => $w1,
'wunsch2' => $w2,
'wunsch3' => $w3
];
if (!empty($_POST['tid'])) {
$wpdb->update("{$prefix}kc_teilnehmer", $data, ['id'=>intval($_POST['tid'])]);
echo '<div class="notice notice-success">Teilnehmer aktualisiert!</div>';
} else {
$wpdb->insert("{$prefix}kc_teilnehmer", $data);
echo '<div class="notice notice-success">Teilnehmer angelegt!</div>';
}
}
}
}
}
// Teilnehmer bearbeiten
if (isset($_GET['edit_teilnehmer'])) {
$tid = intval($_GET['edit_teilnehmer']);
$tn = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_teilnehmer WHERE id=%d", $tid));
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Teilnehmer bearbeiten</h2>';
echo '<form method="post">';
echo '<input type="hidden" name="tid" value="'.intval($tn->id).'">';
echo '<input type="text" name="vorname" placeholder="Vorname" value="'.esc_attr($tn->vorname).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<input type="text" name="nachname" placeholder="Nachname" value="'.esc_attr($tn->nachname).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
// Wahl select
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wahl</label>';
echo '<select id="kc-wahl-select" name="wahl_id" required style="margin-bottom:8px;width:100%;padding:7px;">';
foreach($all_wahlen as $w) {
$selw = (intval($tn->wahl_id) === intval($w->id)) ? ' selected' : '';
echo '<option value="'.intval($w->id).'"'.$selw.'>'.esc_html($w->name).'</option>';
}
echo '</select>';
// Phase select (fallback content)
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Phase</label>';
echo '<select id="kc-phase-select" name="phase" required style="margin-bottom:8px;width:100%;padding:7px;">';
$cur_phases = isset($wahl_map[intval($tn->wahl_id)]) ? $wahl_map[intval($tn->wahl_id)]['phases'] : 1;
for($i=1;$i<=$cur_phases;$i++){
$selp = (intval($tn->phase) === $i) ? ' selected' : '';
echo '<option value="'.intval($i).'"'.$selp.'>'.intval($i).'</option>';
}
echo '</select>';
// Wunsch selects
for ($k=1;$k<=3;$k++){
$cur = intval($tn->{'wunsch'.$k});
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wunsch '.intval($k).'</label>';
echo '<select id="kc-wunsch'.intval($k).'" name="wunsch'.intval($k).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<option value="">Bitte Wähle einen Workshop aus</option>';
foreach($all_workshops as $ws) {
$sel = ($cur === intval($ws->id)) ? ' selected' : '';
echo '<option value="'.intval($ws->id).'"'.$sel.'>'.esc_html($ws->name).'</option>';
}
echo '</select>';
}
echo '<button name="kc_teilnehmer_save" class="kc-btn">Speichern</button>';
echo '<a href="?page=kc_teilnehmer" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>';
echo '</form>';
// Provide JS map and init JS: restrict phases/workshops based on Wahl and init Select2
echo '<script>var KC_WAHL_MAP = '.json_encode($wahl_map, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP).';</script>';
echo '<script>
(function(){
function fillPhaseOptions(wid){
var ph = KC_WAHL_MAP[wid] ? KC_WAHL_MAP[wid].phases : 1;
var sel = document.getElementById("kc-phase-select"); if(!sel) return; sel.innerHTML = "";
for(var i=1;i<=ph;i++){ var o=document.createElement("option"); o.value=i; o.text=i; sel.appendChild(o); }
}
function fillWorkshopOptions(wid){
var list = KC_WAHL_MAP[wid] ? KC_WAHL_MAP[wid].workshops : [];
["kc-wunsch1","kc-wunsch2","kc-wunsch3"].forEach(function(sid){
var sel = document.getElementById(sid); if(!sel) return; var cur = sel.value; sel.innerHTML = "";
var empty = document.createElement("option"); empty.value = ""; empty.text = "Bitte Wähle einen Workshop aus"; sel.appendChild(empty);
list.forEach(function(w){ var o=document.createElement("option"); o.value = w.id; o.text = w.name; sel.appendChild(o); });
if(cur) sel.value = cur;
});
}
jQuery(function($){
try{ $("#kc-wunsch1,#kc-wunsch2,#kc-wunsch3,#kc-wahl-select,#kc-phase-select").select2({width: "100%"}); }catch(e){}
var wsel = document.getElementById("kc-wahl-select"); if(!wsel) return;
wsel.addEventListener("change", function(){ fillPhaseOptions(this.value); fillWorkshopOptions(this.value); try{ $("#kc-wunsch1,#kc-wunsch2,#kc-wunsch3,#kc-phase-select").trigger("change.select2"); }catch(e){} });
// initial fill
fillPhaseOptions(wsel.value); fillWorkshopOptions(wsel.value);
});
})();
</script>';
echo '</div>';
return;
}
// Neuer Teilnehmer anlegen
if (isset($_GET['new'])) {
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Neuen Teilnehmer anlegen</h2>';
echo '<form method="post">';
echo '<input type="text" name="vorname" placeholder="Vorname" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<input type="text" name="nachname" placeholder="Nachname" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wahl</label>';
echo '<select id="kc-wahl-select" name="wahl_id" required style="margin-bottom:8px;width:100%;padding:7px;">';
foreach($all_wahlen as $w) {
echo '<option value="'.intval($w->id).'">'.esc_html($w->name).'</option>';
}
echo '</select>';
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Phase</label>';
echo '<select id="kc-phase-select" name="phase" required style="margin-bottom:8px;width:100%;padding:7px;">';
$first_wahl = reset($all_wahlen);
$first_ph = $first_wahl ? max(1,intval($first_wahl->anzahl_einheiten)) : 1;
for($i=1;$i<=$first_ph;$i++) echo '<option value="'.intval($i).'">'.intval($i).'</option>';
echo '</select>';
for ($k=1;$k<=3;$k++){
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wunsch '.intval($k).'</label>';
echo '<select id="kc-wunsch'.intval($k).'" name="wunsch'.intval($k).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<option value="">Bitte Wähle einen Workshop aus</option>';
foreach($all_workshops as $ws) {
echo '<option value="'.intval($ws->id).'">'.esc_html($ws->name).'</option>';
}
echo '</select>';
}
echo '<button name="kc_teilnehmer_save" class="kc-btn">Speichern</button>';
echo '<a href="?page=kc_teilnehmer" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>';
echo '</form>';
// Init Select2 for nicer selects if available
echo '<script>jQuery(function($){try{ $("#kc-wunsch1,#kc-wunsch2,#kc-wunsch3,#kc-wahl-select,#kc-phase-select").select2({width:"100%"}); }catch(e){} });</script>';
echo '</div>';
return;
}
// Übersicht
// Map für Wahl-ID => Name
$wahl_name_map = [];
foreach($all_wahlen as $w) {
$wahl_name_map[intval($w->id)] = $w->name;
}
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Teilnehmer</h2>';
echo '<a class="kc-btn" style="float:right;margin-bottom:12px;" href="?page=kc_teilnehmer&new=1">+ Neuer Teilnehmer</a>';
// Wahl-Filter-Buttons sortiert nach KC1, KC2, KC3, dann Rest
$kc_buttons = [];
$rest_buttons = [];
foreach($all_wahlen as $w) {
$name = strtoupper($w->name);
if ($name === 'KC1' || $name === 'KC2' || $name === 'KC3') {
$kc_buttons[$name] = $w;
} else {
$rest_buttons[] = $w;
}
}
echo '<div style="margin:12px 0;clear:both;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">'
.'<label style="font-weight:600;">Wahlen filtern:</label>';
echo '<button type="button" class="kc-btn kc-wahl-filter-btn" data-wahl-id="" style="padding:6px 10px;">Alle</button>';
foreach(["KC1","KC2","KC3"] as $kc) {
if(isset($kc_buttons[$kc])) {
$w = $kc_buttons[$kc];
echo '<button type="button" class="kc-btn kc-wahl-filter-btn" data-wahl-id="'.intval($w->id).'" style="padding:6px 10px;">'.esc_html($w->name).'</button>';
}
}
foreach($rest_buttons as $w) {
echo '<button type="button" class="kc-btn kc-wahl-filter-btn" data-wahl-id="'.intval($w->id).'" style="padding:6px 10px;">'.esc_html($w->name).'</button>';
}
echo '<span id="kc-wahl-filter-count" style="color:#666;font-size:90%;margin-left:12px;">Alle anzeigen</span>';
echo '</div>';
// Platzhalter für Phasen-Filter
echo '<div id="kc-phase-filter-row" style="display:none;margin:8px 0 16px 0;gap:8px;align-items:center;flex-wrap:wrap;"></div>';
// Teilnehmer laden und gruppieren nach Wahl und Phase
$teilnehmer = $wpdb->get_results("SELECT * FROM {$prefix}kc_teilnehmer ORDER BY wahl_id, phase, nachname, vorname");
$gruppen = [];
foreach ($teilnehmer as $tn) {
$wid = intval($tn->wahl_id);
$ph = intval($tn->phase);
$gruppen[$wid][$ph][] = $tn;
}
foreach ($gruppen as $wid => $phasen) {
$wahl_disp = isset($wahl_name_map[$wid]) ? esc_html($wahl_name_map[$wid]) : $wid;
// Gesamtanzahl Teilnehmer für diese Wahl berechnen
$gesamt = 0;
foreach ($phasen as $tns) $gesamt += count($tns);
echo '<details open style="margin:10px 0 18px 0;border:1px solid #eaeaea;border-radius:6px;padding:8px;">';
echo '<summary style="font-weight:700;cursor:pointer;">'. $wahl_disp . ' <span style="color:#555;font-weight:600;">(' . $gesamt . ' TN)</span></summary>';
foreach ($phasen as $phase => $tns) {
echo '<details open style="margin:10px 0 10px 0;border:1px solid #f0f0f0;border-radius:6px;padding:8px;">';
echo '<summary style="font-weight:600;">Phase '.intval($phase).' <span style="color:#555;font-weight:600;">('.count($tns).' TN)</span></summary>';
echo '<table class="kc-admin-table" style="margin:8px 0;">';
echo '<thead><tr><th>Vorname</th><th>Nachname</th><th>Wahl</th><th>Phase</th><th>Wunsch 1</th><th>Wunsch 2</th><th>Wunsch 3</th><th>Aktion</th></tr></thead><tbody>';
foreach ($tns as $tn) {
$w1_id = intval($tn->wunsch1);
$w2_id = intval($tn->wunsch2);
$w3_id = intval($tn->wunsch3);
$w1_disp = $w1_id && isset($workshops_map[$w1_id]) ? esc_html($workshops_map[$w1_id]) : ($w1_id ? intval($w1_id) : '<em style="color:#999;">Keine</em>');
$w2_disp = $w2_id && isset($workshops_map[$w2_id]) ? esc_html($workshops_map[$w2_id]) : ($w2_id ? intval($w2_id) : '<em style="color:#999;">Keine</em>');
$w3_disp = $w3_id && isset($workshops_map[$w3_id]) ? esc_html($workshops_map[$w3_id]) : ($w3_id ? intval($w3_id) : '<em style="color:#999;">Keine</em>');
$wahl_disp = isset($wahl_name_map[intval($tn->wahl_id)]) ? esc_html($wahl_name_map[intval($tn->wahl_id)]) : intval($tn->wahl_id);
echo "<tr data-wahl-id='".intval($tn->wahl_id)."'>
<td>".esc_html($tn->vorname)."</td>
<td>".esc_html($tn->nachname)."</td>
<td>".$wahl_disp."</td>
<td>".intval($tn->phase)."</td>
<td>".$w1_disp."</td>
<td>".$w2_disp."</td>
<td>".$w3_disp."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_teilnehmer&edit_teilnehmer={$tn->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_teilnehmer&delete_teilnehmer={$tn->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
</td>
</tr>";
}
echo '</tbody></table>';
echo '</details>';
}
echo '</details>';
}
// JS für Wahl- und Phasen-Filter
echo '<style>.kc-wahl-filter-btn.active{background:#4CAF50;color:#fff;} .kc-phase-filter-btn.active{background:#1976d2;color:#fff;}</style>';
echo '<script>
(function() {
var btns = Array.prototype.slice.call(document.querySelectorAll(".kc-wahl-filter-btn"));
var rows = Array.prototype.slice.call(document.querySelectorAll(".kc-admin-table tbody tr"));
var status = document.getElementById("kc-wahl-filter-count");
var phaseRow = document.getElementById("kc-phase-filter-row");
// Wahl-Map für Phasen
var wahlMap = '.json_encode($wahl_map, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP).';
var currentWahl = "";
var currentPhase = "";
function renderPhaseButtons(wahlId) {
phaseRow.innerHTML = "";
if (!wahlId || !wahlMap[wahlId]) { phaseRow.style.display = "none"; currentPhase = ""; return; }
var phases = wahlMap[wahlId].phases || 1;
var label = document.createElement("label");
label.textContent = "Phasen filtern:";
label.style.fontWeight = "600";
label.style.marginRight = "8px";
phaseRow.appendChild(label);
var allBtn = document.createElement("button");
allBtn.type = "button";
allBtn.className = "kc-btn kc-phase-filter-btn active";
allBtn.setAttribute("data-phase", "");
allBtn.style.padding = "6px 10px";
allBtn.textContent = "Alle";
phaseRow.appendChild(allBtn);
for(var i=1;i<=phases;i++){
var btn = document.createElement("button");
btn.type = "button";
btn.className = "kc-btn kc-phase-filter-btn";
btn.setAttribute("data-phase", i);
btn.style.padding = "6px 10px";
btn.textContent = i;
phaseRow.appendChild(btn);
}
phaseRow.style.display = "flex";
}
function applyFilter(wahlId, phase) {
var shown = 0;
rows.forEach(function(row) {
var rid = row.getAttribute("data-wahl-id");
var ph = row.querySelector("td:nth-child(4)").textContent.trim();
var show = false;
if (!wahlId) {
show = true;
} else if (!phase) {
show = (rid == wahlId);
} else {
show = (rid == wahlId && ph == phase);
}
row.style.display = show ? "" : "none";
if (show) shown++;
});
if (status) status.textContent = (wahlId ? (shown+" angezeigt") : "Alle anzeigen");
}
function attachPhaseEvents() {
var phaseBtns = Array.prototype.slice.call(document.querySelectorAll(".kc-phase-filter-btn"));
phaseBtns.forEach(function(btn) {
btn.addEventListener("click", function() {
phaseBtns.forEach(function(b){b.classList.remove("active");});
btn.classList.add("active");
currentPhase = btn.getAttribute("data-phase");
applyFilter(currentWahl, currentPhase);
});
});
}
btns.forEach(function(btn) {
btn.addEventListener("click", function() {
btns.forEach(function(b){b.classList.remove("active");});
btn.classList.add("active");
var wahlId = btn.getAttribute("data-wahl-id");
currentWahl = wahlId;
currentPhase = "";
renderPhaseButtons(wahlId);
applyFilter(wahlId, "");
attachPhaseEvents();
});
});
// Default: Alle aktiv
if(btns.length) btns[0].classList.add("active");
renderPhaseButtons("");
applyFilter("", "");
})();
</script>';
echo '</div>';
}
?>