diff options
author | Sebastian <sebasjm@gmail.com> | 2024-01-08 14:24:07 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2024-01-08 14:24:20 -0300 |
commit | b0610d24571593909cd6683b340fa266de046e31 (patch) | |
tree | 691abc2d9228735eb0a25526eab628168fa8d5a0 /packages/merchant-backoffice-ui | |
parent | 35cbcce79e22d7c3cd9ac1c676ad9b3480bd3fe1 (diff) | |
download | wallet-core-b0610d24571593909cd6683b340fa266de046e31.tar.xz |
order config using relative time
Diffstat (limited to 'packages/merchant-backoffice-ui')
6 files changed, 240 insertions, 262 deletions
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx index ef4df1df4..8b6b5636d 100644 --- a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx +++ b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx @@ -20,16 +20,19 @@ */ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { formatDuration, intervalToDuration } from "date-fns"; -import { h, VNode } from "preact"; +import { ComponentChildren, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { SimpleModal } from "../modal/index.js"; import { DurationPicker } from "../picker/DurationPicker.js"; import { InputProps, useField } from "./useField.js"; +import { Duration } from "@gnu-taler/taler-util"; export interface Props<T> extends InputProps<T> { expand?: boolean; readonly?: boolean; withForever?: boolean; + side?: ComponentChildren; + withoutClear?: boolean; } export function InputDuration<T>({ @@ -41,19 +44,22 @@ export function InputDuration<T>({ help, readonly, withForever, + withoutClear, + side, }: Props<keyof T>): VNode { const [opened, setOpened] = useState(false); const { i18n } = useTranslationContext(); - const { error, required, value, onChange } = useField<T>(name); + const { error, required, value: anyValue, onChange } = useField<T>(name); let strValue = ""; + const value: Duration = anyValue if (!value) { strValue = ""; - } else if (value.d_us === "forever") { + } else if (value.d_ms === "forever") { strValue = i18n.str`forever`; } else { strValue = formatDuration( - intervalToDuration({ start: 0, end: value.d_us / 1000 }), + intervalToDuration({ start: 0, end: value.d_ms }), { locale: { formatDistance: (name, value) => { @@ -97,72 +103,80 @@ export function InputDuration<T>({ )} </label> </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <div class="field has-addons"> - <p class={expand ? "control is-expanded " : "control "}> - <input - class="input" - type="text" - readonly - value={strValue} - placeholder={placeholder} + + <div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <div class="field has-addons"> + <p class={expand ? "control is-expanded " : "control "}> + <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> + )} + </p> + <div + class="control" 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); - }} - > - <a class="button is-static"> - <span class="icon"> - <i class="mdi mdi-clock" /> - </span> - </a> + > + <a class="button is-static"> + <span class="icon"> + <i class="mdi mdi-clock" /> + </span> + </a> + </div> </div> + {error && <p class="help is-danger">{error}</p>} </div> - {error && <p class="help is-danger">{error}</p>} + {withForever && ( + <span data-tooltip={i18n.str`change value to never`}> + <button + class="button is-info mr-3" + onClick={() => onChange({ d_ms: "forever" } as any)} + > + <i18n.Translate>forever</i18n.Translate> + </button> + </span> + )} + {!readonly && !withoutClear && ( + <span data-tooltip={i18n.str`change value to empty`}> + <button + class="button is-info " + onClick={() => onChange(undefined as any)} + > + <i18n.Translate>clear</i18n.Translate> + </button> + </span> + )} + {side} </div> - {withForever && ( - <span data-tooltip={i18n.str`change value to never`}> - <button - class="button is-info mr-3" - onClick={() => onChange({ d_us: "forever" } as any)} - > - <i18n.Translate>forever</i18n.Translate> - </button> - </span> - )} - {!readonly && ( - <span data-tooltip={i18n.str`change value to empty`}> - <button - class="button is-info " - onClick={() => onChange(undefined as any)} - > - <i18n.Translate>clear</i18n.Translate> - </button> - </span> - )} + <span> + {help} + </span> </div> + + {opened && ( <SimpleModal onCancel={() => setOpened(false)}> <DurationPicker days hours minutes - value={!value || value.d_us === "forever" ? 0 : value.d_us} + value={!value || value.d_ms === "forever" ? 0 : value.d_ms} onChange={(v) => { - onChange({ d_us: v } as any); + onChange({ d_ms: v } as any); }} /> </SimpleModal> diff --git a/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx index 0968b0a17..ba003cce5 100644 --- a/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx +++ b/packages/merchant-backoffice-ui/src/components/picker/DurationPicker.tsx @@ -42,7 +42,7 @@ export function DurationPicker({ onChange, value, }: Props): VNode { - const ss = 1000 * 1000; + const ss = 1000; const ms = ss * 60; const hs = ms * 60; const ds = hs * 24; diff --git a/packages/merchant-backoffice-ui/src/declaration.d.ts b/packages/merchant-backoffice-ui/src/declaration.d.ts index dc53e3e83..f99dd1867 100644 --- a/packages/merchant-backoffice-ui/src/declaration.d.ts +++ b/packages/merchant-backoffice-ui/src/declaration.d.ts @@ -23,7 +23,7 @@ type HashCode = string; type EddsaPublicKey = string; type EddsaSignature = string; type WireTransferIdentifierRawP = string; -type RelativeTime = Duration; +type RelativeTime = TalerProtocolDuration; type ImageDataUrl = string; type MerchantUserType = "business" | "individual"; @@ -38,9 +38,12 @@ interface Timestamp { // never happen. t_s: number | "never"; } -interface Duration { +interface TalerProtocolDuration { d_us: number | "forever"; } +interface Duration { + d_ms: number | "forever"; +} interface WithId { id: string; diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx index 093c24c3d..d13b7e929 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx @@ -32,13 +32,16 @@ import { MerchantBackend } from "../../../declaration.js"; import { INSTANCE_ID_REGEX } from "../../../utils/constants.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; import { SetTokenNewInstanceModal } from "../../../components/modal/index.js"; +import { Duration } from "@gnu-taler/taler-util"; -export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & { +export type Entity = Omit<Omit<MerchantBackend.Instances.InstanceConfigurationMessage, "default_pay_delay">, "default_wire_transfer_delay"> & { auth_token?: string; + default_pay_delay: Duration, + default_wire_transfer_delay: Duration, }; interface Props { - onCreate: (d: Entity) => Promise<void>; + onCreate: (d: MerchantBackend.Instances.InstanceConfigurationMessage) => Promise<void>; onBack?: () => void; forceId?: string; } @@ -49,8 +52,8 @@ function with_defaults(id?: string): Partial<Entity> { // accounts: [], user_type: "business", use_stefan: true, - default_pay_delay: { d_us: 2 * 1000 * 60 * 60 * 1000 }, // two hours - default_wire_transfer_delay: { d_us: 1000 * 2 * 60 * 60 * 24 * 1000 }, // two days + default_pay_delay: { d_ms: 2 * 60 * 60 * 1000 }, // two hours + default_wire_transfer_delay: { d_ms: 2 * 60 * 60 * 24 * 1000 }, // two days }; } @@ -88,9 +91,9 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { default_pay_delay: !value.default_pay_delay ? i18n.str`required` : !!value.default_wire_transfer_delay && - value.default_wire_transfer_delay.d_us !== "forever" && - value.default_pay_delay.d_us !== "forever" && - value.default_pay_delay.d_us > value.default_wire_transfer_delay.d_us ? + value.default_wire_transfer_delay.d_ms !== "forever" && + value.default_pay_delay.d_ms !== "forever" && + value.default_pay_delay.d_ms > value.default_wire_transfer_delay.d_ms ? i18n.str`pay delay can't be greater than wire transfer delay` : undefined, default_wire_transfer_delay: !value.default_wire_transfer_delay ? i18n.str`required` @@ -124,7 +127,12 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { if (!value.jurisdiction) value.jurisdiction = {}; // remove above use conversion // schema.validateSync(value, { abortEarly: false }) - return onCreate(value as Entity); + value.default_pay_delay = Duration.toTalerProtocolDuration(value.default_pay_delay!) as any + value.default_wire_transfer_delay = Duration.toTalerProtocolDuration(value.default_wire_transfer_delay!) as any + // delete value.default_pay_delay; + // delete value.default_wire_transfer_delay; + + return onCreate(value as any as MerchantBackend.Instances.InstanceConfigurationMessage); }; function updateToken(token: string | null) { @@ -174,54 +182,54 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode { </FormProvider> <div class="level"> - <div class="level-item has-text-centered"> - <h1 class="title"> - <button - class={ - !isTokenSet - ? "button is-danger has-tooltip-bottom" - : !value.auth_token - ? "button has-tooltip-bottom" - : "button is-info has-tooltip-bottom" - } - data-tooltip={i18n.str`change authorization configuration`} - onClick={() => updateIsTokenDialogActive(true)} - > - <div class="icon is-centered"> - <i class="mdi mdi-lock-reset" /> - </div> - <span> - <i18n.Translate>Set access token</i18n.Translate> - </span> - </button> - </h1> + <div class="level-item has-text-centered"> + <h1 class="title"> + <button + class={ + !isTokenSet + ? "button is-danger has-tooltip-bottom" + : !value.auth_token + ? "button has-tooltip-bottom" + : "button is-info has-tooltip-bottom" + } + data-tooltip={i18n.str`change authorization configuration`} + onClick={() => updateIsTokenDialogActive(true)} + > + <div class="icon is-centered"> + <i class="mdi mdi-lock-reset" /> + </div> + <span> + <i18n.Translate>Set access token</i18n.Translate> + </span> + </button> + </h1> + </div> </div> - </div> - <div class="level"> - <div class="level-item has-text-centered"> - {!isTokenSet ? ( - <p class="is-size-6"> - <i18n.Translate> - Access token is not yet configured. This instance can't be - created. - </i18n.Translate> - </p> - ) : value.auth_token === undefined ? ( - <p class="is-size-6"> - <i18n.Translate> - No access token. Authorization must be handled externally. - </i18n.Translate> - </p> - ) : ( - <p class="is-size-6"> - <i18n.Translate> - Access token is set. Authorization is handled by the - merchant backend. - </i18n.Translate> - </p> - )} + <div class="level"> + <div class="level-item has-text-centered"> + {!isTokenSet ? ( + <p class="is-size-6"> + <i18n.Translate> + Access token is not yet configured. This instance can't be + created. + </i18n.Translate> + </p> + ) : value.auth_token === undefined ? ( + <p class="is-size-6"> + <i18n.Translate> + No access token. Authorization must be handled externally. + </i18n.Translate> + </p> + ) : ( + <p class="is-size-6"> + <i18n.Translate> + Access token is set. Authorization is handled by the + merchant backend. + </i18n.Translate> + </p> + )} + </div> </div> - </div> <div class="buttons is-right mt-5"> {onBack && ( <button class="button" onClick={onBack}> 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 2b1741276..a30f79169 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,32 +19,32 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { Amounts } from "@gnu-taler/taler-util"; +import { AbsoluteTime, Amounts, Duration, TalerProtocolDuration } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { add, isAfter, isBefore, isFuture } from "date-fns"; -import { Fragment, h, VNode } from "preact"; +import { format, isFuture } from "date-fns"; +import { Fragment, VNode, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import { FormErrors, FormProvider, } from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; -import { InputBoolean } from "../../../../components/form/InputBoolean.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputDate } from "../../../../components/form/InputDate.js"; +import { InputDuration } from "../../../../components/form/InputDuration.js"; import { InputGroup } from "../../../../components/form/InputGroup.js"; import { InputLocation } from "../../../../components/form/InputLocation.js"; import { InputNumber } from "../../../../components/form/InputNumber.js"; +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 { Duration, MerchantBackend, WithId } from "../../../../declaration.js"; +import { MerchantBackend, WithId } from "../../../../declaration.js"; +import { useSettings } from "../../../../hooks/useSettings.js"; import { OrderCreateSchema as schema } from "../../../../schemas/index.js"; import { rate } from "../../../../utils/amount.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; -import { useSettings } from "../../../../hooks/useSettings.js"; -import { InputToggle } from "../../../../components/form/InputToggle.js"; interface Props { onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void; @@ -54,23 +54,13 @@ interface Props { } interface InstanceConfig { use_stefan: boolean; - default_pay_delay: Duration; - default_wire_transfer_delay: Duration; + default_pay_delay: TalerProtocolDuration; + default_wire_transfer_delay: TalerProtocolDuration; } function with_defaults(config: InstanceConfig, currency: string): Partial<Entity> { - const defaultPayDeadline = - !config.default_pay_delay || config.default_pay_delay.d_us === "forever" - ? undefined - : add(new Date(), { - seconds: config.default_pay_delay.d_us / (1000 * 1000), - }); - const defaultWireDeadline = - !config.default_wire_transfer_delay || config.default_wire_transfer_delay.d_us === "forever" - ? undefined - : add(new Date(), { - seconds: config.default_wire_transfer_delay.d_us / (1000 * 1000), - }); + const defaultPayDeadline = Duration.fromTalerProtocolDuration(config.default_pay_delay); + const defaultWireDeadline = Duration.fromTalerProtocolDuration(config.default_wire_transfer_delay); return { inventoryProducts: {}, @@ -78,10 +68,10 @@ function with_defaults(config: InstanceConfig, currency: string): Partial<Entity pricing: {}, payments: { max_fee: undefined, - pay_deadline: defaultPayDeadline, - refund_deadline: defaultPayDeadline, createToken: true, - wire_transfer_deadline: defaultWireDeadline, + pay_deadline: (defaultPayDeadline), + refund_deadline: (defaultPayDeadline), + wire_transfer_deadline: (defaultWireDeadline), }, shipping: {}, extra: {}, @@ -107,10 +97,10 @@ interface Shipping { fullfilment_url?: string; } interface Payments { - refund_deadline?: Date; - pay_deadline?: Date; - wire_transfer_deadline?: Date; - auto_refund_deadline?: Date; + refund_deadline: Duration; + pay_deadline: Duration; + wire_transfer_deadline: Duration; + auto_refund_deadline: Duration; max_fee?: string; createToken: boolean; minimum_age?: number; @@ -164,53 +154,41 @@ export function CreatePage({ ? i18n.str`must be greater than 0` : undefined, }), - // extra: - // value.extra && !stringIsValidJSON(value.extra) - // ? i18n.str`not a valid json` - // : undefined, payments: undefinedIfEmpty({ refund_deadline: !value.payments?.refund_deadline ? undefined - : !isFuture(value.payments.refund_deadline) - ? i18n.str`should be in the future` - : value.payments.pay_deadline && - isBefore(value.payments.refund_deadline, value.payments.pay_deadline) - ? i18n.str`refund deadline cannot be before pay deadline` - : value.payments.wire_transfer_deadline && - isBefore( - value.payments.wire_transfer_deadline, - value.payments.refund_deadline, - ) - ? i18n.str`wire transfer deadline cannot be before refund deadline` - : undefined, - pay_deadline: !value.payments?.pay_deadline - ? i18n.str`required` - : !isFuture(value.payments.pay_deadline) - ? i18n.str`should be in the future` + : 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 && - isBefore( + Duration.cmp( value.payments.wire_transfer_deadline, - value.payments.pay_deadline, - ) - ? i18n.str`wire transfer deadline cannot be before pay deadline` + value.payments.refund_deadline, + ) === -1 + ? i18n.str`wire transfer deadline cannot be before refund deadline` : undefined, - wire_transfer_deadline: !value.payments?.wire_transfer_deadline + pay_deadline: !value.payments?.pay_deadline ? i18n.str`required` - : !isFuture(value.payments.wire_transfer_deadline) - ? i18n.str`should be in the future` + : value.payments.wire_transfer_deadline && + 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 + ? i18n.str`required` + : undefined, auto_refund_deadline: !value.payments?.auto_refund_deadline ? undefined - : !isFuture(value.payments.auto_refund_deadline) - ? i18n.str`should be in the future` - : !value.payments?.refund_deadline - ? i18n.str`should have a refund deadline` - : !isAfter( - value.payments.refund_deadline, - value.payments.auto_refund_deadline, - ) - ? i18n.str`auto refund cannot be after refund deadline` - : undefined, + : !value.payments?.refund_deadline + ? i18n.str`should have a refund deadline` + : Duration.cmp( + value.payments.refund_deadline, + value.payments.auto_refund_deadline, + ) == -1 + ? i18n.str`auto refund cannot be after refund deadline` + : undefined, }), shipping: undefinedIfEmpty({ @@ -226,7 +204,7 @@ export function CreatePage({ ); const submit = (): void => { - const order = schema.cast(value); + const order = value as any; //schema.cast(value); if (!value.payments) return; if (!value.shipping) return; @@ -236,29 +214,18 @@ export function CreatePage({ summary: order.pricing.summary, products: productList, extra: undefinedIfEmpty(value.extra), - pay_deadline: value.payments.pay_deadline - ? { - t_s: Math.floor(value.payments.pay_deadline.getTime() / 1000), - } - : 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 - ? { - t_s: Math.floor( - value.payments.wire_transfer_deadline.getTime() / 1000, - ), - } + ? AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.wire_transfer_deadline)) : undefined, refund_deadline: value.payments.refund_deadline - ? { - t_s: Math.floor(value.payments.refund_deadline.getTime() / 1000), - } + ? AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.refund_deadline)) : undefined, auto_refund: value.payments.auto_refund_deadline - ? { - d_us: Math.floor( - value.payments.auto_refund_deadline.getTime() * 1000, - ), - } + ? Duration.toTalerProtocolDuration(value.payments.auto_refund_deadline) : undefined, max_fee: value.payments.max_fee as string, @@ -360,10 +327,18 @@ export function CreatePage({ 0, ); + // if there is no default pay deadline const noDefault_payDeadline = !instance_default.payments || !instance_default.payments.pay_deadline + // and there is no defailt wire 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 whenPay = !value.payments?.pay_deadline ? undefined : AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.pay_deadline) + const whenRefund = !value.payments?.refund_deadline ? undefined : AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.refund_deadline) + const whenWire = !value.payments?.wire_transfer_deadline ? undefined : AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.wire_transfer_deadline) + const whenAutoRefund = !value.payments?.auto_refund_deadline ? undefined : AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.auto_refund_deadline) + return ( <div> @@ -522,10 +497,13 @@ export function CreatePage({ label={i18n.str`Taler payment options`} tooltip={i18n.str`Override default Taler payment settings for this order`} > - {(settings.advanceOrderMode || noDefault_payDeadline) && <InputDate + {(settings.advanceOrderMode || noDefault_payDeadline) && <InputDuration name="payments.pay_deadline" - label={i18n.str`Payment deadline`} - tooltip={i18n.str`Deadline for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline.`} + label={i18n.str`Payment time`} + help={whenPay && whenPay.t_ms !== "never" ? i18n.str`Deadline at ${format(whenPay.t_ms, "dd/MM/yy HH:mm")}` : i18n.str`Without 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={() => { @@ -543,10 +521,13 @@ export function CreatePage({ </span> } />} - {settings.advanceOrderMode && <InputDate + {settings.advanceOrderMode && <InputDuration name="payments.refund_deadline" - label={i18n.str`Refund deadline`} - tooltip={i18n.str`Time until which the order can be refunded by the merchant.`} + label={i18n.str`Refund time`} + help={whenRefund && whenRefund.t_ms !== "never" ? i18n.str`Deadline at ${format(whenRefund.t_ms, "dd/MM/yy HH:mm")}` : i18n.str`Without 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={() => { @@ -563,10 +544,13 @@ export function CreatePage({ </span> } />} - {(settings.advanceOrderMode || noDefault_wireDeadline) && <InputDate + {(settings.advanceOrderMode || noDefault_wireDeadline) && <InputDuration name="payments.wire_transfer_deadline" - label={i18n.str`Wire transfer deadline`} - tooltip={i18n.str`Deadline for the exchange to make the wire transfer.`} + label={i18n.str`Wire transfer time`} + help={whenWire && whenWire.t_ms !== "never" ? i18n.str`Deadline at ${format(whenWire.t_ms, "dd/MM/yy HH:mm")}` : i18n.str`Without 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={() => { @@ -583,10 +567,12 @@ export function CreatePage({ </span> } />} - {settings.advanceOrderMode && <InputDate + {settings.advanceOrderMode && <InputDuration name="payments.auto_refund_deadline" - label={i18n.str`Auto-refund deadline`} + label={i18n.str`Auto-refund interval`} + help={whenAutoRefund && whenAutoRefund.t_ms !== "never" ? i18n.str`Deadline at ${format(whenAutoRefund.t_ms, "dd/MM/yy HH:mm")}` : i18n.str`Without deadline`} tooltip={i18n.str`Time until which the wallet will automatically check for refunds without user interaction.`} + withForever />} {settings.advanceOrderMode && <InputCurrency @@ -703,3 +689,5 @@ function asProduct(p: ProductAndQuantity): MerchantBackend.Product { minimum_age: p.product.minimum_age, }; } + + diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx index 5b88b550f..a27a0cb06 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx @@ -31,14 +31,16 @@ import { DefaultInstanceFormFields } from "../../../components/instance/DefaultI import { useInstanceContext } from "../../../context/instance.js"; import { MerchantBackend } from "../../../declaration.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; +import { Duration } from "@gnu-taler/taler-util"; -type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { - auth_token?: string; +export type Entity = Omit<Omit<MerchantBackend.Instances.InstanceReconfigurationMessage, "default_pay_delay">, "default_wire_transfer_delay"> & { + default_pay_delay: Duration, + default_wire_transfer_delay: Duration, }; //MerchantBackend.Instances.InstanceAuthConfigurationMessage interface Props { - onUpdate: (d: Entity) => void; + onUpdate: (d: MerchantBackend.Instances.InstanceReconfigurationMessage) => void; selected: MerchantBackend.Instances.QueryInstancesResponse; isLoading: boolean; onBack: () => void; @@ -47,21 +49,12 @@ interface Props { function convert( from: MerchantBackend.Instances.QueryInstancesResponse, ): Entity { - const { ...rest } = from; - // const accounts = qAccounts - // .filter((a) => a.active) - // .map( - // (a) => - // ({ - // payto_uri: a.payto_uri, - // credit_facade_url: a.credit_facade_url, - // credit_facade_credentials: a.credit_facade_credentials, - // } as MerchantBackend.Instances.MerchantBankAccount), - // ); + const { default_pay_delay, default_wire_transfer_delay, ...rest } = from; + const defaults = { use_stefan: false, - default_pay_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 }, //two hours - default_wire_transfer_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 * 2 }, //two hours + default_pay_delay: Duration.fromTalerProtocolDuration(default_pay_delay), + default_wire_transfer_delay: Duration.fromTalerProtocolDuration(default_wire_transfer_delay), }; return { ...defaults, ...rest }; } @@ -72,20 +65,6 @@ export function UpdatePage({ onBack, }: Props): VNode { const { id } = useInstanceContext(); - // const currentTokenValue = getTokenValuePart(token); - - // function updateToken(token: string | undefined | null) { - // const value = - // token && token.startsWith("secret-token:") - // ? token.substring("secret-token:".length) - // : token; - - // if (!token) { - // onChangeAuth({ method: "external" }); - // } else { - // onChangeAuth({ method: "token", token: `secret-token:${value}` }); - // } - // } const [value, valueHandler] = useState<Partial<Entity>>(convert(selected)); @@ -101,9 +80,9 @@ export function UpdatePage({ default_pay_delay: !value.default_pay_delay ? i18n.str`required` : !!value.default_wire_transfer_delay && - value.default_wire_transfer_delay.d_us !== "forever" && - value.default_pay_delay.d_us !== "forever" && - value.default_pay_delay.d_us > value.default_wire_transfer_delay.d_us ? + value.default_wire_transfer_delay.d_ms !== "forever" && + value.default_pay_delay.d_ms !== "forever" && + value.default_pay_delay.d_ms > value.default_wire_transfer_delay.d_ms ? i18n.str`pay delay can't be greater than wire transfer delay` : undefined, default_wire_transfer_delay: !value.default_wire_transfer_delay ? i18n.str`required` @@ -127,7 +106,13 @@ export function UpdatePage({ ); const submit = async (): Promise<void> => { - await onUpdate(value as Entity); + const { default_pay_delay, default_wire_transfer_delay, ...rest } = value as Required<Entity>; + const result: MerchantBackend.Instances.InstanceReconfigurationMessage = { + default_pay_delay: Duration.toTalerProtocolDuration(default_pay_delay), + default_wire_transfer_delay: Duration.toTalerProtocolDuration(default_wire_transfer_delay), + ...rest, + } + await onUpdate(result); }; // const [active, setActive] = useState(false); @@ -144,26 +129,6 @@ export function UpdatePage({ </span> </div> </div> - {/* <div class="level-right"> - <div class="level-item"> - <h1 class="title"> - <button - class="button is-danger" - data-tooltip={i18n.str`Change the authorization method use for this instance.`} - onClick={(): void => { - setActive(!active); - }} - > - <div class="icon is-left"> - <i class="mdi mdi-lock-reset" /> - </div> - <span> - <i18n.Translate>Manage access token</i18n.Translate> - </span> - </button> - </h1> - </div> - </div> */} </div> </div> </section> |