From 0c35946b58a7c239f99ab12ac22794f9117b89eb Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Fri, 30 Jan 2026 15:52:55 +0100 Subject: [PATCH 01/58] Adding plugin header for Workshop-Wahlen --- konficastle-workshopwahl.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/konficastle-workshopwahl.php b/konficastle-workshopwahl.php index 8e71ad4..8f323af 100644 --- a/konficastle-workshopwahl.php +++ b/konficastle-workshopwahl.php @@ -1,10 +1,11 @@ Date: Fri, 30 Jan 2026 15:54:15 +0100 Subject: [PATCH 02/58] replacing of header --- konficastle-workshopwahl.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/konficastle-workshopwahl.php b/konficastle-workshopwahl.php index 8f323af..790cae2 100644 --- a/konficastle-workshopwahl.php +++ b/konficastle-workshopwahl.php @@ -1,10 +1,10 @@ Date: Fri, 30 Jan 2026 16:33:09 +0100 Subject: [PATCH 03/58] generated importent files --- .github/copilot-instructions.md | 37 ++++++++++++++++++++++++ README.md | 50 ++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..547b781 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,37 @@ +# Copilot Instructions for Workshop-Wahlen + +## Projektüberblick + +Dieses WordPress-Plugin verwaltet Workshop-Wahlen für Konfi-Castle-Events. Es bietet ein Admin-Backend (Wahlen, Workshops, Teilnehmer, Teamer, Zuteilungen) und ein Teilnehmer-Frontend. + +## Architektur & Komponenten +- **Hauptdatei:** `konficastle-workshopwahl.php` – Initialisiert Hooks, lädt Assets, Admin-Menüs. +- **Backend:** + - `includes/` mit Modulen für Wahlen, Workshops, Teilnehmer, Teamer, Zuteilungen, Datenverwaltung, Force-Zuteilung + - Zentrale Zuteilungslogik: `includes/zuteilungslogik.php` +- **Frontend:** + - Shortcodes: `[konficastle_workshopwahl wahl=ID]`, `[konficastle_workshop_ergebnis wahl=ID]` +- **Datenbank:** Tabellen werden über `install.php` angelegt, Präfix: `kc_` + +## Wichtige Patterns & Workflows +- **Admin-Tabs:** Navigation über `kc_admin_tabs()` +- **Namensschema:** Funktionen und Tabellen mit `kc_`-Präfix +- **Force-Zuteilungen:** Manuelle Zuweisungen haben Vorrang +- **Testdaten:** Nur User ID 1 kann über Admin Daten generieren (`admin-data.php`) +- **CSV-Export:** Über Admin-Zuteilungen möglich +- **Teamer-Passwort:** Verwaltung über Admin, Hash in WP-Optionen + +## Beispiele & Einstiegspunkte +- **Admin-Menü:** `konficastle-workshopwahl.php`, `includes/admin-wahlen.php` +- **Zuteilungslogik:** `includes/zuteilungslogik.php` +- **Frontend-Formular:** `includes/frontend-form.php` + +## Hinweise für AI Agents +- Immer `$wpdb->prefix` für DB-Tabellen verwenden +- Backend-Logik ist modular, jede Entität hat eigene Datei +- Keine komplexe JS-Logik im Frontend, Validierung serverseitig +- Bei Änderungen an der Zuteilungslogik: Testszenarien über Testdaten-Adminseite nutzen + +--- + +Siehe auch README.md für weitere Details. \ No newline at end of file diff --git a/README.md b/README.md index 78379fd..e81a526 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,50 @@ -# Workshop-Wahlen +# Workshop-Wahlen – Entwickler-Übersicht + +## Überblick + +Dieses WordPress-Plugin ermöglicht die Verwaltung und Durchführung von Workshop-Wahlen für Konfi-Castle-Events. Es bietet ein vollständiges Backend für Admins (Wahlen, Workshops, Teilnehmer, Teamer, Zuteilungen) und ein Frontend-Formular für Teilnehmer. + +## Architektur & Hauptkomponenten + +- **Haupt-Plugin-Datei:** `konficastle-workshopwahl.php` – Registriert Hooks, lädt Assets, initialisiert Admin-Menüs. +- **Backend-Module (im `includes/`-Verzeichnis):** + - `admin-wahlen.php`, `admin-workshops.php`, `admin-teilnehmer.php`, `admin-teamer.php`, `admin-zuteilungen.php`, `admin-data.php`, `force-zuteilung.php`: Jeweils eigene Admin-Seiten für die Verwaltung der zugehörigen Entitäten. + - `zuteilungslogik.php`: Kernlogik für die automatische Zuteilung von Teilnehmern zu Workshops (inkl. Force-Zuteilungen und Kapazitätsprüfung). +- **Frontend:** + - `frontend-form.php`: Shortcode `[konficastle_workshopwahl wahl=ID]` für das Teilnehmer-Formular. + - `frontend-ergebnis.php`: Shortcode `[konficastle_workshop_ergebnis wahl=ID]` für Ergebnisanzeige. +- **Styles & Assets:** + - `assets/`: Enthält CSS für Admin und Frontend. + +## Datenbank & Installation + +- Tabellen werden über `install.php` beim Aktivieren angelegt (z.B. `kc_wahlen`, `kc_workshops`, `kc_teilnehmer`, `kc_zuteilung`, ...). +- Tabellenpräfix wird dynamisch über `$wpdb->prefix` verwendet. + +## Entwickler-Workflows + +- **Testdaten:** Über die Admin-Seite "Datenverwaltung" (`admin-data.php`) können Testdaten generiert werden (nur User ID 1). +- **CSV-Export:** Zuteilungen können über die Admin-Seite exportiert werden (`admin-zuteilungen.php`). +- **Zuteilungslogik:** Anpassungen an der Kernlogik erfolgen in `zuteilungslogik.php`. +- **Shortcodes:** + - `[konficastle_workshopwahl wahl=ID]` – Teilnehmer-Frontend + - `[konficastle_workshop_ergebnis wahl=ID]` – Ergebnisanzeige + +## Besondere Konventionen & Hinweise + +- **Namensschema:** Alle Plugin-Funktionen und Tabellen sind mit `kc_` (Konfi-Castle) prefixiert. +- **Admin-Tabs:** Navigation zwischen Admin-Seiten über `kc_admin_tabs()`. +- **Force-Zuteilungen:** Manuelle Zuweisungen haben Vorrang vor automatischer Logik. +- **Teamer-Passwort:** Verwaltung über eigene Admin-Seite, Passwort-Hash in WP-Optionen. +- **Frontend-Validierung:** Erfolgt serverseitig, keine komplexe JS-Logik im Frontend. + +## Einstiegspunkte & Beispiele + +- **Admin-Menüstruktur:** Siehe `konficastle-workshopwahl.php` und `includes/admin-wahlen.php`. +- **Zuteilungslogik:** Siehe `includes/zuteilungslogik.php` (Funktionen wie `kc_run_zuteilung`). +- **Frontend-Formular:** Siehe `includes/frontend-form.php` (Shortcode-Handler). + +--- + +Für Detailfragen zu Datenbankstruktur, Shortcodes oder Zuteilungslogik siehe die jeweiligen Dateien im `includes/`-Verzeichnis. -- 2.49.1 From 440956320da9bab92302631b2d632b8f0379b71a Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Fri, 30 Jan 2026 17:00:32 +0100 Subject: [PATCH 04/58] adding encryption to form and addid form checks --- assets/frontend-form.js | 60 ++++++++++++++++++++++++++++++++++++++ includes/admin-teamer.php | 6 ++-- includes/frontend-form.php | 17 +++++++++-- 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 assets/frontend-form.js diff --git a/assets/frontend-form.js b/assets/frontend-form.js new file mode 100644 index 0000000..bdc7f85 --- /dev/null +++ b/assets/frontend-form.js @@ -0,0 +1,60 @@ +// Client-side validation for Workshopwahl frontend form +// This script validates required fields and email format before submission + +document.addEventListener('DOMContentLoaded', function () { + var form = document.querySelector('.kc-workshopwahl-form'); + if (!form) return; + + form.addEventListener('submit', function (e) { + var valid = true; + var errorMessages = []; + + // Example: Validate required text fields + var requiredFields = form.querySelectorAll('[required]'); + requiredFields.forEach(function (field) { + if (!field.value.trim()) { + valid = false; + errorMessages.push(field.getAttribute('data-label') || field.name + ' ist erforderlich.'); + field.classList.add('kc-field-error'); + } else { + field.classList.remove('kc-field-error'); + } + }); + + // Example: Validate email format + var emailField = form.querySelector('input[type="email"]'); + if (emailField && emailField.value) { + var emailPattern = /^[^@\s]+@[^@\s]+\.[^@\s]+$/; + if (!emailPattern.test(emailField.value)) { + valid = false; + errorMessages.push('Bitte eine gültige E-Mail-Adresse eingeben.'); + emailField.classList.add('kc-field-error'); + } else { + emailField.classList.remove('kc-field-error'); + } + } + + // Example: Validate max workshop selections (if relevant) + var maxWorkshops = parseInt(form.getAttribute('data-max-workshops'), 10); + if (maxWorkshops) { + var checked = form.querySelectorAll('input[type="checkbox"][name^="workshop_"]:checked'); + if (checked.length > maxWorkshops) { + valid = false; + errorMessages.push('Es dürfen maximal ' + maxWorkshops + ' Workshops gewählt werden.'); + } + } + + // Show error messages + var errorBox = form.querySelector('.kc-form-errors'); + if (!errorBox) { + errorBox = document.createElement('div'); + errorBox.className = 'kc-form-errors'; + form.prepend(errorBox); + } + errorBox.innerHTML = errorMessages.length ? '' : ''; + + if (!valid) { + e.preventDefault(); + } + }); +}); diff --git a/includes/admin-teamer.php b/includes/admin-teamer.php index e40934b..06affe9 100644 --- a/includes/admin-teamer.php +++ b/includes/admin-teamer.php @@ -14,8 +14,10 @@ function kc_teamer_page() { delete_option('kc_teamer_password_hash'); echo '
Teamer-Passwort entfernt.
'; } else { - update_option('kc_teamer_password_hash', wp_hash_password($pw)); - echo '
Teamer-Passwort gespeichert.
'; + // Sichere Speicherung mit password_hash + $hash = password_hash($pw, PASSWORD_DEFAULT); + update_option('kc_teamer_password_hash', $hash); + echo '
Teamer-Passwort gespeichert.
'; } } } diff --git a/includes/frontend-form.php b/includes/frontend-form.php index a83effd..0a043a2 100644 --- a/includes/frontend-form.php +++ b/includes/frontend-form.php @@ -4,6 +4,10 @@ add_shortcode('konficastle_workshopwahl', function($atts) { $wahl_id = intval($atts['wahl']); global $wpdb; + // Enqueue client-side validation JS + add_action('wp_footer', function() { + echo ''; + }); // KRITISCHER TEST: Ausgabe ganz am Anfang //$debug_output = '
'; @@ -278,8 +282,17 @@ add_shortcode('konficastle_teamer_create', function($atts) { } else { $pw = trim($_POST['kc_teamer_pw'] ?? ''); $saved_hash = get_option('kc_teamer_password_hash', ''); - if (empty($saved_hash) || !wp_check_password($pw, $saved_hash)) { - $msg = '
Falsches Passwort.
'; + $valid_pw = false; + if (!empty($saved_hash)) { + if (password_verify($pw, $saved_hash)) { + $valid_pw = true; + } else if (function_exists('wp_check_password') && wp_check_password($pw, $saved_hash)) { + // Rückwärtskompatibilität: alter Hash + $valid_pw = true; + } + } + if (!$valid_pw) { + $msg = '
Falsches Passwort.
'; } else { $vorname = sanitize_text_field($_POST['vorname'] ?? ''); $nachname = sanitize_text_field($_POST['nachname'] ?? ''); -- 2.49.1 From 7a45a787533fe3bfbd87aa3a7c103839f56b315d Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Fri, 30 Jan 2026 17:08:58 +0100 Subject: [PATCH 05/58] Ich habe im Frondend die Ergebniss anzeifge verbessert --- includes/frontend-ergebnis.php | 10 +++++----- includes/frontend-form.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/frontend-ergebnis.php b/includes/frontend-ergebnis.php index 34b32e3..d815da6 100644 --- a/includes/frontend-ergebnis.php +++ b/includes/frontend-ergebnis.php @@ -209,8 +209,8 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) { foreach($teilnehmer as $t) { $is_me = in_array(intval($t->id), $my_ids); $name = esc_html($t->vorname.' '.$t->nachname); - $label = $name . ' ('.intval($t->phase).')'; - echo '
'. $label .'
'; + $label = $name . ' ' . intval($t->phase) . ''; + echo '
'. $label .'
'; } echo '
'; // kc-participants echo ''; // content @@ -225,11 +225,11 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) { if (!empty($nicht_zugeteilt)) { echo '
'; echo 'Nicht zugeteilt:
'; - echo '
    '; + echo '
    '; foreach($nicht_zugeteilt as $t) { - echo '
  • '.esc_html($t->vorname.' '.$t->nachname).' (Phase '.intval($t->phase).')
  • '; + echo ''.esc_html($t->vorname.' '.$t->nachname).' '.intval($t->phase).''; } - echo '
