From 661393452711878b296bfd250bcc8e32f4f7d8d4 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Tue, 13 Feb 2024 15:20:33 -0500 Subject: [PATCH 01/30] initial thoughts --- docs/DesignNotes.md | 89 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 docs/DesignNotes.md diff --git a/docs/DesignNotes.md b/docs/DesignNotes.md new file mode 100644 index 0000000..eca3b38 --- /dev/null +++ b/docs/DesignNotes.md @@ -0,0 +1,89 @@ +# Initial Design Thoughts Regarding a Client Side UI/UX + +## Background + +The current design target is to create something in a few weeks for a June 8th live demo. The date and (my) personal skill level are driving the client UI side design and implementation. + +Currently, the current tech choice to achieve this is plain html, css, and javascript leveraging Google's devtools and Microsoft's Visual Studio Code. + +## Uber Context + +Summary and a duplication from other sources: + +The [VTP-dev-end](https://github.com/TrustTheVote-Project/VTP-dev-env) holds/defines the development/run-time environment. Contained within VTP-dev-end as submodules are three repos: +- [VTP-mock-election.US.15](https://github.com/TrustTheVote-Project/VTP-mock-election.US.15) - contains the election configuration and data +- [VTP-web-api](https://github.com/TrustTheVote-Project/VTP-web-api) - contains the FASTapi rest interface +- [VoteTrackerPlus](https://github.com/TrustTheVote-Project/VoteTrackerPlus) - contains the python backend code + +This repo, [VTP-web-client](https://github.com/TrustTheVote-Project/VTP-web-client) contains the client frontend/UI/UX + +See [DesignNotes.md](https://github.com/TrustTheVote-Project/VTP-web-api/blob/main/docs/DesignNotes.md) in the VTP-web-api repo for details on the rest api. + +## User Story 1 + +#### What happens when a client (smart device browser of choice) initial connects to the VTP demo + +1. Displays a short welcome and help message that briefly explains the standard contest buttons. There is only one active button on the page - continue. Activating that continues to step 2. + +2. Displays the first contest. At the moment two types of contests are supported, plurality and IRV (RCV). + +If plurality: +- can select up to the number of open positions by tapping the choice of interest in the vertical list +- individual choices can be de-selected by tapping again +- if more than the allowable number of choices are selected, a dialog abort window appears + +If IRV/RCV: +- the vertical list of candidates is displayed below an (initially empty) vertical list of ranked/selected candidates above +- clicking an candidate from the below list adds the candidate to the bottom of the upper list and removes the candidate from the below list +- swiping a candidate left or right in the upper list will remove the candidate from the upper list and re-appear the candidate in the original order in the below list +- the upper list is draggable - the upper list can be reordered at will + +Common buttons/actions/UI: + +- there is a 'de-select all' button somewhere in the lower left portion of the page +- there is a 'continue' button somewhere in the lower right portion of the page + - if the continue button is activated and there is an undervote situation, a warning window appears that allows the user to go to the next contest or stay with the current contest +- across the top is a horizontal progress bar divided in equals sections by contest + - each contest is clickable and takes the user to that contest + - completed contests are solid bright green, undervote contests are solid somewhat dimm yellow, and no-vote contests are red outlined boxes +- immediately below the progress bar in the left corner is a 'previous contest' button +- immediately below the progress bar in the right corner is a next contest' button + - when the last contest in focus, the upper right button says 'checkout' +- when the checkout box is clicked, all the contests are displayed with only two buttons: go-back and this-is-my-vote + - go-back goes to the previous page with the checkout box in the upper right + - this-is-my-vote submits the ballot + +Note that the backend python server side will re-validate the incoming ballot CVR. The this-is-my-vote entrypoint can return various errors to the user: + +- a non compliant contest selection was found +- there was a problem on the server side + +If there is no error, the endpoint returns: + +- the voter's ballot check as if it were paper (no links) but with the QR code being a valid link (into the voter's backend server's still active git workspace) +- the voter's row offset into the ballot check is temporarily displayed +- in addition there is, for demo purposes only, two additional buttons on this page + - a verify-ballot-receipt button + - a tally-contests button + +## User Story 2 + +#### What happens when the user clicks the QR code (or takes a photo of the QR code) + +Displays the git repo copy of the contents of the ballot check. All the digests are active links to the actual git digests for a specific vote for a specific contest. + +## User Story 3 + +#### What happens when the user wants to verify their ballot check? + +The user clicks the verify-ballot-receipt (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. + +The page contains a re-verify button that re-verifies the receipt honoring the new values in the various fields. + +## User Story 4 + +#### What happens when the user wants to tally the election + +The user clicks the tally-contests button (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. + +The page contains a re-tally button that re-tallies the election honoring the new values in the various fields. From 7ebcd44ef2ee6d0c27bc38bb121d329db182a385 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Tue, 13 Feb 2024 15:41:02 -0500 Subject: [PATCH 02/30] minor cleanup --- docs/DesignNotes.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/DesignNotes.md b/docs/DesignNotes.md index eca3b38..6b9974c 100644 --- a/docs/DesignNotes.md +++ b/docs/DesignNotes.md @@ -21,9 +21,9 @@ See [DesignNotes.md](https://github.com/TrustTheVote-Project/VTP-web-api/blob/ma ## User Story 1 -#### What happens when a client (smart device browser of choice) initial connects to the VTP demo +#### What happens when a client (smart device browser of choice) initially connects to the VTP demo -1. Displays a short welcome and help message that briefly explains the standard contest buttons. There is only one active button on the page - continue. Activating that continues to step 2. +1. Displays a short welcome and help message that briefly explains the standard contest buttons. There is only one active button on the page, __continue__. Activating that continues to step 2. 2. Displays the first contest. At the moment two types of contests are supported, plurality and IRV (RCV). @@ -40,20 +40,21 @@ If IRV/RCV: Common buttons/actions/UI: -- there is a 'de-select all' button somewhere in the lower left portion of the page -- there is a 'continue' button somewhere in the lower right portion of the page +- there is a __de-select all__ button somewhere in the lower left portion of the page +- there is a __continue__ button somewhere in the lower right portion of the page - if the continue button is activated and there is an undervote situation, a warning window appears that allows the user to go to the next contest or stay with the current contest -- across the top is a horizontal progress bar divided in equals sections by contest +- across the top is a horizontal progress bar divided into equal width sections by contest - each contest is clickable and takes the user to that contest - - completed contests are solid bright green, undervote contests are solid somewhat dimm yellow, and no-vote contests are red outlined boxes -- immediately below the progress bar in the left corner is a 'previous contest' button -- immediately below the progress bar in the right corner is a next contest' button - - when the last contest in focus, the upper right button says 'checkout' -- when the checkout box is clicked, all the contests are displayed with only two buttons: go-back and this-is-my-vote - - go-back goes to the previous page with the checkout box in the upper right - - this-is-my-vote submits the ballot + - if there is an undervote in the current contest, a proceed-able warning window appears + - in the completion bar, completed contests are solid bright green, undervote contests are solid somewhat dimm yellow, and no-vote or undervote contests that are to the left of any green or yellow contest are red outlined boxes. +- immediately below the progress bar in the left corner is a __previous contest__ button +- immediately below the progress bar in the right corner is a __next contest__ button + - when the last contest in focus, the upper right button says __checkout__ +- when the __checkout__ box is clicked, all the contests are displayed with only two buttons: __go-back__ and __this-is-my-vote__ + - __go-back__ goes to the previous page with the checkout box in the upper right + - __this-is-my-vote__ submits the ballot -Note that the backend python server side will re-validate the incoming ballot CVR. The this-is-my-vote entrypoint can return various errors to the user: +Note that the backend python server side will re-validate the incoming ballot CVR. The __this-is-my-vote__ entrypoint can return various errors to the user: - a non compliant contest selection was found - there was a problem on the server side @@ -63,8 +64,8 @@ If there is no error, the endpoint returns: - the voter's ballot check as if it were paper (no links) but with the QR code being a valid link (into the voter's backend server's still active git workspace) - the voter's row offset into the ballot check is temporarily displayed - in addition there is, for demo purposes only, two additional buttons on this page - - a verify-ballot-receipt button - - a tally-contests button + - a __verify-ballot-receipt__ button + - a __tally-contests__ button ## User Story 2 @@ -76,14 +77,14 @@ Displays the git repo copy of the contents of the ballot check. All the digests #### What happens when the user wants to verify their ballot check? -The user clicks the verify-ballot-receipt (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. +The user clicks the __verify ballot receipt__ (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. -The page contains a re-verify button that re-verifies the receipt honoring the new values in the various fields. +The page contains a __re-verify__ button that re-verifies the receipt honoring the new values in the various fields. ## User Story 4 #### What happens when the user wants to tally the election -The user clicks the tally-contests button (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. +The user clicks the __tally contests__ button (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. -The page contains a re-tally button that re-tallies the election honoring the new values in the various fields. +The page contains a __re-tally contests__ button that re-tallies the election honoring the new values in the various fields. From 6cf6ed27731775e6d275f355e369388eafaad6dd Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Wed, 14 Feb 2024 17:13:07 -0500 Subject: [PATCH 03/30] trying to figure out initial file structure --- docs/DesignNotes.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/DesignNotes.md b/docs/DesignNotes.md index 6b9974c..2de4a8f 100644 --- a/docs/DesignNotes.md +++ b/docs/DesignNotes.md @@ -2,10 +2,12 @@ ## Background -The current design target is to create something in a few weeks for a June 8th live demo. The date and (my) personal skill level are driving the client UI side design and implementation. +The current design target is to create something in a few weeks for a June 8th live demo. The date and skill level are driving the client UI side design and implementation. Currently, the current tech choice to achieve this is plain html, css, and javascript leveraging Google's devtools and Microsoft's Visual Studio Code. +See the VoteTracker+ [project page](https://github.com/orgs/TrustTheVote-Project/projects/2/views/1) for status. + ## Uber Context Summary and a duplication from other sources: @@ -19,13 +21,17 @@ This repo, [VTP-web-client](https://github.com/TrustTheVote-Project/VTP-web-clie See [DesignNotes.md](https://github.com/TrustTheVote-Project/VTP-web-api/blob/main/docs/DesignNotes.md) in the VTP-web-api repo for details on the rest api. -## User Story 1 +## User Story 1 - landing page + +#### What happens when a client (smart device browser of choice) initially connects to the VTP demo? + +Displays a short welcome and help message that briefly explains the standard contest buttons. There is only one active button on the page, __continue__. Activating that continues to step 2. -#### What happens when a client (smart device browser of choice) initially connects to the VTP demo +## User Story 2 - voting -1. Displays a short welcome and help message that briefly explains the standard contest buttons. There is only one active button on the page, __continue__. Activating that continues to step 2. +#### What happens when the voter starts to mark the ballot (votes)? -2. Displays the first contest. At the moment two types of contests are supported, plurality and IRV (RCV). +Displays the first contest. At the moment two types of contests are supported, plurality and IRV (RCV). If plurality: - can select up to the number of open positions by tapping the choice of interest in the vertical list @@ -67,13 +73,13 @@ If there is no error, the endpoint returns: - a __verify-ballot-receipt__ button - a __tally-contests__ button -## User Story 2 +## User Story 3 - inspecting the ballot check/QR code -#### What happens when the user clicks the QR code (or takes a photo of the QR code) +#### What happens when the user clicks the QR code (or takes a photo of the QR code)? Displays the git repo copy of the contents of the ballot check. All the digests are active links to the actual git digests for a specific vote for a specific contest. -## User Story 3 +## User Story 4 - verifying the ballot check #### What happens when the user wants to verify their ballot check? @@ -81,9 +87,9 @@ The user clicks the __verify ballot receipt__ (end of User Story 1) and a new pa The page contains a __re-verify__ button that re-verifies the receipt honoring the new values in the various fields. -## User Story 4 +## User Story 5 - tallying the election -#### What happens when the user wants to tally the election +#### What happens when the user wants to tally the election? The user clicks the __tally contests__ button (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. From 797d8c1a13d919ab7953bff7643ab169d9e2ba26 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Wed, 14 Feb 2024 17:47:44 -0500 Subject: [PATCH 04/30] initial pass on a timeline --- docs/ProjectPlan.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 docs/ProjectPlan.md diff --git a/docs/ProjectPlan.md b/docs/ProjectPlan.md new file mode 100644 index 0000000..ab30fdf --- /dev/null +++ b/docs/ProjectPlan.md @@ -0,0 +1,22 @@ +## Project Plan + +The basic idea is to stub out static versions of the various pages and write the javascript to support not. Then incrementally add support for a deeper and deeper integration with the web-api. The web-api hopefully will not need major changes. + +1. Create just the controls and UI without dealing with any JSON data - just get the UI controls going + +2. Add JSON decoding support and integrate with static JSON data. This includes reading and sending JSON. + +3. Start plugging in the web-api and to start using actual live JSON data from the web-api + +4. Start debugging live tests + +#### Tiemline + +Working backwards, need to complete all four of the above by end of may. Completion means that the live demo is running without significant issues on a stand along router. Working backwards: + +- Completion of #1: 02/21 (1 week) +- Completion of #2: 03/21 (4 weeks) +- Completion of #3: 04/17 (4 weeks) +- Completion of #4: 05/01 (2 weeks) + +which leaves 3 weeks of buffer From 79ed0594309dd63808c12875c98419994ae7802b Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Thu, 15 Feb 2024 11:52:00 -0500 Subject: [PATCH 05/30] chaser --- docs/DesignNotes.md | 55 ++++++++++---------------------- docs/ProjectPlan.md | 76 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 50 deletions(-) diff --git a/docs/DesignNotes.md b/docs/DesignNotes.md index 2de4a8f..cf52a03 100644 --- a/docs/DesignNotes.md +++ b/docs/DesignNotes.md @@ -1,31 +1,10 @@ -# Initial Design Thoughts Regarding a Client Side UI/UX - -## Background - -The current design target is to create something in a few weeks for a June 8th live demo. The date and skill level are driving the client UI side design and implementation. - -Currently, the current tech choice to achieve this is plain html, css, and javascript leveraging Google's devtools and Microsoft's Visual Studio Code. - -See the VoteTracker+ [project page](https://github.com/orgs/TrustTheVote-Project/projects/2/views/1) for status. - -## Uber Context - -Summary and a duplication from other sources: - -The [VTP-dev-end](https://github.com/TrustTheVote-Project/VTP-dev-env) holds/defines the development/run-time environment. Contained within VTP-dev-end as submodules are three repos: -- [VTP-mock-election.US.15](https://github.com/TrustTheVote-Project/VTP-mock-election.US.15) - contains the election configuration and data -- [VTP-web-api](https://github.com/TrustTheVote-Project/VTP-web-api) - contains the FASTapi rest interface -- [VoteTrackerPlus](https://github.com/TrustTheVote-Project/VoteTrackerPlus) - contains the python backend code - -This repo, [VTP-web-client](https://github.com/TrustTheVote-Project/VTP-web-client) contains the client frontend/UI/UX - -See [DesignNotes.md](https://github.com/TrustTheVote-Project/VTP-web-api/blob/main/docs/DesignNotes.md) in the VTP-web-api repo for details on the rest api. +# VoteTracker+ Client Side UI/UX Design Notes ## User Story 1 - landing page #### What happens when a client (smart device browser of choice) initially connects to the VTP demo? -Displays a short welcome and help message that briefly explains the standard contest buttons. There is only one active button on the page, __continue__. Activating that continues to step 2. +Displays a short welcome and help message that briefly explains the standard contest buttons. There is only one active button on the page, __CONTINUE__. Activating that continues to step 2. ## User Story 2 - voting @@ -46,21 +25,21 @@ If IRV/RCV: Common buttons/actions/UI: -- there is a __de-select all__ button somewhere in the lower left portion of the page -- there is a __continue__ button somewhere in the lower right portion of the page +- there is a __DE-SELECT ALL__ button somewhere in the lower left portion of the page +- there is a __CONTINUE__ button somewhere in the lower right portion of the page - if the continue button is activated and there is an undervote situation, a warning window appears that allows the user to go to the next contest or stay with the current contest - across the top is a horizontal progress bar divided into equal width sections by contest - each contest is clickable and takes the user to that contest - if there is an undervote in the current contest, a proceed-able warning window appears - in the completion bar, completed contests are solid bright green, undervote contests are solid somewhat dimm yellow, and no-vote or undervote contests that are to the left of any green or yellow contest are red outlined boxes. -- immediately below the progress bar in the left corner is a __previous contest__ button -- immediately below the progress bar in the right corner is a __next contest__ button - - when the last contest in focus, the upper right button says __checkout__ -- when the __checkout__ box is clicked, all the contests are displayed with only two buttons: __go-back__ and __this-is-my-vote__ - - __go-back__ goes to the previous page with the checkout box in the upper right - - __this-is-my-vote__ submits the ballot +- immediately below the progress bar in the left corner is a __PREVIOUS CONTEST__ button +- immediately below the progress bar in the right corner is a __NEXT CONTEST__ button + - when the last contest in focus, the upper right button says __CHECKOUT__ +- when the __CHECKOUT__ box is clicked, all the contests are displayed with only two buttons: __GO BACK__ and __THIS IS MY VOTE__ + - __GO BACK__ goes to the previous page with the checkout box in the upper right + - __THIS IS MY VOTE__ submits the ballot -Note that the backend python server side will re-validate the incoming ballot CVR. The __this-is-my-vote__ entrypoint can return various errors to the user: +Note that the backend python server side will re-validate the incoming ballot CVR. The __THIS IS MY VOTE__ entrypoint can return various errors to the user: - a non compliant contest selection was found - there was a problem on the server side @@ -70,8 +49,8 @@ If there is no error, the endpoint returns: - the voter's ballot check as if it were paper (no links) but with the QR code being a valid link (into the voter's backend server's still active git workspace) - the voter's row offset into the ballot check is temporarily displayed - in addition there is, for demo purposes only, two additional buttons on this page - - a __verify-ballot-receipt__ button - - a __tally-contests__ button + - a __VERIFY BALLOT RECEIPT__ button + - a __TALLY CONTESTS__ button ## User Story 3 - inspecting the ballot check/QR code @@ -83,14 +62,14 @@ Displays the git repo copy of the contents of the ballot check. All the digests #### What happens when the user wants to verify their ballot check? -The user clicks the __verify ballot receipt__ (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. +The user clicks the __VERIFY BALLOT RECEIPT__ (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. -The page contains a __re-verify__ button that re-verifies the receipt honoring the new values in the various fields. +The page contains a __RE-VERIFY__ button that re-verifies the receipt honoring the new values in the various fields. ## User Story 5 - tallying the election #### What happens when the user wants to tally the election? -The user clicks the __tally contests__ button (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. +The user clicks the __TALLY CONTESTS__ button (end of User Story 1) and a new page displays the 'console log' output of that function with digests hyperlinked. The output page contains a few by default blank fields that the user can optionally fill in. One of them is a row offset. -The page contains a __re-tally contests__ button that re-tallies the election honoring the new values in the various fields. +The page contains a __RE-TALLY CONTESTS__ button that re-tallies the election honoring the new values in the various fields. diff --git a/docs/ProjectPlan.md b/docs/ProjectPlan.md index ab30fdf..4380510 100644 --- a/docs/ProjectPlan.md +++ b/docs/ProjectPlan.md @@ -1,22 +1,74 @@ -## Project Plan +# VoteTracker+ Client Side UI/UX Project Plan -The basic idea is to stub out static versions of the various pages and write the javascript to support not. Then incrementally add support for a deeper and deeper integration with the web-api. The web-api hopefully will not need major changes. +#### 1. Background -1. Create just the controls and UI without dealing with any JSON data - just get the UI controls going +The current design target is to create a usable client/voter frontend in a few weeks for a June 8th live demo. The date and available skill level are driving the client UI side design and implementation. -2. Add JSON decoding support and integrate with static JSON data. This includes reading and sending JSON. +The current tech choice to achieve this is a plain html, css, and javascript implementation. The current development environment is Google's devtools, Microsoft's Visual Studio Code, and emacs :-). -3. Start plugging in the web-api and to start using actual live JSON data from the web-api +See the VoteTracker+ [project page](https://github.com/orgs/TrustTheVote-Project/projects/2/views/1) for status. -4. Start debugging live tests +#### 2. Overall VTP Git Repo Context -#### Tiemline +Summary of the VTP git repo's: -Working backwards, need to complete all four of the above by end of may. Completion means that the live demo is running without significant issues on a stand along router. Working backwards: +The [VTP-dev-end](https://github.com/TrustTheVote-Project/VTP-dev-env) holds/defines the development/run-time environment. Contained within VTP-dev-end as submodules are three repos: +- [VTP-mock-election.US.15](https://github.com/TrustTheVote-Project/VTP-mock-election.US.15) - contains the election configuration and data +- [VTP-web-api](https://github.com/TrustTheVote-Project/VTP-web-api) - contains the FASTapi rest interface +- [VoteTrackerPlus](https://github.com/TrustTheVote-Project/VoteTrackerPlus) - contains the python backend code -- Completion of #1: 02/21 (1 week) -- Completion of #2: 03/21 (4 weeks) -- Completion of #3: 04/17 (4 weeks) -- Completion of #4: 05/01 (2 weeks) +This repo, [VTP-web-client](https://github.com/TrustTheVote-Project/VTP-web-client) contains the client frontend a.k.a. the voter's /UI/UX + +See [DesignNotes.md](DesignNotes.md) in this repo for details on the end user (voter) user stories + +#### 3. Basic Project Plan + +The basic project plan is to stub out static versions of the various pages and write the javascript to support that. Then incrementally add support for deeper integrations with the restful web-api. The web-api hopefully will not require major work at this point. + +The four major milestones are: + +1. Create just the controls and UI without dealing with any JSON data - just get the basic html/css/JS UI controls for the five web pages (see below) working. + +2. Add JSON decoding support and integrate with static JSON data. This includes reading and sending (static) JSON to a stubbed out backend. + +3. Plug in the web-api and use actual live JSON data from the web-api + +4. Full end-to-end testing of the demo + +#### 4. Timeline + +To achieve the demo all four of the above major milestones need to be completed by end of may. Project completion means that the live demo is running without significant issues on a standalong router disconnected from any WAN. Thus: + +- Completion of milestone 1: 02/21 (1 week) +- Completion of milestone 2: 03/21 (4 weeks) +- Completion of milestone 3: 04/17 (4 weeks) +- Completion of milestone 4: 05/01 (2 weeks) which leaves 3 weeks of buffer + +#### 5. File layout (this git repo <-> end user web server) + +Given the decomposition into [5 user stories](./DesingNotes.md), out-of-the-gate associate different html pages with each user story. + +1. index.html + - the landing page + - one js button that jumps to the (next) voting.html page +2. voting.html + - handles a single contest + - provides navigation to previous, next, other contests (same page), and to the checkout.html page + - provides progress status +3. checkout.html + - allows the voter to double check their ballot prior to casting + - does not support editing the ballot + - provides three options: spoil ballot (exit), edit ballot (back to voting.html), and submit/cast ballot +4. ballot check displaying + - either create the JS to retrieve the data from the web-api (new endpoint), creating an explicit ballot-check.html + - or standup a http server running somewhere that is pointing to the repo of interest for the demo. This would probably require changing the upstream remote git repo URI to the demo LAN local git server, which would be ok as it would completely isolate the demo. +5. verify-ballot-check.html + - handles the output of the ballot check function (basically console log output) + - supplies various end user functions associated with verify their ballot check - a UX TBD +6. tally-election.html + - handles the output of tallying the election (basically console log output) +- initial UX target is to supply a few buttons for enabling switches and converting digests to links (back to the local LAN git server or the upstream remote - see 3 above) + +Regarding css and javascript, created as needed in standard locations. From e5b3c2e7593374fffe6e15ca306016b4f23831dd Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Thu, 29 Feb 2024 01:50:56 -0500 Subject: [PATCH 06/30] initial copies from the odin-recipe repo --- blank-ballot.js | 144 +++++++++++++ stylesheets/voting.css | 70 ++++++ voting.html | 480 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 694 insertions(+) create mode 100644 blank-ballot.js create mode 100644 stylesheets/voting.css create mode 100644 voting.html diff --git a/blank-ballot.js b/blank-ballot.js new file mode 100644 index 0000000..c88e273 --- /dev/null +++ b/blank-ballot.js @@ -0,0 +1,144 @@ +const blankBallotJSON = `{ + "active_ggos": [ + ".", + "GGOs/states/Massachusetts", + "GGOs/states/Massachusetts/GGOs/counties/Middlesex", + "GGOs/states/Massachusetts/GGOs/towns/Concord" + ], + "ballot_filename": "000,001,002,003,ballot.json", + "ballot_node": "GGOs/states/Massachusetts/GGOs/towns/Concord", + "ballot_subdir": "GGOs/states/Massachusetts/GGOs/towns/Concord", + "contests": { + "GGOs/states/Massachusetts": [ + { + "U.S. President": { + "choices": [ + { + "name": "Circle Party Ticket", + "ticket_names": [ + "Rey Skywalker", + "Obi-Wan Kenobi" + ] + }, + { + "name": "Square Party Ticket", + "ticket_names": [ + "Atticus Finch", + "Hermione Granger" + ] + }, + { + "name": "Triangle Party Ticket", + "ticket_names": [ + "Evelyn Quan Wang", + "Waymond Wang" + ] + } + ], + "contest_type": "ticket", + "max": 1, + "selection": [ + "0: Circle Party Ticket" + ], + "tally": "plurality", + "ticket_offices": [ + "President", + "Vice President" + ], + "uid": "0000" + } + }, + { + "U.S. Senate": { + "choices": [ + { + "name": "Anthony Alpha", + "party": "Circle Party" + }, + { + "name": "Betty Beta", + "party": "Dyad Party" + }, + { + "name": "Gloria Gamma", + "party": "Triangle Party" + }, + { + "name": "David Delta", + "party": "Square Party" + }, + { + "name": "Emily Echo", + "party": "Pentagon Party" + }, + { + "name": "Francis Foxtrot", + "party": "Hexagon Party" + } + ], + "selection": [ + "5: Francis Foxtrot", + "4: Emily Echo", + "3: David Delta" + ], + "tally": "rcv", + "uid": "0001" + } + }, + { + "Governor": { + "choices": [ + { + "name": "Spencer Cogswell", + "party": "Circle Party" + }, + { + "name": "Cosmo Spacely", + "party": "Triangle Party" + } + ], + "max": 1, + "selection": [ + "1: Cosmo Spacely" + ], + "tally": "plurality", + "uid": "0002" + } + } + ], + "GGOs/states/Massachusetts/GGOs/counties/Middlesex": [ + { + "County Clerk": { + "choices": [ + "Jean-Luc Picard", + "Katniss Everdeen", + "James T. Kirk" + ], + "max": 1, + "selection": [ + "0: Jean-Luc Picard" + ], + "tally": "plurality", + "uid": "0003" + } + } + ], + "GGOs/states/Massachusetts/GGOs/towns/Concord": [ + { + "Question 1 - should the starting time of the annual town meeting be moved to 6:30 PM?": { + "choices": [ + "yes", + "no" + ], + "description": "Should the Town of Concord start the annual Town Meeting at 6:30 PM instead of 7:00 PM?", + "max": 1, + "selection": [ + "0: yes" + ], + "tally": "plurality", + "uid": "0004" + } + } + ] + } +}`; diff --git a/stylesheets/voting.css b/stylesheets/voting.css new file mode 100644 index 0000000..be277ab --- /dev/null +++ b/stylesheets/voting.css @@ -0,0 +1,70 @@ +/* Set the overall styles for the progress bar */ +#progressBar { + display: flex; + height: 20px; + width: 100%; + text-align: center; + background-color: #E5E4E2; +} +#youAreHereBar { + display: flex; + height: 20px; + width: 100%; + text-align: center; +} + +/* Each section (class) gets an individual black border */ +.progSection { + flex: 1; /* Equal distribution of space */ + border-right: 1px solid black; +} +.yrhSection { + flex: 1; /* Equal distribution of space */ +} + +/* when a plurality candidate is selected or not */ +.selected { + background-color: #D5F5E3; +} +.unselected { + background-color: #f5f5f5; +} + +/* the incoming choices */ +#choiceList li { + margin: 10px; + padding: 15px; + border: 1px solid #dfdfdf; + list-style-type: none; +} + +/* in RCV the selection is a sortable list - the defaults */ +#sortableList { + list-style: none; + padding: 0; + margin: 0; +} + +#sortableList li { + margin: 10px; + padding: 15px; + border: 1px solid #dfdfdf; + background: #f5f5f5; +} + +#sortableList li.hint { + border: 1px solid #ffc49a; + background: #feffb4; +} + +#sortableList li.active { + border: 1px solid #ffa5a5; + background: #ffe7e7; +} + +/* bottom section */ +.noBullets { + list-style-type: none; +} + + diff --git a/voting.html b/voting.html new file mode 100644 index 0000000..56b43fc --- /dev/null +++ b/voting.html @@ -0,0 +1,480 @@ + + + + + + + + Voting - User Story 2 + + + + +
+
+ +

