From 01ea5658598e7635db874e0ab2a2ed8c516daeae Mon Sep 17 00:00:00 2001 From: sosokker Date: Mon, 11 Sep 2023 23:38:13 +0700 Subject: [PATCH 1/7] Add User+Authenticate(login,logout,reset password) Also add view for signup page + custom form for signup --- mysite/settings.py | 6 +++ mysite/urls.py | 1 + polls/forms.py | 27 +++++++++++ polls/models.py | 1 + polls/templates/polls/index.html | 17 +++++-- polls/templates/registration/login.html | 37 +++++++++++++++ .../registration/password_reset_complete.html | 2 + .../registration/password_reset_confirm.html | 12 +++++ .../registration/password_reset_done.html | 2 + .../registration/password_reset_form.html | 8 ++++ polls/templates/registration/signup.html | 46 +++++++++++++++++++ polls/urls.py | 1 + polls/views.py | 9 +++- 13 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 polls/forms.py create mode 100644 polls/templates/registration/login.html create mode 100644 polls/templates/registration/password_reset_complete.html create mode 100644 polls/templates/registration/password_reset_confirm.html create mode 100644 polls/templates/registration/password_reset_done.html create mode 100644 polls/templates/registration/password_reset_form.html create mode 100644 polls/templates/registration/signup.html diff --git a/mysite/settings.py b/mysite/settings.py index 572a80b..f5523db 100644 --- a/mysite/settings.py +++ b/mysite/settings.py @@ -126,3 +126,9 @@ STATICFILES_DIRS = [BASE_DIR] # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +LOGIN_REDIRECT_URL = "home_redirect" +LOGOUT_REDIRECT_URL = "home_redirect" + +EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend" +EMAIL_FILE_PATH = BASE_DIR / "sent_emails" \ No newline at end of file diff --git a/mysite/urls.py b/mysite/urls.py index 1b67d3f..5e952b1 100644 --- a/mysite/urls.py +++ b/mysite/urls.py @@ -7,4 +7,5 @@ urlpatterns = [ path('', RedirectView.as_view(pattern_name='polls:index'), name='home_redirect'), path("polls/", include("polls.urls")), path('admin/', admin.site.urls), + path("accounts/", include("django.contrib.auth.urls")), ] diff --git a/polls/forms.py b/polls/forms.py new file mode 100644 index 0000000..1e738ea --- /dev/null +++ b/polls/forms.py @@ -0,0 +1,27 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.models import User + + +class SignUpForm(UserCreationForm): + tailwind_class = "w-full border-2 border-gray-300 bg-gray-100 rounded-lg focus:ring focus:border-blue-300 focus:shadow-none" + + username = forms.CharField(widget=forms.TextInput(attrs={'class': tailwind_class}), + error_messages={ + 'unique': 'This username is already in use.', + 'invalid': 'Invalid username format.', + 'max_length': 'Username should not exceed 150 characters.', + } + ) + password1 = forms.CharField(widget=forms.PasswordInput(attrs={'class': tailwind_class}), + error_messages={'min_length': 'Password must contain at least 8 characters.',} + ) + password2 = forms.CharField(widget=forms.PasswordInput(attrs={'class': tailwind_class}),) + + class Meta: + model = User + fields = ('username', 'password1', 'password2') + + error_messages = { + 'password_mismatch': "The two password fields didn't match.", + } \ No newline at end of file diff --git a/polls/models.py b/polls/models.py index 1e06a4e..24d3eb2 100644 --- a/polls/models.py +++ b/polls/models.py @@ -14,6 +14,7 @@ from django.utils import timezone from django.contrib import admin from django.core.validators import MaxValueValidator, MinValueValidator from django.db.models import Sum +from django.contrib.auth.models import User class Question(models.Model): diff --git a/polls/templates/polls/index.html b/polls/templates/polls/index.html index ff23cdc..3451240 100644 --- a/polls/templates/polls/index.html +++ b/polls/templates/polls/index.html @@ -26,15 +26,22 @@ - - + {% if user.is_authenticated %} + + + Sign out + + {% else %} + Sign in + {% endif %} diff --git a/polls/templates/registration/login.html b/polls/templates/registration/login.html new file mode 100644 index 0000000..99361b9 --- /dev/null +++ b/polls/templates/registration/login.html @@ -0,0 +1,37 @@ + + + + + + Sign In Page + + + +
+

