commit 8c142162591487ca02173c3b9c1ac62205b26e7e
parent 5dabf9f4103562b9315600667792459c0e7ed9c8
Author: Sebastian <sebasjm@gmail.com>
Date: Sat, 5 Jul 2025 17:39:50 -0300
fix #10142
Diffstat:
3 files changed, 90 insertions(+), 17 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts b/packages/merchant-backoffice-ui/src/hooks/instance.ts
@@ -220,7 +220,7 @@ function getLongPollingReasonSet(
): KycStatusLongPollingReason | undefined {
const acc = data.find((k) => getLongPollingReason(k));
if (!acc) return undefined;
- console.log("found reason", acc);
+ // console.log("found reason", acc);
return getLongPollingReason(acc);
}
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
@@ -20,6 +20,7 @@
*/
import {
+ AmountLike,
AmountString,
Amounts,
Duration,
@@ -45,10 +46,13 @@ import { TextField } from "../../../../components/form/TextField.js";
import { useSessionContext } from "../../../../context/session.js";
import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
import { WithId } from "../../../../declaration.js";
+import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
+import { NotificationCard } from "../../../../components/menu/index.js";
type Entity = {
description?: string;
otpId?: string | null;
+ currency: string;
summary?: string;
amount?: string;
minimum_age?: number;
@@ -64,10 +68,41 @@ interface Props {
template: TalerMerchantApi.TemplateDetails & WithId;
}
+function changeToCurrency(am: string | undefined, currency: string | undefined): string | undefined {
+ if (!am || !currency) return undefined
+ const amount = Amounts.parse(am);
+ if (!amount) return undefined;
+ const newAmount = {
+ ...amount,
+ currency,
+ };
+ return Amounts.stringify(newAmount);
+}
+
export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
const { config, state: session } = useSessionContext();
+ const cList = Object.values(config.currencies).map((d) => d.name);
+ const supportedCurrencies = Object.keys(config.currencies);
+
+ const currentAmount =
+ template.editable_defaults?.amount ??
+ (template.template_contract.amount as AmountString | undefined);
+
+ const default_currency = supportedCurrencies[0];
+
+ const template_currency = currentAmount
+ ? Amounts.currencyOf(currentAmount)
+ : default_currency;
+
+ const unsupportedCurrency =
+ supportedCurrencies.indexOf(template_currency) === -1;
+
+ const startingAmount = unsupportedCurrency
+ ? changeToCurrency(currentAmount, default_currency)
+ : currentAmount
+
const [state, setState] = useState<Partial<Entity>>({
description: template.template_description,
minimum_age: template.template_contract.minimum_age,
@@ -79,9 +114,8 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
: undefined,
summary:
template.editable_defaults?.summary ?? template.template_contract.summary,
- amount:
- template.editable_defaults?.amount ??
- (template.template_contract.amount as AmountString | undefined),
+ currency: unsupportedCurrency ? default_currency : template_currency,
+ amount: startingAmount,
currency_editable: !!template.editable_defaults?.currency,
summary_editable: template.editable_defaults?.summary !== undefined,
amount_editable: template.editable_defaults?.amount !== undefined,
@@ -93,6 +127,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
if (!newState.amount_editable) {
newState.currency_editable = false;
}
+ newState.amount = changeToCurrency(newState.amount, newState.currency)
return newState;
});
}
@@ -138,13 +173,13 @@ 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,
);
- const zero = Amounts.stringify(Amounts.zeroOfCurrency(config.currency));
+ const zero = Amounts.stringify(
+ Amounts.zeroOfCurrency(state.currency ?? default_currency),
+ );
const submitForm = () => {
if (hasErrors) return Promise.reject();
@@ -160,7 +195,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
currency:
cList.length > 1 && state.currency_editable
? undefined
- : config.currency,
+ : state.currency,
};
return onUpdate({
template_description: state.description!,
@@ -171,7 +206,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
currency:
cList.length === 1 || !state.currency_editable
? undefined
- : config.currency,
+ : state.currency,
},
otp_id: state.otpId!,
});
@@ -199,6 +234,15 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
</div>
</section>
<hr />
+ {unsupportedCurrency ? (
+ <NotificationCard
+ notification={{
+ message: i18n.str`The template configuration needs to be fixed.`,
+ description: i18n.str`The currency of the template is ${template_currency} and is not in the list of supported currencies.`,
+ type: "WARN",
+ }}
+ />
+ ) : undefined}
<section class="section is-main-section">
<div class="columns">
@@ -225,9 +269,31 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
label={i18n.str`Summary is editable`}
tooltip={i18n.str`Allow the user to change the summary.`}
/>
- <InputCurrency<Entity>
+ {supportedCurrencies.length > 1 ? (
+ <InputSelector
+ name="currency"
+ label={i18n.str`Supported currencies`}
+ values={supportedCurrencies}
+ // toStr={(str) => {
+ // if (str === "none") return i18n.str`Without authentication`;
+ // if (str === "basic")
+ // return i18n.str`With username and password`;
+ // if (str === "bearer") return i18n.str`With token`;
+ // return i18n.str`Do not change`;
+ // }}
+ />
+ ) : undefined}
+
+ <InputWithAddon<Entity>
name="amount"
label={i18n.str`Amount`}
+ addonBefore={state.currency}
+ inputType="number"
+ toStr={(v?: AmountString) => v?.split(":")[1] || ""}
+ fromStr={(v: string) =>
+ !v ? undefined : `${state.currency}:${v}`
+ }
+ inputExtra={{ min: 0, step: 0.001 }}
tooltip={i18n.str`If specified, this template will create orders with the same price`}
/>
<InputToggle<Entity>
@@ -243,13 +309,11 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
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>
)}
+ {/* <TextField name="sc" label={i18n.str`Supported currencies`}>
+ <i18n.Translate>{cList.join(", ")}</i18n.Translate>
+ </TextField> */}
<InputNumber<Entity>
name="minimum_age"
label={i18n.str`Minimum age`}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
@@ -30,6 +30,7 @@ import {
} from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
+import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
type Entity = TalerMerchantApi.TemplateContractDetails;
@@ -103,12 +104,20 @@ export function UsePage({ id, template, onCreateOrder, onBack }: Props): VNode {
valueHandler={setState}
errors={errors}
>
- <InputCurrency<Entity>
+ <InputWithAddon<Entity>
name="amount"
label={i18n.str`Amount`}
- readonly={!!template.template_contract.amount}
+ addonBefore={state.currency}
+ inputType="number"
+ toStr={(v?: AmountString) => v?.split(":")[1] || ""}
+ fromStr={(v: string) =>
+ !v ? undefined : `${state.currency}:${v}`
+ }
+ inputExtra={{ min: 0, step: 0.001 }}
tooltip={i18n.str`Amount of the order`}
+ readonly={!!template.template_contract.amount}
/>
+
<Input<Entity>
name="summary"
inputType="multiline"