commit f62dbfaf8593ecaa72c3911f6d55713beeee3dc8
parent 7fd25c6cb00f106e8487283dbeee59b562c382e9
Author: Sebastian <sebasjm@taler-systems.com>
Date: Thu, 5 Mar 2026 15:33:44 -0300
fix #11189
Diffstat:
5 files changed, 103 insertions(+), 63 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/components/SolveMFA.tsx b/packages/merchant-backoffice-ui/src/components/SolveMFA.tsx
@@ -26,9 +26,7 @@ import {
usePreference,
} from "../hooks/preference.js";
import { FormErrors, FormProvider } from "./form/FormProvider.js";
-import { Input } from "./form/Input.js";
import { InputCode } from "./form/InputCode.js";
-import { time } from "console";
const TALER_SCREEN_ID = 5;
@@ -38,6 +36,7 @@ export interface Props {
currentChallenge: ChallengeResponse;
focus?: boolean;
initial?: { request: Challenge; response: ChallengeRequestResponse };
+ showFull?: Partial<Record<TanChannel, string>>;
}
interface Form {
@@ -54,12 +53,14 @@ function SolveChallenge({
onCancel,
onSolved,
focus,
+ showFull,
}: {
onCancel: () => void;
challenge: Challenge;
expiration: AbsoluteTime;
onSolved: () => void;
focus?: boolean;
+ showFull: Partial<Record<TanChannel, string>>;
}): VNode {
const { i18n } = useTranslationContext();
const { lib } = useSessionContext();
@@ -170,6 +171,14 @@ function SolveChallenge({
{(function (): VNode {
switch (challenge.tan_channel) {
case TanChannel.SMS:
+ if (showFull[TanChannel.SMS]) {
+ return (
+ <i18n.Translate>
+ The verification code sent to the phone "
+ <b>{showFull[TanChannel.SMS]}</b>"
+ </i18n.Translate>
+ );
+ }
return (
<i18n.Translate>
The verification code sent to the phone number ending
@@ -177,6 +186,14 @@ function SolveChallenge({
</i18n.Translate>
);
case TanChannel.EMAIL:
+ if (showFull[TanChannel.EMAIL]) {
+ return (
+ <i18n.Translate>
+ The verification code sent to the email address "
+ <b>{showFull[TanChannel.EMAIL]}</b>"
+ </i18n.Translate>
+ );
+ }
return (
<i18n.Translate>
The verification code sent to the email address
@@ -256,6 +273,7 @@ export function SolveMFAChallenges({
onCancel,
initial,
focus,
+ showFull,
}: Props): VNode {
const { i18n } = useTranslationContext();
const { state: session, lib, logIn } = useSessionContext();
@@ -361,20 +379,7 @@ export function SolveMFAChallenges({
challenge={selected.ch}
expiration={selected.expiration}
focus={focus}
- // onSolved={() => {
- // setSelected(undefined);
- // const newSolved = [...solved, selected.ch.challenge_id];
-
- // const done = currentChallenge.combi_and
- // ? newSolved.length === currentChallenge.challenges.length
- // : newSolved.length > 0;
-
- // if (done) {
- // onCompleted(newSolved);
- // } else {
- // setSolved(newSolved);
- // }
- // }}
+ showFull={showFull ?? {}}
onSolved={async () => {
setSelected(undefined);
const total = [...solved, selected.ch.challenge_id];
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
@@ -66,13 +66,27 @@ export function InputToggle<T>({
*
</span>
)}
- {tooltip && <Tooltip text={tooltip} ><i class="icon mdi mdi-information" /></Tooltip>}
+ {tooltip && (
+ <Tooltip text={tooltip}>
+ <i class="icon mdi mdi-information" />
+ </Tooltip>
+ )}
</label>
</div>
<div class="field-body is-flex-grow-3">
<div class="field">
<p class={expand ? "control is-expanded" : "control"}>
<label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
+ {/* <div class="group relative inline-flex w-11 shrink-0 rounded-full bg-gray-200 p-0.5 inset-ring inset-ring-gray-900/5 outline-offset-2 outline-indigo-600 transition-colors duration-200 ease-in-out has-checked:bg-indigo-600 has-focus-visible:outline-2 dark:bg-white/5 dark:inset-ring-white/10 dark:outline-indigo-500 dark:has-checked:bg-indigo-500">
+ <span class="size-5 rounded-full bg-white shadow-xs ring-1 ring-gray-900/5 transition-transform duration-200 ease-in-out group-has-checked:translate-x-5"></span>
+ <input
+ type="checkbox"
+ name="setting"
+ aria-label="Use setting"
+ class="absolute inset-0 size-full appearance-none focus:outline-hidden"
+ />
+ </div> */}
+
<input
type="checkbox"
class={"toggle-checkbox"}
@@ -86,16 +100,26 @@ export function InputToggle<T>({
}}
readonly={readonly}
name={String(name)}
+ style={{visibility:"inherit", appearance:"none"}}
disabled={readonly}
onChange={onCheckboxClick}
/>
<div
+ // type="button"
class={`toggle-switch ${readonly ? "disabled" : ""} ${
toBoolean(value) === undefined ? "no-dot" : ""
}`}
+ // tabIndex={0}
+ // role="switch"
+ // aria-checked={toBoolean(value)}
+ // onKeyPress={(e) => {
+ // if (e.key === "y") {
+ // onCheckboxClick();
+ // }
+ // }}
style={{ cursor: readonly ? "default" : undefined }}
- ></div>
+ />
</label>
<p>{help}</p>
</p>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
@@ -39,6 +39,7 @@ export interface Props<T> extends InputProps<T> {
inputExtra?: any;
children?: ComponentChildren;
side?: ComponentChildren;
+ focus?:boolean;
autoComplete?: SupportedAutocomplete;
}
@@ -60,6 +61,7 @@ export function InputWithAddon<T>({
inputType,
inputExtra,
side,
+ focus,
addonAfter,
addonAfterAction,
toStr = defaultToString,
@@ -112,6 +114,7 @@ export function InputWithAddon<T>({
readonly={readonly}
autoComplete={autoComplete}
disabled={readonly}
+ focus={focus}
name={String(name)}
value={toStr(value)}
onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void =>
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
@@ -826,23 +826,23 @@ export function CreatePage({
</div>
</InputGroup>
</FragmentPersonaFlag>
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" type="button" onClick={onBack}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ )}
+ <ButtonBetterBulma
+ class="button is-success"
+ type="submit"
+ onClick={create}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </ButtonBetterBulma>
+ </div>
</FormProvider>
-
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <ButtonBetterBulma
- class="button is-success"
- type="submit"
- onClick={create}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </ButtonBetterBulma>
- </div>
</div>
+
<div class="column" />
</div>
</section>
diff --git a/packages/merchant-backoffice-ui/src/paths/newAccount/index.tsx b/packages/merchant-backoffice-ui/src/paths/newAccount/index.tsx
@@ -236,6 +236,10 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode {
onCompleted={retry}
initial={mfa.initial}
focus
+ showFull={{
+ [TanChannel.EMAIL]: value.email,
+ [TanChannel.SMS]: value.phone,
+ }}
onCancel={mfa.doCancelChallenge}
/>
);
@@ -256,21 +260,22 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode {
<i18n.Translate>Self provision</i18n.Translate>
</p>
</header>
- <section
- class="modal-card-body"
- style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
+ <FormProvider<Account>
+ name="settings"
+ errors={errors}
+ object={value}
+ valueHandler={valueHandler}
>
- <FormProvider<Account>
- name="settings"
- errors={errors}
- object={value}
- valueHandler={valueHandler}
+ <section
+ class="modal-card-body"
+ style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
>
<InputWithAddon<Account>
name="id"
label={i18n.str`Username`}
tooltip={i18n.str`Name of the instance in URLs. The 'admin' instance is special in that it is used to administer other instances.`}
autoComplete="username"
+ focus
/>
<Input<Account>
@@ -298,6 +303,7 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode {
label={i18n.str`Email`}
tooltip={i18n.str`Contact email`}
name="email"
+ inputType="email"
autoComplete="email"
/>
) : undefined}
@@ -306,6 +312,7 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode {
label={i18n.str`Phone`}
tooltip={i18n.str`Contact phone number`}
name="phone"
+ inputType="tel"
autoComplete="tel"
/>
) : undefined}
@@ -318,6 +325,7 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode {
<a
href="/terms"
target="_blank"
+ tabIndex={-1}
referrerpolicy="no-referrer"
>
<i18n.Translate>Terms of service</i18n.Translate>
@@ -326,30 +334,30 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode {
}
tooltip={i18n.str`You must accept the Terms of service to continue.`}
/>
- </FormProvider>
- </section>
- <footer
- class="modal-card-foot "
- style={{
- justifyContent: "space-between",
- border: "1px solid",
- borderTop: 0,
- }}
- >
- <button
- class="button"
- type="button"
- onClick={() => {
- saveForm({});
- onCancel();
+ </section>
+ <footer
+ class="modal-card-foot "
+ style={{
+ justifyContent: "space-between",
+ border: "1px solid",
+ borderTop: 0,
}}
>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <ButtonBetterBulma onClick={create} type="submit">
- <i18n.Translate>Create</i18n.Translate>
- </ButtonBetterBulma>
- </footer>
+ <button
+ class="button"
+ type="button"
+ onClick={() => {
+ saveForm({});
+ onCancel();
+ }}
+ >
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ <ButtonBetterBulma onClick={create} type="submit">
+ <i18n.Translate>Create</i18n.Translate>
+ </ButtonBetterBulma>
+ </footer>
+ </FormProvider>
</div>
</div>
</div>