aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages/RegistrationPage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/pages/RegistrationPage.tsx')
-rw-r--r--packages/demobank-ui/src/pages/RegistrationPage.tsx426
1 files changed, 0 insertions, 426 deletions
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
deleted file mode 100644
index e1d32002b..000000000
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-import { AccessToken, Logger, TranslatedString } from "@gnu-taler/taler-util";
-import {
- LocalNotificationBanner,
- ShowInputErrorLabel,
- useLocalNotification,
- useTranslationContext
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { useBankCoreApiContext } from "../context/config.js";
-import { useBackendState } from "../hooks/backend.js";
-import { undefinedIfEmpty } from "../utils.js";
-import { getRandomPassword, getRandomUsername } from "./rnd.js";
-import { useSettingsContext } from "../context/settings.js";
-
-const logger = new Logger("RegistrationPage");
-
-export function RegistrationPage({
- onComplete,
- onCancel
-}: {
- onComplete: () => void;
- onCancel: () => void;
-}): VNode {
- const { i18n } = useTranslationContext();
- const { config } = useBankCoreApiContext();
- if (!config.allow_registrations) {
- return (
- <p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
- );
- }
- return <RegistrationForm onComplete={onComplete} onCancel={onCancel} />;
-}
-
-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.
- */
-function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, onCancel: () => void }): VNode {
- const backend = useBackendState();
- 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 [notification, notify, handleError] = useLocalNotification()
- const settings = useSettingsContext();
-
- const { api } = useBankCoreApiContext()
- // const { register } = useTestingAPI();
- const { i18n } = useTranslationContext();
-
- const errors = undefinedIfEmpty({
- 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`
- : repeatPassword !== password
- ? i18n.str`Passwords don't match`
- : undefined,
- });
-
- async function doRegistrationAndLogin(name: string, username: string, password: string, onComplete: () => void) {
- await handleError(async () => {
- const creationResponse = await api.createAccount("" as AccessToken, { name, username, password });
- if (creationResponse.type === "fail") {
- switch (creationResponse.case) {
- case "invalid-phone-or-email": return notify({
- type: "error",
- title: i18n.str`Server replied with invalid phone or email.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "insufficient-funds": return notify({
- type: "error",
- title: i18n.str`Registration is disabled because the bank ran out of bonus credit.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "unauthorized": return notify({
- type: "error",
- title: i18n.str`No enough permission to create that account.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "payto-already-exists": return notify({
- type: "error",
- title: i18n.str`That account id is already taken.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "username-already-exists": return notify({
- type: "error",
- title: i18n.str`That username is already taken.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "username-reserved": return notify({
- type: "error",
- title: i18n.str`That username can't be used because is reserved.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- case "user-cant-set-debt": return notify({
- type: "error",
- title: i18n.str`Only admin is allow to set debt limit.`,
- description: creationResponse.detail.hint as TranslatedString,
- debug: creationResponse.detail,
- })
- default: assertUnreachable(creationResponse)
- }
- }
- const resp = await api.getAuthenticationAPI(username).createAccessToken(password, {
- scope: "readwrite",
- duration: { d_us: "forever" },
- refreshable: true,
- })
-
- if (resp.type === "ok") {
- backend.logIn({ username, token: resp.body.access_token });
- } else {
- switch (resp.case) {
- case "wrong-credentials": return notify({
- type: "error",
- title: i18n.str`Wrong credentials for "${username}"`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- case "not-found": return notify({
- type: "error",
- title: i18n.str`Account not found`,
- description: resp.detail.hint as TranslatedString,
- debug: resp.detail,
- })
- default: assertUnreachable(resp)
- }
- }
- onComplete()
- })
- }
-
- async function doRegistrationStep() {
- if (!username || !password || !name) return;
- await doRegistrationAndLogin(name, username, password, () => {
- setUsername(undefined);
- setPassword(undefined);
- setRepeatPassword(undefined);
- onComplete();
- })
- }
-
- async function doRandomRegistration(tries: number = 3) {
- const user = getRandomUsername();
-
- const pass = settings.simplePasswordForRandomAccounts ? "123" : getRandomPassword();
- const username = `_${user.first}-${user.second}_`
- const name = `${user.first}, ${user.second}`
- await doRegistrationAndLogin(name, username, pass, onComplete)
-
- }
-
- return (
- <Fragment>
- <LocalNotificationBanner notification={notification} />
-
- <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`Account registration`}</h2>
- </div>
-
- <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
- <form class="space-y-6" noValidate
- onSubmit={(e) => {
- e.preventDefault();
- }}
- autoCapitalize="none"
- autoCorrect="off"
- >
- <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>
- </label>
- <div class="mt-2">
- <input
- autoFocus
- type="text"
- name="username"
- id="username"
- 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={username ?? ""}
- enterkeyhint="next"
- placeholder="identification"
- autocomplete="username"
- required
- onInput={(e): void => {
- setUsername(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.username}
- isDirty={username !== undefined}
- />
- </div>
- </div>
-
- <div>
- <div class="flex items-center justify-between">
- <label for="password" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Password</i18n.Translate>
- <b style={{ color: "red" }}> *</b>
- </label>
- </div>
- <div class="mt-2">
- <input
- type="password"
- name="password"
- id="password"
- autocomplete="current-password"
- 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"
- enterkeyhint="send"
- value={password ?? ""}
- placeholder="Password"
- required
- onInput={(e): void => {
- setPassword(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.password}
- isDirty={password !== undefined}
- />
- </div>
- </div>
-
- <div>
- <div class="flex items-center justify-between">
- <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">
- <i18n.Translate>Repeat password</i18n.Translate>
- <b style={{ color: "red" }}> *</b>
- </label>
- </div>
- <div class="mt-2">
- <input
- type="password"
- name="register-repeat"
- id="register-repeat"
- autocomplete="current-password"
- 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"
- enterkeyhint="send"
- value={repeatPassword ?? ""}
- placeholder="Same password"
- required
- onInput={(e): void => {
- setRepeatPassword(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.repeatPassword}
- isDirty={repeatPassword !== undefined}
- />
- </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"
- onClick={(e) => {
- e.preventDefault()
- onCancel()
- }}
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <button type="submit"
- class=" rounded-md bg-indigo-600 disabled:bg-gray-300 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"
- disabled={!!errors}
- onClick={async (e) => {
- e.preventDefault()
-
- doRegistrationStep()
- }}
- >
- <i18n.Translate>Register</i18n.Translate>
- </button>
- </div>
-
- </form>
-
- {settings.allowRandomAccountCreation &&
- <p class="mt-10 text-center text-sm text-gray-500 border-t">
- <button type="submit"
- class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
- onClick={(e) => {
- e.preventDefault()
- doRandomRegistration()
- }}
- >
- <i18n.Translate>Create a random user</i18n.Translate>
- </button>
- </p>
- }
- </div>
- </div>
-
- </Fragment>
- );
-}
-
-export function assertUnreachable(x: never): never {
- throw new Error("Didn't expect to get here");
-}