'; + echo ''; } echo ''; // kc-result diff --git a/includes/frontend-form.php b/includes/frontend-form.php index 0a043a2..c99d774 100644 --- a/includes/frontend-form.php +++ b/includes/frontend-form.php @@ -20,7 +20,7 @@ add_shortcode('konficastle_workshopwahl', function($atts) { $wahl = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}kc_wahlen WHERE id=%d", $wahl_id)); if(!$wahl || !$wahl->freigegeben) { - return $debug_output . '
Die Workshopwahl ist aktuell nicht freigeschaltet.
'; + return $debug_output . '
'; } // Ermittle erlaubte Workshops pro Phase für diese Wahl. -- 2.49.1 From e57ac1bd6db63af38c9a82c38840db0a14930712 Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Fri, 30 Jan 2026 17:12:12 +0100 Subject: [PATCH 06/58] gesperte Wahl angepasst --- includes/frontend-form.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/includes/frontend-form.php b/includes/frontend-form.php index c99d774..320921e 100644 --- a/includes/frontend-form.php +++ b/includes/frontend-form.php @@ -4,10 +4,6 @@ add_shortcode('konficastle_workshopwahl', function($atts) { $wahl_id = intval($atts['wahl']); global $wpdb; - // Enqueue client-side validation JS - add_action('wp_footer', function() { - echo ''; - }); // KRITISCHER TEST: Ausgabe ganz am Anfang //$debug_output = '
'; @@ -20,7 +16,7 @@ add_shortcode('konficastle_workshopwahl', function($atts) { $wahl = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}kc_wahlen WHERE id=%d", $wahl_id)); if(!$wahl || !$wahl->freigegeben) { - return $debug_output . '
'; + return $debug_output . '
'; } // Ermittle erlaubte Workshops pro Phase für diese Wahl. @@ -282,17 +278,8 @@ add_shortcode('konficastle_teamer_create', function($atts) { } else { $pw = trim($_POST['kc_teamer_pw'] ?? ''); $saved_hash = get_option('kc_teamer_password_hash', ''); - $valid_pw = false; - if (!empty($saved_hash)) { - if (password_verify($pw, $saved_hash)) { - $valid_pw = true; - } else if (function_exists('wp_check_password') && wp_check_password($pw, $saved_hash)) { - // Rückwärtskompatibilität: alter Hash - $valid_pw = true; - } - } - if (!$valid_pw) { - $msg = '
Falsches Passwort.
'; + if (empty($saved_hash) || !wp_check_password($pw, $saved_hash)) { + $msg = '
Falsches Passwort.
'; } else { $vorname = sanitize_text_field($_POST['vorname'] ?? ''); $nachname = sanitize_text_field($_POST['nachname'] ?? ''); -- 2.49.1 From 306868735c01a5a69c8a2c56990f0784a132fed0 Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Fri, 30 Jan 2026 17:27:43 +0100 Subject: [PATCH 07/58] deleted --- .github/copilot-instructions.md | 37 --------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 547b781..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,37 +0,0 @@ -# Copilot Instructions for Workshop-Wahlen - -## Projektüberblick - -Dieses WordPress-Plugin verwaltet Workshop-Wahlen für Konfi-Castle-Events. Es bietet ein Admin-Backend (Wahlen, Workshops, Teilnehmer, Teamer, Zuteilungen) und ein Teilnehmer-Frontend. - -## Architektur & Komponenten -- **Hauptdatei:** `konficastle-workshopwahl.php` – Initialisiert Hooks, lädt Assets, Admin-Menüs. -- **Backend:** - - `includes/` mit Modulen für Wahlen, Workshops, Teilnehmer, Teamer, Zuteilungen, Datenverwaltung, Force-Zuteilung - - Zentrale Zuteilungslogik: `includes/zuteilungslogik.php` -- **Frontend:** - - Shortcodes: `[konficastle_workshopwahl wahl=ID]`, `[konficastle_workshop_ergebnis wahl=ID]` -- **Datenbank:** Tabellen werden über `install.php` angelegt, Präfix: `kc_` - -## Wichtige Patterns & Workflows -- **Admin-Tabs:** Navigation über `kc_admin_tabs()` -- **Namensschema:** Funktionen und Tabellen mit `kc_`-Präfix -- **Force-Zuteilungen:** Manuelle Zuweisungen haben Vorrang -- **Testdaten:** Nur User ID 1 kann über Admin Daten generieren (`admin-data.php`) -- **CSV-Export:** Über Admin-Zuteilungen möglich -- **Teamer-Passwort:** Verwaltung über Admin, Hash in WP-Optionen - -## Beispiele & Einstiegspunkte -- **Admin-Menü:** `konficastle-workshopwahl.php`, `includes/admin-wahlen.php` -- **Zuteilungslogik:** `includes/zuteilungslogik.php` -- **Frontend-Formular:** `includes/frontend-form.php` - -## Hinweise für AI Agents -- Immer `$wpdb->prefix` für DB-Tabellen verwenden -- Backend-Logik ist modular, jede Entität hat eigene Datei -- Keine komplexe JS-Logik im Frontend, Validierung serverseitig -- Bei Änderungen an der Zuteilungslogik: Testszenarien über Testdaten-Adminseite nutzen - ---- - -Siehe auch README.md für weitere Details. \ No newline at end of file -- 2.49.1 From 7e661c4a580ff982f78c38023c565eb67dbae16b Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Fri, 30 Jan 2026 17:31:00 +0100 Subject: [PATCH 08/58] add deployment --- .gitea/workflows/deploy.yml | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .gitea/workflows/deploy.yml diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..95081c1 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,49 @@ +name: Deploy Workshop-Wahlen (DEV / PROD) + +on: + push: + branches: + - develop + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + # DEV + - name: Deploy to DEV + if: github.ref == 'refs/heads/develop' + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.SFTP_HOST }} + port: ${{ secrets.SFTP_PORT }} + username: ${{ secrets.SFTP_USER }} + password: ${{ secrets.SFTP_PASS }} + source: | + assets + includes + *.php + README.md + target: "/dev.konfi-castle.com/wp-content/plugins/konficastle-workshopwahl/" + rm: true + + # PROD + - name: Deploy to PROD + if: github.ref == 'refs/heads/main' + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.SFTP_HOST }} + port: ${{ secrets.SFTP_PORT }} + username: ${{ secrets.SFTP_USER }} + password: ${{ secrets.SFTP_PASS }} + source: | + assets + includes + *.php + README.md + target: "/httpdocs/wp-content/plugins/konficastle-workshopwahl/" + rm: true -- 2.49.1 From 6203a74a2d4bd368771f5be20c93b0c1e71b2317 Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Fri, 30 Jan 2026 17:41:56 +0100 Subject: [PATCH 09/58] reorder css --- assets/frontend-form.css | 1 + assets/frontend.css | 121 +++++++++++++++++++ assets/kc-admin-style.css | 1 + assets/kc-admin.css | 205 +++++++++++++++++++++++++++++++++ includes/admin-teilnehmer.php | 2 +- includes/frontend-ergebnis.php | 23 +--- includes/frontend-form.php | 21 +--- konficastle-workshopwahl.php | 11 +- 8 files changed, 339 insertions(+), 46 deletions(-) create mode 100644 assets/frontend.css create mode 100644 assets/kc-admin.css diff --git a/assets/frontend-form.css b/assets/frontend-form.css index 17320a4..5c44038 100644 --- a/assets/frontend-form.css +++ b/assets/frontend-form.css @@ -102,3 +102,4 @@ .kc-form-container h2 {font-size:1.4em} .kc-form-row {margin-bottom:14px} } +[Diese Datei ist leer.] diff --git a/assets/frontend.css b/assets/frontend.css new file mode 100644 index 0000000..bd1e878 --- /dev/null +++ b/assets/frontend.css @@ -0,0 +1,121 @@ +.kc-form-container { + box-sizing: border-box; + width: 100%; + max-width: 680px; + margin: 28px auto; + background: #f8fbe7; + border-left: 8px solid #b6d333; + border-radius: 14px; + box-shadow: 0 5px 22px #8eae291a; + padding: 28px 22px; + font-family: 'Segoe UI', Arial, sans-serif; +} + +.kc-form-container h2 { + color: #3b5323; + font-family: 'Montserrat', Arial, sans-serif; + font-size: 2em; + margin-top: 0; + margin-bottom: 0.5em; + letter-spacing: -1px; +} + +.kc-form-row { + margin-bottom: 22px; +} + +.kc-form-row label { + display: block; + font-weight: 700; + color: #1c3866; + margin-bottom: 7px; + font-size: 1.07em; +} + +.kc-form-row select, +.kc-form-row input[type="text"], +.kc-form-row input[type="email"], +.kc-form-row input[type="number"] { + box-sizing: border-box; + width: 100%; + padding: 10px; + border: 1.7px solid #d6e39f; + border-radius: 5px; + background: #fcffe9; + font-size: 1.09em; + margin-top: 3px; + transition: border .2s; +} + +.kc-form-row input[type="text"]:focus, +.kc-form-row select:focus { + outline: none; + border-color: #b6d333; + background: #fffde8; +} + +.kc-form-row input[type="submit"] { + background: #2f5393; + color: #fff; + padding: 13px 28px; + font-weight: bold; + font-size: 1.13em; + border: 0; + border-radius: 7px; + cursor: pointer; + margin-top: 6px; + box-shadow: 0 2px 8px #1c38661a; + transition: background .2s; +} +.kc-form-row input[type="submit"]:hover { + background: #1c3866; +} + +.kc-required { + color: #e42626; + font-weight: bold; +} +.kc-success-msg { + color: #288830; + background: #f2fbe2; + border-left: 4px solid #b6d333; + padding: 15px 15px 15px 22px; + margin-bottom: 25px; + font-size: 1.1em; + border-radius: 7px; +} +.kc-error-msg { + color: #a80000; + background: #ffeaea; + border-left: 4px solid #e12b2b; + padding: 15px 15px 15px 22px; + margin-bottom: 25px; + font-size: 1.1em; + border-radius: 7px; +} +@media (max-width: 900px) { + .kc-form-container {padding:20px 14px;} +} + +@media (max-width: 520px) { + .kc-form-container {padding:14px 10px;border-radius:10px;margin:14px 10px;} + .kc-form-container h2 {font-size:1.4em} + .kc-form-row {margin-bottom:14px} +} + +.kc-result{font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:#222;} +.kc-result h3{margin-top:0;text-align:center;color:#154a3b;} +.kc-phase{margin:18px 0;padding:12px;border-radius:12px;background:#fbfffe;border:1px solid #e6f3ee;} +.kc-result .kc-inner { max-width:1100px; margin:0 auto; padding:0 14px; box-sizing:border-box; } +.kc-workshops-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));gap:14px;margin-top:12px;align-items:start;} +.kc-workshop-card{background:#ffffff;border-radius:12px;overflow:hidden;border:1px solid #e9f4f0;box-shadow:0 2px 6px rgba(8,38,28,0.04);width:100%;} +.kc-workshop-card .title{background:linear-gradient(90deg, rgba(45,166,106,0.04), rgba(13,89,71,0.02));display:flex;flex-wrap:wrap;align-items:center;justify-content:center;padding:18px 12px;font-weight:800;color:#0d5947;font-size:1.06rem;letter-spacing:0.2px;border-bottom:1px solid rgba(229,244,240,0.8);gap:12px;} +.kc-workshop-card .title .count{color:#6b6b6b;font-weight:600;font-size:0.9rem;margin-left:6px;} +.kc-workshop-card .title .teamers{font-weight:600;color:#145a47;font-size:0.9rem;opacity:0.92;} +.kc-workshop-card .title .teamers small{font-weight:500;color:#4c7a6a;opacity:0.9;font-size:0.85rem;margin-left:6px;} +.kc-workshop-card .content{padding:12px 18px 18px 18px;} +.kc-participants{display:grid;grid-template-columns:1fr 1fr;gap:6px 12px;font-size:0.95rem;color:#2b2b2b;} +.kc-participant{padding:6px 8px;border-radius:6px;background:transparent;} +.kc-participant.me{background:#fffbe6;border:1px solid #ffeab2;} +.kc-notassigned{background:#fff6f6;border:1px solid #ffd2d2;padding:12px;border-radius:10px;margin-top:12px;} +@media(max-width:700px){ .kc-participants{grid-template-columns:1fr;} } diff --git a/assets/kc-admin-style.css b/assets/kc-admin-style.css index 60934ab..8648401 100644 --- a/assets/kc-admin-style.css +++ b/assets/kc-admin-style.css @@ -201,3 +201,4 @@ details[open] summary:before { transform: rotate(0deg); } .kc-admin-table td { text-align: left; padding: 10px 12px; white-space: normal; } .kc-admin-table td:before { content: attr(data-label); font-weight:700; display:block; margin-bottom:6px; color:#4176be; } } +[Diese Datei ist leer.] diff --git a/assets/kc-admin.css b/assets/kc-admin.css new file mode 100644 index 0000000..ed22b63 --- /dev/null +++ b/assets/kc-admin.css @@ -0,0 +1,205 @@ +.kc-admin-tabs { + margin-bottom: 28px; + border-bottom: 2px solid #e0e6ef; + background: #f8fbe7; + border-radius: 18px 18px 0 0; + box-shadow: 0 1px 10px #b6d33321; + padding: 8px 18px 0 18px; +} +.kc-tabnav { + display: inline-block; + padding: 12px 32px 11px 32px; + border-radius: 12px 12px 0 0; + font-weight: 700; + font-size: 1.12em; + margin-right: 7px; + box-shadow: 0 2px 11px #b6d33318; + text-decoration: none; + background: #f8fbe7; + color: #4176be; + border: none; + outline: none; + transition: all .18s; + letter-spacing: 0.5px; +} +.kc-tabnav-active, +.kc-tabnav:focus, +.kc-tabnav:hover { + background: linear-gradient(90deg, #326dd2 0%, #b6d333 100%); + color: #fff !important; + box-shadow: 0 6px 24px #326dd241; + font-weight: 800; + letter-spacing: 1px; +} +.kc-admin-table-wrap { + box-sizing: border-box; + background: #fff; + border-radius: 17px; + box-shadow: 0 4px 28px #326dd21c, 0 1.5px 7px #b6d33324; + padding: 28px 22px 30px 22px; + margin: 28px 0; + /* allow children to overflow (tables may scroll), don't clip action buttons */ + overflow: visible; +} + +/* Collapsible details styling for admin Zuteilungen */ +details.kc-wahl-details, details.kc-phase-details { + background: #fff; + border: 1px solid #e9eef6; + border-radius: 10px; + padding: 8px; + margin-bottom: 12px; +} +details.kc-wahl-details > summary, details.kc-phase-details > summary { + list-style: none; + cursor: pointer; + padding: 8px 10px; +} +details.kc-wahl-details[open] > summary, details.kc-phase-details[open] > summary { + background: linear-gradient(90deg,#f2f9ff 0%, #f6fff5 100%); + border-radius: 8px; +} +/* Wahl summary layout */ +details.kc-wahl-details > summary { display: flex; align-items: center; justify-content: space-between; } +details.kc-wahl-details > summary .kc-wahl-title { font-weight:700; color:#2b5f9a; } +details.kc-wahl-details > summary .kc-wahl-actions { margin-left: 12px; } +details.kc-wahl-details .kc-wahl-actions .kc-btn { margin-left:8px; } + +/* Workshop card inside phase */ +.kc-workshop-card { + background: #f9f9f9; + padding: 10px; + margin: 8px 0; + border-radius: 8px; + box-shadow: 0 1px 6px #00000012; +} +.kc-workshop-card table { margin-top:8px; } +.kc-workshop-card b { font-size: 1.05em; } +.kc-workshop-card .kc-count { color:#555; font-size:0.92em; } + +.kc-unassigned { + background: #fff6f6; + border-left: 4px solid #f2b0b0; + padding: 10px; + margin-top: 8px; + border-radius: 6px; +} + +/* Smaller tweaks for details summaries inside admin area */ +details summary { outline: none; } +details summary::-webkit-details-marker { display: none; } +details summary:before { content: '\25B6'; display:inline-block; transform:rotate(90deg); margin-right:8px; } +details[open] summary:before { transform: rotate(0deg); } +.kc-admin-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + margin-bottom: 28px; + background: #fafdff; + border-radius: 13px; + overflow: auto; /* horizontal scroll on small screens */ + box-shadow: 0 2px 12px #b6d33313; +} +.kc-admin-table th, .kc-admin-table td { + padding: 13px 18px; + text-align: left; + font-size: 1.07em; + vertical-align: middle; +} +.kc-admin-table thead { + background: #eaf6ff; + color: #4176be; + font-weight: 800; +} +.kc-admin-table tbody tr { + border-bottom: 1px solid #eef3fa; +} +.kc-admin-table tbody tr:nth-child(even) { + background: #f8fbe7; +} +.kc-admin-table tbody tr:hover { + background: #e0ebf6; + transition: background 0.2s; +} +.kc-admin-table th { + font-weight: bold; + border-bottom: 2px solid #b6d33342; +} +.kc-admin-table .kc-actions { + white-space: nowrap; + display: flex; + gap: 8px; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; /* allow buttons to wrap on very small widths */ +} +.kc-btn { + background: linear-gradient(90deg,#4176be 40%, #b6d333 100%); + color: #fff; + padding: 10px 25px; + border-radius: 8px; + border: 0; + margin: 0 5px; + font-weight: 700; + cursor: pointer; + display: inline-block; + text-decoration: none; + font-size: 1.04em; + box-shadow: 0 1.5px 6px #b6d33321; + transition: background .17s, box-shadow .19s; +} +.kc-btn.del { + background: #e12b2b !important; + color: #fff; +} +.kc-btn.edit { + background: #ff9800 !important; + color: #fff; +} +.kc-btn:hover { + opacity: 0.91; + background: linear-gradient(90deg,#26529e 40%, #97b321 100%); + color: #fff; + box-shadow: 0 3px 10px #4176be29; +} +.notice-success { + background: #f6ffed; + border-left: 6px solid #b6d333; + color: #1c5322; + padding: 13px 20px; + margin: 0 0 22px 0; + border-radius: 9px; + font-size: 1.08em; + font-weight: 500; +} +.notice-error { + background: #fff3f3; + border-left: 6px solid #e12b2b; + color: #9d1d2e; + padding: 13px 20px; + margin: 0 0 22px 0; + border-radius: 9px; + font-size: 1.08em; + font-weight: 500; +} +.kc-required { + color: #e42626; + font-weight: bold; + margin-left: 2px; +} +.kc-wahl-filter-btn.active{background:#4CAF50;color:#fff;} +.kc-phase-filter-btn.active{background:#1976d2;color:#fff;} +@media (max-width: 800px) { + .kc-admin-table-wrap {padding: 12px;} + .kc-admin-table th, .kc-admin-table td {padding: 8px 6px;} + .kc-btn {padding: 8px 13px;} +} + +@media (max-width: 600px) { + /* Make tables readable on mobile by switching to block rows */ + .kc-admin-table thead { display: none; } + .kc-admin-table, .kc-admin-table tbody, .kc-admin-table tr, .kc-admin-table td { display: block; width: 100%; } + .kc-admin-table tr { margin-bottom: 12px; border-bottom: 1px solid #eef3fa; } + .kc-admin-table td { text-align: left; padding: 10px 12px; white-space: normal; } + .kc-admin-table td:before { content: attr(data-label); font-weight:700; display:block; margin-bottom:6px; color:#4176be; } +} diff --git a/includes/admin-teilnehmer.php b/includes/admin-teilnehmer.php index b4caf65..5288641 100644 --- a/includes/admin-teilnehmer.php +++ b/includes/admin-teilnehmer.php @@ -309,7 +309,7 @@ function kc_teilnehmer_page() { echo ''; } // JS für Wahl- und Phasen-Filter - echo ''; + // CSS moved to admin-teilnehmer.css echo '