feat: Product View toggling
- Added fully functional list and grid view toggling - Added ProductGrid and ProductList controllers - Moved html snippets, rendered via JS now - Item Group page also rendered via common controller - Paging section rendered via JS - Minor style changes
This commit is contained in:
parent
48b3ce82b9
commit
897c08c8e7
148
erpnext/e_commerce/product_grid.js
Normal file
148
erpnext/e_commerce/product_grid.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
erpnext.ProductGrid = class {
|
||||||
|
/* Options:
|
||||||
|
- items: Items
|
||||||
|
- settings: E Commerce Settings
|
||||||
|
- products_section: Products Wrapper
|
||||||
|
- preference: If preference is not grid view, render but hide
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
Object.assign(this, options);
|
||||||
|
|
||||||
|
if (this.preference !== "Grid View") {
|
||||||
|
this.products_section.addClass("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.make();
|
||||||
|
}
|
||||||
|
|
||||||
|
make() {
|
||||||
|
let me = this;
|
||||||
|
let html = ``;
|
||||||
|
|
||||||
|
this.items.forEach(item => {
|
||||||
|
let title = item.web_item_name || item.item_name || item.item_code || "";
|
||||||
|
title = title.length > 50 ? title.substr(0, 50) + "..." : title;
|
||||||
|
|
||||||
|
html += `<div class="col-sm-4 item-card"><div class="card text-left">`;
|
||||||
|
html += me.get_image_html(item, title);
|
||||||
|
html += me.get_card_body_html(item, title, me.settings);
|
||||||
|
html += `</div></div>`;
|
||||||
|
})
|
||||||
|
|
||||||
|
let $product_wrapper = this.products_section;
|
||||||
|
$product_wrapper.append(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_image_html(item, title) {
|
||||||
|
let image = item.website_image || item.image;
|
||||||
|
|
||||||
|
if(image) {
|
||||||
|
return `
|
||||||
|
<div class="card-img-container">
|
||||||
|
<a href="/${ item.route || '#' }" style="text-decoration: none;">
|
||||||
|
<img class="card-img" src="${ image }" alt="${ title }">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
return `
|
||||||
|
<a href="/${ item.route || '#' }" style="text-decoration: none;">
|
||||||
|
<div class="card-img-top no-image">
|
||||||
|
${ frappe.get_abbr(title) }
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_card_body_html(item, title, settings) {
|
||||||
|
let body_html = `
|
||||||
|
<div class="card-body text-left" style="width:100%">
|
||||||
|
<div style="margin-top: 16px; display: flex;">
|
||||||
|
`;
|
||||||
|
body_html += this.get_title_with_indicator(item, title);
|
||||||
|
|
||||||
|
if (!item.has_variants && settings.enable_wishlist) {
|
||||||
|
body_html += this.get_wishlist_icon(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
body_html += `</div>`; // close div on line 50
|
||||||
|
body_html += `<div class="product-category">${ item.item_group || '' }</div>`;
|
||||||
|
|
||||||
|
if (item.formatted_price) {
|
||||||
|
body_html += this.get_price_html(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
body_html += this.get_primary_button(item, settings);
|
||||||
|
body_html += `</div>`; // close div on line 49
|
||||||
|
|
||||||
|
return body_html;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_title_with_indicator(item, title, settings) {
|
||||||
|
let title_html = `
|
||||||
|
<a href="/${ item.route || '#' }">
|
||||||
|
<div class="product-title">
|
||||||
|
${ title || '' }
|
||||||
|
`;
|
||||||
|
if (item.in_stock) {
|
||||||
|
title_html += `<span class="indicator ${ item.in_stock } card-indicator"></span>`;
|
||||||
|
}
|
||||||
|
title_html += `</div></a>`;
|
||||||
|
return title_html
|
||||||
|
}
|
||||||
|
|
||||||
|
get_wishlist_icon(item) {
|
||||||
|
let icon_class = item.wished ? "wished" : "not-wished";
|
||||||
|
return `
|
||||||
|
<div class="like-action"
|
||||||
|
data-item-code="${ item.item_code }"
|
||||||
|
data-price="${ item.price || '' }"
|
||||||
|
data-formatted-price="${ item.formatted_price || '' }">
|
||||||
|
<svg class="icon sm">
|
||||||
|
<use class="${ icon_class } wish-icon" href="#icon-heart"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_price_html(item) {
|
||||||
|
let price_html = `
|
||||||
|
<div class="product-price">
|
||||||
|
${ item.formatted_price || '' }
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (item.formatted_mrp) {
|
||||||
|
price_html += `
|
||||||
|
<small class="ml-1 text-muted">
|
||||||
|
<s>${ item.formatted_mrp }</s>
|
||||||
|
</small>
|
||||||
|
<small class="ml-1" style="color: #F47A7A; font-weight: 500;">
|
||||||
|
${ item.discount } OFF
|
||||||
|
</small>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
price_html += `</div>`;
|
||||||
|
return price_html;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_primary_button(item, settings) {
|
||||||
|
if (item.has_variants) {
|
||||||
|
return `
|
||||||
|
<a href="/${ item.route || '#' }">
|
||||||
|
<div class="btn btn-sm btn-explore-variants w-100 mt-4">
|
||||||
|
${ __('Explore') }
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
} else if (settings.enabled && (settings.allow_items_not_in_stock || item.in_stock !== "red")) {
|
||||||
|
return `
|
||||||
|
<div id="${ item.name }" class="btn
|
||||||
|
btn-sm btn-add-to-cart-list not-added w-100 mt-4"
|
||||||
|
data-item-code="${ item.item_code }">
|
||||||
|
${ __('Add to Cart') }
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
158
erpnext/e_commerce/product_list.js
Normal file
158
erpnext/e_commerce/product_list.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
erpnext.ProductList = class {
|
||||||
|
/* Options:
|
||||||
|
- items: Items
|
||||||
|
- settings: E Commerce Settings
|
||||||
|
- products_section: Products Wrapper
|
||||||
|
- preference: If preference is not list view, render but hide
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
Object.assign(this, options);
|
||||||
|
|
||||||
|
if (this.preference !== "List View") {
|
||||||
|
this.products_section.addClass("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.make();
|
||||||
|
}
|
||||||
|
|
||||||
|
make() {
|
||||||
|
let me = this;
|
||||||
|
let html = `<br><br>`;
|
||||||
|
|
||||||
|
this.items.forEach(item => {
|
||||||
|
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 mt-6 w-100' style="border-bottom: 1px solid var(--table-border-color); padding-bottom: 1rem;">`;
|
||||||
|
html += me.get_image_html(item, title);
|
||||||
|
html += me.get_row_body_html(item, title, me.settings);
|
||||||
|
html += `</div>`;
|
||||||
|
})
|
||||||
|
|
||||||
|
let $product_wrapper = this.products_section;
|
||||||
|
$product_wrapper.append(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_image_html(item, title) {
|
||||||
|
let image = item.website_image || item.image;
|
||||||
|
|
||||||
|
if(image) {
|
||||||
|
return `
|
||||||
|
<div class="col-2 border text-center rounded product-image" style="overflow: hidden; max-height: 200px;">
|
||||||
|
<a class="product-link product-list-link" href="/${ item.route || '#' }">
|
||||||
|
<img itemprop="image" class="website-image h-100 w-100" alt="${ title }"
|
||||||
|
src="${ image }">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
return `
|
||||||
|
<a href="/${ item.route || '#' }" style="text-decoration: none;">
|
||||||
|
<div class="card-img-top no-image">
|
||||||
|
${ frappe.get_abbr(title) }
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_row_body_html(item, title, settings) {
|
||||||
|
let body_html = `<div class='col-9 text-left'>`;
|
||||||
|
body_html += this.get_title_html(item, title, settings);
|
||||||
|
body_html += this.get_item_details(item, settings);
|
||||||
|
body_html += `</div>`;
|
||||||
|
return body_html;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_title_html(item, title, settings) {
|
||||||
|
let title_html = `<div style="display: flex; margin-left: -15px;">`;
|
||||||
|
title_html += `
|
||||||
|
<div class="col-8" style="margin-right: -15px;">
|
||||||
|
<a class="" href="/${ item.route || '#' }"
|
||||||
|
style="color: var(--gray-800); font-weight: 500;">
|
||||||
|
${ title }
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (item.in_stock) {
|
||||||
|
title_html += `<span class="indicator ${ item.in_stock } card-indicator"></span>`;
|
||||||
|
}
|
||||||
|
title_html += `</div>`;
|
||||||
|
|
||||||
|
if (settings.enable_wishlist || settings.enabled) {
|
||||||
|
title_html += `<div class="col-4" style="display:flex">`;
|
||||||
|
if (!item.has_variants && settings.enable_wishlist) {
|
||||||
|
title_html += this.get_wishlist_icon(item);
|
||||||
|
}
|
||||||
|
title_html += this.get_primary_button(item, settings);
|
||||||
|
title_html += `</div>`;
|
||||||
|
}
|
||||||
|
title_html += `</div>`;
|
||||||
|
|
||||||
|
return title_html;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_item_details(item, settings) {
|
||||||
|
let details = `
|
||||||
|
<p class="product-code">
|
||||||
|
Item Code : ${ item.item_code }
|
||||||
|
</p>
|
||||||
|
<div class="text-muted mt-2">
|
||||||
|
${ item.description || '' }
|
||||||
|
</div>
|
||||||
|
<div class="product-price">
|
||||||
|
${ item.formatted_price || '' }
|
||||||
|
`;
|
||||||
|
|
||||||
|
if(item.formatted_mrp) {
|
||||||
|
details += `
|
||||||
|
<small class="ml-1 text-muted">
|
||||||
|
<s>${ item.formatted_mrp }</s>
|
||||||
|
</small>
|
||||||
|
<small class="ml-1" style="color: #F47A7A; font-weight: 500;">
|
||||||
|
${ item.discount } OFF
|
||||||
|
</small>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
details += `</div>`;
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_wishlist_icon(item) {
|
||||||
|
let icon_class = item.wished ? "wished" : "not-wished";
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="like-action mr-4"
|
||||||
|
data-item-code="${ item.item_code }"
|
||||||
|
data-price="${ item.price || '' }"
|
||||||
|
data-formatted-price="${ item.formatted_price || '' }">
|
||||||
|
<svg class="icon sm">
|
||||||
|
<use class="${ icon_class } wish-icon" href="#icon-heart"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_primary_button(item, settings) {
|
||||||
|
if (item.has_variants) {
|
||||||
|
return `
|
||||||
|
<a href="/${ item.route || '#' }">
|
||||||
|
<div class="btn btn-sm btn-explore-variants" style="margin-bottom: 0; margin-top: 4px; max-height: 30px;">
|
||||||
|
${ __('Explore') }
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
} else if (settings.enabled && (settings.allow_items_not_in_stock || item.in_stock !== "red")) {
|
||||||
|
return `
|
||||||
|
<div id="${ item.name }" class="btn
|
||||||
|
btn-sm btn-add-to-cart-list not-added"
|
||||||
|
data-item-code="${ item.item_code }"
|
||||||
|
style="margin-bottom: 0; margin-top: 0px; max-height: 30px;">
|
||||||
|
${ __('Add to Cart') }
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,80 +1,56 @@
|
|||||||
erpnext.ProductView = class {
|
erpnext.ProductView = class {
|
||||||
/* Options: View Type */
|
/* Options:
|
||||||
|
- View Type
|
||||||
|
- Products Section Wrapper,
|
||||||
|
- Item Group: If its an Item Group page
|
||||||
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
Object.assign(this, options);
|
Object.assign(this, options);
|
||||||
this.render_view_toggler();
|
this.preference = "List View";
|
||||||
|
|
||||||
|
this.products_section.empty();
|
||||||
|
this.prepare_view_toggler();
|
||||||
this.get_item_filter_data();
|
this.get_item_filter_data();
|
||||||
this.render_list_view();
|
|
||||||
this.render_grid_view();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render_view_toggler() {
|
prepare_view_toggler() {
|
||||||
["btn-list-view", "btn-grid-view"].forEach(view => {
|
if(!$("#list").length || !$("#image-view").length) {
|
||||||
let icon = view === "btn-list-view" ? "list" : "image-view";
|
this.render_view_toggler();
|
||||||
this.products_section.append(`
|
this.bind_view_toggler_actions();
|
||||||
<div class="form-group mb-0" id="toggle-view">
|
this.set_view_state();
|
||||||
<button id="${icon}" class="btn ${view} mr-2">
|
}
|
||||||
<span>
|
|
||||||
<svg class="icon icon-md">
|
|
||||||
<use href="#icon-${icon}"></use>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>`);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#list").click(function() {
|
|
||||||
let $btn = $(this);
|
|
||||||
$btn.removeClass('btn-primary');
|
|
||||||
$btn.addClass('btn-primary');
|
|
||||||
$(".btn-grid-view").removeClass('btn-primary');
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#image-view").click(function() {
|
|
||||||
let $btn = $(this);
|
|
||||||
$btn.removeClass('btn-primary');
|
|
||||||
$btn.addClass('btn-primary');
|
|
||||||
$(".btn-list-view").removeClass('btn-primary');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.products_area = this.products_section.append(`
|
|
||||||
<br><br>
|
|
||||||
<div id="products-area" class="row products-list mt-4"></div>
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_item_filter_data() {
|
get_item_filter_data() {
|
||||||
// Get Items and Discount Filters to render
|
// Get and render all Items related components
|
||||||
let me = this;
|
let me = this;
|
||||||
const filters = frappe.utils.get_query_params();
|
let args = this.get_query_filters();
|
||||||
let {field_filters, attribute_filters} = filters;
|
|
||||||
|
|
||||||
field_filters = field_filters ? JSON.parse(field_filters) : {};
|
|
||||||
attribute_filters = attribute_filters ? JSON.parse(attribute_filters) : {};
|
|
||||||
|
|
||||||
|
$('#list').prop('disabled', true);
|
||||||
|
$('#image-view').prop('disabled', true);
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.www.all-products.index.get_product_filter_data',
|
method: 'erpnext.www.all-products.index.get_product_filter_data',
|
||||||
args: {
|
args: args,
|
||||||
field_filters: field_filters,
|
|
||||||
attribute_filters: attribute_filters,
|
|
||||||
item_group: me.item_group
|
|
||||||
},
|
|
||||||
callback: function(result) {
|
callback: function(result) {
|
||||||
if (!result.exc) {
|
if (!result.exc && result) {
|
||||||
me.render_filters(result.message[1]);
|
me.render_filters(result.message[1]);
|
||||||
|
|
||||||
// Append pre-rendered products
|
if (me.item_group) {
|
||||||
// TODO: get products as is and style via js
|
me.render_item_sub_categories(result.message[3]);
|
||||||
me.products = result.message;
|
}
|
||||||
$("#products-area").append(result.message[0]);
|
// Render views
|
||||||
|
me.render_list_view(result.message[0], result.message[2]);
|
||||||
|
me.render_grid_view(result.message[0], result.message[2]);
|
||||||
|
me.products = result.message[0];
|
||||||
|
|
||||||
|
// Bottom paging
|
||||||
|
me.add_paging_section(result.message[2]);
|
||||||
} else {
|
} else {
|
||||||
$("#products-area").append(`
|
me.render_no_products_section();
|
||||||
<div class="d-flex justify-content-center p-3 text-muted">
|
|
||||||
${__('No products found')}
|
|
||||||
</div>`);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#list').prop('disabled', false);
|
||||||
|
$('#image-view').prop('disabled', false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -83,11 +59,159 @@ erpnext.ProductView = class {
|
|||||||
this.get_discount_filter_html(filter_data.discount_filters);
|
this.get_discount_filter_html(filter_data.discount_filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_grid_view(items, settings) {
|
||||||
|
// loop over data and add grid html to it
|
||||||
|
let me = this;
|
||||||
|
this.prepare_product_area_wrapper("grid");
|
||||||
|
|
||||||
|
frappe.require('assets/js/e-commerce.min.js', function() {
|
||||||
|
new erpnext.ProductGrid({
|
||||||
|
items: items,
|
||||||
|
products_section: $("#products-grid-area"),
|
||||||
|
settings: settings,
|
||||||
|
preference: me.preference
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render_list_view(items, settings) {
|
||||||
|
let me = this;
|
||||||
|
this.prepare_product_area_wrapper("list");
|
||||||
|
|
||||||
|
frappe.require('assets/js/e-commerce.min.js', function() {
|
||||||
|
new erpnext.ProductList({
|
||||||
|
items: items,
|
||||||
|
products_section: $("#products-list-area"),
|
||||||
|
settings: settings,
|
||||||
|
preference: me.preference
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_product_area_wrapper(view) {
|
||||||
|
let left_margin = view == "list" ? "ml-2" : "";
|
||||||
|
let top_margin = view == "list" ? "mt-8" : "mt-4";
|
||||||
|
return this.products_section.append(`
|
||||||
|
<br>
|
||||||
|
<div id="products-${view}-area" class="row products-list ${ top_margin } ${ left_margin }"></div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_query_filters() {
|
||||||
|
const filters = frappe.utils.get_query_params();
|
||||||
|
let {field_filters, attribute_filters} = filters;
|
||||||
|
|
||||||
|
field_filters = field_filters ? JSON.parse(field_filters) : {};
|
||||||
|
attribute_filters = attribute_filters ? JSON.parse(attribute_filters) : {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
field_filters: field_filters,
|
||||||
|
attribute_filters: attribute_filters,
|
||||||
|
item_group: this.item_group,
|
||||||
|
start: filters.start || null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_paging_section(settings) {
|
||||||
|
$(".product-paging-area").remove();
|
||||||
|
|
||||||
|
if(this.products) {
|
||||||
|
let paging_html = `
|
||||||
|
<div class="row product-paging-area mt-5">
|
||||||
|
<div class="col-3">
|
||||||
|
</div>
|
||||||
|
<div class="col-9 text-right">
|
||||||
|
`;
|
||||||
|
let query_params = frappe.utils.get_query_params();
|
||||||
|
let start = query_params.start ? cint(JSON.parse(query_params.start)) : 0;
|
||||||
|
let page_length = settings.products_per_page || 0;
|
||||||
|
|
||||||
|
if(start > 0) {
|
||||||
|
paging_html += `
|
||||||
|
<button class="btn btn-default btn-prev" data-start="${ start - page_length }" style="float: left">
|
||||||
|
${ __("Prev") }
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
if(this.products.length > page_length || this.products.length == page_length) {
|
||||||
|
paging_html += `
|
||||||
|
<button class="btn btn-default btn-next" data-start="${ start + page_length }">
|
||||||
|
${ __("Next") }
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
paging_html += `</div></div>`;
|
||||||
|
|
||||||
|
$(".page_content").append(paging_html);
|
||||||
|
this.bind_paging_action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render_view_toggler() {
|
||||||
|
["btn-list-view", "btn-grid-view"].forEach(view => {
|
||||||
|
let icon = view === "btn-list-view" ? "list" : "image-view";
|
||||||
|
this.products_section.append(`
|
||||||
|
<div class="form-group mb-0" id="toggle-view">
|
||||||
|
<button id="${ icon }" class="btn ${ view } mr-2">
|
||||||
|
<span>
|
||||||
|
<svg class="icon icon-md">
|
||||||
|
<use href="#icon-${ icon }"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_view_toggler_actions() {
|
||||||
|
$("#list").click(function() {
|
||||||
|
let $btn = $(this);
|
||||||
|
$btn.removeClass('btn-primary');
|
||||||
|
$btn.addClass('btn-primary');
|
||||||
|
$(".btn-grid-view").removeClass('btn-primary');
|
||||||
|
|
||||||
|
$("#products-grid-area").addClass("hidden");
|
||||||
|
$("#products-list-area").removeClass("hidden");
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#image-view").click(function() {
|
||||||
|
let $btn = $(this);
|
||||||
|
$btn.removeClass('btn-primary');
|
||||||
|
$btn.addClass('btn-primary');
|
||||||
|
$(".btn-list-view").removeClass('btn-primary');
|
||||||
|
|
||||||
|
$("#products-list-area").addClass("hidden");
|
||||||
|
$("#products-grid-area").removeClass("hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set_view_state() {
|
||||||
|
if (this.preference === "List View") {
|
||||||
|
$("#list").addClass('btn-primary');
|
||||||
|
$("#image-view").removeClass('btn-primary');
|
||||||
|
} else {
|
||||||
|
$("#image-view").addClass('btn-primary');
|
||||||
|
$("#list").removeClass('btn-primary');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_paging_action() {
|
||||||
|
$('.btn-prev, .btn-next').click((e) => {
|
||||||
|
const $btn = $(e.target);
|
||||||
|
$btn.prop('disabled', true);
|
||||||
|
const start = $btn.data('start');
|
||||||
|
let query_params = frappe.utils.get_query_params();
|
||||||
|
query_params.start = start;
|
||||||
|
let path = window.location.pathname + '?' + frappe.utils.get_url_from_dict(query_params);
|
||||||
|
window.location.href = path;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
get_discount_filter_html(filter_data) {
|
get_discount_filter_html(filter_data) {
|
||||||
|
$("#discount-filters").remove();
|
||||||
if (filter_data) {
|
if (filter_data) {
|
||||||
$("#product-filters").append(`
|
$("#product-filters").append(`
|
||||||
<div id="discount-filters" class="mb-4 filter-block pb-5">
|
<div id="discount-filters" class="mb-4 filter-block pb-5">
|
||||||
<div class="filter-label mb-3">${__("Discounts")}</div>
|
<div class="filter-label mb-3">${ __("Discounts") }</div>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
@ -95,13 +219,13 @@ erpnext.ProductView = class {
|
|||||||
filter_data.forEach(filter => {
|
filter_data.forEach(filter => {
|
||||||
html += `
|
html += `
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label data-value="${filter[0]}">
|
<label data-value="${ filter[0] }">
|
||||||
<input type="radio" class="product-filter discount-filter"
|
<input type="radio" class="product-filter discount-filter"
|
||||||
name="discount" id="${filter[0]}"
|
name="discount" id="${ filter[0] }"
|
||||||
data-filter-name="discount" data-filter-value="${filter[0]}"
|
data-filter-name="discount" data-filter-value="${ filter[0] }"
|
||||||
>
|
>
|
||||||
<span class="label-area" for="${filter[0]}">
|
<span class="label-area" for="${ filter[0] }">
|
||||||
${filter[1]}
|
${ filter[1] }
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -113,12 +237,35 @@ erpnext.ProductView = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_list_view() {
|
render_no_products_section() {
|
||||||
// loop over data and add list html to it
|
$("#products-area").append(`
|
||||||
|
<div class="d-flex justify-content-center p-3 text-muted">
|
||||||
|
${ __('No products found') }
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
render_grid_view() {
|
render_item_sub_categories(categories) {
|
||||||
// loop over data and add grid html to it
|
if(categories) {
|
||||||
}
|
let sub_group_html = `
|
||||||
|
<div class="sub-category-container">
|
||||||
|
<div class="heading"> ${ __('Sub Categories') } </div>
|
||||||
|
</div>
|
||||||
|
<div class="sub-category-container scroll-categories">
|
||||||
|
`;
|
||||||
|
|
||||||
|
categories.forEach(category => {
|
||||||
|
sub_group_html += `
|
||||||
|
<a href="${ category.route || '#' }" style="text-decoration: none;">
|
||||||
|
<div class="category-pill">
|
||||||
|
${ category.name }
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
sub_group_html += `</div>`;
|
||||||
|
|
||||||
|
$("#product-listing").prepend(sub_group_html);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -68,6 +68,8 @@
|
|||||||
"public/js/hierarchy_chart/hierarchy_chart_mobile.js"
|
"public/js/hierarchy_chart/hierarchy_chart_mobile.js"
|
||||||
],
|
],
|
||||||
"js/e-commerce.min.js": [
|
"js/e-commerce.min.js": [
|
||||||
"e_commerce/product_view.js"
|
"e_commerce/product_view.js",
|
||||||
|
"e_commerce/product_grid.js",
|
||||||
|
"e_commerce/product_list.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -229,11 +229,6 @@ body.product-page {
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-code {
|
|
||||||
color: var(--text-muted);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.product-description {
|
.product-description {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--gray-800);
|
color: var(--gray-800);
|
||||||
@ -303,6 +298,11 @@ body.product-page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-code {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.item-configurator-dialog {
|
.item-configurator-dialog {
|
||||||
.modal-header {
|
.modal-header {
|
||||||
padding: var(--padding-md) var(--padding-xl);
|
padding: var(--padding-md) var(--padding-xl);
|
||||||
|
|||||||
@ -79,7 +79,6 @@ class ItemGroup(NestedSet, WebsiteGenerator):
|
|||||||
"title": self.name
|
"title": self.name
|
||||||
})
|
})
|
||||||
|
|
||||||
context.sub_categories = get_child_groups(self.name)
|
|
||||||
if self.slideshow:
|
if self.slideshow:
|
||||||
values = {
|
values = {
|
||||||
'show_indicators': 1,
|
'show_indicators': 1,
|
||||||
|
|||||||
@ -35,24 +35,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div id="product-listing" class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
|
<div id="product-listing" class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
|
||||||
{% if sub_categories %}
|
|
||||||
<div class="sub-category-container">
|
|
||||||
<div class="heading"> {{ _('Sub Categories') }} </div>
|
|
||||||
</div>
|
|
||||||
<div class="sub-category-container scroll-categories">
|
|
||||||
{% for row in sub_categories%}
|
|
||||||
<a href="{{ row.route or '#' }}" style="text-decoration: none;">
|
|
||||||
<div class="category-pill">
|
|
||||||
{{ row.name }}
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- Products Rendered in all-products/index.js-->
|
<!-- Products Rendered in all-products/index.js-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 order-1 col-md-3 order-md-1">
|
<div class="col-12 order-1 col-md-3 order-md-1">
|
||||||
<div class="collapse d-md-block mr-4 filters-section" id="product-filters">
|
<div class="collapse d-md-block mr-4 filters-section" id="product-filters">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-5 title-section">
|
<div class="d-flex justify-content-between align-items-center mb-5 title-section">
|
||||||
@ -87,23 +72,6 @@
|
|||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-6">
|
|
||||||
<div class="col-3">
|
|
||||||
</div>
|
|
||||||
<div class="col-9">
|
|
||||||
{% if frappe.form_dict.start|int > 0 %}
|
|
||||||
<button class="btn btn-outline-secondary btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">
|
|
||||||
{{ _("Prev") }}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if items|length >= page_length %}
|
|
||||||
<button class="btn btn-outline-secondary btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}"
|
|
||||||
style="float: right;">
|
|
||||||
{{ _("Next") }}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{%- macro item_card(item, settings=None, is_featured=False, is_full_width=False, align="Left") -%}
|
{%- macro item_card(item, is_featured=False, is_full_width=False, align="Left") -%}
|
||||||
{%- set align_items_class = resolve_class({
|
{%- set align_items_class = resolve_class({
|
||||||
'align-items-end': align == 'Right',
|
'align-items-end': align == 'Right',
|
||||||
'align-items-center': align == 'Center',
|
'align-items-center': align == 'Center',
|
||||||
@ -80,12 +80,12 @@
|
|||||||
<img class="card-img" src="{{ image }}" alt="{{ title }}">
|
<img class="card-img" src="{{ image }}" alt="{{ title }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{{ item_card_body(title, settings, description, item, is_featured, align) }}
|
{{ item_card_body(title, description, item, is_featured, align) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{{ item_card_body(title, settings, description, item, is_featured, align) }}
|
{{ item_card_body(title, description, item, is_featured, align) }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@ -106,13 +106,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ item_card_body(title, settings, description, item, is_featured, align) }}
|
{{ item_card_body(title, description, item, is_featured, align) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- endmacro -%}
|
{%- endmacro -%}
|
||||||
|
|
||||||
{%- macro item_card_body(title, settings, description, item, is_featured, align) -%}
|
{%- macro item_card_body(title, description, item, is_featured, align) -%}
|
||||||
{%- set align_class = resolve_class({
|
{%- set align_class = resolve_class({
|
||||||
'text-right': align == 'Right',
|
'text-right': align == 'Right',
|
||||||
'text-center': align == 'Center' and not is_featured,
|
'text-center': align == 'Center' and not is_featured,
|
||||||
@ -128,52 +128,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{% if not item.has_variants and settings.enable_wishlist %}
|
|
||||||
<div class="like-action"
|
|
||||||
data-item-code="{{ item.item_code }}"
|
|
||||||
data-price="{{ item.price }}"
|
|
||||||
data-formatted-price="{{ item.get('formatted_price') }}">
|
|
||||||
<svg class="icon sm">
|
|
||||||
{%- set icon_class = "wished" if item.wished else "not-wished"-%}
|
|
||||||
<use class="{{ icon_class }} wish-icon" href="#icon-heart"></use>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% if is_featured %}
|
{% if is_featured %}
|
||||||
<div class="product-price">{{ item.formatted_price or '' }}</div>
|
<div class="product-price">{{ item.formatted_price or '' }}</div>
|
||||||
<div class="product-description ellipsis">{{ description or '' }}</div>
|
<div class="product-description ellipsis">{{ description or '' }}</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="product-category">{{ item.item_group or '' }}</div>
|
<div class="product-category">{{ item.item_group or '' }}</div>
|
||||||
|
|
||||||
{% if item.formatted_price %}
|
|
||||||
<div class="product-price">
|
|
||||||
{{ item.formatted_price or '' }}
|
|
||||||
|
|
||||||
{% if item.get("formatted_mrp") %}
|
|
||||||
<small class="ml-1 text-muted">
|
|
||||||
<s>{{ item.formatted_mrp }}</s>
|
|
||||||
</small>
|
|
||||||
<small class="ml-1" style="color: #F47A7A; font-weight: 500;">
|
|
||||||
{{ item.discount }} OFF
|
|
||||||
</small>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if item.has_variants %}
|
|
||||||
<a href="/{{ item.route or '#' }}">
|
|
||||||
<div class="btn btn-sm btn-explore-variants w-100 mt-4">
|
|
||||||
{{ _('Explore') }}
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{% elif settings.enabled and (settings.allow_items_not_in_stock or item.in_stock != "red")%}
|
|
||||||
<div id="{{ item.name }}" class="btn btn-sm btn-add-to-cart-list not-added w-100 mt-4"
|
|
||||||
data-item-code="{{ item.item_code }}">
|
|
||||||
{{ _('Add to Cart') }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{%- endmacro -%}
|
{%- endmacro -%}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{% from "erpnext/templates/includes/macros.html" import item_card, item_card_body, product_image_square %}
|
{% from "erpnext/templates/includes/macros.html" import item_card, item_card_body, product_image_square %}
|
||||||
|
<!-- Used in Product Search -->
|
||||||
<a class="product-link product-list-link" href="{{ route|abs_url }}">
|
<a class="product-link product-list-link" href="{{ route|abs_url }}">
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='col-xs-3 col-sm-2 product-image-wrapper'>
|
<div class='col-xs-3 col-sm-2 product-image-wrapper'>
|
||||||
|
|||||||
@ -32,8 +32,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<!-- Items section -->
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<!-- Items section -->
|
||||||
<div id="product-listing" class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
|
<div id="product-listing" class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
|
||||||
<!-- Rendered via JS -->
|
<!-- Rendered via JS -->
|
||||||
</div>
|
</div>
|
||||||
@ -82,21 +82,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO -->
|
|
||||||
<!-- Paging Section -->
|
|
||||||
<!-- <div class="row product-paging-area mt-5">
|
|
||||||
<div class="col-3">
|
|
||||||
</div>
|
|
||||||
<div class="col-9 text-right">
|
|
||||||
{% if frappe.form_dict.start|int > 0 %}
|
|
||||||
<button class="btn btn-default btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">{{ _("Prev") }}</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if items|length >= page_length %}
|
|
||||||
<button class="btn btn-default btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
frappe.ready(() => {
|
frappe.ready(() => {
|
||||||
$('.btn-prev, .btn-next').click((e) => {
|
$('.btn-prev, .btn-next').click((e) => {
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
$(() => {
|
$(() => {
|
||||||
class ProductListing {
|
class ProductListing {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
let me = this;
|
||||||
let is_item_group_page = $(".item-group-content").data("item-group");
|
let is_item_group_page = $(".item-group-content").data("item-group");
|
||||||
let item_group = is_item_group_page || null;
|
this.item_group = is_item_group_page || null;
|
||||||
|
|
||||||
// Render Products
|
// Render Products and Discount Filters
|
||||||
frappe.require('assets/js/e-commerce.min.js', function() {
|
frappe.require('assets/js/e-commerce.min.js', function() {
|
||||||
new erpnext.ProductView({
|
new erpnext.ProductView({
|
||||||
|
view_type: "List",
|
||||||
products_section: $('#product-listing'),
|
products_section: $('#product-listing'),
|
||||||
item_group: item_group
|
item_group: me.item_group
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -19,6 +21,7 @@ $(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bind_filters() {
|
bind_filters() {
|
||||||
|
let me = this;
|
||||||
this.field_filters = {};
|
this.field_filters = {};
|
||||||
this.attribute_filters = {};
|
this.attribute_filters = {};
|
||||||
|
|
||||||
@ -73,17 +76,14 @@ $(() => {
|
|||||||
window.history.pushState('filters', '', `${location.pathname}?` + query_string);
|
window.history.pushState('filters', '', `${location.pathname}?` + query_string);
|
||||||
|
|
||||||
$('.page_content input').prop('disabled', true);
|
$('.page_content input').prop('disabled', true);
|
||||||
this.get_items_with_filters()
|
frappe.require('assets/js/e-commerce.min.js', function() {
|
||||||
.then(html => {
|
new erpnext.ProductView({
|
||||||
$('.products-list').html(html);
|
view_type: "List",
|
||||||
})
|
products_section: $('#product-listing'),
|
||||||
.then(data => {
|
item_group: me.item_group
|
||||||
$('.page_content input').prop('disabled', false);
|
|
||||||
return data;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
$('.page_content input').prop('disabled', false);
|
|
||||||
});
|
});
|
||||||
|
$('.page_content input').prop('disabled', false);
|
||||||
|
});
|
||||||
}, 1000));
|
}, 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,27 +133,6 @@ $(() => {
|
|||||||
this.attribute_filters = attribute_filters;
|
this.attribute_filters = attribute_filters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_items_with_filters() {
|
|
||||||
const { attribute_filters, field_filters } = this;
|
|
||||||
const args = {
|
|
||||||
field_filters: if_key_exists(field_filters),
|
|
||||||
attribute_filters: if_key_exists(attribute_filters)
|
|
||||||
};
|
|
||||||
|
|
||||||
const item_group = $(".item-group-content").data('item-group');
|
|
||||||
if (item_group) {
|
|
||||||
Object.assign(field_filters, { item_group });
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
frappe.call('erpnext.www.all-products.index.get_products_html_for_website', args)
|
|
||||||
.then(r => {
|
|
||||||
if (r.exc) reject(r.exc);
|
|
||||||
else resolve(r.message);
|
|
||||||
})
|
|
||||||
.fail(reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new ProductListing();
|
new ProductListing();
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import frappe
|
|||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
from erpnext.e_commerce.product_query import ProductQuery
|
from erpnext.e_commerce.product_query import ProductQuery
|
||||||
from erpnext.e_commerce.filters import ProductFiltersBuilder
|
from erpnext.e_commerce.filters import ProductFiltersBuilder
|
||||||
|
from erpnext.setup.doctype.item_group.item_group import get_child_groups
|
||||||
|
|
||||||
sitemap = 1
|
sitemap = 1
|
||||||
|
|
||||||
@ -24,56 +25,25 @@ def get_product_filter_data():
|
|||||||
search = frappe.form_dict.search
|
search = frappe.form_dict.search
|
||||||
field_filters = frappe.parse_json(frappe.form_dict.field_filters)
|
field_filters = frappe.parse_json(frappe.form_dict.field_filters)
|
||||||
attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters)
|
attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters)
|
||||||
start = cint(frappe.parse_json(frappe.form_dict.start))
|
start = cint(frappe.parse_json(frappe.form_dict.start)) if frappe.form_dict.start else 0
|
||||||
item_group = frappe.form_dict.item_group
|
item_group = frappe.form_dict.item_group
|
||||||
else:
|
else:
|
||||||
search, attribute_filters, item_group = None, None, None
|
search, attribute_filters, item_group = None, None, None
|
||||||
field_filters = {}
|
field_filters = {}
|
||||||
start = 0
|
start = 0
|
||||||
|
|
||||||
|
sub_categories = []
|
||||||
if item_group:
|
if item_group:
|
||||||
field_filters['item_group'] = item_group
|
field_filters['item_group'] = item_group
|
||||||
|
sub_categories = get_child_groups(item_group)
|
||||||
|
|
||||||
engine = ProductQuery()
|
engine = ProductQuery()
|
||||||
items, discounts = engine.query(attribute_filters, field_filters, search_term=search, start=start)
|
items, discounts = engine.query(attribute_filters, field_filters, search_term=search, start=start)
|
||||||
|
|
||||||
item_html = []
|
|
||||||
for item in items:
|
|
||||||
item_html.append(frappe.render_template('erpnext/www/all-products/item_row.html', {
|
|
||||||
'item': item,
|
|
||||||
'e_commerce_settings': engine.settings
|
|
||||||
}))
|
|
||||||
html = ''.join(item_html)
|
|
||||||
|
|
||||||
if not items:
|
|
||||||
html = frappe.render_template('erpnext/www/all-products/not_found.html', {})
|
|
||||||
|
|
||||||
# discount filter data
|
# discount filter data
|
||||||
filters = {}
|
filters = {}
|
||||||
if discounts:
|
if discounts:
|
||||||
filter_engine = ProductFiltersBuilder()
|
filter_engine = ProductFiltersBuilder()
|
||||||
filters["discount_filters"] = filter_engine.get_discount_filters(discounts)
|
filters["discount_filters"] = filter_engine.get_discount_filters(discounts)
|
||||||
|
|
||||||
return html, filters
|
return items or [], filters, engine.settings, sub_categories
|
||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
|
||||||
def get_products_html_for_website(field_filters=None, attribute_filters=None):
|
|
||||||
"""Get Products on filter change."""
|
|
||||||
field_filters = frappe.parse_json(field_filters)
|
|
||||||
attribute_filters = frappe.parse_json(attribute_filters)
|
|
||||||
|
|
||||||
engine = ProductQuery()
|
|
||||||
items, discounts = engine.query(attribute_filters, field_filters, search_term=None, start=0)
|
|
||||||
|
|
||||||
item_html = []
|
|
||||||
for item in items:
|
|
||||||
item_html.append(frappe.render_template('erpnext/www/all-products/item_row.html', {
|
|
||||||
'item': item,
|
|
||||||
'e_commerce_settings': engine.settings
|
|
||||||
}))
|
|
||||||
html = ''.join(item_html)
|
|
||||||
|
|
||||||
if not items:
|
|
||||||
html = frappe.render_template('erpnext/www/all-products/not_found.html', {})
|
|
||||||
|
|
||||||
return html
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{% from "erpnext/templates/includes/macros.html" import item_card, item_card_body %}
|
|
||||||
|
|
||||||
{{ item_card(item, e_commerce_settings) }}
|
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user