taler-typescript-core

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

commit 64e124ee48ccfd694d6a3ffb885be1692c9ea9a1
parent 0aee1fcbf037c2c9c202e11a05a99eba00dd0e54
Author: Sebastian <sebasjm@gmail.com>
Date:   Wed, 29 Oct 2025 16:40:11 -0300

pwd and kyc

Diffstat:
Mpackages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx | 32++++++++++++++++++++++++++++++--
Mpackages/merchant-backoffice-ui/src/paths/instance/password/DetailPage.tsx | 25++++++++++++++++---------
Mpackages/merchant-backoffice-ui/src/paths/instance/password/index.tsx | 75++++++++++++++++++++++++++++++---------------------------------------------
3 files changed, 76 insertions(+), 56 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx @@ -21,6 +21,7 @@ import { HttpStatusCode, + Paytos, TalerError, TalerMerchantApi, assertUnreachable, @@ -124,10 +125,37 @@ function ShowInstructionForKycRedirect({ const { i18n } = useTranslationContext(); switch (e.status) { case TalerMerchantApi.MerchantAccountKycStatus.KYC_WIRE_REQUIRED: + const uri = Paytos.fromString(e.payto_uri); + if (uri.type === "fail") { + return ( + <ConfirmModal + label={i18n.str`Ok`} + description={i18n.str`Wire account is invalid`} + active + onCancel={onCancel} + > + <p style={{ paddingTop: 0 }}> + <i18n.Translate> + The backend service responded with "{e.payto_uri}" which is invalid. + </i18n.Translate> + </p> + </ConfirmModal> + ); + } + const tgs = (e.payto_kycauths ?? []) + .map((d) => Paytos.fromString(d)) + .filter((d) => { + if (d.type !== "ok") { + // FIXME: maybe this need to be shown to the user more promptly + console.error("server replied with a wrong payto://", d) + } + return d.type === "ok" + }) + .map((d) => d.body); return ( <ValidBankAccount - origin={parsePaytoUri(e.payto_uri)!} - targets={(e.payto_kycauths ?? []).map((d) => parsePaytoUri(d)!)} + origin={uri.body} + targets={tgs} onCancel={onCancel} /> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/password/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/password/DetailPage.tsx @@ -19,24 +19,31 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ButtonBetterBulma, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { + ButtonBetterBulma, + LocalNotificationBannerBulma, + SafeHandlerTemplate, + useChallengeHandler, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { FormProvider } from "../../../components/form/FormProvider.js"; import { Input } from "../../../components/form/Input.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; +import { SolveMFAChallenges } from "../../../components/SolveMFA.js"; +import { HttpStatusCode } from "@gnu-taler/taler-util"; interface Props { instanceId: string; - onNewPassword: (s: string) => void; - // onNewPassword: (c: string | undefined, s: string) => void; onBack?: () => void; + changePassword: SafeHandlerTemplate<[pass:string], any>; } export function DetailPage({ instanceId, onBack, - onNewPassword, + changePassword, }: Props): VNode { type State = { // old_token: string; @@ -48,6 +55,7 @@ export function DetailPage({ new_token: "", repeat_token: "", }); + const { i18n } = useTranslationContext(); const errors = undefinedIfEmpty({ @@ -57,9 +65,9 @@ export function DetailPage({ // : undefined, new_token: !form.new_token ? i18n.str`Required` - // : form.new_token === form.old_token - // ? i18n.str`Can't be the same as the old password` - : undefined, + : // : form.new_token === form.old_token + // ? i18n.str`Can't be the same as the old password` + undefined, repeat_token: form.new_token !== form.repeat_token ? i18n.str`Is not the same` @@ -123,13 +131,12 @@ export function DetailPage({ )} <ButtonBetterBulma type="submit" - disabled={hasErrors} data-tooltip={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={submitForm} + onClick={changePassword} > <i18n.Translate>Confirm change</i18n.Translate> </ButtonBetterBulma> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/password/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/password/index.tsx @@ -18,7 +18,7 @@ import { HttpStatusCode, MerchantAuthMethod, TalerError, - assertUnreachable + assertUnreachable, } from "@gnu-taler/taler-util"; import { LocalNotificationBannerBulma, @@ -35,9 +35,7 @@ import { useInstanceDetails, useManagedInstanceDetails, } from "../../../hooks/instance.js"; -import { - LoginPage -} from "../../login/index.js"; +import { LoginPage } from "../../login/index.js"; import { NotFoundPageOrAdminCreate } from "../../notfound/index.js"; import { DetailPage } from "./DetailPage.js"; @@ -56,11 +54,24 @@ export default function PasswordPage({ onCancel, onChange }: Props): VNode { if (result instanceof TalerError) { return <ErrorLoadingMerchant error={result} />; } + if (result.type === "fail") { + switch (result.case) { + case HttpStatusCode.Unauthorized: { + return <LoginPage />; + } + case HttpStatusCode.NotFound: { + return <NotFoundPageOrAdminCreate />; + } + default: { + assertUnreachable(result); + } + } + } const { i18n } = useTranslationContext(); const mfa = useChallengeHandler(); const changePassword = safeFunctionHandler( - (token: AccessToken, id: string, pwd: string, challengeIds: string[]) => + (token: AccessToken, pwd: string, challengeIds: string[]) => lib.instance.updateCurrentInstanceAuthentication( token, { @@ -69,6 +80,7 @@ export default function PasswordPage({ onCancel, onChange }: Props): VNode { }, { challengeIds }, ), + !session.token ? undefined : [session.token, "", []], ); changePassword.onSuccess = (suc) => { onChange(); @@ -88,8 +100,7 @@ export default function PasswordPage({ onCancel, onChange }: Props): VNode { const retry = changePassword.lambda((ids: string[]) => [ changePassword.args![0], - changePassword.args![2], - changePassword.args![2], + changePassword.args![1], ids, ]); if (mfa.pendingChallenge) { @@ -101,38 +112,18 @@ export default function PasswordPage({ onCancel, onChange }: Props): VNode { /> ); } - if (result.type === "fail") { - switch (result.case) { - case HttpStatusCode.Unauthorized: { - return <LoginPage />; - } - case HttpStatusCode.NotFound: { - return <NotFoundPageOrAdminCreate />; - } - default: { - assertUnreachable(result); - } - } - } - // + return ( <Fragment> <LocalNotificationBannerBulma notification={notification} /> <DetailPage onBack={onCancel} instanceId={result.body.name} - onNewPassword={async (newPassword): Promise<void> => { - return ( - !session.token - ? changePassword - : changePassword.withArgs( - session.token, - instanceId, - newPassword, - [], - ) - ).call(); - }} + changePassword={changePassword.lambda((pass: string) => [ + changePassword.args![0], + pass, + changePassword.args![2], + ])} /> </Fragment> ); @@ -218,18 +209,12 @@ export function AdminPassword({ <DetailPage onBack={onCancel} instanceId={result.body.name} - onNewPassword={async (newPassword): Promise<void> => { - return ( - !session.token - ? changePassword - : changePassword.withArgs( - session.token, - instanceId, - newPassword, - [], - ) - ).call(); - }} + changePassword={changePassword.lambda((pass: string) => [ + changePassword.args![0], + changePassword.args![1], + pass, + changePassword.args![3], + ])} /> </Fragment> );