[IMP] point_of_sale: improve product product_list
In mobile, the product list can now be displayed as a grid or a list. Items in the list and grid views will now always have the same author, to maintain consistency. closes odoo/odoo#126861 Signed-off-by: Robin Heinz (rhe) <rhe@odoo.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import { BackButton } from "@point_of_sale/app/navbar/back_button/back_button";
|
||||
import { Component, useState, useExternalListener } from "@odoo/owl";
|
||||
import { ClosePosPopup } from "@point_of_sale/app/navbar/closing_popup/closing_popup";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen";
|
||||
|
||||
export class Navbar extends Component {
|
||||
static template = "point_of_sale.Navbar";
|
||||
@@ -107,4 +108,14 @@ export class Navbar extends Component {
|
||||
showBackButton() {
|
||||
return this.pos.showBackButton() && this.ui.isSmall;
|
||||
}
|
||||
|
||||
toggleProductView() {
|
||||
const newView = this.pos.productListView === "grid" ? "list" : "grid";
|
||||
window.localStorage.setItem("productListView", newView);
|
||||
this.pos.productListView = this.pos.productListView === "grid" ? "list" : "grid";
|
||||
}
|
||||
|
||||
get showToggleProductView() {
|
||||
return this.pos.mainScreen.component === ProductScreen && this.ui.isSmall;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,11 @@
|
||||
Close Session
|
||||
</span>
|
||||
</li>
|
||||
<li t-if="showToggleProductView" class="menu-item navbar-button close-button" t-on-click="toggleProductView">
|
||||
<span>
|
||||
Switch Product View
|
||||
</span>
|
||||
</li>
|
||||
<li t-if="this.env.debug" t-on-click="() => debug.toggleWidget()" class="menu-item navbar-button">
|
||||
<span>Debug Window</span>
|
||||
</li>
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="point_of_sale.ProductItem" owl="1">
|
||||
<article class="product" tabindex="0" t-on-keypress="spaceClickProduct"
|
||||
<article t-attf-class="product {{this.pos.productListViewMode}}" tabindex="0" t-on-keypress="spaceClickProduct"
|
||||
t-on-click="() => this.pos.addProductToCurrentOrder(props.product)"
|
||||
t-att-data-product-id="props.product.id"
|
||||
t-attf-aria-labelledby="article_product_{{props.product.id}}">
|
||||
<t t-if="props.product.image_128">
|
||||
<div class="product-img">
|
||||
<img t-att-src="imageUrl" t-att-alt="props.product.display_name" />
|
||||
</div>
|
||||
</t>
|
||||
<div class="product-img">
|
||||
<img t-if="props.product.image_128" t-att-src="imageUrl" t-att-alt="props.product.display_name" />
|
||||
</div>
|
||||
<div class="product-content">
|
||||
<div t-attf-class="#{props.product.image_128 ? 'product-name' : 'product-name no-image' }" t-attf-id="article_product_{{props.product.id}}">
|
||||
<t t-esc="props.product.display_name" />
|
||||
|
||||
-1
@@ -20,7 +20,6 @@ export class ProductsWidgetControlPanel extends Component {
|
||||
this.updateSearch = debounce(this.updateSearch, 100);
|
||||
this.state = useState({ mobileSearchBarIsShown: false, isMobile: false });
|
||||
this.productsWidgetControl = useRef("products-widget-control");
|
||||
this.ui = useService("ui");
|
||||
|
||||
const toggleIsMobile = () => this.toggleIsMobile();
|
||||
onMounted(() => {
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
hasNoCategories="hasNoCategories"
|
||||
loadProductFromServer.bind="onPressEnterKey" />
|
||||
<div class="product-list-container">
|
||||
<div t-if="productsToDisplay.length != 0 and posHasValidProduct" class="product-list">
|
||||
<div t-if="productsToDisplay.length != 0 and posHasValidProduct" t-attf-class="{{this.pos.productListViewMode}} product-list">
|
||||
<t t-foreach="productsToDisplay" t-as="product" t-key="product.id">
|
||||
<ProductItem
|
||||
product="product" />
|
||||
|
||||
@@ -80,18 +80,20 @@ export class PosStore extends Reactive {
|
||||
"number_buffer",
|
||||
"barcode_reader",
|
||||
"hardware_proxy",
|
||||
"ui",
|
||||
];
|
||||
constructor() {
|
||||
super();
|
||||
this.ready = this.setup(...arguments).then(() => this);
|
||||
}
|
||||
// use setup instead of constructor because setup can be patched.
|
||||
async setup(env, { popup, orm, number_buffer, hardware_proxy, barcode_reader }) {
|
||||
async setup(env, { popup, orm, number_buffer, hardware_proxy, barcode_reader, ui }) {
|
||||
this.env = env;
|
||||
this.orm = orm;
|
||||
this.popup = popup;
|
||||
this.numberBuffer = number_buffer;
|
||||
this.barcodeReader = barcode_reader;
|
||||
this.ui = ui;
|
||||
|
||||
this.db = new PosDB(); // a local database used to search trough products and categories & store pending orders
|
||||
this.unwatched = markRaw({});
|
||||
@@ -120,6 +122,7 @@ export class PosStore extends Reactive {
|
||||
|
||||
this.numpadMode = "quantity";
|
||||
this.mobile_pane = "right";
|
||||
this.productListView = window.localStorage.getItem("productListView") || "grid";
|
||||
|
||||
// Record<orderlineId, { 'qty': number, 'orderline': { qty: number, refundedQty: number, orderUid: string }, 'destinationOrderUid': string }>
|
||||
this.toRefundLines = {};
|
||||
@@ -174,7 +177,9 @@ export class PosStore extends Reactive {
|
||||
this.preloadImages();
|
||||
this.showScreen("ProductScreen");
|
||||
}
|
||||
|
||||
get productListViewMode() {
|
||||
return this.productListView && this.ui.isSmall ? this.productListView : "grid";
|
||||
}
|
||||
getDefaultSearchDetails() {
|
||||
return {
|
||||
fieldName: "RECEIPT_NUMBER",
|
||||
|
||||
@@ -852,7 +852,7 @@ td {
|
||||
|
||||
/* ********* The product list ********* */
|
||||
|
||||
.pos .product-list {
|
||||
.pos .product-list.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(122px, 1fr));
|
||||
padding: 3px;
|
||||
@@ -1112,28 +1112,49 @@ td {
|
||||
/* e) the product */
|
||||
|
||||
.pos .product {
|
||||
position:relative;
|
||||
background:#fff;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position:relative;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
background:#fff;
|
||||
align-items: stretch;
|
||||
}
|
||||
.pos .product.grid {
|
||||
height: 170px;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pos .product.list {
|
||||
height: 90px;
|
||||
flex-direction: row-reverse;
|
||||
margin: 3px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pos .product-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.pos .product.list .product-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pos .product.grid .product-img {
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
margin: 3px;
|
||||
}
|
||||
.pos .product.list .product-img {
|
||||
height: 84px;
|
||||
width: 84px;
|
||||
margin: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pos .product .product-img img {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.pos .product:active {
|
||||
box-shadow: 0 0 0 3px $primary;
|
||||
|
||||
Reference in New Issue
Block a user