mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-01 11:19:09 +00:00
Chore: Miscellaneous UI review changes
- Added bg (variable) to pages, card space separation visible - Removed `show brand line` in settings, shown by default - Re-arranged settings by importance - View toggling primary colour is grey - Only populate recent searches on successful search - Hit only one server side api, once while searching - List view primary button float right - Discounts takes upper limit eg. 10% and below - Navbar icons only wiggle on qty increase in cart/wishlist - Pay button in SO portal - Remove bottom white space below item full page image, min-height fits to content - List view vertical space between heading and item code - Empty offer subtitle handled
This commit is contained in:
@@ -6,6 +6,11 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"products_per_page",
|
||||
"filter_categories_section",
|
||||
"enable_field_filters",
|
||||
"filter_fields",
|
||||
"enable_attribute_filters",
|
||||
"filter_attributes",
|
||||
"display_settings_section",
|
||||
"hide_variants",
|
||||
"enable_variants",
|
||||
@@ -18,15 +23,6 @@
|
||||
"show_apply_coupon_code_in_website",
|
||||
"show_contact_us_button",
|
||||
"show_attachments",
|
||||
"guest_display_settings_section",
|
||||
"hide_price_for_guest",
|
||||
"redirect_on_action",
|
||||
"add_ons_section",
|
||||
"enable_wishlist",
|
||||
"column_break_22",
|
||||
"enable_reviews",
|
||||
"column_break_23",
|
||||
"enable_recommendations",
|
||||
"section_break_18",
|
||||
"company",
|
||||
"price_list",
|
||||
@@ -42,19 +38,22 @@
|
||||
"save_quotations_as_draft",
|
||||
"payment_gateway_account",
|
||||
"payment_success_url",
|
||||
"filter_categories_section",
|
||||
"enable_field_filters",
|
||||
"filter_fields",
|
||||
"enable_attribute_filters",
|
||||
"filter_attributes",
|
||||
"shop_by_category_section",
|
||||
"slideshow",
|
||||
"add_ons_section",
|
||||
"enable_wishlist",
|
||||
"column_break_22",
|
||||
"enable_reviews",
|
||||
"column_break_23",
|
||||
"enable_recommendations",
|
||||
"item_search_settings_section",
|
||||
"redisearch_warning",
|
||||
"search_index_fields",
|
||||
"show_categories_in_search_autocomplete",
|
||||
"show_brand_line",
|
||||
"is_redisearch_loaded"
|
||||
"is_redisearch_loaded",
|
||||
"shop_by_category_section",
|
||||
"slideshow",
|
||||
"guest_display_settings_section",
|
||||
"hide_price_for_guest",
|
||||
"redirect_on_action"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -309,14 +308,6 @@
|
||||
"label": "Show Categories in Search Autocomplete",
|
||||
"read_only_depends_on": "eval:!doc.is_redisearch_loaded"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "e.g. \"iPhone 12 by Apple\"",
|
||||
"fieldname": "show_brand_line",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Brand Line",
|
||||
"read_only_depends_on": "eval:!doc.is_redisearch_loaded"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_redisearch_loaded",
|
||||
@@ -379,7 +370,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-24 21:10:45.669526",
|
||||
"modified": "2021-08-31 12:23:06.187619",
|
||||
"modified_by": "Administrator",
|
||||
"module": "E-commerce",
|
||||
"name": "E Commerce Settings",
|
||||
|
||||
@@ -21,7 +21,6 @@ class ECommerceSettings(Document):
|
||||
self.validate_field_filters()
|
||||
self.validate_attribute_filters()
|
||||
self.validate_checkout()
|
||||
self.validate_brand_check()
|
||||
self.validate_search_index_fields()
|
||||
|
||||
if self.enabled:
|
||||
@@ -71,10 +70,6 @@ class ECommerceSettings(Document):
|
||||
|
||||
self.search_index_fields = ','.join(fields)
|
||||
|
||||
def validate_brand_check(self):
|
||||
if self.show_brand_line and not ("brand" in self.search_index_fields):
|
||||
self.search_index_fields += ",brand"
|
||||
|
||||
def validate_price_list_exchange_rate(self):
|
||||
"Check if exchange rate exists for Price List currency (to Company's currency)."
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
|
||||
@@ -187,12 +187,17 @@ class WebsiteItem(WebsiteGenerator):
|
||||
|
||||
def get_context(self, context):
|
||||
context.show_search = True
|
||||
context.search_link = '/search'
|
||||
context.search_link = "/search"
|
||||
context.body_class = "product-page"
|
||||
|
||||
context.parents = get_parent_item_groups(self.item_group, from_item=True) # breadcumbs
|
||||
self.attributes = frappe.get_all("Item Variant Attribute",
|
||||
fields=["attribute", "attribute_value"],
|
||||
filters={"parent": self.item_code})
|
||||
|
||||
if self.slideshow:
|
||||
context.update(get_slideshow(self))
|
||||
|
||||
self.set_variant_context(context)
|
||||
self.set_attribute_context(context)
|
||||
self.set_disabled_attributes(context)
|
||||
@@ -254,11 +259,9 @@ class WebsiteItem(WebsiteGenerator):
|
||||
|
||||
context[fieldname] = value
|
||||
|
||||
if self.slideshow:
|
||||
if context.variant and context.variant.slideshow:
|
||||
context.update(get_slideshow(context.variant))
|
||||
else:
|
||||
context.update(get_slideshow(self))
|
||||
if self.slideshow and context.variant and context.variant.slideshow:
|
||||
context.update(get_slideshow(context.variant))
|
||||
|
||||
|
||||
def set_attribute_context(self, context):
|
||||
if not self.has_variants:
|
||||
|
||||
@@ -135,11 +135,15 @@ class ProductFiltersBuilder:
|
||||
min_discount, max_discount = discounts[0], discounts[1]
|
||||
# [25, 60] rounded min max
|
||||
min_range_absolute, max_range_absolute = floor(min_discount), floor(max_discount)
|
||||
|
||||
min_range = int(min_discount - (min_range_absolute % 10)) # 20
|
||||
max_range = int(max_discount - (max_range_absolute % 10)) # 60
|
||||
|
||||
min_range = (min_range + 10) if min_range != min_range_absolute else min_range # 30 (upper limit of 25.89 in range of 10)
|
||||
max_range = (max_range + 10) if max_range != max_range_absolute else max_range # 60
|
||||
|
||||
for discount in range(min_range, (max_range + 1), 10):
|
||||
label = f"{discount}% and above"
|
||||
label = f"{discount}% and below"
|
||||
discount_filters.append([discount, label])
|
||||
|
||||
return discount_filters
|
||||
@@ -226,7 +226,7 @@ class TestProductDataEngine(unittest.TestCase):
|
||||
|
||||
self.assertEqual(len(discount_filters[0]), 2)
|
||||
self.assertEqual(discount_filters[0][0], 10)
|
||||
self.assertEqual(discount_filters[0][1], "10% and above")
|
||||
self.assertEqual(discount_filters[0][1], "10% and below")
|
||||
|
||||
def test_product_list_with_discount_filters(self):
|
||||
"Test if discount filters are applied correctly."
|
||||
@@ -261,7 +261,7 @@ class TestProductDataEngine(unittest.TestCase):
|
||||
)
|
||||
items = result.get("items")
|
||||
|
||||
# check if only product with 10% and above discount are fetched in the right order
|
||||
# check if only product with 10% and below discount are fetched in the right order
|
||||
self.assertEqual(len(items), 2)
|
||||
self.assertEqual(items[0].get("item_code"), "Test 13I Laptop")
|
||||
self.assertEqual(items[1].get("item_code"), "Test 12I Laptop")
|
||||
|
||||
@@ -143,7 +143,7 @@ erpnext.ProductGrid = class {
|
||||
|
||||
get_stock_availability(item, settings) {
|
||||
if (settings.show_stock_availability && !item.has_variants && !item.in_stock) {
|
||||
return `<span class="out-of-stock">${ __("Out of stock") }</span>`;
|
||||
return `<span class="out-of-stock mb-2 mt-1">${ __("Out of stock") }</span>`;
|
||||
}
|
||||
return ``;
|
||||
}
|
||||
@@ -161,7 +161,7 @@ erpnext.ProductGrid = class {
|
||||
return `
|
||||
<div id="${ item.name }" class="btn
|
||||
btn-sm btn-primary btn-add-to-cart-list
|
||||
w-100 mt-4 ${ item.in_cart ? 'hidden' : '' }"
|
||||
w-100 mt-2 ${ item.in_cart ? 'hidden' : '' }"
|
||||
data-item-code="${ item.item_code }">
|
||||
<span class="mr-2">
|
||||
<svg class="icon icon-md">
|
||||
|
||||
@@ -24,7 +24,7 @@ erpnext.ProductList = class {
|
||||
let title = item.web_item_name || item.item_name || item.item_code || "";
|
||||
title = title.length > 200 ? title.substr(0, 200) + "..." : title;
|
||||
|
||||
html += `<div class='row list-row w-100'>`;
|
||||
html += `<div class='row list-row w-100 mb-4'>`;
|
||||
html += me.get_image_html(item, title, me.settings);
|
||||
html += me.get_row_body_html(item, title, me.settings);
|
||||
html += `</div>`;
|
||||
@@ -86,7 +86,7 @@ erpnext.ProductList = class {
|
||||
`;
|
||||
|
||||
if (settings.enabled) {
|
||||
title_html += `<div class="col-4" style="display:flex">`;
|
||||
title_html += `<div class="col-4 cart-action-container ${item.in_cart ? 'd-flex' : ''}">`;
|
||||
title_html += this.get_primary_button(item, settings);
|
||||
title_html += `</div>`;
|
||||
}
|
||||
@@ -151,9 +151,7 @@ erpnext.ProductList = class {
|
||||
if (item.has_variants) {
|
||||
return `
|
||||
<a href="/${ item.route || '#' }">
|
||||
<div class="btn btn-sm btn-explore-variants btn"
|
||||
style="margin-bottom: 0; max-height: 30px; float: right;
|
||||
padding: 0.25rem 1rem; min-width: 135px;">
|
||||
<div class="btn btn-sm btn-explore-variants btn mb-0 mt-0">
|
||||
${ __('Explore') }
|
||||
</div>
|
||||
</a>
|
||||
@@ -161,10 +159,10 @@ erpnext.ProductList = class {
|
||||
} else if (settings.enabled && (settings.allow_items_not_in_stock || item.in_stock)) {
|
||||
return `
|
||||
<div id="${ item.name }" class="btn
|
||||
btn-sm btn-primary btn-add-to-cart-list
|
||||
btn-sm btn-primary btn-add-to-cart-list mb-0
|
||||
${ item.in_cart ? 'hidden' : '' }"
|
||||
data-item-code="${ item.item_code }"
|
||||
style="margin-bottom: 0; margin-top: 0px !important; max-height: 30px; float: right;
|
||||
style="margin-top: 0px !important; max-height: 30px; float: right;
|
||||
padding: 0.25rem 1rem; min-width: 135px;">
|
||||
<span class="mr-2">
|
||||
<svg class="icon icon-md">
|
||||
@@ -174,14 +172,14 @@ erpnext.ProductList = class {
|
||||
${ __('Add to Cart') }
|
||||
</div>
|
||||
|
||||
<div class="cart-indicator ${item.in_cart ? '' : 'hidden'}" style="position: unset;">
|
||||
<div class="cart-indicator list-indicator ${item.in_cart ? '' : 'hidden'}">
|
||||
1
|
||||
</div>
|
||||
|
||||
<a href="/cart">
|
||||
<div id="${ item.name }" class="btn
|
||||
btn-sm btn-primary btn-add-to-cart-list
|
||||
ml-4 go-to-cart
|
||||
ml-4 go-to-cart mb-0 mt-0
|
||||
${ item.in_cart ? '' : 'hidden' }"
|
||||
data-item-code="${ item.item_code }"
|
||||
style="padding: 0.25rem 1rem; min-width: 135px;">
|
||||
|
||||
@@ -38,39 +38,37 @@ erpnext.ProductSearch = class {
|
||||
let query = e.target.value;
|
||||
|
||||
if (query.length == 0) {
|
||||
me.populateResults([]);
|
||||
me.populateCategoriesList([]);
|
||||
me.populateResults(null);
|
||||
me.populateCategoriesList(null);
|
||||
}
|
||||
|
||||
if (query.length < 3 || !query.length) return;
|
||||
|
||||
// Populate recent search chips
|
||||
me.setRecentSearches(query);
|
||||
|
||||
// Fetch and populate product results
|
||||
frappe.call({
|
||||
method: "erpnext.templates.pages.product_search.search",
|
||||
args: {
|
||||
query: query
|
||||
},
|
||||
callback: (data) => {
|
||||
me.populateResults(data);
|
||||
let product_results = null, category_results = null;
|
||||
|
||||
// Populate product results
|
||||
product_results = data.message ? data.message.product_results : null;
|
||||
me.populateResults(product_results);
|
||||
|
||||
// Populate categories
|
||||
if (me.category_container) {
|
||||
category_results = data.message ? data.message.category_results : null;
|
||||
me.populateCategoriesList(category_results);
|
||||
}
|
||||
|
||||
// Populate recent search chips only on successful queries
|
||||
if (!$.isEmptyObject(product_results) || !$.isEmptyObject(category_results)) {
|
||||
me.setRecentSearches(query);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Populate categories
|
||||
if (me.category_container) {
|
||||
frappe.call({
|
||||
method: "erpnext.templates.pages.product_search.get_category_suggestions",
|
||||
args: {
|
||||
query: query
|
||||
},
|
||||
callback: (data) => {
|
||||
me.populateCategoriesList(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.search_dropdown.removeClass("hidden");
|
||||
});
|
||||
}
|
||||
@@ -186,17 +184,16 @@ erpnext.ProductSearch = class {
|
||||
this.attachEventListenersToChips();
|
||||
}
|
||||
|
||||
populateResults(data) {
|
||||
if (data.length === 0 || data.message.results.length === 0) {
|
||||
populateResults(product_results) {
|
||||
if (!product_results || product_results.length === 0) {
|
||||
let empty_html = ``;
|
||||
this.products_container.html(empty_html);
|
||||
return;
|
||||
}
|
||||
|
||||
let html = "";
|
||||
let search_results = data.message.results;
|
||||
|
||||
search_results.forEach((res) => {
|
||||
product_results.forEach((res) => {
|
||||
let thumbnail = res.thumbnail || '/assets/erpnext/images/ui-states/cart-empty-state.png';
|
||||
html += `
|
||||
<div class="dropdown-item" style="display: flex;">
|
||||
@@ -212,8 +209,8 @@ erpnext.ProductSearch = class {
|
||||
this.products_container.html(html);
|
||||
}
|
||||
|
||||
populateCategoriesList(data) {
|
||||
if (data.length === 0 || data.message.results.length === 0) {
|
||||
populateCategoriesList(category_results) {
|
||||
if (!category_results || category_results.length === 0) {
|
||||
let empty_html = `
|
||||
<div class="category-container mt-2">
|
||||
<div class="category-chips">
|
||||
@@ -229,8 +226,8 @@ erpnext.ProductSearch = class {
|
||||
<b>${ __("Categories") }</b>
|
||||
</div>
|
||||
`;
|
||||
let search_results = data.message.results;
|
||||
search_results.forEach((category) => {
|
||||
|
||||
category_results.forEach((category) => {
|
||||
html += `
|
||||
<a href="/${category.route}" class="btn btn-sm category-chip mr-2 mb-2"
|
||||
style="font-size: 13px" role="button">
|
||||
|
||||
@@ -129,7 +129,7 @@ erpnext.ProductView = class {
|
||||
|
||||
prepare_product_area_wrapper(view) {
|
||||
let left_margin = view == "list" ? "ml-2" : "";
|
||||
let top_margin = view == "list" ? "mt-8" : "mt-4";
|
||||
let top_margin = view == "list" ? "mt-6" : "mt-minus-1";
|
||||
return this.products_section.append(`
|
||||
<br>
|
||||
<div id="products-${view}-area" class="row products-list ${ top_margin } ${ left_margin }"></div>
|
||||
@@ -191,7 +191,7 @@ erpnext.ProductView = class {
|
||||
|
||||
prepare_search() {
|
||||
$(".toolbar").append(`
|
||||
<div class="input-group col-6 p-0">
|
||||
<div class="input-group col-8 p-0">
|
||||
<div class="dropdown w-100" id="dropdownMenuSearch">
|
||||
<input type="search" name="query" id="search-box" class="form-control font-md"
|
||||
placeholder="Search for Products"
|
||||
@@ -213,7 +213,7 @@ erpnext.ProductView = class {
|
||||
}
|
||||
|
||||
render_view_toggler() {
|
||||
$(".toolbar").append(`<div class="toggle-container col-6 p-0"></div>`);
|
||||
$(".toolbar").append(`<div class="toggle-container col-4 p-0"></div>`);
|
||||
|
||||
["btn-list-view", "btn-grid-view"].forEach(view => {
|
||||
let icon = view === "btn-list-view" ? "list" : "image-view";
|
||||
|
||||
Reference in New Issue
Block a user