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)
sqlite3 (2.6.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)
tilt (2.6.0)
timeout (0.4.3)

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -41,16 +41,16 @@ msgstr "Bé"
msgid "Very good"
msgstr "Molt bé"
#: ../vedia.rb:127
#: ../vedia.rb:125
msgid "Incorrect email or password."
msgstr "Correu o contrasenya incorrecte."
#: ../vedia.rb:149
#: ../vedia.rb:147
msgid "Reset your password"
msgstr "Reiniciar contrasenya"
#: ../views/admin.erb:1 ../views/admin_users.erb:1 ../views/admin_votes.erb:1
#: ../views/layout.erb:26
#: ../views/layout.erb:35
msgid "Admin"
msgstr "Admin"
@ -58,21 +58,22 @@ msgstr "Admin"
msgid "Users"
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"
msgstr "Votacions"
#: ../views/admin.erb:21 ../views/home.erb:12 ../views/votes_show_draft.erb:1
msgid "(Draft)"
msgstr "(Esborrany)"
#: ../views/admin.erb:29 ../views/votes.erb:11 ../views/votes_edit.erb:5
#: ../views/votes_open.erb:5 ../views/votes_show_draft.erb:5
msgid "Draft"
msgstr "Esborrany"
#: ../views/admin.erb:23 ../views/home.erb:16
msgid "(Open)"
msgstr "(Oberta)"
#: ../views/admin.erb:31 ../views/votes.erb:13 ../views/votes_show_open.erb:5
msgid "Open"
msgstr "Oberta"
#: ../views/admin.erb:25 ../views/home.erb:20
msgid "(Closed)"
msgstr "(Tancada)"
#: ../views/admin.erb:33 ../views/votes.erb:15 ../views/votes_show_closed.erb:5
msgid "Closed"
msgstr "Tancada"
#: ../views/admin_users.erb:5 ../views/admin_votes.erb:7
msgid "Created: %{date}"
@ -100,7 +101,7 @@ msgstr "Votacions organitzades"
#: ../views/admin_users.erb:19 ../views/admin_users.erb:38
#: ../views/admin_votes.erb:27 ../views/admin_votes.erb:46
#: ../views/votes_edit.erb:21
#: ../views/votes_edit.erb:32
msgid "Delete"
msgstr "Suprimir"
@ -109,6 +110,7 @@ msgid "No vote organized."
msgstr "Cap votació organitzada."
#: ../views/admin_users.erb:30 ../views/admin_votes.erb:38
#: ../views/votes_show_closed.erb:89
msgid "Ratings"
msgstr "Valoracions"
@ -126,14 +128,13 @@ msgstr "Veure votació"
#: ../views/admin_votes.erb:11
msgid "Secure ID: %{secure_id}"
msgstr "ID segura"
msgstr "ID segura: %{secure_id}"
#: ../views/admin_votes.erb:13
msgid "Description: %{description}"
msgstr "Descripció: %{description}"
#: ../views/admin_votes.erb:15 ../views/votes_open.erb:3
#: ../views/votes_show_closed.erb:4 ../views/votes_show_open.erb:12
#: ../views/admin_votes.erb:15 ../views/votes_open.erb:12
msgid "Closing date: %{date}"
msgstr "Data de tancament: %{date}"
@ -145,9 +146,9 @@ msgstr "Cap"
msgid "State: %{state}"
msgstr "Estat: %{state}"
#: ../views/admin_votes.erb:19 ../views/votes_edit.erb:39
#: ../views/votes_show_closed.erb:79 ../views/votes_show_draft.erb:5
#: ../views/votes_show_open.erb:54
#: ../views/admin_votes.erb:19 ../views/votes_edit.erb:90
#: ../views/votes_show_closed.erb:123 ../views/votes_show_draft.erb:29
#: ../views/votes_show_open.erb:79
msgid "Organizers"
msgstr "Organitzadores"
@ -155,45 +156,50 @@ msgstr "Organitzadores"
msgid "No organizer."
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"
msgstr "Suprimir la votació"
#: ../views/home.erb:1 ../views/layout.erb:24
msgid "Home"
msgstr "Inici"
#: ../views/candidates_edit.erb:1
msgid "Edit candidate"
msgstr "Edita la opció"
#: ../views/home.erb:11
msgid "(Draft, organized by you)"
msgstr "(Esborrany, organitzada per tu)"
#: ../views/candidates_edit.erb:6 ../views/votes_edit.erb:48
msgid "Enter a name."
msgstr "Entra un nom."
#: ../views/home.erb:15
msgid "(Open, organized by you)"
msgstr "(Oberta, organitzada per tu)"
#: ../views/candidates_edit.erb:13 ../views/votes_edit.erb:54
msgid "Name"
msgstr "Nom"
#: ../views/home.erb:19
msgid "(Closed, organized by you)"
msgstr "(Tancada, organitzada per tu)"
#: ../views/candidates_edit.erb:17 ../views/votes_edit.erb:58
#: ../views/votes_edit_description.erb:17 ../views/votes_new.erb:17
msgid "Description"
msgstr "Descripció"
#: ../views/home.erb:28 ../views/votes_new.erb:12
msgid "Create new vote"
msgstr "Crear una nova votació"
#: ../views/candidates_edit.erb:19 ../views/votes_edit.erb:60
#: ../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 ""
"Pots fer servir <a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a>."
#: ../views/layout.erb:22
msgid "Logged in as %{email}."
msgstr "Estàs connectada com a %{email}."
#: ../views/candidates_edit.erb:21 ../views/votes_edit_description.erb:21
msgid "Save"
msgstr "Guardar"
#: ../views/layout.erb:22
#: ../views/layout.erb:40
msgid "Logout"
msgstr "Desconnexió"
#: ../views/layout.erb:30 ../views/login.erb:1 ../views/login.erb:16
#: ../views/login.erb:1 ../views/login.erb:16
msgid "Login"
msgstr "Connexió"
#: ../views/login.erb:9 ../views/reset.erb:5 ../views/reset_change.erb:13
#: ../views/signup.erb:24 ../views/votes_edit.erb:51
#: ../views/votes_show_closed.erb:99 ../views/votes_show_open.erb:80
#: ../views/signup.erb:24 ../views/votes_edit.erb:102
#: ../views/votes_show_closed.erb:137 ../views/votes_show_open.erb:95
msgid "Email"
msgstr "Correu"
@ -211,8 +217,8 @@ msgstr "Crear un compte"
msgid "Reset password"
msgstr "Reiniciar contrasenya"
#: ../views/reset_change.erb:6 ../views/signup.erb:6
msgid "Specify a password."
#: ../views/reset_change.erb:6 ../views/signup.erb:17
msgid "Enter a password."
msgstr "Entra una contrasenya."
#: ../views/reset_email.erb:1
@ -239,14 +245,23 @@ msgstr ""
msgid "If you don't receive the email, please check your spam folder."
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."
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}."
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
msgid "Results:"
msgstr "Resultats:"
@ -255,65 +270,59 @@ msgstr "Resultats:"
msgid "Visit this link to see the full results:"
msgstr "Visita aquest enllaç per veure els resultats complets:"
#: ../views/votes_edit.erb:1
msgid "Edit draft vote"
msgstr "Editar un esborrany de votació"
#: ../views/votes_edit.erb:16 ../views/votes_edit.erb:29
msgid "Edit"
msgstr "Editar"
#: ../views/votes_edit.erb:5 ../views/votes_new.erb:5
msgid "Title"
msgstr "Títol"
#: ../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
#: ../views/votes_edit.erb:20 ../views/votes_open.erb:36
#: ../views/votes_show_closed.erb:22 ../views/votes_show_draft.erb:16
#: ../views/votes_show_open.erb:19
msgid "Candidates"
msgstr "Opcions"
#: ../views/votes_edit.erb:25 ../views/votes_edit.erb:36
#: ../views/votes_edit.erb:41
msgid "Add candidate"
msgstr "Afegeix una opció"
#: ../views/votes_edit.erb:29
msgid "Name"
msgstr "Nom"
#: ../views/votes_edit.erb:43
msgid "This vote has no candidates yet. Add a first candidate."
msgstr "Aquesta votació no te cap opció encara. Afegeix una primera opció."
#: ../views/votes_edit.erb:47 ../views/votes_edit.erb:54
#: ../views/votes_show_closed.erb:95 ../views/votes_show_closed.erb:102
#: ../views/votes_show_open.erb:76 ../views/votes_show_open.erb:83
msgid "Add organizer"
msgstr "Afegeix organitzadora"
#: ../views/votes_edit.erb:63 ../views/votes_edit.erb:65
msgid "Add"
msgstr "Afegir"
#: ../views/votes_edit.erb:57 ../views/votes_show_closed.erb:89
#: ../views/votes_show_open.erb:66
msgid "Actions for organizers"
msgstr "Accions per les organitzadores"
#: ../views/votes_edit.erb:72
msgid "Add at least 2 candidates before opening the vote to participants."
msgstr "Afegeix com a mínim 2 opcions abans d'obrir la votació als votants."
#: ../views/votes_edit.erb:61 ../views/votes_edit.erb:64
#: ../views/votes_open.erb:12
#: ../views/votes_edit.erb:80 ../views/votes_open.erb:21
msgid "Open vote to participants"
msgstr "Obrir la votació als votants"
#: ../views/votes_edit.erb:62
msgid "Add at least 2 candidates before opening the vote to participants."
msgstr "Afegeix com a mínim 2 opcions abans d'obrir la votació als votants."
#: ../views/votes_edit.erb:98 ../views/votes_edit.erb:105
#: ../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 "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
msgid "New vote"
msgstr "Nova votació"
#: ../views/votes_open.erb:1
msgid "Open vote"
msgstr "Obrir la votació"
#: ../views/votes_open.erb:5
#: ../views/votes_open.erb:14
msgid ""
"The vote will automatically close on that date and the results will\n"
"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"
"s'enviaran a tothom per email."
#: ../views/votes_open.erb:8
#: ../views/votes_open.erb:17
msgid ""
"You won't be able to modify or delete this vote anymore after\n"
"opening it."
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"
msgstr "Cancel·lar"
#: ../views/votes_show_closed.erb:11
msgid "All ratings"
msgstr "Totes les valoracions"
msgid "Closed on %{date}"
msgstr "Tancada el %{date}"
#: ../views/votes_show_closed.erb:15
msgid "Participant"
msgstr "Votant"
#: ../views/votes_show_closed.erb:33 ../views/votes_show_closed.erb:73
#: ../views/votes_show_closed.erb:35
msgid "Results"
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
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."
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"
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>."
msgstr "Falta una valoració per l'opció <i>%{name}</i>."
#: ../views/votes_show_open.erb:17
msgid "Your ratings"
msgstr "Les teves valoracions"
#: ../views/votes_show_open.erb:54
msgid "Vote"
msgstr "Votar"
#: ../views/votes_show_open.erb:44
#: ../views/votes_show_open.erb:57
msgid "Participants"
msgstr "Votants"
#: ../views/votes_show_open.erb:69
#: ../views/votes_show_open.erb:68
msgid "Change back to draft vote"
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"
msgstr "Tancar la votació i veure els resultats"

