feat: Wishlist from card actions
- Add remove items from wishlist - Wishlist icon at nav bar - Animate wishlist icon in card and navbar - Remember wished state after refresh as well
This commit is contained in:
parent
4f64d1c7f2
commit
96cc5068b2
@ -3,8 +3,52 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Wishlist(Document):
|
||||
pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_to_wishlist(item_code, price):
|
||||
"""Insert Item into wishlist."""
|
||||
web_item_data = frappe.db.get_value("Website Item", {"item_code": item_code},
|
||||
["image", "website_warehouse", "name", "item_name"], as_dict=1)
|
||||
|
||||
wished_item_dict = {
|
||||
"item_code": item_code,
|
||||
"item_name": web_item_data.get("item_name"),
|
||||
"website_item": web_item_data.get("name"),
|
||||
"price": frappe.utils.flt(price),
|
||||
"image": web_item_data.get("image"),
|
||||
"website_warehouse": web_item_data.get("website_warehouse")
|
||||
}
|
||||
|
||||
if not frappe.db.exists("Wishlist", frappe.session.user):
|
||||
# initialise wishlist
|
||||
wishlist = frappe.get_doc({"doctype": "Wishlist"})
|
||||
wishlist.user = frappe.session.user
|
||||
wishlist.append("items", wished_item_dict)
|
||||
wishlist.save(ignore_permissions=True)
|
||||
else:
|
||||
wishlist = frappe.get_doc("Wishlist", frappe.session.user)
|
||||
item = wishlist.append('items', wished_item_dict)
|
||||
item.db_insert()
|
||||
|
||||
if hasattr(frappe.local, "cookie_manager"):
|
||||
frappe.local.cookie_manager.set_cookie("wish_count", str(len(wishlist.items)))
|
||||
|
||||
@frappe.whitelist()
|
||||
def remove_from_wishlist(item_code):
|
||||
if frappe.db.exists("Wishlist Items", {"item_code": item_code}):
|
||||
frappe.db.sql("""
|
||||
delete
|
||||
from `tabWishlist Items`
|
||||
where item_code=%(item_code)s
|
||||
"""%{"item_code": frappe.db.escape(item_code)})
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
wishlist = frappe.get_doc("Wishlist", frappe.session.user)
|
||||
if hasattr(frappe.local, "cookie_manager"):
|
||||
frappe.local.cookie_manager.set_cookie("wish_count", str(len(wishlist.items)))
|
@ -12,6 +12,8 @@
|
||||
"item_details_section",
|
||||
"description",
|
||||
"column_break_7",
|
||||
"section_break_8",
|
||||
"price",
|
||||
"image",
|
||||
"image_view",
|
||||
"warehouse_section",
|
||||
@ -52,6 +54,7 @@
|
||||
},
|
||||
{
|
||||
"fetch_from": "item_code.description",
|
||||
"fetch_if_empty": 1,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Description"
|
||||
@ -62,6 +65,7 @@
|
||||
},
|
||||
{
|
||||
"fetch_from": "item_code.image",
|
||||
"fetch_if_empty": 1,
|
||||
"fieldname": "image",
|
||||
"fieldtype": "Attach",
|
||||
"hidden": 1,
|
||||
@ -69,6 +73,7 @@
|
||||
},
|
||||
{
|
||||
"fetch_from": "item_code.image",
|
||||
"fetch_if_empty": 1,
|
||||
"fieldname": "image_view",
|
||||
"fieldtype": "Image",
|
||||
"hidden": 1,
|
||||
@ -87,12 +92,21 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "price",
|
||||
"fieldtype": "Float",
|
||||
"label": "Price"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-10 19:13:41.310816",
|
||||
"modified": "2021-03-12 18:23:03.487891",
|
||||
"modified_by": "Administrator",
|
||||
"module": "E-commerce",
|
||||
"name": "Wishlist Items",
|
||||
|
@ -67,6 +67,7 @@ class ProductQuery:
|
||||
product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info')
|
||||
if product_info:
|
||||
item.formatted_price = (product_info.get('price') or {}).get('formatted_price')
|
||||
item.price = product_info['price'].get('price_list_rate')
|
||||
|
||||
if self.settings.show_stock_availability and item.get("website_warehouse"):
|
||||
stock_qty = frappe.utils.flt(
|
||||
@ -78,6 +79,11 @@ class ProductQuery:
|
||||
"actual_qty")
|
||||
)
|
||||
item.in_stock = "green" if stock_qty else "red"
|
||||
|
||||
item.wished = False
|
||||
if frappe.db.exists("Wishlist Items", {"item_code": item.item_code}):
|
||||
item.wished = True
|
||||
|
||||
return result
|
||||
|
||||
def query_items(self, conditions, or_conditions, substitutions, start=0):
|
||||
|
@ -138,7 +138,7 @@ def update_cart(item_code, qty, additional_notes=None, with_items=False):
|
||||
"additional_notes": additional_notes
|
||||
})
|
||||
else:
|
||||
quotation_items[0].qty = qty + 1
|
||||
quotation_items[0].qty = qty
|
||||
quotation_items[0].additional_notes = additional_notes
|
||||
|
||||
apply_cart_settings(quotation=quotation)
|
||||
|
@ -11,7 +11,8 @@
|
||||
],
|
||||
"js/erpnext-web.min.js": [
|
||||
"public/js/website_utils.js",
|
||||
"public/js/shopping_cart.js"
|
||||
"public/js/shopping_cart.js",
|
||||
"public/js/wishlist.js"
|
||||
],
|
||||
"css/erpnext-web.css": [
|
||||
"public/scss/website.scss",
|
||||
|
39
erpnext/public/js/wishlist.js
Normal file
39
erpnext/public/js/wishlist.js
Normal file
@ -0,0 +1,39 @@
|
||||
frappe.provide("erpnext.e_commerce");
|
||||
var wishlist = erpnext.e_commerce;
|
||||
|
||||
frappe.ready(function() {
|
||||
$(".wishlist").toggleClass('hidden', true);
|
||||
wishlist.set_wishlist_count();
|
||||
});
|
||||
|
||||
$.extend(wishlist, {
|
||||
set_wishlist_count: function() {
|
||||
var wish_count = frappe.get_cookie("wish_count");
|
||||
if(frappe.session.user==="Guest") {
|
||||
wish_count = 0;
|
||||
}
|
||||
|
||||
if(wish_count) {
|
||||
$(".wishlist").toggleClass('hidden', false);
|
||||
}
|
||||
|
||||
var $wishlist = $('.wishlist-icon');
|
||||
var $badge = $wishlist.find("#wish-count");
|
||||
|
||||
if(parseInt(wish_count) === 0 || wish_count === undefined) {
|
||||
$wishlist.css("display", "none");
|
||||
}
|
||||
else {
|
||||
$wishlist.css("display", "inline");
|
||||
}
|
||||
if(wish_count) {
|
||||
$badge.html(wish_count);
|
||||
$wishlist.addClass('cart-animate');
|
||||
setTimeout(() => {
|
||||
$wishlist.removeClass('cart-animate');
|
||||
}, 500);
|
||||
} else {
|
||||
$badge.remove();
|
||||
}
|
||||
}
|
||||
});
|
@ -349,20 +349,20 @@ body.product-page {
|
||||
}
|
||||
}
|
||||
|
||||
.cart-icon {
|
||||
.cart-badge {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
left: -12px;
|
||||
background: var(--red-600);
|
||||
width: 16px;
|
||||
align-items: center;
|
||||
height: 16px;
|
||||
font-size: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.shopping-badge {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
left: -12px;
|
||||
background: var(--red-600);
|
||||
width: 16px;
|
||||
align-items: center;
|
||||
height: 16px;
|
||||
font-size: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
||||
.cart-animate {
|
||||
animation: wiggle 0.5s linear;
|
||||
}
|
||||
@ -555,7 +555,28 @@ body.product-page {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.wish-icon {
|
||||
.like-animate {
|
||||
animation: expand cubic-bezier(0.04, 0.4, 0.5, 0.95) 1.6s forwards 1;
|
||||
}
|
||||
|
||||
@keyframes expand {
|
||||
30% {
|
||||
transform: scale(1.6);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes heart { 0%, 17.5% { font-size: 0; } }
|
||||
|
||||
.not-wished {
|
||||
cursor: pointer;
|
||||
stroke: #F47A7A !important;
|
||||
|
||||
@ -565,10 +586,8 @@ body.product-page {
|
||||
}
|
||||
|
||||
.wished {
|
||||
.wish-icon {
|
||||
stroke: none;
|
||||
fill: #F47A7A !important;
|
||||
}
|
||||
stroke: none;
|
||||
fill: #F47A7A !important;
|
||||
}
|
||||
|
||||
.list-row-checkbox {
|
||||
|
@ -127,12 +127,11 @@
|
||||
<span class="indicator {{ item.in_stock }} card-indicator"></span>
|
||||
{% endif %}
|
||||
{% if not item.has_variants %}
|
||||
<input class="level-item list-row-checkbox hidden-xs"
|
||||
type="checkbox" data-name="{{ title }}" style="display: none !important;">
|
||||
<div class="like-action"
|
||||
data-name="{{ title }}" data-doctype="Item">
|
||||
data-item-code="{{ item.item_code }}" data-price="{{ item.price }}">
|
||||
<svg class="icon sm">
|
||||
<use class="wish-icon" href="#icon-heart"></use>
|
||||
{%- set icon_class = "wished" if item.wished else "not-wished"-%}
|
||||
<use class="{{ icon_class }} wish-icon" href="#icon-heart"></use>
|
||||
</svg>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -6,7 +6,15 @@
|
||||
<svg class="icon icon-lg">
|
||||
<use href="#icon-assets"></use>
|
||||
</svg>
|
||||
<span class="badge badge-primary cart-badge" id="cart-count"></span>
|
||||
<span class="badge badge-primary shopping-badge" id="cart-count"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="wishlist wishlist-icon hidden">
|
||||
<a class="nav-link" href="/cart">
|
||||
<svg class="icon icon-lg">
|
||||
<use href="#icon-heart"></use>
|
||||
</svg>
|
||||
<span class="badge badge-primary shopping-badge" id="wish-count"></span>
|
||||
</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
@ -73,6 +73,11 @@ $(() => {
|
||||
}
|
||||
|
||||
bind_card_actions() {
|
||||
this.bind_add_to_cart_action();
|
||||
this.bind_wishlist_action();
|
||||
}
|
||||
|
||||
bind_add_to_cart_action() {
|
||||
$('.page_content').on('click', '.btn-add-to-cart-list', (e) => {
|
||||
const $btn = $(e.currentTarget);
|
||||
$btn.prop('disabled', true);
|
||||
@ -91,18 +96,77 @@ $(() => {
|
||||
animate_add_to_cart(button) {
|
||||
// Create 'added to cart' animation
|
||||
let btn_id = "#" + button[0].id;
|
||||
button.removeClass('not-added');
|
||||
button.addClass('added-to-cart');
|
||||
this.toggle_button_class(button, 'not-added', 'added-to-cart');
|
||||
$(btn_id).text('Added to Cart');
|
||||
|
||||
// undo
|
||||
setTimeout(() => {
|
||||
button.removeClass('added-to-cart');
|
||||
button.addClass('not-added');
|
||||
this.toggle_button_class(button, 'added-to-cart', 'not-added');
|
||||
$(btn_id).text('Add to Cart');
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
bind_wishlist_action() {
|
||||
$('.page_content').on('click', '.like-action', (e) => {
|
||||
const $btn = $(e.currentTarget);
|
||||
const $wish_icon = $btn.find('.wish-icon');
|
||||
let me = this;
|
||||
|
||||
if ($wish_icon.hasClass('wished')) {
|
||||
// un-wish item
|
||||
$btn.removeClass("like-animate");
|
||||
this.toggle_button_class($wish_icon, 'wished', 'not-wished');
|
||||
frappe.call({
|
||||
type: "POST",
|
||||
method: "erpnext.e_commerce.doctype.wishlist.wishlist.remove_from_wishlist",
|
||||
args: {
|
||||
item_code: $btn.data('item-code')
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.exc) {
|
||||
me.toggle_button_class($wish_icon, 'wished', 'not-wished');
|
||||
frappe.msgprint({
|
||||
message: __("Sorry, something went wrong. Please refresh."),
|
||||
indicator: "red",
|
||||
title: __("Note")}
|
||||
);
|
||||
} else {
|
||||
erpnext.e_commerce.set_wishlist_count();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$btn.addClass("like-animate");
|
||||
this.toggle_button_class($wish_icon, 'not-wished', 'wished');
|
||||
frappe.call({
|
||||
type: "POST",
|
||||
method: "erpnext.e_commerce.doctype.wishlist.wishlist.add_to_wishlist",
|
||||
args: {
|
||||
item_code: $btn.data('item-code'),
|
||||
price: $btn.data('price')
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.exc) {
|
||||
me.toggle_button_class($wish_icon, 'wished', 'not-wished');
|
||||
frappe.msgprint({
|
||||
message: __("Sorry, something went wrong. Please refresh."),
|
||||
indicator: "red",
|
||||
title: __("Note")}
|
||||
);
|
||||
} else {
|
||||
erpnext.e_commerce.set_wishlist_count();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggle_button_class(button, remove, add) {
|
||||
button.removeClass(remove);
|
||||
button.addClass(add);
|
||||
}
|
||||
|
||||
bind_search() {
|
||||
$('input[type=search]').on('keydown', (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user