From 59514408b9600d0523b657cdb9968b42724df1bb Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 16 Mar 2021 00:05:53 +0530 Subject: [PATCH] feat: Wishlist Page - Navbar icon with badge count for wishlist - Wishlist page with cards - Cards can be moved to cart or removed in a click - Separated all wishlist related methods into wishlist.js - Made a common js method(util) to add/remove wishlist items - Bug fix: Make sure items are removed from session user's wishlist --- .../e_commerce/doctype/wishlist/wishlist.py | 10 +- .../wishlist_items/wishlist_items.json | 25 +++- erpnext/e_commerce/product_query.py | 2 +- erpnext/public/js/shopping_cart.js | 35 +++++ erpnext/public/js/wishlist.js | 133 +++++++++++++++++- erpnext/public/scss/shopping_cart.scss | 24 ++++ erpnext/templates/includes/macros.html | 65 ++++++++- .../includes/navbar/navbar_items.html | 2 +- erpnext/templates/pages/wishlist.html | 24 ++++ erpnext/templates/pages/wishlist.py | 39 +++++ erpnext/www/all-products/index.js | 94 +------------ 11 files changed, 345 insertions(+), 108 deletions(-) create mode 100644 erpnext/templates/pages/wishlist.html create mode 100644 erpnext/templates/pages/wishlist.py diff --git a/erpnext/e_commerce/doctype/wishlist/wishlist.py b/erpnext/e_commerce/doctype/wishlist/wishlist.py index 7527c6f415..83bdff6115 100644 --- a/erpnext/e_commerce/doctype/wishlist/wishlist.py +++ b/erpnext/e_commerce/doctype/wishlist/wishlist.py @@ -10,18 +10,22 @@ class Wishlist(Document): pass @frappe.whitelist() -def add_to_wishlist(item_code, price): +def add_to_wishlist(item_code, price, formatted_price=None): """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) + ["image", "website_warehouse", "name", "item_name", "item_group", "route"] + , as_dict=1) wished_item_dict = { "item_code": item_code, "item_name": web_item_data.get("item_name"), + "item_group": web_item_data.get("item_group"), "website_item": web_item_data.get("name"), "price": frappe.utils.flt(price), + "formatted_price": formatted_price, "image": web_item_data.get("image"), - "website_warehouse": web_item_data.get("website_warehouse") + "warehouse": web_item_data.get("website_warehouse"), + "route": web_item_data.get("route") } if not frappe.db.exists("Wishlist", frappe.session.user): diff --git a/erpnext/e_commerce/doctype/wishlist_items/wishlist_items.json b/erpnext/e_commerce/doctype/wishlist_items/wishlist_items.json index 18065a8861..0b132737b3 100644 --- a/erpnext/e_commerce/doctype/wishlist_items/wishlist_items.json +++ b/erpnext/e_commerce/doctype/wishlist_items/wishlist_items.json @@ -9,13 +9,16 @@ "website_item", "column_break_3", "item_name", + "item_group", "item_details_section", "description", "column_break_7", - "section_break_8", - "price", + "route", "image", "image_view", + "section_break_8", + "price", + "formatted_price", "warehouse_section", "warehouse" ], @@ -101,12 +104,28 @@ "fieldname": "price", "fieldtype": "Float", "label": "Price" + }, + { + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group" + }, + { + "fieldname": "route", + "fieldtype": "Small Text", + "label": "Route" + }, + { + "fieldname": "formatted_price", + "fieldtype": "Data", + "label": "Formatted Price" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-03-12 18:23:03.487891", + "modified": "2021-03-15 16:37:40.405333", "modified_by": "Administrator", "module": "E-commerce", "name": "Wishlist Items", diff --git a/erpnext/e_commerce/product_query.py b/erpnext/e_commerce/product_query.py index 28d33e6e81..c37f8fb6b2 100644 --- a/erpnext/e_commerce/product_query.py +++ b/erpnext/e_commerce/product_query.py @@ -81,7 +81,7 @@ class ProductQuery: item.in_stock = "green" if stock_qty else "red" item.wished = False - if frappe.db.exists("Wishlist Items", {"item_code": item.item_code}): + if frappe.db.exists("Wishlist Items", {"item_code": item.item_code, "parent": frappe.session.user}): item.wished = True return result diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index bcfa983e4a..b57862b93a 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -186,5 +186,40 @@ $.extend(shopping_cart, { $(".shopping-cart").toggleClass('hidden', r.message ? false : true); } }); + }, + + animate_add_to_cart(button) { + // Create 'added to cart' animation + let btn_id = "#" + button[0].id; + this.toggle_button_class(button, 'not-added', 'added-to-cart'); + $(btn_id).text('Added to Cart'); + + // undo + setTimeout(() => { + this.toggle_button_class(button, 'added-to-cart', 'not-added'); + $(btn_id).text('Add to Cart'); + }, 2000); + }, + + toggle_button_class(button, remove, add) { + button.removeClass(remove); + button.addClass(add); + }, + + bind_add_to_cart_action() { + $('.page_content').on('click', '.btn-add-to-cart-list', (e) => { + const $btn = $(e.currentTarget); + $btn.prop('disabled', true); + + this.animate_add_to_cart($btn); + + const item_code = $btn.data('item-code'); + erpnext.shopping_cart.update_cart({ + item_code, + qty: 1 + }); + + }); } + }); diff --git a/erpnext/public/js/wishlist.js b/erpnext/public/js/wishlist.js index 328bdb996d..6ab1906b60 100644 --- a/erpnext/public/js/wishlist.js +++ b/erpnext/public/js/wishlist.js @@ -1,13 +1,12 @@ -frappe.provide("erpnext.e_commerce"); -var wishlist = erpnext.e_commerce; +frappe.provide("erpnext.wishlist"); +var wishlist = erpnext.wishlist; -frappe.ready(function() { - $(".wishlist").toggleClass('hidden', true); - wishlist.set_wishlist_count(); -}); +frappe.provide("erpnext.shopping_cart"); +var shopping_cart = erpnext.shopping_cart; $.extend(wishlist, { set_wishlist_count: function() { + // set badge count for wishlist icon var wish_count = frappe.get_cookie("wish_count"); if(frappe.session.user==="Guest") { wish_count = 0; @@ -35,5 +34,127 @@ $.extend(wishlist, { } else { $badge.remove(); } + }, + + bind_move_to_cart_action: function() { + // move item to cart from wishlist + $('.page_content').on("click", ".btn-add-to-cart", (e) => { + const $move_to_cart_btn = $(e.currentTarget); + let item_code = $move_to_cart_btn.data("item-code"); + + shopping_cart.shopping_cart_update({ + item_code, + qty: 1, + cart_dropdown: true + }); + + let success_action = function() { + const $card_wrapper = $move_to_cart_btn.closest(".item-card"); + $card_wrapper.addClass("wish-removed"); + }; + let args = { item_code: item_code }; + this.add_remove_from_wishlist("remove", args, success_action, null, true); + }); + }, + + bind_remove_action: function() { + // remove item from wishlist + $('.page_content').on("click", ".remove-wish", (e) => { + const $remove_wish_btn = $(e.currentTarget); + let item_code = $remove_wish_btn.data("item-code"); + + let success_action = function() { + const $card_wrapper = $remove_wish_btn.closest(".item-card"); + $card_wrapper.addClass("wish-removed"); + }; + let args = { item_code: item_code }; + this.add_remove_from_wishlist("remove", args, success_action); + }); + }, + + bind_wishlist_action() { + // 'wish'('like') or 'unwish' item in product listing + $('.page_content').on('click', '.like-action', (e) => { + const $btn = $(e.currentTarget); + const $wish_icon = $btn.find('.wish-icon'); + let me = this; + + let success_action = function() { + erpnext.wishlist.set_wishlist_count(); + }; + + if ($wish_icon.hasClass('wished')) { + // un-wish item + $btn.removeClass("like-animate"); + this.toggle_button_class($wish_icon, 'wished', 'not-wished'); + + let args = { item_code: $btn.data('item-code') }; + let failure_action = function() { + me.toggle_button_class($wish_icon, 'not-wished', 'wished'); + }; + this.add_remove_from_wishlist("remove", args, success_action, failure_action); + } else { + // wish item + $btn.addClass("like-animate"); + this.toggle_button_class($wish_icon, 'not-wished', 'wished'); + + let args = { + item_code: $btn.data('item-code'), + price: $btn.data('price'), + formatted_price: $btn.data('formatted-price') + }; + let failure_action = function() { + me.toggle_button_class($wish_icon, 'wished', 'not-wished'); + }; + this.add_remove_from_wishlist("add", args, success_action, failure_action); + } + }); + }, + + toggle_button_class(button, remove, add) { + button.removeClass(remove); + button.addClass(add); + }, + + add_remove_from_wishlist(action, args, success_action, failure_action, async=false) { + /* AJAX call to add or remove Item from Wishlist + action: "add" or "remove" + args: args for method (item_code, price, formatted_price), + success_action: method to execute on successs, + failure_action: method to execute on failure, + async: make call asynchronously (true/false). */ + let method = "erpnext.e_commerce.doctype.wishlist.wishlist.add_to_wishlist"; + if (action === "remove") { + method = "erpnext.e_commerce.doctype.wishlist.wishlist.remove_from_wishlist"; + } + + frappe.call({ + type: "POST", + method: method, + args: args, + callback: function (r) { + if (r.exc) { + if (failure_action && (typeof failure_action === 'function')) {failure_action();} + frappe.msgprint({ + message: __("Sorry, something went wrong. Please refresh."), + indicator: "red", title: __("Note") + }); + } else { + if (success_action && (typeof success_action === 'function')) {success_action();} + } + } + }); } + +}); + +frappe.ready(function() { + if (window.location.pathname !== "/wishlist") { + $(".wishlist").toggleClass('hidden', true); + wishlist.set_wishlist_count(); + } else { + wishlist.bind_move_to_cart_action(); + wishlist.bind_remove_action(); + } + }); \ No newline at end of file diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index 3d66f146c0..6a96e41256 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -648,3 +648,27 @@ body.product-page { color: white; } } + +.wishlist-cart-not-added { + color: var(--blue-500); + background-color: white; + border: 1px solid var(--blue-500); + --icon-stroke: var(--blue-500); + + &:hover { + background-color: var(--blue-500); + color: white; + --icon-stroke: white; + } +} + +.remove-wish { + &:hover { + background-color: var(--gray-100); + border: 1px solid var(--icon-stroke); + } +} + +.wish-removed { + display: none; +} diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index 743daaf7e1..aec201e0d9 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -118,7 +118,6 @@ 'text-left': align == 'Left' or is_featured, }) -%}
-
{{ title or '' }}
@@ -128,7 +127,9 @@ {% endif %} {% if not item.has_variants %} {%- endmacro -%} + + +{%- macro wishlist_card(item, settings) %} +
+
+ {% if item.image %} +
+ + {{ title }} + +
+ + + + + +
+ +
+ {% else %} + +
+ {{ frappe.utils.get_abbr(title) }} +
+
+ {% endif %} + + {{ wishlist_card_body(item, settings) }} + + +
+
+{%- endmacro -%} + +{%- macro wishlist_card_body(item, settings) %} +
+
+
{{ item.item_name or item.item_code or ''}}
+
+
{{ item.formatted_price or '' }}
+ + {% if (item.available and settings.show_stock_availability) or (not settings.show_stock_availability) %} + + + {% else %} +
+ {{ _("Not in Stock") }} +
+ {% endif %} +
+{%- endmacro -%} diff --git a/erpnext/templates/includes/navbar/navbar_items.html b/erpnext/templates/includes/navbar/navbar_items.html index 54ed98af88..793bacb665 100644 --- a/erpnext/templates/includes/navbar/navbar_items.html +++ b/erpnext/templates/includes/navbar/navbar_items.html @@ -10,7 +10,7 @@