Compare commits

...

24 commits

Author SHA1 Message Date
d44c3b6f0b Add missing GET 2025-12-04 18:31:06 -06:00
61b5882d48 Validate new organizer 2025-12-04 18:30:58 -06:00
e4df728a04 Validate email address 2025-12-04 17:41:28 -06:00
55a5801da4 Add hint about Markdown 2025-12-04 17:27:13 -06:00
243d27781d Put multiple actions on the same line 2025-12-04 17:17:12 -06:00
cc6a684bc6 Display vote title 2025-12-04 16:59:46 -06:00
0b08485ffc Highlight winner 2025-12-04 16:47:39 -06:00
25c3650e59 Update translations 2025-12-04 02:53:53 +01:00
d5e7f6c4b3 Reload form with same data 2025-12-04 02:53:31 +01:00
ecbf830e81 Reorder as in form 2025-12-04 02:52:25 +01:00
b2e76f4363 Validate candidate name 2025-12-04 02:23:04 +01:00
f8b5f80823 Reload form with same data 2025-12-04 02:04:36 +01:00
78abcce758 Validate vote title 2025-12-04 01:47:17 +01:00
4ad1c4cb86 Use <p> consistently for form alerts 2025-12-04 01:46:23 +01:00
0ddb1364bc Simplify 2025-12-04 01:45:59 +01:00
a5a9e92e18 Allow editing candidates 2025-12-04 01:26:32 +01:00
62e70e3935 Make sure the candidate belongs to the vote 2025-12-04 01:25:18 +01:00
5a278fcddf Rewrite views with Bootstrap 2025-12-04 01:03:46 +01:00
529f7118b1 Fix using format_date on first page after login 2025-12-03 22:41:25 +01:00
134ebec01f Use Bootstrap badge 2025-12-03 19:02:45 +01:00
d87d191cc9 Add Bootstrap header and navigation 2025-12-03 18:49:39 +01:00
be9c415048 Move editing the vote description to a dedicated page
So that /votes/:id remains a preview page, even for organizers.
2025-12-03 18:05:54 +01:00
888dcc1a99 Allow spoofing admin in development environment 2025-12-03 17:49:59 +01:00
1d4f7af63d Simplify 2025-10-13 20:22:45 +02:00
29 changed files with 15125 additions and 600 deletions

View file

@ -93,9 +93,6 @@ GEM
singleton (0.3.0) singleton (0.3.0)
sqlite3 (2.6.0) sqlite3 (2.6.0)
mini_portile2 (~> 2.8.0) mini_portile2 (~> 2.8.0)
sqlite3 (2.6.0-arm64-darwin)
sqlite3 (2.6.0-x86_64-darwin)
sqlite3 (2.6.0-x86_64-linux-gnu)
text (1.3.1) text (1.3.1)
tilt (2.6.0) tilt (2.6.0)
timeout (0.4.3) timeout (0.4.3)

