mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
1123 lines
33 KiB
JavaScript
1123 lines
33 KiB
JavaScript
{% load i18n %}
|
|
|
|
/* globals
|
|
inventreeGet,
|
|
showAlertOrCache,
|
|
*/
|
|
|
|
/* exported
|
|
attachSecondaryModal,
|
|
clearField,
|
|
clearFieldOptions,
|
|
closeModal,
|
|
enableField,
|
|
getFieldValue,
|
|
reloadFieldOptions,
|
|
showModalImage,
|
|
removeRowFromModalForm,
|
|
showQuestionDialog,
|
|
*/
|
|
|
|
/*
|
|
* Create and display a new modal dialog
|
|
*
|
|
* options:
|
|
* - title: Form title to render
|
|
* - submitText: Text to render on 'submit' button (default = "Submit")
|
|
* - closeText: Text to render on 'close' button (default = "Cancel")
|
|
* - focus: Name of field to focus on after launching
|
|
*/
|
|
function createNewModal(options={}) {
|
|
|
|
var id = 1;
|
|
|
|
// Check out what modal forms are already being displayed
|
|
$('.inventree-modal').each(function() {
|
|
|
|
var split = this.id.split('-');
|
|
var modal_id = parseInt(split[2]);
|
|
|
|
if (modal_id >= id) {
|
|
id = modal_id + 1;
|
|
}
|
|
});
|
|
|
|
var html = `
|
|
<div class='modal fade modal-fixed-footer modal-primary inventree-modal' role='dialog' id='modal-form-${id}' tabindex='-1'>
|
|
<div class='modal-dialog'>
|
|
<div class='modal-content'>
|
|
<div class="modal-header">
|
|
<h4 id='modal-title' class='modal-title'>
|
|
<!-- Form title to be injected here -->
|
|
</h4>
|
|
<button type='button' class='btn-close' data-bs-dismiss='modal' aria-label='{% trans "Close" %}'></button>
|
|
</div>
|
|
<div class='modal-body modal-form-content-wrapper'>
|
|
<div id='non-field-errors'>
|
|
<!-- Form error messages go here -->
|
|
</div>
|
|
<div id='pre-form-content'>
|
|
<!-- Content can be inserted here *before* the form fields -->
|
|
</div>
|
|
|
|
<div id='form-content' class='modal-form-content'>
|
|
<!-- Form content will be injected here-->
|
|
</div>
|
|
<div id='post-form-content'>
|
|
<!-- Content can be inserted here *after* the form fields -->
|
|
</div>
|
|
</div>
|
|
<div class='modal-footer'>
|
|
<div id='modal-footer-buttons'>
|
|
<!-- Extra buttons can be inserted here -->
|
|
</div>
|
|
<span class='flex-item' style='flex-grow: 1;'></span>
|
|
<h4><span id='modal-progress-spinner' class='fas fa-circle-notch fa-spin' style='display: none;'></span></h4>
|
|
<button type='button' class='btn btn-secondary' id='modal-form-close' data-bs-dismiss='modal'>{% trans "Cancel" %}</button>
|
|
<button type='button' class='btn btn-primary' id='modal-form-submit'>{% trans "Submit" %}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
$('body').append(html);
|
|
|
|
var modal_name = `#modal-form-${id}`;
|
|
|
|
$(modal_name).on('shown.bs.modal', function() {
|
|
$(modal_name + ' .modal-form-content').scrollTop(0);
|
|
|
|
if (options.focus) {
|
|
getFieldByName(modal_name, options.focus).focus();
|
|
}
|
|
});
|
|
|
|
// Automatically remove the modal when it is deleted!
|
|
$(modal_name).on('hidden.bs.modal', function() {
|
|
$(modal_name).remove();
|
|
});
|
|
|
|
// Capture "enter" key input
|
|
$(modal_name).on('keydown', 'input', function(event) {
|
|
if (event.keyCode == 13) {
|
|
event.preventDefault();
|
|
// Simulate a click on the 'Submit' button
|
|
$(modal_name).find('#modal-form-submit').click();
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
$(modal_name).modal({
|
|
backdrop: 'static',
|
|
keyboard: user_settings.FORMS_CLOSE_USING_ESCAPE,
|
|
});
|
|
|
|
// Set labels based on supplied options
|
|
modalSetTitle(modal_name, options.title || '{% trans "Form Title" %}');
|
|
modalSetSubmitText(modal_name, options.submitText || '{% trans "Submit" %}');
|
|
modalSetCloseText(modal_name, options.cancelText || '{% trans "Cancel" %}');
|
|
|
|
if (options.hideSubmitButton) {
|
|
$(modal_name).find('#modal-form-submit').hide();
|
|
}
|
|
|
|
if (options.hideCloseButton) {
|
|
$(modal_name).find('#modal-form-cancel').hide();
|
|
}
|
|
|
|
// Return the "name" of the modal
|
|
return modal_name;
|
|
}
|
|
|
|
|
|
function makeOption(text, value, title) {
|
|
/* Format an option for a select element
|
|
*/
|
|
|
|
var html = `<option value='${value || text}'`;
|
|
|
|
if (title) {
|
|
html += ` title='${title}'`;
|
|
}
|
|
|
|
html += `>${text}</option>`;
|
|
|
|
return html;
|
|
}
|
|
|
|
function makeOptionsList(elements, textFunc, valueFunc, titleFunc) {
|
|
/*
|
|
* Programatically generate a list of <option> elements,
|
|
* from the (assumed array) of elements.
|
|
* For each element, we pass the element to the supplied functions,
|
|
* which (in turn) generate display / value / title values.
|
|
*
|
|
* Args:
|
|
* - elements: List of elements
|
|
* - textFunc: Function which takes an element and generates the text to be displayed
|
|
* - valueFunc: optional function which takes an element and generates the value
|
|
* - titleFunc: optional function which takes an element and generates a title
|
|
*/
|
|
|
|
var options = [];
|
|
|
|
elements.forEach(function(element) {
|
|
|
|
var text = textFunc(element);
|
|
var value = null;
|
|
var title = null;
|
|
|
|
if (valueFunc) {
|
|
value = valueFunc(element);
|
|
} else {
|
|
value = text;
|
|
}
|
|
|
|
if (titleFunc) {
|
|
title = titleFunc(element);
|
|
}
|
|
|
|
options.push(makeOption(text, value, title));
|
|
});
|
|
|
|
return options;
|
|
}
|
|
|
|
|
|
function setFieldOptions(fieldName, optionList, options={}) {
|
|
/* Set the options for a <select> field.
|
|
*
|
|
* Args:
|
|
* - fieldName: The name of the target field
|
|
* - Options: List of formatted <option> strings
|
|
* - append: If true, options will be appended, otherwise will replace existing options.
|
|
*/
|
|
|
|
|
|
var append = options.append || false;
|
|
|
|
var modal = options.modal || '#modal-form';
|
|
|
|
var field = getFieldByName(modal, fieldName);
|
|
|
|
var addEmptyOption = options.addEmptyOption || true;
|
|
|
|
// If not appending, clear out the field...
|
|
if (!append) {
|
|
field.find('option').remove();
|
|
}
|
|
|
|
if (addEmptyOption) {
|
|
// Add an 'empty' option at the top of the list
|
|
field.append(`<option value="">---------</option>`);
|
|
}
|
|
|
|
optionList.forEach(function(option) {
|
|
field.append(option);
|
|
});
|
|
|
|
}
|
|
|
|
|
|
function clearFieldOptions(fieldName) {
|
|
/**
|
|
* Clear (emtpy) the options list for a particular field
|
|
*/
|
|
|
|
setFieldOptions(fieldName, []);
|
|
}
|
|
|
|
|
|
function reloadFieldOptions(fieldName, options) {
|
|
/* Reload the options for a given field,
|
|
* using an AJAX request.
|
|
*
|
|
* Args:
|
|
* - fieldName: The name of the field
|
|
* - options:
|
|
* -- url: Query url
|
|
* -- params: Query params
|
|
* -- value: A function which takes a returned option and returns the 'value' (if not specified, the `pk` field is used)
|
|
* -- text: A function which takes a returned option and returns the 'text'
|
|
* -- title: A function which takes a returned option and returns the 'title' (optional!)
|
|
*/
|
|
|
|
inventreeGet(options.url, options.params, {
|
|
success: function(response) {
|
|
var opts = makeOptionsList(response,
|
|
function(item) {
|
|
return options.text(item);
|
|
},
|
|
function(item) {
|
|
if (options.value) {
|
|
return options.value(item);
|
|
} else {
|
|
// Fallback is to use the 'pk' field
|
|
return item.pk;
|
|
}
|
|
},
|
|
function(item) {
|
|
if (options.title) {
|
|
return options.title(item);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
);
|
|
|
|
// Update the target field with the new options
|
|
setFieldOptions(fieldName, opts);
|
|
},
|
|
error: function() {
|
|
console.log('Error GETting field options');
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function enableField(fieldName, enabled, options={}) {
|
|
/* Enable (or disable) a particular field in a modal.
|
|
*
|
|
* Args:
|
|
* - fieldName: The name of the field
|
|
* - enabled: boolean enabled / disabled status
|
|
* - options:
|
|
*/
|
|
|
|
var modal = options.modal || '#modal-form';
|
|
|
|
var field = getFieldByName(modal, fieldName);
|
|
|
|
field.prop('disabled', !enabled);
|
|
}
|
|
|
|
function clearField(fieldName, options={}) {
|
|
|
|
setFieldValue(fieldName, '', options);
|
|
}
|
|
|
|
function setFieldValue(fieldName, value, options={}) {
|
|
|
|
var modal = options.modal || '#modal-form';
|
|
|
|
var field = getFieldByName(modal, fieldName);
|
|
|
|
field.val(value);
|
|
}
|
|
|
|
function getFieldValue(fieldName, options={}) {
|
|
|
|
var modal = options.modal || '#modal-form';
|
|
|
|
var field = getFieldByName(modal, fieldName);
|
|
|
|
return field.val();
|
|
}
|
|
|
|
|
|
function partialMatcher(params, data) {
|
|
/* Replacement function for the 'matcher' parameter for a select2 dropdown.
|
|
|
|
Intead of performing an exact match search, a partial match search is performed.
|
|
This splits the search term by the space ' ' character and matches each segment.
|
|
Segments can appear out of order and are not case sensitive
|
|
|
|
Args:
|
|
params.term : search query
|
|
data.text : text to match
|
|
*/
|
|
|
|
// Quickly check for an empty search query
|
|
if ($.trim(params.term) == '') {
|
|
return data;
|
|
}
|
|
|
|
// Do not display the item if there is no 'text' property
|
|
if (typeof data.text === 'undefined') {
|
|
return null;
|
|
}
|
|
|
|
var search_terms = params.term.toLowerCase().trim().split(' ');
|
|
|
|
var match_text = data.text.toLowerCase().trim();
|
|
|
|
for (var ii = 0; ii < search_terms.length; ii++) {
|
|
if (!match_text.includes(search_terms[ii])) {
|
|
// Text must contain each search term
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Default: match!
|
|
return data;
|
|
}
|
|
|
|
|
|
function attachSelect(modal) {
|
|
/* Attach 'select2' functionality to any drop-down list in the modal.
|
|
* Provides search filtering for dropdown items
|
|
*/
|
|
|
|
$(modal + ' .select').select2({
|
|
dropdownParent: $(modal),
|
|
// dropdownAutoWidth parameter is required to work properly with modal forms
|
|
dropdownAutoWidth: false,
|
|
matcher: partialMatcher,
|
|
});
|
|
|
|
$(modal + ' .select2-container').addClass('select-full-width');
|
|
$(modal + ' .select2-container').css('width', '100%');
|
|
}
|
|
|
|
|
|
function loadingMessageContent() {
|
|
/* Render a 'loading' message to display in a form
|
|
* when waiting for a response from the server
|
|
*/
|
|
|
|
// TODO - This can be made a lot better
|
|
return `<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> {% trans 'Waiting for server...' %}`;
|
|
}
|
|
|
|
|
|
function afterForm(response, options) {
|
|
/* afterForm is called after a form is successfully submitted,
|
|
* and the form is dismissed.
|
|
* Used for general purpose functionality after form submission:
|
|
*
|
|
* - Display a bootstrap alert (success / info / warning / danger)
|
|
* - Run a supplied success callback function
|
|
* - Redirect the browser to a different URL
|
|
* - Reload the page
|
|
*/
|
|
|
|
// Should we show alerts immediately or cache them?
|
|
var cache = (options.follow && response.url) ||
|
|
options.redirect ||
|
|
options.reload;
|
|
|
|
// Display any messages
|
|
if (response.success) {
|
|
showAlertOrCache(response.success, cache, {style: 'success'});
|
|
}
|
|
|
|
if (response.info) {
|
|
showAlertOrCache(response.info, cache, {style: 'info'});
|
|
}
|
|
|
|
if (response.warning) {
|
|
showAlertOrCache(response.warning, cache, {style: 'warning'});
|
|
}
|
|
|
|
if (response.danger) {
|
|
showAlertOrCache(response.danger, cache, {style: 'danger'});
|
|
}
|
|
|
|
// Was a callback provided?
|
|
if (options.success) {
|
|
options.success(response);
|
|
} else if (options.follow && response.url) {
|
|
window.location.href = response.url;
|
|
} else if (options.redirect) {
|
|
window.location.href = options.redirect;
|
|
} else if (options.reload) {
|
|
location.reload();
|
|
}
|
|
}
|
|
|
|
function modalShowSubmitButton(modal, show=true) {
|
|
/* Show (or hide) the 'Submit' button for the given modal form
|
|
*/
|
|
|
|
if (show) {
|
|
$(modal).find('#modal-form-submit').show();
|
|
} else {
|
|
$(modal).find('#modal-form-submit').hide();
|
|
}
|
|
}
|
|
|
|
|
|
function modalEnable(modal, enable=true) {
|
|
/* Enable (or disable) modal form elements to prevent user input
|
|
*/
|
|
|
|
// Enable or disable the submit button
|
|
$(modal).find('#modal-form-submit').prop('disabled', !enable);
|
|
}
|
|
|
|
|
|
function modalSetTitle(modal, title='') {
|
|
/* Update the title of a modal form
|
|
*/
|
|
$(modal + ' #modal-title').html(title);
|
|
}
|
|
|
|
|
|
function modalSetContent(modal, content='') {
|
|
/* Update the content panel of a modal form
|
|
*/
|
|
$(modal).find('.modal-form-content').html(content);
|
|
}
|
|
|
|
|
|
function modalSetSubmitText(modal, text) {
|
|
if (text) {
|
|
$(modal).find('#modal-form-submit').html(text);
|
|
}
|
|
}
|
|
|
|
|
|
function modalSetCloseText(modal, text) {
|
|
if (text) {
|
|
$(modal).find('#modal-form-close').html(text);
|
|
}
|
|
}
|
|
|
|
|
|
function modalSetButtonText(modal, submit_text, close_text) {
|
|
/* Set the button text for a modal form
|
|
*
|
|
* submit_text - text for the form submit button
|
|
* close_text - text for the form dismiss button
|
|
*/
|
|
modalSetSubmitText(modal, submit_text);
|
|
modalSetCloseText(modal, close_text);
|
|
}
|
|
|
|
|
|
function closeModal(modal='#modal-form') {
|
|
/* Dismiss (hide) a modal form
|
|
*/
|
|
$(modal).modal('hide');
|
|
}
|
|
|
|
|
|
function modalSubmit(modal, callback) {
|
|
/* Perform the submission action for the modal form
|
|
*/
|
|
$(modal).off('click', '#modal-form-submit');
|
|
|
|
$(modal).on('click', '#modal-form-submit', function() {
|
|
callback();
|
|
});
|
|
|
|
$(modal).on('click', '.modal-form-button', function() {
|
|
// Append data to form
|
|
var name = $(this).attr('name');
|
|
var value = $(this).attr('value');
|
|
var input = '<input id="id_act-btn_' + name + '" type="hidden" name="act-btn_' + name + '" value="' + value + '">';
|
|
$('.js-modal-form').append(input);
|
|
callback();
|
|
});
|
|
}
|
|
|
|
|
|
function removeRowFromModalForm(e) {
|
|
/* Remove a row from a table in a modal form */
|
|
e = e || window.event;
|
|
|
|
var src = e.target || e.srcElement;
|
|
|
|
var row = $(src).attr('row');
|
|
|
|
$('#' + row).remove();
|
|
}
|
|
|
|
|
|
function renderErrorMessage(xhr) {
|
|
|
|
var html = '<b>' + xhr.statusText + '</b><br>';
|
|
|
|
html += '<b>Error Code - ' + xhr.status + '</b><br><hr>';
|
|
|
|
html += `
|
|
<div class='panel-group'>
|
|
<div class='panel'>
|
|
<div class='panel panel-heading'>
|
|
<div class='panel-title'>
|
|
<a data-bs-toggle='collapse' href="#collapse-error-info">{% trans "Show Error Information" %}</a>
|
|
</div>
|
|
</div>
|
|
<div class='panel-collapse collapse' id='collapse-error-info'>
|
|
<div class='panel-content'>`;
|
|
|
|
html += xhr.responseText;
|
|
|
|
html += `
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
|
|
return html;
|
|
}
|
|
|
|
|
|
function showAlertDialog(title, content, options={}) {
|
|
/* Display a modal dialog message box.
|
|
*
|
|
* title - Title text
|
|
* content - HTML content of the dialog window
|
|
*/
|
|
|
|
if (options.alert_style) {
|
|
// Wrap content in an alert block
|
|
content = `<div class='alert alert-block alert-${options.alert_style}'>${content}</div>`;
|
|
}
|
|
|
|
|
|
var modal = createNewModal({
|
|
title: title,
|
|
cancelText: '{% trans "Close" %}',
|
|
hideSubmitButton: true,
|
|
});
|
|
|
|
modalSetContent(modal, content);
|
|
|
|
$(modal).modal('show');
|
|
}
|
|
|
|
|
|
function showQuestionDialog(title, content, options={}) {
|
|
/* Display a modal dialog for user input (Yes/No confirmation dialog)
|
|
*
|
|
* title - Title text
|
|
* content - HTML content of the dialog window
|
|
* options:
|
|
* modal - Modal target (default = 'modal-question-dialog')
|
|
* accept_text - Text for the accept button (default = 'Accept')
|
|
* cancel_text - Text for the cancel button (default = 'Cancel')
|
|
* accept - Function to run if the user presses 'Accept'
|
|
* cancel - Functino to run if the user presses 'Cancel'
|
|
*/
|
|
|
|
var modal = createNewModal({
|
|
title: title,
|
|
submitText: options.accept_text || '{% trans "Accept" %}',
|
|
cancelText: options.cancel_text || '{% trans "Cancel" %}',
|
|
});
|
|
|
|
modalSetContent(modal, content);
|
|
|
|
$(modal).on('click', '#modal-form-submit', function() {
|
|
$(modal).modal('hide');
|
|
|
|
if (options.accept) {
|
|
options.accept();
|
|
}
|
|
});
|
|
|
|
$(modal).modal('show');
|
|
}
|
|
|
|
function openModal(options) {
|
|
/* Open a modal form, and perform some action based on the provided options object:
|
|
*
|
|
* options can contain:
|
|
*
|
|
* modal - ID of the modal form element (default = '#modal-form')
|
|
* title - Custom title for the form
|
|
* content - Default content for the form panel
|
|
* submit_text - Label for the submit button (default = 'Submit')
|
|
* close_text - Label for the close button (default = 'Close')
|
|
*/
|
|
|
|
var modal = options.modal || '#modal-form';
|
|
|
|
// Ensure that the 'warning' div is hidden
|
|
$(modal).find('#form-validation-warning').css('display', 'none');
|
|
|
|
$(modal).on('shown.bs.modal', function() {
|
|
$(modal + ' .modal-form-content').scrollTop(0);
|
|
if (options.focus) {
|
|
getFieldByName(modal, options.focus).focus();
|
|
}
|
|
});
|
|
|
|
// Prevent 'enter' key from submitting the form using the normal method
|
|
$(modal).on('keydown', '.js-modal-form', function(event) {
|
|
if (event.keyCode == 13) {
|
|
event.preventDefault();
|
|
|
|
// Simulate a click on the 'Submit' button
|
|
$(modal).find('#modal-form-submit').click();
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
// Unless the title is explicitly set, display loading message
|
|
if (options.title) {
|
|
modalSetTitle(modal, options.title);
|
|
} else {
|
|
modalSetTitle(modal, '{% trans "Loading Data" %}...');
|
|
}
|
|
|
|
// Unless the content is explicitly set, display loading message
|
|
if (options.content) {
|
|
modalSetContent(modal, options.content);
|
|
} else {
|
|
modalSetContent(modal, loadingMessageContent());
|
|
}
|
|
|
|
// Default labels for 'Submit' and 'Close' buttons in the form
|
|
var submit_text = options.submit_text || '{% trans "Submit" %}';
|
|
var close_text = options.close_text || '{% trans "Close" %}';
|
|
|
|
modalSetButtonText(modal, submit_text, close_text);
|
|
|
|
$(modal).modal({
|
|
backdrop: 'static',
|
|
keyboard: user_settings.FORMS_CLOSE_USING_ESCAPE,
|
|
});
|
|
|
|
// Disable the form
|
|
modalEnable(modal, false);
|
|
|
|
// Finally, display the modal window
|
|
$(modal).modal('show');
|
|
}
|
|
|
|
|
|
function injectModalForm(modal, form_html) {
|
|
/* Inject form content into the modal.
|
|
* Updates the HTML of the form content, and then applies some other updates
|
|
*/
|
|
$(modal).find('.modal-form-content').html(form_html);
|
|
attachSelect(modal);
|
|
}
|
|
|
|
|
|
function getFieldByName(modal, name) {
|
|
/* Find the field (with the given name) within the modal */
|
|
|
|
return $(modal).find(`#id_${name}`);
|
|
}
|
|
|
|
|
|
function insertNewItemButton(modal, options) {
|
|
/* Insert a button into a modal form, after a field label.
|
|
* Looks for a <label> tag inside the form with the attribute "for='id_<field>'"
|
|
* Inserts a button at the end of this lael element.
|
|
*/
|
|
|
|
var html = `<span style='float: right;'>`;
|
|
|
|
html += `<div type='button' class='btn btn-primary btn-secondary'`;
|
|
|
|
if (options.title) {
|
|
html += ` title='${ options.title}'`;
|
|
}
|
|
|
|
html += ` id='btn-new-${options.field}'>${options.label}</div>`;
|
|
|
|
html += '</span>';
|
|
|
|
$(modal).find('label[for="id_'+ options.field + '"]').append(html);
|
|
}
|
|
|
|
|
|
function attachSecondaryModal(modal, options) {
|
|
/* Attach a secondary modal form to the primary modal form.
|
|
* Inserts a button into the primary form which, when clicked,
|
|
* will launch the secondary modal to do /something/ and then return a result.
|
|
*
|
|
* options:
|
|
* field: Name of the field to attach to
|
|
* label: Button text
|
|
* title: Hover text to display over button (optional)
|
|
* url: URL for the secondary modal
|
|
* query: Query params for the secondary modal
|
|
*/
|
|
|
|
// Insert the button
|
|
insertNewItemButton(modal, options);
|
|
|
|
var data = options.data || {};
|
|
|
|
// Add a callback to the button
|
|
$(modal).find('#btn-new-' + options.field).on('click', function() {
|
|
|
|
// Launch the secondary modal
|
|
launchModalForm(
|
|
options.url,
|
|
{
|
|
modal: '#modal-form-secondary',
|
|
data: data,
|
|
success: function(response) {
|
|
|
|
/* A successful object creation event should return a response which contains:
|
|
* - pk: ID of the newly created object
|
|
* - text: String descriptor of the newly created object
|
|
*
|
|
* So it is simply a matter of appending and selecting the new object!
|
|
*/
|
|
|
|
var select = '#id_' + options.field;
|
|
|
|
var option = new Option(response.text, response.pk, true, true);
|
|
|
|
$(modal).find(select).append(option).trigger('change');
|
|
}
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
function attachSecondaries(modal, secondaries) {
|
|
/* Attach a provided list of secondary modals */
|
|
|
|
// 2021-07-18 - Secondary modals will be disabled for now, until they are re-implemented in the "API forms" architecture
|
|
|
|
// for (var i = 0; i < secondaries.length; i++) {
|
|
// attachSecondaryModal(modal, secondaries[i]);
|
|
// }
|
|
}
|
|
|
|
function insertActionButton(modal, options) {
|
|
/* Insert a custom submission button */
|
|
|
|
var element = $(modal).find('#modal-footer-buttons');
|
|
|
|
// check if button already present
|
|
var already_present = false;
|
|
for (var child=element[0].firstElementChild; child; child=child.nextElementSibling) {
|
|
if (item.firstElementChild.name == options.name) {
|
|
already_present = true;
|
|
}
|
|
}
|
|
|
|
if (already_present == false) {
|
|
var html = `
|
|
<span style='float: right;'>
|
|
<button name='${options.name}' type='submit' class='btn btn-outline-secondary modal-form-button' value='${options.name}'>
|
|
${options.title}
|
|
</button>
|
|
</span>`;
|
|
element.append(html);
|
|
}
|
|
}
|
|
|
|
function attachButtons(modal, buttons) {
|
|
/* Attach a provided list of buttons */
|
|
|
|
for (var i = 0; i < buttons.length; i++) {
|
|
insertActionButton(modal, buttons[i]);
|
|
}
|
|
}
|
|
|
|
|
|
function attachFieldCallback(modal, callback) {
|
|
/* Attach a 'callback' function to a given field in the modal form.
|
|
* When the value of that field is changed, the callback function is performed.
|
|
*
|
|
* options:
|
|
* - field: The name of the field to attach to
|
|
* - action: A function to perform
|
|
*/
|
|
|
|
// Find the field input in the form
|
|
var field = getFieldByName(modal, callback.field);
|
|
|
|
field.change(function() {
|
|
|
|
if (callback.action) {
|
|
// Run the callback function with the new value of the field!
|
|
callback.action(field.val(), field);
|
|
} else {
|
|
console.log(`Value changed for field ${callback.field} - ${field.val()}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function attachCallbacks(modal, callbacks) {
|
|
/* Attach a provided list of callback functions */
|
|
|
|
for (var i = 0; i < callbacks.length; i++) {
|
|
attachFieldCallback(modal, callbacks[i]);
|
|
}
|
|
}
|
|
|
|
|
|
function handleModalForm(url, options) {
|
|
/* Update a modal form after data are received from the server.
|
|
* Manages POST requests until the form is successfully submitted.
|
|
*
|
|
* The server should respond with a JSON object containing a boolean value 'form_valid'
|
|
* Form submission repeats (after user interaction) until 'form_valid' = true
|
|
*/
|
|
|
|
var modal = options.modal || '#modal-form';
|
|
|
|
var form = $(modal).find('.js-modal-form');
|
|
|
|
form.ajaxForm({
|
|
url: url,
|
|
dataType: 'json',
|
|
type: 'post'
|
|
});
|
|
|
|
form.submit(function() {
|
|
// We should never get here (form submission is overridden)
|
|
alert('form submit');
|
|
return false;
|
|
});
|
|
|
|
modalSubmit(modal, function() {
|
|
$(modal).find('.js-modal-form').ajaxSubmit({
|
|
url: url,
|
|
beforeSend: function() {
|
|
// Disable modal until the server returns a response
|
|
modalEnable(modal, false);
|
|
},
|
|
// POST was successful
|
|
success: function(response) {
|
|
// Re-enable the modal
|
|
modalEnable(modal, true);
|
|
if ('form_valid' in response) {
|
|
// Form data was validated correctly
|
|
if (response.form_valid) {
|
|
$(modal).modal('hide');
|
|
afterForm(response, options);
|
|
} else {
|
|
// Form was returned, invalid!
|
|
|
|
// Disable error message with option or response
|
|
if (!options.hideErrorMessage && !response.hideErrorMessage) {
|
|
var warningDiv = $(modal).find('#form-validation-warning');
|
|
warningDiv.css('display', 'block');
|
|
}
|
|
|
|
if (response.html_form) {
|
|
injectModalForm(modal, response.html_form);
|
|
|
|
if (options.after_render) {
|
|
options.after_render(modal, response);
|
|
}
|
|
|
|
if (options.secondary) {
|
|
attachSecondaries(modal, options.secondary);
|
|
}
|
|
|
|
// Set modal title with response
|
|
if (response.title) {
|
|
modalSetTitle(modal, response.title);
|
|
}
|
|
|
|
// Clean custom action buttons
|
|
$(modal).find('#modal-footer-buttons').html('');
|
|
|
|
// Add custom action buttons with response
|
|
if (response.buttons) {
|
|
attachButtons(modal, response.buttons);
|
|
}
|
|
} else {
|
|
$(modal).modal('hide');
|
|
showAlertDialog('{% trans "Invalid response from server" %}', '{% trans "Form data missing from server response" %}');
|
|
}
|
|
}
|
|
} else {
|
|
$(modal).modal('hide');
|
|
afterForm(response, options);
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
// There was an error submitting form data via POST
|
|
|
|
$(modal).modal('hide');
|
|
showAlertDialog('{% trans "Error posting form data" %}', renderErrorMessage(xhr));
|
|
},
|
|
complete: function() {
|
|
// TODO
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
function launchModalForm(url, options = {}) {
|
|
/* Launch a modal form, and request data from the server to fill the form
|
|
* If the form data is returned from the server, calls handleModalForm()
|
|
*
|
|
* A successful request will return a JSON object with, at minimum,
|
|
* an object called 'html_form'
|
|
*
|
|
* If the request is NOT successful, displays an appropriate error message.
|
|
*
|
|
* options:
|
|
*
|
|
* modal - Name of the modal (default = '#modal-form')
|
|
* data - Data to pass through to the AJAX request to fill the form
|
|
* submit_text - Text for the submit button (default = 'Submit')
|
|
* close_text - Text for the close button (default = 'Close')
|
|
* no_post - If true, only display form data, hide submit button, and disallow POST
|
|
* after_render - Callback function to run after form is rendered
|
|
* secondary - List of secondary modals to attach
|
|
* callback - List of callback functions to attach to inputs
|
|
* focus - Select which field to focus on by default
|
|
* buttons - additional buttons that should be added as array with [name, title]
|
|
*/
|
|
|
|
var modal = options.modal || '#modal-form';
|
|
|
|
// Default labels for 'Submit' and 'Close' buttons in the form
|
|
var submit_text = options.submit_text || '{% trans "Submit" %}';
|
|
var close_text = options.close_text || '{% trans "Close" %}';
|
|
|
|
// Clean custom action buttons
|
|
$(modal).find('#modal-footer-buttons').html('');
|
|
|
|
// Form the ajax request to retrieve the django form data
|
|
var ajax_data = {
|
|
url: url,
|
|
type: 'get',
|
|
dataType: 'json',
|
|
beforeSend: function() {
|
|
openModal({
|
|
modal: modal,
|
|
submit_text: submit_text,
|
|
close_text: close_text,
|
|
focus: options.focus
|
|
});
|
|
},
|
|
success: function(response) {
|
|
|
|
// Enable the form
|
|
modalEnable(modal, true);
|
|
|
|
if (response.title) {
|
|
modalSetTitle(modal, response.title);
|
|
}
|
|
|
|
if (response.html_form) {
|
|
injectModalForm(modal, response.html_form);
|
|
|
|
if (options.after_render) {
|
|
options.after_render(modal, response);
|
|
}
|
|
|
|
if (options.secondary) {
|
|
attachSecondaries(modal, options.secondary);
|
|
}
|
|
|
|
if (options.callback) {
|
|
attachCallbacks(modal, options.callback);
|
|
}
|
|
|
|
if (options.no_post) {
|
|
modalShowSubmitButton(modal, false);
|
|
} else {
|
|
modalShowSubmitButton(modal, true);
|
|
handleModalForm(url, options);
|
|
}
|
|
|
|
if (options.buttons) {
|
|
attachButtons(modal, options.buttons);
|
|
}
|
|
|
|
// Add custom buttons from response
|
|
if (response.buttons) {
|
|
attachButtons(modal, response.buttons);
|
|
}
|
|
|
|
} else {
|
|
$(modal).modal('hide');
|
|
showAlertDialog('{% trans "Invalid server response" %}', '{% trans "JSON response missing form data" %}');
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
|
|
$(modal).modal('hide');
|
|
|
|
if (xhr.status == 0) {
|
|
// No response from the server
|
|
showAlertDialog(
|
|
'{% trans "No Response" %}',
|
|
'{% trans "No response from the InvenTree server" %}',
|
|
);
|
|
} else if (xhr.status == 400) {
|
|
showAlertDialog(
|
|
'{% trans "Error 400: Bad Request" %}',
|
|
'{% trans "Server returned error code 400" %}',
|
|
);
|
|
} else if (xhr.status == 401) {
|
|
showAlertDialog(
|
|
'{% trans "Error 401: Not Authenticated" %}',
|
|
'{% trans "Authentication credentials not supplied" %}',
|
|
);
|
|
} else if (xhr.status == 403) {
|
|
showAlertDialog(
|
|
'{% trans "Error 403: Permission Denied" %}',
|
|
'{% trans "You do not have the required permissions to access this function" %}',
|
|
);
|
|
} else if (xhr.status == 404) {
|
|
showAlertDialog(
|
|
'{% trans "Error 404: Resource Not Found" %}',
|
|
'{% trans "The requested resource could not be located on the server" %}',
|
|
);
|
|
} else if (xhr.status == 408) {
|
|
showAlertDialog(
|
|
'{% trans "Error 408: Timeout" %}',
|
|
'{% trans "Connection timeout while requesting data from server" %}',
|
|
);
|
|
} else {
|
|
showAlertDialog('{% trans "Error requesting form data" %}', renderErrorMessage(xhr));
|
|
}
|
|
|
|
console.log('Modal form error: ' + xhr.status);
|
|
console.log('Message: ' + xhr.responseText);
|
|
}
|
|
};
|
|
|
|
// Add in extra request data if provided
|
|
if (options.data) {
|
|
ajax_data.data = options.data;
|
|
}
|
|
|
|
// Send the AJAX request
|
|
$.ajax(ajax_data);
|
|
}
|
|
|
|
|
|
function hideModalImage() {
|
|
|
|
var modal = $('#modal-image-dialog');
|
|
|
|
modal.animate({
|
|
opacity: 0.0,
|
|
}, 250, function() {
|
|
modal.hide();
|
|
});
|
|
|
|
}
|
|
|
|
|
|
function showModalImage(image_url) {
|
|
// Display full-screen modal image
|
|
|
|
var modal = $('#modal-image-dialog');
|
|
|
|
// Set image content
|
|
$('#modal-image').attr('src', image_url);
|
|
|
|
modal.hide();
|
|
modal.show();
|
|
|
|
modal.animate({
|
|
opacity: 1.0,
|
|
}, 250);
|
|
|
|
$('#modal-image-close').click(function() {
|
|
hideModalImage();
|
|
});
|
|
|
|
modal.click(function() {
|
|
hideModalImage();
|
|
});
|
|
}
|