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 3ab9679 Revamp voter landing page.
3ab9679 is described below
commit 3ab9679455eedf6f4c9435a8b039c5db68705cf5
Author: Greg Stein <[email protected]>
AuthorDate: Fri Feb 20 04:15:47 2026 -0600
Revamp voter landing page.
Better organization for open/current elections, upcoming, and past.
Big template change to move to tabs. Supporting template variables in
pages.py
h/t Claude Sonnet
---
v3/server/pages.py | 21 ++-
v3/server/templates/voter.ezt | 302 +++++++++++++++++++++++++++++-------------
2 files changed, 225 insertions(+), 98 deletions(-)
diff --git a/v3/server/pages.py b/v3/server/pages.py
index e9b951c..898b773 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -153,12 +153,24 @@ async def voter_page():
election = steve.election.Election.open_to_pid(DB_FNAME, result.uid)
owned = steve.election.Election.owned_elections(DB_FNAME, result.uid)
- result.election = [postprocess_election(e) for e in election]
- result.upcoming = [postprocess_election(e) for e in
steve.election.Election.upcoming_to_pid(DB_FNAME, result.uid)]
+ result.open_elections = [postprocess_election(e) for e in election]
+ result.upcoming_elections = [postprocess_election(e) for e in
steve.election.Election.upcoming_to_pid(DB_FNAME, result.uid)]
+ result.past_elections = [ ] ### TBD
- result.len_election = len(election)
+ result.len_open = len(result.open_elections)
+ result.len_upcoming = len(result.upcoming_elections)
+ result.len_past = len(result.past_elections)
+
+ ### no longer needed? move to navbar?
result.len_owned = len(owned)
+ if result.len_open:
+ result.active_tab = 'open'
+ elif result.len_upcoming:
+ result.active_tab = 'upcoming'
+ else:
+ result.active_tab = 'past'
+
return result
@@ -602,6 +614,9 @@ def postprocess_election(e):
if 'issue_count' not in e:
e.issue_count = 5 ### arbitrary. just provide a value
+ ### need to figure this out later.
+ e.has_voted = None # EZT False
+
return e
diff --git a/v3/server/templates/voter.ezt b/v3/server/templates/voter.ezt
index d4a6dc5..2325db7 100644
--- a/v3/server/templates/voter.ezt
+++ b/v3/server/templates/voter.ezt
@@ -1,110 +1,222 @@
[include "header.ezt"]
<div class="container">
- <h1>[title]</h1>
[include "flashes.ezt"]
- [for election]
- <div class="w-auto mb-4">
- <a href="/vote-on/[election.eid]" class="text-decoration-none">
- <div class="card h-100">
- <div class="card-body">
- <h5 class="card-title">[election.title][#
- ][if-any election.closed] (closed)[else][#
- ][if-any election.is_opened]
(open)[end][end]</h5>
- <p class="card-text">
- You have [election.issue_count] issues to vote
upon.
- <br/>
- eid: [election.eid]
+ <ul class="nav nav-tabs mb-3" id="electionTabs" role="tablist">
+ <li class="nav-item" role="presentation">
+ <button class="nav-link [if-any active_tab][else]active[end]"
id="open-tab"
+ data-bs-toggle="tab" data-bs-target="#open"
+ type="button" role="tab"
+ aria-controls="open"
+ aria-selected="[if-any
active_tab]false[else]true[end]">
+ Voting
+ [if-any len_open]
+ <span class="ms-1 text-muted small">([len_open])</span>
+ [end]
+ </button>
+ </li>
+ <li class="nav-item" role="presentation">
+ <button class="nav-link" id="upcoming-tab"
+ data-bs-toggle="tab" data-bs-target="#upcoming"
+ type="button" role="tab"
+ aria-controls="upcoming"
+ aria-selected="false">
+ Upcoming
+ [if-any len_upcoming]
+ <span class="ms-1 text-muted
small">([len_upcoming])</span>
+ [end]
+ </button>
+ </li>
+ <li class="nav-item" role="presentation">
+ <button class="nav-link" id="past-tab"
+ data-bs-toggle="tab" data-bs-target="#past"
+ type="button" role="tab"
+ aria-controls="past"
+ aria-selected="false">
+ Past
+ [if-any len_past]
+ <span class="ms-1 text-muted small">([len_past])</span>
+ [end]
+ </button>
+ </li>
+ </ul>
- [if-any election.closed]
- Closed
- <span title="[election.fmt_close_at_full]"
- style="border-bottom: 1px dotted
#007bff;"
- >[election.fmt_close_at]</span>
- [else]
- [if-any election.open_at]
- <div>
- [if-any election.is_opened]
- Opened
- [else]
- Opening
- [end]
- <span title="[election.fmt_open_at_full]"
- style="border-bottom: 1px dotted
#007bff;"
- >[election.fmt_open_at]</span>
- </div>
- [end]
- [if-any election.close_at]
- <div>
- Closing
- <span title="[election.fmt_close_at_full]"
- style="border-bottom: 1px dotted
#007bff;"
- >[election.fmt_close_at]</span>
- </div>
- [end]
- [end]
+ <div class="tab-content" id="electionTabContent">
- </p>
- </div>
- <div class="card-footer text-muted small">
- Created by [election.owner_name]
([election.owner_pid])
- [if-any election.authz]
- (authz: [election.authz])
- [end]
- </div>
+ <!-- Open Elections -->
+ <div class="tab-pane fade show active" id="open" role="tabpanel"
aria-labelledby="open-tab">
+ [if-any open_elections]
+ <div class="row g-3">
+ [for open_elections]
+ <div class="col-12 col-md-6">
+ <div class="card h-100">
+ <div class="card-body d-flex flex-column">
+ <h5 class="card-title
mb-3">[open_elections.title]</h5>
+ <p class="card-text text-muted small
mb-3">
+ [open_elections.issue_count] issues
+ ·
+ Opened
+ <span
title="[open_elections.fmt_open_at_full]"
+ style="border-bottom: 1px
dotted currentColor; cursor: help;"
+
>[open_elections.fmt_open_at]</span>
+ [if-any open_elections.close_at]
+ ·
+ Closes
+ <span
title="[open_elections.fmt_close_at_full]"
+ style="border-bottom:
1px dotted currentColor; cursor: help;"
+
>[open_elections.fmt_close_at]</span>
+ [end]
+ </p>
+ <div class="mt-auto">
+ [if-any open_elections.has_voted]
+ <a
href="/vote-on/[open_elections.eid]"
+ class="btn
btn-outline-primary">Recast Vote</a>
+ [else]
+ <a
href="/vote-on/[open_elections.eid]"
+ class="btn
btn-primary">Vote Now</a>
+ [end]
+ </div>
+ </div>
+ <div class="card-footer text-muted small
d-flex justify-content-between align-items-center">
+ <span>
+ [open_elections.owner_name]
+ <span class="text-muted"
style="font-size: 0.8em;">([open_elections.owner_pid])</span>
+ [if-any open_elections.authz]
+ ·
[open_elections.authz]
+ [end]
+ </span>
+ <span class="text-muted"
style="font-size: 0.8em;">eid: [open_elections.eid]</span>
+ </div>
+ </div>
+ </div>
+ [end]
</div>
- </a>
+ [else]
+ <p class="text-muted">No elections are currently open for
voting.</p>
+ [end]
</div>
- [end]
- <h2>Upcoming Elections</h2>
- [if-any upcoming]
- [for upcoming]
- <div class="w-auto mb-4">
- <a href="/vote-on/[upcoming.eid]" class="text-decoration-none">
- <div class="card h-100 bg-light">
- <div class="card-body">
- <h5 class="card-title">[upcoming.title] <span
class="badge badge-secondary">Upcoming</span></h5>
- <p class="card-text">
- You have [upcoming.issue_count] issues to vote
upon.
- <br/>
- eid: [upcoming.eid]
-
- [if-any upcoming.open_at]
- <div>
- Opening
- <span title="[upcoming.fmt_open_at_full]"
- style="border-bottom: 1px dotted
#007bff;"
- >[upcoming.fmt_open_at]</span>
- </div>
- [end]
- [if-any upcoming.close_at]
- <div>
- Closing
- <span title="[upcoming.fmt_close_at_full]"
- style="border-bottom: 1px dotted
#007bff;"
- >[upcoming.fmt_close_at]</span>
- </div>
- [end]
+ <!-- Upcoming Elections -->
+ <div class="tab-pane fade" id="upcoming" role="tabpanel"
aria-labelledby="upcoming-tab">
+ [if-any upcoming_elections]
+ <div class="row g-3">
+ [for upcoming_elections]
+ <div class="col-12 col-md-6">
+ <div class="card h-100">
+ <div class="card-body d-flex flex-column">
+ <h5 class="card-title
mb-3">[upcoming_elections.title]</h5>
+ <p class="card-text mb-0">
+ [if-any upcoming_elections.open_at]
+ <span class="fw-semibold">
+ Opens
+ <span
title="[upcoming_elections.fmt_open_at_full]"
+
style="border-bottom: 1px dotted currentColor; cursor: help;"
+
>[upcoming_elections.fmt_open_at]</span>
+ </span>
+ [else]
+ <span class="fw-semibold
text-muted">Opening date not yet set</span>
+ [end]
+ </p>
+ [if-any upcoming_elections.close_at]
+ <p class="card-text text-muted
small mt-1 mb-0">
+ Closes
+ <span
title="[upcoming_elections.fmt_close_at_full]"
+ style="border-bottom:
1px dotted currentColor; cursor: help;"
+
>[upcoming_elections.fmt_close_at]</span>
+ </p>
+ [end]
+ <p class="card-text text-muted small
mt-2 mb-0">
+ [upcoming_elections.issue_count]
issues
+ </p>
+ </div>
+ <div class="card-footer text-muted small
d-flex justify-content-between align-items-center">
+ <span>
+ [upcoming_elections.owner_name]
+ <span class="text-muted"
style="font-size: 0.8em;">([upcoming_elections.owner_pid])</span>
+ [if-any upcoming_elections.authz]
+ ·
[upcoming_elections.authz]
+ [end]
+ </span>
+ <span class="text-muted"
style="font-size: 0.8em;">eid: [upcoming_elections.eid]</span>
+ </div>
+ </div>
+ </div>
+ [end]
+ </div>
+ [else]
+ <p class="text-muted">No upcoming elections.</p>
+ [end]
+ </div>
- </p>
- </div>
- <div class="card-footer text-muted small">
- Created by [upcoming.owner_name]
([upcoming.owner_pid])
- [if-any upcoming.authz]
- (authz: [upcoming.authz])
- [end]
- </div>
+ <!-- Past Elections -->
+ <div class="tab-pane fade" id="past" role="tabpanel"
aria-labelledby="past-tab">
+ [if-any past_elections]
+ <div class="row g-3">
+ [for past_elections]
+ <div class="col-12 col-md-6">
+ <div class="card h-100">
+ <div class="card-body">
+ <h5 class="card-title
mb-3">[past_elections.title]</h5>
+ <p class="card-text text-muted small
mb-0">
+ Opened
+ <span
title="[past_elections.fmt_open_at_full]"
+ style="border-bottom: 1px
dotted currentColor; cursor: help;"
+
>[past_elections.fmt_open_at]</span>
+ ·
+ Closed
+ <span
title="[past_elections.fmt_close_at_full]"
+ style="border-bottom: 1px
dotted currentColor; cursor: help;"
+
>[past_elections.fmt_close_at]</span>
+ </p>
+ </div>
+ <div class="card-footer text-muted small
d-flex justify-content-between align-items-center">
+ <span>
+ [past_elections.owner_name]
+ <span class="text-muted"
style="font-size: 0.8em;">([past_elections.owner_pid])</span>
+ [if-any past_elections.authz]
+ ·
[past_elections.authz]
+ [end]
+ </span>
+ <span class="text-muted"
style="font-size: 0.8em;">eid: [past_elections.eid]</span>
+ </div>
+ </div>
+ </div>
+ [end]
</div>
- </a>
+ [else]
+ <p class="text-muted">No past elections.</p>
+ [end]
</div>
- [end]
- [else]
- <p>No upcoming elections.</p>
- [end]
- <p>
- You have [len_owned] <a href="/admin">elections to manage</a>.
- </p>
+ </div>
</div>
+
+<script>
+// Activate the correct tab based on server-supplied active_tab value.
+// Falls back to "open" if the value is unrecognized.
+(function () {
+ var active = "[active_tab]";
+ var map = { open: "open-tab", upcoming: "upcoming-tab", past: "past-tab" };
+ var btnId = map[[]active] || "open-tab";
+ var btn = document.getElementById(btnId);
+ if (btn) {
+ // Deactivate all tabs first
+ document.querySelectorAll("#electionTabs .nav-link").forEach(function
(el) {
+ el.classList.remove("active");
+ el.setAttribute("aria-selected", "false");
+ });
+ document.querySelectorAll(".tab-pane").forEach(function (el) {
+ el.classList.remove("show", "active");
+ });
+ // Activate the chosen tab and pane
+ btn.classList.add("active");
+ btn.setAttribute("aria-selected", "true");
+ var pane = document.getElementById(active in map ? active : "open");
+ if (pane) {
+ pane.classList.add("show", "active");
+ }
+ }
+}());
+</script>
+
[include "footer.ezt"]