[IMP] pos_self_order: add search bar
After the merge of kiosk and mobile self, the search bar had not been reimplemented. In this commit, we add it. closes odoo/odoo#137063 Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component, useEffect, useRef } from "@odoo/owl";
|
||||
import { Component, useEffect, useRef, useState } from "@odoo/owl";
|
||||
import { useSelfOrder } from "@pos_self_order/app/self_order_service";
|
||||
import { ProductCard } from "@pos_self_order/app/components/product_card/product_card";
|
||||
import { CancelPopup } from "@pos_self_order/app/components/cancel_popup/cancel_popup";
|
||||
import { useService, useChildRef } from "@web/core/utils/hooks";
|
||||
import { OrderWidget } from "@pos_self_order/app/components/order_widget/order_widget";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { fuzzyLookup } from "@web/core/utils/search";
|
||||
|
||||
export class ProductListPage extends Component {
|
||||
static template = "pos_self_order.ProductListPage";
|
||||
@@ -19,7 +20,10 @@ export class ProductListPage extends Component {
|
||||
this.productsList = useRef("productsList");
|
||||
this.categoryList = useRef("categoryList");
|
||||
this.currentProductCard = useChildRef();
|
||||
|
||||
this.state = useState({
|
||||
search: false,
|
||||
searchInput: "",
|
||||
});
|
||||
this.categoryButton = Object.fromEntries(
|
||||
Array.from(this.selfOrder.categoryList).map((category) => {
|
||||
return [category.name, useRef(`category_${category.name}`)];
|
||||
@@ -73,12 +77,32 @@ export class ProductListPage extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
get width() {
|
||||
return window.innerWidth;
|
||||
}
|
||||
|
||||
get productCategory() {
|
||||
const productByCat = this.selfOrder.productsGroupedByCategory;
|
||||
const currentCategory = this.selfOrder.currentCategory;
|
||||
return productByCat[currentCategory] ? productByCat[currentCategory] : [];
|
||||
}
|
||||
|
||||
focusSearch() {
|
||||
this.state.search = !this.state.search;
|
||||
|
||||
if (!this.state.search) {
|
||||
this.state.searchInput = "";
|
||||
}
|
||||
}
|
||||
|
||||
getFilteredProducts(products) {
|
||||
return fuzzyLookup(
|
||||
this.state.searchInput,
|
||||
products,
|
||||
(product) => product.name + product.description_sale
|
||||
);
|
||||
}
|
||||
|
||||
scrollTo(ref = null, { behavior = "smooth" } = {}) {
|
||||
this.productsList.el.scroll({
|
||||
top: ref?.el ? ref.el.offsetTop - this.productsList.el.offsetTop : 0,
|
||||
|
||||
+26
-14
@@ -1,18 +1,28 @@
|
||||
.category-list {
|
||||
.navbar-container {
|
||||
height: 225px;
|
||||
overflow-y: hidden;
|
||||
|
||||
.category-item {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
padding: 0px !important;
|
||||
.category-list {
|
||||
width: calc(100% - 50px);
|
||||
overflow-y: hidden;
|
||||
|
||||
.category-item {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
.search-button {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.product-list {
|
||||
overflow-y: scroll;
|
||||
margin-top: 225px;
|
||||
@@ -34,16 +44,18 @@
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.category-list {
|
||||
.navbar-container {
|
||||
height: 60px;
|
||||
overflow-y: hidden;
|
||||
|
||||
.category-item {
|
||||
height: 40px;
|
||||
}
|
||||
.category-list {
|
||||
.category-item {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: none;
|
||||
img {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-19
@@ -3,23 +3,39 @@
|
||||
<t t-name="pos_self_order.ProductListPage">
|
||||
<div class="vh-100 overflow-hidden">
|
||||
<!-- Categories selector -->
|
||||
<nav id="listgroup-categories" class="navbar category-list fixed-top d-flex flex-column flex-wrap gap-3 align-content-start py-2 px-3 shadow-sm bg-view" t-ref="categoryList">
|
||||
<a
|
||||
t-foreach="Array.from(selfOrder.categoryList)"
|
||||
t-as="category"
|
||||
t-key="category.id"
|
||||
t-ref="category_{{category.name}}"
|
||||
t-attf-class="nav-link btn btn-light d-flex flex-column align-items-center justify-content-center category-item h-100 border px-1 overflow-hidden"
|
||||
t-attf-href="#scrollspy_{{category.id}}">
|
||||
<img t-if="category.has_image"
|
||||
class="rounded w-100 h-75 ratio ratio-1x1"
|
||||
t-attf-src="/pos-self/get-category-image/{{ category.id }}"
|
||||
alt="Product image"
|
||||
loading="lazy"
|
||||
onerror="this.remove()" />
|
||||
<span class="w-100 fs-5 text-truncate" t-esc="category.name" />
|
||||
</a>
|
||||
</nav>
|
||||
<div class="navbar-container w-100 fixed-top bg-view shadow-sm">
|
||||
<div t-attf-class="{{ this.state.search and width > 576 ? 'h-75' : 'h-100' }} {{ !this.state.search || width > 576 ? '' : 'd-none' }} d-flex w-100">
|
||||
<nav id="listgroup-categories" class="navbar category-list d-flex flex-column flex-wrap gap-3 align-content-start py-2 px-3 h-100" t-ref="categoryList">
|
||||
<a
|
||||
t-foreach="Array.from(selfOrder.categoryList)"
|
||||
t-as="category"
|
||||
t-key="category.id"
|
||||
t-ref="category_{{category.name}}"
|
||||
t-attf-class="nav-link btn btn-light d-flex flex-column align-items-center justify-content-center category-item h-100 border px-1 overflow-hidden"
|
||||
t-attf-href="#scrollspy_{{category.id}}">
|
||||
<img t-if="category.has_image"
|
||||
class="rounded w-100 h-75 ratio ratio-1x1"
|
||||
t-attf-src="/pos-self/get-category-image/{{ category.id }}"
|
||||
alt="Product image"
|
||||
loading="lazy"
|
||||
onerror="this.remove()" />
|
||||
<span class="w-100 fs-5 text-truncate" t-esc="category.name" />
|
||||
</a>
|
||||
</nav>
|
||||
<button t-if="!this.state.search" class="btn btn-link text-dark px-3 search-button" t-on-click="focusSearch">
|
||||
<i class="oi oi-search px-1"/>
|
||||
</button>
|
||||
</div>
|
||||
<div t-if="this.state.search" t-attf-class="{{ this.state.search and width > 576 ? 'h-25' : 'h-100' }} d-flex align-items-center w-100">
|
||||
<input type="text"
|
||||
class="form-control p-3 border-0 shadow-none"
|
||||
placeholder="Search product"
|
||||
t-model="state.searchInput"/>
|
||||
<button class="btn bg-200 rounded" t-on-click="focusSearch">
|
||||
<i class="fa fa-times"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Products list -->
|
||||
<div
|
||||
@@ -38,11 +54,14 @@
|
||||
t-attf-categId="{{category.id}}"
|
||||
t-ref="productsWithCategory_{{category.name}}"
|
||||
class="product-list-category row p-3">
|
||||
<h2 class="pt-3 pb-1 px-4 m-0 fs-3" t-esc="category.name" />
|
||||
<t t-foreach="selfOrder.productsGroupedByCategory[category.name]" t-as="product" t-key="product.id">
|
||||
<t t-set="products" t-value="selfOrder.productsGroupedByCategory[category.name]" />
|
||||
<t t-set="availableProducts" t-value="!state.searchInput ? products : getFilteredProducts(products)" />
|
||||
<h2 t-if="availableProducts.length > 0" class="pt-3 pb-1 px-4 m-0 fs-3" t-esc="category.name" />
|
||||
<t t-foreach="availableProducts" t-as="product" t-key="product.id">
|
||||
<ProductCard product="product" currentProductCard="product.id === selfOrder.lastEditedProductId and currentProductCard" />
|
||||
</t>
|
||||
</section>
|
||||
<p t-if="getFilteredProducts(selfOrder.products).length === 0" class="mx-auto mt-3 text-center">No products found</p>
|
||||
</div>
|
||||
|
||||
<!-- Page buttons -->
|
||||
|
||||
Reference in New Issue
Block a user