From f3421554add9bfcae4d5eb8150e89c4b0f994395 Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Mon, 26 Apr 2021 11:15:20 +0530 Subject: [PATCH] feat: Add Category autocomplete with config in settings --- .../e_commerce_settings.json | 11 ++++- erpnext/e_commerce/website_item_indexing.py | 24 +++++++---- erpnext/templates/pages/product_search.py | 17 +++++++- erpnext/www/all-products/search.html | 16 +++++++- erpnext/www/all-products/search.js | 41 +++++++++++++++---- 5 files changed, 90 insertions(+), 19 deletions(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json index 76bf283e04..bfdfb0d17a 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json @@ -46,7 +46,8 @@ "shop_by_category_section", "slideshow", "item_search_settings_section", - "search_index_fields" + "search_index_fields", + "show_categories_in_search_autocomplete" ], "fields": [ { @@ -313,12 +314,18 @@ "fieldname": "item_search_settings_section", "fieldtype": "Section Break", "label": "Item Search Settings" + }, + { + "default": "1", + "fieldname": "show_categories_in_search_autocomplete", + "fieldtype": "Check", + "label": "Show Categories in Search Autocomplete" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-04-23 13:30:50.286088", + "modified": "2021-04-26 09:50:40.581354", "modified_by": "Administrator", "module": "E-commerce", "name": "E Commerce Settings", diff --git a/erpnext/e_commerce/website_item_indexing.py b/erpnext/e_commerce/website_item_indexing.py index 0e48a2d7e6..faf5760a6a 100644 --- a/erpnext/e_commerce/website_item_indexing.py +++ b/erpnext/e_commerce/website_item_indexing.py @@ -17,6 +17,7 @@ from redisearch import ( WEBSITE_ITEM_INDEX = 'website_items_index' WEBSITE_ITEM_KEY_PREFIX = 'website_item:' WEBSITE_ITEM_NAME_AUTOCOMPLETE = 'website_items_name_dict' +WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE = 'website_items_category_dict' ALLOWED_INDEXABLE_FIELDS_SET = { 'item_code', @@ -108,27 +109,36 @@ def delete_item_from_index(website_item_doc): return True def define_autocomplete_dictionary(): - print("Defining ac dict...") - # AC for name - # TODO: AC for category + """Creates an autocomplete search dictionary for `name`. + Also creats autocomplete dictionary for `categories` if + checked in E Commerce Settings""" r = redis.Redis("localhost", 13000) - ac = AutoCompleter(WEBSITE_ITEM_NAME_AUTOCOMPLETE, port=13000) + name_ac = AutoCompleter(WEBSITE_ITEM_NAME_AUTOCOMPLETE, port=13000) + cat_ac = AutoCompleter(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE, port=13000) + ac_categories = frappe.db.get_single_value( + 'E Commerce Settings', + 'show_categories_in_search_autocomplete' + ) + + # Delete both autocomplete dicts try: r.delete(WEBSITE_ITEM_NAME_AUTOCOMPLETE) + r.delete(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE) except: return False items = frappe.get_all( 'Website Item', - fields=['web_item_name'], + fields=['web_item_name', 'item_group'], filters={"published": True} ) for item in items: - print("adding suggestion: " + item.web_item_name) - ac.add_suggestions(Suggestion(item.web_item_name)) + name_ac.add_suggestions(Suggestion(item.web_item_name)) + if ac_categories and item.item_group: + cat_ac.add_suggestions(Suggestion(item.item_group)) return True diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py index d935a7a744..755dfecd71 100644 --- a/erpnext/templates/pages/product_search.py +++ b/erpnext/templates/pages/product_search.py @@ -9,7 +9,11 @@ from erpnext.e_commerce.shopping_cart.product_info import set_product_info_for_w # For SEARCH ------- from redisearch import AutoCompleter, Client, Query -from erpnext.e_commerce.website_item_indexing import WEBSITE_ITEM_INDEX, WEBSITE_ITEM_NAME_AUTOCOMPLETE +from erpnext.e_commerce.website_item_indexing import ( + WEBSITE_ITEM_INDEX, + WEBSITE_ITEM_NAME_AUTOCOMPLETE, + WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE +) # ----------------- no_cache = 1 @@ -83,3 +87,14 @@ def search(query): def convert_to_dict(redis_search_doc): return redis_search_doc.__dict__ + +@frappe.whitelist(allow_guest=True) +def get_category_suggestions(query): + if not query: + # TODO: return top/recent searches + return [] + + ac = AutoCompleter(WEBSITE_ITEM_CATEGORY_AUTOCOMPLETE, port=13000) + suggestions = ac.get_suggestions(query, num=10) + + return [s.string for s in suggestions] \ No newline at end of file diff --git a/erpnext/www/all-products/search.html b/erpnext/www/all-products/search.html index ef74536286..1c58e65cc7 100644 --- a/erpnext/www/all-products/search.html +++ b/erpnext/www/all-products/search.html @@ -12,7 +12,19 @@ {% block page_content %} - + +

Products

+ + +{% set show_categories = frappe.db.get_single_value('E Commerce Settings', 'show_categories_in_search_autocomplete') %} + +{% if show_categories %} +
+

Categories

+ +
+{% endif %} + {% endblock %} \ No newline at end of file diff --git a/erpnext/www/all-products/search.js b/erpnext/www/all-products/search.js index 2080f35c62..aa47a4d7e2 100644 --- a/erpnext/www/all-products/search.js +++ b/erpnext/www/all-products/search.js @@ -1,10 +1,10 @@ -console.log("search.js reloaded"); +console.log("search.js loaded"); -const search_box = document.getElementById("search-box"); +const searchBox = document.getElementById("search-box"); const results = document.getElementById("results"); +const categoryList = document.getElementById("category-suggestions"); function populateResults(data) { - console.log(data); html = "" for (let res of data.message) { html += `
  • @@ -12,11 +12,24 @@ function populateResults(data) { ${res.web_item_name}
  • ` } - console.log(html); results.innerHTML = html; } -search_box.addEventListener("input", (e) => { +function populateCategoriesList(data) { + if (data.length === 0) { + categoryList.innerText = "No matches"; + return; + } + + html = "" + for (let category of data.message) { + html += `
  • ${category}
  • ` + } + + categoryList.innerHTML = html; +} + +searchBox.addEventListener("input", (e) => { frappe.call({ method: "erpnext.templates.pages.product_search.search", args: { @@ -25,5 +38,19 @@ search_box.addEventListener("input", (e) => { callback: (data) => { populateResults(data); } - }) -}); \ No newline at end of file + }); + + // If there is a suggestion list node + if (categoryList) { + frappe.call({ + method: "erpnext.templates.pages.product_search.get_category_suggestions", + args: { + query: e.target.value + }, + callback: (data) => { + populateCategoriesList(data); + } + }); + } +}); +