[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant-backoffice] 03/05: joint inventory product and custom pr
From: |
gnunet |
Subject: |
[taler-merchant-backoffice] 03/05: joint inventory product and custom products |
Date: |
Thu, 24 Jun 2021 14:30:12 +0200 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a commit to branch master
in repository merchant-backoffice.
commit 520e1d886faeeda6dd56ca5451baf1e6329ab1ea
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Jun 22 12:31:30 2021 -0300
joint inventory product and custom products
---
packages/frontend/src/components/form/useField.tsx | 4 --
.../product/InventoryProductForm.stories.tsx | 58 ++++++++++++++++
.../product}/InventoryProductForm.tsx | 31 ++++++---
.../product}/NonInventoryProductForm.tsx | 30 ++++----
.../src/components/product/ProductList.tsx | 7 +-
.../paths/instance/orders/create/CreatePage.tsx | 80 +++++++++-------------
6 files changed, 128 insertions(+), 82 deletions(-)
diff --git a/packages/frontend/src/components/form/useField.tsx
b/packages/frontend/src/components/form/useField.tsx
index a04be70..8479d7a 100644
--- a/packages/frontend/src/components/form/useField.tsx
+++ b/packages/frontend/src/components/form/useField.tsx
@@ -49,10 +49,6 @@ export function useField<T>(name: keyof T): Use<T[typeof
name]> {
const initial = readField(initialObject, String(name))
const isDirty = value !== initial
const hasError = readField(errors, String(name))
- if (name == 'pricing.order_price') {
-
- console.log(value, initial, value === initial)
- }
return {
error: isDirty ? hasError : undefined,
required: !isDirty && hasError,
diff --git
a/packages/frontend/src/components/product/InventoryProductForm.stories.tsx
b/packages/frontend/src/components/product/InventoryProductForm.stories.tsx
new file mode 100644
index 0000000..6504d85
--- /dev/null
+++ b/packages/frontend/src/components/product/InventoryProductForm.stories.tsx
@@ -0,0 +1,58 @@
+/*
+ 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, FunctionalComponent } from 'preact';
+import { InventoryProductForm as TestedComponent } from
'./InventoryProductForm';
+
+
+export default {
+ title: 'Components/Product/Add',
+ component: TestedComponent,
+ argTypes: {
+ onAddProduct: { action: 'onAddProduct' },
+ },
+};
+
+function createExample<Props>(Component: FunctionalComponent<Props>, props:
Partial<Props>) {
+ const r = (args: any) => <Component {...args} />
+ r.args = props
+ return r
+}
+
+export const WithASimpleList = createExample(TestedComponent, {
+ inventory:[{
+ id: 'this id',
+ description: 'this is the description',
+ } as any]
+});
+
+export const WithAProductSelected = createExample(TestedComponent, {
+ inventory:[],
+ currentProducts: {
+ thisid: {
+ quantity: 1,
+ product: {
+ id: 'asd',
+ description: 'asdsadsad',
+ } as any
+ }
+ }
+});
diff --git
a/packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx
b/packages/frontend/src/components/product/InventoryProductForm.tsx
similarity index 76%
rename from
packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx
rename to packages/frontend/src/components/product/InventoryProductForm.tsx
index dde4505..6f05f26 100644
---
a/packages/frontend/src/paths/instance/orders/create/InventoryProductForm.tsx
+++ b/packages/frontend/src/components/product/InventoryProductForm.tsx
@@ -15,12 +15,12 @@
*/
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { FormProvider, FormErrors } from
"../../../../components/form/FormProvider";
-import { InputNumber } from "../../../../components/form/InputNumber";
-import { InputSearchProduct } from
"../../../../components/form/InputSearchProduct";
-import { MerchantBackend, WithId } from "../../../../declaration";
-import { Translate, useTranslator } from "../../../../i18n";
-import { ProductMap } from "./CreatePage";
+import { FormProvider, FormErrors } from "../form/FormProvider";
+import { InputNumber } from "../form/InputNumber";
+import { InputSearchProduct } from "../form/InputSearchProduct";
+import { MerchantBackend, WithId } from "../../declaration";
+import { Translate, useTranslator } from "../../i18n";
+import { ProductMap } from "../../paths/instance/orders/create/CreatePage";
type Form = {
product: MerchantBackend.Products.ProductDetail & WithId,
@@ -77,10 +77,19 @@ export function InventoryProductForm({ currentProducts,
onAddProduct, inventory
}
return <FormProvider<Form> errors={errors} object={state}
valueHandler={setState}>
- <InputSearchProduct selected={state.product} onChange={(p) => setState(v
=> ({ ...v, product: p }))} products={inventory} />
- { state.product && !productWithInfiniteStock && <InputNumber<Form>
name="quantity" label={i18n`Quantity`} tooltip={i18n`how many products will be
added`} /> }
- <div class="buttons is-right mt-5">
- <button class="button is-success" disabled={!state.product}
onClick={submit}><Translate>Add</Translate></button>
- </div>
+ <InputSearchProduct selected={state.product} onChange={(p) => setState(v
=> ({ ...v, product: p }))} products={inventory} />
+ { state.product && <div class="columns mt-5">
+ <div class="column is-four-fifths">
+ {!productWithInfiniteStock &&
+ <InputNumber<Form> name="quantity" label={i18n`Quantity`}
tooltip={i18n`how many products will be added`} />
+ }
+ </div>
+ <div class="column">
+ <div class="buttons is-right">
+ <button class="button is-success"
onClick={submit}><Translate>Add</Translate></button>
+ </div>
+ </div>
+ </div> }
+
</FormProvider>
}
diff --git
a/packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx
b/packages/frontend/src/components/product/NonInventoryProductForm.tsx
similarity index 85%
rename from
packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx
rename to packages/frontend/src/components/product/NonInventoryProductForm.tsx
index 34e5213..3ba4764 100644
---
a/packages/frontend/src/paths/instance/orders/create/NonInventoryProductForm.tsx
+++ b/packages/frontend/src/components/product/NonInventoryProductForm.tsx
@@ -15,21 +15,20 @@
*/
import { Fragment, h, VNode } from "preact";
import { useCallback, useEffect, useState } from "preact/hooks";
-import { FormProvider, FormErrors } from
"../../../../components/form/FormProvider";
-import { Input } from "../../../../components/form/Input";
-import { InputCurrency } from "../../../../components/form/InputCurrency";
-import { InputImage } from "../../../../components/form/InputImage";
-import { InputNumber } from "../../../../components/form/InputNumber";
-import { InputTaxes } from "../../../../components/form/InputTaxes";
-import { ConfirmModal } from "../../../../components/modal";
-import { MerchantBackend } from "../../../../declaration";
-import { useListener } from "../../../../hooks/listener";
-
+import * as yup from 'yup';
+import { FormErrors, FormProvider } from "../form/FormProvider";
+import { Input } from "../form/Input";
+import { InputCurrency } from "../form/InputCurrency";
+import { InputImage } from "../form/InputImage";
+import { InputNumber } from "../form/InputNumber";
+import { InputTaxes } from "../form/InputTaxes";
+import { MerchantBackend } from "../../declaration";
+import { useListener } from "../../hooks/listener";
+import { Translate, useTranslator } from "../../i18n";
import {
NonInventoryProductSchema as schema
-} from '../../../../schemas';
-import * as yup from 'yup';
-import { Translate, useTranslator } from "../../../../i18n";
+} from '../../schemas';
+
type Entity = MerchantBackend.Product
@@ -63,11 +62,9 @@ export function NonInventoryProductFrom({ productToEdit,
onAddProduct }: Props):
const i18n = useTranslator()
- console.log('submit form', submitForm)
-
return <Fragment>
<div class="buttons">
- <button class="button is-success" onClick={() =>
setShowCreateProduct(true)} ><Translate>add product</Translate></button>
+ <button class="button is-success" onClick={() =>
setShowCreateProduct(true)} ><Translate>add custom product</Translate></button>
</div>
{showCreateProduct && <div class="modal is-active">
<div class="modal-background " onClick={() =>
setShowCreateProduct(false)} />
@@ -125,7 +122,6 @@ export function ProductForm({ onSubscribe, initial }:
ProductProps) {
const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !==
undefined)
useEffect(() => {
- console.log('has errors', hasErrors)
onSubscribe(hasErrors ? undefined : submit)
}, [submit, hasErrors])
diff --git a/packages/frontend/src/components/product/ProductList.tsx
b/packages/frontend/src/components/product/ProductList.tsx
index b1486d6..c343b75 100644
--- a/packages/frontend/src/components/product/ProductList.tsx
+++ b/packages/frontend/src/components/product/ProductList.tsx
@@ -15,9 +15,9 @@
*/
import { h, VNode } from "preact"
import { MerchantBackend } from "../../declaration"
-import { multiplyPrice } from "../../utils/amount"
+import { Amounts } from "@gnu-taler/taler-util";
import emptyImage from "../../assets/empty.png";
-import { Translate, useTranslator } from "../../i18n";
+import { Translate } from "../../i18n";
interface Props {
list: MerchantBackend.Product[],
@@ -28,7 +28,6 @@ interface Props {
}[]
}
export function ProductList({ list, actions = [] }: Props): VNode {
- const i18n = useTranslator()
return <div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
<thead>
@@ -52,7 +51,7 @@ export function ProductList({ list, actions = [] }: Props):
VNode {
{entry.quantity === 0 ? '--' : `${entry.quantity} ${entry.unit}`}
</td>
<td >{entry.price}</td>
- <td >{multiplyPrice(entry.price, entry.quantity)}</td>
+ <td
>{Amounts.stringify(Amounts.mult(Amounts.parseOrThrow(entry.price),
entry.quantity).amount)}</td>
<td class="is-actions-cell right-sticky">
{actions.map((a, i) => {
return <div key={i} class="buttons is-right">
diff --git a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
index e4d19a8..f6550aa 100644
--- a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx
@@ -20,7 +20,7 @@
*/
import { add, isBefore, isFuture } from "date-fns";
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { Amounts } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { FormProvider, FormErrors } from
"../../../../components/form/FormProvider";
@@ -35,8 +35,8 @@ import { Duration, MerchantBackend, WithId } from
"../../../../declaration";
import { Translate, useTranslator } from "../../../../i18n";
import { OrderCreateSchema as schema } from '../../../../schemas/index';
import { rate } from "../../../../utils/amount";
-import { InventoryProductForm } from "./InventoryProductForm";
-import { NonInventoryProductFrom } from "./NonInventoryProductForm";
+import { InventoryProductForm } from
"../../../../components/product/InventoryProductForm";
+import { NonInventoryProductFrom } from
"../../../../components/product/NonInventoryProductForm";
interface Props {
onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void;
@@ -113,6 +113,9 @@ const stringIsValidJSON = (value: string) => {
}
}
+function undefinedIfEmpty<T>(obj: T): T | undefined {
+ return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj :
undefined
+}
export function CreatePage({ onCreate, onBack, instanceConfig,
instanceInventory }: Props): VNode {
const config = useConfigContext()
@@ -124,19 +127,15 @@ export function CreatePage({ onCreate, onBack,
instanceConfig, instanceInventory
const i18n = useTranslator()
- function check<T>(obj: T): T | undefined {
- return Object.keys(obj).some(k => (obj as any)[k] !== undefined) ? obj :
undefined
- }
-
const errors: FormErrors<Entity> = {
- pricing: {
- summary: !value.pricing?.summary ? i18n`required`:undefined,
- order_price: !value.pricing?.order_price ? i18n`required`: (
+ pricing: undefinedIfEmpty({
+ summary: !value.pricing?.summary ? i18n`required` : undefined,
+ order_price: !value.pricing?.order_price ? i18n`required` : (
(Amounts.parse(value.pricing.order_price)?.value || 0) <= 0 ?
i18n`must be greater than 0` : undefined
)
- },
+ }),
extra: value.extra && !stringIsValidJSON(value.extra) ? i18n`not a valid
json` : undefined,
- payments: check({
+ payments: undefinedIfEmpty({
refund_deadline: !value.payments?.refund_deadline ? i18n`required` : (
!isFuture(value.payments.refund_deadline) ? i18n`should be in the
future` : (
value.payments.pay_deadline && value.payments.refund_deadline &&
isBefore(value.payments.refund_deadline, value.payments.pay_deadline) ?
@@ -230,21 +229,24 @@ export function CreatePage({ onCreate, onBack,
instanceConfig, instanceInventory
const hasProducts = inventoryList.length > 0 || productList.length > 0
const totalPrice = Amounts.add(totalPriceInventory, totalPriceProducts)
+ const totalAsString = Amounts.stringify(totalPrice.amount);
+ const allProducts = productList.concat(inventoryList.map(asProduct))
+
useEffect(() => {
valueHandler(v => {
return ({
...v, pricing: {
- ...v.pricing!,
- // products_price: (Amounts.isZero(totalPrice.amount) ? undefined :
Amounts.stringify(totalPrice.amount))!,
- // order_price: (Amounts.isZero(totalPrice.amount) ? undefined :
Amounts.stringify(totalPrice.amount))!,
+ ...v.pricing,
+ products_price: hasProducts ? totalAsString : undefined,
+ order_price: hasProducts ? totalAsString : undefined,
// products_price: Amounts.stringify(totalPrice.amount),
// order_price: Amounts.stringify(totalPrice.amount),
}
})
})
- }, [hasProducts, totalPrice])
+ }, [hasProducts, totalAsString])
- const discountOrRise = rate(value.pricing?.order_price ||
`${config.currency}:0`, Amounts.stringify(totalPrice.amount))
+ const discountOrRise = rate(value.pricing?.order_price ||
`${config.currency}:0`, totalAsString)
// useEffect(() => {
// valueHandler(v => {
@@ -263,54 +265,40 @@ export function CreatePage({ onCreate, onBack,
instanceConfig, instanceInventory
<div class="column" />
<div class="column is-four-fifths">
- <InputGroup name="inventory_products" label={i18n`Manage products
from inventory in order`} alternative={
- inventoryList.length > 0 && <p>
- {/* // FIXME: translating plural singular */}
- {inventoryList.length} products
- with a total price of {Amounts.stringify(totalPriceInventory)}.
+ {/* // FIXME: translating plural singular */}
+ <InputGroup name="inventory_products" label={i18n`Manage products in
order`} alternative={
+ allProducts.length > 0 && <p>
+ {allProducts.length} products
+ with a total price of {totalAsString}.
</p>
- } tooltip={i18n`Manage list of products from managed inventory
included in the order.`}>
+ } tooltip={i18n`Manage list of products in the order.`}>
+
<InventoryProductForm
currentProducts={value.inventoryProducts || {}}
onAddProduct={addProductToTheInventoryList}
inventory={instanceInventory}
/>
- {inventoryList.length > 0 &&
- <ProductList list={inventoryList.map(asProduct)}
- actions={[{
- name: i18n`Remove`,
- tooltip: i18n`Remove this product from the order.`,
- handler: (e) =>
removeProductFromTheInventoryList(e.product_id!)
- }]}
- />
- }
- </InputGroup>
-
- <InputGroup name="products" label={i18n`Manage products outside of
inventory in order`} alternative={
- productList.length > 0 && <p>
- {/* // FIXME: translating plural singular */}
- {productList.length} products
- with a total price of {Amounts.stringify(totalPriceProducts)}.
- </p>
- } tooltip={i18n`Manage list of products without inventory management
included in the order.`}>
<NonInventoryProductFrom productToEdit={editingProduct}
onAddProduct={(p) => {
setEditingProduct(undefined)
return addNewProduct(p)
}} />
- {productList.length > 0 &&
- <ProductList list={productList}
+ {allProducts.length > 0 &&
+ <ProductList list={allProducts}
actions={[{
name: i18n`Remove`,
tooltip: i18n`Remove this product from the order.`,
handler: (e, index) => {
- removeFromNewProduct(index);
- setEditingProduct(e);
+ if (e.product_id) {
+ removeProductFromTheInventoryList(e.product_id)
+ } else {
+ removeFromNewProduct(index);
+ setEditingProduct(e);
+ }
}
}]}
/>
-
}
</InputGroup>
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-merchant-backoffice] branch master updated (24491ab -> 61f845b), gnunet, 2021/06/24
- [taler-merchant-backoffice] 04/05: split payment and shipping, gnunet, 2021/06/24
- [taler-merchant-backoffice] 05/05: duration picker, gnunet, 2021/06/24
- [taler-merchant-backoffice] 01/05: add qr, gnunet, 2021/06/24
- [taler-merchant-backoffice] 02/05: from star to alert, using amounts api, gnunet, 2021/06/24
- [taler-merchant-backoffice] 03/05: joint inventory product and custom products,
gnunet <=