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
The following commit(s) were added to refs/heads/trunk by this push:
new 30d211a Initial draft of state workflow for an Election.
30d211a is described below
commit 30d211ae7b0260a85b5ab2c0b92eee3073744f91
Author: Greg Stein <[email protected]>
AuthorDate: Tue Oct 7 08:20:19 2025 -0500
Initial draft of state workflow for an Election.
Display a state diagrom from Editable to Open to Closed. Provide
buttons to advance the state of the Election. Confirmation dialogs
since these actions are irreversible.
TBD: hook the buttons into server interactions to actually perform the
workflow. This draft is client-side only.
Note: this is really janky. CSS outside of <head/> and some JS after
the final </html>. It works in Chrome, but this is not ideal.
Committing for now, to get the content in here, with future refinement.
---
v3/server/templates/manage.ezt | 166 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 166 insertions(+)
diff --git a/v3/server/templates/manage.ezt b/v3/server/templates/manage.ezt
index 4534f18..dbcef4c 100644
--- a/v3/server/templates/manage.ezt
+++ b/v3/server/templates/manage.ezt
@@ -1,10 +1,176 @@
[include "header.ezt"]
+
+ <style>
+ .state-diagram {
+ display: grid;
+ grid-template-columns: auto auto auto auto auto; /* State1 Arrow1
State2 Arrow2 State3 */
+ grid-template-rows: auto auto; /* States row, Buttons row */
+ gap: 10px;
+ justify-items: center;
+ align-items: center;
+ max-width: 700px;
+ margin: 20px auto;
+ padding: 20px;
+ border: 1px solid #dee2e6;
+ border-radius: 8px;
+ }
+
+ .state {
+ padding: 8px 16px;
+ border-radius: 4px;
+ text-align: center;
+ font-size: 1rem;
+ }
+
+ .current {
+ font-weight: bold;
+ background-color: #fff3cd; /* Bootstrap warning light */
+ border: 1px solid #ffca2c;
+ }
+
+ .arrow {
+ font-size: 1.2em;
+ color: #6c757d; /* Bootstrap secondary */
+ }
+
+ .action-button {
+ grid-row: 2;
+ font-weight: bold;
+ }
+
+ .open-button {
+ grid-column: 3; /* Aligned under State2 (Open for voting) */
+ }
+
+ .close-button {
+ grid-column: 5; /* Aligned under State3 (Closed) */
+ }
+ </style>
+
<div class="container">
<h1>[title]</h1>
+ <div class="sticky-top">
+ <h2>[e_title]</h2>
+ </div>
+
+ <div class="state-diagram">
+ <div id="state-editing" class="state">Editing</div>
+ <div id="arrow1" class="arrow">→</div>
+ <div id="state-open" class="state">Open for Voting</div>
+ <div id="arrow2" class="arrow">→</div>
+ <div id="state-closed" class="state">Closed</div>
+
+ <button id="open-btn" class="btn btn-success action-button
open-button">Open</button>
+ <button id="close-btn" class="btn btn-danger action-button
close-button d-none">Close</button>
+ </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>
+
+ <!-- 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>
+
+
<ul>
[for issues]
<li>[issues.title]</li>
[end]
</ul>
</div>
+
[include "footer.ezt"]
+
+<script>
+ let currentState = 'editing'; // Initial state: 'editing', 'open',
'closed'
+
+ const states = {
+ editing: document.getElementById('state-editing'),
+ open: document.getElementById('state-open'),
+ closed: document.getElementById('state-closed')
+ };
+
+ const openBtn = document.getElementById('open-btn');
+ const closeBtn = document.getElementById('close-btn');
+ const openModal = new
bootstrap.Modal(document.getElementById('openConfirmModal'));
+ const closeModal = new
bootstrap.Modal(document.getElementById('closeConfirmModal'));
+ const confirmOpen = document.getElementById('confirm-open');
+ const confirmClose = document.getElementById('confirm-close');
+
+ function updateUI() {
+ // Remove current class from all
+ Object.values(states).forEach(state =>
state.classList.remove('current'));
+
+ // Highlight current
+ [# careful! opening bracket here, within ezt syntax ]
+ states[[]currentState].classList.add('current');
+
+ // Show/hide buttons
+ if (currentState === 'editing') {
+ openBtn.classList.remove('d-none');
+ closeBtn.classList.add('d-none');
+ } else if (currentState === 'open') {
+ openBtn.classList.add('d-none');
+ closeBtn.classList.remove('d-none');
+ } else { // closed
+ openBtn.classList.add('d-none');
+ closeBtn.classList.add('d-none');
+ }
+ }
+
+ openBtn.addEventListener('click', () => {
+ openModal.show();
+ });
+
+ confirmOpen.addEventListener('click', () => {
+ currentState = 'open';
+ updateUI();
+ openModal.hide();
+ // Here, you would call your backend API to update the election
state
+ });
+
+ closeBtn.addEventListener('click', () => {
+ closeModal.show();
+ });
+
+ confirmClose.addEventListener('click', () => {
+ currentState = 'closed';
+ updateUI();
+ closeModal.hide();
+ // Here, you would call your backend API to update the election
state
+ });
+
+ // Initial update
+ updateUI();
+ </script>