diff options
author | Sebastian <sebasjm@gmail.com> | 2024-08-19 13:35:18 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2024-08-19 13:35:34 -0300 |
commit | 4755837dcb817e100657aa1e7d189a8358166f36 (patch) | |
tree | 4be14581525d18e345481d369c59695d471b900a /packages/kyc-ui | |
parent | 093bd7c9b69ccbddfa5d651640ed2da6953ab7f5 (diff) |
start ui
Diffstat (limited to 'packages/kyc-ui')
-rw-r--r-- | packages/kyc-ui/src/Routing.tsx | 12 | ||||
-rw-r--r-- | packages/kyc-ui/src/pages/Start.tsx | 325 |
2 files changed, 211 insertions, 126 deletions
diff --git a/packages/kyc-ui/src/Routing.tsx b/packages/kyc-ui/src/Routing.tsx index 54bec1404..101d83d0b 100644 --- a/packages/kyc-ui/src/Routing.tsx +++ b/packages/kyc-ui/src/Routing.tsx @@ -21,7 +21,7 @@ import { } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { assertUnreachable } from "@gnu-taler/taler-util"; +import { AccessToken, assertUnreachable } from "@gnu-taler/taler-util"; import { useErrorBoundary } from "preact/hooks"; import { CallengeCompleted } from "./pages/CallengeCompleted.js"; import { Frame } from "./pages/Frame.js"; @@ -74,16 +74,10 @@ function PublicRounting(): VNode { return <div>not found</div> } case "start": { - const secret = safeGetParam(location.params, "secret"); - const redirectURL = safeToURL( - safeGetParam(location.params, "redirect_uri"), - ); - return ( <Start - clientId={location.values.token} - secret={secret} - redirectURL={redirectURL} + //FIX: validate token + token={location.values.token as AccessToken} onCreated={() => { navigateTo(publicPages.completed.url({})); }} diff --git a/packages/kyc-ui/src/pages/Start.tsx b/packages/kyc-ui/src/pages/Start.tsx index 2b97641e0..ec7e37e00 100644 --- a/packages/kyc-ui/src/pages/Start.tsx +++ b/packages/kyc-ui/src/pages/Start.tsx @@ -14,13 +14,21 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ import { + AccessToken, HttpStatusCode, + KycBuiltInFromId, + KycRequirementInformation, + KycRequirementInformationId, + TalerError, + assertUnreachable, createRFC8959AccessTokenEncoded, encodeCrock, randomBytes } from "@gnu-taler/taler-util"; import { + Attention, Button, + Loading, LocalNotificationBanner, ShowInputErrorLabel, useExchangeApiContext, @@ -31,69 +39,128 @@ import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { safeToURL } from "../Routing.js"; import { useSessionState } from "../hooks/session.js"; +import { useKycInfo } from "../hooks/kyc.js"; type Props = { - clientId: string; - secret: string | undefined; - redirectURL: URL | undefined; + token: AccessToken; onCreated: () => void; focus?: boolean; }; export function Start({ - clientId, - secret, - redirectURL, + token, focus, onCreated, }: Props): VNode { const { i18n } = useTranslationContext(); const [notification, withErrorHandler] = useLocalNotificationHandler(); const { lib } = useExchangeApiContext(); - const { start } = useSessionState(); - const [password, setPassword] = useState<string | undefined>(secret); - const [url, setUrl] = useState<string | undefined>(redirectURL?.href); + const { state, start } = useSessionState(); + const result = useKycInfo({ accessToken: token }) + + if (!result) { + return <Loading />; + } + if (result instanceof TalerError) { + return <pre>{JSON.stringify(result, undefined, 2)}</pre>; + } + + if (result.type === "fail") { + switch (result.case) { + case HttpStatusCode.NotModified: { + return <div> not modified </div>; + } + case HttpStatusCode.NoContent: { + return <div> not requirements </div>; + } + default: { + assertUnreachable(result); + } + } + } const errors = undefinedIfEmpty({ - password: !password ? i18n.str`required` : undefined, - url: !url - ? i18n.str`required` - : !safeToURL(url) - ? i18n.str`invalid format` - : undefined, + // password: !password ? i18n.str`required` : undefined, + // url: !url + // ? i18n.str`required` + // : !safeToURL(url) + // ? i18n.str`invalid format` + // : undefined, }); const onStart = - !!errors || password === undefined || url === undefined + !!errors ? undefined : withErrorHandler( - async () => { - return { - type: "ok", - body: {}, - } - // return lib.exchange.uploadKycForm( - // "clientId", - // createRFC8959AccessTokenEncoded(password), - // ); - }, - (ok) => { - // start({ - // nonce: ok.body.nonce, - // clientId, - // redirectURL: url, - // state: encodeCrock(randomBytes(32)), - // }); - - onCreated(); - }, - // () => { - // // switch (fail.case) { - // // case HttpStatusCode.NotFound: - // // return i18n.str`Client doesn't exist.`; - // // } - // }, - ); + async () => { + return { + type: "ok", + body: {}, + } + // return lib.exchange.uploadKycForm( + // "clientId", + // createRFC8959AccessTokenEncoded(password), + // ); + }, + (ok) => { + // start({ + // nonce: ok.body.nonce, + // clientId, + // redirectURL: url, + // state: encodeCrock(randomBytes(32)), + // }); + + onCreated(); + }, + // () => { + // // switch (fail.case) { + // // case HttpStatusCode.NotFound: + // // return i18n.str`Client doesn't exist.`; + // // } + // }, + ); + + const requirements: typeof result.body.requirements = [{ + description: "this is the form description, click to show the form field bla bla bla", + form: "asdasd" as KycBuiltInFromId, + description_i18n: {}, + id: "ASDASD" as KycRequirementInformationId, + }, { + description: "this is the description of the link and service provider.", + form: "LINK", + description_i18n: {}, + id: "ASDASD" as KycRequirementInformationId, + }, { + description: "you can't click this becuase this is only information, wait until AML officer replies.", + form: "INFO", + description_i18n: {}, + id: "ASDASD" as KycRequirementInformationId, + }] + if (!result.body.requirements.length) { + return <Fragment> + <LocalNotificationBanner notification={notification} /> + <div class="isolate bg-white px-6 py-12"> + <div class="mx-auto max-w-2xl text-center"> + <h2 class="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl"> + <i18n.Translate> + No requirements for this account + </i18n.Translate> + </h2> + </div> + <div class="m-8"> + <Attention + title={i18n.str`Kyc completed`} + type="success" + > + <i18n.Translate> + You can close this now + </i18n.Translate> + </Attention> + </div> + </div> + + </Fragment> + } return ( <Fragment> <LocalNotificationBanner notification={notification} /> @@ -102,91 +169,115 @@ export function Start({ <div class="mx-auto max-w-2xl text-center"> <h2 class="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl"> <i18n.Translate> - Setup new challenge with client ID: "{clientId}" + Complete any of the following requirements </i18n.Translate> </h2> </div> - <form - method="POST" - class="mx-auto mt-4 max-w-xl sm:mt-20" - onSubmit={(e) => { - e.preventDefault(); - }} - > - <div class="sm:col-span-2"> - <label - for="email" - class="block text-sm font-semibold leading-6 text-gray-900" - > - <i18n.Translate>Password</i18n.Translate> - </label> - <div class="mt-2.5"> - <input - type="password" - name="password" - id="password" - ref={focus ? doAutoFocus : undefined} - maxLength={512} - autocomplete="password" - value={password} - onChange={(e) => { - setPassword(e.currentTarget.value); - }} - readOnly={secret !== undefined} - class="block w-full read-only:bg-slate-200 rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> - <ShowInputErrorLabel - message={errors?.password} - isDirty={password !== undefined} - /> - </div> - </div> - - <div class="sm:col-span-2"> - <label - for="email" - class="block text-sm font-semibold leading-6 text-gray-900" - > - <i18n.Translate>Redirect URL</i18n.Translate> - </label> - <div class="mt-2.5"> - <input - type="text" - name="redirect_url" - id="redirect_url" - maxLength={512} - autocomplete="redirect_url" - value={url} - onChange={(e) => { - setUrl(e.currentTarget.value); - }} - readOnly={redirectURL !== undefined} - class="block w-full read-only:bg-slate-200 rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - /> - <ShowInputErrorLabel - message={errors?.url} - isDirty={url !== undefined} - /> - </div> - </div> - </form> - <div class="mt-10"> - <Button - type="submit" - // disabled={!onStart} - class="block w-full disabled:bg-gray-300 rounded-md bg-indigo-600 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - handler={onStart} - > - <i18n.Translate>Start</i18n.Translate> - </Button> + <div class="mt-8"> + <ul role="list" class=" divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl"> + {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} /> + </li> + })} + </ul> </div> + </div> </Fragment> ); } +function RequirementRow({ requirement: req }: { requirement: KycRequirementInformation }): VNode { + const { i18n } = useTranslationContext() + switch (req.form) { + case "INFO": { + 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="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" /> + </svg> + </div> + <div class="min-w-0 flex-auto"> + <p class="text-sm font-semibold leading-6 text-gray-900"> + <span class="absolute inset-x-0 -top-px bottom-0"></span> + <i18n.Translate>Information</i18n.Translate> + </p> + <p class="mt-1 flex text-xs leading-5 text-gray-500"> + {req.description} + </p> + </div> + </div> + </Fragment> + } + 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"> + <a href="#"> + <span class="absolute inset-x-0 -top-px bottom-0"></span> + <i18n.Translate>Link</i18n.Translate> + </a> + </p> + <p class="mt-1 flex text-xs leading-5 text-gray-500"> + {req.description} + </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">Open link</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> + } + default: { + 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="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" /> + </svg> + </div> + <div class="min-w-0 flex-auto"> + <p class="text-sm font-semibold leading-6 text-gray-900"> + <a href="#"> + <span class="absolute inset-x-0 -top-px bottom-0"></span> + <i18n.Translate>Form</i18n.Translate> + </a> + </p> + <p class="mt-1 flex text-xs leading-5 text-gray-500"> + {req.description} + </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">Fill form</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> + } + } +} + /** * Show the element when the load ended * @param element |