This is an automated email from the ASF dual-hosted git repository. gstein pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/steve.git
commit 932ec2465f9fd0664d1187ad63c3b0ddc7a0ba70 Author: Greg Stein <[email protected]> AuthorDate: Mon Dec 22 19:30:49 2025 -0600 copy of manage.ezt; will revise to manage an STV vote --- v3/server/templates/manage-stv.ezt | 292 +++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) diff --git a/v3/server/templates/manage-stv.ezt b/v3/server/templates/manage-stv.ezt new file mode 100644 index 0000000..2a0821b --- /dev/null +++ b/v3/server/templates/manage-stv.ezt @@ -0,0 +1,292 @@ +[include "header.ezt"] + <div class="container"> + <h1>[title]</h1> + [include "flashes.ezt"] + + <div class="sticky-top"> + <h2>[e_title]</h2> + </div> + + <div class="state-diagram"> + <div id="state-editing" + class="state [is e_state "editable"]current[end]">Editing</div> + <div id="arrow1" class="arrow">→</div> + <div id="state-open" + class="state [is e_state "open"]current[end]">Open for Voting</div> + <div id="arrow2" class="arrow">→</div> + <div id="state-closed" + class="state [is e_state "closed"]current[end]">Closed</div> + + [is e_state "editable"] + <button id="open-btn" class="btn btn-success action-button open-button"> + <i class="bi bi-unlock me-1"></i>Open + </button> + [end] + [is e_state "open"] + <button id="close-btn" class="btn btn-danger action-button close-button"> + <i class="bi bi-lock me-1"></i>Close + </button> + [end] + </div> + + <!-- Open Confirmation Modal --> + <div class="modal fade" id="openConfirmModal" tabindex="-1" aria-labelledby="openConfirmModalLabel" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="openConfirmModalLabel">Confirm Open Election</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + Opening this election will allow voting and is irreversible. Are you sure? + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-success" id="confirm-open">Confirm</button> + </div> + </div> + </div> + </div>[# id=openConfirmModal ] + + <!-- Close Confirmation Modal --> + <div class="modal fade" id="closeConfirmModal" tabindex="-1" aria-labelledby="closeConfirmModalLabel" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="closeConfirmModalLabel">Confirm Close Election</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + Closing this election will end voting permanently. Are you sure? + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-danger" id="confirm-close">Confirm</button> + </div> + </div> + </div> + </div>[# id=closeConfirmModal ] + + <!-- Edit/Add Issue Modal --> + <div class="modal fade" id="issueModal" tabindex="-1" aria-labelledby="issueModalLabel" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="issueModalLabel">Edit Issue</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + <form id="issueForm" method="POST" action=""> + <input type="hidden" name="csrf_token" value="[csrf_token]"> + <input type="hidden" id="issueId" name="issueId" value=""> + <div class="mb-3"> + <label for="issueTitle" class="form-label">Title</label> + <input type="text" class="form-control" id="issueTitle" name="title" required + aria-describedby="issueTitleHelp"> + <div id="issueTitleHelp" class="form-text"> + A brief title for this issue or ballot item. + </div> + <div class="invalid-feedback">Title is required.</div> + </div> + <div class="mb-3"> + <label for="issueDescription" class="form-label">Description</label> + <textarea class="form-control" id="issueDescription" name="description" rows="4" + aria-describedby="issueDescriptionHelp"></textarea> + <div id="issueDescriptionHelp" class="form-text"> + Optional details about this issue for voters. + </div> + </div> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-primary" id="saveIssueBtn" onclick="saveIssue()">Save</button> + </div> + </div>[# modal-content ] + </div>[# modal-dialog ] + </div>[# id=issueModal ] + + <!-- Delete Issue Confirmation Modal --> + <div class="modal fade" id="deleteIssueModal" tabindex="-1" aria-labelledby="deleteIssueModalLabel" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="deleteIssueModalLabel">Confirm Delete</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + <p id="deleteIssueMessage">Are you sure you want to delete this issue?</p> + <form id="deleteIssueForm" method="POST" action=""> + <input type="hidden" name="csrf_token" value="[csrf_token]"> + <input type="hidden" id="deleteIssueId" name="issueId" value=""> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + <button type="submit" class="btn btn-danger" form="deleteIssueForm" id="deleteIssueBtn"> + <i class="bi bi-trash me-1"></i>Delete + </button> + </div> + </div>[# modal-content ] + </div>[# modal-dialog ] + </div>[# id=deleteIssueModal ] + + <div> + State: [e_state] + </div> + <div> + Estimate to open: <input type="date"> + </div> + <div class="mb-3"> + Estimate to close: <input type="date"> + </div> + + <div class="mb-4"> + <button type="button" class="btn btn-primary" onclick="openAddIssueModal()"> + <i class="bi bi-plus-circle me-1"></i>Add Issue + </button> + <button type="button" class="btn btn-outline-secondary ms-2" onclick="toggleAllDescriptions()"> + <i class="bi bi-arrows-expand me-1"></i> + <span id="toggle-all-text">Expand All</span> + </button> + </div> + + <div id="issues-list" class="list-group"> + [for issues] + <div class="list-group-item issue-item"> + <div class="d-flex justify-content-between align-items-center"> + <div> + <span class="twiddle bi bi-caret-right-fill me-2" onclick="toggleDescription('[issues.iid]')"></span> + <strong>[issues.title]</strong> + </div> + <div class="action-buttons"> + <button type="button" class="btn btn-outline-primary btn-sm me-1" + onclick="openEditIssueModal('[issues.iid]', + '[format "js,html"][issues.title][end]', + '[format "js,html"][issues.description][end]')" + aria-label="Edit Issue"> + <i class="bi bi-pencil"></i> + </button> + <button type="button" class="btn btn-outline-danger btn-sm" + onclick="openDeleteIssueModal('[issues.iid]', + '[format "js,html"][issues.title][end]')" + aria-label="Delete Issue"> + <i class="bi bi-trash"></i> + </button> + </div> + </div> + <div id="description-[issues.iid]" class="description mt-2">[issues.description]</div> + </div> + [end] + </div> + + </div> + +[include "footer.ezt"] + +<script> + const openModal = new bootstrap.Modal(document.getElementById('openConfirmModal')); + const closeModal = new bootstrap.Modal(document.getElementById('closeConfirmModal')); + + [is e_state "editable"] + const openBtn = document.getElementById('open-btn'); + openBtn.addEventListener('click', () => { + openModal.show(); + }); + [end] + const confirmOpen = document.getElementById('confirm-open'); + confirmOpen.addEventListener('click', () => { + openModal.hide(); + window.location.href = '/do-open/[eid]'; + }); + + [is e_state "open"] + const closeBtn = document.getElementById('close-btn'); + closeBtn.addEventListener('click', () => { + closeModal.show(); + }); + [end] + const confirmClose = document.getElementById('confirm-close'); + confirmClose.addEventListener('click', () => { + closeModal.hide(); + window.location.href = '/do-close/[eid]'; + }); + + // Toggle individual description + function toggleDescription(issueId) { + const desc = document.getElementById(`description-${issueId}`); + const twiddle = desc.previousElementSibling.querySelector('.twiddle'); + if (desc && twiddle) { + desc.classList.toggle('show'); + twiddle.classList.toggle('bi-caret-right-fill'); + twiddle.classList.toggle('bi-caret-down-fill'); + } + } + + // Expand/Collapse All descriptions + let allExpanded = false; + function toggleAllDescriptions() { + allExpanded = !allExpanded; + document.querySelectorAll('.description').forEach(desc => { + desc.classList.toggle('show', allExpanded); + }); + document.querySelectorAll('.twiddle').forEach(twiddle => { + twiddle.classList.toggle('bi-caret-right-fill', !allExpanded); + twiddle.classList.toggle('bi-caret-down-fill', allExpanded); + }); + const toggleBtn = document.querySelector('.btn-outline-secondary'); + const toggleIcon = toggleBtn.querySelector('i'); + const toggleText = document.getElementById('toggle-all-text'); + + if (allExpanded) { + toggleIcon.className = 'bi bi-arrows-collapse me-1'; + toggleText.textContent = 'Collapse All'; + } else { + toggleIcon.className = 'bi bi-arrows-expand me-1'; + toggleText.textContent = 'Expand All'; + } + } + + // Open modal for adding issue + function openAddIssueModal() { + const form = document.getElementById('issueForm'); + form.action = '/do-add-issue/[eid]'; + document.getElementById('issueModalLabel').textContent = 'Add Issue'; + document.getElementById('issueId').value = ''; + document.getElementById('issueTitle').value = ''; + document.getElementById('issueDescription').value = ''; + document.getElementById('issueTitle').classList.remove('is-invalid'); + showModal('issueModal'); + } + + // Open modal for editing issue + function openEditIssueModal(issueId, title, description) { + const form = document.getElementById('issueForm'); + form.action = `/do-edit-issue/[eid]/${issueId}`; + document.getElementById('issueModalLabel').textContent = 'Edit Issue'; + document.getElementById('issueId').value = issueId; + document.getElementById('issueTitle').value = title; + document.getElementById('issueDescription').value = description; + document.getElementById('issueTitle').classList.remove('is-invalid'); + showModal('issueModal'); + } + + // Open modal for deleting issue + function openDeleteIssueModal(issueId, title) { + const form = document.getElementById('deleteIssueForm'); + form.action = `/do-delete-issue/[eid]/${issueId}`; + document.getElementById('deleteIssueId').value = issueId; + document.getElementById('deleteIssueMessage').textContent = `Are you sure you want to delete "${title}"?`; + showModal('deleteIssueModal'); + } + + // Save issue (add or edit) + function saveIssue() { + if (!validateRequiredField('issueTitle')) { + return; + } + + submitFormWithLoading('issueForm', 'saveIssueBtn', 'Saving...'); + } + +</script>