View file

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-10-13 20:19+0200\n" "POT-Creation-Date: 2025-12-04 17:25-0600\n"
"PO-Revision-Date: 2025-03-29 20:41-0600\n" "PO-Revision-Date: 2025-03-29 20:41-0600\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -41,16 +41,16 @@ msgstr "Bé"
msgid "Very good" msgid "Very good"
msgstr "Molt bé" msgstr "Molt bé"
#: ../vedia.rb:127 #: ../vedia.rb:125
msgid "Incorrect email or password." msgid "Incorrect email or password."
msgstr "Correu o contrasenya incorrecte." msgstr "Correu o contrasenya incorrecte."
#: ../vedia.rb:149 #: ../vedia.rb:147
msgid "Reset your password" msgid "Reset your password"
msgstr "Reiniciar contrasenya" msgstr "Reiniciar contrasenya"
#: ../views/admin.erb:1 ../views/admin_users.erb:1 ../views/admin_votes.erb:1 #: ../views/admin.erb:1 ../views/admin_users.erb:1 ../views/admin_votes.erb:1
#: ../views/layout.erb:26 #: ../views/layout.erb:35
msgid "Admin" msgid "Admin"
msgstr "Admin" msgstr "Admin"
@ -58,21 +58,22 @@ msgstr "Admin"
msgid "Users" msgid "Users"
msgstr "Usuàries" msgstr "Usuàries"
#: ../views/admin.erb:13 ../views/home.erb:3 #: ../views/admin.erb:13 ../views/layout.erb:31 ../views/votes.erb:1
msgid "Votes" msgid "Votes"
msgstr "Votacions" msgstr "Votacions"
#: ../views/admin.erb:21 ../views/home.erb:12 ../views/votes_show_draft.erb:1 #: ../views/admin.erb:29 ../views/votes.erb:11 ../views/votes_edit.erb:5
msgid "(Draft)" #: ../views/votes_open.erb:5 ../views/votes_show_draft.erb:5
msgstr "(Esborrany)" msgid "Draft"
msgstr "Esborrany"
#: ../views/admin.erb:23 ../views/home.erb:16 #: ../views/admin.erb:31 ../views/votes.erb:13 ../views/votes_show_open.erb:5
msgid "(Open)" msgid "Open"
msgstr "(Oberta)" msgstr "Oberta"
#: ../views/admin.erb:25 ../views/home.erb:20 #: ../views/admin.erb:33 ../views/votes.erb:15 ../views/votes_show_closed.erb:5
msgid "(Closed)" msgid "Closed"
msgstr "(Tancada)" msgstr "Tancada"
#: ../views/admin_users.erb:5 ../views/admin_votes.erb:7 #: ../views/admin_users.erb:5 ../views/admin_votes.erb:7
msgid "Created: %{date}" msgid "Created: %{date}"
@ -100,7 +101,7 @@ msgstr "Votacions organitzades"
#: ../views/admin_users.erb:19 ../views/admin_users.erb:38 #: ../views/admin_users.erb:19 ../views/admin_users.erb:38
#: ../views/admin_votes.erb:27 ../views/admin_votes.erb:46 #: ../views/admin_votes.erb:27 ../views/admin_votes.erb:46
#: ../views/votes_edit.erb:21 #: ../views/votes_edit.erb:32
msgid "Delete" msgid "Delete"
msgstr "Suprimir" msgstr "Suprimir"
@ -109,6 +110,7 @@ msgid "No vote organized."
msgstr "Cap votació organitzada." msgstr "Cap votació organitzada."
#: ../views/admin_users.erb:30 ../views/admin_votes.erb:38 #: ../views/admin_users.erb:30 ../views/admin_votes.erb:38
#: ../views/votes_show_closed.erb:89
msgid "Ratings" msgid "Ratings"
msgstr "Valoracions" msgstr "Valoracions"
@ -126,14 +128,13 @@ msgstr "Veure votació"
#: ../views/admin_votes.erb:11 #: ../views/admin_votes.erb:11
msgid "Secure ID: %{secure_id}" msgid "Secure ID: %{secure_id}"
msgstr "ID segura" msgstr "ID segura: %{secure_id}"
#: ../views/admin_votes.erb:13 #: ../views/admin_votes.erb:13
msgid "Description: %{description}" msgid "Description: %{description}"
msgstr "Descripció: %{description}" msgstr "Descripció: %{description}"
#: ../views/admin_votes.erb:15 ../views/votes_open.erb:3 #: ../views/admin_votes.erb:15 ../views/votes_open.erb:12
#: ../views/votes_show_closed.erb:4 ../views/votes_show_open.erb:12
msgid "Closing date: %{date}" msgid "Closing date: %{date}"
msgstr "Data de tancament: %{date}" msgstr "Data de tancament: %{date}"
@ -145,9 +146,9 @@ msgstr "Cap"
msgid "State: %{state}" msgid "State: %{state}"
msgstr "Estat: %{state}" msgstr "Estat: %{state}"
#: ../views/admin_votes.erb:19 ../views/votes_edit.erb:39 #: ../views/admin_votes.erb:19 ../views/votes_edit.erb:90
#: ../views/votes_show_closed.erb:79 ../views/votes_show_draft.erb:5 #: ../views/votes_show_closed.erb:123 ../views/votes_show_draft.erb:29
#: ../views/votes_show_open.erb:54 #: ../views/votes_show_open.erb:79
msgid "Organizers" msgid "Organizers"
msgstr "Organitzadores" msgstr "Organitzadores"
@ -155,45 +156,50 @@ msgstr "Organitzadores"
msgid "No organizer." msgid "No organizer."
msgstr "Cap organitzadora." msgstr "Cap organitzadora."
#: ../views/admin_votes.erb:60 ../views/votes_edit.erb:68 #: ../views/admin_votes.erb:60 ../views/votes_edit.erb:86
msgid "Delete vote" msgid "Delete vote"
msgstr "Suprimir la votació" msgstr "Suprimir la votació"
#: ../views/home.erb:1 ../views/layout.erb:24 #: ../views/candidates_edit.erb:1
msgid "Home" msgid "Edit candidate"
msgstr "Inici" msgstr "Edita la opció"
#: ../views/home.erb:11 #: ../views/candidates_edit.erb:6 ../views/votes_edit.erb:48
msgid "(Draft, organized by you)" msgid "Enter a name."
msgstr "(Esborrany, organitzada per tu)" msgstr "Entra un nom."
#: ../views/home.erb:15 #: ../views/candidates_edit.erb:13 ../views/votes_edit.erb:54
msgid "(Open, organized by you)" msgid "Name"
msgstr "(Oberta, organitzada per tu)" msgstr "Nom"
#: ../views/home.erb:19 #: ../views/candidates_edit.erb:17 ../views/votes_edit.erb:58
msgid "(Closed, organized by you)" #: ../views/votes_edit_description.erb:17 ../views/votes_new.erb:17
msgstr "(Tancada, organitzada per tu)" msgid "Description"
msgstr "Descripció"
#: ../views/home.erb:28 ../views/votes_new.erb:12 #: ../views/candidates_edit.erb:19 ../views/votes_edit.erb:60
msgid "Create new vote" #: ../views/votes_edit_description.erb:19 ../views/votes_new.erb:19
msgstr "Crear una nova votació" msgid ""
"You can use <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>"
"."
msgstr ""
"Pots fer servir <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>."
#: ../views/layout.erb:22 #: ../views/candidates_edit.erb:21 ../views/votes_edit_description.erb:21
msgid "Logged in as %{email}." msgid "Save"
msgstr "Estàs connectada com a %{email}." msgstr "Guardar"
#: ../views/layout.erb:22 #: ../views/layout.erb:40
msgid "Logout" msgid "Logout"
msgstr "Desconnexió" msgstr "Desconnexió"
#: ../views/layout.erb:30 ../views/login.erb:1 ../views/login.erb:16 #: ../views/login.erb:1 ../views/login.erb:16
msgid "Login" msgid "Login"
msgstr "Connexió" msgstr "Connexió"
#: ../views/login.erb:9 ../views/reset.erb:5 ../views/reset_change.erb:13 #: ../views/login.erb:9 ../views/reset.erb:5 ../views/reset_change.erb:13
#: ../views/signup.erb:24 ../views/votes_edit.erb:51 #: ../views/signup.erb:24 ../views/votes_edit.erb:102
#: ../views/votes_show_closed.erb:99 ../views/votes_show_open.erb:80 #: ../views/votes_show_closed.erb:137 ../views/votes_show_open.erb:95
msgid "Email" msgid "Email"
msgstr "Correu" msgstr "Correu"
@ -211,8 +217,8 @@ msgstr "Crear un compte"
msgid "Reset password" msgid "Reset password"
msgstr "Reiniciar contrasenya" msgstr "Reiniciar contrasenya"
#: ../views/reset_change.erb:6 ../views/signup.erb:6 #: ../views/reset_change.erb:6 ../views/signup.erb:17
msgid "Specify a password." msgid "Enter a password."
msgstr "Entra una contrasenya." msgstr "Entra una contrasenya."
#: ../views/reset_email.erb:1 #: ../views/reset_email.erb:1
@ -239,14 +245,23 @@ msgstr ""
msgid "If you don't receive the email, please check your spam folder." msgid "If you don't receive the email, please check your spam folder."
msgstr "Si no reps l'email, revisa el teu correu brossa." msgstr "Si no reps l'email, revisa el teu correu brossa."
#: ../views/signup.erb:14 #: ../views/signup.erb:6
msgid "Email is not a valid email address." msgid "Email is not a valid email address."
msgstr "El correu no és una direcció de correu vàlida." msgstr "El correu no és una direcció de correu vàlida."
#: ../views/signup.erb:17 #: ../views/signup.erb:9
msgid "An account already exists for %{email}." msgid "An account already exists for %{email}."
msgstr "Un compte ja existeix pel correu %{email}." msgstr "Un compte ja existeix pel correu %{email}."
#: ../views/votes.erb:18 ../views/votes_edit.erb:6
#: ../views/votes_show_closed.erb:7
msgid "Organizer"
msgstr "Organitzadora"
#: ../views/votes.erb:28 ../views/votes_new.erb:21
msgid "Create new vote"
msgstr "Crear una nova votació"
#: ../views/votes_close_email.erb:1 #: ../views/votes_close_email.erb:1
msgid "Results:" msgid "Results:"
msgstr "Resultats:" msgstr "Resultats:"
@ -255,65 +270,59 @@ msgstr "Resultats:"
msgid "Visit this link to see the full results:" msgid "Visit this link to see the full results:"
msgstr "Visita aquest enllaç per veure els resultats complets:" msgstr "Visita aquest enllaç per veure els resultats complets:"
#: ../views/votes_edit.erb:1 #: ../views/votes_edit.erb:16 ../views/votes_edit.erb:29
msgid "Edit draft vote" msgid "Edit"
msgstr "Editar un esborrany de votació" msgstr "Editar"
#: ../views/votes_edit.erb:5 ../views/votes_new.erb:5 #: ../views/votes_edit.erb:20 ../views/votes_open.erb:36
msgid "Title" #: ../views/votes_show_closed.erb:22 ../views/votes_show_draft.erb:16
msgstr "Títol" #: ../views/votes_show_open.erb:19
#: ../views/votes_edit.erb:9 ../views/votes_edit.erb:33
#: ../views/votes_new.erb:9
msgid "Description"
msgstr "Descripció"
#: ../views/votes_edit.erb:12 ../views/votes_show_open.erb:41
msgid "Save"
msgstr "Guardar"
#: ../views/votes_edit.erb:15 ../views/votes_open.erb:23
#: ../views/votes_show_draft.erb:13
msgid "Candidates" msgid "Candidates"
msgstr "Opcions" msgstr "Opcions"
#: ../views/votes_edit.erb:25 ../views/votes_edit.erb:36 #: ../views/votes_edit.erb:41
msgid "Add candidate" msgid "Add candidate"
msgstr "Afegeix una opció" msgstr "Afegeix una opció"
#: ../views/votes_edit.erb:29 #: ../views/votes_edit.erb:43
msgid "Name" msgid "This vote has no candidates yet. Add a first candidate."
msgstr "Nom" msgstr "Aquesta votació no te cap opció encara. Afegeix una primera opció."
#: ../views/votes_edit.erb:47 ../views/votes_edit.erb:54 #: ../views/votes_edit.erb:63 ../views/votes_edit.erb:65
#: ../views/votes_show_closed.erb:95 ../views/votes_show_closed.erb:102 msgid "Add"
#: ../views/votes_show_open.erb:76 ../views/votes_show_open.erb:83 msgstr "Afegir"
msgid "Add organizer"
msgstr "Afegeix organitzadora"
#: ../views/votes_edit.erb:57 ../views/votes_show_closed.erb:89 #: ../views/votes_edit.erb:72
#: ../views/votes_show_open.erb:66 msgid "Add at least 2 candidates before opening the vote to participants."
msgid "Actions for organizers" msgstr "Afegeix com a mínim 2 opcions abans d'obrir la votació als votants."
msgstr "Accions per les organitzadores"
#: ../views/votes_edit.erb:61 ../views/votes_edit.erb:64 #: ../views/votes_edit.erb:80 ../views/votes_open.erb:21
#: ../views/votes_open.erb:12
msgid "Open vote to participants" msgid "Open vote to participants"
msgstr "Obrir la votació als votants" msgstr "Obrir la votació als votants"
#: ../views/votes_edit.erb:62 #: ../views/votes_edit.erb:98 ../views/votes_edit.erb:105
msgid "Add at least 2 candidates before opening the vote to participants." #: ../views/votes_show_closed.erb:133 ../views/votes_show_closed.erb:140
msgstr "Afegeix com a mínim 2 opcions abans d'obrir la votació als votants." #: ../views/votes_show_open.erb:91 ../views/votes_show_open.erb:98
msgid "Add organizer"
msgstr "Afegeix organitzadora"
#: ../views/votes_edit_description.erb:1
msgid "Edit vote description"
msgstr "Edita la descripció de la votació"
#: ../views/votes_edit_description.erb:6 ../views/votes_new.erb:6
msgid "Enter a title."
msgstr "Entra un títol."
#: ../views/votes_edit_description.erb:13 ../views/votes_new.erb:13
msgid "Title"
msgstr "Títol"
#: ../views/votes_new.erb:1 #: ../views/votes_new.erb:1
msgid "New vote" msgid "New vote"
msgstr "Nova votació" msgstr "Nova votació"
#: ../views/votes_open.erb:1 #: ../views/votes_open.erb:14
msgid "Open vote"
msgstr "Obrir la votació"
#: ../views/votes_open.erb:5
msgid "" msgid ""
"The vote will automatically close on that date and the results will\n" "The vote will automatically close on that date and the results will\n"
"be sent to all users by email." "be sent to all users by email."
@ -321,72 +330,76 @@ msgstr ""
"La votació es tancarà automàticament en aquesta data i els resultats\n" "La votació es tancarà automàticament en aquesta data i els resultats\n"
"s'enviaran a tothom per email." "s'enviaran a tothom per email."
#: ../views/votes_open.erb:8 #: ../views/votes_open.erb:17
msgid "" msgid ""
"You won't be able to modify or delete this vote anymore after\n" "You won't be able to modify or delete this vote anymore after\n"
"opening it." "opening it."
msgstr "Ja no podràs modificar o suprimir aquesta votació desprès d'obrir-la." msgstr "Ja no podràs modificar o suprimir aquesta votació desprès d'obrir-la."
#: ../views/votes_open.erb:16 #: ../views/votes_open.erb:25
msgid "Cancel" msgid "Cancel"
msgstr "Cancel·lar" msgstr "Cancel·lar"
#: ../views/votes_show_closed.erb:11 #: ../views/votes_show_closed.erb:11
msgid "All ratings" msgid "Closed on %{date}"
msgstr "Totes les valoracions" msgstr "Tancada el %{date}"
#: ../views/votes_show_closed.erb:15 #: ../views/votes_show_closed.erb:35
msgid "Participant"
msgstr "Votant"
#: ../views/votes_show_closed.erb:33 ../views/votes_show_closed.erb:73
msgid "Results" msgid "Results"
msgstr "Resultats" msgstr "Resultats"
#: ../views/votes_show_closed.erb:37
msgid "Rank"
msgstr "Rang"
#: ../views/votes_show_closed.erb:38
msgid "Candidate"
msgstr "Opció"
#: ../views/votes_show_closed.erb:39 #: ../views/votes_show_closed.erb:39
msgid "Majority Judgment"
msgstr "Judici majoritari"
#: ../views/votes_show_closed.erb:40
msgid "Proponents"
msgstr "Defensores"
#: ../views/votes_show_closed.erb:41
msgid "Opponents"
msgstr "Detractores"
#: ../views/votes_show_closed.erb:75
msgid "No results are available because nobody voted on this vote." msgid "No results are available because nobody voted on this vote."
msgstr "No hi ha cap resultat perquè ningú va votar en aquesta votació." msgstr "No hi ha cap resultat perquè ningú va votar en aquesta votació."
#: ../views/votes_show_closed.erb:92 #: ../views/votes_show_closed.erb:46
msgid "Rank"
msgstr "Rang"
#: ../views/votes_show_closed.erb:47
msgid "Candidate"
msgstr "Opció"
#: ../views/votes_show_closed.erb:48
msgid "Majority Judgment"
msgstr "Judici majoritari"
#: ../views/votes_show_closed.erb:49
msgid "Proponents"
msgstr "Defensores"
#: ../views/votes_show_closed.erb:50
msgid "Opponents"
msgstr "Detractores"
#: ../views/votes_show_closed.erb:94
msgid "Participant"
msgstr "Votant"
#: ../views/votes_show_closed.erb:118
msgid "Reopen voting period" msgid "Reopen voting period"
msgstr "Reobrir el període de votació" msgstr "Reobrir el període de votació"
#: ../views/votes_show_open.erb:6 #: ../views/votes_show_open.erb:8
msgid "Closes on %{date}"
msgstr "Tanca el %{date}"
#: ../views/votes_show_open.erb:24
msgid "Missing rating for candidate <i>%{name}</i>." msgid "Missing rating for candidate <i>%{name}</i>."
msgstr "Falta una valoració per l'opció <i>%{name}</i>." msgstr "Falta una valoració per l'opció <i>%{name}</i>."
#: ../views/votes_show_open.erb:17 #: ../views/votes_show_open.erb:54
msgid "Your ratings" msgid "Vote"
msgstr "Les teves valoracions" msgstr "Votar"
#: ../views/votes_show_open.erb:44 #: ../views/votes_show_open.erb:57
msgid "Participants" msgid "Participants"
msgstr "Votants" msgstr "Votants"
#: ../views/votes_show_open.erb:69 #: ../views/votes_show_open.erb:68
msgid "Change back to draft vote" msgid "Change back to draft vote"
msgstr "Tornar a l'esborrany de votació" msgstr "Tornar a l'esborrany de votació"
#: ../views/votes_show_open.erb:73 #: ../views/votes_show_open.erb:72
msgid "Close votes and show results" msgid "Close votes and show results"
msgstr "Tancar la votació i veure els resultats" msgstr "Tancar la votació i veure els resultats"

