From b4e561279b5ba74b12f18f2c9a3bd1dc4abf39de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Mon, 8 Nov 2021 00:56:33 +0100 Subject: [PATCH] Add code for component autocompletion in add-stock-modal --- shimatta_kenkyusho/api/serializers.py | 10 ++- shimatta_kenkyusho/api/urls.py | 1 + shimatta_kenkyusho/api/views.py | 7 ++ .../static/js/add-stock-modal.js | 44 +++++++++++++ shimatta_kenkyusho/static/js/autocomplete.js | 64 ++++++++++++++----- .../static/js/kenyusho-api-v1.js | 4 ++ .../parts/modals/add-stock-modal.html | 25 ++++++++ .../parts/modals/delete-storage-modal.html | 2 +- .../parts/modals/new-substorage-modal.html | 2 +- .../templates/parts/stocks-detail.html | 11 ++++ 10 files changed, 152 insertions(+), 18 deletions(-) create mode 100644 shimatta_kenkyusho/static/js/add-stock-modal.js create mode 100644 shimatta_kenkyusho/templates/parts/modals/add-stock-modal.html diff --git a/shimatta_kenkyusho/api/serializers.py b/shimatta_kenkyusho/api/serializers.py index aa1ceef..ba8cc63 100644 --- a/shimatta_kenkyusho/api/serializers.py +++ b/shimatta_kenkyusho/api/serializers.py @@ -32,10 +32,13 @@ class StorageSerializer(serializers.HyperlinkedModelSerializer): class ComponentSerializer(serializers.HyperlinkedModelSerializer): package_data = PackageSerializerNoLink(source='package', read_only=True) + ro_manufacturer_name = serializers.ReadOnlyField(source='manufacturer.name') + ro_image = serializers.ReadOnlyField(source='get_resolved_image') class Meta: model = parts_models.Component - fields = ['url', 'id', 'name', 'package_data', 'package', 'pref_distri'] + fields = ['url', 'id', 'name', 'package_data', 'package', 'pref_distri', 'image', 'manufacturer', 'ro_manufacturer_name', 'ro_image'] + class StockSerializer(serializers.HyperlinkedModelSerializer): ro_package_name = serializers.ReadOnlyField(source='component.package.name') @@ -53,3 +56,8 @@ class DistributorSerializer(serializers.HyperlinkedModelSerializer): class StockIncrementDecrementSerializer(serializers.Serializer): increment = serializers.IntegerField() + +class ManufacturerSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = parts_models.Manufacturer + fields = '__all__' \ No newline at end of file diff --git a/shimatta_kenkyusho/api/urls.py b/shimatta_kenkyusho/api/urls.py index 1b58c29..cfa57da 100644 --- a/shimatta_kenkyusho/api/urls.py +++ b/shimatta_kenkyusho/api/urls.py @@ -11,6 +11,7 @@ router.register(r'parts/components', PartsComponentViewSet) router.register(r'parts/stocks', PartsStockViewSet) router.register(r'parts/packages', PartsPackageViewSet) router.register(r'parts/distributors', PartsDistributorviewSet) +router.register(r'parts/manufacturers', PartsManufacturerViewSet) urlpatterns = [ path('', include(router.urls)), diff --git a/shimatta_kenkyusho/api/views.py b/shimatta_kenkyusho/api/views.py index b0c54a5..3c85ea5 100644 --- a/shimatta_kenkyusho/api/views.py +++ b/shimatta_kenkyusho/api/views.py @@ -49,6 +49,13 @@ class PartsComponentViewSet(viewsets.ModelViewSet): filter_backends = [filters.SearchFilter] search_fields = ['name', 'package__name', 'manufacturer__name'] +class PartsManufacturerViewSet(viewsets.ModelViewSet): + queryset = parts_models.Manufacturer.objects.all() + serializer_class = ManufacturerSerializer + permission_classes = [permissions.DjangoModelPermissions] + filter_backends = [filters.SearchFilter] + search_fields = ['name'] + class PartsStockViewSet(viewsets.ModelViewSet): queryset = parts_models.Stock.objects.all() serializer_class = StockSerializer diff --git a/shimatta_kenkyusho/static/js/add-stock-modal.js b/shimatta_kenkyusho/static/js/add-stock-modal.js new file mode 100644 index 0000000..7d6e72f --- /dev/null +++ b/shimatta_kenkyusho/static/js/add-stock-modal.js @@ -0,0 +1,44 @@ +new AutocompleteCustomUi('add-stock-search', 'add-stock-search-ac-dropdown', +function(search, autocomplete_obj) { + api_search_component(search, function(results) { + components = results.results; + var test = []; + for (var i = 0; i < components.length; i++) { + var c = components[i]; + var node = document.createElement('div'); + node.setAttribute('class', 'd-flex align-items-center'); + var img_container = document.createElement('div'); + img_container.setAttribute('class', 'flex-shrink-0'); + var text_container = document.createElement('div'); + text_container.setAttribute('class', 'flex-grow-1 ms-1'); + var img = document.createElement('img'); + var img_path = fallback_img_path; + var style = "width:64px;max-height:64px;"; + if (c.ro_image != null) { + img_path = c.ro_image; + style = "max-width:64px;max-height:64px;"; + } + img.setAttribute('src', img_path); + img.setAttribute('style', style) + img_container.appendChild(img); + + var name_text = document.createTextNode(c.name); + var heading = document.createElement('h6'); + heading.appendChild(name_text); + text_container.appendChild(heading); + if (c.package_data != null) { + text_container.appendChild(document.createTextNode('in '+c.package_data.name)); + } + if (c.ro_manufacturer_name) { + text_container.appendChild(document.createTextNode(' by '+c.ro_manufacturer_name)); + } + + node.appendChild(img_container); + node.appendChild(text_container); + + test.push({'ui': node, 'data': c.url}) + } + autocomplete_obj.show_results(test, + function(data) {console.log(data);}); + }, function(){}); +}); \ No newline at end of file diff --git a/shimatta_kenkyusho/static/js/autocomplete.js b/shimatta_kenkyusho/static/js/autocomplete.js index 4b573c7..a1992a9 100644 --- a/shimatta_kenkyusho/static/js/autocomplete.js +++ b/shimatta_kenkyusho/static/js/autocomplete.js @@ -1,6 +1,6 @@ const autocomplete_query_delay_ms = 60; -class AutocompleteText { +class AutocompleteCustomUi { constructor(text_id, dropdown_id, query_function) { this.text_id = text_id; @@ -10,9 +10,56 @@ class AutocompleteText { document.getElementById(text_id).addEventListener("keyup", this.ac_delay(function(event) { this.query_callback(document.getElementById(this.text_id).value, this); }, autocomplete_query_delay_ms).bind(this)); - + } + /** + * + * @param {*} nodes_to_add A liust of dictionaries containing the shown objects and the data called when clicked. + * + * The list: {{'ui': , 'data': 'my_data'}, {}, ...} + * + */ + show_results(nodes_to_add, data_clicked_callback) { + var ul = document.getElementById(this.dropdown_id); + ul.innerHTML = ''; + this.select_data = {}; + + for (var i = 0; i < nodes_to_add.length; i++) { + var ui = nodes_to_add[i]['ui']; + var data = nodes_to_add[i]['data'] + var dropdown_node = document.createElement('li'); + dropdown_node.dataset.ac_clicked = data; + dropdown_node.addEventListener('click', + (e) => { + data_clicked_callback(e.currentTarget.dataset.ac_clicked); + var text_box = document.getElementById(this.text_id); + var dropdown = bootstrap.Dropdown.getOrCreateInstance(text_box); + dropdown.hide(); + }); + + dropdown_node.setAttribute('class', 'dropdown-item'); + dropdown_node.appendChild(ui); + ul.appendChild(dropdown_node); + } + + var dropdown = bootstrap.Dropdown.getOrCreateInstance(document.getElementById(this.text_id)); + dropdown.show(); + } + + ac_delay(callback, ms) { + var timer = 0; + return function() { + var context = this, args = arguments; + clearTimeout(timer); + timer = setTimeout(function() { + callback.apply(context, args); + }, ms || 0); + }; + } +} + +class AutocompleteText extends AutocompleteCustomUi { show_results(results) { var ul = document.getElementById(this.dropdown_id); ul.innerHTML = ''; @@ -41,17 +88,4 @@ class AutocompleteText { var dropdown = bootstrap.Dropdown.getOrCreateInstance(document.getElementById(this.text_id)); dropdown.show(); } - - ac_delay(callback, ms) { - var timer = 0; - return function() { - var context = this, args = arguments; - clearTimeout(timer); - timer = setTimeout(function() { - callback.apply(context, args); - }, ms || 0); - }; - } - - } \ No newline at end of file diff --git a/shimatta_kenkyusho/static/js/kenyusho-api-v1.js b/shimatta_kenkyusho/static/js/kenyusho-api-v1.js index cf4c4dc..5011d83 100644 --- a/shimatta_kenkyusho/static/js/kenyusho-api-v1.js +++ b/shimatta_kenkyusho/static/js/kenyusho-api-v1.js @@ -27,4 +27,8 @@ function api_ajax_request_without_send(method, url, onSuccess, onFail) { function api_search_user(search, onSuccess, onFail) { return api_ajax_request_without_send('GET', api_urls_v1['user-list']+`?search=${encodeURIComponent(search)}`, function(method, url, json) {onSuccess(json);}, onFail); +} + +function api_search_component(search, onSuccess, onFail) { + return api_ajax_request_without_send('GET', api_urls_v1['component-list']+`?search=${encodeURIComponent(search)}`, function(method, url, json) {onSuccess(json);}, onFail); } \ No newline at end of file diff --git a/shimatta_kenkyusho/templates/parts/modals/add-stock-modal.html b/shimatta_kenkyusho/templates/parts/modals/add-stock-modal.html new file mode 100644 index 0000000..df88199 --- /dev/null +++ b/shimatta_kenkyusho/templates/parts/modals/add-stock-modal.html @@ -0,0 +1,25 @@ +{% comment %} +Input context: +- form: A stock-create-form +{% endcomment %} + \ No newline at end of file diff --git a/shimatta_kenkyusho/templates/parts/modals/delete-storage-modal.html b/shimatta_kenkyusho/templates/parts/modals/delete-storage-modal.html index 298cfcb..d9af1b3 100644 --- a/shimatta_kenkyusho/templates/parts/modals/delete-storage-modal.html +++ b/shimatta_kenkyusho/templates/parts/modals/delete-storage-modal.html @@ -2,7 +2,7 @@