Initial commit

This commit is contained in:
s4luorth
2026-02-07 13:06:39 +01:00
commit 9e1957266c
7 changed files with 2043 additions and 0 deletions

184
assets/css/admin.css Normal file
View File

@@ -0,0 +1,184 @@
/**
* Door Status Voting - Admin Styles
*/
.dsv-admin-wrap {
max-width: 1200px;
}
.dsv-admin-wrap h1 {
font-size: 28px;
margin-bottom: 25px;
}
/* Grid Layout */
.dsv-admin-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.dsv-admin-card {
background: white;
border: 1px solid #ccd0d4;
border-radius: 8px;
padding: 20px 25px;
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.dsv-admin-card h2 {
margin: 0 0 15px 0;
padding: 0 0 10px 0;
border-bottom: 1px solid #eee;
font-size: 16px;
}
.dsv-admin-card-full {
grid-column: 1 / -1;
}
/* Shortcode Display */
.dsv-shortcode {
display: block;
background: #f0f0f1;
padding: 12px 15px;
border-radius: 4px;
font-size: 14px;
margin: 10px 0;
user-select: all;
cursor: pointer;
}
.dsv-shortcode:hover {
background: #e5e5e5;
}
/* Form Elements */
#dsv-settings-form textarea {
width: 100%;
font-family: monospace;
}
#dsv-settings-form .button {
margin-top: 10px;
}
.dsv-save-status {
margin-left: 10px;
color: #00a32a;
font-weight: 500;
}
/* Table Styles */
.dsv-admin-card table {
margin-top: 10px;
}
.dsv-admin-card table th,
.dsv-admin-card table td {
padding: 12px 10px;
}
/* Badges */
.dsv-badge {
display: inline-block;
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
color: white;
}
.dsv-badge-open {
background: #4CAF50;
}
.dsv-badge-closed {
background: #f44336;
}
.dsv-badge-tied {
background: #FF9800;
}
.dsv-meta-empty {
color: #999;
}
/* Meta-Status Buttons */
.dsv-meta-buttons {
margin-top: 5px;
display: flex;
gap: 3px;
}
.dsv-meta-set-btn {
min-width: 28px;
padding: 2px 5px !important;
font-size: 12px !important;
line-height: 1.4 !important;
height: auto !important;
}
.dsv-meta-set-btn.active {
box-shadow: inset 0 0 0 2px #2271b1;
background: #f0f6fc;
}
/* Abweichung hervorheben */
.dsv-mismatch {
background: #fff3cd !important;
}
.dsv-mismatch td {
border-left-color: #FF9800;
}
.dsv-mismatch td:first-child {
border-left: 3px solid #FF9800;
}
/* Disabled Label */
.dsv-disabled-label {
color: #d63638;
font-size: 12px;
}
/* Action Buttons */
.dsv-actions-cell {
min-width: 280px;
}
.dsv-action-buttons {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.dsv-action-buttons .button {
font-size: 11px;
padding: 2px 8px;
height: auto;
line-height: 1.8;
}
.dsv-reset-type-btn {
min-width: 85px;
}
/* No Data */
.dsv-no-data {
color: #666;
font-style: italic;
padding: 20px;
text-align: center;
background: #f9f9f9;
border-radius: 4px;
}
/* Responsive */
@media (max-width: 782px) {
.dsv-admin-grid {
grid-template-columns: 1fr;
}
}

550
assets/css/frontend.css Normal file
View File

@@ -0,0 +1,550 @@
/* ============================================
DOOR STATUS VOTING - FRONTEND STYLES
Angepasst an Website-Design mit Elementor-Variablen
============================================ */
:root {
--dsv-primary: #2F4858;
--dsv-text: #141F27;
--dsv-accent: #C5422A;
--dsv-bg: #F6F9FC;
--dsv-white: #FFFFFF;
--dsv-hover: #B33822;
--dsv-border: #E0E5E9;
--dsv-success: #4CAF50;
--dsv-error: #C5422A;
}
/* ============================================
HAUPT-WIDGET
============================================ */
.dsv-widget {
background: var(--dsv-bg);
border-radius: 12px;
padding: 24px 28px;
width: 100%;
border: 1px solid var(--dsv-border);
box-shadow: 0 4px 12px rgba(47, 72, 88, 0.06);
font-family: var(--e-global-typography-text-font-family), Sans-serif;
font-size: var(--e-global-typography-text-font-size);
font-weight: var(--e-global-typography-text-font-weight);
color: var(--e-global-color-text, var(--dsv-text));
box-sizing: border-box;
}
/* Titel */
.dsv-title {
text-align: center;
font-size: 18px;
font-weight: 600;
color: var(--dsv-primary);
margin: 0 0 18px 0;
}
/* Hauptzeile: Tür links, Voting rechts */
.dsv-main-row {
display: flex;
align-items: center;
gap: 30px;
}
/* ============================================
TÜR CONTAINER
============================================ */
.dsv-door-container {
position: relative;
width: 110px;
height: 150px;
flex-shrink: 0;
perspective: 500px;
}
/* Status Badge */
.dsv-status-badge {
position: absolute;
top: -10px;
left: 50%;
transform: translateX(-50%);
padding: 4px 12px;
border-radius: 12px;
font-size: 11px;
font-weight: 700;
color: white;
z-index: 10;
white-space: nowrap;
transition: all 0.5s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.dsv-status-open {
background: linear-gradient(135deg, var(--dsv-success), #43A047);
box-shadow: 0 3px 10px rgba(76, 175, 80, 0.35);
}
.dsv-status-closed {
background: linear-gradient(135deg, var(--dsv-accent), var(--dsv-hover));
box-shadow: 0 3px 10px rgba(197, 66, 42, 0.35);
}
.dsv-status-tied {
background: linear-gradient(135deg, #FF9800, #F57C00);
box-shadow: 0 3px 10px rgba(255, 152, 0, 0.35);
}
/* Türrahmen */
.dsv-door-frame {
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
width: 85px;
height: 120px;
background: linear-gradient(135deg, #5D4037 0%, #4E342E 100%);
border-radius: 4px 4px 0 0;
border: 4px solid #3E2723;
border-bottom: 6px solid #3E2723;
box-shadow: inset 0 0 12px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
/* Licht hinter der Tür */
.dsv-light {
position: absolute;
inset: 3px;
border-radius: 2px;
background: linear-gradient(180deg, #FFF9C4 0%, #FFEE58 50%, #FFC107 100%);
opacity: 0;
transition: opacity 0.7s ease;
}
.dsv-widget[data-status="open"] .dsv-light {
opacity: 1;
box-shadow: 0 0 35px 12px rgba(255, 235, 59, 0.3);
}
.dsv-widget[data-status="tied"] .dsv-light {
opacity: 0.5;
}
/* Lichtstrahlen */
.dsv-light-rays {
position: absolute;
inset: 3px;
overflow: hidden;
border-radius: 2px;
opacity: 0;
transition: opacity 0.7s ease;
}
.dsv-widget[data-status="open"] .dsv-light-rays,
.dsv-widget[data-status="tied"] .dsv-light-rays {
opacity: 1;
}
.dsv-ray {
position: absolute;
width: 2px;
height: 100%;
background: rgba(255, 255, 200, 0.4);
filter: blur(1px);
transform: rotate(5deg);
}
.dsv-ray:nth-child(1) { left: 15%; }
.dsv-ray:nth-child(2) { left: 30%; }
.dsv-ray:nth-child(3) { left: 45%; }
.dsv-ray:nth-child(4) { left: 60%; }
.dsv-ray:nth-child(5) { left: 75%; }
/* ============================================
DIE TÜR
============================================ */
.dsv-door {
position: absolute;
top: 3px;
left: 3px;
width: 71px;
height: 106px;
border-radius: 2px 2px 0 0;
transform-origin: left center;
transform-style: preserve-3d;
transition: transform 0.7s cubic-bezier(0.4, 0, 0.2, 1);
background:
linear-gradient(90deg,
rgba(0,0,0,0.1) 0%,
transparent 3%,
transparent 97%,
rgba(0,0,0,0.1) 100%
),
linear-gradient(180deg,
#8D6E63 0%,
#795548 15%,
#8D6E63 30%,
#6D4C41 50%,
#795548 70%,
#8D6E63 85%,
#795548 100%
);
box-shadow: 2px 0 6px rgba(0, 0, 0, 0.3);
}
.dsv-widget[data-status="open"] .dsv-door {
transform: rotateY(-70deg);
box-shadow: 4px 0 12px rgba(0, 0, 0, 0.4);
}
.dsv-widget[data-status="tied"] .dsv-door {
transform: rotateY(-35deg);
box-shadow: 3px 0 8px rgba(0, 0, 0, 0.35);
}
.dsv-widget[data-status="closed"] .dsv-door {
transform: rotateY(0deg);
}
/* Holzmaserung */
.dsv-door-grain {
position: absolute;
inset: 0;
border-radius: 2px 2px 0 0;
opacity: 0.15;
background-image:
repeating-linear-gradient(
92deg,
transparent,
transparent 5px,
rgba(0,0,0,0.1) 5px,
rgba(0,0,0,0.1) 6px
);
}
/* Türpaneele */
.dsv-door-panel {
position: absolute;
left: 6px;
right: 6px;
border: 2px solid rgba(62, 39, 35, 0.4);
border-radius: 2px;
background: rgba(93, 64, 55, 0.2);
}
.dsv-door-panel-top {
top: 10px;
height: 32px;
}
.dsv-door-panel-bottom {
bottom: 10px;
height: 42px;
}
/* Türklinke */
.dsv-door-handle {
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
}
.dsv-handle-plate {
width: 9px;
height: 26px;
border-radius: 4px;
background: linear-gradient(135deg, #FFD54F 0%, #FFA000 50%, #FF8F00 100%);
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
}
.dsv-handle-lever {
position: absolute;
top: 50%;
right: -1px;
transform: translateY(-50%);
width: 11px;
height: 5px;
border-radius: 0 2px 2px 0;
background: linear-gradient(180deg, #FFD54F 0%, #FFA000 100%);
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}
/* Schatten */
.dsv-shadow {
position: absolute;
bottom: -5px;
left: 50%;
transform: translateX(-50%);
width: 75px;
height: 10px;
background: radial-gradient(ellipse, rgba(0,0,0,0.2) 0%, transparent 70%);
transition: all 0.7s ease;
}
.dsv-widget[data-status="open"] .dsv-shadow {
width: 90px;
transform: translateX(-50%) scaleX(1.2);
}
/* ============================================
VOTING SECTION (rechte Seite)
============================================ */
.dsv-voting-section {
flex: 1;
min-width: 0;
}
/* Fortschrittsbalken */
.dsv-progress-container {
margin-bottom: 14px;
}
.dsv-progress-bar {
position: relative;
height: 28px;
background: var(--dsv-bg);
border-radius: 14px;
overflow: hidden;
border: 1px solid var(--dsv-border);
display: flex;
}
.dsv-progress-open {
height: 100%;
background: linear-gradient(90deg, var(--dsv-success) 0%, #66BB6A 100%);
display: flex;
align-items: center;
justify-content: flex-start;
padding-left: 12px;
transition: width 0.7s cubic-bezier(0.4, 0, 0.2, 1);
}
.dsv-progress-closed {
height: 100%;
background: linear-gradient(90deg, #EF5350 0%, var(--dsv-accent) 100%);
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 12px;
transition: width 0.7s cubic-bezier(0.4, 0, 0.2, 1);
}
.dsv-progress-count {
color: white;
font-size: 12px;
font-weight: 700;
text-shadow: 0 1px 2px rgba(0,0,0,0.2);
}
.dsv-progress-divider {
position: absolute;
left: 50%;
top: 0;
width: 2px;
height: 100%;
background: rgba(255,255,255,0.5);
transform: translateX(-50%);
}
.dsv-progress-labels {
display: flex;
justify-content: space-between;
margin-top: 6px;
font-size: 12px;
font-weight: var(--e-global-typography-text-font-weight);
}
.dsv-label-open {
color: #388E3C;
}
.dsv-label-closed {
color: var(--dsv-accent);
}
/* ============================================
VOTING BUTTONS
============================================ */
.dsv-buttons {
display: flex;
gap: 12px;
}
.dsv-buttons.dsv-voted {
justify-content: flex-start;
}
.dsv-btn {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 18px;
border: none;
border-radius: 8px;
font-family: var(--e-global-typography-text-font-family), Sans-serif;
font-size: 14px;
font-weight: 600;
color: white;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 3px 10px rgba(0,0,0,0.15);
}
.dsv-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.dsv-btn:active {
transform: translateY(0);
}
.dsv-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.dsv-btn-open {
background: linear-gradient(135deg, var(--dsv-success) 0%, #388E3C 100%);
}
.dsv-btn-open:hover {
box-shadow: 0 5px 15px rgba(76, 175, 80, 0.4);
}
.dsv-btn-closed {
background: linear-gradient(135deg, var(--dsv-accent) 0%, var(--dsv-hover) 100%);
}
.dsv-btn-closed:hover {
box-shadow: 0 5px 15px rgba(197, 66, 42, 0.4);
}
.dsv-btn-icon {
font-size: 16px;
}
.dsv-btn-text {
font-size: 14px;
}
/* Voted Message mit Remove Button */
.dsv-voted-message {
display: inline-flex;
align-items: center;
gap: 10px;
padding: 10px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
border: 1px solid;
}
.dsv-voted-open {
background: #E8F5E9;
color: #2E7D32;
border-color: #A5D6A7;
}
.dsv-voted-closed {
background: #FFEBEE;
color: var(--dsv-hover);
border-color: #FFCDD2;
}
.dsv-voted-icon {
font-size: 16px;
}
/* Remove Vote Button */
.dsv-remove-vote {
background: transparent;
border: none;
color: inherit;
opacity: 0.5;
cursor: pointer;
padding: 4px 8px;
margin-left: 6px;
border-radius: 4px;
font-size: 14px;
transition: all 0.2s;
}
.dsv-remove-vote:hover {
opacity: 1;
background: rgba(0,0,0,0.08);
}
/* ============================================
INFO TEXT
============================================ */
.dsv-info {
color: #666;
font-size: 12px;
margin: 12px 0 0 0;
font-weight: var(--e-global-typography-text-font-weight);
}
.dsv-total {
font-weight: 600;
color: var(--dsv-primary);
}
/* ============================================
ANIMATIONEN
============================================ */
.dsv-widget.dsv-animating .dsv-door {
transition: transform 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
@keyframes dsv-pulse {
0% { transform: translateX(-50%) scale(1); }
50% { transform: translateX(-50%) scale(1.08); }
100% { transform: translateX(-50%) scale(1); }
}
.dsv-widget.dsv-just-voted .dsv-status-badge {
animation: dsv-pulse 0.5s ease;
}
/* ============================================
RESPONSIVE
============================================ */
@media (max-width: 550px) {
.dsv-widget {
padding: 20px;
}
.dsv-main-row {
flex-direction: column;
gap: 20px;
}
.dsv-door-container {
transform: scale(0.95);
}
.dsv-voting-section {
width: 100%;
}
.dsv-btn {
padding: 11px 14px;
font-size: 13px;
}
.dsv-title {
font-size: 16px;
}
}

259
assets/js/admin.js Normal file
View File

@@ -0,0 +1,259 @@
/**
* Door Status Voting - Admin JavaScript
*/
(function($) {
'use strict';
$(document).ready(function() {
initSettingsForm();
initResetButtons();
initResetByTypeButtons();
initMismatchFilter();
initMetaStatusButtons();
});
/**
* Settings Form Handler
*/
function initSettingsForm() {
$('#dsv-settings-form').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var $btn = $form.find('.button-primary');
var $status = $form.find('.dsv-save-status');
$btn.prop('disabled', true).text('Speichern...');
$status.text('');
$.ajax({
type: 'POST',
url: dsvAdmin.ajaxUrl,
data: {
action: 'dsv_save_settings',
nonce: dsvAdmin.nonce,
disabled_posts: $('#dsv-disabled-posts').val()
},
success: function(response) {
if (response.success) {
$status.text('✓ Gespeichert').css('color', '#00a32a');
} else {
$status.text('✗ Fehler: ' + (response.data?.message || 'Unbekannt')).css('color', '#d63638');
}
},
error: function() {
$status.text('✗ Verbindungsfehler').css('color', '#d63638');
},
complete: function() {
$btn.prop('disabled', false).text('Einstellungen speichern');
setTimeout(function() {
$status.fadeOut(300, function() {
$(this).text('').show();
});
}, 3000);
}
});
});
}
/**
* Reset All Buttons Handler
*/
function initResetButtons() {
$(document).on('click', '.dsv-reset-btn', function() {
var $btn = $(this);
var $row = $btn.closest('tr');
var postId = $btn.data('post-id');
if (!confirm('Alle Votes für diesen Beitrag wirklich zurücksetzen?')) {
return;
}
$btn.prop('disabled', true).text('Löschen...');
$.ajax({
type: 'POST',
url: dsvAdmin.ajaxUrl,
data: {
action: 'dsv_reset_votes',
nonce: dsvAdmin.nonce,
post_id: postId
},
success: function(response) {
if (response.success) {
$row.fadeOut(300, function() {
$(this).remove();
checkEmptyTable();
});
} else {
alert('Fehler: ' + (response.data?.message || 'Unbekannt'));
$btn.prop('disabled', false).text('Alle löschen');
}
},
error: function() {
alert('Verbindungsfehler');
$btn.prop('disabled', false).text('Alle löschen');
}
});
});
}
/**
* Reset By Type Buttons Handler
*/
function initResetByTypeButtons() {
$(document).on('click', '.dsv-reset-type-btn', function() {
var $btn = $(this);
var $row = $btn.closest('tr');
var postId = $btn.data('post-id');
var type = $btn.data('type');
var typeLabel = type === 'open' ? 'Geöffnet' : 'Geschlossen';
if (!confirm('Alle "' + typeLabel + '" Stimmen für diesen Beitrag löschen?')) {
return;
}
var originalText = $btn.text();
$btn.prop('disabled', true).text('...');
$.ajax({
type: 'POST',
url: dsvAdmin.ajaxUrl,
data: {
action: 'dsv_reset_votes_by_type',
nonce: dsvAdmin.nonce,
post_id: postId,
type: type
},
success: function(response) {
if (response.success) {
// Zähler aktualisieren
$row.find('.dsv-count-open').text(response.data.votes.open);
$row.find('.dsv-count-closed').text(response.data.votes.closed);
$row.find('.dsv-count-total').text(response.data.votes.open + response.data.votes.closed);
// Status Badge aktualisieren
var $badge = $row.find('.dsv-badge');
$badge.removeClass('dsv-badge-open dsv-badge-closed dsv-badge-tied');
if (response.data.votes.open > response.data.votes.closed) {
$badge.addClass('dsv-badge-open').text('Geöffnet');
} else if (response.data.votes.closed > response.data.votes.open) {
$badge.addClass('dsv-badge-closed').text('Geschlossen');
} else {
$badge.addClass('dsv-badge-tied').text('Unklar');
}
// Wenn keine Votes mehr, Zeile entfernen
if (response.data.votes.open === 0 && response.data.votes.closed === 0) {
$row.fadeOut(300, function() {
$(this).remove();
checkEmptyTable();
});
} else {
// Kurzes Feedback
$btn.text('✓').css('color', '#00a32a');
setTimeout(function() {
$btn.prop('disabled', false).text(originalText).css('color', '');
}, 1000);
}
} else {
alert('Fehler: ' + (response.data?.message || 'Unbekannt'));
$btn.prop('disabled', false).text(originalText);
}
},
error: function() {
alert('Verbindungsfehler');
$btn.prop('disabled', false).text(originalText);
}
});
});
}
/**
* Filter: Nur Abweichungen anzeigen
*/
function initMismatchFilter() {
var $btn = $('#dsv-filter-mismatch');
var active = false;
$btn.on('click', function() {
active = !active;
var $table = $('#dsv-votes-table');
if (active) {
$table.find('tbody tr').not('.dsv-mismatch').hide();
$btn.addClass('button-primary').text('Alle anzeigen');
} else {
$table.find('tbody tr').show();
$btn.removeClass('button-primary').text('Nur Abweichungen anzeigen');
}
});
}
/**
* Meta-Status per Knopfdruck ändern
*/
function initMetaStatusButtons() {
$(document).on('click', '.dsv-meta-set-btn', function() {
var $btn = $(this);
var $cell = $btn.closest('.dsv-meta-status-cell');
var postId = $btn.data('post-id');
var status = $btn.data('status');
$cell.find('.dsv-meta-set-btn').prop('disabled', true);
$.ajax({
type: 'POST',
url: dsvAdmin.ajaxUrl,
data: {
action: 'dsv_update_meta_status',
nonce: dsvAdmin.nonce,
post_id: postId,
status: status
},
success: function(response) {
if (response.success) {
// Badge aktualisieren
var badgeClass = 'dsv-badge-tied';
var lower = status.toLowerCase();
if (lower === 'geöffnet') badgeClass = 'dsv-badge-open';
else if (lower === 'geschlossen') badgeClass = 'dsv-badge-closed';
var $badge = $cell.find('.dsv-badge, .dsv-meta-empty');
if ($badge.hasClass('dsv-meta-empty')) {
$badge.removeClass('dsv-meta-empty').addClass('dsv-badge');
}
$badge.removeClass('dsv-badge-open dsv-badge-closed dsv-badge-tied')
.addClass(badgeClass)
.text(status);
// Active-State der Buttons
$cell.find('.dsv-meta-set-btn').removeClass('active');
$btn.addClass('active');
} else {
alert('Fehler: ' + (response.data?.message || 'Unbekannt'));
}
},
error: function() {
alert('Verbindungsfehler');
},
complete: function() {
$cell.find('.dsv-meta-set-btn').prop('disabled', false);
}
});
});
}
/**
* Prüfen ob Tabelle leer ist
*/
function checkEmptyTable() {
if ($('tbody tr').length === 0) {
location.reload();
}
}
})(jQuery);

257
assets/js/frontend.js Normal file
View File

@@ -0,0 +1,257 @@
/**
* Door Status Voting - Frontend JavaScript
*/
(function() {
'use strict';
document.addEventListener('DOMContentLoaded', function() {
initDoorVoting();
});
function initDoorVoting() {
const widgets = document.querySelectorAll('.dsv-widget');
widgets.forEach(function(widget) {
// Vote Buttons
const buttons = widget.querySelectorAll('.dsv-btn');
buttons.forEach(function(btn) {
btn.addEventListener('click', function() {
handleVote(widget, btn.dataset.vote);
});
});
// Remove Vote Button
const removeBtn = widget.querySelector('.dsv-remove-vote');
if (removeBtn) {
removeBtn.addEventListener('click', function() {
handleRemoveVote(widget);
});
}
});
}
function handleVote(widget, vote) {
const postId = widget.dataset.postId;
const hasVoted = widget.dataset.hasVoted === 'true';
if (hasVoted) return;
// Buttons deaktivieren
const buttons = widget.querySelectorAll('.dsv-btn');
buttons.forEach(function(btn) {
btn.disabled = true;
});
// Animation starten
widget.classList.add('dsv-animating');
// AJAX Request
const formData = new FormData();
formData.append('action', 'dsv_vote');
formData.append('post_id', postId);
formData.append('vote', vote);
formData.append('nonce', dsvConfig.nonce);
fetch(dsvConfig.ajaxUrl, {
method: 'POST',
body: formData,
credentials: 'same-origin'
})
.then(function(response) {
return response.json();
})
.then(function(data) {
if (data.success) {
updateWidget(widget, data.data, vote);
} else {
console.error('Vote error:', data.data?.message);
buttons.forEach(function(btn) {
btn.disabled = false;
});
}
})
.catch(function(error) {
console.error('Network error:', error);
buttons.forEach(function(btn) {
btn.disabled = false;
});
})
.finally(function() {
widget.classList.remove('dsv-animating');
});
}
function handleRemoveVote(widget) {
const postId = widget.dataset.postId;
// Button deaktivieren
const removeBtn = widget.querySelector('.dsv-remove-vote');
if (removeBtn) {
removeBtn.disabled = true;
removeBtn.textContent = '...';
}
widget.classList.add('dsv-animating');
const formData = new FormData();
formData.append('action', 'dsv_remove_vote');
formData.append('post_id', postId);
formData.append('nonce', dsvConfig.nonce);
fetch(dsvConfig.ajaxUrl, {
method: 'POST',
body: formData,
credentials: 'same-origin'
})
.then(function(response) {
return response.json();
})
.then(function(data) {
if (data.success) {
resetWidgetToVoting(widget, data.data);
} else {
console.error('Remove vote error:', data.data?.message);
if (removeBtn) {
removeBtn.disabled = false;
removeBtn.textContent = '✕';
}
}
})
.catch(function(error) {
console.error('Network error:', error);
if (removeBtn) {
removeBtn.disabled = false;
removeBtn.textContent = '✕';
}
})
.finally(function() {
widget.classList.remove('dsv-animating');
});
}
function updateWidget(widget, data, userVote) {
// Status aktualisieren
widget.dataset.status = data.status;
widget.dataset.hasVoted = 'true';
widget.dataset.userVote = userVote;
// Badge aktualisieren
const badge = widget.querySelector('.dsv-status-badge');
badge.className = 'dsv-status-badge dsv-status-' + data.status;
if (data.status === 'open') {
badge.innerHTML = '🟢 GEÖFFNET';
} else if (data.status === 'closed') {
badge.innerHTML = '🔴 GESCHLOSSEN';
} else {
badge.innerHTML = '🟡 UNKLAR';
}
// Fortschrittsbalken aktualisieren
updateProgressBar(widget, data);
// Buttons durch Bestätigung ersetzen
const buttonsContainer = widget.querySelector('.dsv-buttons');
buttonsContainer.classList.add('dsv-voted');
const voteClass = userVote === 'open' ? 'dsv-voted-open' : 'dsv-voted-closed';
const voteIcon = userVote === 'open' ? '✓' : '✗';
const voteText = userVote === 'open' ? 'Geöffnet' : 'Geschlossen';
buttonsContainer.innerHTML =
'<div class="dsv-voted-message ' + voteClass + '">' +
'<span class="dsv-voted-icon">' + voteIcon + '</span>' +
'<span class="dsv-voted-text">Du hast für "' + voteText + '" gestimmt</span>' +
'<button type="button" class="dsv-remove-vote" title="Stimme zurücknehmen">✕</button>' +
'</div>';
// Event Listener für neuen Remove Button
const newRemoveBtn = buttonsContainer.querySelector('.dsv-remove-vote');
newRemoveBtn.addEventListener('click', function() {
handleRemoveVote(widget);
});
// Total aktualisieren
widget.querySelector('.dsv-total').textContent = data.total;
// Pulse Animation
widget.classList.add('dsv-just-voted');
setTimeout(function() {
widget.classList.remove('dsv-just-voted');
}, 500);
}
function resetWidgetToVoting(widget, data) {
// Status aktualisieren
widget.dataset.status = data.status;
widget.dataset.hasVoted = 'false';
widget.dataset.userVote = '';
// Badge aktualisieren
const badge = widget.querySelector('.dsv-status-badge');
badge.className = 'dsv-status-badge dsv-status-' + data.status;
if (data.status === 'open') {
badge.innerHTML = '🟢 GEÖFFNET';
} else if (data.status === 'closed') {
badge.innerHTML = '🔴 GESCHLOSSEN';
} else {
badge.innerHTML = '🟡 UNKLAR';
}
// Fortschrittsbalken aktualisieren
updateProgressBar(widget, data);
// Voting Buttons wieder anzeigen
const buttonsContainer = widget.querySelector('.dsv-buttons');
buttonsContainer.classList.remove('dsv-voted');
buttonsContainer.innerHTML =
'<button type="button" class="dsv-btn dsv-btn-open" data-vote="open">' +
'<span class="dsv-btn-icon">✓</span>' +
'<span class="dsv-btn-text">Geöffnet</span>' +
'</button>' +
'<button type="button" class="dsv-btn dsv-btn-closed" data-vote="closed">' +
'<span class="dsv-btn-icon">✗</span>' +
'<span class="dsv-btn-text">Geschlossen</span>' +
'</button>';
// Event Listener für neue Buttons
const newButtons = buttonsContainer.querySelectorAll('.dsv-btn');
newButtons.forEach(function(btn) {
btn.addEventListener('click', function() {
handleVote(widget, btn.dataset.vote);
});
});
// Total aktualisieren
widget.querySelector('.dsv-total').textContent = data.total;
}
function updateProgressBar(widget, data) {
const progressOpen = widget.querySelector('.dsv-progress-open');
const progressClosed = widget.querySelector('.dsv-progress-closed');
progressOpen.style.width = data.openPercent + '%';
progressClosed.style.width = (100 - data.openPercent) + '%';
// Zähler
if (data.openPercent > 15) {
progressOpen.innerHTML = '<span class="dsv-progress-count">' + data.votes.open + '</span>';
} else {
progressOpen.innerHTML = '';
}
if ((100 - data.openPercent) > 15) {
progressClosed.innerHTML = '<span class="dsv-progress-count">' + data.votes.closed + '</span>';
} else {
progressClosed.innerHTML = '';
}
// Labels
widget.querySelector('.dsv-label-open').textContent = 'Geöffnet (' + data.openPercent + '%)';
widget.querySelector('.dsv-label-closed').textContent = 'Geschlossen (' + (100 - data.openPercent) + '%)';
}
})();