View file

@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-10-13 20:19+0200\n" "POT-Creation-Date: 2025-12-04 17:25-0600\n"
"PO-Revision-Date: 2025-10-13 20:19+0200\n" "PO-Revision-Date: 2025-12-04 17:25-0600\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
@ -42,16 +42,16 @@ msgstr ""
msgid "Very good" msgid "Very good"
msgstr "" msgstr ""
#: ../vedia.rb:127 #: ../vedia.rb:125
msgid "Incorrect email or password." msgid "Incorrect email or password."
msgstr "" msgstr ""
#: ../vedia.rb:149 #: ../vedia.rb:147
msgid "Reset your password" msgid "Reset your password"
msgstr "" msgstr ""
#: ../views/admin.erb:1 ../views/admin_users.erb:1 ../views/admin_votes.erb:1 #: ../views/admin.erb:1 ../views/admin_users.erb:1 ../views/admin_votes.erb:1
#: ../views/layout.erb:26 #: ../views/layout.erb:35
msgid "Admin" msgid "Admin"
msgstr "" msgstr ""
@ -59,20 +59,21 @@ msgstr ""
msgid "Users" msgid "Users"
msgstr "" msgstr ""
#: ../views/admin.erb:13 ../views/home.erb:3 #: ../views/admin.erb:13 ../views/layout.erb:31 ../views/votes.erb:1
msgid "Votes" msgid "Votes"
msgstr "" msgstr ""
#: ../views/admin.erb:21 ../views/home.erb:12 ../views/votes_show_draft.erb:1 #: ../views/admin.erb:29 ../views/votes.erb:11 ../views/votes_edit.erb:5
msgid "(Draft)" #: ../views/votes_open.erb:5 ../views/votes_show_draft.erb:5
msgid "Draft"
msgstr "" msgstr ""
#: ../views/admin.erb:23 ../views/home.erb:16 #: ../views/admin.erb:31 ../views/votes.erb:13 ../views/votes_show_open.erb:5
msgid "(Open)" msgid "Open"
msgstr "" msgstr ""
#: ../views/admin.erb:25 ../views/home.erb:20 #: ../views/admin.erb:33 ../views/votes.erb:15 ../views/votes_show_closed.erb:5
msgid "(Closed)" msgid "Closed"
msgstr "" msgstr ""
#: ../views/admin_users.erb:5 ../views/admin_votes.erb:7 #: ../views/admin_users.erb:5 ../views/admin_votes.erb:7
@ -101,7 +102,7 @@ msgstr ""
#: ../views/admin_users.erb:19 ../views/admin_users.erb:38 #: ../views/admin_users.erb:19 ../views/admin_users.erb:38
#: ../views/admin_votes.erb:27 ../views/admin_votes.erb:46 #: ../views/admin_votes.erb:27 ../views/admin_votes.erb:46
#: ../views/votes_edit.erb:21 #: ../views/votes_edit.erb:32
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
@ -110,6 +111,7 @@ msgid "No vote organized."
msgstr "" msgstr ""
#: ../views/admin_users.erb:30 ../views/admin_votes.erb:38 #: ../views/admin_users.erb:30 ../views/admin_votes.erb:38
#: ../views/votes_show_closed.erb:89
msgid "Ratings" msgid "Ratings"
msgstr "" msgstr ""
@ -133,8 +135,7 @@ msgstr ""
msgid "Description: %{description}" msgid "Description: %{description}"
msgstr "" msgstr ""
#: ../views/admin_votes.erb:15 ../views/votes_open.erb:3 #: ../views/admin_votes.erb:15 ../views/votes_open.erb:12
#: ../views/votes_show_closed.erb:4 ../views/votes_show_open.erb:12
msgid "Closing date: %{date}" msgid "Closing date: %{date}"
msgstr "" msgstr ""
@ -146,9 +147,9 @@ msgstr ""
msgid "State: %{state}" msgid "State: %{state}"
msgstr "" msgstr ""
#: ../views/admin_votes.erb:19 ../views/votes_edit.erb:39 #: ../views/admin_votes.erb:19 ../views/votes_edit.erb:90
#: ../views/votes_show_closed.erb:79 ../views/votes_show_draft.erb:5 #: ../views/votes_show_closed.erb:123 ../views/votes_show_draft.erb:29
#: ../views/votes_show_open.erb:54 #: ../views/votes_show_open.erb:79
msgid "Organizers" msgid "Organizers"
msgstr "" msgstr ""
@ -156,45 +157,49 @@ msgstr ""
msgid "No organizer." msgid "No organizer."
msgstr "" msgstr ""
#: ../views/admin_votes.erb:60 ../views/votes_edit.erb:68 #: ../views/admin_votes.erb:60 ../views/votes_edit.erb:86
msgid "Delete vote" msgid "Delete vote"
msgstr "" msgstr ""
#: ../views/home.erb:1 ../views/layout.erb:24 #: ../views/candidates_edit.erb:1
msgid "Home" msgid "Edit candidate"
msgstr "" msgstr ""
#: ../views/home.erb:11 #: ../views/candidates_edit.erb:6 ../views/votes_edit.erb:48
msgid "(Draft, organized by you)" msgid "Enter a name."
msgstr "" msgstr ""
#: ../views/home.erb:15 #: ../views/candidates_edit.erb:13 ../views/votes_edit.erb:54
msgid "(Open, organized by you)" msgid "Name"
msgstr "" msgstr ""
#: ../views/home.erb:19 #: ../views/candidates_edit.erb:17 ../views/votes_edit.erb:58
msgid "(Closed, organized by you)" #: ../views/votes_edit_description.erb:17 ../views/votes_new.erb:17
msgid "Description"
msgstr "" msgstr ""
#: ../views/home.erb:28 ../views/votes_new.erb:12 #: ../views/candidates_edit.erb:19 ../views/votes_edit.erb:60
msgid "Create new vote" #: ../views/votes_edit_description.erb:19 ../views/votes_new.erb:19
msgid ""
"You can use <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>"
"."
msgstr "" msgstr ""
#: ../views/layout.erb:22 #: ../views/candidates_edit.erb:21 ../views/votes_edit_description.erb:21
msgid "Logged in as %{email}." msgid "Save"
msgstr "" msgstr ""
#: ../views/layout.erb:22 #: ../views/layout.erb:40
msgid "Logout" msgid "Logout"
msgstr "" msgstr ""
#: ../views/layout.erb:30 ../views/login.erb:1 ../views/login.erb:16 #: ../views/login.erb:1 ../views/login.erb:16
msgid "Login" msgid "Login"
msgstr "" msgstr ""
#: ../views/login.erb:9 ../views/reset.erb:5 ../views/reset_change.erb:13 #: ../views/login.erb:9 ../views/reset.erb:5 ../views/reset_change.erb:13
#: ../views/signup.erb:24 ../views/votes_edit.erb:51 #: ../views/signup.erb:24 ../views/votes_edit.erb:102
#: ../views/votes_show_closed.erb:99 ../views/votes_show_open.erb:80 #: ../views/votes_show_closed.erb:137 ../views/votes_show_open.erb:95
msgid "Email" msgid "Email"
msgstr "" msgstr ""
@ -212,8 +217,8 @@ msgstr ""
msgid "Reset password" msgid "Reset password"
msgstr "" msgstr ""
#: ../views/reset_change.erb:6 ../views/signup.erb:6 #: ../views/reset_change.erb:6 ../views/signup.erb:17
msgid "Specify a password." msgid "Enter a password."
msgstr "" msgstr ""
#: ../views/reset_email.erb:1 #: ../views/reset_email.erb:1
@ -238,14 +243,23 @@ msgstr ""
msgid "If you don't receive the email, please check your spam folder." msgid "If you don't receive the email, please check your spam folder."
msgstr "" msgstr ""
#: ../views/signup.erb:14 #: ../views/signup.erb:6
msgid "Email is not a valid email address." msgid "Email is not a valid email address."
msgstr "" msgstr ""
#: ../views/signup.erb:17 #: ../views/signup.erb:9
msgid "An account already exists for %{email}." msgid "An account already exists for %{email}."
msgstr "" msgstr ""
#: ../views/votes.erb:18 ../views/votes_edit.erb:6
#: ../views/votes_show_closed.erb:7
msgid "Organizer"
msgstr ""
#: ../views/votes.erb:28 ../views/votes_new.erb:21
msgid "Create new vote"
msgstr ""
#: ../views/votes_close_email.erb:1 #: ../views/votes_close_email.erb:1
msgid "Results:" msgid "Results:"
msgstr "" msgstr ""
@ -254,136 +268,134 @@ msgstr ""
msgid "Visit this link to see the full results:" msgid "Visit this link to see the full results:"
msgstr "" msgstr ""
#: ../views/votes_edit.erb:1 #: ../views/votes_edit.erb:16 ../views/votes_edit.erb:29
msgid "Edit draft vote" msgid "Edit"
msgstr "" msgstr ""
#: ../views/votes_edit.erb:5 ../views/votes_new.erb:5 #: ../views/votes_edit.erb:20 ../views/votes_open.erb:36
msgid "Title" #: ../views/votes_show_closed.erb:22 ../views/votes_show_draft.erb:16
msgstr "" #: ../views/votes_show_open.erb:19
#: ../views/votes_edit.erb:9 ../views/votes_edit.erb:33
#: ../views/votes_new.erb:9
msgid "Description"
msgstr ""
#: ../views/votes_edit.erb:12 ../views/votes_show_open.erb:41
msgid "Save"
msgstr ""
#: ../views/votes_edit.erb:15 ../views/votes_open.erb:23
#: ../views/votes_show_draft.erb:13
msgid "Candidates" msgid "Candidates"
msgstr "" msgstr ""
#: ../views/votes_edit.erb:25 ../views/votes_edit.erb:36 #: ../views/votes_edit.erb:41
msgid "Add candidate" msgid "Add candidate"
msgstr "" msgstr ""
#: ../views/votes_edit.erb:29 #: ../views/votes_edit.erb:43
msgid "Name" msgid "This vote has no candidates yet. Add a first candidate."
msgstr "" msgstr ""
#: ../views/votes_edit.erb:47 ../views/votes_edit.erb:54 #: ../views/votes_edit.erb:63 ../views/votes_edit.erb:65
#: ../views/votes_show_closed.erb:95 ../views/votes_show_closed.erb:102 msgid "Add"
#: ../views/votes_show_open.erb:76 ../views/votes_show_open.erb:83
msgid "Add organizer"
msgstr "" msgstr ""
#: ../views/votes_edit.erb:57 ../views/votes_show_closed.erb:89 #: ../views/votes_edit.erb:72
#: ../views/votes_show_open.erb:66 msgid "Add at least 2 candidates before opening the vote to participants."
msgid "Actions for organizers"
msgstr "" msgstr ""
#: ../views/votes_edit.erb:61 ../views/votes_edit.erb:64 #: ../views/votes_edit.erb:80 ../views/votes_open.erb:21
#: ../views/votes_open.erb:12
msgid "Open vote to participants" msgid "Open vote to participants"
msgstr "" msgstr ""
#: ../views/votes_edit.erb:62 #: ../views/votes_edit.erb:98 ../views/votes_edit.erb:105
msgid "Add at least 2 candidates before opening the vote to participants." #: ../views/votes_show_closed.erb:133 ../views/votes_show_closed.erb:140
#: ../views/votes_show_open.erb:91 ../views/votes_show_open.erb:98
msgid "Add organizer"
msgstr ""
#: ../views/votes_edit_description.erb:1
msgid "Edit vote description"
msgstr ""
#: ../views/votes_edit_description.erb:6 ../views/votes_new.erb:6
msgid "Enter a title."
msgstr ""
#: ../views/votes_edit_description.erb:13 ../views/votes_new.erb:13
msgid "Title"
msgstr "" msgstr ""
#: ../views/votes_new.erb:1 #: ../views/votes_new.erb:1
msgid "New vote" msgid "New vote"
msgstr "" msgstr ""
#: ../views/votes_open.erb:1 #: ../views/votes_open.erb:14
msgid "Open vote"
msgstr ""
#: ../views/votes_open.erb:5
msgid "" msgid ""
"The vote will automatically close on that date and the results will\n" "The vote will automatically close on that date and the results will\n"
"be sent to all users by email." "be sent to all users by email."
msgstr "" msgstr ""
#: ../views/votes_open.erb:8 #: ../views/votes_open.erb:17
msgid "" msgid ""
"You won't be able to modify or delete this vote anymore after\n" "You won't be able to modify or delete this vote anymore after\n"
"opening it." "opening it."
msgstr "" msgstr ""
#: ../views/votes_open.erb:16 #: ../views/votes_open.erb:25
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: ../views/votes_show_closed.erb:11 #: ../views/votes_show_closed.erb:11
msgid "All ratings" msgid "Closed on %{date}"
msgstr "" msgstr ""
#: ../views/votes_show_closed.erb:15 #: ../views/votes_show_closed.erb:35
msgid "Participant"
msgstr ""
#: ../views/votes_show_closed.erb:33 ../views/votes_show_closed.erb:73
msgid "Results" msgid "Results"
msgstr "" msgstr ""
#: ../views/votes_show_closed.erb:37
msgid "Rank"
msgstr ""
#: ../views/votes_show_closed.erb:38
msgid "Candidate"
msgstr ""
#: ../views/votes_show_closed.erb:39 #: ../views/votes_show_closed.erb:39
msgid "Majority Judgment"
msgstr ""
#: ../views/votes_show_closed.erb:40
msgid "Proponents"
msgstr ""
#: ../views/votes_show_closed.erb:41
msgid "Opponents"
msgstr ""
#: ../views/votes_show_closed.erb:75
msgid "No results are available because nobody voted on this vote." msgid "No results are available because nobody voted on this vote."
msgstr "" msgstr ""
#: ../views/votes_show_closed.erb:92 #: ../views/votes_show_closed.erb:46
msgid "Rank"
msgstr ""
#: ../views/votes_show_closed.erb:47
msgid "Candidate"
msgstr ""
#: ../views/votes_show_closed.erb:48
msgid "Majority Judgment"
msgstr ""
#: ../views/votes_show_closed.erb:49
msgid "Proponents"
msgstr ""
#: ../views/votes_show_closed.erb:50
msgid "Opponents"
msgstr ""
#: ../views/votes_show_closed.erb:94
msgid "Participant"
msgstr ""
#: ../views/votes_show_closed.erb:118
msgid "Reopen voting period" msgid "Reopen voting period"
msgstr "" msgstr ""
#: ../views/votes_show_open.erb:6 #: ../views/votes_show_open.erb:8
msgid "Closes on %{date}"
msgstr ""
#: ../views/votes_show_open.erb:24
msgid "Missing rating for candidate <i>%{name}</i>." msgid "Missing rating for candidate <i>%{name}</i>."
msgstr "" msgstr ""
#: ../views/votes_show_open.erb:17 #: ../views/votes_show_open.erb:54
msgid "Your ratings" msgid "Vote"
msgstr "" msgstr ""
#: ../views/votes_show_open.erb:44 #: ../views/votes_show_open.erb:57
msgid "Participants" msgid "Participants"
msgstr "" msgstr ""
#: ../views/votes_show_open.erb:69 #: ../views/votes_show_open.erb:68
msgid "Change back to draft vote" msgid "Change back to draft vote"
msgstr "" msgstr ""
#: ../views/votes_show_open.erb:73 #: ../views/votes_show_open.erb:72
msgid "Close votes and show results" msgid "Close votes and show results"
msgstr "" msgstr ""