View file

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

199
vedia.rb
View file

@ -76,9 +76,7 @@ enable :sessions
MajorityJudgment.values = settings.values
get '/' do
require_login
@votes = Vote.all
erb :home
redirect '/votes'
end
get '/style.css' do
@ -138,19 +136,27 @@ get '/reset' do
end
post '/reset' do
@user = User.find_by(email: params[:email])
if @user
@reset = SecureRandom.uuid
@user.reset = hash_password(@reset)
@user.save
mail = Mail.new
mail.from = settings.admin_email
mail.to = @user.email
mail.subject = _("Reset your password")
mail.body = erb :reset_email, :layout => false
mail.deliver
@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])
if @user
@reset = SecureRandom.uuid
@user.reset = hash_password(@reset)
@user.save
mail = Mail.new
mail.from = settings.admin_email
mail.to = @user.email
mail.subject = _("Reset your password")
mail.body = erb :reset_email, :layout => false
mail.deliver
end
erb :reset_sent
end
erb :reset_sent
end
get '/reset/:uuid' do
@ -266,19 +272,34 @@ post '/admin/votes/:id/delete' do
redirect '/admin'
end
get '/votes' do
require_login
@votes = Vote.all
erb :votes
end
get '/votes/new' do
require_login
erb :votes_new
end
post '/votes' do
post '/votes/new' do
require_login
@vote = Vote.create(secure_id: SecureRandom.hex(8),
title: params[:title],
description: params[:description],
state: 'draft')
@vote.users << current_user
redirect '/votes/' + @vote.secure_id
@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),
title: params[:title],
description: params[:description],
state: 'draft')
@vote.users << current_user
redirect '/votes/' + @vote.secure_id
end
end
get '/votes/:id' do
@ -298,15 +319,42 @@ get '/votes/:id' do
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
require_login
find_vote
require_organizer
require_draft_vote
@vote.title = params[:title]
@vote.description = params[:description]
@vote.save
erb :votes_edit
@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.description = params[:description]
@vote.save
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
post '/votes/:id/candidates' do
@ -314,11 +362,55 @@ post '/votes/:id/candidates' do
find_vote
require_organizer
require_draft_vote
@candidate = Candidate.new(name: params[:name],
description: params[:description])
@candidate.vote = @vote
@candidate.save
redirect '/votes/' + @vote.secure_id
@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],
description: params[:description])
@candidate.vote = @vote
@candidate.save
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
post '/votes/:id/candidates/:cid/delete' do
@ -326,7 +418,8 @@ post '/votes/:id/candidates/:cid/delete' do
find_vote
require_organizer
require_draft_vote
@candidate = Candidate.find(params[:cid])
find_candidate
require_candidate_in_vote
@candidate.destroy
redirect '/votes/' + @vote.secure_id
end
@ -414,12 +507,33 @@ post '/votes/:id/ratings' do
end
end
get '/votes/:id/organizers' do
require_login
find_vote
require_organizer
redirect '/votes/' + @vote.secure_id
end
post '/votes/:id/organizers' do
require_login
find_vote
require_organizer
user = User.find_by(email: params[:email])
@vote.users << user
@errors = []
if not params[:email] =~ URI::MailTo::EMAIL_REGEXP
@errors << OpenStruct.new(:attribute => :email, :type => :invalid)
else
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
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
end
@ -454,6 +568,8 @@ helpers do
def current_user
if session[:user_id]
User.find(session[:user_id])
elsif settings.spoof_admin
User.find(1)
else
nil
end
@ -475,6 +591,14 @@ helpers do
@vote = Vote.find_by(secure_id: params[:id])
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
redirect '/votes/' + @vote.secure_id unless @vote.users.exists?(current_user.id)
end
@ -496,7 +620,12 @@ helpers do
end
def format_date(timestamp)
"#{TZInfo::Timezone.get(session[:timezone]).to_local(timestamp).strftime('%F')}"
if session[:timezone]
"#{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
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| %>
<li>
<a href="/admin/users/<%= user.id %>"><%= user.email %></a>
@ -10,20 +10,31 @@
<% end %>
</ul>
<h2><%= _("Votes") %></h2>
<h2 class="mb-4"><%= _("Votes") %></h2>
<ul>
<% @votes.reverse.each do |vote| %>
<li>
<a href="/admin/votes/<%= vote.id %>"><%= vote.title %></a>
<% case vote.state
when 'draft' %>
<%= _("(Draft)") %>
<% when 'open' %>
<%= _("(Open)") %>
<% when 'closed' %>
<%= _("(Closed)") %>
<table class="table table-striped mb-5">
<thead>
<tr>
<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
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 %>
</td>
<td><a href="/admin/votes/<%= vote.id %>"><%= vote.title %></a></td>
</tr>
<% end %>
</li>
<% end %>
</ul>
</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><%= _("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? %>
<ul>
<ul class="mb-5">
<% @user.organizers.each do |organizer| %>
<li>
<%= organizer.user.email %>
<%= organizer.vote.title %>
<a href="/admin/users/<%= @user.id %>/organizers/<%= organizer.vote.id %>/delete"><%= _("Delete") %></a>
</li>
<% end %>
@ -23,15 +23,15 @@
<% else %>
<p><%= _("No vote organized.") %></p>
<p class="mb-5"><%= _("No vote organized.") %></p>
<% end %>
<h2><%= _("Ratings") %></h2>
<h2 class="mb-4"><%= _("Ratings") %></h2>
<% unless @user.ratings.blank? %>
<ul>
<ul class="mb-5">
<% @user.ratings.collect { |rating| rating.vote }.uniq.each do |vote| %>
<li>
<%= vote.title %>
@ -42,14 +42,14 @@
<% else %>
<p><%= _("No rating.") %></p>
<p class="mb-5"><%= _("No rating.") %></p>
<% end %>
<% if @user.ratings.blank? and @user.organizers.blank? %>
<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>
<% 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>
@ -14,13 +14,13 @@
<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? %>
<ul>
<ul class="mb-5">
<% @vote.organizers.each do |organizer| %>
<li>
<%= organizer.user.email %>
@ -35,7 +35,7 @@
<% end %>
<h2><%= _("Ratings") %></h2>
<h2 class="mb-4"><%= _("Ratings") %></h2>
<% 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>
<script src="/chartkick.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">
</head>
<script>
@ -18,17 +20,37 @@
});
</script>
<body>
<% if current_user %>
<p><%= _("Logged in as %{email}.") % { email: current_user.email } %> <a href="/logout"><%= _("Logout") %></a></p>
<p>
<a href="/"><%= _("Home") %></a>
<% if is_admin %>
<a href="/admin"><%= _("Admin") %></a>
<div class="container py-4">
<header class="d-flex flex-wrap justify-content-center py-3 border-bottom">
<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 %>
<a href="/admin" class="nav-link"><%= _("Admin") %></a>
<% end %>
</li>
<li class="nav-item">
<% if current_user %>
<a href="/logout" class="nav-link"><%= _("Logout") %></a>
<% 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 %>
</p>
<% else %>
<p><a href="/login"><%= _("Login") %></a></p>
<% end %>
<%= yield %>
</div>
<%= yield %>
</div>
</body>
</html>