Progress bar per contest and current contest

+ + +
+ + +
+ + +
+ + + + + + + From 21c6a9b2ba1c3ffc957731bafda1386f258eb4bb Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Thu, 29 Feb 2024 11:58:12 -0500 Subject: [PATCH 07/30] fixing missing choices when choice is not a dictionary --- .gitignore | 16 ++++++++++++++++ Makefile | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3409fdb --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Emacs +*~ +.#* +\#*\# +TAGS + +# Mac +.DS_Store + +# pytest +__pycache__ +.pytest_cache + +# Ignore python local install build symlinks et al +.venv +poetry.lock diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8e07f61 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +# Ancient Makefile implicit rule disabler +(%): % +%:: %,v +%:: RCS/%,v +%:: s.% +%:: SCCS/s.% +%.out: % +%.c: %.w %.ch +%.tex: %.w %.ch +%.mk: + +# Variables +DOC_DIR := docs +SRC_DIR := . +TEST_DIR := tests + +# Use colors for errors and warnings when in an interactive terminal +INTERACTIVE := $(shell test -t 0 && echo 1) +ifdef INTERACTIVE + RED := \033[0;31m + END := \033[0m +else + RED := + END := +endif + +# Let there be no default target +.PHONY: default +default: + @echo "${RED}There is no default make target.${END} Specify one of:" + @echo "etags - constructs an emacs tags table" + @echo "" + @echo "See ${BUILD_DIR}/README.md for more details and info" + +# emacs tags (for javascript need GNU's universal-ctag package) +.PHONY: etags +etags: + /opt/homebrew/bin/ctags -e -R From afb2f43dcaef9a00b37d8c0485cfb84649b68180 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Thu, 29 Feb 2024 13:02:29 -0500 Subject: [PATCH 08/30] cleaning up some annoying UI bugs --- Makefile | 2 +- voting.html | 70 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 8e07f61..fedf9ef 100644 --- a/Makefile +++ b/Makefile @@ -35,4 +35,4 @@ default: # emacs tags (for javascript need GNU's universal-ctag package) .PHONY: etags etags: - /opt/homebrew/bin/ctags -e -R + /opt/homebrew/bin/ctags -e -R --language-force=javascript diff --git a/voting.html b/voting.html index 56b43fc..139bc86 100644 --- a/voting.html +++ b/voting.html @@ -154,19 +154,20 @@ } // Will set up the upperSection - function setUpperSection(contestType, maxSelection) { + function setUpperSection(thisContestName, thisContestValue) { const rootElement = document.getElementById("upperSection"); const newItem = document.createElement("span"); - if (contestType == "plurality") { - let innerText = "

