Merge branch 'rebrand-ui' of https://github.com/frappe/erpnext into rebrand-ui
diff --git a/erpnext/portal/doctype/products_settings/products_settings.py b/erpnext/portal/doctype/products_settings/products_settings.py
index ae7dc68..c1da3fe 100644
--- a/erpnext/portal/doctype/products_settings/products_settings.py
+++ b/erpnext/portal/doctype/products_settings/products_settings.py
@@ -17,6 +17,7 @@
 
 		self.validate_field_filters()
 		self.validate_attribute_filters()
+		frappe.clear_document_cache("Product Settings")
 
 	def validate_field_filters(self):
 		if not (self.enable_field_filters and self.filter_fields): return
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index 9ba4cdc..4693d44 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -1,6 +1,7 @@
 import frappe
 from frappe.utils import cint
 from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager
+from erpnext.shopping_cart.product_info import get_product_info_for_website
 
 def get_field_filter_data():
 	product_settings = get_product_settings()
@@ -356,10 +357,10 @@
 
 	results = frappe.db.sql('''
 		SELECT
-			`tabItem`.`name`, `tabItem`.`item_name`,
+			`tabItem`.`name`, `tabItem`.`item_name`, `tabItem`.`item_code`,
 			`tabItem`.`website_image`, `tabItem`.`image`,
 			`tabItem`.`web_long_description`, `tabItem`.`description`,
-			`tabItem`.`route`
+			`tabItem`.`route`, `tabItem`.`item_group`
 		FROM
 			`tabItem`
 		{left_join}
@@ -384,6 +385,8 @@
 	for r in results:
 		r.description = r.web_long_description or r.description
 		r.image = r.website_image or r.image
+		product_info = get_product_info_for_website(r.item_code, skip_quotation_creation=True).get('product_info')
+		r.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None
 
 	return results
 
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index d30bc8c..b4a1cf8 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -13,7 +13,8 @@
 		"public/js/shopping_cart.js"
 	],
 	"css/erpnext-web.css": [
-		"public/scss/website.scss"
+		"public/scss/website.scss",
+		"public/scss/shopping_cart.scss"
 	],
 	"js/marketplace.min.js": [
 		"public/js/hub/marketplace.js"
diff --git a/erpnext/public/images/ui-states/cart-empty-state.png b/erpnext/public/images/ui-states/cart-empty-state.png
new file mode 100644
index 0000000..e1ead0e
--- /dev/null
+++ b/erpnext/public/images/ui-states/cart-empty-state.png
Binary files differ
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
new file mode 100644
index 0000000..7bce6ab
--- /dev/null
+++ b/erpnext/public/scss/shopping_cart.scss
@@ -0,0 +1,490 @@
+@import "frappe/public/scss/desk/variables";
+@import "frappe/public/scss/common/mixins";
+
+body.product-page {
+	background: var(--gray-50);
+}
+
+
+.item-breadcrumbs {
+	.breadcrumb-container {
+		ol.breadcrumb {
+			background-color: var(--gray-50) !important;
+		}
+
+		a {
+			color: var(--gray-900);
+		}
+	}
+}
+
+.carousel-control {
+	height: 42px;
+	width: 42px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background: white;
+	box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.08), 0px 1px 2px 1px rgba(0, 0, 0, 0.06);
+	border-radius: 100px;
+}
+
+.carousel-control-prev,
+.carousel-control-next {
+	opacity: 1;
+}
+
+.carousel-body {
+	position: absolute;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+}
+
+.carousel-content {
+	max-width: 400px;
+}
+
+.card {
+	border: none;
+}
+
+.product-category-section {
+	.card:hover {
+		box-shadow: 0px 16px 45px 6px rgba(0, 0, 0, 0.08), 0px 8px 10px -10px rgba(0, 0, 0, 0.04);
+	}
+
+	.card-grid {
+		display: grid;
+		grid-gap: 15px;
+		grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));;
+	}
+}
+
+.item-card-group-section {
+	.card {
+		height: 360px;
+		align-items: center;
+		justify-content: center;
+
+		&:hover {
+			box-shadow: 0px 16px 60px rgba(0, 0, 0, 0.08), 0px 8px 30px -20px rgba(0, 0, 0, 0.04);
+			transition: box-shadow 400ms;
+		}
+	}
+
+	// .card-body {
+	// 	text-align: center;
+	// }
+
+	// .featured-item {
+	// 	.card-body {
+	// 		text-align: left;
+	// 	}
+	// }
+
+	.card-img {
+		max-height: 210px;
+		object-fit: contain;
+		margin-top: 1.25rem;
+	}
+
+	.no-image {
+		@include flex(flex, center, center, null);
+		height: 200px;
+		margin: 0 auto;
+		margin-top: var(--margin-xl);
+		background: var(--gray-100);
+		width: 80%;
+		border-radius: var(--border-radius);
+		font-size: 2rem;
+		color: var(--gray-500);
+	}
+
+	.product-title {
+		font-size: 14px;
+		color: var(--gray-800);
+		font-weight: 500;
+	}
+
+	.product-description {
+		font-size: 12px;
+		color: var(--text-color);
+		margin: 20px 0;
+		display: -webkit-box;
+		-webkit-line-clamp: 6;
+		-webkit-box-orient: vertical;
+
+		p {
+			margin-bottom: 0.5rem;
+		}
+	}
+
+	.product-category {
+		font-size: 13px;
+		color: var(--text-muted);
+		margin: var(--margin-sm) 0;
+	}
+
+	.product-price {
+		font-size: 18px;
+		font-weight: 600;
+		color: var(--text-color);
+		margin: var(--margin-sm) 0;
+	}
+
+	.item-card {
+		padding: var(--padding-sm);
+	}
+}
+
+[data-doctype="Item Group"],
+#page-all-products {
+	.page-header {
+		font-size: 20px;
+		font-weight: 700;
+		color: var(--text-color);
+	}
+
+	.filters-section {
+		.title-section {
+			border-bottom: 1px solid var(--table-border-color);
+		}
+
+		.filter-title {
+			font-weight: 500;
+		}
+
+		.clear-filters {
+			font-size: 13px;
+		}
+
+		.filter-label {
+			font-size: 11px;
+			font-weight: 600;
+			color: var(--gray-700);
+			text-transform: uppercase;
+		}
+
+		.filter-block {
+			border-bottom: 1px solid var(--table-border-color);
+		}
+
+		.checkbox {
+			.label-area {
+				font-size: 13px;
+				color: var(--gray-800);
+			}
+		}
+	}
+}
+
+.product-container {
+	@include card($padding: var(--padding-md));
+	min-height: 70vh;
+
+	.product-details {
+		max-width: 40%;
+		margin-left: -30px;
+
+		.btn-add-to-cart {
+			font-size: var(--text-base);
+		}
+	}
+
+	.product-title {
+		font-size: 24px;
+		font-weight: 600;
+		color: var(--text-color);
+	}
+
+	.product-code {
+		color: var(--text-muted);
+		font-size: 13px;
+	}
+
+	.product-description {
+		font-size: 13px;
+		color: var(--gray-800);
+	}
+
+	.product-image {
+		border-color: var(--table-border-color) !important;
+		padding: 15px;
+
+		@include media-breakpoint-between(xs, md) {
+			height: 300px;
+			width: 300px;
+		}
+
+		@include media-breakpoint-up(lg) {
+			height: 350px;
+			width: 350px;
+		}
+
+	 	img {
+			object-fit: contain;
+		}
+	}
+
+	.item-slideshow {
+		@include media-breakpoint-between(xs, md) {
+			max-height: 320px;
+		}
+
+		@include media-breakpoint-up(lg) {
+			max-height: 430px;
+		}
+
+		overflow: scroll;
+	}
+
+	.item-slideshow-image {
+		height: 4rem;
+		width: 6rem;
+		object-fit: contain;
+		padding: 0.5rem;
+		border: 1px solid var(--table-border-color);
+		border-radius: 4px;
+		cursor: pointer;
+
+		&:hover, &.active {
+			border-color: $primary;
+		}
+	}
+
+	.item-cart {
+		.product-price {
+			font-size: 20px;
+			color: var(--text-color);
+			font-weight: 600;
+
+			.formatted-price {
+				color: var(--text-muted);
+				font-size: var(--text-base);
+			}
+		}
+
+		.no-stock {
+			font-size: var(--text-base);
+		}
+	}
+}
+
+.item-configurator-dialog {
+	.modal-header {
+		padding: var(--padding-md) var(--padding-xl);
+	}
+
+	.modal-body {
+		padding: 0 var(--padding-xl);
+		padding-bottom: var(--padding-xl);
+
+		.status-area {
+			.alert {
+				padding: var(--padding-xs) var(--padding-sm);
+				font-size: var(--text-sm);
+			}
+		}
+
+		.form-layout {
+			max-height: 50vh;
+			overflow-y: auto;
+		}
+
+		.section-body {
+			.form-column {
+				.form-group {
+					.control-label {
+						font-size: var(--text-md);
+						color: var(--gray-700);
+					}
+
+					.help-box {
+						margin-top: 2px;
+						font-size: var(--text-sm);
+					}
+				}
+			}
+		}
+
+		svg {
+			display: none;
+		}
+	}
+}
+
+.item-group-slideshow {
+	.item-group-description {
+		// max-width: 900px;
+	}
+
+	.carousel-inner.rounded-carousel {
+		border-radius: $card-border-radius;
+	}
+}
+
+.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%;
+	}
+}
+
+
+#page-cart {
+	.shopping-cart-header {
+		font-weight: bold;
+	}
+
+	.cart-container {
+		color: var(--text-color);
+
+		.frappe-card {
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+		}
+
+		.cart-items-header {
+			font-weight: 600;
+		}
+
+		.cart-table {
+			th, tr, td {
+				border-color: var(--border-color);
+				border-width: 1px;
+			}
+
+			th {
+				font-weight: normal;
+				font-size: 13px;
+				color: var(--text-muted);
+				padding: var(--padding-sm) 0;
+			}
+
+			td {
+				padding: var(--padding-sm) 0;
+				color: var(--text-color);
+			}
+
+			.cart-items {
+				.item-title {
+					font-size: var(--text-base);
+					font-weight: 500;
+					color: var(--text-color);
+				}
+
+				.item-subtitle {
+					color: var(--text-muted);
+					font-size: var(--text-md);
+				}
+
+				.item-subtotal {
+					font-size: var(--text-base);
+					font-weight: 500;
+				}
+
+				.item-rate {
+					font-size: var(--text-md);
+					color: var(--text-muted);
+				}
+
+				textarea {
+					width: 40%;
+				}
+			}
+
+			.cart-tax-items {
+				.item-grand-total {
+					font-size: 16px;
+					font-weight: 600;
+					color: var(--text-color);
+				}
+			}
+		}
+
+		.cart-addresses {
+			hr {
+				border-color: var(--border-color);
+			}
+		}
+
+		.number-spinner {
+			width: 75%;
+			.cart-btn {
+				border: none;
+				background: var(--gray-100);
+				box-shadow: none;
+				height: 28px;
+				align-items: center;
+				display: flex;
+			}
+
+			.cart-qty {
+				height: 28px;
+				font-size: var(--text-md);
+			}
+		}
+
+		.place-order-container {
+			.btn-place-order {
+				width: 62%;
+			}
+		}
+	}
+}
+
+.cart-empty.frappe-card {
+	min-height: 76vh;
+	@include flex(flex, center, center, column);
+
+	.cart-empty-message {
+		font-size: 18px;
+		color: var(--text-color);
+		font-weight: bold;
+	}
+}
+
+.address-card {
+	.card-title {
+		font-size: var(--text-base);
+		font-weight: 500;
+	}
+
+	.card-text {
+		font-size: var(--text-md);
+		color: var(--gray-700);
+	}
+
+	.card-link {
+		font-size: var(--text-md);
+
+		svg use {
+			stroke: var(--blue-500);
+		}
+	}
+
+	.btn-change-address {
+		color: var(--blue-500);
+		box-shadow: none;
+		border: 1px solid var(--blue-500);
+	}
+}
+
+.modal .address-card {
+	.card-body {
+		padding: var(--padding-sm);
+		border-radius: var(--border-radius);
+		border: 1px solid var(--dark-border-color);
+	}
+}
+
diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss
index 24a1b37..56b717c 100644
--- a/erpnext/public/scss/website.scss
+++ b/erpnext/public/scss/website.scss
@@ -1,29 +1,10 @@
 @import "frappe/public/scss/website/variables";
 
-.product-image img {
-	min-height: 20rem;
-	max-height: 30rem;
-}
-
 .filter-options {
 	max-height: 300px;
 	overflow: auto;
 }
 
-.item-slideshow-image {
-	height: 3rem;
-	width: 3rem;
-	object-fit: contain;
-	padding: 0.5rem;
-	border: 1px solid $border-color;
-	border-radius: 4px;
-	cursor: pointer;
-
-	&:hover, &.active {
-		border-color: $primary;
-	}
-}
-
 .address-card {
 	cursor: pointer;
 	position: relative;
@@ -43,10 +24,10 @@
 
 .check {
 	display: inline-flex;
-    padding: 0.25rem;
-    background: $primary;
-    color: white;
-    border-radius: 50%;
+	padding: 0.25rem;
+	background: $primary;
+	color: white;
+	border-radius: 50%;
 	font-size: 12px;
 	width: 24px;
 	height: 24px;
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index 9892dc3..1413cb2 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -61,6 +61,19 @@
 				frappe.set_route("List", "Item", {"item_group": frm.doc.name});
 			});
 		}
+		
+		frappe.model.with_doctype('Item', () => {
+			const item_meta = frappe.get_meta('Item');
+
+			const valid_fields = item_meta.fields.filter(
+				df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
+			).map(df => ({ label: df.label, value: df.fieldname }));
+
+			const field = frappe.meta.get_docfield("Website Filter Field", "fieldname", frm.docname);
+			field.fieldtype = 'Select';
+			field.options = valid_fields;
+			frm.fields_dict.filter_fields.grid.refresh();
+		});
 	},
 
 	set_root_readonly: function(frm) {
diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json
index 004421d..31624ed 100644
--- a/erpnext/setup/doctype/item_group/item_group.json
+++ b/erpnext/setup/doctype/item_group/item_group.json
@@ -24,8 +24,12 @@
   "route",
   "weightage",
   "slideshow",
+  "website_title",
   "description",
   "website_specifications",
+  "website_filters_section",
+  "filter_fields",
+  "filter_attributes",
   "lft",
   "rgt",
   "old_parent"
@@ -180,6 +184,28 @@
    "options": "Item Group",
    "print_hide": 1,
    "report_hide": 1
+  },
+  {
+   "fieldname": "website_filters_section",
+   "fieldtype": "Section Break",
+   "label": "Website Filters"
+  },
+  {
+   "fieldname": "filter_fields",
+   "fieldtype": "Table",
+   "label": "Item Fields",
+   "options": "Website Filter Field"
+  },
+  {
+   "fieldname": "filter_attributes",
+   "fieldtype": "Table",
+   "label": "Attributes",
+   "options": "Website Attribute"
+  },
+  {
+   "fieldname": "website_title",
+   "fieldtype": "Data",
+   "label": "Title"
   }
  ],
  "icon": "fa fa-sitemap",
@@ -188,7 +214,7 @@
  "is_tree": 1,
  "links": [],
  "max_attachments": 3,
- "modified": "2020-03-18 18:10:34.383363",
+ "modified": "2020-12-30 12:57:38.876956",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Item Group",
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 4377840..bff806d 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -13,13 +13,16 @@
 from erpnext.shopping_cart.product_info import set_product_info_for_website
 from erpnext.utilities.product import get_qty_in_stock
 from six.moves.urllib.parse import quote
+from erpnext.shopping_cart.product_query import ProductQuery
+from erpnext.shopping_cart.filters import ProductFiltersBuilder
 
 class ItemGroup(NestedSet, WebsiteGenerator):
 	nsm_parent_field = 'parent_item_group'
 	website = frappe._dict(
 		condition_field = "show_in_website",
 		template = "templates/generators/item_group.html",
-		no_cache = 1
+		no_cache = 1,
+		no_breadcrumbs = 1
 	)
 
 	def autoname(self):
@@ -70,18 +73,58 @@
 		context.page_length = cint(frappe.db.get_single_value('Products Settings', 'products_per_page')) or 6
 		context.search_link = '/product_search'
 
-		start = int(frappe.form_dict.start or 0)
-		if start < 0:
+		if frappe.form_dict:
+			search = frappe.form_dict.search
+			field_filters = frappe.parse_json(frappe.form_dict.field_filters)
+			attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters)
+			start = frappe.parse_json(frappe.form_dict.start)
+		else:
+			search = None
+			attribute_filters = None
+			field_filters = {}
 			start = 0
+
+		if not field_filters:
+			field_filters = {}
+
+		# Ensure the query remains within current item group
+		field_filters['item_group'] = self.name
+
+		engine = ProductQuery()
+		context.items = engine.query(attribute_filters, field_filters, search, start)
+
+		filter_engine = ProductFiltersBuilder(self.name)
+
+		context.field_filters = filter_engine.get_field_filters()
+		context.attribute_filters = filter_engine.get_attribute_fitlers()
+
 		context.update({
-			"items": get_product_list_for_group(product_group = self.name, start=start,
-				limit=context.page_length + 1, search=frappe.form_dict.get("search")),
 			"parents": get_parent_item_groups(self.parent_item_group),
 			"title": self.name
 		})
 
 		if self.slideshow:
-			context.update(get_slideshow(self))
+			values = {
+				'show_indicators': 1,
+				'show_controls': 0,
+				'rounded': 1,
+				'slider_name': self.slideshow
+			}
+			slideshow = frappe.get_doc("Website Slideshow", self.slideshow)
+			slides = slideshow.get({"doctype":"Website Slideshow Item"})
+			for index, slide in enumerate(slides):
+				values[f"slide_{index + 1}_image"] = slide.image
+				values[f"slide_{index + 1}_title"] = slide.heading
+				values[f"slide_{index + 1}_subtitle"] = slide.description
+				values[f"slide_{index + 1}_theme"] = slide.theme or "Light"
+				values[f"slide_{index + 1}_content_align"] = slide.content_align or "Centre"
+				values[f"slide_{index + 1}_primary_action_label"] = slide.label
+				values[f"slide_{index + 1}_primary_action"] = slide.url
+
+			context.slideshow = values
+
+		context.breadcrumbs = 0
+		context.title = self.website_title or self.name
 
 		return context
 
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index c2549fe..fa9dced 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -42,15 +42,31 @@
 
 	return {
 		"doc": decorate_quotation_doc(doc),
-		"shipping_addresses": [{"name": address.name, "title": address.address_title, "display": address.display}
-			for address in addresses if address.address_type == "Shipping"],
-		"billing_addresses": [{"name": address.name, "title": address.address_title, "display": address.display}
-			for address in addresses if address.address_type == "Billing"],
+		"shipping_addresses": get_shipping_addresses(party),
+		"billing_addresses": get_billing_addresses(party),
 		"shipping_rules": get_applicable_shipping_rules(party),
 		"cart_settings": frappe.get_cached_doc("Shopping Cart Settings")
 	}
 
 @frappe.whitelist()
+def get_shipping_addresses(party=None):
+	if not party:
+		party = get_party()
+	addresses = get_address_docs(party=party)
+	return [{"name": address.name, "title": address.address_title, "display": address.display}
+		for address in addresses if address.address_type == "Shipping"
+	]
+
+@frappe.whitelist()
+def get_billing_addresses(party=None):
+	if not party:
+		party = get_party()
+	addresses = get_address_docs(party=party)
+	return [{"name": address.name, "title": address.address_title, "display": address.display}
+		for address in addresses if address.address_type == "Billing"
+	]
+
+@frappe.whitelist()
 def place_order():
 	quotation = _get_cart_quotation()
 	cart_settings = frappe.db.get_value("Shopping Cart Settings", None,
@@ -203,27 +219,33 @@
 @frappe.whitelist()
 def update_cart_address(address_type, address_name):
 	quotation = _get_cart_quotation()
-	address_display = get_address_display(frappe.get_doc("Address", address_name).as_dict())
+	address_doc = frappe.get_doc("Address", address_name).as_dict()
+	address_display = get_address_display(address_doc)
 
 	if address_type.lower() == "billing":
 		quotation.customer_address = address_name
 		quotation.address_display = address_display
 		quotation.shipping_address_name == quotation.shipping_address_name or address_name
+		address_doc = next((doc for doc in get_billing_addresses() if doc["name"] == address_name), None)
 	elif address_type.lower() == "shipping":
 		quotation.shipping_address_name = address_name
 		quotation.shipping_address = address_display
 		quotation.customer_address == quotation.customer_address or address_name
-
+		address_doc = next((doc for doc in get_shipping_addresses() if doc["name"] == address_name), None)
 	apply_cart_settings(quotation=quotation)
 
 	quotation.flags.ignore_permissions = True
 	quotation.save()
 
 	context = get_cart_quotation(quotation)
+	context['address'] = address_doc
+
 	return {
 		"taxes": frappe.render_template("templates/includes/order/order_taxes.html",
 			context),
-		}
+		"address": frappe.render_template("templates/includes/cart/address_card.html",
+			context)
+	}
 
 def guess_territory():
 	territory = None
diff --git a/erpnext/shopping_cart/filters.py b/erpnext/shopping_cart/filters.py
new file mode 100644
index 0000000..6c63d87
--- /dev/null
+++ b/erpnext/shopping_cart/filters.py
@@ -0,0 +1,82 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _dict
+
+class ProductFiltersBuilder:
+	def __init__(self, item_group=None):
+		if not item_group or item_group == "Products Settings":
+			self.doc = frappe.get_doc("Products Settings")
+		else:
+			self.doc = frappe.get_doc("Item Group", item_group)
+
+		self.item_group = item_group
+
+	def get_field_filters(self):
+		filter_fields = [row.fieldname for row in self.doc.filter_fields]
+
+		meta = frappe.get_meta('Item')
+		fields = [df for df in meta.fields if df.fieldname in filter_fields]
+
+		filter_data = []
+		for df in fields:
+			filters = {}
+			if df.fieldtype == "Link":
+				if self.item_group:
+					filters['item_group'] = self.item_group
+
+				values =  frappe.get_all("Item", fields=[df.fieldname], filters=filters, distinct="True", pluck=df.fieldname)
+			else:
+				doctype = df.get_link_doctype()
+
+				# apply enable/disable/show_in_website filter
+				meta = frappe.get_meta(doctype)
+
+				if meta.has_field('enabled'):
+					filters['enabled'] = 1
+				if meta.has_field('disabled'):
+					filters['disabled'] = 0
+				if meta.has_field('show_in_website'):
+					filters['show_in_website'] = 1
+
+				values = [d.name for d in frappe.get_all(doctype, filters)]
+
+			# Remove None
+			values = values.remove(None) if None in values else values
+			if values:
+				filter_data.append([df, values])
+
+		return filter_data
+
+	def get_attribute_fitlers(self):
+		attributes = [row.attribute for row in self.doc.filter_attributes]
+		attribute_docs = [
+			frappe.get_doc('Item Attribute', attribute) for attribute in attributes
+		]
+
+		valid_attributes = []
+
+		for attr_doc in attribute_docs:
+			selected_attributes = []
+			for attr in attr_doc.item_attribute_values:
+				filters= [
+					["Item Variant Attribute", "attribute", "=", attr.parent],
+					["Item Variant Attribute", "attribute_value", "=", attr.attribute_value]
+				]
+				if self.item_group:
+					filters.append(["item_group", "=", self.item_group])
+
+				if frappe.db.get_all("Item", filters, limit=1):
+					selected_attributes.append(attr)
+
+			if selected_attributes:
+				valid_attributes.append(
+					_dict(
+						item_attribute_values=selected_attributes,
+						name=attr_doc.name
+					)
+				)
+
+		return valid_attributes
diff --git a/erpnext/shopping_cart/product_query.py b/erpnext/shopping_cart/product_query.py
new file mode 100644
index 0000000..da9e798
--- /dev/null
+++ b/erpnext/shopping_cart/product_query.py
@@ -0,0 +1,120 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+from erpnext.shopping_cart.product_info import get_product_info_for_website
+
+class ProductQuery:
+	"""Query engine for product listing
+
+	Attributes:
+	    cart_settings (Document): Settings for Cart
+	    fields (list): Fields to fetch in query
+	    filters (TYPE): Description
+	    or_filters (list): Description
+	    page_length (Int): Length of page for the query
+	    settings (Document): Products Settings DocType
+	    filters (list)
+	    or_filters (list)
+	"""
+
+	def __init__(self):
+		self.settings = frappe.get_doc("Products Settings")
+		self.cart_settings = frappe.get_doc("Shopping Cart Settings")
+		self.page_length = self.settings.products_per_page or 20
+		self.fields = ['name', 'item_name', 'item_code', 'website_image', 'variant_of', 'has_variants', 'item_group', 'image', 'web_long_description', 'description', 'route']
+		self.filters = [['show_in_website', '=', 1]]
+		self.or_filters = []
+
+	def query(self, attributes=None, fields=None, search_term=None, start=0):
+		"""Summary
+
+		Args:
+		    attributes (dict, optional): Item Attribute filters
+		    fields (dict, optional): Field level filters
+		    search_term (str, optional): Search term to lookup
+		    start (int, optional): Page start
+
+		Returns:
+		    list: List of results with set fields
+		"""
+		if fields: self.build_fields_filters(fields)
+		if search_term: self.build_search_filters(search_term)
+
+		result = []
+
+		if attributes:
+			all_items = []
+			for attribute, values in attributes.items():
+				if not isinstance(values, list):
+					values = [values]
+
+				items = frappe.get_all(
+					"Item",
+					fields=self.fields,
+					filters=[
+						*self.filters,
+						["Item Variant Attribute", "attribute", "=", attribute],
+						["Item Variant Attribute", "attribute_value", "in", values],
+					],
+					or_filters=self.or_filters,
+					start=start,
+					limit=self.page_length
+				)
+
+				items_dict = {item.name: item for item in items}
+				# TODO: Replace Variants by their parent templates
+
+				all_items.append(set(items_dict.keys()))
+
+			result = [items_dict.get(item) for item in list(set.intersection(*all_items))]
+		else:
+			result = frappe.get_all("Item", fields=self.fields, filters=self.filters, or_filters=self.or_filters, start=start, limit=self.page_length)
+
+		for item in result:
+			product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get('product_info')
+			item.formatted_price = product_info['price'].get('formatted_price') if product_info['price'] else None
+
+		return result
+
+	def build_fields_filters(self, filters):
+		"""Build filters for field values
+
+		Args:
+		    filters (dict): Filters
+		"""
+		for field, values in filters.items():
+			if not values:
+				continue
+
+			if isinstance(values, list):
+				# If value is a list use `IN` query
+				self.filters.append([field, 'IN', values])
+			else:
+				# `=` will be faster than `IN` for most cases
+				self.filters.append([field, '=', values])
+
+	def build_search_filters(self, search_term):
+		"""Query search term in specified fields
+
+		Args:
+		    search_term (str): Search candidate
+		"""
+		# Default fields to search from
+		default_fields = {'name', 'item_name', 'description', 'item_group'}
+
+		# Get meta search fields
+		meta = frappe.get_meta("Item")
+		meta_fields = set(meta.get_search_fields())
+
+		# Join the meta fields and default fields set
+		search_fields = default_fields.union(meta_fields)
+		try:
+			if frappe.db.count('Item', cache=True) > 50000:
+				search_fields.remove('description')
+		except KeyError:
+			pass
+
+		# Build or filters for query
+		search = '%{}%'.format(search_term)
+		self.or_filters += [[field, 'like', search] for field in search_fields]
diff --git a/erpnext/shopping_cart/search.py b/erpnext/shopping_cart/search.py
new file mode 100644
index 0000000..012d09f
--- /dev/null
+++ b/erpnext/shopping_cart/search.py
@@ -0,0 +1,127 @@
+import frappe
+from frappe.search.full_text_search import FullTextSearch
+from whoosh.fields import TEXT, ID, KEYWORD, Schema
+from frappe.website.render import render_page
+from frappe.utils import strip_html_tags
+from whoosh.qparser import MultifieldParser, FieldsPlugin, WildcardPlugin
+from whoosh.analysis import StemmingAnalyzer
+from whoosh.query import Prefix
+
+INDEX_NAME = "products"
+
+class ProductSearch(FullTextSearch):
+	""" Wrapper for WebsiteSearch """
+
+	def get_schema(self):
+		return Schema(
+			title=TEXT(stored=True, field_boost=1.5),
+			name=ID(stored=True),
+			path=ID(stored=True),
+			content=TEXT(stored=True, analyzer=StemmingAnalyzer()),
+			keywords=KEYWORD(stored=True, scorable=True, commas=True),
+		)
+
+	def get_id(self):
+		return "name"
+
+	def get_items_to_index(self):
+		"""Get all routes to be indexed, this includes the static pages
+		in www/ and routes from published documents
+
+		Returns:
+			self (object): FullTextSearch Instance
+		"""
+		items = get_all_published_items()
+		documents = [self.get_document_to_index(item) for item in items]
+		return documents
+
+	def get_document_to_index(self, item):
+		try:
+			item = frappe.get_doc("Item", item)
+			title = item.item_name
+			keywords = [item.item_group]
+
+			if item.brand:
+				keywords.append(item.brand)
+
+			if item.website_image_alt:
+				keywords.append(item.website_image_alt)
+
+			if item.has_variants and item.variant_based_on == "Item Attribute":
+				keywords = keywords + [attr.attribute for attr in item.attributes]
+
+			if item.web_long_description:
+				content = strip_html_tags(item.web_long_description)
+			elif description:
+				content = strip_html_tags(item.description)
+
+			return frappe._dict(
+				title=title,
+				name=item.name,
+				path=item.route,
+				content=content,
+				keywords=", ".join(keywords),
+			)
+		except Exception:
+			pass
+
+	def search(self, text, scope=None, limit=20):
+		"""Search from the current index
+
+		Args:
+			text (str): String to search for
+			scope (str, optional): Scope to limit the search. Defaults to None.
+			limit (int, optional): Limit number of search results. Defaults to 20.
+
+		Returns:
+			[List(_dict)]: Search results
+		"""
+		ix = self.get_index()
+
+		results = None
+		out = []
+
+		with ix.searcher() as searcher:
+			parser = MultifieldParser(["title", "content", "keywords"], ix.schema)
+			parser.remove_plugin_class(FieldsPlugin)
+			parser.remove_plugin_class(WildcardPlugin)
+			query = parser.parse(text)
+
+			filter_scoped = None
+			if scope:
+				filter_scoped = Prefix(self.id, scope)
+			results = searcher.search(query, limit=limit, filter=filter_scoped)
+
+			for r in results:
+				out.append(self.parse_result(r))
+
+		return out
+
+	def parse_result(self, result):
+		title_highlights = result.highlights("title")
+		content_highlights = result.highlights("content")
+		keyword_highlights = result.highlights("keywords")
+
+		return frappe._dict(
+			title=result["title"],
+			path=result["path"],
+			keywords=result["keywords"],
+			title_highlights=title_highlights,
+			content_highlights=content_highlights,
+			keyword_highlights=keyword_highlights,
+		)
+
+def get_all_published_items():
+	return frappe.get_all("Item", filters={"variant_of": "", "show_in_website": 1},pluck="name")
+
+def update_index_for_path(path):
+	search = ProductSearch(INDEX_NAME)
+	return search.update_index_by_name(path)
+
+def remove_document_from_index(path):
+	search = ProductSearch(INDEX_NAME)
+	return search.remove_document_from_index(path)
+
+def build_index_for_all_routes():
+	search = ProductSearch(INDEX_NAME)
+	return search.build()
\ No newline at end of file
diff --git a/erpnext/shopping_cart/web_template/__init__.py b/erpnext/shopping_cart/web_template/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/__init__.py
diff --git a/erpnext/shopping_cart/web_template/hero_slider/__init__.py b/erpnext/shopping_cart/web_template/hero_slider/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/hero_slider/__init__.py
diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html
new file mode 100644
index 0000000..1b39534
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html
@@ -0,0 +1,85 @@
+{%- macro slide(image, title, subtitle, action, label, index, align="Left", theme="Dark") -%}
+{%- set align_class = resolve_class({
+	'text-right': align == 'Right',
+	'text-centre': align == 'Center',
+	'text-left': align == 'Left',
+}) -%}
+
+{%- set heading_class = resolve_class({
+	'text-white': theme == 'Dark',
+	'': theme == 'Light',
+}) -%}
+<div class="carousel-item {{ 'active' if index=='1' else ''}}" style="height: 450px;">
+	<img class="d-block h-100 w-100" style="object-fit: cover;" src="{{ image }}" alt="{{ title }}">
+	{%- if title or subtitle -%}
+	<div class="carousel-body container d-flex {{ align_class }}">
+		<div class="carousel-content align-self-center">
+			{%- if title -%}<h1 class="{{ heading_class }}">{{ title }}</h1>{%- endif -%}
+			{%- if subtitle -%}<p class="text-muted mt-2">{{ subtitle }}</p>{%- endif -%}
+			{%- if action -%}
+			<a href="{{ action }}" class="btn btn-primary mt-3">
+				{{ label }}
+			</a>
+			{%- endif -%}
+		</div>
+	</div>
+	{%- endif -%}
+</div>
+{%- endmacro -%}
+
+<div id="{{ slider_name }}" class="section-carousel carousel slide" data-ride="carousel">
+	{%- if show_indicators -%}
+	<ol class="carousel-indicators">
+		{%- for index in ['1', '2', '3', '4', '5'] -%}
+		{%- if values['slide_' + index + '_image'] -%}
+			<li data-target="#{{ slider_name }}" data-slide-to="{{ frappe.utils.cint(index) - 1 }}" class="{{ 'active' if index=='1' else ''}}"></li>
+		{%- endif -%}
+		{%- endfor -%}
+	</ol>
+	{%- endif -%}
+	<div class="carousel-inner {{ resolve_class({'rounded-carousel': rounded }) }}">
+		{%- for index in ['1', '2', '3', '4', '5'] -%}
+			{%- set image = values['slide_' + index + '_image'] -%}
+			{%- set title = values['slide_' + index + '_title'] -%}
+			{%- set subtitle = values['slide_' + index + '_subtitle'] -%}
+			{%- set primary_action = values['slide_' + index + '_primary_action'] -%}
+			{%- set primary_action_label = values['slide_' + index + '_primary_action_label'] -%}
+			{%- set align = values['slide_' + index + '_content_align'] -%}
+			{%- set theme = values['slide_' + index + '_theme'] -%}
+
+			{%- if image -%}
+				{{ slide(image, title, subtitle, primary_action, primary_action_label, index, align, theme) }}
+			{%- endif -%}
+
+		{%- endfor -%}
+	</div>
+	{%- if show_controls -%}
+	<a class="carousel-control-prev" href="#{{ slider_name }}" role="button" data-slide="prev">
+		<div class="carousel-control">
+			<svg class="mr-1" width="20" height="20" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+				<path d="M11.625 3.75L6.375 9L11.625 14.25" stroke="#4C5A67" stroke-linecap="round" stroke-linejoin="round"/>
+			</svg>
+		</div>
+		<span class="sr-only">Previous</span>
+	</a>
+	<a class="carousel-control-next" href="#{{ slider_name }}" role="button" data-slide="next">
+		<div class="carousel-control">
+			<svg class="ml-1" width="20" height="20" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+				<path d="M6.375 14.25L11.625 9L6.375 3.75" stroke="#4C5A67" stroke-linecap="round" stroke-linejoin="round"/>
+			</svg>
+		</div>
+		<span class="sr-only">Next</span>
+	</a>
+	{%- endif -%}
+</div>
+
+<script type="text/javascript">
+	$('.carousel').carousel({
+		interval: false,
+		pause: "hover",
+		wrap: true
+	})
+</script>
+
+<style>
+</style>
\ No newline at end of file
diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json
new file mode 100644
index 0000000..04fb1d2
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.json
@@ -0,0 +1,284 @@
+{
+ "creation": "2020-11-17 15:21:51.207221",
+ "docstatus": 0,
+ "doctype": "Web Template",
+ "fields": [
+  {
+   "fieldname": "slider_name",
+   "fieldtype": "Data",
+   "label": "Slider Name",
+   "reqd": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "show_indicators",
+   "fieldtype": "Check",
+   "label": "Show Indicators",
+   "reqd": 0
+  },
+  {
+   "default": "1",
+   "fieldname": "show_controls",
+   "fieldtype": "Check",
+   "label": "Show Controls",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1",
+   "fieldtype": "Section Break",
+   "label": "Slide 1",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1_image",
+   "fieldtype": "Attach Image",
+   "label": "Image",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1_title",
+   "fieldtype": "Data",
+   "label": "Title",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1_subtitle",
+   "fieldtype": "Small Text",
+   "label": "Subtitle",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1_primary_action_label",
+   "fieldtype": "Data",
+   "label": "Primary Action Label",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1_primary_action",
+   "fieldtype": "Data",
+   "label": "Primary Action",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1_content_align",
+   "fieldtype": "Select",
+   "label": "Content Align",
+   "options": "Left\nCentre\nRight",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_1_theme",
+   "fieldtype": "Select",
+   "label": "Slide Theme",
+   "options": "Dark\nLight",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_2",
+   "fieldtype": "Section Break",
+   "label": "Slide 2",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_2_image",
+   "fieldtype": "Attach Image",
+   "label": "Image ",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_2_title",
+   "fieldtype": "Data",
+   "label": "Title ",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_2_subtitle",
+   "fieldtype": "Small Text",
+   "label": "Subtitle ",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_2_primary_action_label",
+   "fieldtype": "Data",
+   "label": "Primary Action Label ",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_2_primary_action",
+   "fieldtype": "Data",
+   "label": "Primary Action ",
+   "reqd": 0
+  },
+  {
+   "default": "Left",
+   "fieldname": "slide_2_content_align",
+   "fieldtype": "Select",
+   "label": "Content Align",
+   "options": "Left\nCentre\nRight",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_2_theme",
+   "fieldtype": "Select",
+   "label": "Slide Theme",
+   "options": "Dark\nLight",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3",
+   "fieldtype": "Section Break",
+   "label": "Slide 3",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3_image",
+   "fieldtype": "Attach Image",
+   "label": "Image",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3_title",
+   "fieldtype": "Data",
+   "label": "Title",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3_subtitle",
+   "fieldtype": "Small Text",
+   "label": "Subtitle",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3_primary_action_label",
+   "fieldtype": "Data",
+   "label": "Primary Action Label",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3_primary_action",
+   "fieldtype": "Data",
+   "label": "Primary Action",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3_content_align",
+   "fieldtype": "Select",
+   "label": "Content Align",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_3_theme",
+   "fieldtype": "Select",
+   "label": "Slide Theme",
+   "options": "Dark\nLight",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4",
+   "fieldtype": "Section Break",
+   "label": "Slide 4",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4_image",
+   "fieldtype": "Attach Image",
+   "label": "Image",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4_title",
+   "fieldtype": "Data",
+   "label": "Title",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4_subtitle",
+   "fieldtype": "Small Text",
+   "label": "Subtitle",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4_primary_action_label",
+   "fieldtype": "Data",
+   "label": "Primary Action Label",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4_primary_action",
+   "fieldtype": "Data",
+   "label": "Primary Action",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4_content_align",
+   "fieldtype": "Select",
+   "label": "Content Align",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_4_theme",
+   "fieldtype": "Select",
+   "label": "Slide Theme",
+   "options": "Dark\nLight",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5",
+   "fieldtype": "Section Break",
+   "label": "Slide 5",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5_image",
+   "fieldtype": "Attach Image",
+   "label": "Image",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5_title",
+   "fieldtype": "Data",
+   "label": "Title",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5_subtitle",
+   "fieldtype": "Small Text",
+   "label": "Subtitle",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5_primary_action_label",
+   "fieldtype": "Data",
+   "label": "Primary Action Label",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5_primary_action",
+   "fieldtype": "Data",
+   "label": "Primary Action",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5_content_align",
+   "fieldtype": "Select",
+   "label": "Content Align",
+   "reqd": 0
+  },
+  {
+   "fieldname": "slide_5_theme",
+   "fieldtype": "Select",
+   "label": "Slide Theme",
+   "options": "Dark\nLight",
+   "reqd": 0
+  }
+ ],
+ "idx": 2,
+ "modified": "2020-12-29 12:30:02.794994",
+ "modified_by": "Administrator",
+ "module": "Shopping Cart",
+ "name": "Hero Slider",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "type": "Section"
+}
\ No newline at end of file
diff --git a/erpnext/shopping_cart/web_template/item_card_group/__init__.py b/erpnext/shopping_cart/web_template/item_card_group/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/item_card_group/__init__.py
diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html
new file mode 100644
index 0000000..890ae50
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html
@@ -0,0 +1,38 @@
+{% from "erpnext/templates/includes/macros.html" import item_card, item_card_body %}
+
+<div class="section-with-cards item-card-group-section">
+	<div class="item-group-header d-flex justify-content-between">
+		<div class="title-section">
+			{%- if title -%}
+			<h2 class="section-title">{{ title }}</h2>
+			{%- endif -%}
+			{%- if subtitle -%}
+			<p class="section-description">{{ subtitle }}</p>
+			{%- endif -%}
+		</div>
+		<div class="primary-action-section">
+			{%- if primary_action -%}
+			<a href="{{ action }}" class="btn btn-primary pull-right">
+				{{ primary_action_label }}
+			</a>
+			{%- endif -%}
+		</div>
+	</div>
+
+	<div class="row">
+		{%- for index in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] -%}
+		{%- set item = values['card_' + index + '_item'] -%}
+			{%- if item -%}
+				{%- set item = frappe.get_doc("Item", item) -%}
+				{{ item_card(
+					item.item_name, item.image, item.route, item.description,
+					None, item.item_group, values['card_' + index + '_featured'],
+					True, "Center"
+				) }}
+			{%- endif -%}
+		{%- endfor -%}
+	</div>
+</div>
+
+<style>
+</style>
\ No newline at end of file
diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json
new file mode 100644
index 0000000..ad087b0
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.json
@@ -0,0 +1,273 @@
+{
+ "__unsaved": 1,
+ "creation": "2020-11-17 15:35:05.285322",
+ "docstatus": 0,
+ "doctype": "Web Template",
+ "fields": [
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "label": "Title",
+   "reqd": 1
+  },
+  {
+   "fieldname": "subtitle",
+   "fieldtype": "Data",
+   "label": "Subtitle",
+   "reqd": 0
+  },
+  {
+   "__unsaved": 1,
+   "fieldname": "primary_action_label",
+   "fieldtype": "Data",
+   "label": "Primary Action Label",
+   "reqd": 0
+  },
+  {
+   "__islocal": 1,
+   "__unsaved": 1,
+   "fieldname": "primary_action",
+   "fieldtype": "Data",
+   "label": "Primary Action",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_1",
+   "fieldtype": "Section Break",
+   "label": "Card 1",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_1_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_1_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_2",
+   "fieldtype": "Section Break",
+   "label": "Card 2",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_2_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_2_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_3",
+   "fieldtype": "Section Break",
+   "label": "Card 3",
+   "options": "",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_3_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_3_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_4",
+   "fieldtype": "Section Break",
+   "label": "Card 4",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_4_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_4_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_5",
+   "fieldtype": "Section Break",
+   "label": "Card 5",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_5_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_5_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_6",
+   "fieldtype": "Section Break",
+   "label": "Card 6",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_6_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_6_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_7",
+   "fieldtype": "Section Break",
+   "label": "Card 7",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_7_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_7_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_8",
+   "fieldtype": "Section Break",
+   "label": "Card 8",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_8_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_8_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_9",
+   "fieldtype": "Section Break",
+   "label": "Card 9",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_9_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_9_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_10",
+   "fieldtype": "Section Break",
+   "label": "Card 10",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_10_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_10_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_11",
+   "fieldtype": "Section Break",
+   "label": "Card 11",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_11_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_11_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_12",
+   "fieldtype": "Section Break",
+   "label": "Card 12",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_12_item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "fieldname": "card_12_featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "reqd": 0
+  }
+ ],
+ "idx": 0,
+ "modified": "2020-11-19 18:48:52.633045",
+ "modified_by": "Administrator",
+ "module": "Shopping Cart",
+ "name": "Item Card Group",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "type": "Section"
+}
\ No newline at end of file
diff --git a/erpnext/shopping_cart/web_template/product_card/__init__.py b/erpnext/shopping_cart/web_template/product_card/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/product_card/__init__.py
diff --git a/erpnext/shopping_cart/web_template/product_card/product_card.html b/erpnext/shopping_cart/web_template/product_card/product_card.html
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/product_card/product_card.html
diff --git a/erpnext/shopping_cart/web_template/product_card/product_card.json b/erpnext/shopping_cart/web_template/product_card/product_card.json
new file mode 100644
index 0000000..1059c1b
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/product_card/product_card.json
@@ -0,0 +1,33 @@
+{
+ "__unsaved": 1,
+ "creation": "2020-11-17 15:28:47.809342",
+ "docstatus": 0,
+ "doctype": "Web Template",
+ "fields": [
+  {
+   "__unsaved": 1,
+   "fieldname": "item",
+   "fieldtype": "Link",
+   "label": "Item",
+   "options": "Item",
+   "reqd": 0
+  },
+  {
+   "__unsaved": 1,
+   "fieldname": "featured",
+   "fieldtype": "Check",
+   "label": "Featured",
+   "options": "",
+   "reqd": 0
+  }
+ ],
+ "idx": 0,
+ "modified": "2020-11-17 15:33:34.982515",
+ "modified_by": "Administrator",
+ "module": "Shopping Cart",
+ "name": "Product Card",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "type": "Component"
+}
\ No newline at end of file
diff --git a/erpnext/shopping_cart/web_template/product_category_cards/__init__.py b/erpnext/shopping_cart/web_template/product_category_cards/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/product_category_cards/__init__.py
diff --git a/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html
new file mode 100644
index 0000000..06b76af
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.html
@@ -0,0 +1,40 @@
+{%- macro card(title, image, url, text_primary=False) -%}
+{%- set align_class = resolve_class({
+	'text-right': text_primary,
+	'text-centre': align == 'Center',
+	'text-left': align == 'Left',
+}) -%}
+<div class="card h-100">
+	{% if image %}
+	<img class="card-img-top" src="{{ image }}" alt="{{ title }}">
+	{% endif %}
+	<div class="card-body text-center text-muted small">
+		{{ title or '' }}
+	</div>
+	<a href="{{ url or '#' }}" class="stretched-link"></a>
+</div>
+{%- endmacro -%}
+
+<div class="section-with-cards product-category-section">
+	{%- if title -%}
+	<h2 class="section-title">{{ title }}</h2>
+	{%- endif -%}
+	{%- if subtitle -%}
+	<p class="section-description">{{ subtitle }}</p>
+	{%- endif -%}
+	<!-- {%- set card_size = card_size or 'Small' -%} -->
+	<div class="{{ resolve_class({'mt-6': title}) }}">
+		<div class="card-grid">
+			{%- for index in ['1', '2', '3', '4', '5', '6', '7', '8'] -%}
+			{%- set category = values['category_' + index] -%}
+				{%- if category -%}
+					{%- set category = frappe.get_doc("Item Group", category) -%}
+					{{ card(category.name, category.image, category.route) }}
+				{%- endif -%}
+			{%- endfor -%}
+		</div>
+	</div>
+</div>
+
+<style>
+</style>
diff --git a/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json
new file mode 100644
index 0000000..ba5f63b
--- /dev/null
+++ b/erpnext/shopping_cart/web_template/product_category_cards/product_category_cards.json
@@ -0,0 +1,85 @@
+{
+ "__unsaved": 1,
+ "creation": "2020-11-17 15:25:50.855934",
+ "docstatus": 0,
+ "doctype": "Web Template",
+ "fields": [
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "label": "Title",
+   "reqd": 1
+  },
+  {
+   "fieldname": "subtitle",
+   "fieldtype": "Data",
+   "label": "Subtitle",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_1",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_2",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_3",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_4",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_5",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_6",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_7",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  },
+  {
+   "fieldname": "category_8",
+   "fieldtype": "Link",
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 0
+  }
+ ],
+ "idx": 0,
+ "modified": "2020-11-18 17:26:28.726260",
+ "modified_by": "Administrator",
+ "module": "Shopping Cart",
+ "name": "Product Category Cards",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "type": "Section"
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 9e09411..2d5ab5a 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -81,7 +81,7 @@
 				}, __('Create'));
 			}
 
-			frm.page.set_inner_btn_group_as_primary(__('Create'));
+			// frm.page.set_inner_btn_group_as_primary(__('Create'));
 		}
 		if (frm.doc.variant_of) {
 			frm.set_intro(__('This Item is a Variant of {0} (Template).',
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 86ce5cc..47f2614 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -317,6 +317,7 @@
 		context.search_link = '/product_search'
 
 		context.parents = get_parent_item_groups(self.item_group)
+		context.body_class = "product-page"
 
 		self.set_variant_context(context)
 		self.set_attribute_context(context)
diff --git a/erpnext/templates/generators/item/item.html b/erpnext/templates/generators/item/item.html
index d3691a6..135982d 100644
--- a/erpnext/templates/generators/item/item.html
+++ b/erpnext/templates/generators/item/item.html
@@ -3,21 +3,25 @@
 {% block title %} {{ title }} {% endblock %}
 
 {% block breadcrumbs %}
+<div class="item-breadcrumbs small text-muted">
 	{% include "templates/includes/breadcrumbs.html" %}
+</div>
 {% endblock %}
 
 {% block page_content %}
-{% from "erpnext/templates/includes/macros.html" import product_image %}
-<div class="item-content">
-	<div class="product-page-content" itemscope itemtype="http://schema.org/Product">
-		<div class="row mb-5">
-			{% include "templates/generators/item/item_image.html" %}
-			{% include "templates/generators/item/item_details.html" %}
+<div class="product-container">
+	{% from "erpnext/templates/includes/macros.html" import product_image %}
+	<div class="item-content">
+		<div class="product-page-content" itemscope itemtype="http://schema.org/Product">
+			<div class="row mb-5">
+				{% include "templates/generators/item/item_image.html" %}
+				{% include "templates/generators/item/item_details.html" %}
+			</div>
+
+			{% include "templates/generators/item/item_specifications.html" %}
+
+			{{ doc.website_content or '' }}
 		</div>
-
-		{% include "templates/generators/item/item_specifications.html" %}
-
-		{{ doc.website_content or '' }}
 	</div>
 </div>
 {% endblock %}
diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html
index dbf15de..f5adbf0 100644
--- a/erpnext/templates/generators/item/item_add_to_cart.html
+++ b/erpnext/templates/generators/item/item_add_to_cart.html
@@ -6,10 +6,10 @@
 <div class="item-cart row mt-2" data-variant-item-code="{{ item_code }}">
 	<div class="col-md-12">
 		{% if cart_settings.show_price and product_info.price %}
-		<h4>
+		<div class="product-price">
 			{{ product_info.price.formatted_price_sales_uom }}
-			<small class="text-muted">({{ product_info.price.formatted_price }} / {{ product_info.uom }})</small>
-		</h4>
+			<small class="formatted-price">({{ product_info.price.formatted_price }} / {{ product_info.uom }})</small>
+		</div>
 		{% else %}
 			{{ _("Unit of Measurement") }} : {{ product_info.uom }}
 		{% endif %}
@@ -17,11 +17,11 @@
 		{% if cart_settings.show_stock_availability %}
 		<div>
 			{% if product_info.in_stock == 0 %}
-			<span class="text-danger">
+			<span class="text-danger no-stock">
 				{{ _('Not in stock') }}
 			</span>
 			{% elif product_info.in_stock == 1 %}
-			<span class="text-success">
+			<span class="text-success has-stock">
 				{{ _('In stock') }}
 				{% if product_info.show_stock_qty and product_info.stock_qty %}
 					({{ product_info.stock_qty[0][0] }})
@@ -30,7 +30,7 @@
 			{% endif %}
 		</div>
 		{% endif %}
-		<div class="mt-3">
+		<div class="mt-5 mb-5">
 			{% if product_info.price and (cart_settings.allow_items_not_in_stock or product_info.in_stock) %}
 				<a href="/cart"
 					class="btn btn-light btn-view-in-cart {% if not product_info.qty %}hidden{% endif %}"
@@ -40,8 +40,13 @@
 				</a>
 				<button
 					data-item-code="{{item_code}}"
-					class="btn btn-outline-primary btn-add-to-cart {% if product_info.qty %}hidden{% endif %}"
+					class="btn btn-primary btn-add-to-cart {% if product_info.qty %}hidden{% endif %} w-100"
 				>
+					<span class="mr-2">
+						<svg class="icon icon-md">
+							<use href="#icon-assets"></use>
+						</svg>
+					</span>
 					{{ _("Add to Cart") }}
 				</button>
 			{% endif %}
diff --git a/erpnext/templates/generators/item/item_configure.html b/erpnext/templates/generators/item/item_configure.html
index 73f9ec9..b61ac73 100644
--- a/erpnext/templates/generators/item/item_configure.html
+++ b/erpnext/templates/generators/item/item_configure.html
@@ -1,9 +1,9 @@
 {% if shopping_cart and shopping_cart.cart_settings.enabled %}
 {% set cart_settings = shopping_cart.cart_settings %}
 
-<div class="mt-3">
+<div class="mt-5 mb-6">
 	{% if cart_settings.enable_variants | int %}
-	<button class="btn btn-primary btn-configure"
+	<button class="btn btn-primary-light btn-configure"
 		data-item-code="{{ doc.name }}"
 		data-item-name="{{ doc.item_name }}"
 	>
diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js
index 5fd9011..276837e 100644
--- a/erpnext/templates/generators/item/item_configure.js
+++ b/erpnext/templates/generators/item/item_configure.js
@@ -187,42 +187,53 @@
 	}
 
 	get_html_for_item_found({ filtered_items_count, filtered_items, exact_match, product_info }) {
-		const exact_match_message = __('1 exact match.');
-		const one_item = exact_match.length === 1 ?
-			exact_match[0] :
-			filtered_items_count === 1 ?
-				filtered_items[0] : '';
+		const one_item = exact_match.length === 1
+			? exact_match[0]
+			: filtered_items_count === 1
+				? filtered_items[0]
+				: '';
 
 		const item_add_to_cart = one_item ? `
-			<div class="alert alert-success d-flex justify-content-between align-items-center" role="alert">
-				<div>
-					<div>${one_item} ${product_info && product_info.price ? '(' + product_info.price.formatted_price_sales_uom + ')' : ''}</div>
-				</div>
-				<a href data-action="btn_add_to_cart" data-item-code="${one_item}">
-					${__('Add to cart')}
-				</a>
-			</div>
-		`: '';
+			<button data-item-code="${one_item}"
+				class="btn btn-primary btn-add-to-cart w-100"
+				data-action="btn_add_to_cart"
+			>
+				<span class="mr-2">
+					${frappe.utils.icon('assets', 'md')}
+				</span>
+				${__("Add to Cart")}s
+			</button>
+		` : '';
 
 		const items_found = filtered_items_count === 1 ?
 			__('{0} item found.', [filtered_items_count]) :
 			__('{0} items found.', [filtered_items_count]);
 
-		const item_found_status = `
-			<div class="alert alert-warning d-flex justify-content-between align-items-center" role="alert">
-				<span>
-					${exact_match.length === 1 ? '' : items_found}
-					${exact_match.length === 1 ? `<span>${exact_match_message}</span>` : ''}
-				</span>
-				<a href data-action="btn_clear_values">
-					${__('Clear values')}
+		const item_found_status = exact_match.length === 1
+			? `<div class="alert alert-success d-flex justify-content-between align-items-center" role="alert">
+				<div><div>
+					${one_item}
+					${product_info && product_info.price
+						? '(' + product_info.price.formatted_price_sales_uom + ')'
+						: ''
+					}
+				</div></div>
+				<a href data-action="btn_clear_values" data-item-code="${one_item}">
+					${__('Clear Values')}
 				</a>
-			</div>
-		`;
+			</div>`
+			: `<div class="alert alert-warning d-flex justify-content-between align-items-center" role="alert">
+					<span>
+						${items_found}
+					</span>
+					<a href data-action="btn_clear_values">
+						${__('Clear values')}
+					</a>
+			</div>`;
 
 		return `
-			${item_add_to_cart}
 			${item_found_status}
+			${item_add_to_cart}
 		`;
 	}
 
@@ -254,8 +265,8 @@
 	}
 
 	append_status_area() {
-		this.dialog.$status_area = $('<div class="status-area">');
-		this.dialog.$wrapper.find('.modal-body').prepend(this.dialog.$status_area);
+		this.dialog.$status_area = $('<div class="status-area mt-5">');
+		this.dialog.$wrapper.find('.modal-body').append(this.dialog.$status_area);
 		this.dialog.$wrapper.on('click', '[data-action]', (e) => {
 			e.preventDefault();
 			const $target = $(e.currentTarget);
@@ -263,7 +274,7 @@
 			const method = this[action];
 			method.call(this, e);
 		});
-		this.dialog.$body.css({ maxHeight: '75vh', overflow: 'auto', overflowX: 'hidden' });
+		this.dialog.$wrapper.addClass('item-configurator-dialog');
 	}
 
 	get_next_attribute_and_values(selected_attributes) {
diff --git a/erpnext/templates/generators/item/item_details.html b/erpnext/templates/generators/item/item_details.html
index 4cbecb0..3b77585 100644
--- a/erpnext/templates/generators/item/item_details.html
+++ b/erpnext/templates/generators/item/item_details.html
@@ -1,14 +1,21 @@
-<div class="col-md-8">
+<div class="col-md-7 product-details">
 <!-- title -->
-<h1 itemprop="name">
+<h1 class="product-title" itemprop="name">
 	{{ item_name }}
 </h1>
-<p class="text-muted">
+<p class="product-code">
 	<span>{{ _("Item Code") }}:</span>
 	<span itemprop="productID">{{ doc.name }}</span>
 </p>
+{% if has_variants %}
+	<!-- configure template -->
+	{% include "templates/generators/item/item_configure.html" %}
+{% else %}
+	<!-- add variant to cart -->
+	{% include "templates/generators/item/item_add_to_cart.html" %}
+{% endif %}
 <!-- description -->
-<div itemprop="description">
+<div class="product-description" itemprop="description">
 {% if frappe.utils.strip_html(doc.web_long_description or '') %}
 	{{ doc.web_long_description | safe }}
 {% elif frappe.utils.strip_html(doc.description or '')  %}
@@ -17,12 +24,4 @@
 	{{ _("No description given") }}
 {% endif  %}
 </div>
-
-{% if has_variants %}
-	<!-- configure template -->
-	{% include "templates/generators/item/item_configure.html" %}
-{% else %}
-	<!-- add variant to cart -->
-	{% include "templates/generators/item/item_add_to_cart.html" %}
-{% endif %}
 </div>
diff --git a/erpnext/templates/generators/item/item_image.html b/erpnext/templates/generators/item/item_image.html
index 5d46a45..39a30d0 100644
--- a/erpnext/templates/generators/item/item_image.html
+++ b/erpnext/templates/generators/item/item_image.html
@@ -1,42 +1,42 @@
-<div class="col-md-4 h-100">
-{% if slides %}
-{{ product_image(slides[0].image, 'product-image') }}
-<div class="item-slideshow">
-	{% for item in slides %}
-	<img class="item-slideshow-image mt-2 {% if loop.first %}active{% endif %}"
-			src="{{ item.image }}" alt="{{ item.heading }}">
-	{% endfor %}
-</div>
-<!-- Simple image slideshow -->
-<script>
-	frappe.ready(() => {
-		$('.page_content').on('click', '.item-slideshow-image', (e) => {
-			const $img = $(e.currentTarget);
-			const link = $img.prop('src');
-			const $product_image = $('.product-image');
-			$product_image.find('a').prop('href', link);
-			$product_image.find('img').prop('src', link);
+<div class="col-md-5 h-100 d-flex">
+	{% if slides %}
+	<div class="item-slideshow d-flex flex-column mr-3">
+		{% for item in slides %}
+		<img class="item-slideshow-image mb-2 {% if loop.first %}active{% endif %}"
+				src="{{ item.image }}" alt="{{ item.heading }}">
+		{% endfor %}
+	</div>
+	{{ product_image(slides[0].image, 'product-image') }}
+	<!-- Simple image slideshow -->
+	<script>
+		frappe.ready(() => {
+			$('.page_content').on('click', '.item-slideshow-image', (e) => {
+				const $img = $(e.currentTarget);
+				const link = $img.prop('src');
+				const $product_image = $('.product-image');
+				$product_image.find('a').prop('href', link);
+				$product_image.find('img').prop('src', link);
 
-			$('.item-slideshow-image').removeClass('active');
-			$img.addClass('active');
-		});
-	})
-</script>
-{% else %}
-{{ product_image(website_image or image or 'no-image.jpg', alt=website_image_alt or item_name) }}
-{% endif %}
+				$('.item-slideshow-image').removeClass('active');
+				$img.addClass('active');
+			});
+		})
+	</script>
+	{% else %}
+	{{ product_image(website_image or image or 'no-image.jpg', alt=website_image_alt or item_name) }}
+	{% endif %}
 
-<!-- Simple image preview -->
+	<!-- Simple image preview -->
 
-<div class="image-zoom-view" style="display: none;">
-	<button type="button" class="close" aria-label="Close">
-		<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
-		 stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x">
-			<line x1="18" y1="6" x2="6" y2="18"></line>
-			<line x1="6" y1="6" x2="18" y2="18"></line>
-		</svg>
-	</button>
-</div>
+	<div class="image-zoom-view" style="display: none;">
+		<button type="button" class="close" aria-label="Close">
+			<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
+			stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x">
+				<line x1="18" y1="6" x2="6" y2="18"></line>
+				<line x1="6" y1="6" x2="18" y2="18"></line>
+			</svg>
+		</button>
+	</div>
 </div>
 <style>
 	.website-image {
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
index 74b2ae3..393c3a4 100644
--- a/erpnext/templates/generators/item_group.html
+++ b/erpnext/templates/generators/item_group.html
@@ -1,42 +1,139 @@
 {% extends "templates/web.html" %}
 
-{% block breadcrumbs %}
-	{% include "templates/includes/breadcrumbs.html" %}
-{% endblock %}
+{% block header %}
+<!-- <h2>{{ title }}</h2> -->
+{% endblock header %}
 
-{% block header %}<h1>{{ name }}</h1>{% endblock %}
+{% block script %}
+<script type="text/javascript" src="/all-products/index.js"></script>
+{% endblock %}
 
 {% block page_content %}
 <div class="item-group-content" itemscope itemtype="http://schema.org/Product">
 	<div class="item-group-slideshow">
 		{% if slideshow %}<!-- slideshow -->
-		{% include "templates/includes/slideshow.html" %}
+			{{ web_block(
+				"Hero Slider",
+				values=slideshow,
+				add_container=0,
+				add_top_padding=0,
+				add_bottom_padding=0,
+			) }}
 		{% endif %}
+		<h2 class="mt-3">{{ title }}</h2>
 		{% if description %}<!-- description -->
-		<div class="mb-3" itemprop="description">{{ description or ""}}</div>
+		<div class="item-group-description text-muted mb-5" itemprop="description">{{ description or ""}}</div>
 		{% endif %}
 	</div>
 	<div class="row">
-		<div class="col-md-8">
-			{% if items %}
-			<div id="search-list">
-				{% for i in range(0, page_length) %}
-					{% if items[i] %}
-						{%- set item = items[i] %}
+		<div class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
+			<div class="row products-list">
+				{% if items %}
+					{% for item in items %}
 						{% include "erpnext/www/all-products/item_row.html" %}
-					{% endif %}
+					{% endfor %}
+				{% else %}
+					{% include "erpnext/www/all-products/not_found.html" %}
+				{% endif %}
+			</div>
+		</div>
+		<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="d-flex justify-content-between align-items-center mb-5 title-section">
+					<div class="mb-4 filters-title" > {{ _('Filters') }} </div>
+					<a class="mb-4 clear-filters" href="/{{ doc.route }}">{{ _('Clear All') }}</a>
+				</div>
+				{% for field_filter in field_filters %}
+					{%- set item_field =  field_filter[0] %}
+					{%- set values =  field_filter[1] %}
+					<div class="mb-4 filter-block pb-5">
+						<div class="filter-label mb-3">{{ item_field.label }}</div>
+
+						{% if values | len > 20 %}
+						<!-- show inline filter if values more than 20 -->
+						<input type="text" class="form-control form-control-sm mb-2 product-filter-filter"/>
+						{% endif %}
+
+						{% if values %}
+						<div class="filter-options">
+							{% for value in values %}
+							<div class="checkbox" data-value="{{ value }}">
+								<label for="{{value}}">
+									<input type="checkbox"
+										class="product-filter field-filter"
+										id="{{value}}"
+										data-filter-name="{{ item_field.fieldname }}"
+										data-filter-value="{{ value }}"
+									>
+									<span class="label-area">{{ value }}</span>
+								</label>
+							</div>
+							{% endfor %}
+						</div>
+						{% else %}
+						<i class="text-muted">{{ _('No values') }}</i>
+						{% endif %}
+					</div>
+				{% endfor %}
+
+				{% for attribute in attribute_filters %}
+					<div class="mb-4 filter-block pb-5">
+						<div class="filter-label mb-3">{{ attribute.name}}</div>
+						{% if values | len > 20 %}
+						<!-- show inline filter if values more than 20 -->
+						<input type="text" class="form-control form-control-sm mb-2 product-filter-filter"/>
+						{% endif %}
+
+						{% if attribute.item_attribute_values %}
+						<div class="filter-options">
+							{% for attr_value in attribute.item_attribute_values %}
+							<div class="checkbox">
+								<label data-value="{{ value }}">
+									<input type="checkbox"
+										class="product-filter attribute-filter"
+										id="{{attr_value.name}}"
+										data-attribute-name="{{ attribute.name }}"
+										data-attribute-value="{{ attr_value.attribute_value }}"
+										{% if attr_value.checked %} checked {% endif %}>
+										<span class="label-area">{{ attr_value.attribute_value }}</span>
+								</label>
+							</div>
+							{% endfor %}
+						</div>
+						{% else %}
+						<i class="text-muted">{{ _('No values') }}</i>
+						{% endif %}
+					</div>
 				{% endfor %}
 			</div>
-			<div class="item-group-nav-buttons">
-				{% if frappe.form_dict.start|int > 0 %}
-				<a class="btn btn-outline-secondary" href="/{{ pathname }}?start={{ frappe.form_dict.start|int - page_length }}">{{ _("Prev") }}</a>
-				{% endif %}
-				{% if items|length > page_length %}
-				<a class="btn btn-outline-secondary" href="/{{ pathname }}?start={{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</a>
-				{% endif %}
-			</div>
-			{% else %}
-			<div class="text-muted">{{ _("No items listed") }}.</div>
+
+			<script>
+				frappe.ready(() => {
+					$('.product-filter-filter').on('keydown', frappe.utils.debounce((e) => {
+						const $input = $(e.target);
+						const keyword = ($input.val() || '').toLowerCase();
+						const $filter_options = $input.next('.filter-options');
+
+						$filter_options.find('.custom-control').show();
+						$filter_options.find('.custom-control').each((i, el) => {
+							const $el = $(el);
+							const value = $el.data('value').toLowerCase();
+							if (!value.includes(keyword)) {
+								$el.hide();
+							}
+						});
+					}, 300));
+				})
+			</script>
+		</div>
+	</div>
+	<div class="row">
+		<div class="col-12">
+			{% 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 }}">{{ _("Next") }}</button>
 			{% endif %}
 		</div>
 	</div>
diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js
index c6dfd35..0de0267 100644
--- a/erpnext/templates/includes/cart.js
+++ b/erpnext/templates/includes/cart.js
@@ -14,7 +14,7 @@
 	},
 
 	bind_events: function() {
-		shopping_cart.bind_address_select();
+		shopping_cart.bind_address_picker_dialog();
 		shopping_cart.bind_place_order();
 		shopping_cart.bind_request_quotation();
 		shopping_cart.bind_change_qty();
@@ -23,28 +23,76 @@
 		shopping_cart.bind_coupon_code();
 	},
 
-	bind_address_select: function() {
-		$(".cart-addresses").on('click', '.address-card', function(e) {
-			const $card = $(e.currentTarget);
-			const address_type = $card.closest('[data-address-type]').attr('data-address-type');
-			const address_name = $card.closest('[data-address-name]').attr('data-address-name');
-			return frappe.call({
-				type: "POST",
-				method: "erpnext.shopping_cart.cart.update_cart_address",
-				freeze: true,
-				args: {
-					address_type,
-					address_name
-				},
-				callback: function(r) {
-					if(!r.exc) {
-						$(".cart-tax-items").html(r.message.taxes);
-					}
-				}
-			});
+	bind_address_picker_dialog: function() {
+		const d = this.get_update_address_dialog();
+		this.parent.find('.btn-change-address').on('click', (e) => {
+			const type = $(e.currentTarget).parents('.address-container').attr('data-address-type');
+			$(d.get_field('address_picker').wrapper).html(
+				this.get_address_template(type)
+			);
+			d.show();
 		});
 	},
 
+	get_update_address_dialog() {
+		return new frappe.ui.Dialog({
+			title: "Select Address",
+			fields: [{
+				'fieldtype': 'HTML',
+				'fieldname': 'address_picker',
+			}],
+			primary_action_label: __('Set Address'),
+			primary_action: () => {
+				const $card = d.$wrapper.find('.address-card.active');
+				const address_type = $card.closest('[data-address-type]').attr('data-address-type');
+				const address_name = $card.closest('[data-address-name]').attr('data-address-name');
+				frappe.call({
+					type: "POST",
+					method: "erpnext.shopping_cart.cart.update_cart_address",
+					freeze: true,
+					args: {
+						address_type,
+						address_name
+					},
+					callback: function(r) {
+						d.hide();
+						if(!r.exc) {
+							$(".cart-tax-items").html(r.message.taxes);
+							shopping_cart.parent.find(
+								`.address-container[data-address-type="${address_type}"]`
+							).html(r.message.address);
+						}
+					}
+				});
+			}
+		});
+	},
+
+	get_address_template(type) {
+		return {
+			shipping: `<div class="mb-3" data-section="shipping-address">
+				<div class="row no-gutters" data-fieldname="shipping_address_name">
+					{% for address in shipping_addresses %}
+						<div class="mr-3 mb-3 w-100" data-address-name="{{address.name}}" data-address-type="shipping"
+							{% if doc.shipping_address_name == address.name %} data-active {% endif %}>
+							{% include "templates/includes/cart/address_picker_card.html" %}
+						</div>
+					{% endfor %}
+				</div>
+			</div>`,
+			billing: `<div class="mb-3" data-section="billing-address">
+				<div class="row no-gutters" data-fieldname="customer_address">
+					{% for address in billing_addresses %}
+						<div class="mr-3 mb-3 w-100" data-address-name="{{address.name}}" data-address-type="billing"
+							{% if doc.shipping_address_name == address.name %} data-active {% endif %}>
+							{% include "templates/includes/cart/address_picker_card.html" %}
+						</div>
+					{% endfor %}
+				</div>
+			</div>`,
+		}[type];
+	},
+
 	bind_place_order: function() {
 		$(".btn-place-order").on("click", function() {
 			shopping_cart.place_order(this);
@@ -221,6 +269,7 @@
 
 frappe.ready(function() {
 	$(".cart-icon").hide();
+	shopping_cart.parent = $(".cart-container");
 	shopping_cart.bind_events();
 });
 
diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html
index 646210e..667144b 100644
--- a/erpnext/templates/includes/cart/address_card.html
+++ b/erpnext/templates/includes/cart/address_card.html
@@ -1,12 +1,17 @@
 <div class="card address-card h-100">
-	<div class="check" style="position: absolute; right: 15px; top: 15px;">
-		<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
+	<div class="btn btn-sm btn-default btn-change-address" style="position: absolute; right: 0; top: 0;">
+		{{ _('Change') }}
 	</div>
-	<div class="card-body">
-		<h5 class="card-title">{{ address.title }}</h5>
-		<p class="card-text text-muted">
+	<div class="card-body p-0">
+		<div class="card-title">{{ address.title }}</div>
+		<div class="card-text mb-2">
 			{{ address.display }}
-		</p>
-		<a href="/addresses?name={{address.name}}" class="card-link">{{ _('Edit') }}</a>
+		</div>
+		<a href="/addresses?name={{address.name}}" class="card-link">
+			<svg class="icon icon-sm">
+				<use href="#icon-edit"></use>
+			</svg>
+			{{ _('Edit') }}
+		</a>
 	</div>
 </div>
diff --git a/erpnext/templates/includes/cart/address_picker_card.html b/erpnext/templates/includes/cart/address_picker_card.html
new file mode 100644
index 0000000..2334ea2
--- /dev/null
+++ b/erpnext/templates/includes/cart/address_picker_card.html
@@ -0,0 +1,12 @@
+<div class="card address-card h-100">
+	<div class="check" style="position: absolute; right: 15px; top: 15px;">
+		<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
+	</div>
+	<div class="card-body">
+		<h5 class="card-title">{{ address.title }}</h5>
+		<p class="card-text text-muted">
+			{{ address.display }}
+		</p>
+		<a href="/addresses?name={{address.name}}" class="card-link">{{ _('Edit') }}</a>
+	</div>
+</div>
\ No newline at end of file
diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html
index aa25c88..3c8267c 100644
--- a/erpnext/templates/includes/cart/cart_address.html
+++ b/erpnext/templates/includes/cart/cart_address.html
@@ -14,31 +14,39 @@
 		</div>
 </div>
 {% endif %}
-<div class="mb-3" data-section="shipping-address">
-	<h6 class="text-uppercase">{{ _("Shipping Address") }}</h6>
+<div class="mb-3 frappe-card p-5" data-section="shipping-address">
+	<h6>{{ _("Shipping Address") }}</h6>
+	<hr>
+	{% for address in shipping_addresses %}
+	{% if doc.shipping_address_name == address.name %}
 	<div class="row no-gutters" data-fieldname="shipping_address_name">
-		{% for address in shipping_addresses %}
-			<div class="mr-3 mb-3 w-25" data-address-name="{{address.name}}" data-address-type="shipping" {% if doc.shipping_address_name == address.name %} data-active {% endif %}>
-				{% include "templates/includes/cart/address_card.html" %}
-			</div>
-		{% endfor %}
+		<div class="w-100 address-container" data-address-name="{{address.name}}" data-address-type="shipping" data-active>
+			{% include "templates/includes/cart/address_card.html" %}
+		</div>
 	</div>
+	{% endif %}
+	{% endfor %}
 </div>
-<div class="mb-3" data-section="billing-address">
-	<h6 class="text-uppercase">{{ _("Billing Address") }}</h6>
-	<div class="row no-gutters" data-fieldname="customer_address">
-		{% for address in billing_addresses %}
-			<div class="mr-3 mb-3 w-25" data-address-name="{{address.name}}" data-address-type="billing" {% if doc.customer_address == address.name %} data-active {% endif %}>
-				{% include "templates/includes/cart/address_card.html" %}
-			</div>
-		{% endfor %}
-	</div>
+<div class="checkbox ml-1 mb-2">
+	<label for="input_same_billing">
+		<input type="checkbox" id="input_same_billing" checked>
+		<span class="label-area">{{ _('Billing Address is same as Shipping Address') }}</span>
+	</label>
 </div>
-<div class="custom-control custom-checkbox">
-	<input type="checkbox" class="custom-control-input" id="input_same_billing" checked>
-	<label class="custom-control-label" for="input_same_billing">{{ _('Billing Address is same as Shipping Address') }}</label>
+<div class="mb-3 frappe-card p-5" data-section="billing-address">
+	<h6>{{ _("Billing Address") }}</h6>
+	<hr>
+	{% for address in billing_addresses %}
+		{% if doc.customer_address == address.name %}
+		<div class="row no-gutters" data-fieldname="customer_address">
+			<div class="w-100 address-container" data-address-name="{{address.name}}" data-address-type="billing" data-active>
+					{% include "templates/includes/cart/address_card.html" %}
+				</div>
+		</div>
+		{% endif %}
+	{% endfor %}
 </div>
-<button class="btn btn-outline-primary btn-sm mt-3 btn-new-address">{{ _("Add a new address") }}</button>
+<button class="btn btn-outline-primary btn-sm mt-1 btn-new-address bg-white">{{ _("Add a new address") }}</button>
 
 <script>
 frappe.ready(() => {
@@ -55,6 +63,7 @@
 	});
 
 	$('.btn-new-address').click(() => {
+		console.log('clicked');
 		const d = new frappe.ui.Dialog({
 			title: __('New Address'),
 			fields: [
@@ -168,5 +177,12 @@
 	function toggle_billing_address_section(flag) {
 		$('[data-section="billing-address"]').toggle(flag);
 	}
+
+	$('.btn-change-address').click(() => {
+		// const d = new frappe.ui.Dialog({
+		// })
+
+		// d.show();
+	});
 });
 </script>
diff --git a/erpnext/templates/includes/cart/cart_address_picker.html b/erpnext/templates/includes/cart/cart_address_picker.html
new file mode 100644
index 0000000..72cc5f5
--- /dev/null
+++ b/erpnext/templates/includes/cart/cart_address_picker.html
@@ -0,0 +1,4 @@
+<div class="mb-3 frappe-card p-5" data-section="shipping-address">
+	<h6>{{ _("Shipping Address") }}</h6>
+</div>
+
diff --git a/erpnext/templates/includes/cart/cart_items.html b/erpnext/templates/includes/cart/cart_items.html
index ca5744b..75441c4 100644
--- a/erpnext/templates/includes/cart/cart_items.html
+++ b/erpnext/templates/includes/cart/cart_items.html
@@ -1,15 +1,15 @@
 {% for d in doc.items %}
 <tr data-name="{{ d.name }}">
 	<td>
-		<div class="font-weight-bold">
+		<div class="item-title mb-1">
 			{{ d.item_name }}
 		</div>
-		<div>
+		<div class="item-subtitle">
 			{{ d.item_code }}
 		</div>
 		{%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %}
 		{% if variant_of %}
-		<span class="text-muted">
+		<span class="item-subtitle">
 			{{ _('Variant of') }} <a href="{{frappe.db.get_value('Item', variant_of, 'route')}}">{{ variant_of }}</a>
 		</span>
 		{% endif %}
@@ -20,20 +20,20 @@
 	<td class="text-right">
 		<div class="input-group number-spinner">
 			<span class="input-group-prepend d-none d-sm-inline-block">
-				<button class="btn btn-outline-secondary cart-btn" data-dir="dwn">–</button>
+				<button class="btn cart-btn" data-dir="dwn">–</button>
 			</span>
-			<input class="form-control text-right cart-qty border-secondary" value="{{ d.get_formatted('qty') }}" data-item-code="{{ d.item_code }}">
+			<input class="form-control text-center cart-qty" value="{{ d.get_formatted('qty') }}" data-item-code="{{ d.item_code }}">
 			<span class="input-group-append d-none d-sm-inline-block">
-				<button class="btn btn-outline-secondary cart-btn" data-dir="up">+</button>
+				<button class="btn cart-btn" data-dir="up">+</button>
 			</span>
 		</div>
 	</td>
 	{% if cart_settings.enable_checkout %}
-	<td class="text-right">
+	<td class="text-right item-subtotal">
 		<div>
 			{{ d.get_formatted('amount') }}
 		</div>
-		<span class="text-muted">
+		<span class="item-rate">
 			{{ _('Rate:') }} {{ d.get_formatted('rate') }}
 		</span>
 	</td>
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html
index ea6b00f..0adfbc9 100644
--- a/erpnext/templates/includes/macros.html
+++ b/erpnext/templates/includes/macros.html
@@ -8,9 +8,9 @@
 {% endmacro %}
 
 {% macro product_image(website_image, css_class="", alt="") %}
-    <div class="border text-center rounded h-100 {{ css_class }}" style="overflow: hidden;">
+	<div class="border text-center rounded {{ css_class }}" style="overflow: hidden;">
 		<img itemprop="image" class="website-image h-100 w-100" alt="{{ alt }}" src="{{ frappe.utils.quoted(website_image or 'no-image.jpg') | abs_url }}">
-    </div>
+	</div>
 {% endmacro %}
 
 {% macro media_image(website_image, name, css_class="") %}
@@ -18,13 +18,13 @@
 		{% if not website_image -%}
 		<div class="sidebar-standard-image"> <div class="standard-image" style="background-color: rgb(250, 251, 252);">{{name}}</div> </div>
 		{%- endif %}
-    	{% if website_image -%}
+		{% if website_image -%}
 			<a href="{{ frappe.utils.quoted(website_image) }}">
 				<img itemprop="image" src="{{ frappe.utils.quoted(website_image) | abs_url }}"
 					class="img-responsive img-thumbnail sidebar-image" style="min-height:100%; min-width:100%;">
 			</a>
-    	{%- endif %}
-    </div>
+		{%- endif %}
+	</div>
 {% endmacro %}
 
 {% macro render_homepage_section(section) %}
@@ -57,4 +57,65 @@
 </section>
 {% endif %}
 
-{% endmacro %}
\ No newline at end of file
+{% endmacro %}
+
+{%- macro item_card(title, image, url, description, rate, category, is_featured=False, is_full_width=False, align="Left") -%}
+{%- set align_items_class = resolve_class({
+	'align-items-end': align == 'Right',
+	'align-items-center': align == 'Center',
+	'align-items-start': align == 'Left',
+}) -%}
+{%- set col_size = 3 if is_full_width else 4 -%}
+{% if is_featured %}
+<div class="col-sm-{{ col_size*2 }} item-card">
+	<div class="card featured-item {{ align_items_class }}">
+		{% if image %}
+		<div class="row no-gutters">
+			<div class="col-md-6">
+				<img class="card-img" src="{{ image }}" alt="{{ title }}">
+			</div>
+			<div class="col-md-6">
+				{{ item_card_body(title, description, url, rate, category, is_featured, align) }}
+			</div>
+		</div>
+		{% else %}
+			<div class="col-md-12">
+				{{ item_card_body(title, description, url, rate, category, is_featured, align) }}
+			</div>
+		{% endif %}
+	</div>
+</div>
+{% else %}
+<div class="col-sm-{{ col_size }} item-card">
+	<div class="card {{ align_items_class }}">
+		{% if image %}
+		<img class="card-img" src="{{ image }}" alt="{{ title }}">
+		{% else %}
+		<div class="card-img-top no-image">
+			{{ frappe.utils.get_abbr(title) }}
+		</div>
+		{% endif %}
+		{{ item_card_body(title, description, url, rate, category, is_featured, align) }}
+	</div>
+</div>
+{% endif %}
+{%- endmacro -%}
+
+{%- macro item_card_body(title, description, url, rate, category, is_featured, align) -%}
+{%- set align_class = resolve_class({
+	'text-right': align == 'Right',
+	'text-center': align == 'Center' and not is_featured,
+	'text-left': align == 'Left' or is_featured,
+}) -%}
+<div class="card-body {{ align_class }}">
+	<div class="product-title">{{ title or '' }}</div>
+	{% if is_featured %}
+	<div class="product-price">{{ rate or '' }}</div>
+	<div class="product-description ellipsis">{{ description or '' }}</div>
+	{% else %}
+	<div class="product-category">{{ category or '' }}</div>
+	<div class="product-price">{{ rate or '' }}</div>
+	{% endif %}
+</div>
+<a href="/{{ url or '#' }}" class="stretched-link"></a>
+{%- endmacro -%}
\ No newline at end of file
diff --git a/erpnext/templates/includes/navbar/navbar_items.html b/erpnext/templates/includes/navbar/navbar_items.html
index 4daf0e7..133d99e 100644
--- a/erpnext/templates/includes/navbar/navbar_items.html
+++ b/erpnext/templates/includes/navbar/navbar_items.html
@@ -2,9 +2,11 @@
 
 {% block navbar_right_extension %}
 	<li class="shopping-cart cart-icon hidden">
-		<a href="/cart" class="nav-link">
-			{{ _("Cart") }}
-			<span class="badge badge-primary" id="cart-count"></span>
+		<a class="nav-link" href="/cart">
+			<svg class="icon icon-lg">
+				<use href="#icon-assets"></use>
+			</svg>
+			<span class="badge badge-primary cart-badge" id="cart-count"></span>
 		</a>
 	 </li>
 {% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html
index ebec838..d2c458e 100644
--- a/erpnext/templates/includes/order/order_taxes.html
+++ b/erpnext/templates/includes/order/order_taxes.html
@@ -29,12 +29,12 @@
 		{{ _("Discount") }}
 	</th>
 	<th class="text-right tot_quotation_discount">
-        {% set tot_quotation_discount = [] %}
-        {%- for item in doc.items -%}
-        {% if tot_quotation_discount.append((((item.price_list_rate * item.qty)
-            * item.discount_percentage) / 100)) %}{% endif %}
-         {% endfor %}
-         {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }}
+		{% set tot_quotation_discount = [] %}
+		{%- for item in doc.items -%}
+		{% if tot_quotation_discount.append((((item.price_list_rate * item.qty)
+			* item.discount_percentage) / 100)) %}{% endif %}
+		{% endfor %}
+		{{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }}
 	</th>
 </tr>
 {% endif %}
@@ -47,51 +47,52 @@
 		{{ _("Total Amount") }}
 	</th>
 	<th class="text-right">
-        <span>
-        {% set total_amount = [] %}
-        {%- for item in doc.items -%}
-        {% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %}
-        {% endfor %}
-        {{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }}
-        </span>
+		<span>
+		{% set total_amount = [] %}
+		{%- for item in doc.items -%}
+		{% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %}
+		{% endfor %}
+		{{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }}
+		</span>
 	</th>
 </tr>
 <tr>
-    <th class="text-right" colspan="2">
-        {{ _("Applied Coupon Code") }}
-    </th>
-    <th class="text-right">
-        <span>
-        {%- for row in frappe.get_all(doctype="Coupon Code",
-        fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%}
-            <span>{{ row.coupon_code }}</span>
-        {% endfor %}
-        </span>
-    </th>
+	<th class="text-right" colspan="2">
+		{{ _("Applied Coupon Code") }}
+	</th>
+	<th class="text-right">
+		<span>
+		{%- for row in frappe.get_all(doctype="Coupon Code",
+		fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%}
+			<span>{{ row.coupon_code }}</span>
+		{% endfor %}
+		</span>
+	</th>
 </tr>
 <tr>
-    <th class="text-right" colspan="2">
-        {{ _("Discount") }}
-    </th>
-    <th class="text-right">
-        <span>
-        {% set tot_SO_discount = [] %}
-        {%- for item in doc.items -%}
-        {% if tot_SO_discount.append((((item.price_list_rate * item.qty)
-            * item.discount_percentage) / 100)) %}{% endif %}
-        {% endfor %}
-        {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }}
-        </span>
-    </th>
+	<th class="text-right" colspan="2">
+		{{ _("Discount") }}
+	</th>
+	<th class="text-right">
+		<span>
+		{% set tot_SO_discount = [] %}
+		{%- for item in doc.items -%}
+		{% if tot_SO_discount.append((((item.price_list_rate * item.qty)
+			* item.discount_percentage) / 100)) %}{% endif %}
+		{% endfor %}
+		{{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }}
+		</span>
+	</th>
 </tr>
 {% endif %}
 {% endif %}
 
 <tr>
-	<th class="text-right" colspan="2">
+	<th></th>
+	<th class="item-grand-total">
 		{{ _("Grand Total") }}
 	</th>
-	<th class="text-right">
+	<th class="text-right item-grand-total">
 		{{ doc.get_formatted("grand_total") }}
 	</th>
 </tr>
diff --git a/erpnext/templates/includes/products_as_list.html b/erpnext/templates/includes/products_as_list.html
index 88910d0..9bf9fd9 100644
--- a/erpnext/templates/includes/products_as_list.html
+++ b/erpnext/templates/includes/products_as_list.html
@@ -1,4 +1,4 @@
-{% from "erpnext/templates/includes/macros.html" import product_image_square %}
+{% from "erpnext/templates/includes/macros.html" import item_card, item_card_body %}
 
 <a class="product-link product-list-link" href="{{ route|abs_url }}">
 	<div class='row'>
diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html
index 3033d15..2cabf5a 100644
--- a/erpnext/templates/pages/cart.html
+++ b/erpnext/templates/pages/cart.html
@@ -2,7 +2,7 @@
 
 {% block title %} {{ _("Shopping Cart") }} {% endblock %}
 
-{% block header %}<h1>{{ _("Shopping Cart") }}</h1>{% endblock %}
+{% block header %}<h3 class="shopping-cart-header mt-2 mb-6">{{ _("Shopping Cart") }}</h1>{% endblock %}
 
 <!--
 {% block script %}
@@ -18,94 +18,119 @@
 
 {% from "templates/includes/macros.html" import item_name_and_description %}
 
+{% if doc.items %}
 <div class="cart-container">
-	<div id="cart-error" class="alert alert-danger" style="display: none;"></div>
+	<div class="row m-0">
+		<div class="col-md-8 frappe-card p-5">
+			<div>
+				<div id="cart-error" class="alert alert-danger" style="display: none;"></div>
+				<div class="cart-items-header">
+					{{ _('Items') }}
+				</div>
+				<table class="table mt-3 cart-table">
+					<thead>
+						<tr>
+							<th width="60%">{{ _('Item') }}</th>
+							<th width="20%">{{ _('Quantity') }}</th>
+							{% if cart_settings.enable_checkout %}
+							<th width="20%" class="text-right">{{ _('Subtotal') }}</th>
+							{% endif %}
+						</tr>
+					</thead>
+					<tbody class="cart-items">
+						{% include "templates/includes/cart/cart_items.html" %}
+					</tbody>
+					{% if cart_settings.enable_checkout %}
+					<tfoot class="cart-tax-items">
+						{% include "templates/includes/order/order_taxes.html" %}
+					</tfoot>
+					{% endif %}
+				</table>
+			</div>
+			<div class="row">
+				<div class="col-4">
+					{% if cart_settings.enable_checkout %}
+					<a class="btn btn-outline-primary" href="/orders">
+						{{ _('See past orders') }}
+					</a>
+					{% else %}
+					<a class="btn btn-outline-primary" href="/quotations">
+						{{ _('See past quotations') }}
+					</a>
+					{% endif %}
+				</div>
+				<div class="col-8">
+					{% if doc.items %}
+					<div class="place-order-container">
+						{% if cart_settings.enable_checkout %}
+							<button class="btn btn-primary btn-place-order" type="button">
+								{{ _("Place Order") }}
+							</button>
+						{% else %}
+							<button class="btn btn-primary btn-request-for-quotation" type="button">
+								{{ _("Request for Quotation") }}
+							</button>
+						{% endif %}
+					</div>
+					{% endif %}
+				</div>
+			</div>
 
-	{% if doc.items %}
-	<table class="table table-bordered mt-3">
-		<thead>
-			<tr>
-				<th width="60%">{{ _('Item') }}</th>
-				<th width="20%" class="text-right">{{ _('Quantity') }}</th>
-				{% if cart_settings.enable_checkout %}
-				<th width="20%" class="text-right">{{ _('Subtotal') }}</th>
-				{% endif %}
-			</tr>
-		</thead>
-		<tbody class="cart-items">
-			{% include "templates/includes/cart/cart_items.html" %}
-		</tbody>
-		{% if cart_settings.enable_checkout %}
-		<tfoot class="cart-tax-items">
-			{% include "templates/includes/order/order_taxes.html" %}
-		</tfoot>
-		{% endif %}
-	</table>
-	{% else %}
-	<p class="text-muted">{{ _('Your cart is Empty') }}</p>
-	{% endif %}
 
-	{% if doc.items %}
-		<div class="place-order-container">
-			{% if cart_settings.enable_checkout %}
-				<button class="btn btn-primary btn-place-order" type="button">
-					{{ _("Place Order") }}
-				</button>
-			{% else %}
-				<button class="btn btn-primary btn-request-for-quotation" type="button">
-					{{ _("Request for Quotation") }}
-				</button>
+			{% if doc.items %}
+			{% if doc.tc_name %}
+				<div class="terms-and-conditions-link">
+					<a href class="link-terms-and-conditions" data-terms-name="{{ doc.tc_name }}">
+						{{ _("Terms and Conditions") }}
+					</a>
+					<script>
+						frappe.ready(() => {
+							$('.link-terms-and-conditions').click((e) => {
+								e.preventDefault();
+								const $link = $(e.target);
+								const terms_name = $link.attr('data-terms-name');
+								show_terms_and_conditions(terms_name);
+							})
+						});
+						function show_terms_and_conditions(terms_name) {
+							frappe.call('erpnext.shopping_cart.cart.get_terms_and_conditions', { terms_name })
+							.then(r => {
+								frappe.msgprint({
+									title: terms_name,
+									message: r.message
+								});
+							});
+						}
+					</script>
+				</div>
 			{% endif %}
 		</div>
-	{% endif %}
 
-	{% if doc.items %}
-	{% if doc.tc_name %}
-		<div class="terms-and-conditions-link">
-			<a href class="link-terms-and-conditions" data-terms-name="{{ doc.tc_name }}">
-				{{ _("Terms and Conditions") }}
-			</a>
-			<script>
-				frappe.ready(() => {
-					$('.link-terms-and-conditions').click((e) => {
-						e.preventDefault();
-						const $link = $(e.target);
-						const terms_name = $link.attr('data-terms-name');
-						show_terms_and_conditions(terms_name);
-					})
-				});
-				function show_terms_and_conditions(terms_name) {
-					frappe.call('erpnext.shopping_cart.cart.get_terms_and_conditions', { terms_name })
-					.then(r => {
-						frappe.msgprint({
-							title: terms_name,
-							message: r.message
-						});
-					});
-				}
-			</script>
+		<div class="col-md-4">
+			<div class="cart-addresses">
+				{% include "templates/includes/cart/cart_address.html" %}
+				</div>
 		</div>
-	{% endif %}
-
-	<div class="cart-addresses mt-5">
-	{% include "templates/includes/cart/cart_address.html" %}
+		{% endif %}
 	</div>
-	{% endif %}
 </div>
-
-<div class="row mt-5">
-	<div class="col-12">
-		{% if cart_settings.enable_checkout %}
-		<a href="/orders">
+{% else %}
+<div class="cart-empty frappe-card">
+	<div class="cart-empty-state">
+		<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty State">
+	</div>
+	<div class="cart-empty-message mt-4">{{ _('Your cart is Empty') }}</p>
+	{% if cart_settings.enable_checkout %}
+		<a class="btn btn-outline-primary" href="/orders">
 			{{ _('See past orders') }}
 		</a>
 		{% else %}
-		<a href="/quotations">
+		<a class="btn btn-outline-primary" href="/quotations">
 			{{ _('See past quotations') }}
 		</a>
-		{% endif %}
-	</div>
+	{% endif %}
 </div>
+{% endif %}
 
 {% endblock %}
 
diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html
index 0126b59..92c76ad 100644
--- a/erpnext/www/all-products/index.html
+++ b/erpnext/www/all-products/index.html
@@ -1,12 +1,11 @@
 {% extends "templates/web.html" %}
-
 {% block title %}{{ _('Products') }}{% endblock %}
 {% block header %}
-<h1>{{ _('Products') }}</h1>
+<div class="mb-6">{{ _('Products') }}</div>
 {% endblock header %}
 
 {% block page_content %}
-<div class="row">
+<div class="row" style="display: none;">
 	<div class="col-8">
 		<div class="input-group input-group-sm mb-3">
 			<input type="search" class="form-control" placeholder="{{_('Search')}}"
@@ -31,27 +30,34 @@
 </div>
 
 <div class="row">
-	<div class="col-12 order-2 col-md-8 order-md-1 products-list">
-		{% if items %}
-			{% for item in items %}
-				{% include "erpnext/www/all-products/item_row.html" %}
-			{% endfor %}
-		{% else %}
-			{% include "erpnext/www/all-products/not_found.html" %}
-		{% endif %}
+	<div class="col-12 order-2 col-md-9 order-md-2 item-card-group-section">
+		<div class="row products-list">
+			{% if items %}
+				{% for item in items %}
+					{% include "erpnext/www/all-products/item_row.html" %}
+				{% endfor %}
+			{% else %}
+				{% include "erpnext/www/all-products/not_found.html" %}
+			{% endif %}
+		</div>
 	</div>
-	<div class="col-12 order-1 col-md-4 order-md-2">
+	<div class="col-12 order-1 col-md-3 order-md-1">
 
 		{% if frappe.form_dict.start or frappe.form_dict.field_filters or frappe.form_dict.attribute_filters or frappe.form_dict.search %}
-		<a class="mb-3 d-inline-block" href="/all-products">{{ _('Clear filters') }}</a>
+
+
 		{% endif  %}
 
-		<div class="collapse d-md-block" 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="mb-4 filters-title" > {{ _('Filters') }} </div>
+				<a class="mb-4 clear-filters" href="/all-products">{{ _('Clear All') }}</a>
+			</div>
 			{% for field_filter in field_filters %}
 				{%- set item_field =  field_filter[0] %}
 				{%- set values =  field_filter[1] %}
-				<div class="mb-4">
-					<h6>{{ item_field.label }}</h6>
+				<div class="mb-4 filter-block pb-5">
+					<div class="filter-label mb-3">{{ item_field.label }}</div>
 
 					{% if values | len > 20 %}
 					<!-- show inline filter if values more than 20 -->
@@ -61,15 +67,15 @@
 					{% if values %}
 					<div class="filter-options">
 						{% for value in values %}
-						<div class="custom-control custom-checkbox" data-value="{{ value }}">
-							<input type="checkbox"
-								class="product-filter field-filter custom-control-input"
-								id="{{value}}"
-								data-filter-name="{{ item_field.fieldname }}"
-								data-filter-value="{{ value }}"
-							>
-							<label class="custom-control-label" for="{{value}}">
-								{{ value }}
+						<div class="checkbox" data-value="{{ value }}">
+							<label for="{{value}}">
+								<input type="checkbox"
+									class="product-filter field-filter"
+									id="{{value}}"
+									data-filter-name="{{ item_field.fieldname }}"
+									data-filter-value="{{ value }}"
+								>
+								<span class="label-area">{{ value }}</span>
 							</label>
 						</div>
 						{% endfor %}
@@ -81,9 +87,8 @@
 			{% endfor %}
 
 			{% for attribute in attribute_filters %}
-				<div class="mb-4">
-					<h6>{{ attribute.name }}</h6>
-
+				<div class="mb-4 filter-block pb-5">
+					<div class="filter-label mb-3">{{ attribute.name}}</div>
 					{% if values | len > 20 %}
 					<!-- show inline filter if values more than 20 -->
 					<input type="text" class="form-control form-control-sm mb-2 product-filter-filter"/>
@@ -92,16 +97,15 @@
 					{% if attribute.item_attribute_values %}
 					<div class="filter-options">
 						{% for attr_value in attribute.item_attribute_values %}
-						<div class="custom-control custom-checkbox" data-value="{{ value }}">
-							<input type="checkbox"
-								class="product-filter attribute-filter custom-control-input"
-								id="{{attr_value.name}}"
-								data-attribute-name="{{ attribute.name }}"
-								data-attribute-value="{{ attr_value.attribute_value }}"
-								{% if attr_value.checked %} checked {% endif %}
-							>
-							<label class="custom-control-label" for="{{attr_value.name}}">
-								{{ attr_value.attribute_value }}
+						<div class="checkbox">
+							<label data-value="{{ value }}">
+								<input type="checkbox"
+									class="product-filter attribute-filter"
+									id="{{attr_value.name}}"
+									data-attribute-name="{{ attribute.name }}"
+									data-attribute-value="{{ attr_value.attribute_value }}"
+									{% if attr_value.checked %} checked {% endif %}>
+									<span class="label-area">{{ attr_value.attribute_value }}</span>
 							</label>
 						</div>
 						{% endfor %}
@@ -133,13 +137,15 @@
 		</script>
 	</div>
 </div>
-<div class="row">
-	<div class="col-12">
+<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-outline-secondary btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">{{ _("Prev") }}</button>
+		<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-outline-secondary btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</button>
+		<button class="btn btn-default btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</button>
 		{% endif %}
 	</div>
 </div>
@@ -158,6 +164,4 @@
 	});
 </script>
 
-{% endblock %}
-
-
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/www/all-products/index.js b/erpnext/www/all-products/index.js
index cb9e7e6..0721056 100644
--- a/erpnext/www/all-products/index.js
+++ b/erpnext/www/all-products/index.js
@@ -54,7 +54,7 @@
 					field_filters: JSON.stringify(if_key_exists(this.field_filters)),
 					attribute_filters: JSON.stringify(if_key_exists(this.attribute_filters)),
 				});
-				window.history.pushState('filters', '', '/all-products?' + query_string);
+				window.history.pushState('filters', '', `${location.pathname}?` + query_string);
 
 				$('.page_content input').prop('disabled', true);
 				this.get_items_with_filters()
diff --git a/erpnext/www/all-products/index.py b/erpnext/www/all-products/index.py
index 7d7793a..fd6400f 100644
--- a/erpnext/www/all-products/index.py
+++ b/erpnext/www/all-products/index.py
@@ -1,6 +1,8 @@
 import frappe
 from erpnext.portal.product_configurator.utils import (get_products_for_website, get_product_settings,
 	get_field_filter_data, get_attribute_filter_data)
+from erpnext.shopping_cart.product_query import ProductQuery
+from erpnext.shopping_cart.filters import ProductFiltersBuilder
 
 sitemap = 1
 
@@ -10,22 +12,25 @@
 		search = frappe.form_dict.search
 		field_filters = frappe.parse_json(frappe.form_dict.field_filters)
 		attribute_filters = frappe.parse_json(frappe.form_dict.attribute_filters)
+		start = frappe.parse_json(frappe.form_dict.start)
 	else:
 		search = field_filters = attribute_filters = None
+		start = 0
 
-	context.items = get_products_for_website(field_filters, attribute_filters, search)
+	engine = ProductQuery()
+	context.items = engine.query(attribute_filters, field_filters, search, start)
 
 	# Add homepage as parent
 	context.parents = [{"name": frappe._("Home"), "route":"/"}]
 
 	product_settings = get_product_settings()
-	context.field_filters = get_field_filter_data() \
-		if product_settings.enable_field_filters else []
+	filter_engine = ProductFiltersBuilder()
 
-	context.attribute_filters = get_attribute_filter_data() \
-		if product_settings.enable_attribute_filters else []
+	context.field_filters = filter_engine.get_field_filters()
+	context.attribute_filters = filter_engine.get_attribute_fitlers()
 
 	context.product_settings = product_settings
-	context.page_length = product_settings.products_per_page
+	context.body_class = "product-page"
+	context.page_length = product_settings.products_per_page or 20
 
 	context.no_cache = 1
diff --git a/erpnext/www/all-products/item_row.html b/erpnext/www/all-products/item_row.html
index 9e62826..20fc9a4 100644
--- a/erpnext/www/all-products/item_row.html
+++ b/erpnext/www/all-products/item_row.html
@@ -1,24 +1,7 @@
-<div class="card mb-3">
-	<div class="row no-gutters">
-		<div class="col-md-3">
-			<div class="card-body">
-				<a class="no-underline" href="/{{ item.route }}">
-					<img class="website-image" src="{{ item.website_image or item.image or 'no-image.jpg' }}" alt="{{ item.item_name }}">
-				</a>
-			</div>
-		</div>
-		<div class="col-md-9">
-			<div class="card-body">
-				<h5 class="card-title">
-					<a class="text-dark" href="/{{ item.route }}">
-						{{ item.item_name or item.name }}
-					</a>
-				</h5>
-				<p class="card-text">
-					{{ item.website_description or item.description or '<i class="text-muted">No description</i>' }}
-				</p>
-				<a href="/{{ item.route }}" class="btn btn-sm btn-light">{{ _('More details') }}</a>
-			</div>
-		</div>
-	</div>
-</div>
+{% from "erpnext/templates/includes/macros.html" import item_card, item_card_body %}
+
+{{ item_card(
+	item.item_name or item.name, item.website_image or item.image, item.route, item.website_description or item.description,
+	item.formatted_price, item.item_group
+) }}
+