Sign In

+
+ {% csrf_token %} +
+

Username

+ {{ form.username }} +
+
+

Password

+ {{ form.password }} +
+ +
+

+ Don't have an account? Sign up here +

+

+ Forget the Password? Reset here +

+ Back to Poll +
+ + diff --git a/polls/templates/registration/password_reset_complete.html b/polls/templates/registration/password_reset_complete.html new file mode 100644 index 0000000..066a37a --- /dev/null +++ b/polls/templates/registration/password_reset_complete.html @@ -0,0 +1,2 @@ +

Password reset complete

+

Your new password has been set. You can log in now on the log in page.

\ No newline at end of file diff --git a/polls/templates/registration/password_reset_confirm.html b/polls/templates/registration/password_reset_confirm.html new file mode 100644 index 0000000..13db400 --- /dev/null +++ b/polls/templates/registration/password_reset_confirm.html @@ -0,0 +1,12 @@ +{% if validlink %} + +

Set a new password!

+
+ {% csrf_token %} + {{ form.as_p }} + +
+ +{% else %} + +

The password reset link was invalid, possibly because it has already been used. Please request a new password reset.

\ No newline at end of file diff --git a/polls/templates/registration/password_reset_done.html b/polls/templates/registration/password_reset_done.html new file mode 100644 index 0000000..fa1ed5a --- /dev/null +++ b/polls/templates/registration/password_reset_done.html @@ -0,0 +1,2 @@ +

Check your inbox.

+

We've emailed you instructions for setting your password. You should receive the email shortly!

\ No newline at end of file diff --git a/polls/templates/registration/password_reset_form.html b/polls/templates/registration/password_reset_form.html new file mode 100644 index 0000000..98b091c --- /dev/null +++ b/polls/templates/registration/password_reset_form.html @@ -0,0 +1,8 @@ +

Forgot your password?

+

Enter your email address below, and we'll email instructions for setting a new one.

+ +
+ {% csrf_token %} + {{ form.as_p }} + +
\ No newline at end of file diff --git a/polls/templates/registration/signup.html b/polls/templates/registration/signup.html new file mode 100644 index 0000000..0bac13d --- /dev/null +++ b/polls/templates/registration/signup.html @@ -0,0 +1,46 @@ + + + + + Sign Up Page + + + +
+
+

Sign Up

+
+ {% csrf_token %} +
+

Username

+ {{ form.username }} +
+
+

Password

+ {{ form.password1 }} +
+

Password Confirmation

+ {{ form.password2 }} +
+ + +

Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.

+

Your password can’t be too similar to your other personal information. , must contain at least 8 characters, can’t be entirely numeric.

+ +
+ {% if form.errors %} + {% for field in form %} + {% if field.errors %} + {% for error in field.errors %} +

{{ error }}

+ {% endfor %} + {% endif %} + {% endfor %} + {% endif %} +
+

Already have an account? Sign in here