View file

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

View file

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

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
to reset your password.") % { email: params[:email] } %></p>

View file

@ -1,32 +1,32 @@
<h1><%= _("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 %>
<h1 class="mb-5"><%= _("Create account") %></h1>
<% if @user and @user.errors.any? %>
<% @user.errors.each do |error| %>
<% 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 %>
<% 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 %>
<form action="/signup" method="post">
<p>
<label for="email"><%= _("Email") %></label>
<input type="text" name="email" value="<%= params[:email] %>">
</p>
<p>
<label for="password"><%= _("Password") %></label>
<input type="password" name="password" value="<%= params[:password] %>">
</p>
<button type="submit"><%= _("Create account") %></button>
<div class="mb-3">
<label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email" value="<%= params[:email] %>" class="form-control">
</div>
<div class="mb-3">
<label for="password" class="form-label"><%= _("Password") %></label>
<input type="password" name="password" value="<%= params[:password] %>" class="form-control">
</div>
<button type="submit" class="btn btn-primary"><%= _("Create account") %></button>
</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| %>
.value-<%= v[:id] %> {
background: <%= v[:color] %>;
}
.btn-check + .btn-radio-<%= v[:id] %> {
border-color: <%= 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 %>

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">
<p>
<label for="title"><%= _("Title") %></label>
<input type="text" name="title" value="<%= @vote.title %>">
</p>
<p>
<label for="description"><%= _("Description") %></label>
<textarea type="text" name="description"><%= @vote.description %></textarea>
</p>
<button type="submit"><%= _("Save") %></button>
</form>
<div class="mb-5">
<div class="lead">
<%= markdown(@vote.description) %>
</div>
<form action="/votes/<%= @vote.secure_id %>/edit" method="get" class="mb-5">
<button type="submit" class="btn btn-outline-primary"><%= _("Edit") %></button>
</form>
</div>
<h2><%= _("Candidates") %></h2>
<h2 class="mb-4"><%= _("Candidates") %></h2>
<% @vote.candidates.each do |candidate| %>
<h3><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
<form action="/votes/<%= @vote.secure_id %>/candidates/<%= candidate.id %>/delete" method="post">
<button type="submit"><%= _("Delete") %></button>
</form>
<div class="card mb-4">
<div class="card-body">
<h3 class="mb-3"><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
<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 %>
<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">
<p>
<label for="name"><%= _("Name") %></label>
<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>
<% if @vote.candidates.length < 2 %>
<p class="alert alert-warning mb-4"><%= _("Add at least 2 candidates before opening the vote to participants.") %></p>
<% end %>
<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>
<% @vote.organizers.each do |organizer| %>
@ -44,26 +95,12 @@
<% end %>
</ul>
<h3><%= _("Add organizer") %></h3>
<h3 class="mb-3"><%= _("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>
<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 action="/votes/<%= @vote.secure_id %>/organizers" method="post" class="mb-5">
<div class="mb-3">
<label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email" class="form-control">
</div>
<button type="submit" class="btn btn-outline-primary"><%= _("Add organizer") %></button>
</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">
<p>
<label for="title"><%= _("Title") %></label>
<input type="text" name="title">
</p>
<p>
<label for="description"><%= _("Description") %></label>
<textarea type="text" name="description"></textarea>
</p>
<button type="submit"><%= _("Create new vote") %></button>
<% 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/new" 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"><%= _("Create new vote") %></button>
</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
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
opening it.") %></p>
<form action="/votes/<%= @vote.secure_id %>/open" method="post">
<button type="submit"><%= _("Open vote to participants") %></button>
<form action="/votes/<%= @vote.secure_id %>/open" method="post" class="mb-3">
<button type="submit" class="btn btn-primary"><%= _("Open vote to participants") %></button>
</form>
<form action="/votes/<%= @vote.secure_id %>" method="get">
<button type="submit"><%= _("Cancel") %></button>
<button type="submit" class="btn btn-outline-secondary"><%= _("Cancel") %></button>
</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| %>
<h3><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
<div class="card mb-4">
<div class="card-body">
<h3 class="mb-3"><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
</div>
</div>
<% 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 %>
<p><%= _("Closing date: %{date}") % { date: format_date_and_time(@vote.expire_on) } %></p>
<div class="mb-5">
<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 %>
<%= 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>
<tr>
<th><%= _("Participant") %></th>
<% @vote.candidates.each do |candidate| %>
<th><%= candidate.name %></th>
<% 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>
<%= _("No results are available because nobody voted on this vote.") %>
<% else %>
<table class="table mb-4">
<tr>
<th></th>
<th><%= _("Rank") %></th>
<th><%= _("Candidate") %></th>
<th><%= _("Majority Judgment") %></th>
@ -43,11 +52,18 @@
<% i = 0 %>
<% @vote.candidates.sort { |a, b| a.mj <=> b.mj }.reverse.each do |candidate| %>
<% i = i + 1 %>
<tr>
<td><%= i %></td>
<% if i == 1 %>
<tr class="table-success">
<td><i class="bi bi-trophy-fill"></i></td>
<td><%= i %></i></td>
<% else %>
<tr>
<td></td>
<td><%= i %></td>
<% end %>
<td><%= candidate.name %></td>
<% 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.opponents %></td>
</tr>
@ -68,15 +84,43 @@ end
%>
<%= 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 %>
<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>
<% @vote.organizers.each do |organizer| %>
@ -86,20 +130,14 @@ end
<% 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">
<button type="submit"><%= _("Reopen voting period") %></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 action="/votes/<%= @vote.secure_id %>/organizers" method="post" class="mb-5">
<div class="mb-3">
<label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email" class="form-control">
</div>
<button type="submit" class="btn btn-outline-primary"><%= _("Add organizer") %></button>
</form>
<% 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>
<% @vote.organizers.each do |organizer| %>
<li><%= organizer.user.email %></li>
<% end %>
</ul>
<h2><%= _("Candidates") %></h2>
<% @vote.candidates.each do |candidate| %>
<h3><%= candidate.name %></h3>
<%= markdown(candidate.description) %>
<% end %>

View file

@ -1,57 +1,82 @@
<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 %>
<% @errors.each do |error| %>
<% 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 %>
<% if @vote.expire_on %>
<p><%= _("Closing date: %{date}") % { date: format_date_and_time(@vote.expire_on) } %></p>
<% end %>
<form action="/votes/<%= @vote.secure_id %>/ratings" method="post" class="mb-5">
<%= markdown(@vote.description) %>
<h2><%= _("Your ratings") %></h2>
<form action="/votes/<%= @vote.secure_id %>/ratings" method="post">
<ul>
<% @vote.candidates.each do |candidate| %>
<% rating = @vote.ratings.find { |rating| rating.user == current_user and rating.candidate == candidate } %>
<% if rating
value = rating.value
elsif @params[candidate.id.to_s]
value = params[candidate.id.to_s].to_i
else
value = 0
end %>
<li>
<p><b><%= candidate.name %></b></p>
<%= markdown(candidate.description) %>
<ul>
<% 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>
<% end %>
</ul>
</li>
<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 } %>
<% if rating
value = rating.value
elsif @params[candidate.id.to_s]
value = params[candidate.id.to_s].to_i
else
value = 0
end %>
<div class="btn-group">
<% settings.values.reverse.each do |v| %>
<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 %>
</div>
</div>
</div>
<% end %>
</ul>
<button type="submit"><%= _("Save") %></button>
<button type="submit" class="btn btn-primary"><%= _("Vote") %></button>
</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| %>
<li><%= user.email %></li>
<% end %>
</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? %>
<h2><%= _("Organizers") %></h2>
<h2 class="mb-4"><%= _("Organizers") %></h2>
<ul>
<% @vote.organizers.each do |organizer| %>
@ -61,26 +86,16 @@
<% 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">
<button type="submit"><%= _("Change back to draft vote") %></button>
</form>
<form action="/votes/<%= @vote.secure_id %>/close" method="post">
<button type="submit"><%= _("Close votes and show results") %></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 action="/votes/<%= @vote.secure_id %>/organizers" method="post" class="mb-5">
<div class="mb-3">
<label for="email" class="form-label"><%= _("Email") %></label>
<input type="text" name="email" class="form-control">
</div>
<button type="submit" class="btn btn-outline-primary"><%= _("Add organizer") %></button>
</form>
<% end %>