feat: pos item details with new ui

This commit is contained in:
Saqib Ansari
2020-11-11 22:59:10 +05:30
parent cc208839cf
commit fc3315a6d7
4 changed files with 160 additions and 64 deletions

View File

@@ -119,7 +119,6 @@
display: grid; display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
gap: var(--margin-lg); gap: var(--margin-lg);
flex: 1 1 0%;
padding: var(--padding-lg); padding: var(--padding-lg);
padding-top: var(--padding-xs); padding-top: var(--padding-xs);
@@ -471,6 +470,13 @@
background-color: var(--blue-200); background-color: var(--blue-200);
color: white; color: white;
} }
> .edit-cart-btn {
@extend .primary-action;
display: none;
background-color: var(--gray-100);
font-weight: 500;
}
} }
> .numpad-section { > .numpad-section {
@@ -542,7 +548,7 @@
> .invoice-name-date { > .invoice-name-date {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: end; justify-content: flex-end;
> .invoice-name { > .invoice-name {
@extend .nowrap; @extend .nowrap;
@@ -580,4 +586,108 @@
} }
} }
} }
> .item-details-container {
@extend .pos-card;
grid-column: span 4 / span 4;
display: none;
flex-direction: column;
padding: var(--padding-lg);
padding-top: var(--padding-md);
> .item-details-header {
display: flex;
justify-content: space-between;
margin-bottom: var(--margin-md);
> .close-btn {
@extend .pointer-no-select;
}
}
> .item-display {
display: flex;
> .item-name-desc-price {
flex: 1 1 0%;
display: flex;
flex-direction: column;
justify-content: flex-end;
margin-right: var(--margin-md);
> .item-name {
font-size: var(--text-3xl);
font-weight: 600;
}
> .item-desc {
font-size: var(--text-md);
font-weight: 500;
}
> .item-price {
font-size: var(--text-3xl);
font-weight: 700;
}
}
> .item-image {
display: flex;
align-items: center;
justify-content: center;
width: 11rem;
height: 11rem;
border-radius: var(--border-radius-md);
margin-left: var(--margin-md);
color: var(--gray-500);
> img {
@extend .image;
}
> .item-abbr {
@extend .abbr;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--border-radius-md);
font-size: var(--text-3xl);
width: 100%;
height: 100%;
}
}
}
> .discount-section {
display: flex;
align-items: center;
margin-bottom: var(--margin-sm);
> .item-rate {
font-weight: 500;
margin-right: var(--margin-sm);
text-decoration: line-through;
}
> .item-discount {
padding: 3px var(--padding-sm);
border-radius: var(--border-radius-sm);
background-color: var(--green-100);
color: var(--green-700);
font-size: var(--text-sm);
font-weight: 700;
}
}
> .form-container {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: var(--padding-xs);
> .auto-fetch-btn {
@extend .pointer-no-select;
margin: auto var(--margin-xs);
}
}
}
} }

View File

