vote csrf & ajax
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<a href="{% url 'citizen_dashboard' %}" class="btn btn-primary btn-lg px-4 gap-3">Report an Issue</a>
|
<a href="{% url 'citizen_dashboard' %}" class="btn btn-primary btn-lg px-4 gap-3">Report an Issue</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'register' %}" class="btn btn-primary btn-lg px-4 gap-3">Report an Issue</a>
|
<a href="{% url 'login' %}" class="btn btn-primary btn-lg px-4 gap-3">Report an Issue</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="#how-it-works" class="btn btn-outline-light btn-lg px-4">Learn More</a>
|
<a href="#how-it-works" class="btn btn-outline-light btn-lg px-4">Learn More</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2 class="fw-bold">Recently Reported Issues</h2>
|
<h2 class="fw-bold">Recently Reported Issues</h2>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<a href="{% url 'citizen_dashboard' %}" class="btn btn-outline-primary">View All Issues</a>
|
<a href="{% url 'view_all_issues' %}" class="btn btn-primary">View All Issues</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'register' %}" class="btn btn-outline-primary">View All Issues</a>
|
<a href="{% url 'register' %}" class="btn btn-outline-primary">View All Issues</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -148,7 +148,22 @@
|
|||||||
<p class="card-text text-muted">{{ issue.description|truncatewords:15 }}</p>
|
<p class="card-text text-muted">{{ issue.description|truncatewords:15 }}</p>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<small class="text-muted"><i class="fas fa-map-marker-alt me-1"></i> {{ issue.location|truncatewords:2 }}</small>
|
<small class="text-muted"><i class="fas fa-map-marker-alt me-1"></i> {{ issue.location|truncatewords:2 }}</small>
|
||||||
<small class="text-muted"><i class="fas fa-thumbs-up me-1"></i> {{ issue.votes.count|default:"0" }}</small>
|
|
||||||
|
<!-- Vote Button -->
|
||||||
|
<div class="vote-section">
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<button class="btn btn-sm btn-outline-primary vote-btn {% if issue.user_has_voted %}active{% endif %}"
|
||||||
|
data-issue-id="{{ issue.id }}">
|
||||||
|
<i class="fas fa-thumbs-up"></i>
|
||||||
|
<span class="vote-count">{{ issue.vote_count }}</span>
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url 'login' %}" class="btn btn-sm btn-outline-primary">
|
||||||
|
<i class="fas fa-thumbs-up"></i>
|
||||||
|
<span class="vote-count">{{ issue.vote_count }}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer bg-transparent">
|
<div class="card-footer bg-transparent">
|
||||||
@@ -182,4 +197,72 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Vote functionality
|
||||||
|
$('.vote-btn').click(function() {
|
||||||
|
const issueId = $(this).data('issue-id');
|
||||||
|
const button = $(this);
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
const originalHtml = button.html();
|
||||||
|
button.prop('disabled', true);
|
||||||
|
button.html('<i class="fas fa-spinner fa-spin"></i>');
|
||||||
|
|
||||||
|
// Send vote request
|
||||||
|
$.ajax({
|
||||||
|
url: "{% url 'vote_issue' 0 %}".replace('0', issueId),
|
||||||
|
type: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': '{{ csrf_token }}'
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
// Update button state
|
||||||
|
if (response.voted) {
|
||||||
|
button.addClass('active');
|
||||||
|
} else {
|
||||||
|
button.removeClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update vote count
|
||||||
|
button.find('.vote-count').text(response.vote_count);
|
||||||
|
|
||||||
|
// Update button text
|
||||||
|
button.html('<i class="fas fa-thumbs-up"></i> <span class="vote-count">' + response.vote_count + '</span>');
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + response.error);
|
||||||
|
button.html(originalHtml);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('Error voting. Please try again.');
|
||||||
|
button.html(originalHtml);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
button.prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.vote-btn.active {
|
||||||
|
background-color: #0d6efd;
|
||||||
|
color: white;
|
||||||
|
border-color: #0d6efd;
|
||||||
|
}
|
||||||
|
.vote-btn:hover:not(:disabled) {
|
||||||
|
transform: scale(1.05);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.vote-btn:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user