-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Feature [276] Plugins feedback (multiple tasks) (#284)
* added PluginVersionFeedback model * added version_feedback_notify * added plugin feedback received and feedback pending view and url * updated plugin base and detail html template * added create feedback view * notified user when create feedback * added version_feedback_update * updated template and ajax requests * never cache feedback * use submit button for feedback update * updated layout
- Loading branch information
Showing
11 changed files
with
1,037 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
flake8 | ||
pre-commit | ||
freezegun |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Generated by Django 2.2.25 on 2023-06-17 03:19 | ||
|
||
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), | ||
('plugins', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='PluginVersionFeedback', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('task', models.TextField(help_text='A feedback task. Please write your review as a task for this plugin.', max_length=1000, verbose_name='Task')), | ||
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='Created on')), | ||
('completed_on', models.DateTimeField(blank=True, null=True, verbose_name='Completed on')), | ||
('is_completed', models.BooleanField(db_index=True, default=False, verbose_name='Completed')), | ||
('reviewer', models.ForeignKey(help_text='The user who reviewed this plugin.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Reviewed by')), | ||
('version', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedback', to='plugins.PluginVersion')), | ||
], | ||
options={ | ||
'ordering': ['created_on'], | ||
}, | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
170 changes: 170 additions & 0 deletions
170
qgis-app/plugins/templates/plugins/plugin_feedback.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
{% extends 'plugins/plugin_base.html' %}{% load i18n %} | ||
{% block content %} | ||
{% if form.errors %} | ||
<div class="alert alert-error"> | ||
<button type="button" class="close" data-dismiss="alert">×</button> | ||
<p>{% trans "The form contains errors and cannot be submitted, please check the fields highlighted in red." %}</p> | ||
</div> | ||
{% endif %} | ||
{% if form.non_field_errors %} | ||
<div class="alert alert-error"> | ||
<button type="button" class="close" data-dismiss="alert">×</button> | ||
{% for error in form.non_field_errors %} | ||
<p>{{ error }}</p> | ||
{% endfor %} | ||
</div> | ||
{% endif %} | ||
<h2>{% trans "Feedback Plugin" %} {{ version.plugin.name }} {{ version.version }}</h2> | ||
<div class="feedback-list"> | ||
<p>Please tick the checkbox when the task is completed and click the "Update" button to update status.</p> | ||
{% for feedback in feedbacks %} | ||
<div class=" previous-feedback {% if feedback.reviewer == request.user %}with-box{% endif %}" data-feedback-id="{{ feedback.id }}"> | ||
<input type="checkbox" class="statusCheckbox pull-left" name="statusCheckbox" data-feedback-id="{{ feedback.id }}" {% if feedback.is_completed %}checked disabled{% endif %}> | ||
<label class="feedback"> | ||
{{ feedback.task }} | ||
<span class="feedback-info"> —{% if feedback.reviewer.first_name %}{{ feedback.reviewer.first_name }} {{ feedback.reviewer.last_name }}{% else %}{{ feedback.reviewer.username }}{% endif %} | ||
wrote {{ feedback.created_on|timesince }} ago | ||
</span> | ||
</label> | ||
{% if feedback.reviewer == request.user %} | ||
<button type="button" class="btn btn-danger btn-mini deleteButton pull-right" data-feedback-id="{{ feedback.id }}"><i class="icon-remove"></i></button> | ||
{% endif %} | ||
</div> | ||
{% endfor %} | ||
{% if feedbacks %} | ||
<div class="text-center update-feedback"> | ||
<button type="button" id="updateButton" class="btn btn-primary">Update</button> | ||
</div> | ||
{% endif %} | ||
|
||
{% if is_user_has_approval_rights %} | ||
<div class="new-feedback"> | ||
<form method="post" action="{% url 'version_feedback' version.plugin.package_name version.version %}">{% csrf_token %} | ||
<b>New Feedback</b> | ||
{{ form.feedback }} | ||
<div class="text-right"> | ||
<button class="btn btn-primary" type="submit">{% trans "Submit New Feedback" %}</button> | ||
</div> | ||
</form> | ||
</div> | ||
{% endif %} | ||
</div> | ||
|
||
{% endblock %} | ||
|
||
{% block extrajs %} | ||
<style> | ||
.with-box { | ||
border: 1px solid #e8e8e8; | ||
padding: 5px; | ||
border-radius: 5px; | ||
margin-top: 5px; | ||
} | ||
label.feedback{ | ||
width: 90%; | ||
display: inline-block; | ||
vertical-align: top; | ||
} | ||
.feedback-info{ | ||
font-size: 0.75rem; | ||
color: #8D8D8D; | ||
white-space: nowrap; | ||
} | ||
.update-feedback { | ||
margin-top: 10px; | ||
} | ||
.new-feedback{ | ||
padding: 5px; | ||
border-radius: 5px; | ||
margin-top: 20px; | ||
margin-bottom: 5px; | ||
} | ||
input.statusCheckbox{ | ||
margin-right: 5px; | ||
} | ||
button#updateButton[disabled] { | ||
background-color: #545454; | ||
} | ||
</style> | ||
|
||
<script> | ||
$(document).ready(function(){ | ||
const url = window.location.href; | ||
// Disable submit button initially | ||
$("#updateButton").prop("disabled", true); | ||
// Handle checkbox change event | ||
$(".statusCheckbox").change(function() { | ||
// Check if any new checkbox (excluding disabled ones) is checked | ||
const anyNewCheckboxChecked = $(".statusCheckbox:not(:disabled):checked").length > 0; | ||
// Enable or disable the submit button based on new checkbox checked state | ||
$("#updateButton").prop("disabled", !anyNewCheckboxChecked); | ||
}); | ||
|
||
$('.deleteButton').on('click', function() { | ||
const button = $(this); | ||
const feedbackId = button.data('feedback-id'); | ||
const formData = { | ||
'status_feedback': "deleted", | ||
'csrfmiddlewaretoken': '{{ csrf_token }}' | ||
}; | ||
deleteFeedback(feedbackId, formData); | ||
}); | ||
|
||
$("#updateButton").on('click', function() { | ||
let completedTasks = []; | ||
$('.statusCheckbox:checked').each(function() { | ||
const feedbackId = $(this).data('feedback-id'); | ||
completedTasks.push(feedbackId); | ||
}); | ||
const formData = { | ||
completed_tasks: completedTasks, | ||
'csrfmiddlewaretoken': '{{ csrf_token }}' | ||
}; | ||
updateStatus(formData); | ||
}); | ||
|
||
function updateStatus(formData) { | ||
const msg = "Update the task(s) as completed. You cannot revert the update. Please confirm." | ||
if (confirm((msg))) { | ||
$.ajax({ | ||
url: url + 'update/', | ||
type: 'POST', | ||
data: formData, | ||
traditional: true, | ||
success: function(response) { | ||
if (response.success) { | ||
$('.statusCheckbox:checked').each(function() { | ||
$(this).prop('disabled', true); | ||
}); | ||
$("#updateButton").prop("disabled", true); | ||
} | ||
}, | ||
error: function(xhr, status, error) { | ||
console.error('Error updating status:', error); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
function deleteFeedback(feedbackId, formData) { | ||
const msg = "This task will be permanently deleted. Please confirm." | ||
if (confirm(msg)) { | ||
$.ajax({ | ||
type: 'POST', | ||
url: url + feedbackId + '/', | ||
data: formData, | ||
success: function (response) { | ||
if (response.success) { | ||
const feedbackItem = $('.previous-feedback[data-feedback-id="' + feedbackId + '"]'); | ||
feedbackItem.remove(); | ||
} | ||
}, | ||
error: function (xhr, textStatus, errorThrown) { | ||
console.error('Error updating status:', errorThrown); | ||
} | ||
}); | ||
} | ||
} | ||
}) | ||
</script> | ||
{% endblock %} |
Oops, something went wrong.