Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
394 lines
20 KiB
PHP
394 lines
20 KiB
PHP
<?php
|
|
// Admin-Tabs (zentraler Aufruf! CSS wird global über kc-admin-style.css geladen)
|
|
function kc_admin_tabs($active) {
|
|
$menu = [
|
|
'kc_wahlen' => 'Wahlen',
|
|
'kc_teamer' => 'Teamer',
|
|
'kc_workshops' => 'Workshops',
|
|
'kc_teilnehmer' => 'Teilnehmer',
|
|
'kc_force_zuteilung'=> 'Force-Zuteilung',
|
|
'kc_zuteilungen' => 'Zuteilungen'
|
|
];
|
|
echo '<div class="kc-admin-tabs">';
|
|
foreach($menu as $slug=>$label) {
|
|
$url = admin_url('admin.php?page='.$slug);
|
|
$class = ($active==$slug) ? "kc-tabnav-active" : "";
|
|
echo "<a href=\"$url\" class=\"kc-tabnav $class\">$label</a>";
|
|
}
|
|
echo '</div>';
|
|
}
|
|
|
|
function kc_wahlen_page() {
|
|
global $wpdb;
|
|
$prefix = $wpdb->prefix;
|
|
kc_admin_tabs('kc_wahlen');
|
|
|
|
// Workshops zuweisen (Formular und Speicherung)
|
|
if (isset($_GET['zuweisen'])) {
|
|
$wahl_id = intval($_GET['zuweisen']);
|
|
$wahl = $wpdb->get_row("SELECT * FROM {$prefix}kc_wahlen WHERE id=$wahl_id");
|
|
$workshops = $wpdb->get_results("SELECT * FROM {$prefix}kc_workshops ORDER BY name");
|
|
// Sortiere erst nach KC-Nummer (kleinste zuerst), dann nach Wochentag
|
|
usort($workshops, function($a, $b) {
|
|
$kcA = 999; $kcB = 999;
|
|
if (preg_match('/(kc\s*)?(\d+)/i', $a->name, $m)) { $kcA = intval($m[2]); }
|
|
if (preg_match('/(kc\s*)?(\d+)/i', $b->name, $m)) { $kcB = intval($m[2]); }
|
|
if ($kcA !== $kcB) return $kcA <=> $kcB;
|
|
|
|
$days = [
|
|
'montag' => 1,
|
|
'dienstag' => 2,
|
|
'mittwoch' => 3,
|
|
'donnerstag' => 4,
|
|
'freitag' => 5,
|
|
'samstag' => 6,
|
|
'sonntag' => 7
|
|
];
|
|
$dA = 99; $dB = 99;
|
|
$la = strtolower($a->name);
|
|
$lb = strtolower($b->name);
|
|
foreach ($days as $day => $order) {
|
|
if ($dA === 99 && strpos($la, $day) !== false) $dA = $order;
|
|
if ($dB === 99 && strpos($lb, $day) !== false) $dB = $order;
|
|
}
|
|
if ($dA !== $dB) return $dA <=> $dB;
|
|
return strcasecmp($a->name, $b->name);
|
|
});
|
|
if (isset($_POST['kc_workshop_zuweisung_save'])) {
|
|
$wpdb->delete("{$prefix}kc_wahl_workshops", ['wahl_id'=>$wahl_id]);
|
|
$anzahl_einheiten = intval($wahl->anzahl_einheiten);
|
|
for($phase=1; $phase<=$anzahl_einheiten; $phase++) {
|
|
if (!empty($_POST["phase{$phase}_workshops"])) {
|
|
foreach($_POST["phase{$phase}_workshops"] as $workshop_id) {
|
|
$wpdb->insert("{$prefix}kc_wahl_workshops", [
|
|
'wahl_id'=>$wahl_id,
|
|
'workshop_id'=>intval($workshop_id),
|
|
'phase'=>$anzahl_einheiten>1?$phase:1
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
echo '<div class="notice notice-success">Workshops zugewiesen!</div>';
|
|
}
|
|
$zugeordnet = $wpdb->get_results("SELECT * FROM {$prefix}kc_wahl_workshops WHERE wahl_id=$wahl_id");
|
|
$phase_map = [];
|
|
foreach($zugeordnet as $z) $phase_map[$z->phase][] = $z->workshop_id;
|
|
|
|
echo '<div class="kc-admin-table-wrap">';
|
|
echo '<h2>Workshops der Wahl „'.esc_html($wahl->name).'“ zuweisen</h2>';
|
|
echo '<form method="post">';
|
|
echo '<div style="margin:12px 0;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">'
|
|
.'<label for="ws-desc-filter" style="font-weight:600;">Beschreibung filtern:</label>'
|
|
.'<input type="text" id="ws-desc-filter" placeholder="Text in Beschreibung suchen..." style="padding:6px 8px;min-width:220px;border:1px solid #ccc;border-radius:4px;">'
|
|
.'<span id="ws-filter-count" style="color:#666;font-size:90%;">Alle anzeigen</span>'
|
|
.'<div style="display:flex;gap:6px;flex-wrap:wrap;">'
|
|
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="">Alle</button>'
|
|
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="(zu ändern)">(zu ändern)</button>'
|
|
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="KC1">KC1</button>'
|
|
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="KC2">KC2</button>'
|
|
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="KC3">KC3</button>'
|
|
.'</div>'
|
|
.'</div>';
|
|
$anzahl_einheiten = intval($wahl->anzahl_einheiten);
|
|
for($phase=1; $phase<=$anzahl_einheiten; $phase++) {
|
|
echo "<h4>Phase {$phase}".($anzahl_einheiten==2?($phase==1?" (Früh)":" (Spät)"):"")."</h4>";
|
|
echo '<input type="text" class="ws-phase-search" placeholder="Workshops suchen..." data-phase="'.$phase.'" style="width:100%;padding:8px;margin-bottom:8px;border:1px solid #ddd;border-radius:4px;">';
|
|
echo '<div style="max-width:410px;background:#fafbfc;border-radius:9px;padding:12px 16px 9px 16px;box-shadow:0 1px 5px #0001;">';
|
|
foreach ($workshops as $ws) {
|
|
$checked = !empty($phase_map[$phase]) && in_array($ws->id, $phase_map[$phase]) ? 'checked' : '';
|
|
$label = esc_html($ws->name);
|
|
if (!empty($ws->beschreibung)) {
|
|
$label .= ' <span style="color:#666;font-size:90%;">(' . esc_html($ws->beschreibung) . ')</span>';
|
|
}
|
|
echo "<label class='ws-item-phase-{$phase}' style='display:block;margin-bottom:6px;cursor:pointer;' data-name='".esc_attr(strtolower($ws->name.' '.$ws->beschreibung))."' data-desc='".esc_attr(strtolower($ws->beschreibung))."'>"
|
|
."<input type='checkbox' name='phase{$phase}_workshops[]' value='{$ws->id}' {$checked} style='margin-right:8px;'> {$label}"
|
|
."</label>";
|
|
}
|
|
echo '</div><br>';
|
|
}
|
|
echo '<button name="kc_workshop_zuweisung_save" class="kc-btn" style="margin-top:12px;">Speichern</button>
|
|
<a href="?page=kc_wahlen" class="kc-btn del" style="margin-left:20px;">Zurück</a>
|
|
</form>';
|
|
|
|
echo '<script>
|
|
(function() {
|
|
var descInput = document.getElementById("ws-desc-filter");
|
|
var presets = Array.prototype.slice.call(document.querySelectorAll("[data-ws-preset]"));
|
|
var filterCount = document.getElementById("ws-filter-count");
|
|
var searchInputs = Array.prototype.slice.call(document.querySelectorAll(".ws-phase-search"));
|
|
|
|
function applyDescFilter() {
|
|
var q = descInput.value.toLowerCase().trim();
|
|
var totalItems = 0, shownItems = 0;
|
|
|
|
for(var p=1; p<='.intval($wahl->anzahl_einheiten).'; p++) {
|
|
var items = Array.prototype.slice.call(document.querySelectorAll(".ws-item-phase-" + p));
|
|
items.forEach(function(item) {
|
|
var desc = item.getAttribute("data-desc") || "";
|
|
totalItems++;
|
|
var match = q === "" || desc.indexOf(q) > -1;
|
|
item.style.display = match ? "block" : "none";
|
|
if (match) shownItems++;
|
|
});
|
|
}
|
|
|
|
if (filterCount) {
|
|
filterCount.textContent = q === "" ? "Alle anzeigen (" + totalItems + ")" : shownItems + " von " + totalItems + " Treffer";
|
|
}
|
|
}
|
|
|
|
function applyPhaseSearch() {
|
|
searchInputs.forEach(function(input) {
|
|
var phase = input.getAttribute("data-phase");
|
|
var search = input.value.toLowerCase().trim();
|
|
var items = Array.prototype.slice.call(document.querySelectorAll(".ws-item-phase-" + phase));
|
|
|
|
items.forEach(function(item) {
|
|
var name = item.getAttribute("data-name") || "";
|
|
var match = search === "" || name.indexOf(search) > -1;
|
|
item.style.display = match ? "block" : "none";
|
|
});
|
|
});
|
|
}
|
|
|
|
descInput.addEventListener("input", applyDescFilter);
|
|
|
|
presets.forEach(function(btn) {
|
|
btn.addEventListener("click", function() {
|
|
descInput.value = btn.getAttribute("data-ws-preset") || "";
|
|
applyDescFilter();
|
|
descInput.focus();
|
|
});
|
|
});
|
|
|
|
searchInputs.forEach(function(input) {
|
|
input.addEventListener("input", applyPhaseSearch);
|
|
});
|
|
|
|
applyDescFilter();
|
|
})();
|
|
</script>';
|
|
echo '</div>';
|
|
return;
|
|
}
|
|
|
|
// Wahl speichern (Bearbeiten)
|
|
if (isset($_POST['kc_wahl_update'])) {
|
|
$wahl_id = intval($_POST['wahl_id']);
|
|
$wpdb->update("{$prefix}kc_wahlen", [
|
|
'name' => sanitize_text_field($_POST['name']),
|
|
'beschreibung' => sanitize_textarea_field($_POST['beschreibung']),
|
|
'anzahl_einheiten' => intval($_POST['anzahl_einheiten']),
|
|
'freigegeben' => isset($_POST['freigegeben']) ? 1 : 0
|
|
], ['id' => $wahl_id]);
|
|
echo '<div class="notice notice-success">Wahl gespeichert!</div>';
|
|
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_wahlen')) . "'>";
|
|
return;
|
|
}
|
|
|
|
// Bearbeitungsformular
|
|
if (isset($_GET['edit_wahl'])) {
|
|
$wahl_id = intval($_GET['edit_wahl']);
|
|
$wahl = $wpdb->get_row("SELECT * FROM {$prefix}kc_wahlen WHERE id=$wahl_id");
|
|
echo '<div class="kc-admin-table-wrap">';
|
|
echo '<h2>Wahl bearbeiten</h2>
|
|
<form method="post" style="max-width:600px;">
|
|
<input type="hidden" name="wahl_id" value="'.intval($wahl->id).'">
|
|
<input type="text" name="name" placeholder="Name" required value="'.esc_attr($wahl->name).'" style="margin-bottom:6px;width:100%;padding:7px;">
|
|
<input type="text" name="beschreibung" placeholder="Beschreibung" value="'.esc_attr($wahl->beschreibung).'" style="margin-bottom:6px;width:100%;padding:7px;">
|
|
<label style="display:block;margin-bottom:6px;">
|
|
<select name="anzahl_einheiten" required>
|
|
<option value="1" '.($wahl->anzahl_einheiten==1?"selected":"").'>Nur eine Workshop-Phase</option>
|
|
<option value="2" '.($wahl->anzahl_einheiten==2?"selected":"").'>Zwei Workshop-Phasen (Früh/Spät)</option>
|
|
</select> Workshop-Phasen
|
|
</label>
|
|
<label style="margin-bottom:8px;display:block;">
|
|
<input type="checkbox" name="freigegeben" '.($wahl->freigegeben?'checked':'').'> Freigegeben
|
|
</label>
|
|
<button name="kc_wahl_update" class="kc-btn" style="margin-left:0;">Speichern</button>
|
|
<a class="kc-btn del" style="margin-left:24px;" href="?page=kc_wahlen">Abbrechen</a>
|
|
</form>';
|
|
echo '</div>';
|
|
return;
|
|
}
|
|
|
|
// Neue Wahl anlegen
|
|
if(isset($_POST['kc_wahl_neu'])) {
|
|
$wpdb->insert("{$prefix}kc_wahlen", [
|
|
'name' => sanitize_text_field($_POST['name']),
|
|
'beschreibung' => sanitize_textarea_field($_POST['beschreibung']),
|
|
'anzahl_einheiten' => intval($_POST['anzahl_einheiten']),
|
|
'freigegeben' => isset($_POST['freigegeben']) ? 1 : 0
|
|
]);
|
|
echo '<div class="notice notice-success">Wahl angelegt!</div>';
|
|
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_wahlen')) . "'>";
|
|
return;
|
|
}
|
|
|
|
// Freigabe ändern
|
|
if(isset($_POST['kc_wahl_freigabe'])) {
|
|
$wpdb->update("{$prefix}kc_wahlen", ['freigegeben' => intval($_POST['kc_wahl_freigabe'])], ['id' => $_POST['wahl_id']]);
|
|
echo '<div class="notice notice-success">Wahl-Freigabe geändert!</div>';
|
|
}
|
|
|
|
// Wahl löschen (cascade: Teilnehmer, Zuteilungen, Force-Zuteilungen, Wahl-Workshop-Mapping)
|
|
if (isset($_GET['delete_wahl'])) {
|
|
$wahl_id = intval($_GET['delete_wahl']);
|
|
|
|
// delete zuteilungen for this wahl
|
|
$wpdb->delete("{$prefix}kc_zuteilung", ['wahl_id' => $wahl_id]);
|
|
|
|
// delete force zuteilungen
|
|
$wpdb->delete("{$prefix}kc_force_zuteilung", ['wahl_id' => $wahl_id]);
|
|
|
|
// delete teilnehmer
|
|
$wpdb->delete("{$prefix}kc_teilnehmer", ['wahl_id' => $wahl_id]);
|
|
|
|
// delete wahl-workshop mappings
|
|
$wpdb->delete("{$prefix}kc_wahl_workshops", ['wahl_id' => $wahl_id]);
|
|
|
|
// mark the wahl as deleted (soft delete)
|
|
$wpdb->update("{$prefix}kc_wahlen", ['deleted'=>1], ['id'=>$wahl_id]);
|
|
|
|
echo '<div class="notice notice-success">Wahl und zugehörige Teilnehmer/Zuteilungen wurden entfernt.</div>';
|
|
}
|
|
|
|
// Zuteilung ausführen (per Button auf der Übersicht)
|
|
if (isset($_GET['run_zuteilung'])) {
|
|
$wahl_id = intval($_GET['run_zuteilung']);
|
|
// nonce check for safety
|
|
if (!empty($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'kc_run_zuteilung_' . $wahl_id)) {
|
|
if (function_exists('kc_run_zuteilung')) {
|
|
kc_run_zuteilung($wahl_id);
|
|
// nach Zuteilung anzeigen
|
|
echo '<div class="notice notice-success">Zuteilung wurde ausgeführt.</div>';
|
|
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_wahlen&show_zuteilung=' . $wahl_id)) . "'>";
|
|
return;
|
|
} else {
|
|
echo '<div class="notice notice-error">Zuteilungsfunktion nicht verfügbar.</div>';
|
|
}
|
|
} else {
|
|
echo '<div class="notice notice-error">Ungültiger Sicherheits-Token.</div>';
|
|
}
|
|
}
|
|
|
|
echo '<div class="kc-admin-table-wrap">';
|
|
echo '<h2 style="margin-top:0;">Alle Wahlen</h2>';
|
|
$wahlen = $wpdb->get_results("SELECT * FROM {$prefix}kc_wahlen WHERE deleted=0");
|
|
// Sortiere Übersicht: erst KC-Nummer (aufsteigend), dann Wochentag (Mo-So), dann Name
|
|
usort($wahlen, function($a, $b) {
|
|
$kcA = 999; $kcB = 999;
|
|
if (preg_match('/(kc\s*)?(\d+)/i', $a->name, $m)) { $kcA = intval($m[2]); }
|
|
if (preg_match('/(kc\s*)?(\d+)/i', $b->name, $m)) { $kcB = intval($m[2]); }
|
|
if ($kcA !== $kcB) return $kcA <=> $kcB;
|
|
|
|
$days = [
|
|
'montag' => 1,
|
|
'dienstag' => 2,
|
|
'mittwoch' => 3,
|
|
'donnerstag' => 4,
|
|
'freitag' => 5,
|
|
'samstag' => 6,
|
|
'sonntag' => 7
|
|
];
|
|
$dA = 99; $dB = 99;
|
|
$la = strtolower($a->name);
|
|
$lb = strtolower($b->name);
|
|
foreach ($days as $day => $order) {
|
|
if ($dA === 99 && strpos($la, $day) !== false) $dA = $order;
|
|
if ($dB === 99 && strpos($lb, $day) !== false) $dB = $order;
|
|
}
|
|
if ($dA !== $dB) return $dA <=> $dB;
|
|
return strcasecmp($a->name, $b->name);
|
|
});
|
|
echo '<table class="kc-admin-table">';
|
|
echo '<thead><tr><th>Name</th><th>Phasen</th><th>Kapazität (pro Phase)</th><th>Teilnehmer (pro Phase)</th><th>Freigegeben</th><th>Shortcode</th><th>Aktion</th></tr></thead><tbody>';
|
|
foreach($wahlen as $wahl) {
|
|
// compute per-phase capacity and participant counts
|
|
$anz = intval($wahl->anzahl_einheiten);
|
|
$caps = [];
|
|
$counts = [];
|
|
$insufficient = false;
|
|
for($phase=1;$phase<=$anz;$phase++) {
|
|
// total capacity = sum of max_teilnehmer for workshops assigned to this wahl and phase
|
|
$caps[$phase] = intval($wpdb->get_var($wpdb->prepare(
|
|
"SELECT COALESCE(SUM(ws.max_teilnehmer),0) FROM {$prefix}kc_workshops ws JOIN {$prefix}kc_wahl_workshops ww ON ws.id=ww.workshop_id WHERE ww.wahl_id=%d AND ww.phase=%d",
|
|
$wahl->id, $phase
|
|
)));
|
|
// number of participants for this wahl and phase
|
|
$counts[$phase] = intval($wpdb->get_var($wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE wahl_id=%d AND phase=%d",
|
|
$wahl->id, $phase
|
|
)));
|
|
if ($caps[$phase] < $counts[$phase]) $insufficient = true;
|
|
}
|
|
|
|
echo '<tr>';
|
|
echo '<td>'.esc_html($wahl->name).'</td>';
|
|
echo '<td>'.intval($wahl->anzahl_einheiten).'</td>';
|
|
// capacity column (show per phase)
|
|
echo '<td>';
|
|
$cap_labels = [];
|
|
for($phase=1;$phase<=$anz;$phase++) {
|
|
$cap_labels[] = 'P'.$phase.': '.esc_html($caps[$phase]);
|
|
}
|
|
echo implode('<br>', $cap_labels);
|
|
echo '</td>';
|
|
// participants column (show per phase, highlight if exceeding capacity)
|
|
echo '<td>';
|
|
$count_labels = [];
|
|
for($phase=1;$phase<=$anz;$phase++) {
|
|
$label = 'P'.$phase.': '.esc_html($counts[$phase]);
|
|
if ($caps[$phase] < $counts[$phase]) {
|
|
$diff = $counts[$phase] - $caps[$phase];
|
|
$label .= ' <span style="color:#e12b2b;font-weight:700;">(+'.intval($diff).' fehlen)</span>';
|
|
} else {
|
|
$label .= ' <span style="color:#2b8a2b;font-weight:700;">(OK)</span>';
|
|
}
|
|
$count_labels[] = $label;
|
|
}
|
|
echo implode('<br>', $count_labels);
|
|
echo '</td>';
|
|
echo '<td>';
|
|
echo "<form method='post' style='display:inline'>";
|
|
echo "<input type='hidden' name='wahl_id' value='".intval($wahl->id)."'>";
|
|
echo "<button name='kc_wahl_freigabe' value='".($wahl->freigegeben?"0":"1")."' class='kc-btn edit' type='submit' style='background:".($wahl->freigegeben?"#e12b2b":"#b6d333").";color:#fff;font-weight:bold;'>".($wahl->freigegeben?"Schließen":"Freigeben (zum eintragen)")."</button>";
|
|
echo "</form>";
|
|
echo '<span style="color:'.($wahl->freigegeben?"#4176be":"#d40000").';margin-left:6px;font-size:90%;font-weight:bold;">'.($wahl->freigegeben ? "freigegeben" : "geschlossen").'</span>';
|
|
echo '</td>';
|
|
echo '<td><code>[konficastle_workshopwahl wahl="'.intval($wahl->id).'"]</code></td>';
|
|
echo '<td class="kc-actions">';
|
|
echo '<a class="kc-btn" href="?page=kc_wahlen&zuweisen='.intval($wahl->id).'">Workshops zuweisen</a>';
|
|
echo '<a class="kc-btn" href="?page=kc_wahlen&edit_wahl='.intval($wahl->id).'">Bearbeiten</a>';
|
|
$nonce = wp_create_nonce('kc_run_zuteilung_' . intval($wahl->id));
|
|
echo '<a class="kc-btn" href="?page=kc_wahlen&run_zuteilung='.intval($wahl->id).'&_wpnonce='.$nonce.'" onclick="return confirm(\'Zuteilung wirklich starten? Dies überschreibt vorhandene Zuteilungen.\');">Zuteilung starten</a>';
|
|
echo '<a class="kc-btn" href="?page=kc_wahlen&show_zuteilung='.intval($wahl->id).'">Zuteilung anzeigen</a>';
|
|
echo '<a href="?page=kc_wahlen&delete_wahl='.intval($wahl->id).'" class="kc-btn del" onclick="return confirm(\'Wirklich löschen?\');">Löschen</a>';
|
|
echo '</td>';
|
|
echo '</tr>';
|
|
}
|
|
echo '</tbody></table>';
|
|
|
|
// Neue Wahl anlegen-Formular
|
|
echo '<hr><h2>Neue Wahl anlegen</h2>
|
|
<form method="post" style="max-width:600px;">
|
|
<input type="text" name="name" placeholder="Name" required style="margin-bottom:6px;width:100%;padding:7px;">
|
|
<input type="text" name="beschreibung" placeholder="Beschreibung" style="margin-bottom:6px;width:100%;padding:7px;">
|
|
<label style="display:block;margin-bottom:6px;">
|
|
<select name="anzahl_einheiten" required>
|
|
<option value="1">Nur eine Workshop-Phase</option>
|
|
<option value="2">Zwei Workshop-Phasen (z.B. morgens/abends)</option>
|
|
</select> Workshop-Phasen
|
|
</label>
|
|
<label style="margin-bottom:8px;display:block;"><input type="checkbox" name="freigegeben"> Freigeben (zum eintragen)</label>
|
|
<button name="kc_wahl_neu" class="kc-btn" style="margin-left:0;">Wahl anlegen</button>
|
|
</form>';
|
|
echo '</div>';
|
|
|
|
// Zeige die Zuteilung, wenn angefordert:
|
|
if (isset($_GET['show_zuteilung'])) {
|
|
$wahl_id = intval($_GET['show_zuteilung']);
|
|
if (function_exists('kc_zeige_zuteilung')) kc_zeige_zuteilung($wahl_id);
|
|
}
|
|
}
|
|
?>
|