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 62c96128d0617a88f98d487784041925b3ea5ecf
Author: Greg Stein <[email protected]>
AuthorDate: Wed Oct 8 23:45:04 2025 -0500

    Initial work on YNA voting on issues.
---
 v3/server/pages.py              |   1 +
 v3/server/templates/vote-on.ezt | 132 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 130 insertions(+), 3 deletions(-)

diff --git a/v3/server/pages.py b/v3/server/pages.py
index 9dec156..7756eb1 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -131,6 +131,7 @@ def load_election(func):
 async def vote_on_page(election):
     result = await signin_info()
     result.title = 'Vote On Election'
+    result.eid = election.eid
 
     md = election.get_metadata()
     result.e_title = md[1]
diff --git a/v3/server/templates/vote-on.ezt b/v3/server/templates/vote-on.ezt
index f027df9..d195daf 100644
--- a/v3/server/templates/vote-on.ezt
+++ b/v3/server/templates/vote-on.ezt
@@ -4,8 +4,134 @@
         <p>
             You have [issue_count] issues to vote upon, in this election.
         </p>
-        <p>
-            TBD: list
-        </p>
+
+    <!-- Bulk Vote and Clear Controls -->
+    <div class="mb-4">
+      <h5>Bulk Vote (applies to unvoted issues only)</h5>
+      <div class="btn-group" role="group">
+        <button type="button" class="btn btn-outline-primary" 
onclick="bulkVote('y')">Fill Yes</button>
+        <button type="button" class="btn btn-outline-primary" 
onclick="bulkVote('n')">Fill No</button>
+        <button type="button" class="btn btn-outline-primary" 
onclick="bulkVote('a')">Fill Abstain</button>
+      </div>
+      <button type="button" class="btn btn-outline-danger ms-2" 
onclick="clearAllVotes()">Clear All</button>
+      <button type="button" class="btn btn-outline-secondary ms-2" 
onclick="toggleAllDescriptions()">
+        <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="vote-radio">
+            <div class="form-check form-check-inline">
+              <input class="form-check-input" type="radio" name="vote-1" 
id="yes-[issues.iid]" value="y">
+              <label class="form-check-label" for="yes-1">Yes</label>
+            </div>
+            <div class="form-check form-check-inline">
+              <input class="form-check-input" type="radio" name="vote-1" 
id="no-[issues.iid]" value="n">
+              <label class="form-check-label" for="no-1">No</label>
+            </div>
+            <div class="form-check form-check-inline">
+              <input class="form-check-input" type="radio" name="vote-1" 
id="abstain-[issues.iid]" value="a">
+              <label class="form-check-label" for="abstain-1">Abstain</label>
+            </div>
+          </div>
+        </div>
+        <div id="description-[issues.iid]" class="description 
mt-2">[issues.description]</div>
+      </div>
+    [end]
+    </div>
+
+    <!-- Submit Button -->
+    <div class="mt-4">
+      <button type="button" class="btn btn-primary" 
onclick="submitVotes()">Submit Votes</button>
+    </div>
+  </div>
+
     </div>
 [include "footer.ezt"]
+
+  <!-- Bootstrap JS and Popper -->
+  <script>
+    // Toggle individual description
+    function toggleDescription(issueId) {
+      const desc = document.getElementById('description-${issueId}');
+      const twiddle = desc.previousElementSibling.querySelector('.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);
+      });
+      document.getElementById('toggle-all-text').textContent = allExpanded ? 
'Collapse All' : 'Expand All';
+    }
+
+    // Bulk vote (only on unvoted issues)
+    function bulkVote(value) {
+      document.querySelectorAll('.issue-item').forEach(item => {
+        const radios = item.querySelectorAll('input[[]type="radio"]');
+        const isVoted = Array.from(radios).some(r => r.checked);
+        if (!isVoted) {
+          const radioToCheck = item.querySelector('input[[]value="${value}"]');
+          if (radioToCheck) radioToCheck.checked = true;
+        }
+      });
+    }
+
+    // Clear all votes
+    function clearAllVotes() {
+      document.querySelectorAll('.vote-radio 
input[[]type="radio"]').forEach(radio => {
+        radio.checked = false;
+      });
+    }
+
+    // Submit votes
+    function submitVotes() {
+      const votes = {};
+      [# careful! be wary of open-brackets within ezt ]
+      document.querySelectorAll('.vote-radio 
input[[]type="radio"]:checked').forEach(radio => {
+        const issueId = radio.name.split('-')[[]1];
+        votes[[]issueId] = radio.value;
+      });
+
+      if (Object.keys(votes).length === 0) {
+        alert('No votes selected!');
+        return;
+      }
+
+      fetch('/do_vote/[eid]', {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify(votes),
+      })
+        .then(response => {
+          if (!response.ok) throw new Error('Network response was not ok');
+          return response.json();
+        })
+        .then(data => {
+          alert('Votes submitted successfully!');
+          console.log('Server response:', data);
+        })
+        .catch(error => {
+          alert('Error submitting votes: ' + error.message);
+          console.error('Error:', error);
+        });
+    }
+  </script>

Reply via email to