@@ -95,9 +95,7 @@ erpnext.PointOfSale.ItemCart = class {
<div>0.00</div> <div>0.00</div>
</div> </div>
<div class="checkout-btn">Checkout</div> <div class="checkout-btn">Checkout</div>
<div class="edit-cart-btn flex items-center justify-center h-16 pr-8 pl-8 text-center text-grey no-select pointer d-none text-md text-bold"> <div class="edit-cart-btn">Edit Cart</div>`
Edit Cart
</div>`
) )
this.$add_discount_elem = this.$component.find(".add-discount"); this.$add_discount_elem = this.$component.find(".add-discount");
@@ -176,7 +174,7 @@ erpnext.PointOfSale.ItemCart = class {
}); });
this.$component.on('click', '.checkout-btn', function() { this.$component.on('click', '.checkout-btn', function() {
if (!$(this).hasClass('bg-primary')) return; if ($(this).attr('style').indexOf('--blue-500') == -1) return;
me.events.checkout(); me.events.checkout();
me.toggle_checkout_btn(false); me.toggle_checkout_btn(false);
@@ -265,8 +263,9 @@ erpnext.PointOfSale.ItemCart = class {
toggle_item_highlight(item) { toggle_item_highlight(item) {
const $cart_item = $(item); const $cart_item = $(item);
const item_is_highlighted = $cart_item.attr("style") == "background-color:var(--gray-50);";
if (!item) { if (!item || item_is_highlighted) {
this.item_is_selected = false; this.item_is_selected = false;
this.$cart_container.find('.cart-item-wrapper').css("background-color", ""); this.$cart_container.find('.cart-item-wrapper').css("background-color", "");
} else { } else {
@@ -522,7 +521,7 @@ erpnext.PointOfSale.ItemCart = class {
const $item = this.get_cart_item(item); const $item = this.get_cart_item(item);
if (remove_item) { if (remove_item) {
$item && $item.remove(); $item && $item.next().remove() && $item.remove();
} else { } else {
const { item_code, batch_no, uom } = item; const { item_code, batch_no, uom } = item;
const search_field = batch_no ? 'batch_no' : 'item_code'; const search_field = batch_no ? 'batch_no' : 'item_code';

View File

@@ -16,35 +16,36 @@ erpnext.PointOfSale.ItemDetails = class {
prepare_dom() { prepare_dom() {
this.wrapper.append( this.wrapper.append(
`<section class="col-span-4 flex shadow rounded item-details bg-white mx-h-70 h-100 d-none"></section>` `<section class="item-details-container"></section>`
) )
this.$component = this.wrapper.find('.item-details'); this.$component = this.wrapper.find('.item-details-container');
} }
init_child_components() { init_child_components() {
this.$component.html( this.$component.html(
`<div class="details-container flex flex-col p-8 rounded w-full"> `<div class="item-details-header">
<div class="flex justify-between mb-2"> <div class="label">Item Details</div>
<div class="text-grey">ITEM DETAILS</div> <div class="close-btn">
<div class="close-btn text-grey hover-underline pointer no-select">Close</div> <svg width="32" height="32" viewBox="0 0 14 14" fill="none">
<path d="M4.93764 4.93759L7.00003 6.99998M9.06243 9.06238L7.00003 6.99998M7.00003 6.99998L4.93764 9.06238L9.06243 4.93759" stroke="#8D99A6"/>
</svg>
</div> </div>
<div class="item-defaults flex"> </div>
<div class="flex-1 flex flex-col justify-end mr-4 mb-2"> <div class="item-display">
<div class="item-name text-xl font-weight-450"></div> <div class="item-name-desc-price">
<div class="item-description text-md-0 text-grey-200"></div> <div class="item-name"></div>
<div class="item-price text-xl font-bold"></div> <div class="item-desc"></div>
</div> <div class="item-price"></div>
<div class="item-image flex items-center justify-center w-46 h-46 bg-light-grey rounded ml-4 text-6xl text-grey-100"></div>
</div> </div>
<div class="discount-section flex items-center"></div> <div class="item-image"></div>
<div class="text-grey mt-4 mb-6">STOCK DETAILS</div> </div>
<div class="form-container grid grid-cols-2 row-gap-2 col-gap-4 grid-auto-row"></div> <div class="discount-section"></div>
</div>` <div class="form-container"></div>`
) )
this.$item_name = this.$component.find('.item-name'); this.$item_name = this.$component.find('.item-name');
this.$item_description = this.$component.find('.item-description'); this.$item_description = this.$component.find('.item-desc');
this.$item_price = this.$component.find('.item-price'); this.$item_price = this.$component.find('.item-price');
this.$item_image = this.$component.find('.item-image'); this.$item_image = this.$component.find('.item-image');
this.$form_container = this.$component.find('.form-container'); this.$form_container = this.$component.find('.form-container');
@@ -52,7 +53,7 @@ erpnext.PointOfSale.ItemDetails = class {
} }
toggle_item_details_section(item) { toggle_item_details_section(item) {
const { item_code, batch_no, uom } = this.current_item; const { item_code, batch_no, uom } = this.current_item;
const item_code_is_same = item && item_code === item.item_code; const item_code_is_same = item && item_code === item.item_code;
const batch_is_same = item && batch_no == item.batch_no; const batch_is_same = item && batch_no == item.batch_no;
const uom_is_same = item && uom === item.uom; const uom_is_same = item && uom === item.uom;
@@ -104,11 +105,11 @@ erpnext.PointOfSale.ItemDetails = class {
} }
render_dom(item) { render_dom(item) {
let { item_code ,item_name, description, image, price_list_rate } = item; let { item_name, description, image, price_list_rate } = item;
function get_description_html() { function get_description_html() {
if (description) { if (description) {
description = description.indexOf('...') === -1 && description.length > 75 ? description.substr(0, 73) + '...' : description; description = description.indexOf('...') === -1 && description.length > 140 ? description.substr(0, 139) + '...' : description;
return description; return description;
} }
return ``; return ``;
@@ -118,11 +119,9 @@ erpnext.PointOfSale.ItemDetails = class {
this.$item_description.html(get_description_html()); this.$item_description.html(get_description_html());
this.$item_price.html(format_currency(price_list_rate, this.currency)); this.$item_price.html(format_currency(price_list_rate, this.currency));
if (image) { if (image) {
this.$item_image.html( this.$item_image.html(`<img src="${image}" alt="${image}">`);
`<img class="h-full" src="${image}" alt="${image}" style="object-fit: cover;">`
);
} else { } else {
this.$item_image.html(frappe.get_abbr(item_code)); this.$item_image.html(`<div class="item-abbr">${frappe.get_abbr(item_name)}</div>`);
} }
} }
@@ -130,12 +129,8 @@ erpnext.PointOfSale.ItemDetails = class {
render_discount_dom(item) { render_discount_dom(item) {
if (item.discount_percentage) { if (item.discount_percentage) {
this.$dicount_section.html( this.$dicount_section.html(
`<div class="text-grey line-through mr-4 text-md mb-2"> `<div class="item-rate">${format_currency(item.price_list_rate, this.currency)}</div>
${format_currency(item.price_list_rate, this.currency)} <div class="item-discount">${item.discount_percentage}% off</div>`
</div>
<div class="p-1 pr-3 pl-3 rounded w-fit text-bold bg-green-200 mb-2">
${item.discount_percentage}% off
</div>`
) )
this.$item_price.html(format_currency(item.rate, this.currency)); this.$item_price.html(format_currency(item.rate, this.currency));
} else { } else {
@@ -149,9 +144,7 @@ erpnext.PointOfSale.ItemDetails = class {
fields_to_display.forEach((fieldname, idx) => { fields_to_display.forEach((fieldname, idx) => {
this.$form_container.append( this.$form_container.append(
`<div class=""> `<div class="${fieldname}-control" data-fieldname="${fieldname}"></div>`
<div class="item_detail_field ${fieldname}-control" data-fieldname="${fieldname}"></div>
</div>`
) )
const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname); const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname);
@@ -185,22 +178,15 @@ erpnext.PointOfSale.ItemDetails = class {
make_auto_serial_selection_btn(item) { make_auto_serial_selection_btn(item) {
if (item.has_serial_no) { if (item.has_serial_no) {
this.$form_container.append(
`<div class="grid-filler no-select"></div>`
)
if (!item.has_batch_no) { if (!item.has_batch_no) {
this.$form_container.append( this.$form_container.append(
`<div class="grid-filler no-select"></div>` `<div class="grid-filler no-select"></div>`
) )
} }
this.$form_container.append( this.$form_container.append(
`<div class="auto-fetch-btn bg-grey-100 border border-grey text-bold rounded pt-3 pb-3 pl-6 pr-8 text-grey pointer no-select mt-2" `<div class="btn btn-sm btn-secondary auto-fetch-btn">Auto Fetch Serial Numbers</div>`
style="height: 3.3rem">
Auto Fetch Serial Numbers
</div>`
) )
this.$form_container.find('.serial_no-control').find('textarea').css('height', '9rem'); this.$form_container.find('.serial_no-control').find('textarea').css('height', '6rem');
this.$form_container.find('.serial_no-control').parent().addClass('row-span-2');
} }
} }
@@ -294,8 +280,13 @@ erpnext.PointOfSale.ItemDetails = class {
} }
frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => { frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => {
const field_control = me[`${fieldname}_control`]; const field_control = this[`${fieldname}_control`];
if (field_control) { const { item_code, batch_no, uom } = this.current_item;
const item_code_is_same = item_code === item_row.item_code;
const batch_is_same = batch_no == item_row.batch_no;
const uom_is_same = uom === item_row.uom;
if (field_control && item_code_is_same && batch_is_same && uom_is_same) {
field_control.set_value(value); field_control.set_value(value);
cur_pos.update_cart_html(item_row); cur_pos.update_cart_html(item_row);
} }
@@ -409,6 +400,6 @@ erpnext.PointOfSale.ItemDetails = class {
} }
toggle_component(show) { toggle_component(show) {
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none'); show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
} }
} }

View File

@@ -248,20 +248,16 @@ erpnext.PointOfSale.ItemSelector = class {
resize_selector(minimize) { resize_selector(minimize) {
minimize ? minimize ?
this.$component.find('.search-field').removeClass('mr-8') : this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') :
this.$component.find('.search-field').addClass('mr-8'); this.$component.find('.filter-section').css('grid-template-columns', 'repeat(12, minmax(0, 1fr))');
minimize ?
this.$component.find('.filter-section').addClass('flex-col') :
this.$component.find('.filter-section').removeClass('flex-col');
minimize ? minimize ?
this.$component.removeClass('col-span-6').addClass('col-span-2') : this.$component.css('grid-column', 'span 2 / span 2') :
this.$component.removeClass('col-span-2').addClass('col-span-6') this.$component.css('grid-column', 'span 6 / span 6')
minimize ? minimize ?
this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') : this.$items_container.css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') :
this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4') this.$items_container.css('grid-template-columns', 'repeat(4, minmax(0, 1fr))')
} }
toggle_component(show) { toggle_component(show) {