Merge branch 'Aufinal/dialog_utils' into 'aureplop/kpsul_js_refactor'

Utilitaires de dialogue

Ajoute deux type de dialogue avec l'utilisateur
- une classe UserDialog pour ouvrir un simple dialogue jconfirm
- une fonction api_with_auth pour gérer toutes les requêtes API pouvant 
nécessiter un mot de passe

See merge request !199
This commit is contained in:
Aurélien Delobelle 2017-04-03 23:25:55 +02:00
commit dcda67aaf7
4 changed files with 232 additions and 307 deletions

View file

@ -71,14 +71,19 @@
.jconfirm .capslock .glyphicon { .jconfirm .capslock .glyphicon {
position: absolute; position: absolute;
display:none;
padding: 10px; padding: 10px;
right: 0px; right: 0px;
top: 15px; top: 15px;
font-size: 30px; font-size: 30px;
display: none ;
margin-left: 60px !important; margin-left: 60px !important;
} }
.capslock_on .capslock .glyphicon{
display: inline-block !important;
}
.jconfirm .capslock input { .jconfirm .capslock input {
padding-right: 50px; padding-right: 50px;
padding-left: 50px; padding-left: 50px;

View file

@ -124,8 +124,33 @@ $(document).ready(function() {
} }
}); });
} }
}); });
/*
* Capslock management
*/
window.capslock = -1;
$(document).on('keypress', function(e) {
var s = String.fromCharCode(e.which);
if ((s.toUpperCase() === s && s.toLowerCase() !== s && !e.shiftKey)|| //caps on, shift off
(s.toUpperCase() !== s && s.toLowerCase() === s && e.shiftKey)) { //caps on, shift on
$('body').addClass('capslock_on')
} else if ((s.toLowerCase() === s && s.toUpperCase() !== s && !e.shiftKey)|| //caps off, shift off
(s.toLowerCase() !== s && s.toUpperCase() === s && e.shiftKey)) { //caps off, shift on
$('body').removeClass('capslock_on')
}
});
$(document).on('keydown', function(e) {
if (e.which == 20) {
$('body').toggleClass('capslock_on')
}
});
function dateUTCToParis(date) { function dateUTCToParis(date) {
return moment.tz(date, 'UTC').tz('Europe/Paris'); return moment.tz(date, 'UTC').tz('Europe/Paris');
} }
@ -146,6 +171,58 @@ function isValidTrigramme(trigramme) {
return trigramme.match(pattern); return trigramme.match(pattern);
} }
/**
* Dialogs with user via jconfirm
*/
class UserDialog {
static get defaults() {
return {'title': '', 'content': ''};
}
constructor(data) {
$.extend(this, this.constructor.defaults, data);
}
open(settings) {
// Arg management
var pre_content = settings.pre_content || '';
var post_content = settings.post_content || '';
var callback = settings.callback || $.noop;
var that = this;
$.confirm({
title: this.title,
content: pre_content + this.content + post_content,
backgroundDismiss: true,
animation:'top',
closeAnimation:'bottom',
keyboardEnabled: true,
confirm: function() {
var inputs = {};
this.$content.find('input').each(function () {
inputs[$(this).attr('name')] = $(this).val();
});
if (Object.keys(inputs).length > 1)
return callback(inputs);
else
return callback(inputs[Object.keys(inputs)[0]]);
},
onOpen: function() {
var that = this
this.$content.find('input').on('keydown', function(e) {
if (e.keyCode == 13) {
e.preventDefault();
that.$confirmButton.click();
}
});
},
onClose: function() { if (settings.next_focus) { this._lastFocused = settings.next_focus; } }
});
}
}
function getErrorsHtml(data) { function getErrorsHtml(data) {
var content = ''; var content = '';
if ('operation_group' in data['errors']) { if ('operation_group' in data['errors']) {
@ -185,55 +262,55 @@ function getErrorsHtml(data) {
return content; return content;
} }
function requestAuth(data, callback, focus_next = null) { var authDialog = new UserDialog({
var content = getErrorsHtml(data); 'title': 'Authentification requise',
content += '<div class="capslock"><span class="glyphicon glyphicon-lock"></span><input type="password" name="password" autofocus><div>', 'content': '<div class="capslock"><span class="glyphicon glyphicon-lock"></span><input type="password" name="password" autofocus><div>',
$.confirm({ });
title: 'Authentification requise',
content: content,
backgroundDismiss: true,
animation:'top',
closeAnimation:'bottom',
keyboardEnabled: true,
confirm: function() {
var password = this.$content.find('input').val();
callback(password);
},
onOpen: function() {
var that = this;
var capslock = -1 ; // 1 -> caps on ; 0 -> caps off ; -1 or 2 -> unknown
this.$content.find('input').on('keypress', function(e) {
if (e.keyCode == 13)
that.$confirmButton.click();
var s = String.fromCharCode(e.which); function api_with_auth(settings, password) {
if ((s.toUpperCase() === s && s.toLowerCase() !== s && !e.shiftKey)|| //caps on, shift off if (window.api_lock == 1)
(s.toUpperCase() !== s && s.toLowerCase() === s && e.shiftKey)) { //caps on, shift on return false;
capslock = 1 ; window.api_lock = 1;
} else if ((s.toLowerCase() === s && s.toUpperCase() !== s && !e.shiftKey)|| //caps off, shift off
(s.toLowerCase() !== s && s.toUpperCase() === s && e.shiftKey)) { //caps off, shift on var url = settings.url;
capslock = 0 ; if (!url)
} return false;
if (capslock == 1) var data = settings.data || {} ;
$('.capslock .glyphicon').show() ; var on_success = settings.on_success || $.noop ;
else if (capslock == 0) var on_400 = settings.on_400 || $.noop ;
$('.capslock .glyphicon').hide() ;
}); $.ajax({
// Capslock key is not detected by keypress dataType: "json",
this.$content.find('input').on('keydown', function(e) { url: url,
if (e.which == 20) { method: "POST",
capslock = 1-capslock ; data: data,
} beforeSend: function ($xhr) {
if (capslock == 1) $xhr.setRequestHeader("X-CSRFToken", csrftoken);
$('.capslock .glyphicon').show() ; if (password)
else if (capslock == 0) $xhr.setRequestHeader("KFetPassword", password);
$('.capslock .glyphicon').hide() ;
});
}, },
onClose: function() { })
if (focus_next) .done(function(data) {
this._lastFocused = focus_next; on_success(data);
})
.fail(function($xhr) {
var response = $xhr.responseJSON;
switch ($xhr.status) {
case 403:
authDialog.open({
callback: function(password) {
api_with_auth(settings, password)
},
pre_content: getErrorsHtml(response),
next_focus: settings.next_focus,
});
break;
case 400:
on_400(response);
break;
} }
})
.always(function() {
window.api_lock = 0;
}); });
} }

View file

@ -13,8 +13,9 @@
<script type="text/javascript" src="{% static 'kfet/js/moment-fr.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/moment-fr.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/moment-timezone-with-data-2010-2020.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/moment-timezone-with-data-2010-2020.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/bootstrap-datetimepicker.min.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/bootstrap-datetimepicker.min.js' %}"></script>
<script type="text/javascript" src="{% url 'js_reverse' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/multiple-select.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/multiple-select.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/kfet.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/kfet.api.js' %}"></script>
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script> <script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
{% endblock %} {% endblock %}
@ -60,6 +61,9 @@
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function() { $(document).ready(function() {
// Lock to avoid multiple requests
window.lock = 0;
settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})} settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})}
khistory = new KHistory(); khistory = new KHistory();
@ -179,79 +183,17 @@ $(document).ready(function() {
}); });
} }
function requestAuth(data, callback) { function cancelOperations(opes_array) {
var content = getErrorsHtml(data);
content += '<input type="password" name="password" autofocus>',
$.confirm({
title: 'Authentification requise',
content: content,
backgroundDismiss: true,
animation:'top',
closeAnimation:'bottom',
keyboardEnabled: true,
confirm: function() {
var password = this.$content.find('input').val();
callback(password);
},
onOpen: function() {
var that = this;
this.$content.find('input').on('keypress', function(e) {
if (e.keyCode == 13)
that.$confirmButton.click();
});
},
});
}
function getErrorsHtml(data) {
var content = '';
if ('missing_perms' in data['errors']) {
content += 'Permissions manquantes';
content += '<ul>';
for (var i=0; i<data['errors']['missing_perms'].length; i++)
content += '<li>'+data['errors']['missing_perms'][i]+'</li>';
content += '</ul>';
}
if ('negative' in data['errors']) {
var url_base = "{% url 'kfet.account.update' LIQ}";
url_base = base_url(0, url_base.length-8);
for (var i=0; i<data['errors']['negative'].length; i++) {
content += '<a class="btn btn-primary" href="'+url_base+data['errors']['negative'][i]+'/edit" target="_blank">Autorisation de négatif requise pour '+data['errors']['negative'][i]+'</a>';
}
}
return content;
}
function cancelOperations(opes_array, password = '') {
var data = { 'operations' : opes_array } var data = { 'operations' : opes_array }
$.ajax({ api_with_auth({
dataType: "json", url: Urls['kfet.kpsul.cancel_operations'](),
url : "{% url 'kfet.kpsul.cancel_operations' %}", data: data,
method : "POST", on_success: function(data) {
data : data, khistory.$container.find('.ui-selected').removeClass('ui-selected');
beforeSend: function ($xhr) { },
$xhr.setRequestHeader("X-CSRFToken", csrftoken); on_400: function(data) {
if (password != '') displayErrors(getErrorsHtml(data));
$xhr.setRequestHeader("KFetPassword", password);
}, },
})
.done(function(data) {
khistory.$container.find('.ui-selected').removeClass('ui-selected');
})
.fail(function($xhr) {
var data = $xhr.responseJSON;
switch ($xhr.status) {
case 403:
requestAuth(data, function(password) {
cancelOperations(opes_array, password);
});
break;
case 400:
displayErrors(getErrorsHtml(data));
break;
}
}); });
} }