2106
public/bootstrap-icons.css vendored Normal file

File diff suppressed because it is too large Load diff

12048
public/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

141
vedia.rb
View file

@ -76,9 +76,7 @@ enable :sessions
MajorityJudgment.values = settings.values MajorityJudgment.values = settings.values
get '/' do get '/' do
require_login redirect '/votes'
@votes = Vote.all
erb :home
end end
get '/style.css' do get '/style.css' do
@ -138,6 +136,13 @@ get '/reset' do
end end
post '/reset' do post '/reset' do
@errors = []
if not params[:email] =~ URI::MailTo::EMAIL_REGEXP
@errors << OpenStruct.new(:attribute => :email, :type => :invalid)
end
if not @errors.empty?
erb :reset
else
@user = User.find_by(email: params[:email]) @user = User.find_by(email: params[:email])
if @user if @user
@reset = SecureRandom.uuid @reset = SecureRandom.uuid
@ -151,6 +156,7 @@ post '/reset' do
mail.deliver mail.deliver
end end
erb :reset_sent erb :reset_sent
end
end end
get '/reset/:uuid' do get '/reset/:uuid' do
@ -266,19 +272,34 @@ post '/admin/votes/:id/delete' do
redirect '/admin' redirect '/admin'
end end
get '/votes' do
require_login
@votes = Vote.all
erb :votes
end
get '/votes/new' do get '/votes/new' do
require_login require_login
erb :votes_new erb :votes_new
end end
post '/votes' do post '/votes/new' do
require_login require_login
@errors = []
if params[:title].empty?
@errors << OpenStruct.new(:attribute => :title, :type => :blank)
end
if not @errors.empty?
@params = params
erb :votes_new
else
@vote = Vote.create(secure_id: SecureRandom.hex(8), @vote = Vote.create(secure_id: SecureRandom.hex(8),
title: params[:title], title: params[:title],
description: params[:description], description: params[:description],
state: 'draft') state: 'draft')
@vote.users << current_user @vote.users << current_user
redirect '/votes/' + @vote.secure_id redirect '/votes/' + @vote.secure_id
end
end end
get '/votes/:id' do get '/votes/:id' do
@ -298,15 +319,42 @@ get '/votes/:id' do
end end
end end
get '/votes/:id/edit' do
require_login
find_vote
require_organizer
require_draft_vote
params[:title] = @vote.title
params[:description] = @vote.description
erb :votes_edit_description
end
post '/votes/:id/edit' do post '/votes/:id/edit' do
require_login require_login
find_vote find_vote
require_organizer require_organizer
require_draft_vote require_draft_vote
@errors = []
if params[:title].empty?
@errors << OpenStruct.new(:attribute => :title, :type => :blank)
end
if not @errors.empty?
@params = params
erb :votes_edit_description
else
@vote.title = params[:title] @vote.title = params[:title]
@vote.description = params[:description] @vote.description = params[:description]
@vote.save @vote.save
erb :votes_edit redirect '/votes/' + @vote.secure_id
end
end
get '/votes/:id/candidates' do
require_login
find_vote
require_organizer
require_draft_vote
redirect '/votes/' + @vote.secure_id
end end
post '/votes/:id/candidates' do post '/votes/:id/candidates' do
@ -314,11 +362,55 @@ post '/votes/:id/candidates' do
find_vote find_vote
require_organizer require_organizer
require_draft_vote require_draft_vote
@errors = []
if params[:name].empty?
@errors << OpenStruct.new(:attribute => :name, :type => :blank)
end
if not @errors.empty?
@params = params
erb :votes_edit
else
@candidate = Candidate.new(name: params[:name], @candidate = Candidate.new(name: params[:name],
description: params[:description]) description: params[:description])
@candidate.vote = @vote @candidate.vote = @vote
@candidate.save @candidate.save
redirect '/votes/' + @vote.secure_id redirect '/votes/' + @vote.secure_id
end
end
get '/votes/:id/candidates/:cid' do
require_login
find_vote
require_organizer
require_draft_vote
find_candidate
require_candidate_in_vote
params[:name] = @candidate.name
params[:description] = @candidate.description
erb :candidates_edit
end
post '/votes/:id/candidates/:cid' do
require_login
find_vote
require_organizer
require_draft_vote
find_candidate
require_candidate_in_vote
@errors = []
if params[:name].empty?
@errors << OpenStruct.new(:attribute => :name, :type => :blank)
end
if not @errors.empty?
@params = params
erb :candidates_edit
else
@candidate.name = params[:name]
@candidate.description = params[:description]
@candidate.save
redirect '/votes/' + @vote.secure_id
erb :candidates_edit
end
end end
post '/votes/:id/candidates/:cid/delete' do post '/votes/:id/candidates/:cid/delete' do
@ -326,7 +418,8 @@ post '/votes/:id/candidates/:cid/delete' do
find_vote find_vote
require_organizer require_organizer
require_draft_vote require_draft_vote
@candidate = Candidate.find(params[:cid]) find_candidate
require_candidate_in_vote
@candidate.destroy @candidate.destroy
redirect '/votes/' + @vote.secure_id redirect '/votes/' + @vote.secure_id
end end
@ -414,12 +507,33 @@ post '/votes/:id/ratings' do
end end
end end
get '/votes/:id/organizers' do
require_login
find_vote
require_organizer
redirect '/votes/' + @vote.secure_id
end
post '/votes/:id/organizers' do post '/votes/:id/organizers' do
require_login require_login
find_vote find_vote
require_organizer require_organizer
@errors = []
if not params[:email] =~ URI::MailTo::EMAIL_REGEXP
@errors << OpenStruct.new(:attribute => :email, :type => :invalid)
else
user = User.find_by(email: params[:email]) user = User.find_by(email: params[:email])
if not user
@errors << OpenStruct.new(:attribute => :email, :type => :unknown)
@params = params
elsif @vote.users.exists?(user.id)
@errors << OpenStruct.new(:attribute => :email, :type => :duplicate)
else
@vote.users << user @vote.users << user
end
end
# I'm not displaying the error message yet because it would require choosing
# between erb :votes_edit, :votes_show_closed, and :votes_show_open.
redirect '/votes/' + @vote.secure_id redirect '/votes/' + @vote.secure_id
end end
@ -454,6 +568,8 @@ helpers do
def current_user def current_user
if session[:user_id] if session[:user_id]
User.find(session[:user_id]) User.find(session[:user_id])
elsif settings.spoof_admin
User.find(1)
else else
nil nil
end end
@ -475,6 +591,14 @@ helpers do
@vote = Vote.find_by(secure_id: params[:id]) @vote = Vote.find_by(secure_id: params[:id])
end end
def find_candidate
@candidate = Candidate.find(params[:cid])
end
def require_candidate_in_vote
redirect '/votes/' + @vote.secure_id unless @candidate.vote == @vote
end
def require_organizer def require_organizer
redirect '/votes/' + @vote.secure_id unless @vote.users.exists?(current_user.id) redirect '/votes/' + @vote.secure_id unless @vote.users.exists?(current_user.id)
end end
@ -496,7 +620,12 @@ helpers do
end end
def format_date(timestamp) def format_date(timestamp)
if session[:timezone]
"#{TZInfo::Timezone.get(session[:timezone]).to_local(timestamp).strftime('%F')}" "#{TZInfo::Timezone.get(session[:timezone]).to_local(timestamp).strftime('%F')}"
else
# Otherwise, format_date fails on first page after login. Not sure why...
"#{timestamp.strftime('%F')}"
end
end end
def format_date_and_time(timestamp) def format_date_and_time(timestamp)

