mirror of
https://github.com/Sosokker/ku-polls.git
synced 2025-12-18 13:04:05 +01:00
Poll Creation - Authenticate User can create new poll
This commit is contained in:
parent
7e57da4b89
commit
c81dea9a14
@ -5,6 +5,7 @@ from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import Question, Tag
|
||||
|
||||
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"
|
||||
@ -45,3 +46,30 @@ class PollSearchForm(forms.Form):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class PollCreateForm(forms.ModelForm):
|
||||
box_style = "w-full py-2 px-2 border-2 border-gray-300 bg-gray-100 rounded-lg focus:ring focus:border-blue-300 focus:shadow-none"
|
||||
large_box_style = "w-full border-2 border-gray-300 bg-gray-100 rounded-lg focus:ring focus:border-blue-300 focus:shadow-none"
|
||||
|
||||
question_text = forms.CharField(min_length=10, max_length=100, required=True,
|
||||
widget=forms.TextInput(attrs={'class':box_style, 'placeholder':"What is your question?"}))
|
||||
pub_date = forms.DateTimeField(widget=forms.DateInput(attrs={'type': 'date'}))
|
||||
end_date = forms.DateTimeField(widget=forms.DateInput(attrs={'type': 'date'}))
|
||||
short_description = forms.CharField(max_length=200, widget=forms.TextInput(attrs={'class':box_style, 'placeholder':"Short description (Maximum 200 characters)"}))
|
||||
long_description = forms.CharField(max_length=2000, widget=forms.Textarea(attrs={'class':large_box_style, 'placeholder':"Long description (Maximum 2000 characters)"}))
|
||||
tags = forms.MultipleChoiceField(
|
||||
choices=[(tag.id, tag.tag_text) for tag in Tag.objects.all()],
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
user_choice = forms.CharField(
|
||||
widget=forms.TextInput(attrs={'placeholder': 'Enter a choice'}),
|
||||
required=True
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
class Meta:
|
||||
model = Question
|
||||
fields = ['question_text', 'pub_date', 'end_date', 'short_description', 'long_description', 'tags']
|
||||
|
||||
145
polls/templates/polls/creation.html
Normal file
145
polls/templates/polls/creation.html
Normal file
@ -0,0 +1,145 @@
|
||||
{% extends "polls/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<header>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/1.8.1/flowbite.min.css" rel="stylesheet" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/1.8.1/flowbite.min.js"></script>
|
||||
</header>
|
||||
<div class="min-h-screen bg-gray-100 flex items-center justify-center p-8">
|
||||
<div class="bg-white p-8 shadow-md rounded-md w-full border-solid border-neutral-500 border-2">
|
||||
|
||||
<a href="{% url 'polls:index' %}" class="block text-blue-500 hover:underline mb-4">< Back</a>
|
||||
|
||||
<form method="post" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.question_text.id_for_label }}">Question</label>
|
||||
{{ form.question_text }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.pub_date.id_for_label }}">Publication Date</label>
|
||||
{{ form.pub_date }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.end_date.id_for_label }}">End Date</label>
|
||||
{{ form.end_date }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.short_description.id_for_label }}">Short Description</label>
|
||||
{{ form.short_description }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.long_description.id_for_label }}">Long Description</label>
|
||||
{{ form.long_description }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2">Tags</label>
|
||||
{{ form.tags }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2">Choice(Seperate by comma)</label>
|
||||
{{ form.user_choice }}
|
||||
</div>
|
||||
{% comment %} <div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.user_choice.id_for_label }}">User Choice</label>
|
||||
<div class="flex space-x-2">
|
||||
<input type="text" id="{{ form.user_choice.id_for_label }}" class="form-input" placeholder="{{ form.user_choice.field.widget.attrs.placeholder }}">
|
||||
<button type="button" id="add-choice-button" class="bg-blue-500 text-white font-bold py-2 px-4 rounded-full hover:bg-blue-700 focus:outline-none focus:shadow-outline-blue active:bg-blue-800">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="user-choices-container" class="mb-4 pb-2">
|
||||
</div> {% endcomment %}
|
||||
|
||||
<hr class="h-px my-4 bg-neutral-0 border-0 dark:bg-neutral-0" />
|
||||
<!--Create Button-->
|
||||
<a data-modal-target="popup-modal" data-modal-toggle="popup-modal" class="bg-blue-500 text-white font-bold py-2 mt-3 px-4 rounded-full hover:bg-blue-700 focus:outline-none focus:shadow-outline-blue active:bg-blue-800 cursor-pointer">Create Poll</a>
|
||||
|
||||
<!--Code from https://flowbite.com/docs/components/modal/-->
|
||||
<div id="popup-modal" tabindex="-1" class="fixed top-0 left-0 right-0 z-50 hidden p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative w-full max-w-md max-h-full">
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||
<button type="button" class="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="popup-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
<div class="p-6 text-center">
|
||||
<svg class="mx-auto mb-4 text-gray-400 w-12 h-12 dark:text-gray-200" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
||||
</svg>
|
||||
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">Are you sure to create this poll?</h3>
|
||||
<button data-modal-hide="popup-modal" type="submit" class="text-white bg-green-500 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
|
||||
Yes, I'm sure
|
||||
</button>
|
||||
<button data-modal-hide="popup-modal" type="button" class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">No, cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--Thank you CHATGPT-->
|
||||
<!--Thank you CHATGPT-->
|
||||
<!--Thank you CHATGPT-->
|
||||
{% comment %} <script>
|
||||
const userChoicesContainer = document.getElementById('user-choices-container');
|
||||
const userInput = document.getElementById('{{ form.user_choice.id_for_label }}');
|
||||
const addButton = document.getElementById('add-choice-button');
|
||||
|
||||
// Function to create a new choice element
|
||||
function createChoiceElement(choiceValue) {
|
||||
const choiceElement = document.createElement('div');
|
||||
choiceElement.textContent = choiceValue;
|
||||
choiceElement.className = 'bg-gray-300 p-2 rounded-md';
|
||||
|
||||
// Add a delete button next to the choice
|
||||
const deleteButton = document.createElement('button');
|
||||
deleteButton.textContent = 'Delete';
|
||||
deleteButton.className = 'bg-red-500 text-white font-bold py-1 px-2 mx-4 rounded-full hover:bg-red-700 focus:outline-none focus:shadow-outline-red active:bg-red-800';
|
||||
deleteButton.addEventListener('click', function () {
|
||||
// Remove the choice element when the delete button is clicked
|
||||
userChoicesContainer.removeChild(choiceElement);
|
||||
});
|
||||
|
||||
choiceElement.appendChild(deleteButton);
|
||||
return choiceElement;
|
||||
}
|
||||
|
||||
addButton.addEventListener('click', function () {
|
||||
const choiceValue = userInput.value.trim();
|
||||
|
||||
if (choiceValue !== '') {
|
||||
// Create a new choice element
|
||||
const choiceElement = createChoiceElement(choiceValue);
|
||||
|
||||
// Append the choice element to the container
|
||||
userChoicesContainer.appendChild(choiceElement);
|
||||
|
||||
// Clear the user input
|
||||
userInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle delete button clicks using event delegation
|
||||
userChoicesContainer.addEventListener('click', function (event) {
|
||||
if (event.target && event.target.tagName === 'BUTTON' && event.target.textContent === 'Delete') {
|
||||
// Remove the choice element when a delete button is clicked
|
||||
userChoicesContainer.removeChild(event.target.parentNode);
|
||||
}
|
||||
});
|
||||
</script> {% endcomment %}
|
||||
|
||||
{% endblock content %}
|
||||
@ -12,9 +12,6 @@
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
{% comment %}
|
||||
<button class="mr-4 rounded-md bg-green-500 px-4 py-2 text-white">New Poll</button>
|
||||
{% endcomment %}
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<header class="flex items-center justify-center">
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
@ -34,9 +31,9 @@
|
||||
</div>
|
||||
</header>
|
||||
{% if user.is_authenticated %}
|
||||
<button class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
<a href="{% url "polls:create_poll" %}" class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
New Poll
|
||||
</button>
|
||||
</a>
|
||||
<a href="{% url 'logout' %}"
|
||||
class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-red-600 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-red-700 focus:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-neutral-900">
|
||||
<span>Sign out <span class="hidden sm:inline-block">😭</span></span>
|
||||
|
||||
@ -12,4 +12,5 @@ urlpatterns = [
|
||||
path("upvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'upvote'}, name="upvote"),
|
||||
path("downvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'downvote'}, name="downvote"),
|
||||
path("search", views.search_poll, name="search_poll"),
|
||||
path("create", views.create_poll, name="create_poll")
|
||||
]
|
||||
|
||||
@ -12,7 +12,7 @@ from django.contrib.auth import authenticate, login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Q
|
||||
|
||||
from .forms import SignUpForm, PollSearchForm
|
||||
from .forms import SignUpForm, PollSearchForm, PollCreateForm
|
||||
from .models import Choice, Question, Vote
|
||||
|
||||
|
||||
@ -222,4 +222,41 @@ def search_poll(request):
|
||||
# * If user search with empty string then show every poll.
|
||||
if q == '':
|
||||
results = Question.objects.all()
|
||||
return render(request, 'polls/search.html', {'form':form, 'results':results, 'q':q})
|
||||
return render(request, 'polls/search.html', {'form':form, 'results':results, 'q':q})
|
||||
|
||||
|
||||
@login_required
|
||||
def create_poll(request):
|
||||
ip = get_client_ip(request)
|
||||
if request.method == 'POST':
|
||||
form = PollCreateForm(request.POST)
|
||||
if form.is_valid():
|
||||
question_text = form.cleaned_data['question_text']
|
||||
pub_date = form.cleaned_data['pub_date']
|
||||
end_date = form.cleaned_data['end_date']
|
||||
short_description = form.cleaned_data['short_description']
|
||||
long_description = form.cleaned_data.get('long_description', '')
|
||||
user_choices = form.cleaned_data['user_choice']
|
||||
tags = form.cleaned_data['tags']
|
||||
|
||||
question = Question.objects.create(
|
||||
question_text=question_text,
|
||||
pub_date=pub_date,
|
||||
end_date=end_date,
|
||||
short_description=short_description,
|
||||
long_description=long_description,
|
||||
)
|
||||
|
||||
choices = user_choices.split(',') # Split with comma
|
||||
for choice_text in choices:
|
||||
Choice.objects.create(question=question, choice_text=choice_text.strip())
|
||||
|
||||
# Add tags to the question
|
||||
question.tags.set(tags)
|
||||
logger.info(f"User {request.user.username} ({ip}) create poll : {question_text}")
|
||||
return redirect('polls:index')
|
||||
|
||||
else:
|
||||
form = PollCreateForm()
|
||||
|
||||
return render(request, 'polls/creation.html', {'form': form})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user