aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx')
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx139
1 files changed, 139 insertions, 0 deletions
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
new file mode 100644
index 000000000..51f84fd12
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
@@ -0,0 +1,139 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import emptyImage from "../../assets/empty.png";
+import { MerchantBackend, WithId } from "../../declaration";
+import { useInstanceProducts } from "../../hooks/product";
+import { Translate, useTranslator } from "../../i18n";
+import { FormErrors, FormProvider } from "./FormProvider";
+import { InputWithAddon } from "./InputWithAddon";
+
+type Entity = MerchantBackend.Products.ProductDetail & WithId
+
+export interface Props {
+ selected?: Entity;
+ onChange: (p?: Entity) => void;
+ products: (MerchantBackend.Products.ProductDetail & WithId)[],
+}
+
+interface ProductSearch {
+ name: string;
+}
+
+export function InputSearchProduct({ selected, onChange, products }: Props): VNode {
+ const [prodForm, setProdName] = useState<Partial<ProductSearch>>({ name: '' })
+
+ const errors: FormErrors<ProductSearch> = {
+ name: undefined
+ }
+ const i18n = useTranslator()
+
+
+ if (selected) {
+ return <article class="media">
+ <figure class="media-left">
+ <p class="image is-128x128">
+ <img src={selected.image ? selected.image : emptyImage} />
+ </p>
+ </figure>
+ <div class="media-content">
+ <div class="content">
+ <p class="media-meta"><Translate>Product id</Translate>: <b>{selected.id}</b></p>
+ <p><Translate>Description</Translate>: {selected.description}</p>
+ <div class="buttons is-right mt-5">
+ <button class="button is-info" onClick={() => onChange(undefined)}>clear</button>
+ </div>
+ </div>
+ </div>
+ </article>
+ }
+
+ return <FormProvider<ProductSearch> errors={errors} object={prodForm} valueHandler={setProdName} >
+
+ <InputWithAddon<ProductSearch>
+ name="name"
+ label={i18n`Product`}
+ tooltip={i18n`search products by it's description or id`}
+ addonAfter={<span class="icon" ><i class="mdi mdi-magnify" /></span>}
+ >
+ <div>
+ <ProductList
+ name={prodForm.name}
+ list={products}
+ onSelect={(p) => {
+ setProdName({ name: '' })
+ onChange(p)
+ }}
+ />
+ </div>
+ </InputWithAddon>
+
+ </FormProvider>
+
+}
+
+interface ProductListProps {
+ name?: string;
+ onSelect: (p: MerchantBackend.Products.ProductDetail & WithId) => void;
+ list: (MerchantBackend.Products.ProductDetail & WithId)[]
+}
+
+function ProductList({ name, onSelect, list }: ProductListProps) {
+ if (!name) {
+ /* FIXME
+ this BR is added to occupy the space that will be added when the
+ dropdown appears
+ */
+ return <div ><br /></div>
+ }
+ const filtered = list.filter(p => p.id.includes(name) || p.description.includes(name))
+
+ return <div class="dropdown is-active">
+ <div class="dropdown-menu" id="dropdown-menu" role="menu" style={{ minWidth: '20rem' }}>
+ <div class="dropdown-content">
+ {!filtered.length ?
+ <div class="dropdown-item" >
+ <Translate>no products found with that description</Translate>
+ </div> :
+ filtered.map(p => (
+ <div key={p.id} class="dropdown-item" onClick={() => onSelect(p)} style={{ cursor: 'pointer' }}>
+ <article class="media">
+ <div class="media-left">
+ <div class="image" style={{ minWidth: 64 }}><img src={p.image ? p.image : emptyImage} style={{ width: 64, height: 64 }} /></div>
+ </div>
+ <div class="media-content">
+ <div class="content">
+ <p>
+ <strong>{p.id}</strong> <small>{p.price}</small>
+ <br />
+ {p.description}
+ </p>
+ </div>
+ </div>
+ </article>
+ </div>
+ ))
+ }
+ </div>
+ </div>
+ </div>
+}