View file

@ -1,8 +1,8 @@
<h1><%= _("Admin") %></h1> <h1 class="mb-5"><%= _("Admin") %></h1>
<h2><%= _("Users") %></h2> <h2 class="mb-4"><%= _("Users") %></h2>
<ul> <ul class="mb-5">
<% @users.reverse.each do |user| %> <% @users.reverse.each do |user| %>
<li> <li>
<a href="/admin/users/<%= user.id %>"><%= user.email %></a> <a href="/admin/users/<%= user.id %>"><%= user.email %></a>
@ -10,20 +10,31 @@
<% end %> <% end %>
</ul> </ul>
<h2><%= _("Votes") %></h2> <h2 class="mb-4"><%= _("Votes") %></h2>
<ul> <table class="table table-striped mb-5">
<% @votes.reverse.each do |vote| %> <thead>
<li> <tr>
<a href="/admin/votes/<%= vote.id %>"><%= vote.title %></a> <td></td>
<td></td>
</tr>
</thead>
<tbody>
<% @votes.reverse.each do |vote| %>
<tr>
<td><%= format_date(vote.created_at) %></td>
<td class="text-nowrap">
<% case vote.state <% case vote.state
when 'draft' %> when 'draft' %>
<%= _("(Draft)") %> <span class="badge bg-secondary"><%= _("Draft") %></span>
<% when 'open' %> <% when 'open' %>
<%= _("(Open)") %> <span class="badge bg-primary"><%= _("Open") %></span>
<% when 'closed' %> <% when 'closed' %>
<%= _("(Closed)") %> <SPAN class="badge bg-dark"><%= _("Closed") %></span>
<% end %> <% end %>
</li> </td>
<% end %> <td><a href="/admin/votes/<%= vote.id %>"><%= vote.title %></a></td>
</ul> </tr>
<% end %>
</tbody>
</table>

View file

@ -1,21 +1,21 @@
<h1><%= _("Admin") %></h1> <h1 class="mb-5"><%= _("Admin") %></h1>
<h2><%= @user.email %></h2> <h2 class="mb-4"><%= @user.email %></h2>
<p><%= _("Created: %{date}") % { date: format_date(@user.created_at) } %></p> <p><%= _("Created: %{date}") % { date: format_date(@user.created_at) } %></p>
<p><%= _("Updated: %{date}") % { date: format_date(@user.updated_at) } %></p> <p><%= _("Updated: %{date}") % { date: format_date(@user.updated_at) } %></p>
<p><%= _("Admin: %{admin}") % { admin: @user.admin ? _("Yes") : _("No") } %></p> <p class="mb-5"><%= _("Admin: %{admin}") % { admin: @user.admin ? _("Yes") : _("No") } %></p>
<h2><%= _("Organized votes") %></h2> <h2 class="mb-4"><%= _("Organized votes") %></h2>
<% unless @user.organizers.blank? %> <% unless @user.organizers.blank? %>
<ul> <ul class="mb-5">
<% @user.organizers.each do |organizer| %> <% @user.organizers.each do |organizer| %>
<li> <li>
<%= organizer.user.email %> <%= organizer.vote.title %>
<a href="/admin/users/<%= @user.id %>/organizers/<%= organizer.vote.id %>/delete"><%= _("Delete") %></a> <a href="/admin/users/<%= @user.id %>/organizers/<%= organizer.vote.id %>/delete"><%= _("Delete") %></a>
</li> </li>
<% end %> <% end %>
@ -23,15 +23,15 @@
<% else %> <% else %>
<p><%= _("No vote organized.") %></p> <p class="mb-5"><%= _("No vote organized.") %></p>
<% end %> <% end %>
<h2><%= _("Ratings") %></h2> <h2 class="mb-4"><%= _("Ratings") %></h2>
<% unless @user.ratings.blank? %> <% unless @user.ratings.blank? %>
<ul> <ul class="mb-5">
<% @user.ratings.collect { |rating| rating.vote }.uniq.each do |vote| %> <% @user.ratings.collect { |rating| rating.vote }.uniq.each do |vote| %>
<li> <li>
<%= vote.title %> <%= vote.title %>
@ -42,14 +42,14 @@
<% else %> <% else %>
<p><%= _("No rating.") %></p> <p class="mb-5"><%= _("No rating.") %></p>
<% end %> <% end %>
<% if @user.ratings.blank? and @user.organizers.blank? %> <% if @user.ratings.blank? and @user.organizers.blank? %>
<form action="/admin/users/<%= @user.id %>/delete" method="post"> <form action="/admin/users/<%= @user.id %>/delete" method="post">
<button type="submit"><%= _("Delete user") %></button> <button type="submit" class="btn btn-outline-danger"><%= _("Delete user") %></button>
</form> </form>
<% end %> <% end %>

View file

@ -1,6 +1,6 @@
<h1><%= _("Admin") %></h1> <h1 class="mb-5"><%= _("Admin") %></h1>
<h2><%= @vote.title %></h2> <h2 class="mb-4"><%= @vote.title %></h2>
<p><a href="/votes/<%= @vote.secure_id %>"><%= _("View vote") %></a></p> <p><a href="/votes/<%= @vote.secure_id %>"><%= _("View vote") %></a></p>
@ -14,13 +14,13 @@
<p><%= _("Closing date: %{date}") % { date: @vote.expire_on ? format_date_and_time(@vote.expire_on) : _("None") } %></p> <p><%= _("Closing date: %{date}") % { date: @vote.expire_on ? format_date_and_time(@vote.expire_on) : _("None") } %></p>
<p><%= _("State: %{state}") % { state: @vote.state } %></p> <p class="mb-5"><%= _("State: %{state}") % { state: @vote.state } %></p>
<h2><%= _("Organizers") %></h2> <h2 class="mb-4"><%= _("Organizers") %></h2>
<% unless @vote.organizers.blank? %> <% unless @vote.organizers.blank? %>
<ul> <ul class="mb-5">
<% @vote.organizers.each do |organizer| %> <% @vote.organizers.each do |organizer| %>
<li> <li>
<%= organizer.user.email %> <%= organizer.user.email %>
@ -35,7 +35,7 @@
<% end %> <% end %>
<h2><%= _("Ratings") %></h2> <h2 class="mb-4"><%= _("Ratings") %></h2>
<% unless @vote.ratings.blank? %> <% unless @vote.ratings.blank? %>

22
views/candidates_edit.erb Normal file
View file

@ -0,0 +1,22 @@
<h1 class="mb-5"><%= _("Edit candidate") %></h1>
<% if @errors %>
<% @errors.each do |error| %>
<% if error.attribute == :name and error.type == :blank %>
<p class="alert alert-warning mb-4"><%= _("Enter a name.") %></p>
<% end %>
<% end %>
<% end %>
<form action="/votes/<%= @vote.secure_id %>/candidates/<%= @candidate.id %>" method="post">
<div class="mb-3">
<label for="name" class="form-label"><%= _("Name") %></label>
<input type="text" name="name" value="<%= params[:name] %>" class="form-control">
</div>
<div class="mb-3">
<label for="description" class="form-label"><%= _("Description") %></label>
<textarea type="text" name="description" class="form-control"><%= params[:description] %></textarea>
<small class="form-text text-muted"><%= _("You can use <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>.") %></small>
</div>
<button type="submit" class="btn btn-primary"><%= _("Save") %></button>
</form>

View file

@ -1,29 +0,0 @@
<h1><%= _("Home") %></h1>
<h2><%= _("Votes") %></h2>
<ul>
<% @votes.reverse.each do |vote| %>
<li>
<a href="/votes/<%= vote.secure_id %>"><%= vote.title %></a>
<% case vote.state
when 'draft' %>
<% if vote.users.exists?(current_user.id) %><%= _("(Draft, organized by you)") %>
<% else %><%= _("(Draft)") %>
<% end %>
<% when 'open' %>
<% if vote.users.exists?(current_user.id) %><%= _("(Open, organized by you)") %>
<% else %><%= _("(Open)") %>
<% end %>
<% when 'closed' %>
<% if vote.users.exists?(current_user.id) %><%= _("(Closed, organized by you)") %>
<% else %><%= _("(Closed)") %>
<% end %>
<% end %>
</li>
<% end %>
</ul>
<form action="/votes/new" method="get">
<button type="submit"><%= _("Create new vote") %></button>
</form>

View file

@ -6,6 +6,8 @@
<title>Vedia</title> <title>Vedia</title>
<script src="/chartkick.js"></script> <script src="/chartkick.js"></script>
<script src="/Chart.bundle.js"></script> <script src="/Chart.bundle.js"></script>
<link rel="stylesheet" href="/bootstrap.css">
<link rel="stylesheet" href="/bootstrap-icons.css">
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css">
</head> </head>
<script> <script>
@ -18,17 +20,37 @@
}); });
</script> </script>
<body> <body>
<% if current_user %> <div class="container py-4">
<p><%= _("Logged in as %{email}.") % { email: current_user.email } %> <a href="/logout"><%= _("Logout") %></a></p>
<p> <header class="d-flex flex-wrap justify-content-center py-3 border-bottom">
<a href="/"><%= _("Home") %></a> <a href="/" class="d-flex align-items-center pe-5 mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none">
<span class="fs-4">Vedia</span>
</a>
<ul class="nav nav-pills">
<li class="nav-item">
<a href="/votes" class="nav-link"><%= _("Votes") %></a>
</li>
<li class="nav-item">
<% if is_admin %> <% if is_admin %>
<a href="/admin"><%= _("Admin") %></a> <a href="/admin" class="nav-link"><%= _("Admin") %></a>
<% end %> <% end %>
</p> </li>
<% else %> <li class="nav-item">
<p><a href="/login"><%= _("Login") %></a></p> <% if current_user %>
<a href="/logout" class="nav-link"><%= _("Logout") %></a>
<% end %> <% end %>
</li>
</ul>
</header>
<div class="d-flex flex-wrap justify-content-end pe-3 py-3 mb-4">
<% if current_user %>
<%= current_user.email %>
<% end %>
</div>
<%= yield %> <%= yield %>
</div>
</body> </body>
</html> </html>