View file

@ -176,12 +176,6 @@
$(document).ready(function() { $(document).ready(function() {
'use strict'; 'use strict';
// -----
// General
// -----
// Lock to avoid multiple requests
var lock = 0;
// ----- // -----
// Auth // Auth
@ -189,25 +183,20 @@ $(document).ready(function() {
function askComment(callback) { function askComment(callback) {
var comment = $('#id_comment').val(); var comment = $('#id_comment').val();
$.confirm({ var commentDialog = new UserDialog({
title: 'Commentaire requis', title: 'Commentaire requis',
content: '<input type="text" name="comment_opegroup" value="'+comment+'" autofocus>', content: '<input type="text" name="comment_opegroup" value="'+comment+'" autofocus>',
backgroundDismiss: true,
animation: 'top', });
closeAnimation: 'bottom',
keyboardEnabled: true, function confirm_callback(comment) {
confirm: function() { $('#id_comment').val(comment);
$('#id_comment').val(this.$content.find('input').val()); callback();
callback(); }
},
onOpen: function() { commentDialog.open({
var that = this; callback: confirm_callback,
this.$content.find('input').on('keydown', function(e) { next_focus: articleSelect
if (e.keyCode == 13)
that.$confirmButton.click();
});
},
onClose: function() { this._lastFocused = articleSelect; }
}); });
} }
@ -238,42 +227,22 @@ $(document).ready(function() {
var operationGroup = $('#operationgroup_form'); var operationGroup = $('#operationgroup_form');
var operations = $('#operation_formset'); var operations = $('#operation_formset');
function performOperations(password = '') { function performOperations() {
if (lock == 1)
return false;
lock = 1;
var data = operationGroup.serialize() + '&' + operations.serialize(); var data = operationGroup.serialize() + '&' + operations.serialize();
$.ajax({ api_with_auth({
dataType: "json", url: Urls['kfet.kpsul.perform_operations'](),
url : "{% url 'kfet.kpsul.perform_operations' %}", data: data,
method : "POST", on_success: function() {
data : data, updatePreviousOp();
beforeSend: function ($xhr) { coolReset();
$xhr.setRequestHeader("X-CSRFToken", csrftoken);
if (password != '')
$xhr.setRequestHeader("KFetPassword", password);
}, },
}) on_400: function(response) {
.done(function(data) { if ('need_comment' in response['errors'])
updatePreviousOp(); askComment(performOperations);
coolReset(); else
lock = 0; displayErrors(getErrorsHtml(response));
}) },
.fail(function($xhr) { next_focus: articleSelect,
var data = $xhr.responseJSON;
switch ($xhr.status) {
case 403:
requestAuth(data, performOperations, articleSelect);
break;
case 400:
if ('need_comment' in data['errors']) {
askComment(performOperations);
} else {
displayErrors(getErrorsHtml(data));
}
break;
}
lock = 0;
}); });
} }
@ -289,40 +258,18 @@ $(document).ready(function() {
var cancelButton = $('#cancel_operations'); var cancelButton = $('#cancel_operations');
var cancelForm = $('#cancel_form'); var cancelForm = $('#cancel_form');
function cancelOperations(opes_array, password = '') { function cancelOperations(opes_array) {
if (lock == 1)
return false
lock = 1;
var data = { 'operations' : opes_array } var data = { 'operations' : opes_array }
$.ajax({ api_with_auth({
dataType: "json", url: Urls['kfet.kpsul.cancel_operations'](),
url : "{% url 'kfet.kpsul.cancel_operations' %}", data: data,
method : "POST", on_success: function() {
data : data, coolReset();
beforeSend: function ($xhr) {
$xhr.setRequestHeader("X-CSRFToken", csrftoken);
if (password != '')
$xhr.setRequestHeader("KFetPassword", password);
}, },
on_400: function(response) {
}) displayErrors(getErrorsHtml(response));
.done(function(data) { },
coolReset(); next_focus: kpsul.account_manager,
lock = 0;
})
.fail(function($xhr) {
var data = $xhr.responseJSON;
switch ($xhr.status) {
case 403:
requestAuth(data, function(password) {
cancelOperations(opes_array, password);
}, account_manager._$input_trigramme);
break;
case 400:
displayErrors(getErrorsHtml(data));
break;
}
lock = 0;
}); });
} }
@ -690,58 +637,43 @@ $(document).ready(function() {
// Ask deposit or withdraw // Ask deposit or withdraw
// ----- // -----
function askDeposit(is_checkout=1) { function askDeposit(is_checkout=1) {
var title = is_checkout ? 'Montant de la charge' : "Montant de l'édition"; var depositDialog = new UserDialog({
$.confirm({ title: is_checkout ? 'Montant de la charge' : "Montant de l'édition",
title: title,
content: '<input type="number" lang="en" step="0.01" min="0.01" on autofocus placeholder="€">', content: '<input type="number" lang="en" step="0.01" min="0.01" on autofocus placeholder="€">',
backgroundDismiss: true, });
animation:'top',
closeAnimation:'bottom', function callback(amount) {
keyboardEnabled: true, if (!$.isNumeric(amount) || amount <= 0)
confirm: function() { return false;
var amount = this.$content.find('input').val(); addDeposit(amount, is_checkout);
if (!$.isNumeric(amount) || amount <= 0)
return false; }
addDeposit(amount, is_checkout); var next_focus = articleSelect.val() ? articleNb : articleSelect ;
},
onOpen: function() { depositDialog.open({
var that = this callback: callback,
this.$content.find('input').on('keydown', function(e) { next_focus: next_focus,
if (e.keyCode == 13) {
e.preventDefault();
that.$confirmButton.click();
}
});
},
onClose: function() { this._lastFocused = (articleSelect.val() ? articleNb : articleSelect) ; }
}); });
} }
function askWithdraw() { function askWithdraw() {
$.confirm({ var withdrawDialog = new UserDialog({
title: 'Montant du retrait', 'title': 'Montant du retrait',
content: '<input type="number" lang="en" step="0.01" min="0.01" on autofocus placeholder="€">', 'content': '<input type="number" lang="en" step="0.01" min="0.01" on autofocus placeholder="€">',
backgroundDismiss: true, });
animation:'top',
closeAnimation:'bottom', function callback(amount) {
keyboardEnabled: true, if (!$.isNumeric(amount) || amount <= 0)
confirm: function() { return false;
var amount = this.$content.find('input').val(); addWithdraw(amount);
if (!$.isNumeric(amount) || amount <= 0)
return false; }
addWithdraw(amount);
}, withdrawDialog.open({
onOpen: function() { callback: callback,
var that = this next_focus: articleSelect
this.$content.find('input').on('keydown', function(e) {
if (e.keyCode == 13) {
e.preventDefault();
that.$confirmButton.click();
}
});
},
onClose: function() { this._lastFocused = articleSelect; }
}); });
} }
@ -865,60 +797,29 @@ $(document).ready(function() {
$('body').css('animation', 'colorchange 3s infinite'); $('body').css('animation', 'colorchange 3s infinite');
} }
} }
function sendAddcost(trigramme, amount, password='') {
var data = {
trigramme: trigramme,
amount: amount,
}
$.ajax({
dataType: "json",
url : "{% url 'kfet.kpsul.update_addcost' %}",
method : "POST",
data : data,
beforeSend: function ($xhr) {
$xhr.setRequestHeader("X-CSRFToken", csrftoken);
if (password != '')
$xhr.setRequestHeader("KFetPassword", password);
},
})
.fail(function($xhr) {
var data = $xhr.responseJSON;
switch ($xhr.status) {
case 403:
requestAuth(data, function(password) {
sendAddcost(trigramme, amount, password);
}, account_manager._$input_trigramme);
break;
case 400:
askAddcost(getErrorsHtml(data));
break;
}
});
}
function askAddcost(errors = '') { function askAddcost(errors = '') {
$.confirm({ var addcostDialog = new UserDialog({
title: 'Majoration', 'title': 'Majoration',
content: errors + '<input type="text" placeholder="Trigramme" autocomplete="off" name="trigramme" spellcheck="false" style="text-transform:uppercase" autofocus><input type="number" lang="en" step="0.01" min="0.01" placeholder="€" name="amount">', 'content': '<input type="text" placeholder="Trigramme" autocomplete="off" name="trigramme" spellcheck="false" style="text-transform:uppercase" autofocus><input type="number" lang="en" step="0.01" min="0.01" placeholder="€" name="amount">',
backgroundDismiss: true, });
animation:'top',
closeAnimation:'bottom', function callback(data) {
keyboardEnabled: true, api_with_auth({
confirm: function() { url: Urls['kfet.kpsul.update_addcost'](),
var trigramme = this.$content.find('input[name=trigramme]').val().toUpperCase(); data: data,
var amount = this.$content.find('input[name=amount]').val(); on_400: function(response) {
sendAddcost(trigramme, amount); askAddcost(getErrorsHtml(response));
}, },
onOpen: function() { next_focus: kpsul.account_manager,
var that = this });
this.$content.find('input[name=amount]').on('keydown', function(e) { }
if (e.keyCode == 13) {
e.preventDefault(); addcostDialog.open({
that.$confirmButton.click(); callback: callback,
} pre_content: errors,
}); next_focus: kpsul.account_manager,
},
onClose: function() { this._lastFocused = kpsul.account_manager.selection._$input; }
}); });
} }