taler-typescript-core

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

commit 884733943159794b61549dea53e8fcea671403d1
parent ee1c914f9ff1326c588cbaea257f85a2fca4ba78
Author: Sebastian <sebasjm@gmail.com>
Date:   Wed, 21 May 2025 15:40:18 -0300

fix #9992

Diffstat:
Mpackages/kyc-ui/src/pages/Start.tsx | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 177 insertions(+), 85 deletions(-)

diff --git a/packages/kyc-ui/src/pages/Start.tsx b/packages/kyc-ui/src/pages/Start.tsx @@ -34,6 +34,7 @@ import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js"; import { useKycInfo } from "../hooks/kyc.js"; import { FillForm } from "./FillForm.js"; import { useSessionState } from "../hooks/session.js"; +import { AbsoluteTime } from "@gnu-taler/taler-util"; const TALER_SCREEN_ID = 104; @@ -41,7 +42,7 @@ type Props = { token: AccessToken; }; -export function ShowReqList({ +function ShowReqList({ token, onFormSelected, }: { @@ -120,17 +121,12 @@ export function ShowReqList({ > {requirements.map((req, idx) => { return ( - <li - key={idx} - class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6" - > - <RequirementRow - requirement={req} - onFormSelected={() => { - onFormSelected(req); - }} - /> - </li> + <RequirementRow + requirement={req} + onFormSelected={() => { + onFormSelected(req); + }} + /> ); })} </ul> @@ -141,31 +137,14 @@ export function ShowReqList({ } export function Start({ token }: Props): VNode { const [req, setReq] = useState<KycRequirementInformation>(); - const { lib } = useExchangeApiContext(); - const [notification, withErrorHandler] = useLocalNotificationHandler(); - const { state, start } = useSessionState(); if (!req) { return ( <Fragment> - <LocalNotificationBanner notification={notification} /> - <ShowReqList token={token} onFormSelected={async (r) => { const reqId = r.id; if (r.form === "LINK" && reqId) { - const action = withErrorHandler( - async () => { - return lib.exchange.startExternalKycProcess(reqId); - }, - (res) => { - if (state) { - start(state.accessToken) - } - window.open(res.body.redirect_url, "_blank"); - }, - ); - await action.onClick(); } else { setReq(r); } @@ -176,7 +155,6 @@ export function Start({ token }: Props): VNode { } return ( <Fragment> - <LocalNotificationBanner notification={notification} /> <FillForm formId={req.form} requirement={req} @@ -189,6 +167,164 @@ export function Start({ token }: Props): VNode { ); } +function LinkGenerator({ req }: { req: KycRequirementInformation }): VNode { + const { i18n } = useTranslationContext(); + + const [notification, , notify] = useLocalNotificationHandler(); + const { lib } = useExchangeApiContext(); + + const [redirectUrl, setRedirectUrl] = useState<string>(); + const row = ( + <Fragment> + <div class="flex min-w-0 gap-x-4"> + <LocalNotificationBanner notification={notification} /> + <div class="inline-block h-10 w-10 rounded-full"> + {!redirectUrl ? ( + <svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="size-6" + > + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" + /> + </svg> + ) : ( + <svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="size-6" + > + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" + /> + </svg> + )} + </div> + <div class="min-w-0 flex-auto"> + {redirectUrl ? ( + <p class="text-sm font-semibold leading-6 text-gray-900"> + <span class="absolute inset-x-0 -top-px bottom-0"></span> + <i18n.Translate>{req.description}</i18n.Translate> + </p> + ) : ( + // href={redirectUrl} + <p class="text-sm font-semibold leading-6 text-gray-900"> + <button + onClick={async () => { + const res = await lib.exchange.startExternalKycProcess( + req.id!, + ); + if (res.type === "ok") { + setRedirectUrl(res.body.redirect_url); + notify({ + type: "info", + title: i18n.str`Link generated, you can proceed.`, + when: AbsoluteTime.now(), + }); + } else { + switch (res.case) { + case HttpStatusCode.NotFound: { + notify({ + type: "error", + title: i18n.str`could not create redirect url`, + description: i18n.str`not found`, + when: AbsoluteTime.now(), + }); + break; + } + case HttpStatusCode.Conflict: { + notify({ + type: "error", + title: i18n.str`could not create redirect url`, + description: i18n.str`conflict`, + when: AbsoluteTime.now(), + }); + break; + } + case HttpStatusCode.PayloadTooLarge: { + notify({ + type: "error", + title: i18n.str`could not create redirect url`, + description: i18n.str`payload too large`, + when: AbsoluteTime.now(), + }); + break; + } + } + } + }} + > + <span class="absolute inset-x-0 -top-px bottom-0"></span> + <i18n.Translate context="KYC_REQUIREMENT_LINK_DESCRIPTION"> + {req.description} + </i18n.Translate> + </button> + </p> + )} + </div> + </div> + <div class="flex shrink-0 items-center gap-x-4"> + <div class="hidden sm:flex sm:flex-col sm:items-end"> + {!redirectUrl ? ( + <p class="text-sm leading-6 text-gray-900"> + <i18n.Translate>Generate link</i18n.Translate> + </p> + ) : ( + <p class="text-sm leading-6 text-gray-900"> + <i18n.Translate>Open link</i18n.Translate> + </p> + )} + </div> + <svg + class="h-5 w-5 flex-none text-gray-400" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + <path + fill-rule="evenodd" + d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" + clip-rule="evenodd" + /> + </svg> + </div> + </Fragment> + ); + if (redirectUrl) { + return ( + <li key={req.id} class="hover:bg-gray-50 sm:px-6 px-4 py-5"> + <a + href={redirectUrl} + target="_blank" + rel="noreferrer" + class="relative flex justify-between gap-x-6 " + > + {row} + </a> + </li> + ); + } + return ( + <li + key={req.id} + class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6" + > + {row} + </li> + ); +} + function RequirementRow({ requirement: req, onFormSelected, @@ -201,7 +337,10 @@ function RequirementRow({ switch (req.form) { case "INFO": { return ( - <Fragment> + <li + key={req.id} + class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6" + > <div class="flex min-w-0 gap-x-4"> <div class="inline-block h-10 w-10 rounded-full"> <svg @@ -231,65 +370,18 @@ function RequirementRow({ </p> */} </div> </div> - </Fragment> + </li> ); } case "LINK": { - return ( - <Fragment> - <div class="flex min-w-0 gap-x-4"> - <div class="inline-block h-10 w-10 rounded-full"> - <svg - xmlns="http://www.w3.org/2000/svg" - fill="none" - viewBox="0 0 24 24" - stroke-width="1.5" - stroke="currentColor" - class="size-6" - > - <path - stroke-linecap="round" - stroke-linejoin="round" - d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" - /> - </svg> - </div> - <div class="min-w-0 flex-auto"> - <p class="text-sm font-semibold leading-6 text-gray-900"> - <button onClick={onFormSelected}> - <span class="absolute inset-x-0 -top-px bottom-0"></span> - <i18n.Translate context="KYC_REQUIREMENT_LINK_DESCRIPTION"> - {req.description} - </i18n.Translate> - </button> - </p> - </div> - </div> - <div class="flex shrink-0 items-center gap-x-4"> - <div class="hidden sm:flex sm:flex-col sm:items-end"> - <p class="text-sm leading-6 text-gray-900"> - <i18n.Translate>Start</i18n.Translate> - </p> - </div> - <svg - class="h-5 w-5 flex-none text-gray-400" - viewBox="0 0 20 20" - fill="currentColor" - aria-hidden="true" - > - <path - fill-rule="evenodd" - d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" - clip-rule="evenodd" - /> - </svg> - </div> - </Fragment> - ); + return <LinkGenerator key={req.id} req={req} />; } default: { return ( - <Fragment> + <li + key={req.id} + class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6" + > <div class="flex min-w-0 gap-x-4"> <div class="inline-block h-10 w-10 rounded-full"> <svg @@ -338,7 +430,7 @@ function RequirementRow({ /> </svg> </div> - </Fragment> + </li> ); } }