View file

@ -1,19 +1,19 @@
<h1><%= _("Login") %></h1> <h1 class="mb-5"><%= _("Login") %></h1>
<% if @error %> <% if @error %>
<p class="error"><%= @error %></p> <p class="alert alert-warning mb-4"><%= @error %></p>
<% end %> <% end %>
<form action="/login" method="post"> <form action="/login" method="post" class="mb-5">
<p> <div class="mb-3">
<label for="email"><%= _("Email") %></label> <label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email"> <input type="text" name="email" class="form-control">
</p> </div>
<p> <div class="mb-3">
<label for="password"><%= _("Password") %></label> <label for="password" class="form-label"><%= _("Password") %></label>
<input type="password" name="password"> <input type="password" name="password" class="form-control">
</p> </div>
<button type="submit"><%= _("Login") %></button> <button type="submit" class="btn btn-primary"><%= _("Login") %></button>
</form> </form>
<p><a href="/signup"><%= _("Create account") %></a></p> <p><a href="/signup"><%= _("Create account") %></a></p>
<p><a href="/reset"><%= _("Reset password") %></a></p> <p><a href="/reset"><%= _("Reset password") %></a></p>

View file

@ -1,9 +1,17 @@
<h1><%= _("Reset password") %></h1> <h1 class="mb-5"><%= _("Reset password") %></h1>
<% if @errors %>
<% @errors.each do |error| %>
<% if error.attribute == :email and error.type == :invalid %>
<p class="alert alert-warning mb-4"><%= _("Enter an email address.") %></p>
<% end %>
<% end %>
<% end %>
<form action="/reset" method="post"> <form action="/reset" method="post">
<p> <div class="mb-3">
<label for="email"><%= _("Email") %></label> <label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email"> <input type="text" name="email" class="form-control">
</p> </div>
<button type="submit"><%= _("Reset password") %></button> <button type="submit" class="btn btn-primary"><%= _("Reset password") %></button>
</form> </form>

View file

@ -1,21 +1,21 @@
<h1><%= _("Reset password") %></h1> <h1 class="mb-5"><%= _("Reset password") %></h1>
<% if @errors %> <% if @errors %>
<% @errors.each do |error| %> <% @errors.each do |error| %>
<% if error.attribute == :password and error.type == :blank %> <% if error.attribute == :password and error.type == :blank %>
<p class="error"><%= _("Specify a password.") %></p> <p class="alert alert-warning mb-4"><%= _("Enter a password.") %></p>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<form action="/reset/<%= params[:uuid] %>" method="post"> <form action="/reset/<%= params[:uuid] %>" method="post">
<p> <div class="mb-3">
<label for="email"><%= _("Email") %></label> <label for="email" class="form-label"><%= _("Email") %></label>
<span><%= @user.email %></span> <p><strong><%= @user.email %></strong></p>
</p> </div>
<p> <div class="mb-3">
<label for="password"><%= _("Password") %></label> <label for="password" class="form-label"><%= _("Password") %></label>
<input type="password" name="password" value="<%= params[:password] %>"> <input type="password" name="password" value="<%= params[:password] %>" class="form-control">
</p> </div>
<button type="submit"><%= _("Reset password") %></button> <button type="submit" class="btn btn-primary"><%= _("Reset password") %></button>
</form> </form>

View file

@ -1,5 +1,5 @@
<h1><%= _("Reset password") %></h1> <h1 class="mb-5"><%= _("Reset password") %></h1>
<p class="error"><%= _("This password reset link has expired or is invalid.") %></p> <p class="alert alert-warning mb-4"><%= _("This password reset link has expired or is invalid.") %></p>
<p><a href="/reset"><%= _("Try resetting your password again.") %></p> <p><a href="/reset"><%= _("Try resetting your password again.") %></p>

View file

@ -1,4 +1,4 @@
<h1><%= _("Reset password") %></h1> <h1 class="mb-5"><%= _("Reset password") %></h1>
<p><%= _("If an account exists for %{email}, you will get an email with a link <p><%= _("If an account exists for %{email}, you will get an email with a link
to reset your password.") % { email: params[:email] } %></p> to reset your password.") % { email: params[:email] } %></p>

View file

@ -1,32 +1,32 @@
<h1><%= _("Create account") %></h1> <h1 class="mb-5"><%= _("Create account") %></h1>
<% if @errors %>
<% @errors.each do |error| %>
<% if error.attribute == :password and error.type == :blank %>
<p class="error"><%= _("Specify a password.") %></p>
<% end %>
<% end %>
<% end %>
<% if @user and @user.errors.any? %> <% if @user and @user.errors.any? %>
<% @user.errors.each do |error| %> <% @user.errors.each do |error| %>
<% if error.attribute == :email and error.type == :invalid %> <% if error.attribute == :email and error.type == :invalid %>
<p class="error"><%= _("Email is not a valid email address.") %></p> <p class="alert alert-warning mb-4"><%= _("Email is not a valid email address.") %></p>
<% end %> <% end %>
<% if error.attribute == :email and error.type == :taken %> <% if error.attribute == :email and error.type == :taken %>
<p class="error"><%= _("An account already exists for %{email}.") % { email: @user.email } %></p> <p class="alert alert-warning mb-4"><%= _("An account already exists for %{email}.") % { email: @user.email } %></p>
<% end %>
<% end %>
<% end %>
<% if @errors %>
<% @errors.each do |error| %>
<% if error.attribute == :password and error.type == :blank %>
<p class="alert alert-warning mb-4"><%= _("Enter a password.") %></p>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<form action="/signup" method="post"> <form action="/signup" method="post">
<p> <div class="mb-3">
<label for="email"><%= _("Email") %></label> <label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email" value="<%= params[:email] %>"> <input type="text" name="email" value="<%= params[:email] %>" class="form-control">
</p> </div>
<p> <div class="mb-3">
<label for="password"><%= _("Password") %></label> <label for="password" class="form-label"><%= _("Password") %></label>
<input type="password" name="password" value="<%= params[:password] %>"> <input type="password" name="password" value="<%= params[:password] %>" class="form-control">
</p> </div>
<button type="submit"><%= _("Create account") %></button> <button type="submit" class="btn btn-primary"><%= _("Create account") %></button>
</form> </form>

View file

@ -1,18 +1,20 @@
.error {
color: red;
}
p, li {
max-width: 50em;
}
textarea {
width: 80em;
height: 20em;
}
<% settings.values.each do |v| %> <% settings.values.each do |v| %>
.value-<%= v[:id] %> {
.btn-check + .btn-radio-<%= v[:id] %> {
border-color: <%= v[:color] %>;
background: <%= v[:color] %>; background: <%= v[:color] %>;
} opacity: 0.3;
}
.btn-check:checked + .btn-radio-<%= v[:id] %>,
.btn-check:hover + .btn-radio-<%= v[:id] %> {
background: <%= v[:color] %>;
opacity: 1;
}
.bg-<%= v[:id] %> {
color: black;
background-color: <%= v[:color] %>;
}
<% end %> <% end %>

29
views/votes.erb Normal file
View file

@ -0,0 +1,29 @@
<h1 class="mb-5"><%= _("Votes") %></h1>
<table class="table table-striped mb-5">
<tbody>
<% @votes.reverse.each do |vote| %>
<tr>
<td><%= format_date(vote.created_at) %></td>
<td class="text-nowrap">
<% case vote.state
when 'draft' %>
<span class="badge bg-secondary"><%= _("Draft") %></span>
<% when 'open' %>
<span class="badge bg-primary"><%= _("Open") %></span>
<% when 'closed' %>
<SPAN class="badge bg-dark"><%= _("Closed") %></span>
<% end %>
<% if vote.users.exists?(current_user.id) %>
<span class="badge bg-warning text-bg-warning"><%= _("Organizer") %></span>
<% end %>
</td>
<td><a href="/votes/<%= vote.secure_id %>"><%= vote.title %></a></td>
</tr>
<% end %>
</tbody>
</table>
<form action="/votes/new" method="get">
<button type="submit" class="btn btn-primary"><%= _("Create new vote") %></button>
</form>

View file