+ Back to Poll +
+
+ + \ No newline at end of file diff --git a/polls/urls.py b/polls/urls.py index 45ebddd..7ddb37f 100644 --- a/polls/urls.py +++ b/polls/urls.py @@ -8,4 +8,5 @@ urlpatterns = [ path("/", views.DetailView.as_view(), name="detail"), path("/results/", views.ResultsView.as_view(), name="results"), path("/vote/", views.vote, name="vote"), + path("signup/", views.SignUpView.as_view(), name="signup"), ] diff --git a/polls/views.py b/polls/views.py index 9abf5a9..7c65469 100644 --- a/polls/views.py +++ b/polls/views.py @@ -3,8 +3,9 @@ from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from django.utils import timezone -from django.views.generic import TemplateView +from django.urls import reverse_lazy +from .forms import SignUpForm from .models import Choice, Question @@ -66,6 +67,12 @@ class ResultsView(generic.DetailView): return render(self.request, self.template_name, context) +class SignUpView(generic.CreateView): + form_class = SignUpForm + success_url = reverse_lazy('login') + template_name = 'registration/signup.html' + + def vote(request, question_id): """ A function that update the database. Add vote count to choice that user vote From 20a1d6c0fd563f5e5cc22ad7c63b60ea9766ddd7 Mon Sep 17 00:00:00 2001 From: sosokker Date: Mon, 11 Sep 2023 23:46:27 +0700 Subject: [PATCH 2/7] Redirect when non-Authenticate User go to detail/result --- polls/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/polls/views.py b/polls/views.py index 7c65469..40eaf83 100644 --- a/polls/views.py +++ b/polls/views.py @@ -4,6 +4,7 @@ from django.urls import reverse from django.views import generic from django.utils import timezone from django.urls import reverse_lazy +from django.contrib.auth.mixins import LoginRequiredMixin from .forms import SignUpForm from .models import Choice, Question @@ -22,7 +23,7 @@ class IndexView(generic.ListView): )[:5] -class DetailView(generic.DetailView): +class DetailView(LoginRequiredMixin, generic.DetailView): """ Provide a view for detail page, a detail for each poll contain poll question and poll choices. @@ -54,7 +55,7 @@ class DetailView(generic.DetailView): return context -class ResultsView(generic.DetailView): +class ResultsView(LoginRequiredMixin, generic.DetailView): model = Question template_name = "polls/results.html" From 791d9c3b0d3f913ace8b859230fc1cee4c6bf94d Mon Sep 17 00:00:00 2001 From: sosokker Date: Tue, 12 Sep 2023 00:33:57 +0700 Subject: [PATCH 3/7] Use message framework in vote --- polls/templates/polls/detail.html | 23 ++++++-------------- polls/templates/polls/results.html | 14 +++++++++++- polls/views.py | 34 +++++++++++++++--------------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/polls/templates/polls/detail.html b/polls/templates/polls/detail.html index 6778f30..e08d9f8 100644 --- a/polls/templates/polls/detail.html +++ b/polls/templates/polls/detail.html @@ -16,9 +16,8 @@
- -
+ {% comment %}
@@ -33,10 +32,10 @@
-
+ {% endcomment %} -
+
@@ -50,11 +49,6 @@
Please Select a Choice😊
- {% if error_message %} -
-

{{ error_message }}

-
- {% endif %}
@@ -71,7 +65,7 @@ {% endfor %}
- + diff --git a/polls/templates/polls/results.html b/polls/templates/polls/results.html index 2ecdf3a..9f9b845 100644 --- a/polls/templates/polls/results.html +++ b/polls/templates/polls/results.html @@ -25,7 +25,19 @@ -
+ +
+ {% for message in messages %} + {% if message.tags == 'success' %} +
+
+

{{ message }}

