mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #333 from SchrodingersGat/secondary-modals
Provide an after_render callback
This commit is contained in:
commit
35d32fd2ff
@ -245,6 +245,7 @@ class AjaxCreateView(AjaxMixin, CreateView):
|
||||
|
||||
# Return the PK of the newly-created object
|
||||
data['pk'] = obj.pk
|
||||
data['text'] = str(obj)
|
||||
|
||||
try:
|
||||
data['url'] = obj.get_absolute_url()
|
||||
|
@ -34,7 +34,6 @@ InvenTree | Allocate Parts
|
||||
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script src="{% static 'script/inventree/part.js' %}"></script>
|
||||
<script src="{% static 'script/inventree/build.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
@ -670,6 +670,7 @@ class BomItem(models.Model):
|
||||
# A link to the parent part
|
||||
# Each part will get a reverse lookup field 'bom_items'
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='bom_items',
|
||||
help_text='Select parent part',
|
||||
limit_choices_to={
|
||||
'buildable': True,
|
||||
'active': True,
|
||||
@ -678,16 +679,17 @@ class BomItem(models.Model):
|
||||
# A link to the child item (sub-part)
|
||||
# Each part will get a reverse lookup field 'used_in'
|
||||
sub_part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='used_in',
|
||||
help_text='Select part to be used in BOM',
|
||||
limit_choices_to={
|
||||
'consumable': True,
|
||||
'active': True
|
||||
})
|
||||
|
||||
# Quantity required
|
||||
quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
|
||||
quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)], help_text='BOM quantity for this BOM item')
|
||||
|
||||
# Note attached to this BOM line item
|
||||
note = models.CharField(max_length=100, blank=True, help_text='Item notes')
|
||||
note = models.CharField(max_length=100, blank=True, help_text='BOM item notes')
|
||||
|
||||
def clean(self):
|
||||
""" Check validity of the BomItem model.
|
||||
@ -767,7 +769,7 @@ class SupplierPart(models.Model):
|
||||
|
||||
MPN = models.CharField(max_length=100, blank=True, help_text='Manufacturer part number')
|
||||
|
||||
URL = models.URLField(blank=True)
|
||||
URL = models.URLField(blank=True, help_text='URL for external supplier part link')
|
||||
|
||||
description = models.CharField(max_length=250, blank=True, help_text='Supplier part description')
|
||||
|
||||
|
@ -56,8 +56,6 @@
|
||||
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/part.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/bom.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@ -83,7 +81,15 @@
|
||||
{
|
||||
success: function() {
|
||||
$("#bom-table").bootstrapTable('refresh');
|
||||
}
|
||||
},
|
||||
secondary: [
|
||||
{
|
||||
field: 'sub_part',
|
||||
label: 'New Part',
|
||||
title: 'Create New Part',
|
||||
url: "{% url 'part-create' %}",
|
||||
},
|
||||
]
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -61,8 +61,6 @@
|
||||
{% endblock %}
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javacript' src="{% static 'script/inventree/stock.js' %}"></script>
|
||||
{% endblock %}
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
@ -82,15 +80,30 @@
|
||||
|
||||
$("#part-create").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-create' %}",
|
||||
{
|
||||
follow: true,
|
||||
data: {
|
||||
{% if category %}
|
||||
category: {{ category.id }}
|
||||
{% endif %}
|
||||
}
|
||||
});
|
||||
"{% url 'part-create' %}",
|
||||
{
|
||||
follow: true,
|
||||
data: {
|
||||
{% if category %}
|
||||
category: {{ category.id }}
|
||||
{% endif %}
|
||||
},
|
||||
secondary: [
|
||||
{
|
||||
field: 'category',
|
||||
label: 'New Category',
|
||||
title: 'Create new Part Category',
|
||||
url: "{% url 'category-create' %}",
|
||||
},
|
||||
{
|
||||
field: 'default_location',
|
||||
label: 'New Location',
|
||||
title: 'Create new Stock Location',
|
||||
url: "{% url 'stock-location-create' %}",
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
{% if category %}
|
||||
|
@ -20,6 +20,7 @@
|
||||
<li><a href="#" id='deactivate-part' title='Deactivate part'>Deactivate</a></li>
|
||||
{% else %}
|
||||
<li><a href="#" id='activate-part' title='Activate part'>Activate</a></li>
|
||||
<li><a href='#' id='delete-part' title='Delete part'>Delete</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@ -128,8 +129,6 @@
|
||||
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/stock.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_ready %}
|
||||
@ -200,10 +199,10 @@
|
||||
|
||||
$('#delete-part').click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-delete' part.id %}",
|
||||
{
|
||||
redirect: {% if part.category %}"{% url 'category-detail' part.category.id %}"{% else %}"{% url 'part-index' %}"{% endif %}
|
||||
});
|
||||
"{% url 'part-delete' part.id %}",
|
||||
{
|
||||
redirect: {% if part.category %}"{% url 'category-detail' part.category.id %}"{% else %}"{% url 'part-index' %}"{% endif %}
|
||||
});
|
||||
});
|
||||
|
||||
{% endblock %}
|
||||
|
@ -29,6 +29,7 @@ InvenTree | Part List
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script type='text/javascript' src="{% static 'script/inventree/part.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/stock.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_ready %}
|
||||
|
@ -1,6 +1,11 @@
|
||||
{% extends "modal_form.html" %}
|
||||
|
||||
{% block pre_form_content %}
|
||||
|
||||
Are you sure you want to delete part '{{ part.full_name }}'?
|
||||
|
||||
{% if part.used_in_count %}
|
||||
<hr>
|
||||
<p>This part is used in BOMs for {{ part.used_in_count }} other parts. If you delete this part, the BOMs for the following parts will be updated:
|
||||
<ul class="list-group">
|
||||
{% for child in part.used_in.all %}
|
||||
@ -10,6 +15,7 @@ Are you sure you want to delete part '{{ part.full_name }}'?
|
||||
{% endif %}
|
||||
|
||||
{% if part.locations.all|length > 0 %}
|
||||
<hr>
|
||||
<p>There are {{ part.locations.all|length }} stock entries defined for this part. If you delete this part, the following stock entries will also be deleted:
|
||||
<ul class='list-group'>
|
||||
{% for stock in part.locations.all %}
|
||||
@ -20,6 +26,7 @@ Are you sure you want to delete part '{{ part.full_name }}'?
|
||||
{% endif %}
|
||||
|
||||
{% if part.supplier_parts.all|length > 0 %}
|
||||
<hr>
|
||||
<p>There are {{ part.supplier_parts.all|length }} suppliers defined for this part. If you delete this part, the following supplier parts will also be deleted.
|
||||
<ul class='list-group'>
|
||||
{% for spart in part.supplier_parts.all %}
|
||||
@ -30,5 +37,8 @@ Are you sure you want to delete part '{{ part.full_name }}'?
|
||||
{% endif %}
|
||||
|
||||
{% if part.serials.all|length > 0 %}
|
||||
<hr>
|
||||
<p>There are {{ part.serials.all|length }} unique parts tracked for '{{ part.full_name }}'. Deleting this part will permanently remove this tracking information.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
@ -37,8 +37,6 @@
|
||||
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/stock.js' %}"></script>
|
||||
{% endblock %}
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
|
@ -202,6 +202,7 @@ class PartDuplicate(AjaxCreateView):
|
||||
part = form.save()
|
||||
|
||||
data['pk'] = part.pk
|
||||
data['text'] = str(part)
|
||||
|
||||
deep_copy = str2bool(request.POST.get('deep_copy', False))
|
||||
|
||||
@ -321,6 +322,7 @@ class PartCreate(AjaxCreateView):
|
||||
part = form.save()
|
||||
|
||||
data['pk'] = part.pk
|
||||
data['text'] = str(part)
|
||||
|
||||
try:
|
||||
data['url'] = part.get_absolute_url()
|
||||
|
@ -51,7 +51,7 @@
|
||||
background: #eee;
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
z-index: 400;
|
||||
border: 1px solid #555;
|
||||
max-width: 250px;
|
||||
}
|
||||
@ -159,7 +159,15 @@
|
||||
|
||||
.modal {
|
||||
overflow: hidden;
|
||||
z-index: 99999999;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.modal-primary {
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.modal-secondary {
|
||||
z-index: 11000;
|
||||
}
|
||||
|
||||
.js-modal-form .checkbox {
|
||||
@ -170,6 +178,11 @@
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.modal-secondary .modal-dialog {
|
||||
width: 40%;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
@ -192,6 +205,16 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Force a control-label div to be 100% width */
|
||||
.modal .control-label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal .control-label .btn {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
/* The side navigation menu */
|
||||
.sidenav {
|
||||
height: 100%; /* 100% Full-height */
|
||||
@ -217,7 +240,7 @@
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
z-index: 999999;
|
||||
z-index: 5000;
|
||||
pointer-events: none; // Prevent this div from blocking links underneath
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ function afterForm(response, options) {
|
||||
|
||||
// Was a callback provided?
|
||||
if (options.success) {
|
||||
options.success();
|
||||
options.success(response);
|
||||
}
|
||||
else if (options.follow && response.url) {
|
||||
window.location.href = response.url;
|
||||
@ -354,6 +354,81 @@ function injectModalForm(modal, form_html) {
|
||||
}
|
||||
|
||||
|
||||
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'";
|
||||
|
||||
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);
|
||||
|
||||
// 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',
|
||||
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');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function attachSecondaries(modal, secondaries) {
|
||||
/* Attach a provided list of secondary modals */
|
||||
|
||||
for (var i = 0; i < secondaries.length; i++) {
|
||||
attachSecondaryModal(modal, secondaries[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.
|
||||
@ -401,6 +476,14 @@ function handleModalForm(url, options) {
|
||||
else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$(modal).modal('hide');
|
||||
@ -443,6 +526,8 @@ function launchModalForm(url, options = {}) {
|
||||
* 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
|
||||
*/
|
||||
|
||||
var modal = options.modal || '#modal-form';
|
||||
@ -475,6 +560,14 @@ function launchModalForm(url, options = {}) {
|
||||
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.no_post) {
|
||||
modalShowSubmitButton(modal, false);
|
||||
} else {
|
||||
|
@ -345,32 +345,6 @@ function moveStockItems(items, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function deleteStockItems(items, options) {
|
||||
|
||||
var modal = '#modal-delete';
|
||||
|
||||
if ('modal' in options) {
|
||||
modal = options.modal;
|
||||
}
|
||||
|
||||
if (items.length == 0) {
|
||||
alert('No stock items selected');
|
||||
return;
|
||||
}
|
||||
|
||||
function doDelete(parts) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
openModal({
|
||||
modal: modal,
|
||||
title: 'Delete ' + items.length + ' stock items'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function loadStockTable(table, options) {
|
||||
|
||||
table.bootstrapTable({
|
||||
|
@ -32,8 +32,8 @@ class CreateStockItemForm(HelperForm):
|
||||
'part',
|
||||
'supplier_part',
|
||||
'location',
|
||||
'batch',
|
||||
'quantity',
|
||||
'batch',
|
||||
'delete_on_deplete',
|
||||
'status',
|
||||
'notes',
|
||||
|
@ -69,8 +69,6 @@
|
||||
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/stock.js' %}"></script>
|
||||
{% endblock %}
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
@ -123,9 +121,24 @@
|
||||
{% if location %}
|
||||
location: {{ location.id }}
|
||||
{% endif %}
|
||||
}
|
||||
},
|
||||
secondary: [
|
||||
{
|
||||
field: 'part',
|
||||
label: 'New Part',
|
||||
title: 'Create New Part',
|
||||
url: "{% url 'part-create' %}",
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
label: 'New Location',
|
||||
title: 'Create New Location',
|
||||
url: "{% url 'stock-location-create' %}",
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
@ -25,7 +25,6 @@ InvenTree | Stock
|
||||
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/stock.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -95,10 +95,9 @@ InvenTree
|
||||
|
||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/part.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/tables.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/modals.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/sidenav.js' %}"></script>
|
||||
{% block js_load %}
|
||||
{% endblock %}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class='modal fade modal-fixed-footer' tabindex='-1' role='dialog' id='modal-form'>
|
||||
<div class='modal fade modal-fixed-footer modal-primary' tabindex='-1' role='dialog' id='modal-form'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
@ -17,27 +17,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='modal fade modal-fixed-footer' tabindex='-1' role='dialog' id='modal-delete'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h3 id='modal-title'>Confirm Item Deletion</h3>
|
||||
</div>
|
||||
<form action='' method='post' id='delete-form'>
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
<div class='modal-form-content'>
|
||||
</div>
|
||||
<div class='modal-footer'>
|
||||
<button type='button' class='btn btn-default' data-dismiss='modal'>Cancel</button>
|
||||
<button type='button' class='btn btn-danger' id='modal-form-delete'>Delete</button>
|
||||
<div class='modal fade modal-fixed-footer modal-secondary' tabindex='-1' role='dialog' id='modal-form-secondary'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h3 id='modal-title'>Form Title Here</h3>
|
||||
</div>
|
||||
<div class='modal-form-content'>
|
||||
</div>
|
||||
<div class='modal-footer'>
|
||||
<button type='button' class='btn btn-default' id='modal-form-close' data-dismiss='modal'>Close</button>
|
||||
<button type='button' class='btn btn-primary' id='modal-form-submit'>Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='modal fade modal-fixed-footer' tabindex='-1' role='dialog' id='modal-question-dialog'>
|
||||
<div class='modal-dialog'>
|
||||
|
Loading…
Reference in New Issue
Block a user