@ -1,42 +1,93 @@
<h1><%= _("Edit draft vote") %></h1> <div class="mb-5">
<h1><%= @vote.title %></h1>
<ul class="list-unstyled">
<li>
<span class="badge bg-secondary"><%= _("Draft") %></span>
<span class="badge bg-warning text-bg-warning"><%= _("Organizer") %></span>
</li>
</ul>
</div>
<form action="/votes/<%= @vote.secure_id %>/edit" method="post"> <div class="mb-5">
<p> <div class="lead">
<label for="title"><%= _("Title") %></label> <%= markdown(@vote.description) %>
<input type="text" name="title" value="<%= @vote.title %>"> </div>
</p> <form action="/votes/<%= @vote.secure_id %>/edit" method="get" class="mb-5">
<p> <button type="submit" class="btn btn-outline-primary"><%= _("Edit") %></button>
<label for="description"><%= _("Description") %></label> </form>
<textarea type="text" name="description"><%= @vote.description %></textarea> </div>
</p>
<button type="submit"><%= _("Save") %></button>
</form>
<h2><%= _("Candidates") %></h2> <h2 class="mb-4"><%= _("Candidates") %></h2>
<% @vote.candidates.each do |candidate| %> <% @vote.candidates.each do |candidate| %>
<h3><%= candidate.name %></h3> <div class="card mb-4">
<%= markdown(candidate.description) %> <div class="card-body">
<form action="/votes/<%= @vote.secure_id %>/candidates/<%= candidate.id %>/delete" method="post"> <h3 class="mb-3"><%= candidate.name %></h3>
<button type="submit"><%= _("Delete") %></button> <%= markdown(candidate.description) %>
</form> <div class="d-flex">
<form action="/votes/<%= @vote.secure_id %>/candidates/<%= candidate.id %>" method="get" class="me-3">
<button type="submit" class="btn btn-outline-primary"><%= _("Edit") %></button>
</form>
<form action="/votes/<%= @vote.secure_id %>/candidates/<%= candidate.id %>/delete" method="post">
<button type="submit" class="btn btn-outline-danger"><%= _("Delete") %></button>
</form>
</div>
</div>
</div>
<% end %> <% end %>
<h3><%= _("Add candidate") %></h3> <div class="card mb-4">
<div class="card-body">
<h3 class="mb-3"><%= _("Add candidate") %></h3>
<% if @vote.candidates.empty? %>
<p class="alert alert-primary mb-4"><%= _("This vote has no candidates yet. Add a first candidate.") %></p>
<% end %>
<% if @errors %>
<% @errors.each do |error| %>
<% if error.attribute == :name and error.type == :blank %>
<p class="alert alert-warning mb-4"><%= _("Enter a name.") %></p>
<% end %>
<% end %>
<% end %>
<form action="/votes/<%= @vote.secure_id %>/candidates" method="post">
<div class="mb-3">
<label for="name" class="form-label"><%= _("Name") %></label>
<input type="text" name="name" value="<%= params[:name] %>" class="form-control">
</div>
<div class="mb-3">
<label for="description" class="form-label"><%= _("Description") %></label>
<textarea type="text" name="description" class="form-control"><%= params[:description] %></textarea>
<small class="form-text text-muted"><%= _("You can use <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>.") %></small>
</div>
<% if @vote.candidates.length < 2 %>
<button type="submit" class="btn btn-primary"><%= _("Add") %></button>
<% else %>
<button type="submit" class="btn btn-outline-primary"><%= _("Add") %></button>
<% end %>
</form>
</div>
</div>
<form action="/votes/<%= @vote.secure_id %>/candidates" method="post"> <% if @vote.candidates.length < 2 %>
<p> <p class="alert alert-warning mb-4"><%= _("Add at least 2 candidates before opening the vote to participants.") %></p>
<label for="name"><%= _("Name") %></label> <% end %>
<input type="text" name="name">
</p>
<p>
<label for="description"><%= _("Description") %></label>
<textarea type="text" name="description"></textarea>
</p>
<button type="submit"><%= _("Add candidate") %></button>
</form>
<h2><%= _("Organizers") %></h2> <div class="d-flex">
<form action="/votes/<%= @vote.secure_id %>/open" method="get" class="me-3">
<% if @vote.candidates.length < 2 %>
<fieldset disabled="">
<% end %>
<button type="submit" class="btn btn-primary"><%= _("Open vote to participants") %></button>
<% if @vote.candidates.length < 2 %>
</fieldset>
<% end %>
</form>
<form action="/votes/<%= @vote.secure_id %>/delete" method="post" class="mb-5">
<button type="submit" class="btn btn-outline-danger"><%= _("Delete vote") %></button>
</form>
</div>
<h2 class="mb-4"><%= _("Organizers") %></h2>
<ul> <ul>
<% @vote.organizers.each do |organizer| %> <% @vote.organizers.each do |organizer| %>
@ -44,26 +95,12 @@
<% end %> <% end %>
</ul> </ul>
<h3><%= _("Add organizer") %></h3> <h3 class="mb-3"><%= _("Add organizer") %></h3>
<form action="/votes/<%= @vote.secure_id %>/organizers" method="post"> <form action="/votes/<%= @vote.secure_id %>/organizers" method="post" class="mb-5">
<p> <div class="mb-3">
<label for="email"><%= _("Email") %></label> <label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email"> <input type="text" name="email" class="form-control">
</p> </div>
<button type="submit"><%= _("Add organizer") %></button> <button type="submit" class="btn btn-outline-primary"><%= _("Add organizer") %></button>
</form>
<h3><%= _("Actions for organizers") %></h2>
<form action="/votes/<%= @vote.secure_id %>/open" method="get">
<% if @vote.candidates.length < 2 %>
<button type="submit" disabled><%= _("Open vote to participants") %></button>
<p><%= _("Add at least 2 candidates before opening the vote to participants.") %></p>
<% else %>
<button type="submit"><%= _("Open vote to participants") %></button>
<% end %>
</form>
<form action="/votes/<%= @vote.secure_id %>/delete" method="post">
<button type="submit"><%= _("Delete vote") %></button>
</form> </form>

View file

@ -0,0 +1,22 @@
<h1 class="mb-5"><%= _("Edit vote description") %></h1>
<% if @errors %>
<% @errors.each do |error| %>
<% if error.attribute == :title and error.type == :blank %>
<p class="alert alert-warning mb-4"><%= _("Enter a title.") %></p>
<% end %>
<% end %>
<% end %>
<form action="/votes/<%= @vote.secure_id %>/edit" method="post">
<div class="mb-3">
<label for="title" class="form-label"><%= _("Title") %></label>
<input type="text" name="title" value="<%= params[:title] %>" class="form-control">
</div>
<div class="mb-3">
<label for="description" class="form-label"><%= _("Description") %></label>
<textarea type="text" name="description" class="form-control"><%= params[:description] %></textarea>
<small class="form-text text-muted"><%= _("You can use <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>.") %></small>
</div>
<button type="submit" class="btn btn-primary"><%= _("Save") %></button>
</form>

View file

@ -1,13 +1,22 @@
<h1><%= _("New vote") %></h1> <h1 class="mb-5"><%= _("New vote") %></h1>
<form action="/votes" method="post"> <% if @errors %>
<p> <% @errors.each do |error| %>
<label for="title"><%= _("Title") %></label> <% if error.attribute == :title and error.type == :blank %>
<input type="text" name="title"> <p class="alert alert-warning mb-4"><%= _("Enter a title.") %></p>
</p> <% end %>
<p> <% end %>
<label for="description"><%= _("Description") %></label> <% end %>
<textarea type="text" name="description"></textarea>
</p> <form action="/votes/new" method="post">
<button type="submit"><%= _("Create new vote") %></button> <div class="mb-3">
<label for="title" class="form-label"><%= _("Title") %></label>
<input type="text" name="title" value="<%= params[:title] %>" class="form-control">
</div>
<div class="mb-3">
<label for="description" class="form-label"><%= _("Description") %></label>
<textarea type="text" name="description" class="form-control"><%= params[:description] %></textarea>
<small class="form-text text-muted"><%= _("You can use <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>.") %></small>
</div>
<button type="submit" class="btn btn-primary"><%= _("Create new vote") %></button>
</form> </form>

View file

@ -1,6 +1,15 @@
<h1><%= _("Open vote") %></h1> <div class="mb-5">
<h1><%= @vote.title %></h1>
<ul class="list-unstyled">
<li>
<span class="badge bg-secondary"><%= _("Draft") %></span>
</li>
</ul>
</div>
<p><%= _("Closing date: %{date}") % { date: format_date_and_time(@expire_on) } %></p> <div class="alert alert-warning mb-5">
<p><strong><%= _("Closing date: %{date}") % { date: format_date_and_time(@expire_on) } %></strong></p>
<p><%= _("The vote will automatically close on that date and the results will <p><%= _("The vote will automatically close on that date and the results will
be sent to all users by email.") %></p> be sent to all users by email.") %></p>
@ -8,21 +17,29 @@ be sent to all users by email.") %></p>
<p><%= _("You won't be able to modify or delete this vote anymore after <p><%= _("You won't be able to modify or delete this vote anymore after
opening it.") %></p> opening it.") %></p>
<form action="/votes/<%= @vote.secure_id %>/open" method="post"> <form action="/votes/<%= @vote.secure_id %>/open" method="post" class="mb-3">
<button type="submit"><%= _("Open vote to participants") %></button> <button type="submit" class="btn btn-primary"><%= _("Open vote to participants") %></button>
</form> </form>
<form action="/votes/<%= @vote.secure_id %>" method="get"> <form action="/votes/<%= @vote.secure_id %>" method="get">
<button type="submit"><%= _("Cancel") %></button> <button type="submit" class="btn btn-outline-secondary"><%= _("Cancel") %></button>
</form> </form>
<h2><%= @vote.title %></h2> </div>
<%= markdown(@vote.description) %> <div class="mb-5">
<div class="lead">
<%= markdown(@vote.description) %>
</div>
</div>
<h2><%= _("Candidates") %></h2> <h2 class="mb-4"><%= _("Candidates") %></h2>
<% @vote.candidates.each do |candidate| %> <% @vote.candidates.each do |candidate| %>
<h3><%= candidate.name %></h3> <div class="card mb-4">
<%= markdown(candidate.description) %> <div class="card-body">
<h3 class="mb-3"><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
</div>
</div>
<% end %> <% end %>

View file

