taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit dcf109fd69add8b61acb570641fa1deeeef64fd1
parent 4611940d61d721dbd5b8cca9faed3795da113ca6
Author: Sebastian <sebasjm@gmail.com>
Date:   Sat, 15 Nov 2025 18:11:23 -0300

fix #10597

Diffstat:
Mpackages/merchant-backoffice-ui/src/components/notifications/CreatedSuccessfully.tsx | 11++++++++---
Mpackages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx | 128++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx | 2+-
3 files changed, 74 insertions(+), 67 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/components/notifications/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/components/notifications/CreatedSuccessfully.tsx @@ -17,19 +17,24 @@ * * @author Sebastian Javier Marchano (sebasjm) */ +import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { ComponentChildren, h, VNode } from "preact"; +import { doAutoFocus } from "../../../../web-util/src/components/utils.js"; interface Props { onCreateAnother?: () => void; onConfirm: () => void; + focus?: boolean; children: ComponentChildren; } export function CreatedSuccessfully({ children, + focus, onConfirm, onCreateAnother, }: Props): VNode { + const { i18n } = useTranslationContext() return ( <div class="columns is-fullwidth is-vcentered mt-3"> <div class="column" /> @@ -43,11 +48,11 @@ export function CreatedSuccessfully({ <div class="buttons is-right"> {onCreateAnother && ( <button class="button is-info" onClick={onCreateAnother}> - Create another + <i18n.Translate>Create another</i18n.Translate> </button> )} - <button class="button is-info" onClick={onConfirm}> - Continue + <button class="button is-info" ref={focus ? doAutoFocus : undefined} onClick={onConfirm}> + <i18n.Translate>Continue</i18n.Translate> </button> </div> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx @@ -50,13 +50,19 @@ interface Props { onBack?: () => void; } -const algorithms = [0, 1, 2]; -const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"]; +const algorithms = [0, 1]; +const algorithmsNames = ["30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"]; export function CreatePage({ onCreated, onBack }: Props): VNode { const { i18n } = useTranslationContext(); - const [state, setState] = useState<Partial<Entity>>({}); + const [state, setState] = useState<Partial<Entity>>({ + otp_key: "E5V6OSMTZRL2ARYKNDJ4SUDGYOCLQUOT", + otp_ctr: 1, + otp_device_id: "1", + otp_device_description: "asd", + otp_algorithm: 0, + }); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); const { state: session, lib } = useSessionContext(); @@ -68,7 +74,7 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { : !/[a-zA-Z0-9]*/.test(state.otp_device_id) ? i18n.str`Invalid. Please enter letters and numbers only.` : undefined, - otp_algorithm: !state.otp_algorithm ? i18n.str`Required` : undefined, + // otp_algorithm: !state.otp_algorithm ? i18n.str`Required` : undefined, otp_key: !state.otp_key ? i18n.str`Required` : !isRfc3548Base32Charset(state.otp_key) @@ -134,67 +140,63 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { toStr={(v) => algorithmsNames[v]} fromStr={(v) => Number(v)} /> - {state.otp_algorithm ? ( - <Fragment> - <InputWithAddon<Entity> - expand - name="otp_key" - label={i18n.str`Device key`} - inputType={showKey ? "text" : "password"} - help={i18n.str`Be sure to choose a password that is hard to guess, or use the random generator.`} - tooltip={i18n.str`Your device needs to match exactly the same value`} - fromStr={(v) => v.toUpperCase()} - addonAfterAction={() => { - setShowKey(!showKey); + <InputWithAddon<Entity> + expand + name="otp_key" + label={i18n.str`Device key`} + inputType={showKey ? "text" : "password"} + help={i18n.str`Be sure to choose a password that is hard to guess, or use the random generator.`} + tooltip={i18n.str`Your device needs to match exactly the same value`} + fromStr={(v) => v.toUpperCase()} + addonAfterAction={() => { + setShowKey(!showKey); + }} + addonAfter={ + <span class="icon"> + {showKey ? ( + <i class="mdi mdi-eye" /> + ) : ( + <i class="mdi mdi-eye-off" /> + )} + </span> + } + side={ + <button + type="button" + data-tooltip={i18n.str`Generate random secret key`} + class="button is-info mr-3" + onClick={(e) => { + setState((s) => ({ + ...s, + otp_key: randomRfc3548Base32Key(), + })); + e.preventDefault(); }} - addonAfter={ - <span class="icon"> - {showKey ? ( - <i class="mdi mdi-eye" /> - ) : ( - <i class="mdi mdi-eye-off" /> - )} - </span> - } - side={ - <button - type="button" - data-tooltip={i18n.str`Generate random secret key`} - class="button is-info mr-3" - onClick={(e) => { - setState((s) => ({ - ...s, - otp_key: randomRfc3548Base32Key(), - })); - e.preventDefault(); - }} - > - <i18n.Translate>Random</i18n.Translate> - </button> - } - /> - </Fragment> - ) : undefined} - </FormProvider> - - <div class="buttons is-right mt-5"> - {onBack && ( - <button class="button" type="button" onClick={onBack}> - <i18n.Translate>Cancel</i18n.Translate> - </button> - )} - <ButtonBetterBulma - data-tooltip={ - hasErrors - ? i18n.str`Please complete the marked fields` - : i18n.str`Confirm operation` + > + <i18n.Translate>Random</i18n.Translate> + </button> } - onClick={create} - type="submit" - > - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> - </div> + /> + + <div class="buttons is-right mt-5"> + {onBack && ( + <button class="button" type="button" onClick={onBack}> + <i18n.Translate>Cancel</i18n.Translate> + </button> + )} + <ButtonBetterBulma + data-tooltip={ + hasErrors + ? i18n.str`Please complete the marked fields` + : i18n.str`Confirm operation` + } + onClick={create} + type="submit" + > + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </div> + </FormProvider> </div> <div class="column" /> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx @@ -39,7 +39,7 @@ export function CreatedSuccessfully({ const qrTextSafe = `otpauth://totp/${state.instance}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key.substring(0, 6)}...`; return ( - <Template onConfirm={onConfirm} > + <Template onConfirm={onConfirm} focus> <p class="is-size-5"> <i18n.Translate> You can scan the next QR code with your device or save the key before continuing.