From 565631ef87696dff672024b847f6f91aecd020b7 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 26 Jun 2021 14:09:35 +1000 Subject: [PATCH] More features - Custom renderers depending on specified model name - Paginate API results --- InvenTree/InvenTree/static/css/inventree.css | 7 ++ InvenTree/InvenTree/urls.py | 1 + InvenTree/templates/base.html | 1 + InvenTree/templates/js/forms.js | 73 +++++++++++++---- InvenTree/templates/js/model_renderers.js | 83 ++++++++++++++++++++ 5 files changed, 150 insertions(+), 15 deletions(-) create mode 100644 InvenTree/templates/js/model_renderers.js diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css index 573f966a41..706bc32327 100644 --- a/InvenTree/InvenTree/static/css/inventree.css +++ b/InvenTree/InvenTree/static/css/inventree.css @@ -972,4 +972,11 @@ input[type="date"].form-control, input[type="time"].form-control, input[type="da .select2-container { width: 100%; +} + +.select2-thumbnail { + max-width: 24px; + max-height: 24px; + border-radius: 4px; + margin-right: 10px; } \ No newline at end of file diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 2ec7b02f23..6a7ae7bdfd 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -105,6 +105,7 @@ settings_urls = [ dynamic_javascript_urls = [ url(r'^api.js', DynamicJsView.as_view(template_name='js/api.js'), name='api.js'), url(r'^forms.js', DynamicJsView.as_view(template_name='js/forms.js'), name='forms.js'), + url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/model_renderers.js'), name='model_renderers.js'), url(r'^modals.js', DynamicJsView.as_view(template_name='js/modals.js'), name='modals.js'), url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'), url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'), diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index f35dd09fc8..ec7e31a7f0 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -150,6 +150,7 @@ + diff --git a/InvenTree/templates/js/forms.js b/InvenTree/templates/js/forms.js index a1937a5ef8..a68def10b0 100644 --- a/InvenTree/templates/js/forms.js +++ b/InvenTree/templates/js/forms.js @@ -364,7 +364,8 @@ function initializeRelatedField(modal, name, field, options) { // TODO: Add 'placeholder' support for entry select2 fields - // TODO: Add 'pagination' support for the query + // limit size for AJAX requests + var pageSize = options.pageSize || 25; select.select2({ ajax: { @@ -377,31 +378,52 @@ function initializeRelatedField(modal, name, field, options) { cache: true, // matcher: partialMatcher, data: function(params) { + + if (!params.page) { + offset = 0; + } else { + offset = (params.page - 1) * pageSize; + } + // Re-format search term into InvenTree API style return { search: params.term, + offset: offset, + limit: pageSize, }; }, - processResults: function(data) { + processResults: function(response) { // Convert the returned InvenTree data into select2-friendly format - var rows = []; - // Only ever show the first x items - for (var idx = 0; idx < data.length && idx < 50; idx++) { - var row = data[idx]; + var data = []; - // Reformat to match select2 requirements - row.id = row.id || row.pk; + var more = false; - // TODO: Fix me? - row.text = `This is ${field.api_url}${row.id}/`; + if ('count' in response && 'results' in response) { + // Response is paginated + data = response.results; - rows.push(row); + // Any more data available? + if (response.next) { + more = true; + } + + } else { + // Non-paginated response + data = response; + } + + // Each 'row' must have the 'id' attribute + for (var idx = 0; idx < data.length; idx++) { + data[idx].id = data[idx].pk; } // Ref: https://select2.org/data-sources/formats var results = { - results: rows, + results: data, + pagination: { + more: more, + } }; return results; @@ -442,7 +464,7 @@ function initializeRelatedField(modal, name, field, options) { * - parameters: The field definition (OPTIONS) request * - options: Other options provided at time of modal creation by the client */ -function renderModelData(name, model, data, paramaters, options) { +function renderModelData(name, model, data, parameters, options) { if (!data) { return '{% trans "Searching" %}...'; @@ -452,20 +474,41 @@ function renderModelData(name, model, data, paramaters, options) { var html = null; + var renderer = null; + + // Find a custom renderer switch (model) { case 'company': - html = `${data.name} - ${data.description}`; + renderer = renderCompany; + break; + case 'stockitem': + renderer = renderStockItem; + break; + case 'stocklocation': + renderer = renderStockLocation; + break; + case 'part': + renderer = renderPart; + break; + case 'partcategory': + renderer = renderPartCategory; + break; default: break; } + + if (renderer != null) { + html = renderer(name, data, parameters, options); + } if (html != null) { // Render HTML to an object var $state = $(html); return $state; } else { + console.log(`ERROR: Rendering not implemented for model '${model}'`); // Simple text rendering - return data.text; + return data.id; } } diff --git a/InvenTree/templates/js/model_renderers.js b/InvenTree/templates/js/model_renderers.js new file mode 100644 index 0000000000..924d85d35d --- /dev/null +++ b/InvenTree/templates/js/model_renderers.js @@ -0,0 +1,83 @@ +/* + * This file contains functions for rendering various InvenTree database models, + * in particular for displaying them in modal forms in a 'select2' context. + * + * Each renderer is provided with three arguments: + * + * - name: The 'name' of the model instance in the referring model + * - data: JSON data which represents the model instance. Returned via a GET request. + * - parameters: The field parameters provided via an OPTIONS request to the endpoint. + * - options: User options provided by the client + */ + + +// Renderer for "Company" model +function renderCompany(name, data, parameters, options) { + + var html = `${data.name} - ${data.description}`; + + return html; +} + + +// Renderer for "StockItem" model +function renderStockItem(name, data, parameters, options) { + + // TODO - Include part detail, location, quantity + // TODO - Include part image +} + + +// Renderer for "StockLocation" model +function renderStockLocation(name, data, parameters, options) { + + var html = `${data.name}`; + + if (data.description) { + html += ` - ${data.description}`; + } + + if (data.pathstring) { + html += `

${data.pathstring}

`; + } + + return html; +} + + +// Renderer for "Part" model +function renderPart(name, data, parameters, options) { + + var image = data.image; + + if (!image) { + image = `/static/img/blank_image.png`; + } + + var html = ``; + + html += ` ${data.full_name ?? data.name}`; + + if (data.description) { + html += ` - ${data.description}`; + } + + return html; +} + + +// Renderer for "PartCategory" model +function renderPartCategory(name, data, parameters, options) { + + var html = `${data.name}`; + + if (data.description) { + html += ` - ${data.description}`; + } + + if (data.pathstring) { + html += `

${data.pathstring}

`; + } + + return html; +} \ No newline at end of file