aboutsummaryrefslogtreecommitdiff
path: root/packages/kyc-ui
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-08-19 13:35:18 -0300
committerSebastian <sebasjm@gmail.com>2024-08-19 13:35:34 -0300
commit4755837dcb817e100657aa1e7d189a8358166f36 (patch)
tree4be14581525d18e345481d369c59695d471b900a /packages/kyc-ui
parent093bd7c9b69ccbddfa5d651640ed2da6953ab7f5 (diff)
start ui
Diffstat (limited to 'packages/kyc-ui')
-rw-r--r--packages/kyc-ui/src/Routing.tsx12
-rw-r--r--packages/kyc-ui/src/pages/Start.tsx325
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: &quot;{clientId}&quot;
+ 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