@ -1,39 +1,48 @@
<h1><%= @vote.title %></h1> <div class="mb-5">
<h1><%= @vote.title %></h1>
<ul class="list-unstyled">
<li>
<span class="badge bg-dark text-bg-dark"><%= _("Closed") %></span>
<% if @vote.users.exists?(current_user.id) %>
<span class="badge bg-warning text-bg-warning"><%= _("Organizer") %></span>
<% end %>
</li>
<% if @vote.expire_on %>
<li><%= _("Closed on %{date}") % { date: format_date_and_time(@vote.expire_on) } %></li>
<% end %>
</ul>
</div>
<% if @vote.expire_on %> <div class="mb-5">
<p><%= _("Closing date: %{date}") % { date: format_date_and_time(@vote.expire_on) } %></p> <div class="lead">
<%= markdown(@vote.description) %>
</div>
</div>
<h2 class="mb-4"><%= _("Candidates") %></h2>
<% @vote.candidates.each do |candidate| %>
<div class="card mb-4">
<div class="card-body">
<h3 class="mb-3"><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
</div>
</div>
<% end %> <% end %>
<%= markdown(@vote.description) %> <div class="mb-5"></div>
<% unless @vote.ratings.blank? %> <h2 class="mb-4"><%= _("Results") %></h2>
<h2><%= _("All ratings") %></h2> <% if @vote.ratings.blank? %>
<table> <%= _("No results are available because nobody voted on this vote.") %>
<tr>
<th><%= _("Participant") %></th> <% else %>
<% @vote.candidates.each do |candidate| %>
<th><%= candidate.name %></th> <table class="table mb-4">
<% end %>
</tr>
<% @vote.ratings.collect { |rating| rating.user }.uniq.each do |user| %>
<tr>
<td><%= user.email %></td>
<% @vote.candidates.each do |candidate| %>
<% if rating = @vote.ratings.find { |rating| rating.user == user and rating.candidate == candidate } %>
<% value = settings.values.select { |e| e[:id] == rating.value }.first %>
<td class="value-<%= value[:id] %>"><%= _(value[:label]) %></td>
<% end %>
<% end %>
</tr>
<% end %>
</table>
<h2><%= _("Results") %></h2>
<table>
<tr> <tr>
<th></th>
<th><%= _("Rank") %></th> <th><%= _("Rank") %></th>
<th><%= _("Candidate") %></th> <th><%= _("Candidate") %></th>
<th><%= _("Majority Judgment") %></th> <th><%= _("Majority Judgment") %></th>
@ -43,11 +52,18 @@
<% i = 0 %> <% i = 0 %>
<% @vote.candidates.sort { |a, b| a.mj <=> b.mj }.reverse.each do |candidate| %> <% @vote.candidates.sort { |a, b| a.mj <=> b.mj }.reverse.each do |candidate| %>
<% i = i + 1 %> <% i = i + 1 %>
<% if i == 1 %>
<tr class="table-success">
<td><i class="bi bi-trophy-fill"></i></td>
<td><%= i %></i></td>
<% else %>
<tr> <tr>
<td></td>
<td><%= i %></td> <td><%= i %></td>
<% end %>
<td><%= candidate.name %></td> <td><%= candidate.name %></td>
<% value = settings.values.select { |e| e[:id] == candidate.mj.mj }.first %> <% value = settings.values.select { |e| e[:id] == candidate.mj.mj }.first %>
<td class="value-<%= value[:id] %>"><%= _(value[:label]) %></td> <td class="h5"><span class="badge bg-<%= value[:id] %>"><%= _(value[:label]) %></span></td>
<td><%= candidate.mj.proponents %></td> <td><%= candidate.mj.proponents %></td>
<td><%= candidate.mj.opponents %></td> <td><%= candidate.mj.opponents %></td>
</tr> </tr>
@ -68,15 +84,43 @@ end
%> %>
<%= bar_chart data, colors: colors, stacked: true, legend: 'bottom' %> <%= bar_chart data, colors: colors, stacked: true, legend: 'bottom' %>
<% else %> <div class="mb-5"></div>
<h2><%= _("Results") %></h2> <h2 class="mb-4"><%= _("Ratings") %></h2>
<%= _("No results are available because nobody voted on this vote.") %> <table class="table table-striped mb-5">
<thead>
<tr>
<th><%= _("Participant") %></th>
<% @vote.candidates.each do |candidate| %>
<th><%= candidate.name %></th>
<% end %>
</tr>
</thead>
<% @vote.ratings.collect { |rating| rating.user }.uniq.each do |user| %>
<tr>
<td><%= user.email %></td>
<% @vote.candidates.each do |candidate| %>
<% if rating = @vote.ratings.find { |rating| rating.user == user and rating.candidate == candidate } %>
<% value = settings.values.select { |e| e[:id] == rating.value }.first %>
<td class="h5"><span class="badge bg-<%= value[:id] %>"><%= _(value[:label]) %></td>
<% end %>
<% end %>
</tr>
<% end %>
</table>
<% end %> <% end %>
<h2><%= _("Organizers") %></h2> <% if @vote.users.exists?(current_user.id) and @vote.expire_on.nil? %>
<form action="/votes/<%= @vote.secure_id %>/reopen" method="post" class="mb-5">
<button type="submit" class="btn btn-outline-primary"><%= _("Reopen voting period") %></button>
</form>
<% end %>
<h2 class="mb-4"><%= _("Organizers") %></h2>
<ul> <ul>
<% @vote.organizers.each do |organizer| %> <% @vote.organizers.each do |organizer| %>
@ -86,20 +130,14 @@ end
<% if @vote.users.exists?(current_user.id) and @vote.expire_on.nil? %> <% if @vote.users.exists?(current_user.id) and @vote.expire_on.nil? %>
<h3><%= _("Actions for organizers") %></h3> <h3 class="mb-3"><%= _("Add organizer") %></h3>
<form action="/votes/<%= @vote.secure_id %>/reopen" method="post"> <form action="/votes/<%= @vote.secure_id %>/organizers" method="post" class="mb-5">
<button type="submit"><%= _("Reopen voting period") %></button> <div class="mb-3">
</form> <label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email" class="form-control">
<h3><%= _("Add organizer") %></h3> </div>
<button type="submit" class="btn btn-outline-primary"><%= _("Add organizer") %></button>
<form action="/votes/<%= @vote.secure_id %>/organizers" method="post">
<p>
<label for="email"><%= _("Email") %></label>
<input type="text" name="email">
</p>
<button type="submit"><%= _("Add organizer") %></button>
</form> </form>
<% end %> <% end %>

View file

@ -1,18 +1,35 @@
<h1><%= @vote.title %> <%= _("(Draft)") %></h1> <div class="mb-5">
<h1><%= @vote.title %></h1>
<ul class="list-unstyled">
<li>
<span class="badge bg-secondary"><%= _("Draft") %></span>
</li>
</ul>
</div>
<%= markdown(@vote.description) %> <div class="mb-5">
<div class="lead">
<%= markdown(@vote.description) %>
</div>
</div>
<h2><%= _("Organizers") %></h2> <h2 class="mb-4"><%= _("Candidates") %></h2>
<% @vote.candidates.each do |candidate| %>
<div class="card mb-4">
<div class="card-body">
<h3 class="mb-3"><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
</div>
</div>
<% end %>
<div class="mb-5"></div>
<h2 class="mb-4"><%= _("Organizers") %></h2>
<ul> <ul>
<% @vote.organizers.each do |organizer| %> <% @vote.organizers.each do |organizer| %>
<li><%= organizer.user.email %></li> <li><%= organizer.user.email %></li>
<% end %> <% end %>
</ul> </ul>
<h2><%= _("Candidates") %></h2>
<% @vote.candidates.each do |candidate| %>
<h3><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
<% end %>

View file

@ -1,24 +1,38 @@
<h1><%= @vote.title %></h1> <div class="mb-5">
<h1><%= @vote.title %></h1>
<ul class="list-unstyled">
<li>
<span class="badge bg-primary"><%= _("Open") %></span>
</li>
<% if @vote.expire_on %>
<li><%= _("Closes on %{date}") % { date: format_date_and_time(@vote.expire_on) } %></li>
<% end %>
</ul>
</div>
<div class="mb-5">
<div class="lead">
<%= markdown(@vote.description) %>
</div>
</div>
<h2 class="mb-4"><%= _("Candidates") %></h2>
<% if @errors %> <% if @errors %>
<% @errors.each do |error| %> <% @errors.each do |error| %>
<% if error.attribute == :rating and error.type == :blank %> <% if error.attribute == :rating and error.type == :blank %>
<p class="error"><%= _("Missing rating for candidate <i>%{name}</i>.") % { name: error.candidate.name } %></p> <p class="alert alert-warning mb-4"><%= _("Missing rating for candidate <i>%{name}</i>.") % { name: error.candidate.name } %></p>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<% if @vote.expire_on %> <form action="/votes/<%= @vote.secure_id %>/ratings" method="post" class="mb-5">
<p><%= _("Closing date: %{date}") % { date: format_date_and_time(@vote.expire_on) } %></p>
<% end %>
<%= markdown(@vote.description) %>
<h2><%= _("Your ratings") %></h2>
<form action="/votes/<%= @vote.secure_id %>/ratings" method="post">
<ul>
<% @vote.candidates.each do |candidate| %> <% @vote.candidates.each do |candidate| %>
<div class="card mb-4">
<div class="card-body">
<h3 class="mb-3"><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
<% rating = @vote.ratings.find { |rating| rating.user == current_user and rating.candidate == candidate } %> <% rating = @vote.ratings.find { |rating| rating.user == current_user and rating.candidate == candidate } %>
<% if rating <% if rating
value = rating.value value = rating.value
@ -27,31 +41,42 @@
else else
value = 0 value = 0
end %> end %>
<li> <div class="btn-group">
<p><b><%= candidate.name %></b></p>
<%= markdown(candidate.description) %>
<ul>
<% settings.values.reverse.each do |v| %> <% settings.values.reverse.each do |v| %>
<li><input type="radio" name="<%= candidate.id %>" id="<%= candidate.id %>-<%= v[:id] %>" value="<%= v[:id] %>" <% if value == v[:id] %>checked<% end %>><label for="<%= candidate.id %>-<%= v[:id] %>" class="value-<%= v[:id] %>"><%= _(v[:label]) %></label></li> <input type="radio" name="<%= candidate.id %>" id="<%= candidate.id %>-<%= v[:id] %>" value="<%= v[:id] %>" class="btn-check" <% if value == v[:id] %>checked<% end %>>
<label for="<%= candidate.id %>-<%= v[:id] %>" class="btn btn-radio-<%= v[:id] %>"><%= _(v[:label]) %></label>
<% end %> <% end %>
</ul> </div>
</li> </div>
</div>
<% end %> <% end %>
</ul>
<button type="submit"><%= _("Save") %></button> <button type="submit" class="btn btn-primary"><%= _("Vote") %></button>
</form> </form>
<h2><%= _("Participants") %></h2> <h2 class="mb-4"><%= _("Participants") %></h2>
<ul> <ul class="mb-5">
<% @vote.ratings.collect { |rating| rating.user }.uniq.each do |user| %> <% @vote.ratings.collect { |rating| rating.user }.uniq.each do |user| %>
<li><%= user.email %></li> <li><%= user.email %></li>
<% end %> <% end %>
</ul> </ul>
<% if @vote.users.exists?(current_user.id) and @vote.expire_on.nil? %>
<form action="/votes/<%= @vote.secure_id %>/draft" method="post">
<button type="submit" class="btn btn-outline-danger mb-3"><%= _("Change back to draft vote") %></button>
</form>
<form action="/votes/<%= @vote.secure_id %>/close" method="post" class="mb-5">
<button type="submit" class="btn btn-outline-primary mb-3"><%= _("Close votes and show results") %></button>
</form>
<% end %>
<% unless @vote.organizers.blank? %> <% unless @vote.organizers.blank? %>
<h2><%= _("Organizers") %></h2> <h2 class="mb-4"><%= _("Organizers") %></h2>
<ul> <ul>
<% @vote.organizers.each do |organizer| %> <% @vote.organizers.each do |organizer| %>
@ -61,26 +86,16 @@
<% end %> <% end %>
<% if @vote.users.exists?(current_user.id) and @vote.expire_on.nil? %> <% if @vote.users.exists?(current_user.id) or @vote.expire_on.nil? %>
<h3><%= _("Actions for organizers") %></h3> <h3 class="mb-3"><%= _("Add organizer") %></h3>
<form action="/votes/<%= @vote.secure_id %>/draft" method="post"> <form action="/votes/<%= @vote.secure_id %>/organizers" method="post" class="mb-5">
<button type="submit"><%= _("Change back to draft vote") %></button> <div class="mb-3">
</form> <label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email" class="form-control">
<form action="/votes/<%= @vote.secure_id %>/close" method="post"> </div>
<button type="submit"><%= _("Close votes and show results") %></button> <button type="submit" class="btn btn-outline-primary"><%= _("Add organizer") %></button>
</form>
<h3><%= _("Add organizer") %></h3>
<form action="/votes/<%= @vote.secure_id %>/organizers" method="post">
<p>
<label for="email"><%= _("Email") %></label>
<input type="text" name="email">
</p>
<button type="submit"><%= _("Add organizer") %></button>
</form> </form>
<% end %> <% end %>