summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-04-09 11:20:32 -0300
committerSebastian <sebasjm@gmail.com>2024-04-09 11:20:44 -0300
commita08ad2c631da0283a5e10189d06977a96665afae (patch)
tree6bc0e1e9e2818ed342f3d7db01da6370689ec9f9
parentc19d875861356e206cf44cc64cd29fdbdc6ffb32 (diff)
downloadwallet-core-a08ad2c631da0283a5e10189d06977a96665afae.tar.gz
wallet-core-a08ad2c631da0283a5e10189d06977a96665afae.tar.bz2
wallet-core-a08ad2c631da0283a5e10189d06977a96665afae.zip
fix #8638
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx112
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx153
2 files changed, 164 insertions, 101 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index ad36df3cc..90854d820 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -25,13 +25,13 @@ import {
Duration,
TalerError,
TalerMerchantApi,
- TranslatedString
+ TranslatedString,
} from "@gnu-taler/taler-util";
import {
useMerchantApiContext,
- useTranslationContext
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
+import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import {
@@ -69,7 +69,7 @@ interface Props {
export function CreatePage({ onCreate, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
- const { url: backendUrl } = useMerchantApiContext();
+ const { url: backendUrl, config } = useMerchantApiContext();
const devices = useInstanceOtpDevices();
const [state, setState] = useState<Partial<Entity>>({
@@ -81,12 +81,12 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
function updateState(up: (s: Partial<Entity>) => Partial<Entity>) {
setState((old) => {
- const newState = up(old)
+ const newState = up(old);
if (!newState.amount_editable) {
- newState.currency_editable = false
+ newState.currency_editable = false;
}
- return newState
- })
+ return newState;
+ });
}
const parsedPrice = !state.amount ? undefined : Amounts.parse(state.amount);
@@ -98,14 +98,13 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
? i18n.str`no valid. only characters and numbers`
: undefined,
description: !state.description ? i18n.str`should not be empty` : undefined,
- amount:
- !state.amount
- ? undefined
- : !parsedPrice
- ? i18n.str`not valid`
- : Amounts.isZero(parsedPrice)
- ? i18n.str`must be greater than 0`
- : undefined,
+ amount: !state.amount
+ ? undefined
+ : !parsedPrice
+ ? i18n.str`not valid`
+ : Amounts.isZero(parsedPrice)
+ ? i18n.str`must be greater than 0`
+ : undefined,
minimum_age:
state.minimum_age && state.minimum_age < 0
? i18n.str`should be greater that 0`
@@ -119,6 +118,8 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
: undefined,
};
+ const cList = Object.values(config.currencies).map((d) => d.name);
+
const hasErrors = Object.keys(errors).some(
(k) => (errors as Record<string, unknown>)[k] !== undefined,
);
@@ -133,20 +134,33 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!),
amount: state.amount_editable ? undefined : state.amount,
summary: state.summary_editable ? undefined : state.summary,
+ currency:
+ cList.length > 1 && state.currency_editable
+ ? undefined
+ : config.currency,
},
editable_defaults: {
amount: !state.amount_editable ? undefined : state.amount,
summary: !state.summary_editable ? undefined : state.summary,
+ currency:
+ cList.length === 1 || !state.currency_editable
+ ? undefined
+ : config.currency,
},
otp_id: state.otpId!,
});
-
};
- const deviceList = !devices || devices instanceof TalerError || devices.type === "fail" ? [] : devices.body;
- const deviceMap = deviceList.reduce((prev, cur) => {
- prev[cur.otp_device_id] = cur.device_description as TranslatedString
- return prev
- }, {} as Record<string, TranslatedString>)
+ const deviceList =
+ !devices || devices instanceof TalerError || devices.type === "fail"
+ ? []
+ : devices.body;
+ const deviceMap = deviceList.reduce(
+ (prev, cur) => {
+ prev[cur.otp_device_id] = cur.device_description as TranslatedString;
+ return prev;
+ },
+ {} as Record<string, TranslatedString>,
+ );
return (
<div>
<section class="section is-main-section">
@@ -160,7 +174,9 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
>
<InputWithAddon<Entity>
name="id"
- help={new URL(`templates/${state.id ?? ""}`, backendUrl.href).href}
+ help={
+ new URL(`templates/${state.id ?? ""}`, backendUrl.href).href
+ }
label={i18n.str`Identifier`}
tooltip={i18n.str`Name of the template in URLs.`}
/>
@@ -193,13 +209,19 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
label={i18n.str`Amount is editable`}
tooltip={i18n.str`Allow the user to select the amount to pay.`}
/>
- {/* <InputToggle<Entity>
- name="currency_editable"
- readonly={!state.amount_editable}
- label={i18n.str`Currency is editable`}
- tooltip={i18n.str`Allow the user to change currency.`}
- /> */}
-
+ {cList.length > 1 && (
+ <Fragment>
+ <InputToggle<Entity>
+ name="currency_editable"
+ readonly={!state.amount_editable}
+ label={i18n.str`Currency is editable`}
+ tooltip={i18n.str`Allow the user to change currency.`}
+ />
+ <TextField name="sc" label={i18n.str`Supported currencies`}>
+ <i18n.Translate>supported currencies: {cList.join(", ")}</i18n.Translate>
+ </TextField>
+ </Fragment>
+ )}
<InputNumber<Entity>
name="minimum_age"
label={i18n.str`Minimum age`}
@@ -212,26 +234,34 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
help=""
tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
/>
- {!deviceList.length ? <TextField
- name="otpId"
- label={i18n.str`OTP device`}
- tooltip={i18n.str`Use to verify transaction while offline.`}
- >
- <i18n.Translate>No OTP device.</i18n.Translate>&nbsp;<a href="/otp-devices/new"><i18n.Translate>Add one first</i18n.Translate></a>
- </TextField> :
+ {!deviceList.length ? (
+ <TextField
+ name="otpId"
+ label={i18n.str`OTP device`}
+ tooltip={i18n.str`Use to verify transaction while offline.`}
+ >
+ <i18n.Translate>No OTP device.</i18n.Translate>&nbsp;
+ <a href="/otp-devices/new">
+ <i18n.Translate>Add one first</i18n.Translate>
+ </a>
+ </TextField>
+ ) : (
<InputSelector<Entity>
name="otpId"
label={i18n.str`OTP device`}
- values={[undefined, ...deviceList.map(e => e.otp_device_id)]}
+ values={[
+ undefined,
+ ...deviceList.map((e) => e.otp_device_id),
+ ]}
toStr={(v?: string) => {
if (!v) {
- return i18n.str`No device`
+ return i18n.str`No device`;
}
- return deviceMap[v]
+ return deviceMap[v];
}}
tooltip={i18n.str`Use to verify transaction in offline mode.`}
/>
- }
+ )}
</FormProvider>
<div class="buttons is-right mt-5">
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
index f9e2a3b01..3a32409a0 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
@@ -25,10 +25,13 @@ import {
Duration,
TalerError,
TalerMerchantApi,
- TranslatedString
+ TranslatedString,
} from "@gnu-taler/taler-util";
-import { useMerchantApiContext, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
+import {
+ useMerchantApiContext,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import {
@@ -45,12 +48,12 @@ import { TextField } from "../../../../components/form/TextField.js";
import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
type Entity = {
- description?: string,
- otpId?: string | null,
- summary?: string,
- amount?: AmountString,
- minimum_age?: number,
- pay_duration?: Duration,
+ description?: string;
+ otpId?: string | null;
+ summary?: string;
+ amount?: AmountString;
+ minimum_age?: number;
+ pay_duration?: Duration;
summary_editable?: boolean;
amount_editable?: boolean;
currency_editable?: boolean;
@@ -64,17 +67,22 @@ interface Props {
export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
- const { url: backendUrl } = useMerchantApiContext();
-
-
+ const { url: backendUrl, config } = useMerchantApiContext();
const [state, setState] = useState<Partial<Entity>>({
description: template.template_description,
minimum_age: template.template_contract.minimum_age,
otpId: template.otp_id,
- pay_duration: template.template_contract.pay_duration ? Duration.fromTalerProtocolDuration(template.template_contract.pay_duration) : undefined,
- summary: template.editable_defaults?.summary ?? template.template_contract.summary,
- amount: template.editable_defaults?.amount ?? template.template_contract.amount as AmountString | undefined,
+ pay_duration: template.template_contract.pay_duration
+ ? Duration.fromTalerProtocolDuration(
+ template.template_contract.pay_duration,
+ )
+ : undefined,
+ summary:
+ template.editable_defaults?.summary ?? template.template_contract.summary,
+ amount:
+ template.editable_defaults?.amount ??
+ (template.template_contract.amount as AmountString | undefined),
currency_editable: !!template.editable_defaults?.currency,
summary_editable: !!template.editable_defaults?.summary,
amount_editable: !!template.editable_defaults?.amount,
@@ -82,37 +90,38 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
function updateState(up: (s: Partial<Entity>) => Partial<Entity>) {
setState((old) => {
- const newState = up(old)
+ const newState = up(old);
if (!newState.amount_editable) {
- newState.currency_editable = false
+ newState.currency_editable = false;
}
- return newState
- })
+ return newState;
+ });
}
- const devices = useInstanceOtpDevices()
- const deviceList = !devices || devices instanceof TalerError || devices.type === "fail" ? [] : devices.body
- const deviceMap = deviceList.reduce((prev, cur) => {
- prev[cur.otp_device_id] = cur.device_description as TranslatedString
- return prev
- }, {} as Record<string, TranslatedString>)
+ const devices = useInstanceOtpDevices();
+ const deviceList =
+ !devices || devices instanceof TalerError || devices.type === "fail"
+ ? []
+ : devices.body;
+ const deviceMap = deviceList.reduce(
+ (prev, cur) => {
+ prev[cur.otp_device_id] = cur.device_description as TranslatedString;
+ return prev;
+ },
+ {} as Record<string, TranslatedString>,
+ );
- const parsedPrice = !state.amount
- ? undefined
- : Amounts.parse(state.amount);
+ const parsedPrice = !state.amount ? undefined : Amounts.parse(state.amount);
const errors: FormErrors<Entity> = {
- description: !state.description
- ? i18n.str`should not be empty`
- : undefined,
- amount:
- !state.amount
- ? undefined
- : !parsedPrice
- ? i18n.str`not valid`
- : Amounts.isZero(parsedPrice)
- ? i18n.str`must be greater than 0`
- : undefined,
+ description: !state.description ? i18n.str`should not be empty` : undefined,
+ amount: !state.amount
+ ? undefined
+ : !parsedPrice
+ ? i18n.str`not valid`
+ : Amounts.isZero(parsedPrice)
+ ? i18n.str`must be greater than 0`
+ : undefined,
minimum_age:
state.minimum_age && state.minimum_age < 0
? i18n.str`should be greater that 0`
@@ -126,6 +135,8 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
: undefined,
};
+ const cList = Object.values(config.currencies).map((d) => d.name);
+
const hasErrors = Object.keys(errors).some(
(k) => (errors as Record<string, unknown>)[k] !== undefined,
);
@@ -139,17 +150,23 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!),
amount: state.amount_editable ? undefined : state.amount,
summary: state.summary_editable ? undefined : state.summary,
+ currency:
+ cList.length > 1 && state.currency_editable
+ ? undefined
+ : config.currency,
},
editable_defaults: {
amount: !state.amount_editable ? undefined : state.amount,
summary: !state.summary_editable ? undefined : state.summary,
+ currency:
+ cList.length === 1 || !state.currency_editable
+ ? undefined
+ : config.currency,
},
otp_id: state.otpId!,
- })
-
+ });
};
-
return (
<div>
<section class="section">
@@ -176,7 +193,6 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
valueHandler={updateState}
errors={errors}
>
-
<Input<Entity>
name="description"
label={i18n.str`Description`}
@@ -204,12 +220,21 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
label={i18n.str`Amount is editable`}
tooltip={i18n.str`Allow the user to select the amount to pay.`}
/>
- {/* <InputToggle<Entity>
- name="currency_editable"
- readonly={!state.amount_editable}
- label={i18n.str`Currency is editable`}
- tooltip={i18n.str`Allow the user to change currency.`}
- /> */}
+ {cList.length > 1 && (
+ <Fragment>
+ <InputToggle<Entity>
+ name="currency_editable"
+ readonly={!state.amount_editable}
+ label={i18n.str`Currency is editable`}
+ tooltip={i18n.str`Allow the user to change currency.`}
+ />
+ <TextField name="sc" label={i18n.str`Supported currencies`}>
+ <i18n.Translate>
+ supported currencies: {cList.join(", ")}
+ </i18n.Translate>
+ </TextField>
+ </Fragment>
+ )}
<InputNumber<Entity>
name="minimum_age"
label={i18n.str`Minimum age`}
@@ -222,26 +247,34 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
help=""
tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
/>
- {!deviceList.length ? <TextField
- name="otpId"
- label={i18n.str`OTP device`}
- tooltip={i18n.str`Use to verify transaction while offline.`}
- >
- <i18n.Translate>No OTP device.</i18n.Translate>&nbsp;<a href="/otp-devices/new"><i18n.Translate>Add one first</i18n.Translate></a>
- </TextField> :
+ {!deviceList.length ? (
+ <TextField
+ name="otpId"
+ label={i18n.str`OTP device`}
+ tooltip={i18n.str`Use to verify transaction while offline.`}
+ >
+ <i18n.Translate>No OTP device.</i18n.Translate>&nbsp;
+ <a href="/otp-devices/new">
+ <i18n.Translate>Add one first</i18n.Translate>
+ </a>
+ </TextField>
+ ) : (
<InputSelector<Entity>
name="otpId"
label={i18n.str`OTP device`}
- values={[undefined, ...deviceList.map(e => e.otp_device_id)]}
+ values={[
+ undefined,
+ ...deviceList.map((e) => e.otp_device_id),
+ ]}
toStr={(v?: string) => {
if (!v) {
- return i18n.str`No device`
+ return i18n.str`No device`;
}
- return deviceMap[v]
+ return deviceMap[v];
}}
tooltip={i18n.str`Use to verify transaction in offline mode.`}
/>
- }
+ )}
</FormProvider>
<div class="buttons is-right mt-5">