PHP Classes

File: templates/admin/polls.twig

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   templates/admin/polls.twig   Download  
File: templates/admin/polls.twig
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 14,001 bytes
 

Contents

Class file image Download
{% extends "base.twig" %} {% block title %}{{ t('ui.admin.polls.page_title', {}, locale, ['common']) }}{% endblock %} {% block content %} <div class="container-fluid"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> <h1 class="h2">{{ t('ui.admin.polls.heading', {}, locale, ['common']) }}</h1> <button type="button" class="btn btn-primary" onclick="showCreatePollModal()"> <i class="fas fa-plus me-2"></i>{{ t('ui.admin.polls.add_poll', {}, locale, ['common']) }} </button> </div> <div class="card shadow"> <div class="card-body"> <div class="table-responsive"> <table class="table table-hover" id="pollsTable"> <thead class="table-dark"> <tr> <th>{{ t('ui.common.id', {}, locale, ['common']) }}</th> <th>{{ t('ui.admin.polls.question', {}, locale, ['common']) }}</th> <th>{{ t('ui.admin.polls.status', {}, locale, ['common']) }}</th> <th>{{ t('ui.admin.polls.votes', {}, locale, ['common']) }}</th> <th>{{ t('ui.admin.polls.created_by', {}, locale, ['common']) }}</th> <th>{{ t('ui.admin.polls.created', {}, locale, ['common']) }}</th> <th>{{ t('ui.admin.polls.actions', {}, locale, ['common']) }}</th> </tr> </thead> <tbody id="pollsTableBody"> <!-- Populated by JavaScript --> </tbody> </table> </div> </div> </div> </div> <div class="modal fade" id="pollModal" tabindex="-1" aria-labelledby="pollModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="pollModalLabel">{{ t('ui.admin.polls.add_poll', {}, locale, ['common']) }}</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ t('ui.common.close', {}, locale, ['common']) }}"></button> </div> <div class="modal-body"> <form id="pollForm"> <input type="hidden" id="pollId" name="pollId"> <div class="mb-3" id="createdByContainer" style="display: none;"> <label class="form-label">{{ t('ui.admin.polls.created_by', {}, locale, ['common']) }}</label> <input type="text" class="form-control" id="pollCreatedBy" readonly> </div> <div class="mb-3"> <label for="pollQuestion" class="form-label">{{ t('ui.admin.polls.question', {}, locale, ['common']) }} <span class="text-danger">*</span></label> <input type="text" class="form-control" id="pollQuestion" name="question" required> </div> <div class="mb-3"> <label class="form-label">{{ t('ui.admin.polls.options', {}, locale, ['common']) }} <span class="text-danger">*</span></label> <div id="pollOptions"> <!-- Option inputs --> </div> <button type="button" class="btn btn-sm btn-outline-secondary mt-2" onclick="addPollOption()"> <i class="fas fa-plus me-1"></i>{{ t('ui.admin.polls.add_option', {}, locale, ['common']) }} </button> </div> <div class="form-check"> <input class="form-check-input" type="checkbox" id="pollActive" name="is_active" checked> <label class="form-check-label" for="pollActive"> {{ t('ui.admin.polls.active_poll', {}, locale, ['common']) }} </label> </div> <div class="form-text text-muted mt-2"> {{ t('ui.admin.polls.editing_options_resets_votes', {}, locale, ['common']) }} </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ t('ui.common.cancel', {}, locale, ['common']) }}</button> <button type="button" class="btn btn-primary" onclick="savePoll()">{{ t('ui.admin.polls.save_poll', {}, locale, ['common']) }}</button> </div> </div> </div> </div> <script> function apiError(payload, fallback) { if (window.getApiErrorMessage) { return window.getApiErrorMessage(payload, fallback); } if (payload && payload.error) { return String(payload.error); } return fallback; } function uiT(key, fallback, params = {}) { if (window.t) { return window.t(key, params, fallback); } return fallback; } document.addEventListener('DOMContentLoaded', function() { loadI18nNamespaces(['common', 'errors']).then(function() { loadPolls(); }); }); function loadPolls() { fetch('/admin/api/polls') .then(response => response.json()) .then(data => { displayPolls(data.polls || []); }) .catch(error => { console.error('Error loading polls:', error); showAlert(uiT('ui.admin.polls.load_failed', 'Error loading polls'), 'danger'); }); } function displayPolls(polls) { const tbody = document.getElementById('pollsTableBody'); tbody.innerHTML = ''; polls.forEach(poll => { const row = document.createElement('tr'); row.innerHTML = ` <td>${poll.id}</td> <td>${escapeHtml(poll.question)}</td> <td> <span class="badge bg-${poll.is_active ? 'success' : 'secondary'}"> ${poll.is_active ? uiT('ui.admin.polls.active', 'Active') : uiT('ui.admin.polls.inactive', 'Inactive')} </span> </td> <td>${poll.vote_count || 0}</td> <td>${escapeHtml(poll.created_by_username || uiT('ui.common.unknown', 'Unknown'))}</td> <td>${new Date(poll.created_at).toLocaleDateString()}</td> <td> <div class="btn-group btn-group-sm" role="group"> <button type="button" class="btn btn-outline-primary" onclick="editPoll(${poll.id})" title="${uiT('ui.common.edit', 'Edit')}"> <i class="fas fa-edit"></i> </button> <button type="button" class="btn btn-outline-danger" onclick="deletePoll(${poll.id})" title="${uiT('ui.common.delete', 'Delete')}"> <i class="fas fa-trash"></i> </button> </div> </td> `; tbody.appendChild(row); }); } function showCreatePollModal() { document.getElementById('pollModalLabel').textContent = uiT('ui.admin.polls.add_poll', 'Add Poll'); document.getElementById('pollForm').reset(); document.getElementById('pollId').value = ''; document.getElementById('pollActive').checked = true; document.getElementById('createdByContainer').style.display = 'none'; resetPollOptions(['', '']); new bootstrap.Modal(document.getElementById('pollModal')).show(); } function editPoll(pollId) { fetch('/admin/api/polls') .then(response => response.json()) .then(data => { const poll = (data.polls || []).find(item => item.id === pollId); if (!poll) { showAlert(uiT('ui.admin.polls.not_found', 'Poll not found'), 'danger'); return; } document.getElementById('pollModalLabel').textContent = uiT('ui.admin.polls.edit_poll', 'Edit Poll'); document.getElementById('pollId').value = poll.id; document.getElementById('pollQuestion').value = poll.question; document.getElementById('pollActive').checked = !!poll.is_active; if (poll.created_by_username) { document.getElementById('pollCreatedBy').value = poll.created_by_username; document.getElementById('createdByContainer').style.display = 'block'; } else { document.getElementById('createdByContainer').style.display = 'none'; } const options = (poll.options || []).map(opt => opt.option_text); resetPollOptions(options.length ? options : ['', '']); new bootstrap.Modal(document.getElementById('pollModal')).show(); }) .catch(error => { console.error('Error loading poll:', error); showAlert(uiT('ui.admin.polls.load_single_failed', 'Error loading poll'), 'danger'); }); } function resetPollOptions(options) { const optionsContainer = document.getElementById('pollOptions'); optionsContainer.innerHTML = ''; options.forEach(optionText => addPollOption(optionText)); } function addPollOption(value = '') { const optionsContainer = document.getElementById('pollOptions'); const optionRow = document.createElement('div'); optionRow.className = 'input-group mb-2'; optionRow.innerHTML = ` <input type="text" class="form-control poll-option" value="${escapeHtml(value)}" placeholder="${escapeHtml(uiT('ui.admin.polls.option_text_placeholder', 'Option text'))}"> <button class="btn btn-outline-danger" type="button" onclick="removePollOption(this)"> <i class="fas fa-times"></i> </button> `; optionsContainer.appendChild(optionRow); } function removePollOption(button) { const optionsContainer = document.getElementById('pollOptions'); if (optionsContainer.children.length <= 2) { alert(uiT('ui.admin.polls.min_options_required', 'At least two options are required.')); return; } button.closest('.input-group').remove(); } function savePoll() { const pollId = document.getElementById('pollId').value; const question = document.getElementById('pollQuestion').value.trim(); const isActive = document.getElementById('pollActive').checked; const options = Array.from(document.querySelectorAll('.poll-option')) .map(input => input.value.trim()) .filter(text => text !== ''); if (!question) { alert(uiT('ui.admin.polls.question_required', 'Question is required.')); return; } if (options.length < 2) { alert(uiT('ui.admin.polls.provide_min_options', 'Please provide at least two options.')); return; } const payload = { question, options, is_active: isActive }; const url = pollId ? `/admin/api/polls/${pollId}` : '/admin/api/polls'; const method = pollId ? 'PUT' : 'POST'; fetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }) .then(response => response.json()) .then(data => { if (data.success || data.id) { const successMessage = window.getApiMessage ? window.getApiMessage( data, pollId ? uiT('ui.admin.polls.updated_success', 'Poll updated successfully') : uiT('ui.admin.polls.created_success', 'Poll created successfully') ) : (pollId ? uiT('ui.admin.polls.updated_success', 'Poll updated successfully') : uiT('ui.admin.polls.created_success', 'Poll created successfully')); showAlert(successMessage, 'success'); bootstrap.Modal.getInstance(document.getElementById('pollModal')).hide(); loadPolls(); } else { showAlert(apiError(data, uiT('ui.admin.polls.save_failed', 'Error saving poll')), 'danger'); } }) .catch(error => { console.error('Error saving poll:', error); showAlert(uiT('ui.admin.polls.save_failed', 'Error saving poll'), 'danger'); }); } function deletePoll(pollId) { if (!confirm(uiT('ui.admin.polls.delete_confirm', 'Delete this poll? This cannot be undone.'))) { return; } fetch(`/admin/api/polls/${pollId}`, { method: 'DELETE' }) .then(response => response.json()) .then(data => { if (data.success) { const successMessage = window.getApiMessage ? window.getApiMessage(data, uiT('ui.admin.polls.deleted_success', 'Poll deleted successfully')) : uiT('ui.admin.polls.deleted_success', 'Poll deleted successfully'); showAlert(successMessage, 'success'); loadPolls(); } else { showAlert(apiError(data, uiT('ui.admin.polls.delete_failed', 'Error deleting poll')), 'danger'); } }) .catch(error => { console.error('Error deleting poll:', error); showAlert(uiT('ui.admin.polls.delete_failed', 'Error deleting poll'), 'danger'); }); } function escapeHtml(text) { const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }; return String(text).replace(/[&<>"']/g, function(m) { return map[m]; }); } function showAlert(message, type) { const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show`; alertDiv.innerHTML = ` ${message} <button type="button" class="btn-close" data-bs-dismiss="alert"></button> `; const container = document.querySelector('.container-fluid'); container.insertBefore(alertDiv, container.firstChild); setTimeout(() => { alertDiv.remove(); }, 5000); } </script> {% endblock %}