Item Alternate Image Features in e-commerce site. (#15044)

* [ADD] Added Item Alternate Image Features in e-commerce site.

* [IMP] Improved Code for Item Alternate Image Feature.

* [IMP] Improved code for Item alternative image functionality.

* Remove .css file and make a build for erpnext-web.css

* Cleanup styling for item alternate image, also add delegated handler

* Spaces -> Tabs

* Spaces -> Tabs in item.html
This commit is contained in:
Solufyin 2018-08-07 18:28:39 +05:30 committed by Nabin Hait
parent 8f0acf30bf
commit 3be899caff
6 changed files with 187 additions and 412 deletions

View File

@ -21,7 +21,7 @@ docs_app = "foundation"
app_include_js = "assets/js/erpnext.min.js"
app_include_css = "assets/css/erpnext.css"
web_include_js = "assets/js/erpnext-web.min.js"
web_include_css = "assets/erpnext/css/website.css"
web_include_css = "assets/css/erpnext-web.css"
doctype_js = {
"Communication": "public/js/communication.js",

View File

@ -1,49 +1,52 @@
{
"css/erpnext.css": [
"public/less/erpnext.less",
"public/less/hub.less"
],
"js/erpnext-web.min.js": [
"public/js/website_utils.js",
"public/js/shopping_cart.js"
],
"js/erpnext.min.js": [
"public/js/conf.js",
"public/js/utils.js",
"public/js/queries.js",
"public/js/sms_manager.js",
"public/js/utils/party.js",
"public/js/templates/address_list.html",
"public/js/templates/contact_list.html",
"public/js/controllers/stock_controller.js",
"public/js/payment/payments.js",
"public/js/controllers/taxes_and_totals.js",
"public/js/controllers/transaction.js",
"public/js/pos/pos.html",
"public/js/pos/pos_bill_item.html",
"public/js/pos/pos_bill_item_new.html",
"public/js/pos/pos_selected_item.html",
"public/js/pos/pos_item.html",
"public/js/pos/pos_tax_row.html",
"public/js/pos/customer_toolbar.html",
"public/js/pos/pos_invoice_list.html",
"public/js/payment/pos_payment.html",
"public/js/payment/payment_details.html",
"public/js/templates/item_selector.html",
"public/js/templates/employees_to_mark_attendance.html",
"public/js/utils/item_selector.js",
"public/js/help_links.js",
"public/js/agriculture/ternary_plot.js",
"public/js/templates/item_quick_entry.html",
"public/js/utils/item_quick_entry.js",
"public/js/utils/customer_quick_entry.js",
"public/js/education/student_button.html",
"public/js/education/assessment_result_tool.html",
"public/js/hub/hub_factory.js"
],
"js/item-dashboard.min.js": [
"stock/dashboard/item_dashboard.html",
"stock/dashboard/item_dashboard_list.html",
"stock/dashboard/item_dashboard.js"
]
"css/erpnext.css": [
"public/less/erpnext.less",
"public/less/hub.less"
],
"js/erpnext-web.min.js": [
"public/js/website_utils.js",
"public/js/shopping_cart.js"
],
"css/erpnext-web.css": [
"public/less/website.less"
],
"js/erpnext.min.js": [
"public/js/conf.js",
"public/js/utils.js",
"public/js/queries.js",
"public/js/sms_manager.js",
"public/js/utils/party.js",
"public/js/templates/address_list.html",
"public/js/templates/contact_list.html",
"public/js/controllers/stock_controller.js",
"public/js/payment/payments.js",
"public/js/controllers/taxes_and_totals.js",
"public/js/controllers/transaction.js",
"public/js/pos/pos.html",
"public/js/pos/pos_bill_item.html",
"public/js/pos/pos_bill_item_new.html",
"public/js/pos/pos_selected_item.html",
"public/js/pos/pos_item.html",
"public/js/pos/pos_tax_row.html",
"public/js/pos/customer_toolbar.html",
"public/js/pos/pos_invoice_list.html",
"public/js/payment/pos_payment.html",
"public/js/payment/payment_details.html",
"public/js/templates/item_selector.html",
"public/js/templates/employees_to_mark_attendance.html",
"public/js/utils/item_selector.js",
"public/js/help_links.js",
"public/js/agriculture/ternary_plot.js",
"public/js/templates/item_quick_entry.html",
"public/js/utils/item_quick_entry.js",
"public/js/utils/customer_quick_entry.js",
"public/js/education/student_button.html",
"public/js/education/assessment_result_tool.html",
"public/js/hub/hub_factory.js"
],
"js/item-dashboard.min.js": [
"stock/dashboard/item_dashboard.html",
"stock/dashboard/item_dashboard_list.html",
"stock/dashboard/item_dashboard.js"
]
}

View File

@ -1,270 +0,0 @@
.web-long-description {
font-size: 18px;
line-height: 200%;
}
.web-page-content {
margin-bottom: 30px;
}
.item-stock {
margin-bottom: 10px !important;
}
.product-link {
display: block;
text-align: center;
}
@media (max-width: 767px) {
.product-image {
height: 0px;
padding: 0px 0px 100%;
overflow: hidden;
}
}
.product-image-square {
width: 100%;
height: 0;
padding: 50% 0px;
background-size: cover;
background-repeat: no-repeat;
background-position: center top;
}
.product-image.missing-image {
width: 100%;
height: 0;
padding: 50% 0px;
background-size: cover;
background-repeat: no-repeat;
background-position: center top;
position: relative;
background-color: #EBEFF2;
}
.product-image.missing-image .octicon {
font-size: 32px;
color: #d1d8dd;
}
.product-search {
margin-bottom: 15px;
}
@media (max-width: 767px) {
.product-search {
width: 100%;
}
}
.borderless td,
.borderless th {
border-bottom: 1px solid #EBEFF2;
padding-left: 0px !important;
line-height: 1.8em !important;
}
.item-desc {
border-top: 2px solid #EBEFF2;
padding-top: 10px;
}
.featured-products {
border-top: 1px solid #EBEFF2;
}
.transaction-list-item .indicator {
font-weight: inherit;
color: #8D99A6;
}
.transaction-list-item .transaction-time {
margin-top: 5px;
}
.transaction-subheading .indicator {
font-weight: inherit;
color: #8D99A6;
}
.order-container {
margin: 50px 0px;
}
.order-container .order-item-header .h6 {
padding: 7px 15px;
}
.order-container .order-items {
margin: 30px 0px 0px;
}
.order-container .order-item-table {
margin: 0px -15px;
}
.order-container .order-item-header {
border-bottom: 1px solid #d1d8dd;
}
.order-container .order-image-col {
padding-right: 0px;
}
.order-container .order-image {
max-width: 55px;
max-height: 55px;
margin-top: -5px;
}
.order-container .order-taxes {
margin-top: 30px;
}
.order-container .order-taxes .row {
margin-top: 15px;
}
.order-container .tax-grand-total-row {
padding-top: 15px;
padding-bottom: 30px;
}
.order-container .tax-grand-total {
display: inline-block;
font-size: 16px;
font-weight: bold;
margin-top: 5px;
}
.cart-container {
margin: 50px 0px;
}
.cart-container .checkout {
margin-bottom: 15px;
}
.cart-container .cart-item-header .h6 {
padding: 7px 15px;
}
.cart-container .cart-items {
margin: 30px 0px 0px;
}
.cart-container .cart-item-table {
margin: 0px -15px;
}
.cart-container .cart-item-header {
border-bottom: 1px solid #d1d8dd;
}
.cart-container .cart-image-col {
padding-right: 0px;
}
.cart-container .cart-image {
max-width: 55px;
max-height: 55px;
margin-top: -5px;
}
.cart-container .cart-taxes {
margin-top: 30px;
}
.cart-container .cart-taxes .row {
margin-top: 15px;
}
.cart-container .tax-grand-total-row {
border-top: 1px solid #d1d8dd;
padding-top: 15px;
}
.cart-container .cart-addresses {
margin-top: 50px;
}
.cart-items-dropdown .cart-dropdown,
.item_name_dropdown {
display: none;
}
.cart-dropdown-container {
width: 400px;
padding: 15px;
}
.cart-dropdown-container .item-price {
display: block !important;
padding-bottom: 10px;
}
.cart-dropdown-container .cart-item-header {
border-bottom: 1px solid #d1d8dd;
}
.cart-dropdown-container .cart-items-dropdown {
max-height: 350px;
}
.cart-dropdown-container .cart-items-dropdown .cart-dropdown {
display: block;
margin-top: 15px;
}
.cart-dropdown-container .item_name_dropdown {
display: block;
}
.cart-dropdown-container .item-description,
.cart-dropdown-container .cart-items .checkout,
.cart-dropdown-container .item_name_and_description {
display: none;
}
.cart-dropdown-container .checkout-btn {
padding-bottom: 25px;
}
.cart-dropdown-container .col-name-description {
margin-bottom: 8px;
}
.number-spinner {
width: 100px;
margin-top: 5px;
}
.cart-btn {
border-color: #ccc;
}
.cart-qty {
text-align: center;
}
.product-list-link .row {
border-bottom: 1px solid #EBEFF2;
}
.product-list-link .row:hover {
background-color: #fafbfc;
}
.product-list-link .row > div {
padding-top: 15px;
padding-bottom: 15px;
}
.product-list-link:first-child .row {
border-top: 1px solid #EBEFF2;
}
.item-group-nav-buttons {
margin-top: 15px;
}
.footer-subscribe .btn-default {
background-color: transparent;
border: 1px solid #d1d8dd;
}
@media (min-width: 992px) {
.footer-subscribe {
max-width: 350px;
}
}
.item-group-content {
margin-top: 30px;
}
.product-image-img {
border: 1px solid #EBEFF2;
border-radius: 3px;
}
.product-text {
border-top: 1px solid #EBEFF2;
padding: 15px;
word-wrap: break-word;
height: 75px;
display: block;
/* Fallback for non-webkit */
display: -webkit-box;
max-width: 100%;
margin: 0 auto;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.product-image-wrapper {
padding-bottom: 40px;
}
.duration-bar {
display: inline-block;
color: white;
background: #8FD288;
padding: 3px;
}
.duration-invisible {
visibility: hidden;
}
.duration-value {
float: right;
}
.bar-outer-text {
color: #8FD288;
background: none;
float: none;
border: none;
}
.bom-spec {
margin-bottom: 20px;
}

View File

@ -1,7 +1,4 @@
@border-color: #d1d8dd;
@light-border-color: #EBEFF2;
@text-muted: #8D99A6;
@light-bg: #fafbfc;
@import "variables.less";
.web-long-description {
font-size: 18px;
@ -21,6 +18,12 @@
text-align: center;
}
.product-image img {
max-height: 500px;
margin: 0 auto;
}
@media (max-width: 767px) {
.product-image {
height: 0px;
@ -221,7 +224,7 @@
.cart-items-dropdown .cart-dropdown {
display:block;
margin-top:15px;
margin-top:15px;
}
.item_name_dropdown {
@ -342,4 +345,14 @@
.bom-spec {
margin-bottom: 20px;
}
}
// For Item Alternate Image
.item-alternative-image {
padding: 5px;
margin-bottom: 5px;
&:hover {
border-color: @brand-primary;
}
}

View File

@ -8,108 +8,130 @@
{% block page_content %}
{% from "erpnext/templates/includes/macros.html" import product_image %}
<div class="item-content" style="margin-top:20px;">
<div class="item-content">
<div class="product-page-content" itemscope itemtype="http://schema.org/Product">
<div class="row">
<div class="col-sm-6">
<div class="row">
{% if slideshow %}
{% include "templates/includes/slideshow.html" %}
{% else %}
{{ product_image(website_image, "product-full-image") }}
{% endif %}
</div>
<div class="col-sm-6" style="padding-left:20px;">
<h2 itemprop="name" style="margin-top: 0px;">{{ item_name }}</h2>
<p class="text-muted">
{{ _("Item Code") }}: <span itemprop="productID">{{ variant and variant.name or name }}</span></p>
<br>
<div class="item-attribute-selectors">
{% if has_variants %}
{% for d in attributes %}
{% if attribute_values[d.attribute] -%}
<div class="item-view-attribute {% if (attribute_values[d.attribute] | len)==1 -%} hidden {%- endif %}"
style="margin-bottom: 10px;">
<h6 class="text-muted">{{ _(d.attribute) }}</h6>
<select class="form-control"
style="max-width: 140px"
data-attribute="{{ d.attribute }}">
{% for value in attribute_values[d.attribute] %}
<option value="{{ value }}"
{% if selected_attributes and selected_attributes[d.attribute]==value -%}
selected
{%- elif disabled_attributes and value in disabled_attributes.get(d.attribute, []) -%}
disabled
{%- endif %}>
{{ _(value) }}
</option>
{% endfor %}
</select>
{% set slideshow_items = frappe.get_list(doctype="Website Slideshow Item", fields=["image"], filters={ "parent": doc.slideshow }) %}
<div class="col-md-1">
{%- for slideshow_item in slideshow_items -%}
{% set image_src = slideshow_item['image'] %}
{% if image_src %}
<div class="item-alternative-image border">
<img src="{{ image_src }}" height="50" weight="50" />
</div>
{%- endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
<br>
<div style="min-height: 100px; margin: 10px 0;">
<div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
<h4 class="item-price hide" itemprop="price"></h4>
<div class="item-stock hide" itemprop="availability"></div>
<div class="col-md-5">
<div class="item-image">
{% set first_image = slideshow_items[0]['image'] %}
{{ product_image(first_image, "product-full-image") }}
</div>
<div class="item-cart hide">
<div id="item-spinner">
<span style="display: inline-block">
<div class="input-group number-spinner">
<span class="input-group-btn">
<button class="btn btn-default cart-btn" data-dir="dwn">
</button>
</span>
<input class="form-control text-right cart-qty" value="1">
<span class="input-group-btn">
<button class="btn btn-default cart-btn" data-dir="up" style="margin-left:-2px;">
</div>
{% else %}
<div class="col-md-6">
{{ product_image(website_image, "product-full-image") }}
</div>
{% endif %}
<div class="col-sm-6">
<h2 itemprop="name">{{ item_name }}</h2>
<p class="text-muted">
{{ _("Item Code") }}: <span itemprop="productID">{{ variant and variant.name or name }}</span>
</p>
<br>
<div class="item-attribute-selectors">
{% if has_variants and attributes %}
{{ attributes }}
{#
{% for d in attributes %}
{% if attribute_values[d.attribute] -%}
<div class="item-view-attribute {% if (attribute_values[d.attribute] | len)==1 -%} hidden {%- endif %}"
style="margin-bottom: 10px;">
<h6 class="text-muted">{{ _(d.attribute) }}</h6>
<select class="form-control"
style="max-width: 140px"
data-attribute="{{ d.attribute }}">
{% for value in attribute_values[d.attribute] %}
<option value="{{ value }}"
{% if selected_attributes and selected_attributes[d.attribute]==value -%}
selected
{%- elif disabled_attributes and value in disabled_attributes.get(d.attribute, []) -%}
disabled
{%- endif %}>
{{ _(value) }}
</option>
{% endfor %}
</select>
</div>
{%- endif %}
{% endfor %}
#}
{% endif %}
</div>
<br>
<div>
<div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
<h4 class="item-price hide" itemprop="price"></h4>
<div class="item-stock hide" itemprop="availability"></div>
</div>
<div class="item-cart hide">
<div id="item-spinner">
<span style="display: inline-block">
<div class="input-group number-spinner">
<span class="input-group-btn">
<button class="btn btn-default cart-btn" data-dir="dwn">
</button>
</span>
<input class="form-control text-right cart-qty" value="1">
<span class="input-group-btn">
<button class="btn btn-default cart-btn" data-dir="up" style="margin-left:-2px;">
+</button>
</span>
</div>
</span>
</div>
<div id="item-add-to-cart">
<button class="btn btn-primary btn-sm">
{{ _("Add to Cart") }}</button>
</div>
<div id="item-update-cart" style="display: none;">
<a href="/cart" class='btn btn-sm btn-default'>
<i class='octicon octicon-check'></i>
{{ _("View in Cart") }}</a>
</span>
</div>
</span>
</div>
<div id="item-add-to-cart">
<button class="btn btn-primary btn-sm">
{{ _("Add to Cart") }}</button>
</div>
<div id="item-update-cart" style="display: none;">
<a href="/cart" class='btn btn-sm btn-default'>
<i class='octicon octicon-check'></i>
{{ _("View in Cart") }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row item-website-description" style="margin-top:30px; margin-bottom:20px">
<div class="col-md-12">
<div class="h6 text-uppercase">{{ _("Description") }}</div>
<div itemprop="description" class="item-desc">
{{ web_long_description or description or _("No description given") }}
</div>
</div>
</div>
{% if website_specifications -%}
<div class="row item-website-specification" style="margin-top: 40px">
<div class="col-md-12">
<div class="h6 text-uppercase">{{ _("Specifications") }}</div>
<table class="table borderless" style="width: 100%">
{% for d in website_specifications -%}
<tr>
<td class="text-muted" style="width: 30%;">{{ d.label }}</td>
<td>{{ d.description }}</td>
</tr>
{%- endfor %}
</table>
<div class="row item-website-description margin-top">
<div class="col-md-12">
<div class="h6 text-uppercase">{{ _("Description") }}</div>
<div itemprop="description" class="item-desc">
{{ web_long_description or description or _("No description given") }}
</div>
</div>
</div>
{% if website_specifications -%}
<div class="row item-website-specification margin-top">
<div class="col-md-12">
<div class="h6 text-uppercase">{{ _("Specifications") }}</div>
<table class="table">
{% for d in website_specifications -%}
<tr>
<td class="text-muted" style="width: 30%;">{{ d.label }}</td>
<td>{{ d.description }}</td>
</tr>
{%- endfor %}
</table>
</div>
</div>
{%- endif %}
</div>
{%- endif %}
</div>
</div>
<script>

View File

@ -109,6 +109,13 @@ frappe.ready(function() {
window.location.href = window.location.pathname + "?variant=" + item_code;
});
// change the item image src when alternate images are hovered
$(document.body).on('mouseover', '.item-alternative-image', (e) => {
const $alternative_image = $(e.currentTarget);
const src = $alternative_image.find('img').prop('src');
$('.item-image img').prop('src', src);
});
});
var toggle_update_cart = function(qty) {