aboutsummaryrefslogtreecommitdiff
path: root/packages/exchange-backoffice-ui/src/pages/Officer.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/exchange-backoffice-ui/src/pages/Officer.tsx')
-rw-r--r--packages/exchange-backoffice-ui/src/pages/Officer.tsx247
1 files changed, 10 insertions, 237 deletions
diff --git a/packages/exchange-backoffice-ui/src/pages/Officer.tsx b/packages/exchange-backoffice-ui/src/pages/Officer.tsx
index 40ec33018..5320369e4 100644
--- a/packages/exchange-backoffice-ui/src/pages/Officer.tsx
+++ b/packages/exchange-backoffice-ui/src/pages/Officer.tsx
@@ -1,83 +1,11 @@
-import {
- AbsoluteTime,
- Codec,
- TranslatedString,
- buildCodecForObject,
- codecForAbsoluteTime,
- codecForString,
-} from "@gnu-taler/taler-util";
-import {
- notifyError,
- notifyInfo,
- useLocalStorage,
- useMemoryStorage,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import {
- Account,
- LockedAccount,
- UnwrapKeyError,
- createNewAccount,
- unlockAccount,
-} from "../account.js";
-import { createNewForm } from "../handlers/forms.js";
-
-export interface Officer {
- account: LockedAccount;
- when: AbsoluteTime;
-}
-
-const codecForLockedAccount = codecForString() as Codec<LockedAccount>;
-
-export const codecForOfficer = (): Codec<Officer> =>
- buildCodecForObject<Officer>()
- .property("account", codecForLockedAccount) // FIXME
- .property("when", codecForAbsoluteTime) // FIXME
- .build("Officer");
+import { Fragment, h } from "preact";
+import { useOfficer } from "../hooks/useOfficer.js";
+import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
export function Officer() {
- const password = useMemoryStorage("password");
- const officer = useLocalStorage("officer", {
- codec: codecForOfficer(),
- });
- const [keys, setKeys] = useState<Account>();
-
- useEffect(() => {
- if (officer.value === undefined || password.value === undefined) {
- return;
- }
-
- unlockAccount(officer.value.account, password.value)
- .then((keys) => setKeys(keys ?? { accountId: "", pub: "" }))
- .catch((e) => {
- if (e instanceof UnwrapKeyError) {
- console.log(e);
- }
- });
- }, [officer.value, password.value]);
-
- if (officer.value === undefined || !officer.value.account) {
- return (
- <CreateAccount
- onNewAccount={(account, pwd) => {
- password.update(pwd);
- officer.update({ account, when: AbsoluteTime.now() });
- }}
- />
- );
- }
-
- if (password.value === undefined) {
- return (
- <UnlockAccount
- lockedAccount={officer.value.account}
- onAccountUnlocked={(pwd) => {
- password.update(pwd);
- }}
- />
- );
+ const officer = useOfficer();
+ if (officer.state !== "ready") {
+ return <HandleAccountNotReady officer={officer} />;
}
return (
@@ -86,12 +14,12 @@ export function Officer() {
Public key
</h1>
<div class="max-w-xl text-base leading-7 text-gray-700 lg:max-w-lg">
- <p class="mt-6 font-mono break-all">{keys?.accountId}</p>
+ <p class="mt-6 font-mono break-all">{officer.account.accountId}</p>
</div>
<p>
<a
href={`mailto:aml@exchange.taler.net?body=${encodeURIComponent(
- `I want my AML account\n\n\nPubKey: ${keys?.accountId}`,
+ `I want my AML account\n\n\nPubKey: ${officer.account.accountId}`,
)}`}
target="_blank"
rel="noreferrer"
@@ -104,7 +32,7 @@ export function Officer() {
<button
type="button"
onClick={() => {
- password.reset();
+ officer.lock();
}}
class="m-4 block rounded-md border-0 bg-gray-200 px-3 py-2 text-center text-sm text-black shadow-sm "
>
@@ -115,7 +43,7 @@ export function Officer() {
<button
type="button"
onClick={() => {
- officer.reset();
+ officer.forget();
}}
class="m-4 block rounded-md bg-red-600 px-3 py-2 text-center text-sm text-white shadow-sm hover:bg-red-500 "
>
@@ -125,158 +53,3 @@ export function Officer() {
</div>
);
}
-
-function CreateAccount({
- onNewAccount,
-}: {
- onNewAccount: (account: LockedAccount, password: string) => void;
-}): VNode {
- const { i18n } = useTranslationContext();
- const Form = createNewForm<{
- password: string;
- repeat: string;
- }>();
-
- return (
- <div class="flex min-h-full flex-col ">
- <div class="sm:mx-auto sm:w-full sm:max-w-md">
- <h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
- Create account
- </h2>
- </div>
-
- <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px] ">
- <div class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12">
- <Form.Provider
- computeFormState={(v) => {
- return {
- password: {
- error: !v.password
- ? i18n.str`required`
- : v.password.length < 8
- ? i18n.str`should have at least 8 characters`
- : !v.password.match(/[a-z]/) && v.password.match(/[A-Z]/)
- ? i18n.str`should have lowercase and uppercase characters`
- : !v.password.match(/\d/)
- ? i18n.str`should have numbers`
- : !v.password.match(/[^a-zA-Z\d]/)
- ? i18n.str`should have at least one character which is not a number or letter`
- : undefined,
- },
- repeat: {
- // error: !v.repeat
- // ? i18n.str`required`
- // // : v.repeat !== v.password
- // // ? i18n.str`doesn't match`
- // : undefined,
- },
- };
- }}
- onSubmit={async (v) => {
- const account = await createNewAccount(v.password);
- onNewAccount(account, v.password);
- }}
- >
- <div class="mb-4">
- <Form.InputLine
- label={"Password" as TranslatedString}
- name="password"
- type="password"
- help={
- "lower and upper case letters, number and special character" as TranslatedString
- }
- required
- />
- </div>
- <div class="mb-4">
- <Form.InputLine
- label={"Repeat password" as TranslatedString}
- name="repeat"
- type="password"
- required
- />
- </div>
-
- <div class="mt-8">
- <button
- type="submit"
- class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 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"
- >
- Create
- </button>
- </div>
- </Form.Provider>
- </div>
- </div>
- </div>
- );
-}
-
-function UnlockAccount({
- lockedAccount,
- onAccountUnlocked,
-}: {
- lockedAccount: LockedAccount;
- onAccountUnlocked: (password: string) => void;
-}): VNode {
- const Form = createNewForm<{
- password: string;
- }>();
-
- return (
- <div class="flex min-h-full flex-col ">
- <div class="sm:mx-auto sm:w-full sm:max-w-md">
- <h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
- Account locked
- </h2>
- <p class="mt-6 text-lg leading-8 text-gray-600">
- Your account is normally locked anytime you reload. To unlock type
- your password again.
- </p>
- </div>
-
- <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px] ">
- <div class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12">
- <Form.Provider
- onSubmit={async (v) => {
- try {
- // test login
- await unlockAccount(lockedAccount, v.password);
-
- onAccountUnlocked(v.password ?? "");
- notifyInfo("Account unlocked" as TranslatedString);
- } catch (e) {
- if (e instanceof UnwrapKeyError) {
- notifyError(
- "Could not unlock account" as any,
- e.message as any,
- );
- } else {
- throw e;
- }
- }
- }}
- >
- <div class="mb-4">
- <Form.InputLine
- label={"Password" as TranslatedString}
- name="password"
- type="password"
- required
- />
- </div>
-
- <div class="mt-8">
- <button
- type="submit"
- class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 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"
- >
- Unlock
- </button>
- </div>
- </Form.Provider>
- </div>
- </div>
- </div>
- );
-}