summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-03-22 13:56:16 -0300
committerSebastian <sebasjm@gmail.com>2024-03-26 16:57:58 -0300
commite2bfbced7ab027c901913e83ff7dd82240661990 (patch)
tree33752605ccaf19498f8c2a64e0117db16f22ce26 /packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
parent0c265558c4b7b78a13272abf1c4c84e3cf93c987 (diff)
downloadwallet-core-e2bfbced7ab027c901913e83ff7dd82240661990.tar.gz
wallet-core-e2bfbced7ab027c901913e83ff7dd82240661990.tar.bz2
wallet-core-e2bfbced7ab027c901913e83ff7dd82240661990.zip
work in progress, new api being used. merchant now should move into using the full API
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx')
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx525
1 files changed, 311 insertions, 214 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
index 5633d93ab..fca123773 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
@@ -19,10 +19,18 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { AbsoluteTime, Amounts, Duration, TalerProtocolDuration } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import {
+ AbsoluteTime,
+ Amounts,
+ Duration,
+ TalerProtocolDuration,
+} from "@gnu-taler/taler-util";
+import {
+ useMerchantApiContext,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
import { format, isFuture } from "date-fns";
-import { ComponentChildren, Fragment, VNode, h } from "preact";
+import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import {
FormErrors,
@@ -39,10 +47,8 @@ import { InputToggle } from "../../../../components/form/InputToggle.js";
import { InventoryProductForm } from "../../../../components/product/InventoryProductForm.js";
import { NonInventoryProductFrom } from "../../../../components/product/NonInventoryProductForm.js";
import { ProductList } from "../../../../components/product/ProductList.js";
-import { useConfigContext } from "../../../../context/config.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { usePreference } from "../../../../hooks/preference.js";
-import { OrderCreateSchema as schema } from "../../../../schemas/index.js";
import { rate } from "../../../../utils/amount.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
@@ -58,9 +64,16 @@ interface InstanceConfig {
default_wire_transfer_delay: TalerProtocolDuration;
}
-function with_defaults(config: InstanceConfig, currency: string): Partial<Entity> {
- const defaultPayDeadline = Duration.fromTalerProtocolDuration(config.default_pay_delay);
- const defaultWireDeadline = Duration.fromTalerProtocolDuration(config.default_wire_transfer_delay);
+function with_defaults(
+ config: InstanceConfig,
+ _currency: string,
+): Partial<Entity> {
+ const defaultPayDeadline = Duration.fromTalerProtocolDuration(
+ config.default_pay_delay,
+ );
+ const defaultWireDeadline = Duration.fromTalerProtocolDuration(
+ config.default_wire_transfer_delay,
+ );
return {
inventoryProducts: {},
@@ -69,9 +82,9 @@ function with_defaults(config: InstanceConfig, currency: string): Partial<Entity
payments: {
max_fee: undefined,
createToken: true,
- pay_deadline: (defaultPayDeadline),
- refund_deadline: (defaultPayDeadline),
- wire_transfer_deadline: (defaultWireDeadline),
+ pay_deadline: defaultPayDeadline,
+ refund_deadline: defaultPayDeadline,
+ wire_transfer_deadline: defaultWireDeadline,
},
shipping: {},
extra: {},
@@ -114,26 +127,17 @@ interface Entity {
extra: Record<string, 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 instance_default = with_defaults(instanceConfig, config.currency)
+ const { config } = useMerchantApiContext();
+ const instance_default = with_defaults(instanceConfig, config.currency);
const [value, valueHandler] = useState(instance_default);
const zero = Amounts.zeroOfCurrency(config.currency);
- const [settings, updateSettings] = usePreference()
+ const [settings, updateSettings] = usePreference();
const inventoryList = Object.values(value.inventoryProducts || {});
const productList = Object.values(value.products || {});
@@ -158,22 +162,25 @@ export function CreatePage({
refund_deadline: !value.payments?.refund_deadline
? undefined
: value.payments.pay_deadline &&
- Duration.cmp(value.payments.refund_deadline, value.payments.pay_deadline) === -1
- ? i18n.str`refund deadline cannot be before pay deadline`
- : value.payments.wire_transfer_deadline &&
Duration.cmp(
- value.payments.wire_transfer_deadline,
value.payments.refund_deadline,
+ value.payments.pay_deadline,
) === -1
+ ? i18n.str`refund deadline cannot be before pay deadline`
+ : value.payments.wire_transfer_deadline &&
+ Duration.cmp(
+ value.payments.wire_transfer_deadline,
+ value.payments.refund_deadline,
+ ) === -1
? i18n.str`wire transfer deadline cannot be before refund deadline`
: undefined,
pay_deadline: !value.payments?.pay_deadline
? i18n.str`required`
: value.payments.wire_transfer_deadline &&
- Duration.cmp(
- value.payments.wire_transfer_deadline,
- value.payments.pay_deadline,
- ) === -1
+ Duration.cmp(
+ value.payments.wire_transfer_deadline,
+ value.payments.pay_deadline,
+ ) === -1
? i18n.str`wire transfer deadline cannot be before pay deadline`
: undefined,
wire_transfer_deadline: !value.payments?.wire_transfer_deadline
@@ -184,12 +191,11 @@ export function CreatePage({
: !value.payments?.refund_deadline
? i18n.str`should have a refund deadline`
: Duration.cmp(
- value.payments.refund_deadline,
- value.payments.auto_refund_deadline,
- ) == -1
+ value.payments.refund_deadline,
+ value.payments.auto_refund_deadline,
+ ) == -1
? i18n.str`auto refund cannot be after refund deadline`
: undefined,
-
}),
shipping: undefinedIfEmpty({
delivery_date: !value.shipping?.delivery_date
@@ -214,18 +220,34 @@ export function CreatePage({
summary: order.pricing.summary,
products: productList,
extra: undefinedIfEmpty(value.extra),
- pay_deadline: !value.payments.pay_deadline ?
- i18n.str`required` :
- AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.pay_deadline))
- ,// : undefined,
+ pay_deadline: !value.payments.pay_deadline
+ ? i18n.str`required`
+ : AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ value.payments.pay_deadline,
+ ),
+ ), // : undefined,
wire_transfer_deadline: value.payments.wire_transfer_deadline
- ? AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.wire_transfer_deadline))
+ ? AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ value.payments.wire_transfer_deadline,
+ ),
+ )
: undefined,
refund_deadline: value.payments.refund_deadline
- ? AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.refund_deadline))
+ ? AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ value.payments.refund_deadline,
+ ),
+ )
: undefined,
auto_refund: value.payments.auto_refund_deadline
- ? Duration.toTalerProtocolDuration(value.payments.auto_refund_deadline)
+ ? Duration.toTalerProtocolDuration(
+ value.payments.auto_refund_deadline,
+ )
: undefined,
max_fee: value.payments.max_fee as string,
@@ -301,7 +323,7 @@ export function CreatePage({
const totalAsString = Amounts.stringify(totalPrice.amount);
const allProducts = productList.concat(inventoryList.map(asProduct));
- const [newField, setNewField] = useState("")
+ const [newField, setNewField] = useState("");
useEffect(() => {
valueHandler((v) => {
@@ -328,37 +350,43 @@ export function CreatePage({
);
// if there is no default pay deadline
- const noDefault_payDeadline = !instance_default.payments || !instance_default.payments.pay_deadline
+ const noDefault_payDeadline =
+ !instance_default.payments || !instance_default.payments.pay_deadline;
// and there is no default wire deadline
- const noDefault_wireDeadline = !instance_default.payments || !instance_default.payments.wire_transfer_deadline
+ const noDefault_wireDeadline =
+ !instance_default.payments ||
+ !instance_default.payments.wire_transfer_deadline;
// user required to set the taler options
- const requiresSomeTalerOptions = noDefault_payDeadline || noDefault_wireDeadline
-
+ const requiresSomeTalerOptions =
+ noDefault_payDeadline || noDefault_wireDeadline;
return (
<div>
-
<section class="section is-main-section">
<div class="tabs is-toggle is-fullwidth is-small">
<ul>
- <li class={!settings.advanceOrderMode ? "is-active" : ""} onClick={() => {
- updateSettings({
- ...settings,
- advanceOrderMode: false
- })
- }}>
- <a >
- <span><i18n.Translate>Simple</i18n.Translate></span>
+ <li
+ class={!settings.advanceOrderMode ? "is-active" : ""}
+ onClick={() => {
+ updateSettings("advanceOrderMode", false);
+ }}
+ >
+ <a>
+ <span>
+ <i18n.Translate>Simple</i18n.Translate>
+ </span>
</a>
</li>
- <li class={settings.advanceOrderMode ? "is-active" : ""} onClick={() => {
- updateSettings({
- ...settings,
- advanceOrderMode: true
- })
- }}>
- <a >
- <span><i18n.Translate>Advanced</i18n.Translate></span>
+ <li
+ class={settings.advanceOrderMode ? "is-active" : ""}
+ onClick={() => {
+ updateSettings("advanceOrderMode", true);
+ }}
+ >
+ <a>
+ <span>
+ <i18n.Translate>Advanced</i18n.Translate>
+ </span>
</a>
</li>
</ul>
@@ -386,7 +414,7 @@ export function CreatePage({
inventory={instanceInventory}
/>
- {settings.advanceOrderMode &&
+ {settings.advanceOrderMode && (
<NonInventoryProductFrom
productToEdit={editingProduct}
onAddProduct={(p) => {
@@ -394,7 +422,7 @@ export function CreatePage({
return addNewProduct(p);
}}
/>
- }
+ )}
{allProducts.length > 0 && (
<ProductList
@@ -437,8 +465,8 @@ export function CreatePage({
discountOrRise > 0 &&
(discountOrRise < 1
? `discount of %${Math.round(
- (1 - discountOrRise) * 100,
- )}`
+ (1 - discountOrRise) * 100,
+ )}`
: `rise of %${Math.round((discountOrRise - 1) * 100)}`)
}
tooltip={i18n.str`Amount to be paid by the customer`}
@@ -459,7 +487,7 @@ export function CreatePage({
tooltip={i18n.str`Title of the order to be shown to the customer`}
/>
- {settings.advanceOrderMode &&
+ {settings.advanceOrderMode && (
<InputGroup
name="shipping"
label={i18n.str`Shipping and Fulfillment`}
@@ -485,146 +513,201 @@ export function CreatePage({
tooltip={i18n.str`URL to which the user will be redirected after successful payment.`}
/>
</InputGroup>
- }
+ )}
- {(settings.advanceOrderMode || requiresSomeTalerOptions) &&
+ {(settings.advanceOrderMode || requiresSomeTalerOptions) && (
<InputGroup
name="payments"
label={i18n.str`Taler payment options`}
tooltip={i18n.str`Override default Taler payment settings for this order`}
>
- {(settings.advanceOrderMode || noDefault_payDeadline) && <InputDuration
- name="payments.pay_deadline"
- label={i18n.str`Payment time`}
- help={<DeadlineHelp duration={value.payments?.pay_deadline} />}
- withForever
- withoutClear
- tooltip={i18n.str`Time for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline. Time start to run after the order is created.`}
- side={
- <span>
- <button class="button" onClick={() => {
- const c = {
- ...value,
- payments: {
- ...(value.payments ?? {}),
- pay_deadline: instance_default.payments?.pay_deadline
- }
- }
- valueHandler(c)
- }}>
- <i18n.Translate>default</i18n.Translate>
- </button>
- </span>
- }
- />}
- {settings.advanceOrderMode && <InputDuration
- name="payments.refund_deadline"
- label={i18n.str`Refund time`}
- help={<DeadlineHelp duration={value.payments?.refund_deadline} />}
- withForever
- withoutClear
- tooltip={i18n.str`Time while the order can be refunded by the merchant. Time starts after the order is created.`}
- side={
- <span>
- <button class="button" onClick={() => {
- valueHandler({
- ...value,
- payments: {
- ...(value.payments ?? {}),
- refund_deadline: instance_default.payments?.refund_deadline
- }
- })
- }}>
- <i18n.Translate>default</i18n.Translate>
- </button>
- </span>
- }
- />}
- {(settings.advanceOrderMode || noDefault_wireDeadline) && <InputDuration
- name="payments.wire_transfer_deadline"
- label={i18n.str`Wire transfer time`}
- help={<DeadlineHelp duration={value.payments?.wire_transfer_deadline} />}
- withoutClear
- withForever
- tooltip={i18n.str`Time for the exchange to make the wire transfer. Time starts after the order is created.`}
- side={
- <span>
- <button class="button" onClick={() => {
- valueHandler({
- ...value,
- payments: {
- ...(value.payments ?? {}),
- wire_transfer_deadline: instance_default.payments?.wire_transfer_deadline
- }
- })
- }}>
- <i18n.Translate>default</i18n.Translate>
- </button>
- </span>
- }
- />}
- {settings.advanceOrderMode && <InputDuration
- name="payments.auto_refund_deadline"
- label={i18n.str`Auto-refund time`}
- help={<DeadlineHelp duration={value.payments?.auto_refund_deadline} />}
- tooltip={i18n.str`Time until which the wallet will automatically check for refunds without user interaction.`}
- withForever
- />}
-
- {settings.advanceOrderMode && <InputCurrency
- name="payments.max_fee"
- label={i18n.str`Maximum fee`}
- tooltip={i18n.str`Maximum fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`}
- />}
- {settings.advanceOrderMode && <InputToggle
- name="payments.createToken"
- label={i18n.str`Create token`}
- tooltip={i18n.str`If the order ID is easy to guess the token will prevent user to steal orders from others.`}
- />}
- {settings.advanceOrderMode && <InputNumber
- name="payments.minimum_age"
- label={i18n.str`Minimum age required`}
- tooltip={i18n.str`Any value greater than 0 will limit the coins able be used to pay this contract. If empty the age restriction will be defined by the products`}
- help={
- minAgeByProducts > 0
- ? i18n.str`Min age defined by the producs is ${minAgeByProducts}`
- : i18n.str`No product with age restriction in this order`
- }
- />}
+ {(settings.advanceOrderMode || noDefault_payDeadline) && (
+ <InputDuration
+ name="payments.pay_deadline"
+ label={i18n.str`Payment time`}
+ help={
+ <DeadlineHelp duration={value.payments?.pay_deadline} />
+ }
+ withForever
+ withoutClear
+ tooltip={i18n.str`Time for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline. Time start to run after the order is created.`}
+ side={
+ <span>
+ <button
+ class="button"
+ onClick={() => {
+ const c = {
+ ...value,
+ payments: {
+ ...(value.payments ?? {}),
+ pay_deadline:
+ instance_default.payments?.pay_deadline,
+ },
+ };
+ valueHandler(c);
+ }}
+ >
+ <i18n.Translate>default</i18n.Translate>
+ </button>
+ </span>
+ }
+ />
+ )}
+ {settings.advanceOrderMode && (
+ <InputDuration
+ name="payments.refund_deadline"
+ label={i18n.str`Refund time`}
+ help={
+ <DeadlineHelp
+ duration={value.payments?.refund_deadline}
+ />
+ }
+ withForever
+ withoutClear
+ tooltip={i18n.str`Time while the order can be refunded by the merchant. Time starts after the order is created.`}
+ side={
+ <span>
+ <button
+ class="button"
+ onClick={() => {
+ valueHandler({
+ ...value,
+ payments: {
+ ...(value.payments ?? {}),
+ refund_deadline:
+ instance_default.payments?.refund_deadline,
+ },
+ });
+ }}
+ >
+ <i18n.Translate>default</i18n.Translate>
+ </button>
+ </span>
+ }
+ />
+ )}
+ {(settings.advanceOrderMode || noDefault_wireDeadline) && (
+ <InputDuration
+ name="payments.wire_transfer_deadline"
+ label={i18n.str`Wire transfer time`}
+ help={
+ <DeadlineHelp
+ duration={value.payments?.wire_transfer_deadline}
+ />
+ }
+ withoutClear
+ withForever
+ tooltip={i18n.str`Time for the exchange to make the wire transfer. Time starts after the order is created.`}
+ side={
+ <span>
+ <button
+ class="button"
+ onClick={() => {
+ valueHandler({
+ ...value,
+ payments: {
+ ...(value.payments ?? {}),
+ wire_transfer_deadline:
+ instance_default.payments
+ ?.wire_transfer_deadline,
+ },
+ });
+ }}
+ >
+ <i18n.Translate>default</i18n.Translate>
+ </button>
+ </span>
+ }
+ />
+ )}
+ {settings.advanceOrderMode && (
+ <InputDuration
+ name="payments.auto_refund_deadline"
+ label={i18n.str`Auto-refund time`}
+ help={
+ <DeadlineHelp
+ duration={value.payments?.auto_refund_deadline}
+ />
+ }
+ tooltip={i18n.str`Time until which the wallet will automatically check for refunds without user interaction.`}
+ withForever
+ />
+ )}
+
+ {settings.advanceOrderMode && (
+ <InputCurrency
+ name="payments.max_fee"
+ label={i18n.str`Maximum fee`}
+ tooltip={i18n.str`Maximum fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`}
+ />
+ )}
+ {settings.advanceOrderMode && (
+ <InputToggle
+ name="payments.createToken"
+ label={i18n.str`Create token`}
+ tooltip={i18n.str`If the order ID is easy to guess the token will prevent user to steal orders from others.`}
+ />
+ )}
+ {settings.advanceOrderMode && (
+ <InputNumber
+ name="payments.minimum_age"
+ label={i18n.str`Minimum age required`}
+ tooltip={i18n.str`Any value greater than 0 will limit the coins able be used to pay this contract. If empty the age restriction will be defined by the products`}
+ help={
+ minAgeByProducts > 0
+ ? i18n.str`Min age defined by the producs is ${minAgeByProducts}`
+ : i18n.str`No product with age restriction in this order`
+ }
+ />
+ )}
</InputGroup>
- }
+ )}
- {settings.advanceOrderMode &&
+ {settings.advanceOrderMode && (
<InputGroup
name="extra"
label={i18n.str`Additional information`}
tooltip={i18n.str`Custom information to be included in the contract for this order.`}
>
- {Object.keys(value.extra ?? {}).map((key) => {
-
- return <Input
- name={`extra.${key}`}
- inputType="multiline"
- label={key}
- tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`}
- side={
- <button class="button" onClick={(e) => {
- if (value.extra && value.extra[key] !== undefined) {
- console.log(value.extra)
- delete value.extra[key]
- }
- valueHandler({
- ...value,
- })
- }}>remove</button>
- }
- />
+ {Object.keys(value.extra ?? {}).map((key, idx) => {
+ return (
+ <Input
+ name={`extra.${key}`}
+ key={String(idx)}
+ inputType="multiline"
+ label={key}
+ tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`}
+ side={
+ <button
+ class="button"
+ onClick={(e) => {
+ if (
+ value.extra &&
+ value.extra[key] !== undefined
+ ) {
+ console.log(value.extra);
+ delete value.extra[key];
+ }
+ valueHandler({
+ ...value,
+ });
+ e.preventDefault();
+ }}
+ >
+ remove
+ </button>
+ }
+ />
+ );
})}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
<i18n.Translate>Custom field name</i18n.Translate>
- <span class="icon has-tooltip-right" data-tooltip={"new extra field"}>
+ <span
+ class="icon has-tooltip-right"
+ data-tooltip={"new extra field"}
+ >
<i class="mdi mdi-information" />
</span>
</label>
@@ -632,23 +715,33 @@ export function CreatePage({
<div class="field-body is-flex-grow-3">
<div class="field">
<p class="control">
- <input class="input " value={newField} onChange={(e) => setNewField(e.currentTarget.value)} />
+ <input
+ class="input "
+ value={newField}
+ onChange={(e) => setNewField(e.currentTarget.value)}
+ />
</p>
</div>
</div>
- <button class="button" onClick={(e) => {
- setNewField("")
- valueHandler({
- ...value,
- extra: {
- ...(value.extra ?? {}),
- [newField]: ""
- }
- })
- }}>add</button>
+ <button
+ class="button"
+ onClick={(e) => {
+ setNewField("");
+ valueHandler({
+ ...value,
+ extra: {
+ ...(value.extra ?? {}),
+ [newField]: "",
+ },
+ });
+ e.preventDefault();
+ }}
+ >
+ add
+ </button>
</div>
</InputGroup>
- }
+ )}
</FormProvider>
<div class="buttons is-right mt-5">
@@ -686,20 +779,24 @@ function asProduct(p: ProductAndQuantity): MerchantBackend.Product {
};
}
-
function DeadlineHelp({ duration }: { duration?: Duration }): VNode {
const { i18n } = useTranslationContext();
- const [now, setNow] = useState(AbsoluteTime.now())
+ const [now, setNow] = useState(AbsoluteTime.now());
useEffect(() => {
const iid = setInterval(() => {
- setNow(AbsoluteTime.now())
- }, 60 * 1000)
+ setNow(AbsoluteTime.now());
+ }, 60 * 1000);
return () => {
- clearInterval(iid)
- }
- })
- if (!duration) return <i18n.Translate>Disabled</i18n.Translate>
- const when = AbsoluteTime.addDuration(now, duration)
- if (when.t_ms === "never") return <i18n.Translate>No deadline</i18n.Translate>
- return <i18n.Translate>Deadline at {format(when.t_ms, "dd/MM/yy HH:mm")}</i18n.Translate>
+ clearInterval(iid);
+ };
+ });
+ if (!duration) return <i18n.Translate>Disabled</i18n.Translate>;
+ const when = AbsoluteTime.addDuration(now, duration);
+ if (when.t_ms === "never")
+ return <i18n.Translate>No deadline</i18n.Translate>;
+ return (
+ <i18n.Translate>
+ Deadline at {format(when.t_ms, "dd/MM/yy HH:mm")}
+ </i18n.Translate>
+ );
}