This commit is contained in:
parent
be22d52467
commit
ebb504ad54
@ -8,7 +8,7 @@ Add new dropdown elements on detailed issue page to dynamically update issue's s
|
|||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
* Clone repo into plugins directory : `git clone https://github.com/Ilogeek/redmine_issue_dynamic_edit.git`
|
* Clone repo into plugins directory : `git clone https://github.com/Ilogeek/redmine_issue_dynamic_edit.git` (be sure that the parent folder is called `redmine_issue_dynamic_edit`)
|
||||||
* Restart your Redmine instance
|
* Restart your Redmine instance
|
||||||
|
|
||||||
### Customization
|
### Customization
|
||||||
@ -18,6 +18,7 @@ This plugin uses [FontAwesome icons](http://fontawesome.io/)
|
|||||||
|
|
||||||
### Changelog
|
### Changelog
|
||||||
|
|
||||||
|
* **v 0.4.0** : fixed Github issues #2, #4, #9. Edited dropdown display
|
||||||
* **v 0.3.0** : start date, due date, ratio and estimated time fields are now dynamically editable. Translation files added (en, fr). Log added in console when AJAX fails
|
* **v 0.3.0** : start date, due date, ratio and estimated time fields are now dynamically editable. Translation files added (en, fr). Log added in console when AJAX fails
|
||||||
* **v 0.2.0** : fixed "conflict" when trying to add a note after an update from dropdowns. New method used, REST API is not required anymore
|
* **v 0.2.0** : fixed "conflict" when trying to add a note after an update from dropdowns. New method used, REST API is not required anymore
|
||||||
* **v 0.1.0** : initial commit
|
* **v 0.1.0** : initial commit
|
||||||
@ -1,3 +1,3 @@
|
|||||||
= redmine_issue_dynamic_edit
|
= redmine_issue_dynamic_edit
|
||||||
|
|
||||||
Add new dropdowns elements on detailed issue page to dynamically update issue's status, assignee and priority directly in the details block of the issue
|
Add new dropdown elements on detailed issue page to dynamically update issue's status, assignee, priority, start and due dates, ratio and estimated time fields, directly in the details block of the issue.
|
||||||
|
|||||||
@ -12,65 +12,90 @@ var cssId = 'fontAwesome';
|
|||||||
head.appendChild(link);
|
head.appendChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update height of progress bar because of dropdown */
|
$(document).on('click', function(e){
|
||||||
var dynamicEditSelectHeight = $('.dynamicEditSelect').height();
|
$('.issue .attributes .attribute .value').removeClass('edited');
|
||||||
var progressBarHeight = $('table.progress').height();
|
if($(e.target).closest('.value').length) {
|
||||||
$('table.progress').css({'marginTop' : (dynamicEditSelectHeight - progressBarHeight)/2 + "px" });
|
$(e.target).closest('.value').addClass('edited');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* Put new dropdown lists in the detailed info block */
|
/* Put new dropdown lists in the detailed info block */
|
||||||
if($('#statusListDropdown').length > 0) {
|
if($('#statusListDropdown').length > 0) {
|
||||||
var htmlCopy = $('#statusListDropdown').get(0).outerHTML;
|
var htmlCopy = $('#statusListDropdown').get(0).outerHTML;
|
||||||
$('#statusListDropdown').remove();
|
$('#statusListDropdown').remove();
|
||||||
$('.details .attributes .status.attribute .value').html(htmlCopy);
|
$('.details .attributes .status.attribute .value').html( '<span class="showValue">' +
|
||||||
|
$('.details .attributes .status.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||||
|
htmlCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($('#usersListDropdown').length > 0) {
|
if($('#usersListDropdown').length > 0) {
|
||||||
var htmlCopy = $('#usersListDropdown').get(0).outerHTML;
|
var htmlCopy = $('#usersListDropdown').get(0).outerHTML;
|
||||||
$('#usersListDropdown').remove();
|
$('#usersListDropdown').remove();
|
||||||
$('.details .attributes .assigned-to.attribute .value').html(htmlCopy);
|
$('.details .attributes .assigned-to.attribute .value').html( '<span class="showValue">' +
|
||||||
|
$('.details .attributes .assigned-to.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||||
|
htmlCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($('#prioritiesListDropdown').length > 0) {
|
if($('#prioritiesListDropdown').length > 0) {
|
||||||
var htmlCopy = $('#prioritiesListDropdown').get(0).outerHTML;
|
var htmlCopy = $('#prioritiesListDropdown').get(0).outerHTML;
|
||||||
$('#prioritiesListDropdown').remove();
|
$('#prioritiesListDropdown').remove();
|
||||||
$('.details .attributes .priority.attribute .value').html(htmlCopy);
|
$('.details .attributes .priority.attribute .value').html( '<span class="showValue">' +
|
||||||
|
$('.details .attributes .priority.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||||
|
htmlCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($('#doneRatioListDropdown').length > 0) {
|
if($('#doneRatioListDropdown').length > 0) {
|
||||||
var htmlCopy = $('#doneRatioListDropdown').get(0).outerHTML;
|
var htmlCopy = $('#doneRatioListDropdown').get(0).outerHTML;
|
||||||
$('#doneRatioListDropdown').remove();
|
$('#doneRatioListDropdown').remove();
|
||||||
$('.details .attributes .progress.attribute .percent').html(htmlCopy);
|
$('.details .attributes .progress.attribute .value').html('<span class="showValue">' +
|
||||||
|
$('.details .attributes .progress.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||||
|
htmlCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($('#EstimatedTimeInput').length > 0) {
|
if($('#EstimatedTimeInput').length > 0) {
|
||||||
var htmlCopy = $('#EstimatedTimeInput').get(0).outerHTML;
|
var htmlCopy = $('#EstimatedTimeInput').get(0).outerHTML;
|
||||||
$('#EstimatedTimeInput').remove();
|
$('#EstimatedTimeInput').remove();
|
||||||
$('.details .attributes .estimated-hours.attribute .value').html(htmlCopy);
|
$('.details .attributes .estimated-hours.attribute .value').html('<span class="showValue">' +
|
||||||
|
$('.details .attributes .estimated-hours.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||||
|
htmlCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($('#StartDateInput').length > 0) {
|
if($('#StartDateInput').length > 0) {
|
||||||
var htmlCopy = $('#StartDateInput').get(0).outerHTML;
|
var htmlCopy = $('#StartDateInput').get(0).outerHTML;
|
||||||
$('#StartDateInput').remove();
|
$('#StartDateInput').remove();
|
||||||
$('.details .attributes .start-date.attribute .value').html(htmlCopy);
|
$('.details .attributes .start-date.attribute .value').html('<span class="showValue">' +
|
||||||
|
$('.details .attributes .start-date.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||||
|
htmlCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($('#DueDateInput').length > 0) {
|
if($('#DueDateInput').length > 0) {
|
||||||
var htmlCopy = $('#DueDateInput').get(0).outerHTML;
|
var htmlCopy = $('#DueDateInput').get(0).outerHTML;
|
||||||
$('#DueDateInput').remove();
|
$('#DueDateInput').remove();
|
||||||
$('.details .attributes .due-date.attribute .value').html(htmlCopy);
|
$('.details .attributes .due-date.attribute .value').html('<span class="showValue">' +
|
||||||
|
$('.details .attributes .due-date.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||||
|
htmlCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('body').on('click', '.btn.close', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$(e.target).closest('.value').removeClass('edited');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
||||||
|
|
||||||
|
/* hide edit field */
|
||||||
|
$('.details .attributes .' + cssClass + '.attribute .value').removeClass('edited');
|
||||||
|
|
||||||
/* add spin notification */
|
/* add spin notification */
|
||||||
if(type == "progress") { // specific case for progress bar
|
if(type == "progress") { // specific case for progress bar
|
||||||
$('.details .attributes .' + cssClass + '.attribute .percent').append(' <i class="fa fa-refresh fa-spin fa-fw"></i>');
|
$('.details .attributes .' + cssClass + '.attribute .value').append(' <i class="fa fa-refresh fa-spin fa-fw"></i>');
|
||||||
} else {
|
} else {
|
||||||
$('.details .attributes .' + cssClass + '.attribute .value').append(' <i class="fa fa-refresh fa-spin fa-fw"></i>');
|
$('.details .attributes .' + cssClass + '.attribute .value').append(' <i class="fa fa-refresh fa-spin fa-fw"></i>');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update value displayed to "move" edit pen icon */
|
/* update value displayed */
|
||||||
$('.details .attributes .' + cssClass + '.attribute .selectedValue span').html(function(){
|
$('.details .attributes .' + cssClass + '.attribute .showValue').html(function(){
|
||||||
if(type == "select")
|
if(type == "select")
|
||||||
{
|
{
|
||||||
return $('.details .attributes .' + cssClass + '.attribute .value select option:selected').html()
|
return $('.details .attributes .' + cssClass + '.attribute .value select option:selected').html()
|
||||||
@ -111,10 +136,11 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
|||||||
if(type == "progress") { // specific case for progress bar, we need to update the progress bar view
|
if(type == "progress") { // specific case for progress bar, we need to update the progress bar view
|
||||||
var progressBar = "<tbody><tr>";
|
var progressBar = "<tbody><tr>";
|
||||||
var percentTodo = 100 - parseInt(field_value);
|
var percentTodo = 100 - parseInt(field_value);
|
||||||
progressBar += "<td style='width: " + field_value + "%;' class='closed' title='" + field_value + "%'></td>";
|
if(field_value != 0) { progressBar += "<td style='width: " + field_value + "%;' class='closed' title='" + field_value + "%'></td>"; }
|
||||||
progressBar += "<td style='width: " + percentTodo + "%;' class='todo'></td>";
|
if(percentTodo != 0) { progressBar += "<td style='width: " + percentTodo + "%;' class='todo'></td>"; }
|
||||||
progressBar += "</tr></tbody>";
|
progressBar += "</tr></tbody>";
|
||||||
$('.details .attributes .' + cssClass + '.attribute table.progress').attr('class', 'progress progress-' + field_value).html(progressBar);
|
$('.details .attributes .' + cssClass + '.attribute table.progress').attr('class', 'progress progress-' + field_value).html(progressBar);
|
||||||
|
$('.details .attributes .' + cssClass + ' .percent').html(field_value + "%");
|
||||||
} else if( type == "date") { // specific case for start date and due date, we have to update min and max date allowed
|
} else if( type == "date") { // specific case for start date and due date, we have to update min and max date allowed
|
||||||
if(field_name == "start_date")
|
if(field_name == "start_date")
|
||||||
{
|
{
|
||||||
@ -159,11 +185,21 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
|||||||
var domSelectStatus = $('body').find('#statusListDropdown select');
|
var domSelectStatus = $('body').find('#statusListDropdown select');
|
||||||
domSelectStatus.on('change', function(e){
|
domSelectStatus.on('change', function(e){
|
||||||
issueDynamicUpdate('status_id', domSelectStatus.val(), 'select', 'status');
|
issueDynamicUpdate('status_id', domSelectStatus.val(), 'select', 'status');
|
||||||
|
|
||||||
|
/* update the classes status from */
|
||||||
|
$("#content > div.issue").removeClass(function (index, className) {
|
||||||
|
return (className.match (/(^|\s)status-\S+/g) || []).join(' ');
|
||||||
|
}).addClass('status-' + domSelectStatus.val());
|
||||||
}); /* end on change domSelectStatus */
|
}); /* end on change domSelectStatus */
|
||||||
|
|
||||||
var domSelectPriorities = $('body').find('#prioritiesListDropdown select');
|
var domSelectPriorities = $('body').find('#prioritiesListDropdown select');
|
||||||
domSelectPriorities.on('change', function(e){
|
domSelectPriorities.on('change', function(e){
|
||||||
issueDynamicUpdate('priority_id', domSelectPriorities.val(), 'select', 'priority');
|
issueDynamicUpdate('priority_id', domSelectPriorities.val(), 'select', 'priority');
|
||||||
|
|
||||||
|
/* update the classes priority from */
|
||||||
|
$("#content > div.issue").removeClass(function (index, className) {
|
||||||
|
return (className.match (/(^|\s)priority-\S+/g) || []).join(' ');
|
||||||
|
}).addClass('priority-' + domSelectStatus.val());
|
||||||
}); /* end on change domSelectPriorities */
|
}); /* end on change domSelectPriorities */
|
||||||
|
|
||||||
var domSelectUsers = $('body').find('#usersListDropdown select');
|
var domSelectUsers = $('body').find('#usersListDropdown select');
|
||||||
@ -174,10 +210,10 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
|||||||
var domSelectRatio = $('body').find('#doneRatioListDropdown select');
|
var domSelectRatio = $('body').find('#doneRatioListDropdown select');
|
||||||
domSelectRatio.on('change', function(e){
|
domSelectRatio.on('change', function(e){
|
||||||
issueDynamicUpdate('done_ratio', domSelectRatio.val(), 'progress', 'progress');
|
issueDynamicUpdate('done_ratio', domSelectRatio.val(), 'progress', 'progress');
|
||||||
}); /* end on change domSelectUsers */
|
}); /* end on change domSelectRatio */
|
||||||
|
|
||||||
var domInputEstimatedTime = $('body').find('#EstimatedTimeInput input');
|
var domInputEstimatedTime = $('body').find('#EstimatedTimeInput input');
|
||||||
$('#EstimatedTimeInput a.btn').on('click', function(e)
|
$('#EstimatedTimeInput a.btn.validate').on('click', function(e)
|
||||||
{
|
{
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('.estimated-hours .value .error').remove();
|
$('.estimated-hours .value .error').remove();
|
||||||
@ -197,12 +233,12 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
|||||||
$('.details .attributes .estimated-hours.attribute .value input').val()
|
$('.details .attributes .estimated-hours.attribute .value input').val()
|
||||||
);
|
);
|
||||||
if (e.keyCode == 13) {
|
if (e.keyCode == 13) {
|
||||||
$('#EstimatedTimeInput a.btn').click();
|
$('#EstimatedTimeInput a.btn.validate').click();
|
||||||
}
|
}
|
||||||
});/* end EstimatedTime */
|
});/* end EstimatedTime */
|
||||||
|
|
||||||
var domInputStartDate = $('body').find('#StartDateInput input');
|
var domInputStartDate = $('body').find('#StartDateInput input');
|
||||||
$('#StartDateInput a.btn').on('click', function(e)
|
$('#StartDateInput a.btn.validate').on('click', function(e)
|
||||||
{
|
{
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('.start-date .value .error').remove();
|
$('.start-date .value .error').remove();
|
||||||
@ -218,12 +254,12 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
|||||||
|
|
||||||
domInputStartDate.on('keyup', function(e){
|
domInputStartDate.on('keyup', function(e){
|
||||||
if (e.keyCode == 13) {
|
if (e.keyCode == 13) {
|
||||||
$('#StartDateInput a.btn').click();
|
$('#StartDateInput a.btn.validate').click();
|
||||||
}
|
}
|
||||||
});/* end StartDate */
|
});/* end StartDate */
|
||||||
|
|
||||||
var domInputDueDate = $('body').find('#DueDateInput input');
|
var domInputDueDate = $('body').find('#DueDateInput input');
|
||||||
$('#DueDateInput a.btn').on('click', function(e)
|
$('#DueDateInput a.btn.validate').on('click', function(e)
|
||||||
{
|
{
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('.due-date .value .error').remove();
|
$('.due-date .value .error').remove();
|
||||||
@ -239,6 +275,6 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
|
|||||||
|
|
||||||
domInputDueDate.on('keyup', function(e){
|
domInputDueDate.on('keyup', function(e){
|
||||||
if (e.keyCode == 13) {
|
if (e.keyCode == 13) {
|
||||||
$('#DueDateInput a.btn').click();
|
$('#DueDateInput a.btn.validate').click();
|
||||||
}
|
}
|
||||||
});/* end StartDate */
|
});/* end StartDate */
|
||||||
@ -1,5 +1,10 @@
|
|||||||
div.issue .attribute .value {
|
div.issue .attribute .value, div.issue .splitcontent {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.issue .attribute .value .percent {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Progress bar fix */
|
/* Progress bar fix */
|
||||||
@ -7,116 +12,76 @@ table.progress {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If you want to hide edit (pencil) icon, set opacity to 0 below : */
|
|
||||||
.selectedValue {
|
|
||||||
position: absolute;
|
|
||||||
white-space: nowrap;
|
|
||||||
top: 2px;
|
|
||||||
right: 0;
|
|
||||||
left: 5px;
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 1; /* <---------------- */
|
|
||||||
}
|
|
||||||
|
|
||||||
.attribute:hover .selectedValue {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedValue .transparent {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.attribute .error {
|
.attribute .error {
|
||||||
color: #e74c3c;
|
color: #e74c3c;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input Style */
|
.value .fa-pencil {
|
||||||
.dynamicEditInput {
|
opacity: 0;
|
||||||
position: relative;
|
}
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 3px;
|
.issue.details .showValue {
|
||||||
padding: 4px;
|
cursor: pointer;
|
||||||
margin-left: -5px;
|
}
|
||||||
padding-right: 0;
|
|
||||||
|
.issue.details:hover .fa-pencil {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamicEdit {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
position:absolute;
|
||||||
|
opacity: 0;
|
||||||
|
left:0;
|
||||||
|
bottom: 100%;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: white;
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
|
||||||
|
width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dynamicEditInput input {
|
.edited .dynamicEdit {
|
||||||
border: 0px;
|
pointer-events: auto;
|
||||||
font: inherit;
|
|
||||||
color: inherit;
|
|
||||||
background: transparent;
|
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dynamicEditInput .selectedValue {
|
div.issue .attribute .value.edited .dynamicEdit {
|
||||||
top:0; left:0;
|
opacity: 1;
|
||||||
}
|
|
||||||
|
|
||||||
.attribute:hover .dynamicEditInput {
|
|
||||||
border-color: #e0e2e3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.attribute .btn-primary {
|
.attribute .btn-primary {
|
||||||
border-left: 1px solid #e0e2e3;
|
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
opacity: 0;
|
color: white;
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.attribute:hover .btn-primary {
|
|
||||||
pointer-events: auto;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* End Input Style */
|
|
||||||
|
|
||||||
/* Select Style */
|
|
||||||
.dynamicEditSelect select {
|
|
||||||
border-color: transparent;
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid transparent;
|
|
||||||
border-radius: 0;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
background: none;
|
|
||||||
font-size: inherit;
|
|
||||||
color: inherit;
|
|
||||||
font-family: inherit;
|
|
||||||
background: transparent;
|
|
||||||
padding-left:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dynamicEditSelect {
|
|
||||||
padding-left: 5px;
|
|
||||||
margin-left: -6px; /* padding + border */
|
|
||||||
display: inline-block;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
position:relative;
|
padding: 3px;
|
||||||
}
|
|
||||||
|
|
||||||
.attribute:hover .dynamicEditSelect {
|
|
||||||
border-color: #e0e2e3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dynamicEditSelect i.dropdown {
|
|
||||||
position:absolute;
|
|
||||||
right: 5px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.attribute:hover .dynamicEditSelect i.dropdown {
|
.attribute .btn-primary.close {
|
||||||
opacity: 1;
|
background: #c0392b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dynamicEditSelect select option[disabled="disabled"]
|
.attribute .btn-primary.validate {
|
||||||
|
background: #27ae60;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamicEdit select {
|
||||||
|
border:none;
|
||||||
|
background-image:none;
|
||||||
|
background-color:transparent;
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
-moz-box-shadow: none;
|
||||||
|
box-shadow: none;
|
||||||
|
font-size: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dynamicEdit select option[disabled="disabled"]
|
||||||
{
|
{
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
/* End Select Style */
|
|
||||||
@ -3,4 +3,5 @@ en:
|
|||||||
ide_txt_error_positive_number : 'It must be a positive number'
|
ide_txt_error_positive_number : 'It must be a positive number'
|
||||||
ide_txt_error_start_date : 'Start date must be anterior to due date'
|
ide_txt_error_start_date : 'Start date must be anterior to due date'
|
||||||
ide_txt_error_due_date : 'Due date must be posterior to start date'
|
ide_txt_error_due_date : 'Due date must be posterior to start date'
|
||||||
ide_txt_error_ajax_call : 'Error (check your JS console)'
|
ide_txt_error_ajax_call : 'Error (check your JS console)'
|
||||||
|
ide_txt_cancel_btn : 'Cancel the modification'
|
||||||
@ -3,4 +3,5 @@ fr:
|
|||||||
ide_txt_error_positive_number : 'Le nombre doit être positif'
|
ide_txt_error_positive_number : 'Le nombre doit être positif'
|
||||||
ide_txt_error_start_date : "La date de début doit être antérieure à la date d'échéance"
|
ide_txt_error_start_date : "La date de début doit être antérieure à la date d'échéance"
|
||||||
ide_txt_error_due_date : "La date d'échéance doit être postérieure à la date de début"
|
ide_txt_error_due_date : "La date d'échéance doit être postérieure à la date de début"
|
||||||
ide_txt_error_ajax_call : 'Erreur (logs dans la console JS)'
|
ide_txt_error_ajax_call : 'Erreur (logs dans la console JS)'
|
||||||
|
ide_txt_cancel_btn : 'Annuler la modification'
|
||||||
2
init.rb
2
init.rb
@ -6,7 +6,7 @@ Redmine::Plugin.register :redmine_issue_dynamic_edit do
|
|||||||
name 'Redmine Dynamic edit Issue plugin'
|
name 'Redmine Dynamic edit Issue plugin'
|
||||||
author 'Hugo Zilliox'
|
author 'Hugo Zilliox'
|
||||||
description 'Allows users to dynamically update issue attributes in detailed view'
|
description 'Allows users to dynamically update issue attributes in detailed view'
|
||||||
version '0.3.1'
|
version '0.4.0'
|
||||||
url 'https://github.com/ilogeek/redmine_issue_dynamic_edit'
|
url 'https://github.com/ilogeek/redmine_issue_dynamic_edit'
|
||||||
author_url 'https://hzilliox.fr'
|
author_url 'https://hzilliox.fr'
|
||||||
end
|
end
|
||||||
|
|||||||
@ -17,19 +17,21 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
request = context[:request]
|
request = context[:request]
|
||||||
issue_id = request.path_parameters[:id]
|
issue_id = request.path_parameters[:id]
|
||||||
back = request.env['HTTP_REFERER']
|
back = request.env['HTTP_REFERER']
|
||||||
|
|
||||||
|
o = ''
|
||||||
|
|
||||||
if (issue_id)
|
if (issue_id)
|
||||||
issue = Issue.find(issue_id)
|
issue = Issue.find(issue_id)
|
||||||
if (issue)
|
if (issue)
|
||||||
if (User.current.allowed_to?(:edit_issues, project))
|
if (User.current.allowed_to?(:edit_issues, project))
|
||||||
o = ''
|
|
||||||
# o << issue.to_json
|
# if there's a JS error, we hide the generated values
|
||||||
|
o << '<div style="display:none">'
|
||||||
|
|
||||||
# Status dropdown
|
# Status dropdown
|
||||||
statuses = issue.new_statuses_allowed_to(User.current)
|
statuses = issue.new_statuses_allowed_to(User.current)
|
||||||
if (!statuses.empty?)
|
if (!statuses.empty?)
|
||||||
o << "<span class='dynamicEditSelect' id='statusListDropdown'>"
|
o << "<span class='dynamicEdit' id='statusListDropdown'>"
|
||||||
o << "<div class='selectedValue'><span class='transparent'>#{issue.status}</span> <i class=\"fa fa-pencil fa-fw\" aria-hidden=\"true\"></i></div> "
|
|
||||||
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
||||||
statuses.each do |s|
|
statuses.each do |s|
|
||||||
if (s != issue.status)
|
if (s != issue.status)
|
||||||
@ -38,15 +40,15 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
o << "<option value='#{s.id}' selected>#{s.name}</option>"
|
o << "<option value='#{s.id}' selected>#{s.name}</option>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
o << "</select><i class=\"fa fa-angle-down fa-fw dropdown\" aria-hidden=\"true\"></i></span>"
|
o << "</select> <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a></span>"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Users dropdown
|
# Users dropdown
|
||||||
# userCanChangeAssignee = User.current.allowed_to?(:edit_assigned_to, @project, :global => true)
|
# userCanChangeAssignee = User.current.allowed_to?(:edit_assigned_to, @project, :global => true)
|
||||||
assignables = project.assignable_users
|
assignables = project.assignable_users
|
||||||
|
o << assignables.to_json
|
||||||
if (!assignables.empty?)
|
if (!assignables.empty?)
|
||||||
o << "<span class='dynamicEditSelect' id='usersListDropdown'>"
|
o << "<span class='dynamicEdit' id='usersListDropdown'>"
|
||||||
o << "<div class='selectedValue'><span class='transparent'>#{issue.assigned_to}</span> <i class=\"fa fa-pencil fa-fw\" aria-hidden=\"true\"></i></div> "
|
|
||||||
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
||||||
assignables.each do |u|
|
assignables.each do |u|
|
||||||
if (u != issue.assigned_to)
|
if (u != issue.assigned_to)
|
||||||
@ -55,14 +57,13 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
o << "<option value='#{u.id}' selected>#{u.name}</option>"
|
o << "<option value='#{u.id}' selected>#{u.name}</option>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
o << "</select><i class=\"fa fa-angle-down fa-fw dropdown\" aria-hidden=\"true\"></i></span>"
|
o << "</select> <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a></span>"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Priorities dropdown
|
# Priorities dropdown
|
||||||
priorities = IssuePriority.all
|
priorities = IssuePriority.all
|
||||||
if(!priorities.empty?)
|
if(!priorities.empty?)
|
||||||
o << "<span class='dynamicEditSelect' id='prioritiesListDropdown'>"
|
o << "<span class='dynamicEdit' id='prioritiesListDropdown'>"
|
||||||
o << "<div class='selectedValue'><span class='transparent'>#{issue.priority}</span> <i class=\"fa fa-pencil fa-fw\" aria-hidden=\"true\"></i></div> "
|
|
||||||
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
||||||
priorities.each do |p|
|
priorities.each do |p|
|
||||||
if (p != issue.priority)
|
if (p != issue.priority)
|
||||||
@ -71,13 +72,12 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
o << "<option value='#{p.id}' selected>#{p.name}</option>"
|
o << "<option value='#{p.id}' selected>#{p.name}</option>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
o << "</select><i class=\"fa fa-angle-down fa-fw dropdown\" aria-hidden=\"true\"></i></span>"
|
o << "</select> <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a></span>"
|
||||||
end
|
end
|
||||||
|
|
||||||
# %done dropdown
|
# %done dropdown
|
||||||
percent = 0
|
percent = 0
|
||||||
o << "<span class='dynamicEditSelect' id='doneRatioListDropdown'>"
|
o << "<span class='dynamicEdit' id='doneRatioListDropdown'>"
|
||||||
o << "<div class='selectedValue'><span class='transparent'>#{issue.done_ratio}%</span> <i class=\"fa fa-pencil fa-fw\" aria-hidden=\"true\"></i></div> "
|
|
||||||
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
|
||||||
loop do
|
loop do
|
||||||
if (percent == issue.done_ratio)
|
if (percent == issue.done_ratio)
|
||||||
@ -90,20 +90,20 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
o << "</select><i class=\"fa fa-angle-down fa-fw dropdown\" aria-hidden=\"true\"></i></span>"
|
o << "</select> <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a></span>"
|
||||||
|
|
||||||
# Estimated_time dropdown
|
# Estimated_time dropdown
|
||||||
o << "<span class='dynamicEditInput' id='EstimatedTimeInput'>"
|
o << "<span class='dynamicEdit' id='EstimatedTimeInput'>"
|
||||||
o << " <div class='selectedValue'><span class='transparent'>#{issue.estimated_hours}</span> <i class=\"fa fa-pencil fa-fw\" aria-hidden=\"true\"></i></div> "
|
|
||||||
o << " <input type='text' value='#{issue.estimated_hours}' size='6'/>"
|
o << " <input type='text' value='#{issue.estimated_hours}' size='6'/>"
|
||||||
o << "<a href='#' class='btn btn-primary' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
|
o << "<a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
|
||||||
|
o << " <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>"
|
||||||
o << "</span>"
|
o << "</span>"
|
||||||
|
|
||||||
# Start date
|
# Start date
|
||||||
o << "<span class='dynamicEditInput' id='StartDateInput'>"
|
o << "<span class='dynamicEdit' id='StartDateInput'>"
|
||||||
o << " <div class='selectedValue'><span class='transparent'>XXXX/XX/XX</span> <i class=\"fa fa-pencil fa-fw\" aria-hidden=\"true\"></i></div> "
|
|
||||||
o << " <input size=\"10\" value=\"#{issue.start_date}\" type=\"date\" max=\"#{issue.due_date}\">"
|
o << " <input size=\"10\" value=\"#{issue.start_date}\" type=\"date\" max=\"#{issue.due_date}\">"
|
||||||
o << "<a href='#' class='btn btn-primary' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
|
o << " <a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
|
||||||
|
o << " <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>"
|
||||||
o << "</span>"
|
o << "</span>"
|
||||||
o << "<script>"
|
o << "<script>"
|
||||||
o << "//<![CDATA[\n"
|
o << "//<![CDATA[\n"
|
||||||
@ -112,10 +112,10 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
o << "</script>"
|
o << "</script>"
|
||||||
|
|
||||||
# Due date
|
# Due date
|
||||||
o << "<span class='dynamicEditInput' id='DueDateInput'>"
|
o << "<span class='dynamicEdit' id='DueDateInput'>"
|
||||||
o << " <div class='selectedValue'><span class='transparent'>XXXX/XX/XX</span> <i class=\"fa fa-pencil fa-fw\" aria-hidden=\"true\"></i></div> "
|
|
||||||
o << " <input size=\"10\" value=\"#{issue.due_date}\" type=\"date\" min=\"#{issue.start_date}\">"
|
o << " <input size=\"10\" value=\"#{issue.due_date}\" type=\"date\" min=\"#{issue.start_date}\">"
|
||||||
o << "<a href='#' class='btn btn-primary' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
|
o << " <a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
|
||||||
|
o << " <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>"
|
||||||
o << "</span>"
|
o << "</span>"
|
||||||
o << "<script>"
|
o << "<script>"
|
||||||
o << "//<![CDATA[\n"
|
o << "//<![CDATA[\n"
|
||||||
@ -139,6 +139,9 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
|||||||
o << " var _TXT_ERROR_AJAX_CALL = \"" + l(:ide_txt_error_ajax_call) + "\";\n"
|
o << " var _TXT_ERROR_AJAX_CALL = \"" + l(:ide_txt_error_ajax_call) + "\";\n"
|
||||||
|
|
||||||
o << "</script>"
|
o << "</script>"
|
||||||
|
|
||||||
|
# closing the display none div parent
|
||||||
|
o << "</div>"
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user