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 e805ed5db52eac09b80fe9f144cb6798013cbcc5
Author: Greg Stein <[email protected]>
AuthorDate: Fri Feb 20 02:00:19 2026 -0600

    feat: add upcoming elections section to voter page
    
    Co-authored-by: aider (openrouter/x-ai/grok-code-fast-1) <[email protected]>
---
 v3/TODO.md                    |  9 ++-------
 v3/server/pages.py            |  3 ++-
 v3/server/templates/voter.ezt | 46 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/v3/TODO.md b/v3/TODO.md
index d062c76..ddbd3ee 100644
--- a/v3/TODO.md
+++ b/v3/TODO.md
@@ -7,15 +7,10 @@ Based on a review of `v3/server/pages.py` (and related 
templates like `voter.ezt
 - **Impact**: Users won't be able to save dates via the UI, breaking the 
intended workflow.
 - **Resolution**: Added the two endpoints with a refactored helper function 
`_set_election_date` to handle common logic (auth, JSON parsing, validation, 
setting dates, logging, and response). Endpoints now require authentication, 
validate dates, and log actions. CSRF handling remains a TODO (placeholder 
token in use). Test for proper date-setting and error handling. Added 
supporting methods `set_open_at` and `set_close_at` to the Election class in 
`election.py`, and corresponding cursors [...]
 
-## 2. Upcoming Elections Not Populated in `voter_page()`
+## 2. Upcoming Elections Not Populated in `voter_page()` - RESOLVED
 - **Issue**: The `voter.ezt` template checks for `[if-any upcoming]` and loops 
over `upcoming` elections, but `voter_page()` only sets `result.election` (for 
open elections). `result.upcoming` is never defined, so the "Upcoming 
Elections" section will always be empty.
 - **Impact**: Upcoming elections (e.g., those in 'editable' state with a 
future open date) won't display, confusing users.
-- **Suggested Fix**: Modify `voter_page()` to separate elections into 
`upcoming` and `election` (open ones). Assuming 
`steve.election.Election.open_to_pid()` returns all relevant elections, filter 
them:
-  - `upcoming`: Elections where `state == 'editable'` and `open_at` is in the 
future (within `SOON_CUTOFF`).
-  - `election`: The rest (open or closed, but template seems to handle closed 
via `(closed)` text).
-  - Add: `result.upcoming = [postprocess_election(e) for e in election if 
e.state == steve.election.Election.S_EDITABLE and (e.open_at and e.open_at > 
datetime.datetime.now().timestamp())]`
-  - Then, `result.election = [postprocess_election(e) for e in election if e 
not in result.upcoming]`
-  - Update `result.len_election` accordingly.
+- **Resolution**: Added a new Election class method `upcoming_to_pid` to 
return editable elections for a given PID with voting eligibility. Added 
corresponding query `q_upcoming_to_me` in `queries.yaml`. Updated 
`voter_page()` to fetch and post-process upcoming elections into 
`result.upcoming`. Updated `voter.ezt` to include a dedicated "Upcoming 
Elections" section with similar card layout, a "Preview Ballot" link, and 
subtle visual distinction (lighter background, "Upcoming" badge). No  [...]
 
 ## 3. Hardcoded `vtype` in `do_add_issue_endpoint()`
 - **Issue**: `vtype` is hardcoded to `'yna'`, and `kv = None`. The comment 
mentions handling SEATS for STV, but it's not implemented. If users try to add 
STV issues, it will fail or behave incorrectly.
diff --git a/v3/server/pages.py b/v3/server/pages.py
index 8c9daf9..945fb95 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -11,7 +11,7 @@
 # Unless required by applicable law or agreed to in writing,
 # software distributed under the License is distributed on an
 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
+# KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations
 # under the License.
 
@@ -154,6 +154,7 @@ async def voter_page():
         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.len_election = len(election)
     result.len_owned = len(owned)
diff --git a/v3/server/templates/voter.ezt b/v3/server/templates/voter.ezt
index 8def7c2..d4a6dc5 100644
--- a/v3/server/templates/voter.ezt
+++ b/v3/server/templates/voter.ezt
@@ -57,6 +57,52 @@
             </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]
+
+                            </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>
+                    </div>
+                </a>
+            </div>
+          [end]
+        [else]
+          <p>No upcoming elections.</p>
+        [end]
+
         <p>
           You have [len_owned] <a href="/admin">elections to manage</a>.
         </p>

Reply via email to