+
+
+
+ {% endif %} + {% endfor %}
diff --git a/polls/views.py b/polls/views.py index 40eaf83..9bef0b9 100644 --- a/polls/views.py +++ b/polls/views.py @@ -1,10 +1,12 @@ from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404, render +from django.shortcuts import get_object_or_404, render, redirect from django.urls import reverse from django.views import generic from django.utils import timezone -from django.urls import reverse_lazy +from django.urls import reverse_lazy, reverse from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib import messages +from django.contrib.auth.decorators import login_required from .forms import SignUpForm from .models import Choice, Question @@ -73,26 +75,24 @@ class SignUpView(generic.CreateView): success_url = reverse_lazy('login') template_name = 'registration/signup.html' - +@login_required def vote(request, question_id): """ - A function that update the database. Add vote count to choice that user vote - in specific question_id. + A function that updates the database. Adds a vote count to the choice that the user votes for + in a specific question_id. """ - question = get_object_or_404(Question, pk=question_id) try: selected_choice = question.choice_set.get(pk=request.POST["choice"]) except (KeyError, Choice.DoesNotExist): - return render( - request, - "polls/detail.html", - { - "question": question, - "error_message": "You didn't select a choice.", - }, - ) + messages.error(request, "You didn't select a choice.") + return render(request, "polls/detail.html", {"question": question}) else: - selected_choice.votes += 1 - selected_choice.save() - return HttpResponseRedirect(reverse("polls:results", args=(question.id,))) + if request.method == "POST" and "vote-button" in request.POST: + selected_choice.votes += 1 + selected_choice.save() + messages.success(request, "Your vote was recorded successfully🥳") + return HttpResponseRedirect(reverse("polls:results", args=(question.id,))) + else: + messages.error(request, "You cannot vote by typing the URL.") + return render(request, "polls/detail.html", {"question": question}) From 50874dbf40b1f619845e42d22a74720884d513af Mon Sep 17 00:00:00 2001 From: sosokker Date: Tue, 12 Sep 2023 02:47:21 +0700 Subject: [PATCH 4/7] Fix query in Index / Check User Vote/ Refine Model - Use Model from domain model - Change Vote Query because I change model - Write some properties such as participants in Question --- polls/admin.py | 6 +- ..._remove_choice_votes_vote_question_tags.py | 41 +++++++ polls/models.py | 87 +++++++-------- polls/static/polls/js/detail.js | 104 +++++++++++++----- polls/templates/polls/detail.html | 22 +++- polls/templates/polls/index.html | 4 +- polls/templates/polls/results.html | 12 +- polls/views.py | 60 +++++++--- 8 files changed, 227 insertions(+), 109 deletions(-) create mode 100644 polls/migrations/0009_tag_remove_choice_votes_vote_question_tags.py diff --git a/polls/admin.py b/polls/admin.py index ee1e244..3d70f89 100644 --- a/polls/admin.py +++ b/polls/admin.py @@ -13,12 +13,12 @@ class QuestionAdmin(admin.ModelAdmin): (None, {"fields": ["question_text"]}), ("Published date", {"fields": ["pub_date"], "classes": ["collapse"]}), ("End date", {"fields": ["end_date"], "classes": ["collapse"]}), - ("Vote count", {"fields": ["up_vote_count", "down_vote_count"]}), + ("Sentiment Vote count", {"fields": ["up_vote_count", "down_vote_count"]}), ("Participant count", {"fields": ["participant_count"]}), ] - list_display = ["question_text", "pub_date", "end_date", "was_published_recently"] + list_display = ["question_text", "pub_date", "end_date", "was_published_recently", "can_vote"] inlines = [ChoiceInline] - list_filter = ["pub_date"] + list_filter = ["pub_date", ] search_fields = ["question_text"] diff --git a/polls/migrations/0009_tag_remove_choice_votes_vote_question_tags.py b/polls/migrations/0009_tag_remove_choice_votes_vote_question_tags.py new file mode 100644 index 0000000..b724c1f --- /dev/null +++ b/polls/migrations/0009_tag_remove_choice_votes_vote_question_tags.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.4 on 2023-09-11 18:23 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('polls', '0008_alter_question_pub_date'), + ] + + operations = [ + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('tag_text', models.CharField(max_length=50)), + ], + ), + migrations.RemoveField( + model_name='choice', + name='votes', + ), + migrations.CreateModel( + name='Vote', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('choice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.choice')), + ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='question', + name='tags', + field=models.ManyToManyField(blank=True, to='polls.tag'), + ), + ] diff --git a/polls/models.py b/polls/models.py index 24d3eb2..217b0bb 100644 --- a/polls/models.py +++ b/polls/models.py @@ -17,6 +17,16 @@ from django.db.models import Sum from django.contrib.auth.models import User +class Tag(models.Model): + """ + Represents a tag for a poll question. + """ + tag_text = models.CharField(max_length=50) + + def __str__(self): + return self.name + + class Question(models.Model): """ Represents a poll question. @@ -33,23 +43,15 @@ class Question(models.Model): """ question_text = models.CharField(max_length=100) - short_description = models.CharField(max_length=200, default="Cool kids have polls") - long_description = models.TextField( - max_length=2000, default="No description provide for this poll." - ) - pub_date = models.DateTimeField( - "date published", default=timezone.now, editable=True - ) + pub_date = models.DateTimeField("date published", default=timezone.now, editable=True) end_date = models.DateTimeField("date ended", null=True) - up_vote_count = models.PositiveIntegerField( - default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)] - ) - down_vote_count = models.PositiveIntegerField( - default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)] - ) - participant_count = models.PositiveIntegerField( - default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)] - ) + short_description = models.CharField(max_length=200, default="Cool kids have polls") + long_description = models.TextField(max_length=2000, default="No description provide for this poll.") + tags = models.ManyToManyField(Tag, blank=True) + + up_vote_count = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) + down_vote_count = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) + participant_count = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)]) def was_published_recently(self): """ @@ -151,6 +153,13 @@ class Question(models.Model): def down_vote_percentage(self): return self.calculate_vote_percentage()[1] + @property + def participants(self): + """ + Calculate the number of participants based on the number of votes. + """ + return self.vote_set.count() + class Choice(models.Model): """ @@ -164,40 +173,24 @@ class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) - votes = models.PositiveIntegerField( - default=0, validators=[MinValueValidator(0), MaxValueValidator(2147483647)] - ) - def tailwind_width_class(self): - """ - Calculate and return the Tailwind CSS width class based on the 'votes' percentage. - """ - total_votes = self.question.choice_set.aggregate(Sum("votes")).get( - "votes__sum", 0 - ) - #! Tailwind w-0 to w-48 - if total_votes == 0: - return "w-0" - - ratio = self.votes / total_votes - - scaled_value = ratio * 48 - - return f"w-{int(round(scaled_value))}" - - def calculate_percentage(self): - """Calculate percentage of votes for all choices.""" - total_votes_for_question = ( - self.question.choice_set.aggregate(Sum("votes"))["votes__sum"] or 0 - ) - - if total_votes_for_question == 0: - return 0 - else: - return round((self.votes / total_votes_for_question) * 100, 2) + @property + def votes(self): + return self.vote_set.count() def __str__(self): """ Returns a string representation of the choice. """ - return self.choice_text + return f"{self.choice_text} get ({self.votes})" + + +class Vote(models.Model): + """Represent Vote of User for a poll question.""" + + choice = models.ForeignKey(Choice, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + question = models.ForeignKey(Question, on_delete=models.CASCADE) + + def __str__(self): + return f"{self.user} voted for {self.choice} in {self.question}" \ No newline at end of file diff --git a/polls/static/polls/js/detail.js b/polls/static/polls/js/detail.js index 86e7e5a..13c42a0 100644 --- a/polls/static/polls/js/detail.js +++ b/polls/static/polls/js/detail.js @@ -1,38 +1,82 @@ const toggleChoice = (button, choiceId) => { - const choiceInput = document.querySelector(`input[name="choice"][value="${choiceId}"]`); + const choiceInput = document.querySelector(`input[name="choice"][value="${choiceId}"]`); + const selectedChoice2 = document.getElementById("selected-choice-1"); + + if (selectedChoice2 !== null) { if (choiceInput) { - // already selected -> unselect it - if (choiceInput.checked) { - choiceInput.checked = false; - button.classList.remove('bg-green-500', 'border-solid', 'border-2', 'border-black', 'hover:bg-green-600'); - button.classList.add('bg-white', 'border-solid', 'border-2', 'border-black', 'hover:bg-white'); - // Clear display - document.getElementById('selected-choice').textContent = 'Please Select a Choice😊'; - } else { - // Unselect all choices - document.querySelectorAll('input[name="choice"]').forEach((choice) => { - choice.checked = false; - }); + // already selected -> unselect it + if (choiceInput.checked) { + choiceInput.checked = false; + button.classList.remove("bg-green-500", "border-solid", "border-2", "border-black", "hover:bg-green-600"); + button.classList.add("bg-white", "border-solid", "border-2", "border-black", "hover:bg-white"); + // Clear display + document.getElementById("selected-choice-1").textContent = "You have been voted😊 Anyway, you can change the choice anytime before end date"; + } else { + // Unselect all choices + document.querySelectorAll('input[name="choice"]').forEach(choice => { + choice.checked = false; + }); - // Select the clicked choice - choiceInput.checked = true; + // Select the clicked choice + choiceInput.checked = true; - // Reset the style of all choice buttons - document.querySelectorAll('.choice-button').forEach((btn) => { - btn.classList.remove('bg-green-500', 'border-solid', 'border-2', 'border-black', 'hover:bg-green-600'); - btn.classList.add('bg-white', 'border-solid', 'border-2', 'border-black', 'hover:bg-white'); - }); + // Reset the style of all choice buttons + document.querySelectorAll(".choice-button").forEach(btn => { + btn.classList.remove("bg-green-500", "border-solid", "border-2", "border-black", "hover:bg-green-600"); + btn.classList.add("bg-white", "border-solid", "border-2", "border-black", "hover:bg-white"); + }); - button.classList.remove('bg-white', 'border-solid', 'border-2', 'border-black', 'hover:bg-white'); - button.classList.add('bg-green-500', 'border-solid', 'border-2', 'border-black', 'hover:bg-green-600'); + button.classList.remove("bg-white", "border-solid", "border-2", "border-black", "hover:bg-white"); + button.classList.add("bg-green-500", "border-solid", "border-2", "border-black", "hover:bg-green-600"); - const choiceText = button.textContent.trim(); - document.getElementById('selected-choice').textContent = `You select: ${choiceText}`; - } - - // Enable the "Vote" button -> if select - const voteButton = document.getElementById('vote-button'); - voteButton.disabled = !document.querySelector('input[name="choice"]:checked'); + const choiceText = button.textContent.trim(); + document.getElementById("selected-choice-1").textContent = `You select: ${choiceText}`; + } } -}; \ No newline at end of file + // Enable the "Vote" button -> if select + const voteButton = document.getElementById("vote-button"); + voteButton.disabled = !document.querySelector('input[name="choice"]:checked'); + } else { + if (choiceInput) { + // already selected -> unselect it + if (choiceInput.checked) { + choiceInput.checked = false; + button.classList.remove("bg-green-500", "border-solid", "border-2", "border-black", "hover:bg-green-600"); + button.classList.add("bg-white", "border-solid", "border-2", "border-black", "hover:bg-white"); + // Clear display + document.getElementById("selected-choice-2").textContent = "Please Select a Choice😊"; + } else { + // Unselect all choices + document.querySelectorAll('input[name="choice"]').forEach(choice => { + choice.checked = false; + }); + + // Select the clicked choice + choiceInput.checked = true; + + // Reset the style of all choice buttons + document.querySelectorAll(".choice-button").forEach(btn => { + btn.classList.remove("bg-green-500", "border-solid", "border-2", "border-black", "hover:bg-green-600"); + btn.classList.add("bg-white", "border-solid", "border-2", "border-black", "hover:bg-white"); + }); + + button.classList.remove("bg-white", "border-solid", "border-2", "border-black", "hover:bg-white"); + button.classList.add("bg-green-500", "border-solid", "border-2", "border-black", "hover:bg-green-600"); + + const choiceText = button.textContent.trim(); + document.getElementById("selected-choice-2").textContent = `You select: ${choiceText}`; + } + } + // Enable the "Vote" button -> if select + const voteButton = document.getElementById("vote-button"); + voteButton.disabled = !document.querySelector('input[name="choice"]:checked'); + } +}; + +function confirmChangeVote(choiceId) { + const confirmation = confirm("Are you sure you want to change your vote?"); + if (confirmation) { + window.location.href = `{% url 'polls:vote' question.id %}${choiceId}`; + } +} diff --git a/polls/templates/polls/detail.html b/polls/templates/polls/detail.html index e08d9f8..08614ea 100644 --- a/polls/templates/polls/detail.html +++ b/polls/templates/polls/detail.html @@ -46,14 +46,29 @@
{% csrf_token %} -
-
Please Select a Choice😊
-
+ {% if selected_choice %} +
+
You have been voted: {{ selected_choice.choice_text }}
+
+ {% else %} +
+
Please Select a Choice😊
+
+ {% endif %}
{% for choice in question.choice_set.all %} {% endfor %}
diff --git a/polls/templates/polls/index.html b/polls/templates/polls/index.html index 3451240..f215620 100644 --- a/polls/templates/polls/index.html +++ b/polls/templates/polls/index.html @@ -95,7 +95,7 @@
🕒 {{ question.time_left }} - {{ question.participant_count }} Participants 👤 + {{ question.participants }} Participants 👤