diff options
author | Sebastian <sebasjm@gmail.com> | 2021-06-22 11:20:49 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-06-24 09:29:41 -0300 |
commit | a75d12bb47ceaf17648a8fcc7c9d0e515ae2fcae (patch) | |
tree | d783e019c407f728d09abddd7b656520a897da74 /packages | |
parent | ba74497459c00a664eef9ccb390e1b917aae08ac (diff) | |
download | merchant-backoffice-a75d12bb47ceaf17648a8fcc7c9d0e515ae2fcae.tar.gz merchant-backoffice-a75d12bb47ceaf17648a8fcc7c9d0e515ae2fcae.tar.bz2 merchant-backoffice-a75d12bb47ceaf17648a8fcc7c9d0e515ae2fcae.zip |
from star to alert, using amounts api
Diffstat (limited to 'packages')
20 files changed, 137 insertions, 104 deletions
diff --git a/packages/frontend/src/components/form/FormProvider.tsx b/packages/frontend/src/components/form/FormProvider.tsx index d1fb77b..4855553 100644 --- a/packages/frontend/src/components/form/FormProvider.tsx +++ b/packages/frontend/src/components/form/FormProvider.tsx @@ -65,7 +65,7 @@ export function useFormContext<T>() { } export type FormErrors<T> = { - [P in keyof T]?: string + [P in keyof T]?: string | FormErrors<T[P]> } export type FormtoStr<T> = { diff --git a/packages/frontend/src/components/form/Input.tsx b/packages/frontend/src/components/form/Input.tsx index 4147f2c..f7e0b5c 100644 --- a/packages/frontend/src/components/form/Input.tsx +++ b/packages/frontend/src/components/form/Input.tsx @@ -59,8 +59,8 @@ export function Input<T>({ name, readonly, placeholder, tooltip, label, expand, onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void => onChange(fromStr(e.currentTarget.value))} /> {help} {children} - { required && <span class="icon is-danger is-right"> - <i class="mdi mdi-star" /> + { required && <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> </span> } </p> {error && <p class="help is-danger">{error}</p>} diff --git a/packages/frontend/src/components/form/InputDate.tsx b/packages/frontend/src/components/form/InputDate.tsx index 654d608..614e44a 100644 --- a/packages/frontend/src/components/form/InputDate.tsx +++ b/packages/frontend/src/components/form/InputDate.tsx @@ -36,7 +36,7 @@ export function InputDate<T>({ name, readonly, label, placeholder, help, tooltip const [opened, setOpened] = useState(false) const i18n = useTranslator() - const { error, value, onChange } = useField<T>(name); + const { error, required, value, onChange } = useField<T>(name); let strValue = '' if (!value) { @@ -61,12 +61,15 @@ export function InputDate<T>({ name, readonly, label, placeholder, help, tooltip <div class="field-body is-flex-grow-3"> <div class="field"> <div class="field has-addons"> - <p class={expand ? "control is-expanded" : "control"}> + <p class={expand ? "control is-expanded has-icons-right" : "control has-icons-right"}> <input class="input" type="text" readonly value={strValue} placeholder={placeholder} onClick={() => { if (!readonly) setOpened(true) }} /> + { required && <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> + </span> } {help} </p> <div class="control" onClick={() => { if (!readonly) setOpened(true) }}> diff --git a/packages/frontend/src/components/form/InputGroup.tsx b/packages/frontend/src/components/form/InputGroup.tsx index 0720cfb..8d1f512 100644 --- a/packages/frontend/src/components/form/InputGroup.tsx +++ b/packages/frontend/src/components/form/InputGroup.tsx @@ -36,11 +36,14 @@ export function InputGroup<T>({ name, label, children, tooltip, alternative }: P return <div class="card"> <header class="card-header"> - <p class={!group?.hasError ? "card-header-title" : "card-header-title has-text-danger"}> + <p class="card-header-title"> {label} {tooltip && <span class="icon" data-tooltip={tooltip}> <i class="mdi mdi-information" /> </span>} + {group?.hasError && <span class="icon has-text-danger" data-tooltip={tooltip}> + <i class="mdi mdi-alert" /> + </span>} </p> <button class="card-header-icon" aria-label="more options" onClick={(): void => setActive(!active)}> <span class="icon"> diff --git a/packages/frontend/src/components/form/InputWithAddon.tsx b/packages/frontend/src/components/form/InputWithAddon.tsx index cb84c86..d2905df 100644 --- a/packages/frontend/src/components/form/InputWithAddon.tsx +++ b/packages/frontend/src/components/form/InputWithAddon.tsx @@ -19,7 +19,6 @@ * @author Sebastian Javier Marchano (sebasjm) */ import { ComponentChildren, h, VNode } from "preact"; -import { useTranslator } from "../../i18n"; import { InputProps, useField } from "./useField"; export interface Props<T> extends InputProps<T> { @@ -60,8 +59,8 @@ export function InputWithAddon<T>({ name, readonly, addonBefore, children, expan placeholder={placeholder} readonly={readonly} name={String(name)} value={toStr(value)} onChange={(e): void => onChange(fromStr(e.currentTarget.value))} /> - {required && <span class="icon is-danger is-right"> - <i class="mdi mdi-star" /> + {required && <span class="icon has-text-danger is-right"> + <i class="mdi mdi-alert" /> </span>} {help} {children} diff --git a/packages/frontend/src/components/form/useField.tsx b/packages/frontend/src/components/form/useField.tsx index c552711..a04be70 100644 --- a/packages/frontend/src/components/form/useField.tsx +++ b/packages/frontend/src/components/form/useField.tsx @@ -29,7 +29,7 @@ interface Use<V> { initial: any; onChange: (v: V) => void; toStr: (f: V | undefined) => string; - fromStr: (v: string) => V + fromStr: (v: string) => V } export function useField<T>(name: keyof T): Use<T[typeof name]> { @@ -42,16 +42,20 @@ export function useField<T>(name: keyof T): Use<T[typeof name]> { return setValueDeeper(prev, String(field).split('.'), value) }) } - - const defaultToString = ((f?: V):string => String(!f ? '': f)) - const defaultFromString = ((v: string):V => v as any) + + const defaultToString = ((f?: V): string => String(!f ? '' : f)) + const defaultFromString = ((v: string): V => v as any) const value = readField(object, String(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, + required: !isDirty && hasError, value, initial, onChange: updateField(name) as any, @@ -73,7 +77,7 @@ const readField = (object: any, name: string) => { const setValueDeeper = (object: any, names: string[], value: any): any => { if (names.length === 0) return value const [head, ...rest] = names - return {...object, [head]: setValueDeeper(object[head] || {}, rest, value) } + return { ...object, [head]: setValueDeeper(object[head] || {}, rest, value) } } export interface InputProps<T> { diff --git a/packages/frontend/src/components/form/useGroupField.tsx b/packages/frontend/src/components/form/useGroupField.tsx index 0a809e4..a73f464 100644 --- a/packages/frontend/src/components/form/useGroupField.tsx +++ b/packages/frontend/src/components/form/useGroupField.tsx @@ -30,8 +30,11 @@ export function useGroupField<T>(name: keyof T): Use { if (!f) return {}; - const RE = new RegExp(`^${name}`); return { - hasError: Object.keys(f.errors).some(e => RE.test(e)) + hasError: readField(f.errors, String(name)) }; } + +const readField = (object: any, name: string) => { + return name.split('.').reduce((prev, current) => prev && prev[current], object) +} diff --git a/packages/frontend/src/components/product/ProductForm.tsx b/packages/frontend/src/components/product/ProductForm.tsx index 09b5744..9e8ac97 100644 --- a/packages/frontend/src/components/product/ProductForm.tsx +++ b/packages/frontend/src/components/product/ProductForm.tsx @@ -51,6 +51,7 @@ export function ProductForm({ onSubscribe, initial, alreadyExist, }: Props) { taxes: [], next_restock: { t_ms: 'never' }, ...initial, + price: ':0', stock: !initial || initial.total_stock === -1 ? undefined : { current: initial.total_stock || 0, lost: initial.total_lost || 0, diff --git a/packages/frontend/src/declaration.d.ts b/packages/frontend/src/declaration.d.ts index 82bc694..1722a3d 100644 --- a/packages/frontend/src/declaration.d.ts +++ b/packages/frontend/src/declaration.d.ts @@ -844,9 +844,6 @@ export namespace MerchantBackend { fulfillment_url?: string; } - // FIXME: Where is this being used? - // type ProductSpecification = (MinimalInventoryProduct | Product); - interface MinimalInventoryProduct { // Which product is requested (here mandatory!) product_id: string; diff --git a/packages/frontend/src/paths/admin/create/CreatePage.tsx b/packages/frontend/src/paths/admin/create/CreatePage.tsx index 196284e..c9276d7 100644 --- a/packages/frontend/src/paths/admin/create/CreatePage.tsx +++ b/packages/frontend/src/paths/admin/create/CreatePage.tsx @@ -130,7 +130,7 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack}><Translate>Cancel</Translate></button>} <AsyncButton onClick={submit} disabled={!isTokenSet || hasErrors} data-tooltip={ - hasErrors ? i18n`Need to complete fields marked with a star and choose authorization method` : 'confirm operation' + hasErrors ? i18n`Need to complete marked fields and choose authorization method` : 'confirm operation' }><Translate>Confirm</Translate></AsyncButton> </div> diff --git a/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx b/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx index daf48c0..a1d37e2 100644 --- a/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx +++ b/packages/frontend/src/paths/instance/orders/create/Create.stories.tsx @@ -43,7 +43,7 @@ export const Example = createExample(TestedComponent, { default_max_deposit_fee: '', default_max_wire_fee: '', default_pay_delay: { - d_ms: 'forever' + d_ms: new Date().getTime() }, default_wire_fee_amortization: 1 }, diff --git a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx index ecc2f0a..e4d19a8 100644 --- a/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/orders/create/CreatePage.tsx @@ -19,10 +19,10 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { add } from "date-fns"; +import { add, isBefore, isFuture } from "date-fns"; +import { AmountJson, Amounts } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; -import * as yup from 'yup'; import { FormProvider, FormErrors } from "../../../../components/form/FormProvider"; import { Input } from "../../../../components/form/Input"; import { InputCurrency } from "../../../../components/form/InputCurrency"; @@ -34,7 +34,7 @@ import { useConfigContext } from "../../../../context/config"; import { Duration, MerchantBackend, WithId } from "../../../../declaration"; import { Translate, useTranslator } from "../../../../i18n"; import { OrderCreateSchema as schema } from '../../../../schemas/index'; -import { multiplyPrice, rate, sumPrices } from "../../../../utils/amount"; +import { rate } from "../../../../utils/amount"; import { InventoryProductForm } from "./InventoryProductForm"; import { NonInventoryProductFrom } from "./NonInventoryProductForm"; @@ -59,14 +59,15 @@ function with_defaults(config: InstanceConfig): Partial<Entity> { return { inventoryProducts: {}, products: [], - pricing: {} as any, + pricing: { + }, payments: { max_wire_fee: config.default_max_wire_fee, max_fee: config.default_max_deposit_fee, wire_fee_amortization: config.default_wire_fee_amortization, pay_deadline: defaultPayDeadline, refund_deadline: defaultPayDeadline, - } as Partial<Payments>, + }, extra: '' }; } @@ -98,24 +99,60 @@ interface Payments { interface Entity { inventoryProducts: ProductMap, products: MerchantBackend.Product[], - pricing: Pricing; - payments: Payments; + pricing: Partial<Pricing>; + payments: Partial<Payments>; extra: string; } +const stringIsValidJSON = (value: string) => { + try { + JSON.parse(value.trim()) + return true + } catch { + return false + } +} + + export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory }: Props): VNode { + const config = useConfigContext() + const zero = Amounts.getZero(config.currency) const [value, valueHandler] = useState(with_defaults(instanceConfig)) - // const [errors, setErrors] = useState<FormErrors<Entity>>({}) const inventoryList = Object.values(value.inventoryProducts || {}) const productList = Object.values(value.products || {}) - let errors: FormErrors<Entity> = {} - try { - schema.validateSync(value, { abortEarly: false }) - } catch (err) { - const yupErrors = err.inner as yup.ValidationError[] - errors = yupErrors.reduce((prev, cur) => !cur.path ? prev : ({ ...prev, [cur.path]: cur.message }), {}) + 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`: ( + (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({ + 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) ? + i18n`pay deadline cannot be before refund deadline` : undefined + ) + ), + pay_deadline: !value.payments?.pay_deadline ? i18n`required` : ( + !isFuture(value.payments.pay_deadline) ? i18n`should be in the future` : undefined + ), + auto_refund_deadline: !value.payments?.auto_refund_deadline ? undefined : ( + !isFuture(value.payments.auto_refund_deadline) ? i18n`should be in the future` : undefined + ), + delivery_date: !value.payments?.delivery_date ? undefined : ( + !isFuture(value.payments.delivery_date) ? i18n`should be in the future` : undefined + ), + }), } const hasErrors = Object.keys(errors).some(k => (errors as any)[k] !== undefined) @@ -147,8 +184,6 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory onCreate(request); } - const config = useConfigContext() - const addProductToTheInventoryList = (product: MerchantBackend.Products.ProductDetail & WithId, quantity: number) => { valueHandler(v => { const inventoryProducts = { ...v.inventoryProducts } @@ -182,38 +217,44 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory const [editingProduct, setEditingProduct] = useState<MerchantBackend.Product | undefined>(undefined) - const totalPriceInventory = inventoryList.reduce((prev, cur) => sumPrices(prev, multiplyPrice(cur.product.price, cur.quantity)), `${config.currency}:0`) - const totalPriceProducts = productList.reduce((prev, cur) => sumPrices(prev, multiplyPrice(cur.price, cur.quantity)), `${config.currency}:0`) + const totalPriceInventory = inventoryList.reduce((prev, cur) => { + const p = Amounts.parseOrThrow(cur.product.price) + return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount + }, zero) + + const totalPriceProducts = productList.reduce((prev, cur) => { + const p = Amounts.parseOrThrow(cur.price) + return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount + }, zero) const hasProducts = inventoryList.length > 0 || productList.length > 0 - const totalPrice = sumPrices(totalPriceInventory, totalPriceProducts) + const totalPrice = Amounts.add(totalPriceInventory, totalPriceProducts) useEffect(() => { valueHandler(v => { return ({ ...v, pricing: { ...v.pricing!, - products_price: totalPrice, - order_price: totalPrice, + // products_price: (Amounts.isZero(totalPrice.amount) ? undefined : Amounts.stringify(totalPrice.amount))!, + // order_price: (Amounts.isZero(totalPrice.amount) ? undefined : Amounts.stringify(totalPrice.amount))!, + // products_price: Amounts.stringify(totalPrice.amount), + // order_price: Amounts.stringify(totalPrice.amount), } }) }) }, [hasProducts, totalPrice]) + const discountOrRise = rate(value.pricing?.order_price || `${config.currency}:0`, Amounts.stringify(totalPrice.amount)) - const discountOrRise = rate(value.pricing?.order_price || `${config.currency}:0`, totalPrice) - - useEffect(() => { - valueHandler(v => { - return ({ - ...v, pricing: { - ...v.pricing! - } - }) - }) - }, [value.pricing?.order_price]) - - const i18n = useTranslator() + // useEffect(() => { + // valueHandler(v => { + // return ({ + // ...v, pricing: { + // ...v.pricing! + // } + // }) + // }) + // }, [value.pricing?.order_price]) return <div> @@ -223,11 +264,10 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory <div class="column is-four-fifths"> <InputGroup name="inventory_products" label={i18n`Manage products from inventory in order`} alternative={ - inventoryList.length > 0 && - // FIXME: translating plural singular - <p> + inventoryList.length > 0 && <p> + {/* // FIXME: translating plural singular */} {inventoryList.length} products - with a total price of {totalPriceInventory}. + with a total price of {Amounts.stringify(totalPriceInventory)}. </p> } tooltip={i18n`Manage list of products from managed inventory included in the order.`}> <InventoryProductForm @@ -249,9 +289,9 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory <InputGroup name="products" label={i18n`Manage products outside of inventory in order`} alternative={ productList.length > 0 && <p> - // FIXME: translating plural singular + {/* // FIXME: translating plural singular */} {productList.length} products - with a total price of {totalPriceProducts}. + 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) => { @@ -280,7 +320,7 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory <InputCurrency name="pricing.products_price" label={i18n`Total price`} readonly tooltip={i18n`total product price added up`} /> <InputCurrency name="pricing.order_price" label={i18n`Total price`} - addonAfter={value.pricing?.order_price !== totalPrice && (discountOrRise < 1 ? + addonAfter={discountOrRise > 0 && (discountOrRise < 1 ? `discount of %${Math.round((1 - discountOrRise) * 100)}` : `rise of %${Math.round((discountOrRise - 1) * 100)}`) } @@ -302,14 +342,14 @@ export function CreatePage({ onCreate, onBack, instanceConfig, instanceInventory <InputLocation name="payments.delivery_location" /> </InputGroup>} - <InputCurrency name="payments.max_fee" label={i18n`Maximum deposit fee`} tooltip={i18n`Maximum deposit fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`} /> - <InputCurrency name="payments.max_wire_fee" label={i18n`Maximum wire fee`} tooltip={i18n`Maximum aggregate wire fees the merchant is willing to cover for this order. Wire fees exceeding this amount are to be covered by the customers.`}/> - <Input name="payments.wire_fee_amortization" label={i18n`Wire fee amortization`} tooltip={i18n`Factor by which wire fees exceeding the above threshold are divided to determine the share of excess wire fees to be paid explicitly by the consumer.`}/> + <InputCurrency name="payments.max_fee" label={i18n`Maximum deposit fee`} tooltip={i18n`Maximum deposit fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`} /> + <InputCurrency name="payments.max_wire_fee" label={i18n`Maximum wire fee`} tooltip={i18n`Maximum aggregate wire fees the merchant is willing to cover for this order. Wire fees exceeding this amount are to be covered by the customers.`} /> + <Input name="payments.wire_fee_amortization" label={i18n`Wire fee amortization`} tooltip={i18n`Factor by which wire fees exceeding the above threshold are divided to determine the share of excess wire fees to be paid explicitly by the consumer.`} /> <Input name="payments.fullfilment_url" label={i18n`Fulfillment URL`} tooltip={i18n`URL to which the user will be redirected after successful payment.`} /> </InputGroup> <InputGroup name="extra" label={i18n`Additional information`} tooltip={i18n`Custom information to be included in the contract for this order.`}> - <Input name="extra" inputType="multiline" label={`Value`} tooltip={i18n`You must enter a value in JavaScript Object Notation (JSON).`} /> + <Input name="extra" inputType="multiline" label={`Value`} tooltip={i18n`You must enter a value in JavaScript Object Notation (JSON).`} /> </InputGroup> </FormProvider> diff --git a/packages/frontend/src/paths/instance/orders/list/Table.tsx b/packages/frontend/src/paths/instance/orders/list/Table.tsx index ed8ee3a..64a5d22 100644 --- a/packages/frontend/src/paths/instance/orders/list/Table.tsx +++ b/packages/frontend/src/paths/instance/orders/list/Table.tsx @@ -30,9 +30,11 @@ import { InputSelector } from "../../../../components/form/InputSelector"; import { ConfirmModal } from "../../../../components/modal"; import { MerchantBackend, WithId } from "../../../../declaration"; import { Translate, useTranslator } from "../../../../i18n"; -import { RefundSchema as RefundSchema } from "../../../../schemas"; -import { mergeRefunds, subtractPrices, sumPrices } from "../../../../utils/amount"; +import { RefundSchema } from "../../../../schemas"; +import { mergeRefunds } from "../../../../utils/amount"; import { AMOUNT_ZERO_REGEX } from "../../../../utils/constants"; +import { Amounts } from "@gnu-taler/taler-util"; +import { useConfigContext } from "../../../../context/config"; type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId interface Props { @@ -174,13 +176,14 @@ export function RefundModal({ order, onCancel, onConfirm }: RefundModalProps): V } } - const refunds = (order.order_status === 'paid' ? order.refund_details : []) - .reduce(mergeRefunds, []) - const totalRefunded = refunds.map(r => r.amount).reduce((p, c) => sumPrices(c, p), ':0') - const orderPrice = (order.order_status === 'paid' ? order.contract_terms.amount : undefined) - const totalRefundable = !orderPrice ? undefined : (refunds.length ? subtractPrices(orderPrice, totalRefunded) : orderPrice) + const refunds = (order.order_status === 'paid' ? order.refund_details : []).reduce(mergeRefunds, []) - const isRefundable = totalRefundable && !AMOUNT_ZERO_REGEX.test(totalRefundable) + const config = useConfigContext() + const totalRefunded = refunds.map(r => r.amount).reduce((p, c) => Amounts.add(p, Amounts.parseOrThrow(c)).amount, Amounts.getZero(config.currency) ) + const orderPrice = (order.order_status === 'paid' ? Amounts.parseOrThrow(order.contract_terms.amount) : undefined) + const totalRefundable = !orderPrice ? Amounts.getZero(totalRefunded.currency) : (refunds.length ? Amounts.sub(orderPrice, totalRefunded).amount : orderPrice) + + const isRefundable = Amounts.isNonZero(totalRefundable) //FIXME: parameters in the translation return <ConfirmModal description="refund" danger active onCancel={onCancel} onConfirm={validateAndConfirm}> {refunds.length > 0 && <div class="columns"> @@ -212,7 +215,7 @@ export function RefundModal({ order, onCancel, onConfirm }: RefundModalProps): V {isRefundable && <FormProvider<State> errors={errors} object={form} valueHandler={(d) => setValue(d as any)}> <InputCurrency<State> name="refund" label={i18n`Refund`} tooltip={i18n`amount to be refunded`}> - <Translate>Max refundable:</Translate> {totalRefundable} + <Translate>Max refundable:</Translate> {Amounts.stringify(totalRefundable)} </InputCurrency> <InputSelector name="mainReason" label={i18n`Reason`} values={[i18n`duplicated`, i18n`requested by the customer`, i18n`other`]} tooltip={i18n`why this order is being refunded`} /> {form.mainReason && <Input<State> label={i18n`Description`} name="description" tooltip={i18n`more information to give context`} />} diff --git a/packages/frontend/src/paths/instance/products/create/CreatePage.tsx b/packages/frontend/src/paths/instance/products/create/CreatePage.tsx index 0f2411b..ed669f6 100644 --- a/packages/frontend/src/paths/instance/products/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/products/create/CreatePage.tsx @@ -53,7 +53,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} <AsyncButton onClick={submitForm} data-tooltip={ - !submitForm ? i18n`Need to complete fields marked with a star` : 'confirm operation' + !submitForm ? i18n`Need to complete marked fields` : 'confirm operation' } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> </div> diff --git a/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx b/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx index 32d67c0..d7eb3d1 100644 --- a/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx +++ b/packages/frontend/src/paths/instance/products/update/UpdatePage.tsx @@ -66,7 +66,7 @@ export function UpdatePage({ product, onUpdate, onBack }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} <AsyncButton onClick={submitForm} data-tooltip={ - !submitForm ? i18n`Need to complete fields marked with a star` : 'confirm operation' + !submitForm ? i18n`Need to complete marked fields` : 'confirm operation' } disabled={!submitForm}><Translate>Confirm</Translate></AsyncButton> </div> </div> diff --git a/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx b/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx index 05f5e06..2e85cf9 100644 --- a/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/reserves/create/CreatePage.tsx @@ -92,7 +92,7 @@ function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, setReserv setExchangeQueryError(r.message) }) }} data-tooltip={ - hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' } disabled={hasErrors} ><Translate>Next</Translate></AsyncButton> </div> </Fragment> @@ -113,7 +113,7 @@ function ViewStep({ step, setCurrentStep, reserve, onBack, submitForm, setReserv <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={() => setCurrentStep(Steps.EXCHANGE)} ><Translate>Back</Translate></button>} <AsyncButton onClick={submitForm} data-tooltip={ - hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' } disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> </div> </Fragment> diff --git a/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx b/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx index 5c74326..566483e 100644 --- a/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx +++ b/packages/frontend/src/paths/instance/transfers/create/CreatePage.tsx @@ -94,7 +94,7 @@ export function CreatePage({ accounts, onCreate, onBack }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && <button class="button" onClick={onBack} ><Translate>Cancel</Translate></button>} <AsyncButton disabled={hasErrors} data-tooltip={ - hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' } onClick={submitForm} ><Translate>Confirm</Translate></AsyncButton> </div> diff --git a/packages/frontend/src/paths/instance/update/UpdatePage.tsx b/packages/frontend/src/paths/instance/update/UpdatePage.tsx index 7612d6f..c900192 100644 --- a/packages/frontend/src/paths/instance/update/UpdatePage.tsx +++ b/packages/frontend/src/paths/instance/update/UpdatePage.tsx @@ -24,13 +24,6 @@ import { useState } from "preact/hooks"; import * as yup from 'yup'; import { AsyncButton } from "../../../components/exception/AsyncButton"; import { FormProvider, FormErrors } from "../../../components/form/FormProvider"; -import { Input } from "../../../components/form/Input"; -import { InputCurrency } from "../../../components/form/InputCurrency"; -import { InputDuration } from "../../../components/form/InputDuration"; -import { InputGroup } from "../../../components/form/InputGroup"; -import { InputLocation } from "../../../components/form/InputLocation"; -import { InputPayto } from "../../../components/form/InputPayto"; -import { InputSecured } from "../../../components/form/InputSecured"; import { UpdateTokenModal } from "../../../components/modal"; import { useInstanceContext } from "../../../context/instance"; import { MerchantBackend } from "../../../declaration"; @@ -157,7 +150,7 @@ export function UpdatePage({ onUpdate, onChangeAuth, selected, onBack }: Props): <button class="button" onClick={onBack} data-tooltip="cancel operation"><Translate>Cancel</Translate></button> <AsyncButton onClick={submit} data-tooltip={ - hasErrors ? i18n`Need to complete fields marked with a star` : 'confirm operation' + hasErrors ? i18n`Need to complete marked fields` : 'confirm operation' } disabled={hasErrors} ><Translate>Confirm</Translate></AsyncButton> </div> </div> diff --git a/packages/frontend/src/utils/amount.ts b/packages/frontend/src/utils/amount.ts index 823ad9d..062ddaf 100644 --- a/packages/frontend/src/utils/amount.ts +++ b/packages/frontend/src/utils/amount.ts @@ -22,7 +22,7 @@ import { MerchantBackend } from "../declaration"; * @param two * @returns */ -export const sumPrices = (one: string, two: string) => { +const sumPrices = (one: string, two: string) => { const [currency, valueOne] = one.split(':') const [, valueTwo] = two.split(':') return `${currency}:${parseInt(valueOne, 10) + parseInt(valueTwo, 10)}` @@ -55,19 +55,6 @@ export function mergeRefunds(prev: MerchantBackend.Orders.RefundDetails[], cur: return prev } -export const multiplyPrice = (price: string, q: number) => { - const a = Amounts.parseOrThrow(price) - const r = Amounts.mult(a, q) - return Amounts.stringify(r.amount) -} - -export const subtractPrices = (one: string, two: string) => { - const a = Amounts.parseOrThrow(one) - const b = Amounts.parseOrThrow(two) - const r = Amounts.sub(a, b) - return Amounts.stringify(r.amount) -} - export const rate = (one: string, two: string) => { const a = Amounts.parseOrThrow(one) const b = Amounts.parseOrThrow(two) diff --git a/packages/frontend/src/utils/constants.ts b/packages/frontend/src/utils/constants.ts index 403adb9..cbf4342 100644 --- a/packages/frontend/src/utils/constants.ts +++ b/packages/frontend/src/utils/constants.ts @@ -23,7 +23,7 @@ export const PAYTO_REGEX = /^payto:\/\/[a-zA-Z][a-zA-Z0-9-.]+(\/[a-zA-Z0-9\-\.\~\(\)@_%:!$&'*+,;=]*)*\??((amount|receiver-name|sender-name|instruction|message)=[a-zA-Z0-9\-\.\~\(\)@_%:!$'*+,;=]*&?)*$/ export const PAYTO_WIRE_METHOD_LOOKUP = /payto:\/\/([a-zA-Z][a-zA-Z0-9-.]+)\/.*/ -export const AMOUNT_REGEX = /^[a-zA-Z][a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/ +export const AMOUNT_REGEX = /^[a-zA-Z]*:[0-9][0-9,]*\.?[0-9,]*$/ export const INSTANCE_ID_LOOKUP = /^\/instances\/([^/]*)\/?$/ |