taler-typescript-core

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

commit 0a64dc9618014db3a8bab29dd3ae9cf01cf2ff85
parent 4c075fb41d6d13c741c879af335a53b5c3c7c29c
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Thu, 30 Apr 2026 15:40:57 -0300

fix #10904 again, reported by cg

Diffstat:
Mpackages/merchant-backoffice-ui/src/components/SolveMFA.tsx | 400++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mpackages/merchant-backoffice-ui/src/paths/login/index.tsx | 252+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mpackages/merchant-backoffice-ui/src/paths/newAccount/index.tsx | 215++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mpackages/merchant-backoffice-ui/src/paths/resetAccount/index.tsx | 102++++++++++++++++++++++++++++++++++++++-----------------------------------------
4 files changed, 470 insertions(+), 499 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/components/SolveMFA.tsx b/packages/merchant-backoffice-ui/src/components/SolveMFA.tsx @@ -278,130 +278,121 @@ function SolveChallenge({ <div class="columns is-centered" style={{ margin: "auto" }}> <div class="column is-two-thirds "> - <div class="modal is-active" style={{ position: "initial" }}> - <div class="modal-card" style={{ width: "100%", margin: 0 }}> - <header - class="modal-card-head" - style={{ border: "1px solid", borderBottom: 0 }} - > - <p class="modal-card-title"> - <i18n.Translate>Validation code sent.</i18n.Translate> - </p> - </header> - <FormProvider<Form> - name="settings" - errors={errors} - object={value} - valueHandler={valueHandler} - > - <section - class="modal-card-body" - style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} - > - <p> - {(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 with "<b>{challenge.tan_info}</b>" - </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 - starting with "<b>{challenge.tan_info}</b>" - </i18n.Translate> - ); + <FormProvider<Form> + name="settings" + errors={errors} + object={value} + valueHandler={valueHandler} + > + <header style={{ border: "1px solid", borderBottom: 0 }}> + <p class="modal-card-title"> + <i18n.Translate>Validation code sent.</i18n.Translate> + </p> + </header> + <section + class="modal-card-body" + style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} + > + <p> + {(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> + ); } - })()} - </p> - - <InputCode<Form> - name="code" - key={codeKey} - label={i18n.str`Verification code`} - dashesIndex={[3]} - size={8} - focus={focus} - readonly={!tries.solvable || showExpired} - filter={(c) => { - const v = Number.parseInt(c, 10); - if (Number.isNaN(v) || v > 9 || v < 0) return undefined; - return String(v); - }} - /> - - {showExpired ? ( - <div style={{ display: "flex" }}> - <p - class="has-text-danger" - style={{ alignContent: "center", marginRight: 8 }} - > - <i18n.Translate>Code expired.</i18n.Translate> - </p> - </div> - ) : undefined} - <div> - <div style={{ display: "flex" }}> - <p style={{ alignContent: "center", marginRight: 8 }}> + return ( <i18n.Translate> - Didn't received the code? + The verification code sent to the phone number ending + with "<b>{challenge.tan_info}</b>" </i18n.Translate> - </p> - <ButtonBetterBulma - class="button" - type="button" - onClick={resend} - > - <i18n.Translate>Resend</i18n.Translate> - </ButtonBetterBulma> - </div> - </div> - <RetransmissionCodeLimitExpiration - expiration={currentRetransmission} - /> - </section> - <footer - class="modal-card-foot " - style={{ - justifyContent: "space-between", - border: "1px solid", - borderTop: 0, - }} - > - <button class="button" type="button" onClick={onCancel}> - <i18n.Translate>Cancel</i18n.Translate> - </button> + ); + 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 + starting with "<b>{challenge.tan_info}</b>" + </i18n.Translate> + ); + } + })()} + </p> + + <InputCode<Form> + name="code" + key={codeKey} + label={i18n.str`Verification code`} + dashesIndex={[3]} + size={8} + focus={focus} + readonly={!tries.solvable || showExpired} + filter={(c) => { + const v = Number.parseInt(c, 10); + if (Number.isNaN(v) || v > 9 || v < 0) return undefined; + return String(v); + }} + /> + + {showExpired ? ( + <div style={{ display: "flex" }}> + <p + class="has-text-danger" + style={{ alignContent: "center", marginRight: 8 }} + > + <i18n.Translate>Code expired.</i18n.Translate> + </p> + </div> + ) : undefined} + <div> + <div style={{ display: "flex" }}> + <p style={{ alignContent: "center", marginRight: 8 }}> + <i18n.Translate>Didn't received the code?</i18n.Translate> + </p> <ButtonBetterBulma - type="submit" - disabled={!tries.numTries} - onClick={verify} + class="button" + type="button" + onClick={resend} > - <i18n.Translate>Verify</i18n.Translate> + <i18n.Translate>Resend</i18n.Translate> </ButtonBetterBulma> - </footer> - </FormProvider> - </div> - </div> + </div> + </div> + <RetransmissionCodeLimitExpiration + expiration={currentRetransmission} + /> + </section> + <footer + class="modal-card-foot " + style={{ + justifyContent: "space-between", + border: "1px solid", + borderTop: 0, + }} + > + <button class="button" type="button" onClick={onCancel}> + <i18n.Translate>Cancel</i18n.Translate> + </button> + <ButtonBetterBulma + type="submit" + disabled={!tries.numTries} + onClick={verify} + > + <i18n.Translate>Verify</i18n.Translate> + </ButtonBetterBulma> + </footer> + </FormProvider> </div> </div> </Fragment> @@ -532,102 +523,97 @@ export function SolveMFAChallenges({ <div class="columns is-centered" style={{ margin: "auto" }}> <div class="column is-two-thirds "> - <div class="modal is-active" style={{ position: "initial" }}> - <div class="modal-card" style={{ width: "100%", margin: 0 }}> - <header - class="modal-card-head" - style={{ border: "1px solid", borderBottom: 0 }} - > - <p class="modal-card-title"> - <i18n.Translate> - Multi-factor authentication required. - </i18n.Translate> - </p> - </header> + <header + class="modal-card-head" + style={{ border: "1px solid", borderBottom: 0 }} + > + <p class="modal-card-title"> + <i18n.Translate> + Multi-factor authentication required. + </i18n.Translate> + </p> + </header> + <section + class="modal-card-body" + style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} + > + {currentChallenge.combi_and ? ( + <i18n.Translate> + You must complete all of these requirements. + </i18n.Translate> + ) : ( + <i18n.Translate> + You need to complete at least one of this requirements. + </i18n.Translate> + )} + </section> + {currentChallenge.challenges.map((challenge, idx) => { + const noNeedToComplete = + hasSolvedEnough || solved.indexOf(challenge.challenge_id) !== -1; + + const doSend = noNeedToComplete + ? sendMessage + : sendMessage.withArgs(challenge); + + return ( <section class="modal-card-body" - style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} - > - {currentChallenge.combi_and ? ( - <i18n.Translate> - You must complete all of these requirements. - </i18n.Translate> - ) : ( - <i18n.Translate> - You need to complete at least one of this requirements. - </i18n.Translate> - )} - </section> - {currentChallenge.challenges.map((challenge, idx) => { - const noNeedToComplete = - hasSolvedEnough || - solved.indexOf(challenge.challenge_id) !== -1; - - const doSend = noNeedToComplete - ? sendMessage - : sendMessage.withArgs(challenge); - - return ( - <section - class="modal-card-body" - style={{ - border: "1px solid", - borderTop: 0, - borderBottom: 0, - }} - > - {(function (ch: TanChannel): VNode { - switch (ch) { - case TanChannel.SMS: - return ( - <i18n.Translate> - An SMS to the phone number ending with{" "} - <span>{challenge.tan_info}</span> - </i18n.Translate> - ); - case TanChannel.EMAIL: - return ( - <i18n.Translate> - An email to the address starting with{" "} - <span>{challenge.tan_info}</span> - </i18n.Translate> - ); - } - })(challenge.tan_channel)} - - <div - style={{ - justifyContent: "space-between", - display: "flex", - }} - > - <div /> - <ButtonBetterBulma - type="button" - onClick={doSend} - focus={idx === 0 && focus} - > - <i18n.Translate>Continue</i18n.Translate> - </ButtonBetterBulma> - </div> - </section> - ); - })} - <footer - class="modal-card-foot " style={{ - justifyContent: "space-between", border: "1px solid", borderTop: 0, + borderBottom: 0, }} > - <button class="button" type="button" onClick={onCancel}> - <i18n.Translate>Cancel</i18n.Translate> - </button> - <div /> - </footer> - </div> - </div> + {(function (ch: TanChannel): VNode { + switch (ch) { + case TanChannel.SMS: + return ( + <i18n.Translate> + An SMS to the phone number ending with{" "} + <span>{challenge.tan_info}</span> + </i18n.Translate> + ); + case TanChannel.EMAIL: + return ( + <i18n.Translate> + An email to the address starting with{" "} + <span>{challenge.tan_info}</span> + </i18n.Translate> + ); + } + })(challenge.tan_channel)} + + <div + style={{ + justifyContent: "space-between", + display: "flex", + }} + > + <div /> + <ButtonBetterBulma + type="button" + onClick={doSend} + focus={idx === 0 && focus} + > + <i18n.Translate>Continue</i18n.Translate> + </ButtonBetterBulma> + </div> + </section> + ); + })} + <footer + class="modal-card-foot " + style={{ + justifyContent: "space-between", + border: "1px solid", + borderTop: 0, + }} + > + <button class="button" type="button" onClick={onCancel}> + <i18n.Translate>Cancel</i18n.Translate> + </button> + <div /> + </footer> </div> </div> </Fragment> diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx @@ -135,140 +135,136 @@ export function LoginPage({ showCreateAccount, focus }: Props): VNode { <div class="columns is-centered" style={{ margin: "auto" }}> <div class="column is-two-thirds "> - <div class="modal is-active" style={{ position: "initial" }}> - <div class="modal-card" style={{ width: "100%", margin: 0 }}> - <header - class="modal-card-head" - style={{ border: "1px solid", borderBottom: 0 }} - > - <p class="modal-card-title"> - <i18n.Translate>Login required</i18n.Translate> - </p> - </header> - <FormProvider> - <section - class="modal-card-body" - style={{ - borderRight: "1px solid", - borderLeft: "1px solid", - borderTop: 0, - borderBottom: 0, - overflow: "hidden", - }} - > - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"> - <i18n.Translate>Username</i18n.Translate> - <Tooltip text={i18n.str`Instance name.`}> - <i class="icon mdi mdi-information" /> - </Tooltip> - </label> - </div> - <div class="field-body"> - <div class="field"> - <p class="control is-expanded"> - <input - class="input" - type="text" - ref={focus ? doAutoFocus : undefined} - // placeholder={i18n.str`instance name`} - name="username" - autoComplete="username" - value={username} - onInput={(e): void => - setUsername(e?.currentTarget.value) - } - /> - </p> - </div> - </div> + <header + class="modal-card-head" + style={{ border: "1px solid", borderBottom: 0 }} + > + <p class="modal-card-title"> + <i18n.Translate>Login required</i18n.Translate> + </p> + </header> + <FormProvider> + <section + class="modal-card-body" + style={{ + borderRight: "1px solid", + borderLeft: "1px solid", + borderTop: 0, + borderBottom: 0, + overflow: "hidden", + }} + > + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <i18n.Translate>Username</i18n.Translate> + <Tooltip text={i18n.str`Instance name.`}> + <i class="icon mdi mdi-information" /> + </Tooltip> + </label> + </div> + <div class="field-body"> + <div class="field"> + <p class="control is-expanded"> + <input + class="input" + type="text" + ref={focus ? doAutoFocus : undefined} + // placeholder={i18n.str`instance name`} + name="username" + autoComplete="username" + value={username} + onInput={(e): void => + setUsername(e?.currentTarget.value) + } + /> + </p> </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"> - <i18n.Translate>Password</i18n.Translate> - <Tooltip text={i18n.str`Instance password.`}> - <i class="icon mdi mdi-information" /> - </Tooltip> - </label> - </div> - <div class="field-body"> - <div class="field has-addons"> - <p class="control is-expanded"> - <input - class="input" - type={hidePassword ? "password" : "text"} - // placeholder={i18n.str`current password`} - name="token" - autoComplete="current-password" - value={password} - onInput={(e): void => - setPassword(e?.currentTarget.value) - } - /> - </p> - <div - class="control" - style={{ cursor: "pointer" }} - onClick={() => { - setHidePassword((h) => !h); - }} - > - <a class="button is-static point"> - {hidePassword ? ( - <i class="icon mdi mdi-eye-off" /> - ) : ( - <i class="icon mdi mdi-eye" /> - )} - </a> - </div> - </div> + </div> + </div> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <i18n.Translate>Password</i18n.Translate> + <Tooltip text={i18n.str`Instance password.`}> + <i class="icon mdi mdi-information" /> + </Tooltip> + </label> + </div> + <div class="field-body"> + <div class="field has-addons"> + <p class="control is-expanded"> + <input + class="input" + type={hidePassword ? "password" : "text"} + // placeholder={i18n.str`current password`} + name="token" + autoComplete="current-password" + value={password} + onInput={(e): void => + setPassword(e?.currentTarget.value) + } + /> + </p> + <div + class="control" + style={{ cursor: "pointer" }} + onClick={() => { + setHidePassword((h) => !h); + }} + > + <a class="button is-static point"> + {hidePassword ? ( + <i class="icon mdi mdi-eye-off" /> + ) : ( + <i class="icon mdi mdi-eye" /> + )} + </a> </div> </div> - </section> - <footer - class="modal-card-foot " - style={{ - justifyContent: "space-between", - border: "1px solid", - borderTop: 0, - }} + </div> + </div> + </section> + <footer + class="modal-card-foot " + style={{ + justifyContent: "space-between", + border: "1px solid", + borderTop: 0, + }} + > + {!config.have_self_provisioning ? ( + <div /> + ) : ( + <a + href={ + !username || username === "admin" + ? undefined + : `#/account/reset/${username}` + } + class="button " + disabled={!username || username === "admin"} > - {!config.have_self_provisioning ? ( - <div /> - ) : ( - <a - href={ - !username || username === "admin" - ? undefined - : `#/account/reset/${username}` - } - class="button " - disabled={!username || username === "admin"} - > - <i18n.Translate>Forgot password</i18n.Translate> - </a> - )} - <ButtonBetterBulma onClick={login} type="submit"> - <i18n.Translate>Confirm</i18n.Translate> - </ButtonBetterBulma> - </footer> - </FormProvider> - </div> - </div> - {!showCreateAccount ? undefined : ( - <div style={{ marginTop: 8 }}> - <a href={"#/account/new"} class="has-icon"> - <i class="icon mdi mdi-account-plus" /> - <span class="menu-item-label"> - <i18n.Translate>Create new account</i18n.Translate> - </span> - </a> - </div> - )} + <i18n.Translate>Forgot password</i18n.Translate> + </a> + )} + <ButtonBetterBulma onClick={login} type="submit"> + <i18n.Translate>Confirm</i18n.Translate> + </ButtonBetterBulma> + </footer> + </FormProvider> </div> </div> + {!showCreateAccount ? undefined : ( + <div style={{ marginTop: 8 }}> + <a href={"#/account/new"} class="has-icon"> + <i class="icon mdi mdi-account-plus" /> + <span class="menu-item-label"> + <i18n.Translate>Create new account</i18n.Translate> + </span> + </a> + </div> + )} </div> ); } diff --git a/packages/merchant-backoffice-ui/src/paths/newAccount/index.tsx b/packages/merchant-backoffice-ui/src/paths/newAccount/index.tsx @@ -255,119 +255,112 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode { <LocalNotificationBannerBulma notification={notification} /> <div class="columns is-centered" style={{ margin: "auto" }}> <div class="column is-two-thirds "> - <div class="modal is-active" style={{ position: "initial" }}> - <div - class="modal-card" - style={{ width: "100%", height: "100%", margin: 0 }} + <FormProvider<Account> + name="settings" + errors={errors} + object={value} + valueHandler={valueHandler} + > + <header + class="modal-card-head" + style={{ border: "1px solid", borderBottom: 0 }} > - <FormProvider<Account> - name="settings" - errors={errors} - object={value} - valueHandler={valueHandler} - > - <header - class="modal-card-head" - style={{ border: "1px solid", borderBottom: 0 }} - > - <p class="modal-card-title"> - <i18n.Translate>Self provision</i18n.Translate> - </p> - </header> - <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 - /> + <p class="modal-card-title"> + <i18n.Translate>Self provision</i18n.Translate> + </p> + </header> + <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> - name="name" - label={i18n.str`Business name`} - tooltip={i18n.str`Legal name of the business represented by this instance.`} - autoComplete="organization" - /> - <InputPassword<Account> - expand - label={i18n.str`New password`} - tooltip={i18n.str`Next password to be used`} - name="password" - autoComplete="new-password" - /> - <InputPassword<Account> - label={i18n.str`Repeat password`} - tooltip={i18n.str`Confirm the same password`} - name="repeat" - expand - autoComplete="new-password" - /> - {serverRequiresEmail ? ( - <Input<Account> - label={i18n.str`Email`} - tooltip={i18n.str`Contact email`} - name="email" - inputType="email" - autoComplete="email" - /> - ) : undefined} - {serverRequiresSms ? ( - <Input<Account> - label={i18n.str`Phone`} - tooltip={i18n.str`Contact phone number`} - name="phone" - inputType="tel" - autoComplete="tel" - /> - ) : undefined} - <InputToggle - label={i18n.str`Accept the Terms of service`} - name="tos" - help={ - <i18n.Translate> - I understand and agree to the{" "} - <a - href="/terms" - target="_blank" - tabIndex={-1} - referrerpolicy="no-referrer" - > - <i18n.Translate>Terms of service</i18n.Translate> - </a> - </i18n.Translate> - } - tooltip={i18n.str`You must accept the Terms of service to continue.`} - /> - </section> - <footer - class="modal-card-foot " - style={{ - justifyContent: "space-between", - border: "1px solid", - borderTop: 0, - }} - > - <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> + <Input<Account> + name="name" + label={i18n.str`Business name`} + tooltip={i18n.str`Legal name of the business represented by this instance.`} + autoComplete="organization" + /> + <InputPassword<Account> + expand + label={i18n.str`New password`} + tooltip={i18n.str`Next password to be used`} + name="password" + autoComplete="new-password" + /> + <InputPassword<Account> + label={i18n.str`Repeat password`} + tooltip={i18n.str`Confirm the same password`} + name="repeat" + expand + autoComplete="new-password" + /> + {serverRequiresEmail ? ( + <Input<Account> + label={i18n.str`Email`} + tooltip={i18n.str`Contact email`} + name="email" + inputType="email" + autoComplete="email" + /> + ) : undefined} + {serverRequiresSms ? ( + <Input<Account> + label={i18n.str`Phone`} + tooltip={i18n.str`Contact phone number`} + name="phone" + inputType="tel" + autoComplete="tel" + /> + ) : undefined} + <InputToggle + label={i18n.str`Accept the Terms of service`} + name="tos" + help={ + <i18n.Translate> + I understand and agree to the{" "} + <a + href="/terms" + target="_blank" + tabIndex={-1} + referrerpolicy="no-referrer" + > + <i18n.Translate>Terms of service</i18n.Translate> + </a> + </i18n.Translate> + } + tooltip={i18n.str`You must accept the Terms of service to continue.`} + /> + </section> + <footer + class="modal-card-foot " + style={{ + justifyContent: "space-between", + border: "1px solid", + borderTop: 0, + }} + > + <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> </Fragment> diff --git a/packages/merchant-backoffice-ui/src/paths/resetAccount/index.tsx b/packages/merchant-backoffice-ui/src/paths/resetAccount/index.tsx @@ -144,59 +144,55 @@ export function ResetAccount({ <div class="columns is-centered" style={{ margin: "auto" }}> <div class="column is-two-thirds "> - <div class="modal is-active" style={{ position: "initial" }}> - <div class="modal-card" style={{ width: "100%", margin: 0 }}> - <header - class="modal-card-head" - style={{ border: "1px solid", borderBottom: 0 }} - > - <p class="modal-card-title"> - <i18n.Translate> - Resetting access to the instance "{instanceId}" - </i18n.Translate> - </p> - </header> - <FormProvider<Form> - name="settings" - errors={errors} - object={value} - valueHandler={valueHandler} - > - <section - class="modal-card-body" - style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} - > - <InputPassword<Form> - label={i18n.str`New password`} - name="password" - expand - autoComplete="new-password" - /> - <InputPassword<Form> - label={i18n.str`Repeat password`} - name="repeat" - expand - autoComplete="new-password" - /> - </section> - <footer - class="modal-card-foot " - style={{ - justifyContent: "space-between", - border: "1px solid", - borderTop: 0, - }} - > - <button type="button" class="button" onClick={onCancel}> - <i18n.Translate>Cancel</i18n.Translate> - </button> - <ButtonBetterBulma type="submit" onClick={reset}> - <i18n.Translate>Reset</i18n.Translate> - </ButtonBetterBulma> - </footer> - </FormProvider> - </div> - </div> + <header + class="modal-card-head" + style={{ border: "1px solid", borderBottom: 0 }} + > + <p class="modal-card-title"> + <i18n.Translate> + Resetting access to the instance "{instanceId}" + </i18n.Translate> + </p> + </header> + <FormProvider<Form> + name="settings" + errors={errors} + object={value} + valueHandler={valueHandler} + > + <section + class="modal-card-body" + style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }} + > + <InputPassword<Form> + label={i18n.str`New password`} + name="password" + expand + autoComplete="new-password" + /> + <InputPassword<Form> + label={i18n.str`Repeat password`} + name="repeat" + expand + autoComplete="new-password" + /> + </section> + <footer + class="modal-card-foot " + style={{ + justifyContent: "space-between", + border: "1px solid", + borderTop: 0, + }} + > + <button type="button" class="button" onClick={onCancel}> + <i18n.Translate>Cancel</i18n.Translate> + </button> + <ButtonBetterBulma type="submit" onClick={reset}> + <i18n.Translate>Reset</i18n.Translate> + </ButtonBetterBulma> + </footer> + </FormProvider> </div> </div> </Fragment>