aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-09-26 15:18:43 -0300
committerSebastian <sebasjm@gmail.com>2023-09-26 15:18:43 -0300
commit1e4f21cc76345f3881ea8e5ea0e94d27d26da609 (patch)
tree4f921a15b8b146adf479e0068639a49553da4cc1
parentdcdf8fb6a067b4e40b13f1c0f106758cfba76309 (diff)
lang selector and fix logout
-rw-r--r--packages/demobank-ui/package.json2
-rw-r--r--packages/demobank-ui/src/components/LangSelector.tsx78
-rw-r--r--packages/demobank-ui/src/components/app.tsx6
-rw-r--r--packages/demobank-ui/src/context/backend.ts4
-rw-r--r--packages/demobank-ui/src/declaration.d.ts4
-rw-r--r--packages/demobank-ui/src/hooks/backend.ts44
-rw-r--r--packages/demobank-ui/src/hooks/circuit.ts4
-rw-r--r--packages/demobank-ui/src/hooks/config.ts20
-rw-r--r--packages/demobank-ui/src/hooks/useCredentialsChecker.ts2
-rw-r--r--packages/demobank-ui/src/pages/BankFrame.tsx128
-rw-r--r--packages/demobank-ui/src/pages/LoginForm.tsx27
-rw-r--r--packages/demobank-ui/src/pages/OperationState/views.tsx17
-rw-r--r--packages/demobank-ui/src/pages/RegistrationPage.tsx138
-rw-r--r--packages/demobank-ui/src/pages/admin/Account.tsx2
-rw-r--r--packages/demobank-ui/src/pages/business/Home.tsx2
-rw-r--r--packages/demobank-ui/src/settings.ts14
16 files changed, 272 insertions, 220 deletions
diff --git a/packages/demobank-ui/package.json b/packages/demobank-ui/package.json
index 744cb4180..b430ebc24 100644
--- a/packages/demobank-ui/package.json
+++ b/packages/demobank-ui/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@gnu-taler/demobank-ui",
- "version": "0.1.0",
+ "version": "0.9.3-dev.17",
"license": "AGPL-3.0-OR-LATER",
"type": "module",
"scripts": {
diff --git a/packages/demobank-ui/src/components/LangSelector.tsx b/packages/demobank-ui/src/components/LangSelector.tsx
index ca4411682..c1d0f64ef 100644
--- a/packages/demobank-ui/src/components/LangSelector.tsx
+++ b/packages/demobank-ui/src/components/LangSelector.tsx
@@ -42,11 +42,11 @@ function getLangName(s: keyof LangsNames | string): string {
return String(s);
}
-// FIXME: explain "like py".
-export function LangSelectorLikePy(): VNode {
+export function LangSelector(): VNode {
const [updatingLang, setUpdatingLang] = useState(false);
const { lang, changeLanguage } = useTranslationContext();
const [hidden, setHidden] = useState(true);
+
useEffect(() => {
function bodyKeyPress(event: KeyboardEvent) {
if (event.code === "Escape") setHidden(true);
@@ -62,51 +62,49 @@ export function LangSelectorLikePy(): VNode {
};
}, []);
return (
- <Fragment>
- <a
- href="#"
- class="pure-button"
- name="language"
- onClick={(ev) => {
- ev.preventDefault();
- setHidden((h) => !h);
- ev.stopPropagation();
- }}
- >
- {getLangName(lang)}
- </a>
- <div
- id="lang"
- class={hidden ? "hide" : ""}
- style={{
- display: "inline-block",
- }}
- >
- <div style="position: relative; overflow: visible;">
- <div
- class="nav"
- style="position: absolute; max-height: 60vh; overflow-y: auto; margin-left: -120px; margin-top: 20px"
- >
+ <div>
+ <div class="relative mt-2">
+ <button type="button" class="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label"
+ onClick={() => {
+ setHidden((h) => !h);
+ }}>
+ <span class="flex items-center">
+ <img src="https://taler.net/images/languageicon.svg" alt="" class="h-5 w-5 flex-shrink-0 rounded-full" />
+ <span class="ml-3 block truncate">{getLangName(lang)}</span>
+ </span>
+ <span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
+ <svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+ <path fill-rule="evenodd" d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule="evenodd" />
+ </svg>
+ </span>
+ </button>
+
+ {!hidden &&
+ <ul class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" tabIndex={-1} role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
{Object.keys(messages)
.filter((l) => l !== lang)
- .map((l) => (
- <a
- key={l}
- href="#"
- class="navbtn langbtn"
- value={l}
+ .map((lang) => (
+ <li class="text-gray-900 hover:bg-indigo-600 hover:text-white cursor-pointer relative select-none py-2 pl-3 pr-9" role="option"
onClick={() => {
- changeLanguage(l);
+ changeLanguage(lang);
setUpdatingLang(false);
+ setHidden(true)
}}
>
- {getLangName(l)}
- </a>
+ <span class="font-normal block truncate">{getLangName(lang)}</span>
+
+ <span class="text-indigo-600 absolute inset-y-0 right-0 flex items-center pr-4">
+ {/* <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+ <path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd" />
+ </svg> */}
+ </span>
+ </li>
))}
- <br />
- </div>
- </div>
+
+ </ul>
+ }
+
</div>
- </Fragment>
+ </div>
);
}
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
index a587c6f1e..f15a9ee6a 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -78,17 +78,17 @@ function VersionCheck({ children }: { children: ComponentChildren }): VNode {
if (checked === undefined) {
return <Loading />
}
- if (typeof checked === "string") {
+ if (checked.type === "wrong") {
return <BankFrame>
the bank backend is not supported. supported version "{BANK_INTEGRATION_PROTOCOL_VERSION}", server version "{checked}"
</BankFrame>
}
- if (checked === true) {
+ if (checked.type === "ok") {
return <Fragment>{children}</Fragment>
}
return <BankFrame>
- <ErrorLoading error={checked}/>
+ <ErrorLoading error={checked.result}/>
</BankFrame>
}
diff --git a/packages/demobank-ui/src/context/backend.ts b/packages/demobank-ui/src/context/backend.ts
index b311ddbb0..eae187c6d 100644
--- a/packages/demobank-ui/src/context/backend.ts
+++ b/packages/demobank-ui/src/context/backend.ts
@@ -34,6 +34,9 @@ const initial: Type = {
logOut() {
null;
},
+ expired() {
+ null;
+ },
logIn(info) {
null;
},
@@ -65,6 +68,7 @@ export const BackendStateProviderTesting = ({
const value: BackendStateHandler = {
state,
logIn: () => {},
+ expired: () => {},
logOut: () => {},
};
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
index d3d9e02ef..bd7edf033 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -74,7 +74,9 @@ type HashCode = string;
type EddsaPublicKey = string;
type EddsaSignature = string;
type WireTransferIdentifierRawP = string;
-type RelativeTime = Duration;
+type RelativeTime = {
+ d_us: number | "forever"
+};
type ImageDataUrl = string;
interface WithId {
diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts
index 3d5bfa360..889618646 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -46,16 +46,18 @@ import { AccessToken } from "./useCredentialsChecker.js";
* Has the information to reach and
* authenticate at the bank's backend.
*/
-export type BackendState = LoggedIn | LoggedOut;
+export type BackendState = LoggedIn | LoggedOut | Expired;
-export interface BackendCredentials {
+interface LoggedIn {
+ status: "loggedIn";
+ isUserAdministrator: boolean;
username: string;
token: AccessToken;
}
-
-interface LoggedIn extends BackendCredentials {
- status: "loggedIn";
+interface Expired {
+ status: "expired";
isUserAdministrator: boolean;
+ username: string;
}
interface LoggedOut {
status: "loggedOut";
@@ -69,6 +71,13 @@ export const codecForBackendStateLoggedIn = (): Codec<LoggedIn> =>
.property("isUserAdministrator", codecForBoolean())
.build("BackendState.LoggedIn");
+export const codecForBackendStateExpired = (): Codec<Expired> =>
+ buildCodecForObject<Expired>()
+ .property("status", codecForConstString("expired"))
+ .property("username", codecForString())
+ .property("isUserAdministrator", codecForBoolean())
+ .build("BackendState.Expired");
+
export const codecForBackendStateLoggedOut = (): Codec<LoggedOut> =>
buildCodecForObject<LoggedOut>()
.property("status", codecForConstString("loggedOut"))
@@ -79,6 +88,7 @@ export const codecForBackendState = (): Codec<BackendState> =>
.discriminateOn("status")
.alternative("loggedIn", codecForBackendStateLoggedIn())
.alternative("loggedOut", codecForBackendStateLoggedOut())
+ .alternative("expired", codecForBackendStateExpired())
.build("BackendState");
export function getInitialBackendBaseURL(): string {
@@ -94,8 +104,9 @@ export function getInitialBackendBaseURL(): string {
"ERROR: backendBaseURL was overridden by a setting file and missing. Setting value to 'window.origin'",
);
result = window.origin
+ } else {
+ result = bankUiSettings.backendBaseURL;
}
- result = bankUiSettings.backendBaseURL;
} else {
// testing/development path
result = overrideUrl
@@ -115,7 +126,8 @@ export const defaultState: BackendState = {
export interface BackendStateHandler {
state: BackendState;
logOut(): void;
- logIn(info: BackendCredentials): void;
+ expired(): void;
+ logIn(info: {username: string, token: AccessToken}): void;
}
const BACKEND_STATE_KEY = buildStorageKey(
@@ -133,12 +145,22 @@ export function useBackendState(): BackendStateHandler {
BACKEND_STATE_KEY,
defaultState,
);
+ const mutateAll = useMatchMutate();
return {
state,
logOut() {
update(defaultState);
},
+ expired() {
+ if (state.status === "loggedOut") return;
+ const nextState: BackendState = {
+ status: "expired",
+ username: state.username,
+ isUserAdministrator: state.username === "admin",
+ };
+ update(nextState);
+ },
logIn(info) {
//admin is defined by the username
const nextState: BackendState = {
@@ -147,6 +169,7 @@ export function useBackendState(): BackendStateHandler {
isUserAdministrator: info.username === "admin",
};
update(nextState);
+ mutateAll(/.*/)
},
};
}
@@ -194,7 +217,7 @@ export function usePublicBackend(): useBackendType {
number,
]): Promise<HttpResponseOk<T>> {
const delta = -1 * size //descending order
- const params = start ? { delta, start } : {delta}
+ const params = start ? { delta, start } : { delta }
return requestHandler<T>(baseUrl, endpoint, {
params,
});
@@ -262,7 +285,8 @@ export function useAuthenticatedBackend(): useBackendType {
const { state } = useBackendContext();
const { request: requestHandler } = useApiContext();
- const creds = state.status === "loggedIn" ? state.token : undefined;
+ // FIXME: libeufin returns 400 insteand of 401 if there is no auth token
+ const creds = state.status === "loggedIn" ? state.token : "secret-token:a";
const baseUrl = getInitialBackendBaseURL();
const request = useCallback(
@@ -288,7 +312,7 @@ export function useAuthenticatedBackend(): useBackendType {
number,
]): Promise<HttpResponseOk<T>> {
const delta = -1 * size //descending order
- const params = start ? { delta, start } : {delta}
+ const params = start ? { delta, start } : { delta }
return requestHandler<T>(baseUrl, endpoint, {
token: creds,
params,
diff --git a/packages/demobank-ui/src/hooks/circuit.ts b/packages/demobank-ui/src/hooks/circuit.ts
index 4ef80b055..82caafdf2 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -268,7 +268,7 @@ export function useEstimator(): CashoutEstimators {
const { state } = useBackendContext();
const { request } = useApiContext();
const creds =
- state.status === "loggedOut"
+ state.status !== "loggedIn"
? undefined
: state.token;
return {
@@ -340,7 +340,7 @@ export function useBusinessAccountFlag(): boolean | undefined {
const { state } = useBackendContext();
const { request } = useApiContext();
const creds =
- state.status === "loggedOut"
+ state.status !== "loggedIn"
? undefined
: {user: state.username, token: state.token};
diff --git a/packages/demobank-ui/src/hooks/config.ts b/packages/demobank-ui/src/hooks/config.ts
index 4cf677d35..bb5134510 100644
--- a/packages/demobank-ui/src/hooks/config.ts
+++ b/packages/demobank-ui/src/hooks/config.ts
@@ -18,23 +18,29 @@ async function getConfigState(
return result.data;
}
-export function useConfigState(): undefined | true | string | HttpError<SandboxBackend.SandboxError> {
- const [checked, setChecked] = useState<true | string | HttpError<SandboxBackend.SandboxError>>()
+type Result = undefined
+ | { type: "ok", result: SandboxBackend.Config }
+ | { type: "wrong", result: SandboxBackend.Config }
+ | { type: "error", result: HttpError<SandboxBackend.SandboxError> }
+
+export function useConfigState(): Result {
+ const [checked, setChecked] = useState<Result>()
const { request } = useApiContext();
useEffect(() => {
getConfigState(request)
- .then((s) => {
- const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, s.version)
+ .then((result) => {
+ const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, result.version)
if (r?.compatible) {
- setChecked(true);
+ setChecked({ type: "ok",result });
} else {
- setChecked(s.version)
+ setChecked({ type: "wrong",result })
}
})
.catch((error: unknown) => {
if (error instanceof RequestError) {
- setChecked(error.cause);
+ const result = error.cause
+ setChecked({ type:"error", result });
}
});
}, []);
diff --git a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
index f66a4a7c6..02f4544db 100644
--- a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
+++ b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
@@ -15,7 +15,7 @@ export function useCredentialsChecker() {
scope: "readwrite" as "write", //FIX: different than merchant
duration: {
// d_us: "forever" //FIX: should return shortest
- d_us: 1000 * 60 * 60 * 23
+ d_us: 1000 * 1000 * 5 //60 * 60 * 24 * 7
},
refreshable: true,
}
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 5c43d2c3e..3d09fcec7 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -18,7 +18,7 @@ import { Amounts, Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringi
import { notifyError, notifyException, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, h, VNode } from "preact";
import { StateUpdater, useEffect, useErrorBoundary, useState } from "preact/hooks";
-import { LangSelectorLikePy as LangSelector } from "../components/LangSelector.js";
+import { LangSelector } from "../components/LangSelector.js";
import { useBackendContext } from "../context/backend.js";
import { useBusinessAccountDetails } from "../hooks/circuit.js";
import { bankUiSettings } from "../settings.js";
@@ -65,12 +65,14 @@ export function BankFrame({
}, [error])
const demo_sites = [];
- for (const i in bankUiSettings.demoSites)
- demo_sites.push(
- <a href={bankUiSettings.demoSites[i][1]}>
- {bankUiSettings.demoSites[i][0]}
- </a>,
- );
+ if (bankUiSettings.demoSites) {
+ for (const i in bankUiSettings.demoSites)
+ demo_sites.push(
+ <a href={bankUiSettings.demoSites[i][1]}>
+ {bankUiSettings.demoSites[i][0]}
+ </a>,
+ );
+ }
return (<div class="min-h-full flex flex-col m-0" style="min-height: 100vh;">
<div class="bg-indigo-600 pb-32">
@@ -88,14 +90,16 @@ export function BankFrame({
/>
</a>
</div>
- <div class="hidden sm:block lg:ml-10 ">
- <div class="flex space-x-4">
- {/* <!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" --> */}
- {bankUiSettings.demoSites.map(([name, url]) => {
- return <a href={url} class="text-white hover:bg-indigo-500 hover:bg-opacity-75 rounded-md py-2 px-3 text-sm font-medium">{name}</a>
- })}
+ {bankUiSettings.demoSites &&
+ <div class="hidden sm:block lg:ml-10 ">
+ <div class="flex space-x-4">
+ {/* <!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" --> */}
+ {bankUiSettings.demoSites.map(([name, url]) => {
+ return <a href={url} class="text-white hover:bg-indigo-500 hover:bg-opacity-75 rounded-md py-2 px-3 text-sm font-medium">{name}</a>
+ })}
+ </div>
</div>
- </div>
+ }
</div>
<div class="flex">
@@ -166,26 +170,29 @@ export function BankFrame({
<svg class="h-6 w-6 shrink-0 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>
- Log out
- {/* <span class="ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-gray-50 px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200" aria-hidden="true">5</span> */}
+ <i18n.Translate>Log out</i18n.Translate>
</a>
</li>
- <li class="sm:hidden">
- <div class="text-xs font-semibold leading-6 text-gray-400">
- <i18n.Translate>Sites</i18n.Translate>
- </div>
- <ul role="list" class="-mx-2 mt-2 space-y-1">
- {bankUiSettings.demoSites.map(([name, url]) => {
- return <li>
- <a href={url} target="_blank" rel="noopener noreferrer" class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
- <span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-lg border text-[0.625rem] font-medium bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 group-hover:text-indigo-600">&gt;</span>
- <span class="truncate">{name}</span>
- </a>
- </li>
- })}
- </ul>
+ <li>
+ <LangSelector />
</li>
-
+ {bankUiSettings.demoSites &&
+ <li class="sm:hidden">
+ <div class="text-xs font-semibold leading-6 text-gray-400">
+ <i18n.Translate>Sites</i18n.Translate>
+ </div>
+ <ul role="list" class="-mx-2 mt-2 space-y-1">
+ {bankUiSettings.demoSites.map(([name, url]) => {
+ return <li>
+ <a href={url} target="_blank" rel="noopener noreferrer" class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
+ <span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-lg border text-[0.625rem] font-medium bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 group-hover:text-indigo-600">&gt;</span>
+ <span class="truncate">{name}</span>
+ </a>
+ </li>
+ })}
+ </ul>
+ </li>
+ }
<li>
<ul role="list" class="space-y-1">
<li class="mt-2">
@@ -291,63 +298,6 @@ export function BankFrame({
<Footer />
</div >
- // <Fragment>
- // <header
- // class="demobar"
- // style="display: flex; flex-direction: row; justify-content: space-between;"
- // >
- // <a href="#main" class="skip">{i18n.str`Skip to main content`}</a>
- // <div style="max-width: 50em; margin-left: 2em; margin-right: 2em;">
- // {maybeDemoContent(
- // <p>
- // {IS_PUBLIC_ACCOUNT_ENABLED ? (
- // <i18n.Translate>
- // This part of the demo shows how a bank that supports Taler
- // directly would work. In addition to using your own bank
- // account, you can also see the transaction history of some{" "}
- // <a href="/public-accounts">Public Accounts</a>.
- // </i18n.Translate>
- // ) : (
- // <i18n.Translate>
- // This part of the demo shows how a bank that supports Taler
- // directly would work.
- // </i18n.Translate>
- // )}
- // </p>,
- // )}
- // </div>
- // </header>
- // <div style="display:flex; flex-direction: column;" class="navcontainer">
- // <nav class="demolist">
- // {maybeDemoContent(<Fragment>{demo_sites}</Fragment>)}
- // {backend.state.status === "loggedIn" ? (
- // <Fragment>
- // {goToBusinessAccount && !backend.state.isUserAdministrator ? (
- // <MaybeBusinessButton
- // account={backend.state.username}
- // onClick={goToBusinessAccount}
- // />
- // ) : undefined}
-
- // <LangSelector />
-
- // <a
- // href="#"
- // class="pure-button logout-button"
- // onClick={() => {
- // backend.logOut();
- // updateSettings("currentWithdrawalOperationId", undefined);
- // }}
- // >{i18n.str`Logout`}</a>
- // </Fragment>
- // ) : undefined}
- // </nav>
- // </div>
- // <section id="main" class="content">
- // <StatusBanner />
- // {children}
- // </section>
- // </Fragment>
);
}
@@ -393,7 +343,7 @@ function StatusBanner(): VNode {
}
{n.message.debug &&
<div class="mt-2 text-sm text-red-700 font-mono break-all">
- {n.message.debug}
+ {n.message.debug}
</div>
}
</div>
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index 786399d55..14d261622 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -30,14 +30,32 @@ import { undefinedIfEmpty } from "../utils.js";
*/
export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
const backend = useBackendContext();
- const currentUser = backend.state.status === "loggedIn" ? backend.state.username : undefined
+ const currentUser = backend.state.status !== "loggedOut" ? backend.state.username : undefined
const [username, setUsername] = useState<string | undefined>(currentUser);
const [password, setPassword] = useState<string | undefined>();
const { i18n } = useTranslationContext();
const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
+
+ /**
+ * Register form may be shown in the initialization step.
+ * If this is an error when usgin the app the registration
+ * callback is not set
+ */
+ const isSessionExpired = !onRegister
+
+ // useEffect(() => {
+ // if (backend.state.status === "loggedIn") {
+ // backend.expired()
+ // }
+ // },[])
const ref = useRef<HTMLInputElement>(null);
useEffect(function focusInput() {
+ //FIXME: show invalidate session and allow relogin
+ if (isSessionExpired) {
+ localStorage.removeItem("backend-state");
+ window.location.reload()
+ }
ref.current?.focus();
}, []);
const [busy, setBusy] = useState<Record<string, undefined>>()
@@ -124,13 +142,6 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
setBusy(undefined)
}
- /**
- * Register form may be shown in the initialization step.
- * If this is an error when usgin the app the registration
- * callback is not set
- */
- const isSessionExpired = !onRegister
-
return (
<div class="flex min-h-full flex-col justify-center">
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
index 681a3b94d..93b3694d7 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -14,20 +14,15 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { Amounts, stringifyPaytoUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
+import { stringifyWithdrawUri } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
-import { Transactions } from "../../components/Transactions/index.js";
-import { PaymentOptions } from "../PaymentOptions.js";
-import { State } from "./index.js";
-import { CopyButton } from "../../components/CopyButton.js";
-import { bankUiSettings } from "../../settings.js";
-import { useBusinessAccountDetails } from "../../hooks/circuit.js";
-import { useSettings } from "../../hooks/settings.js";
+import { Fragment, VNode, h } from "preact";
import { useEffect, useMemo, useState } from "preact/hooks";
-import { undefinedIfEmpty } from "../../utils.js";
-import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
import { QR } from "../../components/QR.js";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { useSettings } from "../../hooks/settings.js";
+import { undefinedIfEmpty } from "../../utils.js";
+import { State } from "./index.js";
export function InvalidPaytoView({ payto, onClose }: State.InvalidPayto) {
return (
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index a2543f977..2e931a144 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -49,6 +49,8 @@ export function RegistrationPage({
}
export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
+export const PHONE_REGEX = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/;
+export const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
/**
* Collect and submit registration data.
@@ -58,21 +60,33 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
const [username, setUsername] = useState<string | undefined>();
const [name, setName] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();
+ const [phone, setPhone] = useState<string | undefined>();
+ const [email, setEmail] = useState<string | undefined>();
const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
- const {requestNewLoginToken} = useCredentialsChecker()
+ const { requestNewLoginToken } = useCredentialsChecker()
const { register } = useTestingAPI();
const { i18n } = useTranslationContext();
const errors = undefinedIfEmpty({
- name: !name
- ? i18n.str`Missing name`
- : undefined,
+ // name: !name
+ // ? i18n.str`Missing name`
+ // : undefined,
username: !username
? i18n.str`Missing username`
: !USERNAME_REGEX.test(username)
? i18n.str`Use letters and numbers only, and start with a lowercase letter`
: undefined,
+ phone: !phone
+ ? undefined
+ : !PHONE_REGEX.test(phone)
+ ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
+ : undefined,
+ email: !email
+ ? undefined
+ : !EMAIL_REGEX.test(email)
+ ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
+ : undefined,
password: !password ? i18n.str`Missing password` : undefined,
repeatPassword: !repeatPassword
? i18n.str`Missing password`
@@ -82,9 +96,9 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
});
async function doRegistrationStep() {
- if (!username || !password || !name) return;
+ if (!username || !password) return;
try {
- await register({ name, username, password });
+ await register({ name: name ?? "", username, password });
const resp = await requestNewLoginToken(username, password)
setUsername(undefined);
if (resp.valid) {
@@ -167,7 +181,7 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
<div class="flex min-h-full flex-col justify-center">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
- <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Sign up!`}</h2>
+ <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Registration form`}</h2>
</div>
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
@@ -179,34 +193,6 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
autoCorrect="off"
>
<div>
- <label for="name" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Name</i18n.Translate>
- <b style={{ color: "red" }}> *</b>
- </label>
- <div class="mt-2">
- <input
- autoFocus
- type="text"
- name="name"
- id="name"
- class="block w-full rounded-md border-0 py-1.5 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"
- value={name ?? ""}
- enterkeyhint="next"
- placeholder="your name"
- autocomplete="name"
- required
- onInput={(e): void => {
- setName(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.name}
- isDirty={name !== undefined}
- />
- </div>
- </div>
-
- <div>
<label for="username" class="block text-sm font-medium leading-6 text-gray-900">
<i18n.Translate>Username</i18n.Translate>
<b style={{ color: "red" }}> *</b>
@@ -237,7 +223,7 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
<div>
<div class="flex items-center justify-between">
<label for="password" class="block text-sm font-medium leading-6 text-gray-900">
- Password
+ <i18n.Translate>Password</i18n.Translate>
<b style={{ color: "red" }}> *</b>
</label>
</div>
@@ -266,7 +252,7 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
<div>
<div class="flex items-center justify-between">
<label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">
- Repeat assword
+ <i18n.Translate>Repeat password</i18n.Translate>
<b style={{ color: "red" }}> *</b>
</label>
</div>
@@ -292,6 +278,84 @@ function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, on
</div>
</div>
+ <div>
+ <label for="name" class="block text-sm font-medium leading-6 text-gray-900">
+ <i18n.Translate>Name</i18n.Translate>
+ </label>
+ <div class="mt-2">
+ <input
+ autoFocus
+ type="text"
+ name="name"
+ id="name"
+ class="block w-full rounded-md border-0 py-1.5 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"
+ value={name ?? ""}
+ enterkeyhint="next"
+ placeholder="your name"
+ autocomplete="name"
+ required
+ onInput={(e): void => {
+ setName(e.currentTarget.value);
+ }}
+ />
+ {/* <ShowInputErrorLabel
+ message={errors?.name}
+ isDirty={name !== undefined}
+ /> */}
+ </div>
+ </div>
+
+ {/* <div>
+ <label for="phone" class="block text-sm font-medium leading-6 text-gray-900">
+ <i18n.Translate>Phone</i18n.Translate>
+ </label>
+ <div class="mt-2">
+ <input
+ autoFocus
+ type="text"
+ name="phone"
+ id="phone"
+ class="block w-full rounded-md border-0 py-1.5 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"
+ value={phone ?? ""}
+ enterkeyhint="next"
+ placeholder="your phone"
+ autocomplete="none"
+ onInput={(e): void => {
+ setPhone(e.currentTarget.value);
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errors?.phone}
+ isDirty={phone !== undefined}
+ />
+ </div>
+ </div>
+ <div>
+ <label for="email" class="block text-sm font-medium leading-6 text-gray-900">
+ <i18n.Translate>Email</i18n.Translate>
+ </label>
+ <div class="mt-2">
+ <input
+ autoFocus
+ type="text"
+ name="email"
+ id="email"
+ class="block w-full rounded-md border-0 py-1.5 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"
+ value={email ?? ""}
+ enterkeyhint="next"
+ placeholder="your email"
+ autocomplete="email"
+ onInput={(e): void => {
+ setEmail(e.currentTarget.value);
+ }}
+ />
+ <ShowInputErrorLabel
+ message={errors?.email}
+ isDirty={email !== undefined}
+ />
+ </div>
+ </div> */}
+
<div class="flex w-full justify-between">
<button type="submit"
class="ring-1 ring-gray-600 rounded-md bg-white disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-white-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx
index 521bc8eb3..676fc43d0 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -9,7 +9,7 @@ import { Fragment, h, VNode } from "preact";
export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode {
const { i18n } = useTranslationContext();
const r = useBackendContext();
- const account = r.state.status === "loggedIn" ? r.state.username : "admin";
+ const account = r.state.status !== "loggedOut" ? r.state.username : "admin";
const result = useAccountDetails(account);
if (!result.ok) {
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
index c9d798082..2945cb65a 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -32,7 +32,6 @@ import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Cashouts } from "../../components/Cashouts/index.js";
import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
-import { useBackendContext } from "../../context/backend.js";
import { useAccountDetails } from "../../hooks/access.js";
import {
useCashoutDetails,
@@ -46,7 +45,6 @@ import {
undefinedIfEmpty,
} from "../../utils.js";
import { handleNotOkResult } from "../HomePage.js";
-import { LoginForm } from "../LoginForm.js";
import { Amount } from "../PaytoWireTransferForm.js";
import { ShowAccountDetails } from "../ShowAccountDetails.js";
import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
diff --git a/packages/demobank-ui/src/settings.ts b/packages/demobank-ui/src/settings.ts
index f33bf07e2..c4b510b30 100644
--- a/packages/demobank-ui/src/settings.ts
+++ b/packages/demobank-ui/src/settings.ts
@@ -15,13 +15,13 @@
*/
export interface BankUiSettings {
- backendBaseURL: string;
- allowRegistrations: boolean;
- showDemoNav: boolean;
- simplePasswordForRandomAccounts: boolean;
- allowRandomAccountCreation: boolean;
- bankName: string;
- demoSites: [string, string][];
+ backendBaseURL?: string;
+ allowRegistrations?: boolean;
+ showDemoNav?: boolean;
+ simplePasswordForRandomAccounts?: boolean;
+ allowRandomAccountCreation?: boolean;
+ bankName?: string;
+ demoSites?: [string, string][];
}
/**