A plurality contest:

  • Make you selection by clicking. Click again to unselect.
  • "; - if (maxSelection == 1) { + const max = thisContestValue.max; + if (thisContestValue.tally == "plurality") { + let innerText = "

    " + thisContestName + "

    A plurality contest:

    • Make you selection by clicking. Click again to unselect.
    • "; + if (max == 1) { innerText += "
    • You can only make one selection
    "; } else { - innerText += "
  • You can choose upto " + maxSelection + "
"; + innerText += "
  • You can choose upto " + max + "
  • "; } newItem.innerHTML = innerText; } else { - innerText = `

    A RCV (IRV) contest:

    + innerText = "

    " + thisContestName + `

    RCV (IRV) contest:

    • Clicking a candidate will add it to your RCV
    • Your RCV selection is re-orderable by drag-and-drop
    • @@ -183,12 +184,12 @@

      Your RCV selection:

      } // Will set up the lowerSection - function setLowerSection(choiceType) { + function setLowerSection(thisContestValue) { const rootElement = document.getElementById("lowerSection"); const newItem = document.createElement("span"); - if (choiceType == "ticket") { - newItem.innerHTML = "

      Candidates:

      "; - } else if (choiceType == "question") { + if (thisContestValue.contest_type == "ticket") { + newItem.innerHTML = "

      Candidates:

      "; + } else if (thisContestValue.contest_type == "question") { newItem.innerHTML = "

      Your selection:

      "; } else { newItem.innerHTML = "

      Candidates:

      "; @@ -200,9 +201,12 @@

      Your RCV selection:

      } // Setup the choiceList - function setupChoiceList(choices, choiceType, ticketTitles) { + function setupChoiceList(thisContestName, thisContestValue, overrideChoice=null) { const rootElement = document.getElementById("choiceList"); - for (let choice of choices) { + for (let choice of thisContestValue.choices) { + if (overrideChoice) { + choice = overrideChoice; + } const newItem = document.createElement("li"); newItem.classList.add("flex-item"); // Apply a class for styling @@ -216,12 +220,18 @@

      Your RCV selection:

      // Create the text element const textElement = document.createElement("span"); - textElement.textContent = choice.name; - if (choiceType == "ticket") { + // Ugh - need to inspect for the choices data type + if (choice.name) { + textElement.textContent = choice.name; + } else { + // Just an array of strings + textElement.textContent = choice; + } + if (thisContestValue.contest_type == "ticket") { // Note - need to add this as an additional flex-box // so that the 'name' matches the choice let addendum = []; - for (let office of ticketTitles) { + for (let office of thisContestValue.ticket_offices) { addendum.push(office + ":" + choice.ticket_names); } textElement.textContent += "[" + addendum.join(", ") + "]"; @@ -233,6 +243,10 @@

      Your RCV selection:

      newItem.appendChild(svgIcon); newItem.appendChild(textElement); rootElement.appendChild(newItem); + + if (overrideChoice) { + break; + } } } @@ -298,7 +312,7 @@

      Your RCV selection:

      } // RCV event listeners - function setupRCVEventListeners(maxCount) { + function setupRCVEventListeners(thisContestName, thisContestValue) { console.log("Running setupRCVEventListeners:"); // Event listener for selection in the first list (when a candidate is selected) console.log("Running setupRCVEventListeners:"); @@ -316,14 +330,12 @@

      Your RCV selection:

      // add an event listener to the button newButton.addEventListener("click", function (e) { console.log("Running RCV eventListener:"); - var itemName = e.target.parentNode.textContent.trim().replace(/ remove$/, "");; + let itemName = e.target.parentNode.textContent.trim().replace(/ remove$/, ""); console.log("removing:", itemName); // remove it from sortableList e.target.parentNode.remove(); // add to choiceList - const listItem = document.createElement("li"); - listItem.textContent = itemName; - choiceList.appendChild(listItem); + setupChoiceList(thisContestName, thisContestValue, itemName); }); // Create a re-order glyph @@ -359,7 +371,7 @@

      Your RCV selection:

      } // plurality event listeners - function setupPluralityEventListeners(maxCount) { + function setupPluralityEventListeners(thisContestName, thisContestValue) { console.log("Running setupPluralityEventListeners:"); let selectedCount = 0; choiceList.addEventListener("click", (event) => { @@ -376,7 +388,7 @@

      Your RCV selection:

      selectedCount--; // get the svg (first child) and set fill to off console.log("de-selected " + itemIndex + ", " + itemText); - } else if (selectedCount < maxCount) { + } else if (selectedCount < thisContestValue.max) { // Select the item (up to maxSelection selections allowed) listItem.classList.add("selected"); listItem.classList.remove("unselected"); @@ -444,10 +456,8 @@

      Your RCV selection:

      setActiveContest(thisContest); // Setup the upper and lower sections - let contestType = thisContestValue.tally; - let choiceType = thisContestValue.contest_type; - setUpperSection(contestType, thisContestValue.max); - setLowerSection(choiceType); + setUpperSection(thisContestName, thisContestValue); + setLowerSection(thisContestValue); // Note - for the moment let these be globals (until we know more). // Regardless, the upper/lower setup will make sure choiceList and @@ -457,11 +467,11 @@

      Your RCV selection:

      removeButtons = document.getElementsByClassName("remove"); // Setup the choiceList - setupChoiceList(thisContestValue.choices, choiceType, thisContestValue.ticket_offices); - if (contestType == "plurality") { - setupPluralityEventListeners(thisContestValue.max); + setupChoiceList(thisContestName, thisContestValue); + if (thisContestValue.tally == "plurality") { + setupPluralityEventListeners(thisContestName, thisContestValue); } else { - setupRCVEventListeners(thisContestValue.max); + setupRCVEventListeners(thisContestName, thisContestValue); } // Setup the bottomSection - this supplies simply "next context/checkout" From 33cf2cd3113bf8766af315ef219a7383fcc99087 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Thu, 29 Feb 2024 13:20:32 -0500 Subject: [PATCH 09/30] cleaning up some kerning issues --- voting.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/voting.html b/voting.html index 139bc86..b068678 100644 --- a/voting.html +++ b/voting.html @@ -190,9 +190,9 @@

      Your RCV selection:

      if (thisContestValue.contest_type == "ticket") { newItem.innerHTML = "

      Candidates:

      "; } else if (thisContestValue.contest_type == "question") { - newItem.innerHTML = "

      Your selection:

      "; + newItem.innerHTML = "

      Your selection:

      "; } else { - newItem.innerHTML = "

      Candidates:

      "; + newItem.innerHTML = "

      Candidates:

      "; } const newList = document.createElement("ul"); newList.setAttribute("id", "choiceList"); @@ -222,10 +222,10 @@

      Your RCV selection:

      const textElement = document.createElement("span"); // Ugh - need to inspect for the choices data type if (choice.name) { - textElement.textContent = choice.name; + textElement.innerHTML = "  " + choice.name; } else { // Just an array of strings - textElement.textContent = choice; + textElement.innerHTML = "  " + choice; } if (thisContestValue.contest_type == "ticket") { // Note - need to add this as an additional flex-box @@ -234,7 +234,7 @@

      Your RCV selection:

      for (let office of thisContestValue.ticket_offices) { addendum.push(office + ":" + choice.ticket_names); } - textElement.textContent += "[" + addendum.join(", ") + "]"; + textElement.innerHTML += "  [" + addendum.join(", ") + "]"; } // Add the unselected class newItem.classList.add("unselected"); @@ -330,7 +330,7 @@

      Your RCV selection:

      // add an event listener to the button newButton.addEventListener("click", function (e) { console.log("Running RCV eventListener:"); - let itemName = e.target.parentNode.textContent.trim().replace(/ remove$/, ""); + let itemName = e.target.parentNode.textContent.trim().replace(/\sremove$/, ""); console.log("removing:", itemName); // remove it from sortableList e.target.parentNode.remove(); @@ -352,7 +352,7 @@

      Your RCV selection:

      // Create the text element const textElement = document.createElement("span"); - textElement.textContent = selectedText + " "; + textElement.innerHTML = selectedText + "  "; // Append everything ... newItem.appendChild(svgIcon); From 6e5d2d42c74663da39aa490d0b059ba13ff303e6 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Thu, 29 Feb 2024 17:44:45 -0500 Subject: [PATCH 10/30] initial pass at a landing page stub - TBD if it works --- index.html | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 0000000..a130e0a --- /dev/null +++ b/index.html @@ -0,0 +1,34 @@ + + + + + + Landing Page - User Story 1 + + + +

      Welcome to a VoteTrackr+ Demo

      +

      This is a simulated conceptual demo of what voting in an election that is + integrated with the VoteTrackr+ technology

      +

      You may vote as many times as you would like

      +

      Please click the continue button to start voting

      +

      +

      +

      + + + + + From 9edff2eaf52ce320403cf2b74dd66832b5d021eb Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Fri, 1 Mar 2024 12:02:11 -0500 Subject: [PATCH 11/30] cut/paste/edit error --- index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/index.html b/index.html index a130e0a..5ecb4a2 100644 --- a/index.html +++ b/index.html @@ -25,7 +25,6 @@

      Welcome to a VoteTrackr+ Demo

      + Voting - User Story 2 diff --git a/voting.html b/voting.html index e116de9..61120e5 100644 --- a/voting.html +++ b/voting.html @@ -510,12 +510,28 @@

      Your RCV selection:

      if (blankBallot.contests[ggo]) { for (const contest of blankBallot.contests[ggo]) { const newItem = document.createElement("li"); + // a flexbox container + newItem.classList.add("selectionFlexContainer"); + // flexboxes + const leftSide = document.createElement("div"); + leftSide.classList.add("leftItems"); + const box1 = document.createElement("div"); + box1.classList.add("leftItem"); + const box2 = document.createElement("div"); + box2.classList.add("leftItem"); + const box3 = document.createElement("div"); + box3.classList.add("rightItems"); const contestName = Object.keys(contest)[0]; const selections = Object.values(contest)[0].selection; index += 1; - console.log("contest " + index + ", selection = " + selections); - newItem.innerHTML = "Contest " + index + ":  " + contestName; - newItem.innerHTML += "

      " + selections.join(" 
      "); + console.log("contest " + index + " (" + contestName + "), selection = " + selections); + box1.innerHTML = "Contest " + index + ":  " + contestName; + if (selections.length == 0) { + box2.innerHTML = "no selection - skipped"; + box2.classList.add("novoted"); + } else { + box2.innerHTML = selections.join("
      "); + } // Create the goto button const gotoButton = document.createElement("button"); gotoButton.innerText = "Edit Contest " + index; @@ -531,9 +547,13 @@

      Your RCV selection:

      const buttonIdString = Number(this.id.match(/\d+$/)) - 1; setupNewContest(buttonIdString); }); + box3.appendChild(gotoButton); // add the above to lowerSection.choiceList + leftSide.appendChild(box1); + leftSide.appendChild(box2); + newItem.appendChild(leftSide); + newItem.appendChild(box3); rootElement.appendChild(newItem); - rootElement.appendChild(gotoButton); } } } From 7429577c304edde3afc2952ff0ed15400418f692 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Tue, 5 Mar 2024 02:26:18 -0500 Subject: [PATCH 19/30] checkpoint on better progressBar color handling --- stylesheets/voting.css | 21 ++++++++++++++++++--- voting.html | 35 +++++++++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/stylesheets/voting.css b/stylesheets/voting.css index 22322ec..30e76e9 100644 --- a/stylesheets/voting.css +++ b/stylesheets/voting.css @@ -14,11 +14,26 @@ } /* colors for completely voted and undervoted and novoted */ -.novoted { +.novotedText { + color: #FF6B6B; } -.undervoted { +.undervotedText { + color: #FFD166; } -.voted { +.votedText { + color: #6EE7B7; +} +.novotedBG { + background-color: #FF6B6B; +} +.undervotedBG { + background-color: #FFD166; +} +.votedBG { + background-color: #6EE7B7; +} +.activeContest { + background-color: white; } /* Each section (class) gets an individual black border */ diff --git a/voting.html b/voting.html index 61120e5..c9eb823 100644 --- a/voting.html +++ b/voting.html @@ -109,6 +109,8 @@ yrhIcon.innerHTML = ``; // Will set up the progress bars with numberOfContests contests + // NOTE - the printed/user-visible contest numbers are 1 based while + // everything else (the backend, internal, etc) is 0 based. function setupProgressBars(numberOfContests) { const progBarElement = document.getElementById("progressBar"); const yrhBarElement = document.getElementById("youAreHereBar"); @@ -141,12 +143,18 @@ } // Will set the color of the progress bar (per contest) + // NOTE - contestNum is zero based function setProgressBarColor(contestNum, color) { const sectionElement = document.getElementById("progBar" + contestNum); - sectionElement.style.backgroundColor = color; + // Need to clear out all existing background styles + for (const color of ["novotedBG", "undervotedBG", "votedBG", "activeContest"]) { + sectionElement.classList.remove(color) + } + sectionElement.classList.add(color); } // Will set the contents of the youAreHereBar bar (per contest) + // NOTE - contestNum is zero based function setActiveContest(contestNum) { const sectionElement = document.getElementById("yrhBar" + contestNum); const newElement = document.createElement("span"); @@ -466,16 +474,32 @@

      Your RCV selection:

      index += 1; } thisContestValue["selection"] = selection; + // and setting the progressBar color + let max = thisContestValue.max; + let contestNum = nextContest - 1; + if (!max) { + max = thisContestValue.choices.length; + } + console.log(""); + if (selection.length == 0) { + console.log("Contest " + contestNum + " no voted"); + setProgressBarColor(contestNum, "novotedBG"); + } else if (max == selection.length) { + console.log("Contest " + contestNum + " voted"); + setProgressBarColor(contestNum, "votedBG"); + } else { + console.log("Contest " + contestNum + " undervoted voted"); + setProgressBarColor(contestNum, "undervotedBG"); + } } // 2) clearing out the upper and lower node DOM trees document.getElementById("upperSection").replaceChildren(); document.getElementById("lowerSection").replaceChildren(); document.getElementById("bottomSection").replaceChildren(); + // 3) Going somewhere if (nextContest < numberOfContests) { - // 3a) go to next contest setupNewContest(nextContest); } else { - // 3b) go to checkout setupCheckout(); } }); @@ -490,7 +514,6 @@

      Your RCV selection:

      function setupCheckout() { console.log("setupCheckout: setting up checkout page"); // 1) adjust the progress bars - setProgressBarColor(numberOfContests, "#D5F5E3"); setActiveContest(numberOfContests); // 2) loop over the voters selections per contest and create a @@ -528,7 +551,7 @@

      Your RCV selection:

      box1.innerHTML = "Contest " + index + ":  " + contestName; if (selections.length == 0) { box2.innerHTML = "no selection - skipped"; - box2.classList.add("novoted"); + box2.classList.add("novotedText"); } else { box2.innerHTML = selections.join("
      "); } @@ -581,7 +604,7 @@

      Your RCV selection:

      let thisContestValue = Object.values(listOfContests[thisContest])[0]; // and initialize them - setProgressBarColor(thisContest, "#D5F5E3"); + setProgressBarColor(thisContest, "activeContest"); setActiveContest(thisContest); // Setup the upper and lower sections From c56d27a67343083ee9b4fd03285075853009eda8 Mon Sep 17 00:00:00 2001 From: Sandy Currier Date: Tue, 5 Mar 2024 03:06:06 -0500 Subject: [PATCH 20/30] more layout cleanup --- voting.html | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/voting.html b/voting.html index c9eb823..d643831 100644 --- a/voting.html +++ b/voting.html @@ -39,8 +39,6 @@
      -

      Progress bar per contest and current contest

      -